From 34178d6815761978edd75508ee3876734c4ed16b Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:27:59 +0100 Subject: [PATCH 1/7] feat: switch to project --- .../app/src/components/dialog-select-file.tsx | 1 + .../src/components/dialog-select-project.tsx | 81 +++++++++++++++++++ packages/app/src/i18n/ar.ts | 3 + packages/app/src/i18n/br.ts | 3 + packages/app/src/i18n/bs.ts | 3 + packages/app/src/i18n/da.ts | 3 + packages/app/src/i18n/de.ts | 3 + packages/app/src/i18n/en.ts | 3 + packages/app/src/i18n/es.ts | 3 + packages/app/src/i18n/fr.ts | 3 + packages/app/src/i18n/ja.ts | 3 + packages/app/src/i18n/ko.ts | 3 + packages/app/src/i18n/no.ts | 3 + packages/app/src/i18n/pl.ts | 3 + packages/app/src/i18n/ru.ts | 3 + packages/app/src/i18n/th.ts | 3 + packages/app/src/i18n/zh.ts | 4 + packages/app/src/i18n/zht.ts | 3 + packages/app/src/pages/layout.tsx | 9 +++ 19 files changed, 140 insertions(+) create mode 100644 packages/app/src/components/dialog-select-project.tsx diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 29a3666c034..f02c13bc05d 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -38,6 +38,7 @@ type DialogSelectFileMode = "all" | "files" const ENTRY_LIMIT = 5 const COMMON_COMMAND_IDS = [ + "project.switch", "session.new", "workspace.new", "session.previous", diff --git a/packages/app/src/components/dialog-select-project.tsx b/packages/app/src/components/dialog-select-project.tsx new file mode 100644 index 00000000000..223c81a7f3d --- /dev/null +++ b/packages/app/src/components/dialog-select-project.tsx @@ -0,0 +1,81 @@ +import { useDialog } from "@opencode-ai/ui/context/dialog" +import { Dialog } from "@opencode-ai/ui/dialog" +import { FileIcon } from "@opencode-ai/ui/file-icon" +import { List } from "@opencode-ai/ui/list" +import { base64Encode } from "@opencode-ai/util/encode" +import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { useNavigate } from "@solidjs/router" +import { createMemo } from "solid-js" +import { useGlobalSync } from "@/context/global-sync" +import { useLanguage } from "@/context/language" +import { useLayout } from "@/context/layout" + +type Row = { + directory: string + name: string + path: string + search: string +} + +export function DialogSelectProject(props: { onSelect?: (directory: string) => void | Promise }) { + const dialog = useDialog() + const language = useLanguage() + const layout = useLayout() + const navigate = useNavigate() + const sync = useGlobalSync() + const home = createMemo(() => sync.data.path.home) + + const projects = createMemo(() => { + const base = home() + return layout.projects.list().map((project) => { + const path = base ? project.worktree.replace(base, "~") : project.worktree + const name = project.name || getFilename(project.worktree) + return { + directory: project.worktree, + name, + path, + search: `${name}\n${project.worktree}\n${path}`, + } satisfies Row + }) + }) + + const select = (directory: string) => { + dialog.close() + if (props.onSelect) { + void props.onSelect(directory) + return + } + navigate(`/${base64Encode(directory)}/session`) + } + + return ( + + item.directory} + filterKeys={["search"]} + onSelect={(item) => { + if (!item) return + select(item.directory) + }} + > + {(item) => ( +
+
+ +
+ {item.name} + + {getDirectory(item.path)} + + {getFilename(item.path)} +
+
+
+ )} +
+
+ ) +} diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index e8964a66464..9310901486f 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -21,6 +21,7 @@ export const dict = { "theme.scheme.dark": "داكن", "command.sidebar.toggle": "تبديل الشريط الجانبي", "command.project.open": "فتح مشروع", + "command.project.switch": "التبديل إلى مشروع", "command.provider.connect": "اتصال بموفر", "command.server.switch": "تبديل الخادم", "command.settings.open": "فتح الإعدادات", @@ -82,9 +83,11 @@ export const dict = { "command.session.unshare": "إلغاء مشاركة الجلسة", "command.session.unshare.description": "إيقاف مشاركة هذه الجلسة", "palette.search.placeholder": "البحث في الملفات والأوامر والجلسات", + "palette.search.projects.placeholder": "ابحث في المشاريع", "palette.empty": "لا توجد نتائج", "palette.group.commands": "الأوامر", "palette.group.files": "الملفات", + "palette.group.projects": "المشاريع", "dialog.provider.search.placeholder": "البحث عن موفرين", "dialog.provider.empty": "لم يتم العثور على موفرين", "dialog.provider.group.popular": "شائع", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index f23668a0d87..a80be965259 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -21,6 +21,7 @@ export const dict = { "theme.scheme.dark": "Escuro", "command.sidebar.toggle": "Alternar barra lateral", "command.project.open": "Abrir projeto", + "command.project.switch": "Alternar para projeto", "command.provider.connect": "Conectar provedor", "command.server.switch": "Trocar servidor", "command.settings.open": "Abrir configurações", @@ -82,9 +83,11 @@ export const dict = { "command.session.unshare": "Parar de compartilhar sessão", "command.session.unshare.description": "Parar de compartilhar esta sessão", "palette.search.placeholder": "Buscar arquivos, comandos e sessões", + "palette.search.projects.placeholder": "Buscar projetos", "palette.empty": "Nenhum resultado encontrado", "palette.group.commands": "Comandos", "palette.group.files": "Arquivos", + "palette.group.projects": "Projetos", "dialog.provider.search.placeholder": "Buscar provedores", "dialog.provider.empty": "Nenhum provedor encontrado", "dialog.provider.group.popular": "Popular", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 6951f9db1f8..1e216e80a82 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "Prikaži/sakrij bočnu traku", "command.project.open": "Otvori projekat", + "command.project.switch": "Prebaci na projekat", "command.provider.connect": "Poveži provajdera", "command.server.switch": "Promijeni server", "command.settings.open": "Otvori postavke", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "Zaustavi dijeljenje ove sesije", "palette.search.placeholder": "Pretraži datoteke, komande i sesije", + "palette.search.projects.placeholder": "Pretraži projekte", "palette.empty": "Nema rezultata", "palette.group.commands": "Komande", "palette.group.files": "Datoteke", + "palette.group.projects": "Projekti", "dialog.provider.search.placeholder": "Pretraži provajdere", "dialog.provider.empty": "Nema pronađenih provajdera", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index b870fb51a44..bb14ed72093 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "Skift sidebjælke", "command.project.open": "Åbn projekt", + "command.project.switch": "Skift til projekt", "command.provider.connect": "Tilslut udbyder", "command.server.switch": "Skift server", "command.settings.open": "Åbn indstillinger", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "Stop med at dele denne session", "palette.search.placeholder": "Søg i filer, kommandoer og sessioner", + "palette.search.projects.placeholder": "Søg i projekter", "palette.empty": "Ingen resultater fundet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", + "palette.group.projects": "Projekter", "dialog.provider.search.placeholder": "Søg udbydere", "dialog.provider.empty": "Ingen udbydere fundet", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 24d00a6813a..be59316050b 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -25,6 +25,7 @@ export const dict = { "theme.scheme.dark": "Dunkel", "command.sidebar.toggle": "Seitenleiste umschalten", "command.project.open": "Projekt öffnen", + "command.project.switch": "Zu Projekt wechseln", "command.provider.connect": "Anbieter verbinden", "command.server.switch": "Server wechseln", "command.settings.open": "Einstellungen öffnen", @@ -86,9 +87,11 @@ export const dict = { "command.session.unshare": "Teilen der Sitzung aufheben", "command.session.unshare.description": "Teilen dieser Sitzung beenden", "palette.search.placeholder": "Dateien, Befehle und Sitzungen durchsuchen", + "palette.search.projects.placeholder": "Projekte suchen", "palette.empty": "Keine Ergebnisse gefunden", "palette.group.commands": "Befehle", "palette.group.files": "Dateien", + "palette.group.projects": "Projekte", "dialog.provider.search.placeholder": "Anbieter durchsuchen", "dialog.provider.empty": "Keine Anbieter gefunden", "dialog.provider.group.popular": "Beliebt", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index bea29aa352e..a40a909abb6 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "Toggle sidebar", "command.project.open": "Open project", + "command.project.switch": "Switch to project", "command.provider.connect": "Connect provider", "command.server.switch": "Switch server", "command.settings.open": "Open settings", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "Stop sharing this session", "palette.search.placeholder": "Search files, commands, and sessions", + "palette.search.projects.placeholder": "Search projects", "palette.empty": "No results found", "palette.group.commands": "Commands", "palette.group.files": "Files", + "palette.group.projects": "Projects", "dialog.provider.search.placeholder": "Search providers", "dialog.provider.empty": "No providers found", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 30c52c928fb..194c0ee23ea 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "Alternar barra lateral", "command.project.open": "Abrir proyecto", + "command.project.switch": "Cambiar a proyecto", "command.provider.connect": "Conectar proveedor", "command.server.switch": "Cambiar servidor", "command.settings.open": "Abrir ajustes", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "Dejar de compartir esta sesión", "palette.search.placeholder": "Buscar archivos, comandos y sesiones", + "palette.search.projects.placeholder": "Buscar proyectos", "palette.empty": "No se encontraron resultados", "palette.group.commands": "Comandos", "palette.group.files": "Archivos", + "palette.group.projects": "Proyectos", "dialog.provider.search.placeholder": "Buscar proveedores", "dialog.provider.empty": "No se encontraron proveedores", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 3b690937e8a..aca32c8f338 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -21,6 +21,7 @@ export const dict = { "theme.scheme.dark": "Sombre", "command.sidebar.toggle": "Basculer la barre latérale", "command.project.open": "Ouvrir un projet", + "command.project.switch": "Basculer vers un projet", "command.provider.connect": "Connecter un fournisseur", "command.server.switch": "Changer de serveur", "command.settings.open": "Ouvrir les paramètres", @@ -82,9 +83,11 @@ export const dict = { "command.session.unshare": "Ne plus partager la session", "command.session.unshare.description": "Arrêter de partager cette session", "palette.search.placeholder": "Rechercher des fichiers, des commandes et des sessions", + "palette.search.projects.placeholder": "Rechercher des projets", "palette.empty": "Aucun résultat trouvé", "palette.group.commands": "Commandes", "palette.group.files": "Fichiers", + "palette.group.projects": "Projets", "dialog.provider.search.placeholder": "Rechercher des fournisseurs", "dialog.provider.empty": "Aucun fournisseur trouvé", "dialog.provider.group.popular": "Populaire", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index c8a949e7822..2fd7639f007 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -21,6 +21,7 @@ export const dict = { "theme.scheme.dark": "ダーク", "command.sidebar.toggle": "サイドバーの切り替え", "command.project.open": "プロジェクトを開く", + "command.project.switch": "プロジェクトに切り替え", "command.provider.connect": "プロバイダーに接続", "command.server.switch": "サーバーの切り替え", "command.settings.open": "設定を開く", @@ -82,9 +83,11 @@ export const dict = { "command.session.unshare": "セッションの共有を停止", "command.session.unshare.description": "このセッションの共有を停止", "palette.search.placeholder": "ファイル、コマンド、セッションを検索", + "palette.search.projects.placeholder": "プロジェクトを検索", "palette.empty": "結果が見つかりません", "palette.group.commands": "コマンド", "palette.group.files": "ファイル", + "palette.group.projects": "プロジェクト", "dialog.provider.search.placeholder": "プロバイダーを検索", "dialog.provider.empty": "プロバイダーが見つかりません", "dialog.provider.group.popular": "人気", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index d5cedc7deaa..a9cacce0bb4 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -25,6 +25,7 @@ export const dict = { "theme.scheme.dark": "다크", "command.sidebar.toggle": "사이드바 토글", "command.project.open": "프로젝트 열기", + "command.project.switch": "프로젝트로 전환", "command.provider.connect": "공급자 연결", "command.server.switch": "서버 전환", "command.settings.open": "설정 열기", @@ -86,9 +87,11 @@ export const dict = { "command.session.unshare": "세션 공유 중지", "command.session.unshare.description": "이 세션 공유 중지", "palette.search.placeholder": "파일, 명령어 및 세션 검색", + "palette.search.projects.placeholder": "프로젝트 검색", "palette.empty": "결과 없음", "palette.group.commands": "명령어", "palette.group.files": "파일", + "palette.group.projects": "프로젝트", "dialog.provider.search.placeholder": "공급자 검색", "dialog.provider.empty": "공급자 없음", "dialog.provider.group.popular": "인기", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 02a73def023..ea035acf8ed 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -26,6 +26,7 @@ export const dict = { "command.sidebar.toggle": "Veksle sidepanel", "command.project.open": "Åpne prosjekt", + "command.project.switch": "Bytt til prosjekt", "command.provider.connect": "Koble til leverandør", "command.server.switch": "Bytt server", "command.settings.open": "Åpne innstillinger", @@ -92,9 +93,11 @@ export const dict = { "command.session.unshare.description": "Slutt å dele denne sesjonen", "palette.search.placeholder": "Søk i filer, kommandoer og sesjoner", + "palette.search.projects.placeholder": "Søk i prosjekter", "palette.empty": "Ingen resultater funnet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", + "palette.group.projects": "Prosjekter", "dialog.provider.search.placeholder": "Søk etter leverandører", "dialog.provider.empty": "Ingen leverandører funnet", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 587698e6893..2ca42f2f7a5 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -21,6 +21,7 @@ export const dict = { "theme.scheme.dark": "Ciemny", "command.sidebar.toggle": "Przełącz pasek boczny", "command.project.open": "Otwórz projekt", + "command.project.switch": "Przełącz na projekt", "command.provider.connect": "Połącz dostawcę", "command.server.switch": "Przełącz serwer", "command.settings.open": "Otwórz ustawienia", @@ -82,9 +83,11 @@ export const dict = { "command.session.unshare": "Przestań udostępniać sesję", "command.session.unshare.description": "Zatrzymaj udostępnianie tej sesji", "palette.search.placeholder": "Szukaj plików, poleceń i sesji", + "palette.search.projects.placeholder": "Szukaj projektów", "palette.empty": "Brak wyników", "palette.group.commands": "Polecenia", "palette.group.files": "Pliki", + "palette.group.projects": "Projekty", "dialog.provider.search.placeholder": "Szukaj dostawców", "dialog.provider.empty": "Nie znaleziono dostawców", "dialog.provider.group.popular": "Popularne", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 4dc5007a6e9..314fb6c218e 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "Переключить боковую панель", "command.project.open": "Открыть проект", + "command.project.switch": "Переключиться на проект", "command.provider.connect": "Подключить провайдера", "command.server.switch": "Переключить сервер", "command.settings.open": "Открыть настройки", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "Прекратить публикацию сессии", "palette.search.placeholder": "Поиск файлов, команд и сессий", + "palette.search.projects.placeholder": "Поиск проектов", "palette.empty": "Ничего не найдено", "palette.group.commands": "Команды", "palette.group.files": "Файлы", + "palette.group.projects": "Проекты", "dialog.provider.search.placeholder": "Поиск провайдеров", "dialog.provider.empty": "Провайдеры не найдены", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 831cfe598f3..d30b3a45361 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -23,6 +23,7 @@ export const dict = { "command.sidebar.toggle": "สลับแถบข้าง", "command.project.open": "เปิดโปรเจกต์", + "command.project.switch": "สลับไปยังโปรเจกต์", "command.provider.connect": "เชื่อมต่อผู้ให้บริการ", "command.server.switch": "สลับเซิร์ฟเวอร์", "command.settings.open": "เปิดการตั้งค่า", @@ -89,9 +90,11 @@ export const dict = { "command.session.unshare.description": "หยุดการแชร์เซสชันนี้", "palette.search.placeholder": "ค้นหาไฟล์ คำสั่ง และเซสชัน", + "palette.search.projects.placeholder": "ค้นหาโปรเจกต์", "palette.empty": "ไม่พบผลลัพธ์", "palette.group.commands": "คำสั่ง", "palette.group.files": "ไฟล์", + "palette.group.projects": "โปรเจกต์", "dialog.provider.search.placeholder": "ค้นหาผู้ให้บริการ", "dialog.provider.empty": "ไม่พบผู้ให้บริการ", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 9cda1058481..7de409bc636 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -29,6 +29,8 @@ export const dict = { "command.project.open": "打开项目", + "command.project.switch": "切换到项目", + "command.provider.connect": "连接提供商", "command.server.switch": "切换服务器", @@ -116,9 +118,11 @@ export const dict = { "command.session.unshare.description": "停止分享此会话", "palette.search.placeholder": "搜索文件、命令和会话", + "palette.search.projects.placeholder": "搜索项目", "palette.empty": "未找到结果", "palette.group.commands": "命令", "palette.group.files": "文件", + "palette.group.projects": "项目", "dialog.provider.search.placeholder": "搜索提供商", "dialog.provider.empty": "未找到提供商", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 69f963085ed..7194950b32c 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -27,6 +27,7 @@ export const dict = { "command.sidebar.toggle": "切換側邊欄", "command.project.open": "開啟專案", + "command.project.switch": "切換到專案", "command.provider.connect": "連接提供者", "command.server.switch": "切換伺服器", "command.settings.open": "開啟設定", @@ -93,9 +94,11 @@ export const dict = { "command.session.unshare.description": "停止分享此工作階段", "palette.search.placeholder": "搜尋檔案、命令和工作階段", + "palette.search.projects.placeholder": "搜尋專案", "palette.empty": "找不到結果", "palette.group.commands": "命令", "palette.group.files": "檔案", + "palette.group.projects": "專案", "dialog.provider.search.placeholder": "搜尋提供者", "dialog.provider.empty": "找不到提供者", diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index cb194052d1e..a1f46f2dccc 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -52,6 +52,7 @@ import { DialogSettings } from "@/components/dialog-settings" import { useCommand, type CommandOption } from "@/context/command" import { ConstrainDragXAxis } from "@/utils/solid-dnd" import { DialogSelectDirectory } from "@/components/dialog-select-directory" +import { DialogSelectProject } from "@/components/dialog-select-project" import { DialogEditProject } from "@/components/dialog-edit-project" import { Titlebar } from "@/components/titlebar" import { useServer } from "@/context/server" @@ -908,6 +909,14 @@ export default function Layout(props: ParentProps) { keybind: "mod+o", onSelect: () => chooseProject(), }, + { + id: "project.switch", + title: language.t("command.project.switch"), + category: language.t("command.category.project"), + keybind: "mod+shift+o", + disabled: layout.projects.list().length === 0, + onSelect: () => dialog.show(() => ), + }, { id: "provider.connect", title: language.t("command.provider.connect"), From 8c937ff0e8ad1140bc5e21d348e1cf49415db0a3 Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:36:27 +0100 Subject: [PATCH 2/7] feat: recent project section --- .../components/dialog-select-directory.tsx | 34 +++++++- .../app/src/components/dialog-select-file.tsx | 1 - .../src/components/dialog-select-project.tsx | 81 ------------------- packages/app/src/pages/layout.tsx | 11 +-- 4 files changed, 32 insertions(+), 95 deletions(-) delete mode 100644 packages/app/src/components/dialog-select-project.tsx diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 515e640c9fa..6d2bfc1e2d2 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -8,6 +8,7 @@ import fuzzysort from "fuzzysort" import { createMemo, createResource, createSignal } from "solid-js" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" +import { useLayout } from "@/context/layout" import { useLanguage } from "@/context/language" interface DialogSelectDirectoryProps { @@ -19,6 +20,7 @@ interface DialogSelectDirectoryProps { type Row = { absolute: string search: string + category: string } function cleanInput(value: string) { @@ -101,7 +103,7 @@ function displayPath(path: string, input: string, home: string) { return tildeOf(full, home) || full } -function toRow(absolute: string, home: string): Row { +function toRow(absolute: string, home: string, category: string): Row { const full = trimTrailing(absolute) const tilde = tildeOf(full, home) const withSlash = (value: string) => { @@ -113,7 +115,16 @@ function toRow(absolute: string, home: string): Row { const search = Array.from( new Set([full, withSlash(full), tilde, withSlash(tilde), getFilename(full)].filter(Boolean)), ).join("\n") - return { absolute: full, search } + return { absolute: full, search, category } +} + +function uniqueRows(rows: Row[]) { + const seen = new Set() + return rows.filter((row) => { + if (seen.has(row.absolute)) return false + seen.add(row.absolute) + return true + }) } function useDirectorySearch(args: { @@ -237,6 +248,7 @@ function useDirectorySearch(args: { export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { const sync = useGlobalSync() const sdk = useGlobalSDK() + const layout = useLayout() const dialog = useDialog() const language = useLanguage() @@ -266,9 +278,24 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { start, }) + const recentProjects = createMemo(() => { + const category = language.t("home.recentProjects") + return layout.projects.list().map((project) => { + const row = toRow(project.worktree, home(), category) + const name = project.name || getFilename(project.worktree) + return { + ...row, + search: `${row.search}\n${name}`, + } + }) + }) + + const foldersCategory = createMemo(() => language.t("command.project.open")) + const items = async (value: string) => { const results = await directories(value) - return results.map((absolute) => toRow(absolute, home())) + const directoryRows = results.map((absolute) => toRow(absolute, home(), foldersCategory())) + return uniqueRows([...recentProjects(), ...directoryRows]) } function resolve(absolute: string) { @@ -285,6 +312,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { items={items} key={(x) => x.absolute} filterKeys={["search"]} + groupBy={(item) => item.category} ref={(r) => (list = r)} onFilter={(value) => setFilter(cleanInput(value))} onKeyEvent={(e, item) => { diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index f02c13bc05d..29a3666c034 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -38,7 +38,6 @@ type DialogSelectFileMode = "all" | "files" const ENTRY_LIMIT = 5 const COMMON_COMMAND_IDS = [ - "project.switch", "session.new", "workspace.new", "session.previous", diff --git a/packages/app/src/components/dialog-select-project.tsx b/packages/app/src/components/dialog-select-project.tsx deleted file mode 100644 index 223c81a7f3d..00000000000 --- a/packages/app/src/components/dialog-select-project.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useDialog } from "@opencode-ai/ui/context/dialog" -import { Dialog } from "@opencode-ai/ui/dialog" -import { FileIcon } from "@opencode-ai/ui/file-icon" -import { List } from "@opencode-ai/ui/list" -import { base64Encode } from "@opencode-ai/util/encode" -import { getDirectory, getFilename } from "@opencode-ai/util/path" -import { useNavigate } from "@solidjs/router" -import { createMemo } from "solid-js" -import { useGlobalSync } from "@/context/global-sync" -import { useLanguage } from "@/context/language" -import { useLayout } from "@/context/layout" - -type Row = { - directory: string - name: string - path: string - search: string -} - -export function DialogSelectProject(props: { onSelect?: (directory: string) => void | Promise }) { - const dialog = useDialog() - const language = useLanguage() - const layout = useLayout() - const navigate = useNavigate() - const sync = useGlobalSync() - const home = createMemo(() => sync.data.path.home) - - const projects = createMemo(() => { - const base = home() - return layout.projects.list().map((project) => { - const path = base ? project.worktree.replace(base, "~") : project.worktree - const name = project.name || getFilename(project.worktree) - return { - directory: project.worktree, - name, - path, - search: `${name}\n${project.worktree}\n${path}`, - } satisfies Row - }) - }) - - const select = (directory: string) => { - dialog.close() - if (props.onSelect) { - void props.onSelect(directory) - return - } - navigate(`/${base64Encode(directory)}/session`) - } - - return ( - - item.directory} - filterKeys={["search"]} - onSelect={(item) => { - if (!item) return - select(item.directory) - }} - > - {(item) => ( -
-
- -
- {item.name} - - {getDirectory(item.path)} - - {getFilename(item.path)} -
-
-
- )} -
-
- ) -} diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index a1f46f2dccc..3fced1138f6 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -52,7 +52,6 @@ import { DialogSettings } from "@/components/dialog-settings" import { useCommand, type CommandOption } from "@/context/command" import { ConstrainDragXAxis } from "@/utils/solid-dnd" import { DialogSelectDirectory } from "@/components/dialog-select-directory" -import { DialogSelectProject } from "@/components/dialog-select-project" import { DialogEditProject } from "@/components/dialog-edit-project" import { Titlebar } from "@/components/titlebar" import { useServer } from "@/context/server" @@ -906,17 +905,9 @@ export default function Layout(props: ParentProps) { id: "project.open", title: language.t("command.project.open"), category: language.t("command.category.project"), - keybind: "mod+o", + keybind: "mod+o,mod+shift+o", onSelect: () => chooseProject(), }, - { - id: "project.switch", - title: language.t("command.project.switch"), - category: language.t("command.category.project"), - keybind: "mod+shift+o", - disabled: layout.projects.list().length === 0, - onSelect: () => dialog.show(() => ), - }, { id: "provider.connect", title: language.t("command.provider.connect"), From 6c3faa365883b5e87ed01473f07e97722fb94175 Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:39:21 +0100 Subject: [PATCH 3/7] fix: group sort --- .../components/dialog-select-directory.tsx | 22 +++++++++++-------- packages/app/src/pages/layout.tsx | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 6d2bfc1e2d2..e6b8471b587 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -20,7 +20,7 @@ interface DialogSelectDirectoryProps { type Row = { absolute: string search: string - category: string + group: "recent" | "folders" } function cleanInput(value: string) { @@ -103,7 +103,7 @@ function displayPath(path: string, input: string, home: string) { return tildeOf(full, home) || full } -function toRow(absolute: string, home: string, category: string): Row { +function toRow(absolute: string, home: string, group: Row["group"]): Row { const full = trimTrailing(absolute) const tilde = tildeOf(full, home) const withSlash = (value: string) => { @@ -115,7 +115,7 @@ function toRow(absolute: string, home: string, category: string): Row { const search = Array.from( new Set([full, withSlash(full), tilde, withSlash(tilde), getFilename(full)].filter(Boolean)), ).join("\n") - return { absolute: full, search, category } + return { absolute: full, search, group } } function uniqueRows(rows: Row[]) { @@ -279,9 +279,8 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { }) const recentProjects = createMemo(() => { - const category = language.t("home.recentProjects") return layout.projects.list().map((project) => { - const row = toRow(project.worktree, home(), category) + const row = toRow(project.worktree, home(), "recent") const name = project.name || getFilename(project.worktree) return { ...row, @@ -290,11 +289,9 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { }) }) - const foldersCategory = createMemo(() => language.t("command.project.open")) - const items = async (value: string) => { const results = await directories(value) - const directoryRows = results.map((absolute) => toRow(absolute, home(), foldersCategory())) + const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders")) return uniqueRows([...recentProjects(), ...directoryRows]) } @@ -312,7 +309,14 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { items={items} key={(x) => x.absolute} filterKeys={["search"]} - groupBy={(item) => item.category} + groupBy={(item) => item.group} + sortGroupsBy={(a, b) => { + if (a.category === b.category) return 0 + return a.category === "recent" ? -1 : 1 + }} + groupHeader={(group) => + group.category === "recent" ? language.t("home.recentProjects") : language.t("command.project.open") + } ref={(r) => (list = r)} onFilter={(value) => setFilter(cleanInput(value))} onKeyEvent={(e, item) => { diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 3fced1138f6..cb194052d1e 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -905,7 +905,7 @@ export default function Layout(props: ParentProps) { id: "project.open", title: language.t("command.project.open"), category: language.t("command.category.project"), - keybind: "mod+o,mod+shift+o", + keybind: "mod+o", onSelect: () => chooseProject(), }, { From c46aa44a9c2ff8dc6bc66e2bfaf6f4eada9bd23f Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:45:16 +0100 Subject: [PATCH 4/7] chore: remove not needed translations --- packages/app/src/i18n/ar.ts | 3 --- packages/app/src/i18n/br.ts | 3 --- packages/app/src/i18n/bs.ts | 3 --- packages/app/src/i18n/da.ts | 3 --- packages/app/src/i18n/de.ts | 3 --- packages/app/src/i18n/en.ts | 3 --- packages/app/src/i18n/es.ts | 3 --- packages/app/src/i18n/fr.ts | 3 --- packages/app/src/i18n/ja.ts | 3 --- packages/app/src/i18n/ko.ts | 3 --- packages/app/src/i18n/no.ts | 3 --- packages/app/src/i18n/pl.ts | 3 --- packages/app/src/i18n/ru.ts | 3 --- packages/app/src/i18n/th.ts | 3 --- packages/app/src/i18n/zh.ts | 3 --- packages/app/src/i18n/zht.ts | 3 --- 16 files changed, 48 deletions(-) diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 9310901486f..e8964a66464 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -21,7 +21,6 @@ export const dict = { "theme.scheme.dark": "داكن", "command.sidebar.toggle": "تبديل الشريط الجانبي", "command.project.open": "فتح مشروع", - "command.project.switch": "التبديل إلى مشروع", "command.provider.connect": "اتصال بموفر", "command.server.switch": "تبديل الخادم", "command.settings.open": "فتح الإعدادات", @@ -83,11 +82,9 @@ export const dict = { "command.session.unshare": "إلغاء مشاركة الجلسة", "command.session.unshare.description": "إيقاف مشاركة هذه الجلسة", "palette.search.placeholder": "البحث في الملفات والأوامر والجلسات", - "palette.search.projects.placeholder": "ابحث في المشاريع", "palette.empty": "لا توجد نتائج", "palette.group.commands": "الأوامر", "palette.group.files": "الملفات", - "palette.group.projects": "المشاريع", "dialog.provider.search.placeholder": "البحث عن موفرين", "dialog.provider.empty": "لم يتم العثور على موفرين", "dialog.provider.group.popular": "شائع", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index a80be965259..f23668a0d87 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -21,7 +21,6 @@ export const dict = { "theme.scheme.dark": "Escuro", "command.sidebar.toggle": "Alternar barra lateral", "command.project.open": "Abrir projeto", - "command.project.switch": "Alternar para projeto", "command.provider.connect": "Conectar provedor", "command.server.switch": "Trocar servidor", "command.settings.open": "Abrir configurações", @@ -83,11 +82,9 @@ export const dict = { "command.session.unshare": "Parar de compartilhar sessão", "command.session.unshare.description": "Parar de compartilhar esta sessão", "palette.search.placeholder": "Buscar arquivos, comandos e sessões", - "palette.search.projects.placeholder": "Buscar projetos", "palette.empty": "Nenhum resultado encontrado", "palette.group.commands": "Comandos", "palette.group.files": "Arquivos", - "palette.group.projects": "Projetos", "dialog.provider.search.placeholder": "Buscar provedores", "dialog.provider.empty": "Nenhum provedor encontrado", "dialog.provider.group.popular": "Popular", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 1e216e80a82..6951f9db1f8 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "Prikaži/sakrij bočnu traku", "command.project.open": "Otvori projekat", - "command.project.switch": "Prebaci na projekat", "command.provider.connect": "Poveži provajdera", "command.server.switch": "Promijeni server", "command.settings.open": "Otvori postavke", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "Zaustavi dijeljenje ove sesije", "palette.search.placeholder": "Pretraži datoteke, komande i sesije", - "palette.search.projects.placeholder": "Pretraži projekte", "palette.empty": "Nema rezultata", "palette.group.commands": "Komande", "palette.group.files": "Datoteke", - "palette.group.projects": "Projekti", "dialog.provider.search.placeholder": "Pretraži provajdere", "dialog.provider.empty": "Nema pronađenih provajdera", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index bb14ed72093..b870fb51a44 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "Skift sidebjælke", "command.project.open": "Åbn projekt", - "command.project.switch": "Skift til projekt", "command.provider.connect": "Tilslut udbyder", "command.server.switch": "Skift server", "command.settings.open": "Åbn indstillinger", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "Stop med at dele denne session", "palette.search.placeholder": "Søg i filer, kommandoer og sessioner", - "palette.search.projects.placeholder": "Søg i projekter", "palette.empty": "Ingen resultater fundet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", - "palette.group.projects": "Projekter", "dialog.provider.search.placeholder": "Søg udbydere", "dialog.provider.empty": "Ingen udbydere fundet", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index be59316050b..24d00a6813a 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -25,7 +25,6 @@ export const dict = { "theme.scheme.dark": "Dunkel", "command.sidebar.toggle": "Seitenleiste umschalten", "command.project.open": "Projekt öffnen", - "command.project.switch": "Zu Projekt wechseln", "command.provider.connect": "Anbieter verbinden", "command.server.switch": "Server wechseln", "command.settings.open": "Einstellungen öffnen", @@ -87,11 +86,9 @@ export const dict = { "command.session.unshare": "Teilen der Sitzung aufheben", "command.session.unshare.description": "Teilen dieser Sitzung beenden", "palette.search.placeholder": "Dateien, Befehle und Sitzungen durchsuchen", - "palette.search.projects.placeholder": "Projekte suchen", "palette.empty": "Keine Ergebnisse gefunden", "palette.group.commands": "Befehle", "palette.group.files": "Dateien", - "palette.group.projects": "Projekte", "dialog.provider.search.placeholder": "Anbieter durchsuchen", "dialog.provider.empty": "Keine Anbieter gefunden", "dialog.provider.group.popular": "Beliebt", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index a40a909abb6..bea29aa352e 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "Toggle sidebar", "command.project.open": "Open project", - "command.project.switch": "Switch to project", "command.provider.connect": "Connect provider", "command.server.switch": "Switch server", "command.settings.open": "Open settings", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "Stop sharing this session", "palette.search.placeholder": "Search files, commands, and sessions", - "palette.search.projects.placeholder": "Search projects", "palette.empty": "No results found", "palette.group.commands": "Commands", "palette.group.files": "Files", - "palette.group.projects": "Projects", "dialog.provider.search.placeholder": "Search providers", "dialog.provider.empty": "No providers found", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 194c0ee23ea..30c52c928fb 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "Alternar barra lateral", "command.project.open": "Abrir proyecto", - "command.project.switch": "Cambiar a proyecto", "command.provider.connect": "Conectar proveedor", "command.server.switch": "Cambiar servidor", "command.settings.open": "Abrir ajustes", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "Dejar de compartir esta sesión", "palette.search.placeholder": "Buscar archivos, comandos y sesiones", - "palette.search.projects.placeholder": "Buscar proyectos", "palette.empty": "No se encontraron resultados", "palette.group.commands": "Comandos", "palette.group.files": "Archivos", - "palette.group.projects": "Proyectos", "dialog.provider.search.placeholder": "Buscar proveedores", "dialog.provider.empty": "No se encontraron proveedores", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index aca32c8f338..3b690937e8a 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -21,7 +21,6 @@ export const dict = { "theme.scheme.dark": "Sombre", "command.sidebar.toggle": "Basculer la barre latérale", "command.project.open": "Ouvrir un projet", - "command.project.switch": "Basculer vers un projet", "command.provider.connect": "Connecter un fournisseur", "command.server.switch": "Changer de serveur", "command.settings.open": "Ouvrir les paramètres", @@ -83,11 +82,9 @@ export const dict = { "command.session.unshare": "Ne plus partager la session", "command.session.unshare.description": "Arrêter de partager cette session", "palette.search.placeholder": "Rechercher des fichiers, des commandes et des sessions", - "palette.search.projects.placeholder": "Rechercher des projets", "palette.empty": "Aucun résultat trouvé", "palette.group.commands": "Commandes", "palette.group.files": "Fichiers", - "palette.group.projects": "Projets", "dialog.provider.search.placeholder": "Rechercher des fournisseurs", "dialog.provider.empty": "Aucun fournisseur trouvé", "dialog.provider.group.popular": "Populaire", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 2fd7639f007..c8a949e7822 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -21,7 +21,6 @@ export const dict = { "theme.scheme.dark": "ダーク", "command.sidebar.toggle": "サイドバーの切り替え", "command.project.open": "プロジェクトを開く", - "command.project.switch": "プロジェクトに切り替え", "command.provider.connect": "プロバイダーに接続", "command.server.switch": "サーバーの切り替え", "command.settings.open": "設定を開く", @@ -83,11 +82,9 @@ export const dict = { "command.session.unshare": "セッションの共有を停止", "command.session.unshare.description": "このセッションの共有を停止", "palette.search.placeholder": "ファイル、コマンド、セッションを検索", - "palette.search.projects.placeholder": "プロジェクトを検索", "palette.empty": "結果が見つかりません", "palette.group.commands": "コマンド", "palette.group.files": "ファイル", - "palette.group.projects": "プロジェクト", "dialog.provider.search.placeholder": "プロバイダーを検索", "dialog.provider.empty": "プロバイダーが見つかりません", "dialog.provider.group.popular": "人気", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index a9cacce0bb4..d5cedc7deaa 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -25,7 +25,6 @@ export const dict = { "theme.scheme.dark": "다크", "command.sidebar.toggle": "사이드바 토글", "command.project.open": "프로젝트 열기", - "command.project.switch": "프로젝트로 전환", "command.provider.connect": "공급자 연결", "command.server.switch": "서버 전환", "command.settings.open": "설정 열기", @@ -87,11 +86,9 @@ export const dict = { "command.session.unshare": "세션 공유 중지", "command.session.unshare.description": "이 세션 공유 중지", "palette.search.placeholder": "파일, 명령어 및 세션 검색", - "palette.search.projects.placeholder": "프로젝트 검색", "palette.empty": "결과 없음", "palette.group.commands": "명령어", "palette.group.files": "파일", - "palette.group.projects": "프로젝트", "dialog.provider.search.placeholder": "공급자 검색", "dialog.provider.empty": "공급자 없음", "dialog.provider.group.popular": "인기", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index ea035acf8ed..02a73def023 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -26,7 +26,6 @@ export const dict = { "command.sidebar.toggle": "Veksle sidepanel", "command.project.open": "Åpne prosjekt", - "command.project.switch": "Bytt til prosjekt", "command.provider.connect": "Koble til leverandør", "command.server.switch": "Bytt server", "command.settings.open": "Åpne innstillinger", @@ -93,11 +92,9 @@ export const dict = { "command.session.unshare.description": "Slutt å dele denne sesjonen", "palette.search.placeholder": "Søk i filer, kommandoer og sesjoner", - "palette.search.projects.placeholder": "Søk i prosjekter", "palette.empty": "Ingen resultater funnet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", - "palette.group.projects": "Prosjekter", "dialog.provider.search.placeholder": "Søk etter leverandører", "dialog.provider.empty": "Ingen leverandører funnet", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 2ca42f2f7a5..587698e6893 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -21,7 +21,6 @@ export const dict = { "theme.scheme.dark": "Ciemny", "command.sidebar.toggle": "Przełącz pasek boczny", "command.project.open": "Otwórz projekt", - "command.project.switch": "Przełącz na projekt", "command.provider.connect": "Połącz dostawcę", "command.server.switch": "Przełącz serwer", "command.settings.open": "Otwórz ustawienia", @@ -83,11 +82,9 @@ export const dict = { "command.session.unshare": "Przestań udostępniać sesję", "command.session.unshare.description": "Zatrzymaj udostępnianie tej sesji", "palette.search.placeholder": "Szukaj plików, poleceń i sesji", - "palette.search.projects.placeholder": "Szukaj projektów", "palette.empty": "Brak wyników", "palette.group.commands": "Polecenia", "palette.group.files": "Pliki", - "palette.group.projects": "Projekty", "dialog.provider.search.placeholder": "Szukaj dostawców", "dialog.provider.empty": "Nie znaleziono dostawców", "dialog.provider.group.popular": "Popularne", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 314fb6c218e..4dc5007a6e9 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "Переключить боковую панель", "command.project.open": "Открыть проект", - "command.project.switch": "Переключиться на проект", "command.provider.connect": "Подключить провайдера", "command.server.switch": "Переключить сервер", "command.settings.open": "Открыть настройки", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "Прекратить публикацию сессии", "palette.search.placeholder": "Поиск файлов, команд и сессий", - "palette.search.projects.placeholder": "Поиск проектов", "palette.empty": "Ничего не найдено", "palette.group.commands": "Команды", "palette.group.files": "Файлы", - "palette.group.projects": "Проекты", "dialog.provider.search.placeholder": "Поиск провайдеров", "dialog.provider.empty": "Провайдеры не найдены", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index d30b3a45361..831cfe598f3 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -23,7 +23,6 @@ export const dict = { "command.sidebar.toggle": "สลับแถบข้าง", "command.project.open": "เปิดโปรเจกต์", - "command.project.switch": "สลับไปยังโปรเจกต์", "command.provider.connect": "เชื่อมต่อผู้ให้บริการ", "command.server.switch": "สลับเซิร์ฟเวอร์", "command.settings.open": "เปิดการตั้งค่า", @@ -90,11 +89,9 @@ export const dict = { "command.session.unshare.description": "หยุดการแชร์เซสชันนี้", "palette.search.placeholder": "ค้นหาไฟล์ คำสั่ง และเซสชัน", - "palette.search.projects.placeholder": "ค้นหาโปรเจกต์", "palette.empty": "ไม่พบผลลัพธ์", "palette.group.commands": "คำสั่ง", "palette.group.files": "ไฟล์", - "palette.group.projects": "โปรเจกต์", "dialog.provider.search.placeholder": "ค้นหาผู้ให้บริการ", "dialog.provider.empty": "ไม่พบผู้ให้บริการ", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 7de409bc636..ee782261cbb 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -29,7 +29,6 @@ export const dict = { "command.project.open": "打开项目", - "command.project.switch": "切换到项目", "command.provider.connect": "连接提供商", @@ -118,11 +117,9 @@ export const dict = { "command.session.unshare.description": "停止分享此会话", "palette.search.placeholder": "搜索文件、命令和会话", - "palette.search.projects.placeholder": "搜索项目", "palette.empty": "未找到结果", "palette.group.commands": "命令", "palette.group.files": "文件", - "palette.group.projects": "项目", "dialog.provider.search.placeholder": "搜索提供商", "dialog.provider.empty": "未找到提供商", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 7194950b32c..69f963085ed 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -27,7 +27,6 @@ export const dict = { "command.sidebar.toggle": "切換側邊欄", "command.project.open": "開啟專案", - "command.project.switch": "切換到專案", "command.provider.connect": "連接提供者", "command.server.switch": "切換伺服器", "command.settings.open": "開啟設定", @@ -94,11 +93,9 @@ export const dict = { "command.session.unshare.description": "停止分享此工作階段", "palette.search.placeholder": "搜尋檔案、命令和工作階段", - "palette.search.projects.placeholder": "搜尋專案", "palette.empty": "找不到結果", "palette.group.commands": "命令", "palette.group.files": "檔案", - "palette.group.projects": "專案", "dialog.provider.search.placeholder": "搜尋提供者", "dialog.provider.empty": "找不到提供者", From 65690579ade7fce7756a3002d44a2a62b320493d Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:45:55 +0100 Subject: [PATCH 5/7] revert file --- packages/app/src/i18n/zh.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index ee782261cbb..9cda1058481 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -29,7 +29,6 @@ export const dict = { "command.project.open": "打开项目", - "command.provider.connect": "连接提供商", "command.server.switch": "切换服务器", From b296f0c9b67e055788e16deea3edd55990c80c00 Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 19:50:05 +0100 Subject: [PATCH 6/7] feat: limit to 5 --- .../components/dialog-select-directory.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index e6b8471b587..6f68c1f28ac 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -279,14 +279,17 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { }) const recentProjects = createMemo(() => { - return layout.projects.list().map((project) => { - const row = toRow(project.worktree, home(), "recent") - const name = project.name || getFilename(project.worktree) - return { - ...row, - search: `${row.search}\n${name}`, - } - }) + return layout.projects + .list() + .slice(0, 5) + .map((project) => { + const row = toRow(project.worktree, home(), "recent") + const name = project.name || getFilename(project.worktree) + return { + ...row, + search: `${row.search}\n${name}`, + } + }) }) const items = async (value: string) => { From b77bcfb62a98d594f383a1219793415967369196 Mon Sep 17 00:00:00 2001 From: neriousy Date: Thu, 26 Feb 2026 20:02:54 +0100 Subject: [PATCH 7/7] feat: show 5 recently used --- .../components/dialog-select-directory.tsx | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 6f68c1f28ac..91e23f8ffa5 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -279,10 +279,28 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { }) const recentProjects = createMemo(() => { - return layout.projects - .list() + const projects = layout.projects.list() + const byProject = new Map() + + for (const project of projects) { + let at = 0 + const dirs = [project.worktree, ...(project.sandboxes ?? [])] + for (const directory of dirs) { + const sessions = sync.child(directory, { bootstrap: false })[0].session + for (const session of sessions) { + if (session.time.archived) continue + const updated = session.time.updated ?? session.time.created + if (updated > at) at = updated + } + } + byProject.set(project.worktree, at) + } + + return projects + .map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index })) + .sort((a, b) => b.at - a.at || a.index - b.index) .slice(0, 5) - .map((project) => { + .map(({ project }) => { const row = toRow(project.worktree, home(), "recent") const name = project.name || getFilename(project.worktree) return {