diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 8e221577b90..adee0f38109 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -1,3 +1,4 @@ +import { Avatar } from "@opencode-ai/ui/avatar" import { useDialog } from "@opencode-ai/ui/context/dialog" import { Dialog } from "@opencode-ai/ui/dialog" import { FileIcon } from "@opencode-ai/ui/file-icon" @@ -11,13 +12,15 @@ import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid- import { formatKeybind, useCommand, type CommandOption } from "@/context/command" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" -import { useLayout } from "@/context/layout" +import { getAvatarColors, useLayout, type LocalProject } from "@/context/layout" import { useFile } from "@/context/file" import { useLanguage } from "@/context/language" +import { useServer } from "@/context/server" +import { useSettings } from "@/context/settings" import { decode64 } from "@/utils/base64" import { getRelativeTime } from "@/utils/time" -type EntryType = "command" | "file" | "session" +type EntryType = "command" | "file" | "project" | "session" type Entry = { id: string @@ -40,6 +43,8 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil const command = useCommand() const language = useLanguage() const layout = useLayout() + const server = useServer() + const settings = useSettings() const file = useFile() const dialog = useDialog() const params = useParams() @@ -87,6 +92,19 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil path, }) + const projectItem = (project: LocalProject): Entry => ({ + id: "project:" + project.worktree, + type: "project", + title: project.name || getFilename(project.worktree), + description: project.worktree, + category: language.t("palette.group.projects"), + path: project.worktree, + }) + + const projectList = createMemo(() => layout.projects.list()) + const projectLookup = createMemo(() => new Map(projectList().map((p) => [p.worktree, p]))) + const projects = createMemo(() => (settings.palette.projects() ? projectList().map(projectItem) : [])) + const projectDirectory = createMemo(() => decode64(params.dir) ?? "") const project = createMemo(() => { const directory = projectDirectory() @@ -274,7 +292,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil const [files, nextSessions] = await Promise.all([file.searchFiles(query), Promise.resolve(sessions(query))]) const entries = files.map(fileItem) - return [...list(), ...nextSessions, ...entries] + return [...projects(), ...list(), ...nextSessions, ...entries] } const handleMove = (item: Entry | undefined) => { @@ -305,6 +323,12 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil return } + if (item.type === "project" && item.path) { + server.projects.touch(item.path) + navigate(`/${base64Encode(item.path)}`) + return + } + if (item.type === "session") { if (!item.directory || !item.sessionID) return navigate(`/${base64Encode(item.directory)}/session/${item.sessionID}`) @@ -368,6 +392,29 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil + + {(() => { + const project = projectLookup().get(item.path ?? "") + const icon = project?.icon as { override?: string; color?: string } | undefined + return ( +
+
+ +
+ {item.title} + {item.description} +
+
+
+ ) + })()} +
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index b31cfb6cc79..66ec73bb1d1 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -238,6 +238,23 @@ export const SettingsGeneral: Component = () => {
+ {/* Command Palette Section */} +
+

{language.t("settings.palette.projects")}

+ +
+ + settings.palette.setProjects(checked)} + /> + +
+
+ {/* System notifications Section */}

{language.t("settings.general.section.notifications")}

diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx index 19b3846f84e..d990e38f5dd 100644 --- a/packages/app/src/context/settings.tsx +++ b/packages/app/src/context/settings.tsx @@ -33,6 +33,9 @@ export interface Settings { } notifications: NotificationSettings sounds: SoundSettings + palette: { + projects: boolean + } } const defaultSettings: Settings = { @@ -61,6 +64,9 @@ const defaultSettings: Settings = { permissions: "staplebops-02", errors: "nope-03", }, + palette: { + projects: true, + }, } const monoFallback = @@ -158,6 +164,12 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont setStore("notifications", "errors", value) }, }, + palette: { + projects: createMemo(() => store.palette?.projects ?? defaultSettings.palette.projects), + setProjects(value: boolean) { + setStore("palette", "projects", value) + }, + }, sounds: { agent: createMemo(() => store.sounds?.agent ?? defaultSettings.sounds.agent), setAgent(value: string) { diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 3778adcd679..c953098f590 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "لا توجد نتائج", "palette.group.commands": "الأوامر", "palette.group.files": "الملفات", + "palette.group.projects": "المشاريع", + "settings.palette.projects": "عرض المشاريع في لوحة الأوامر", + "settings.palette.projects.description": "تضمين المشاريع في نتائج بحث Cmd+P", "dialog.provider.search.placeholder": "البحث عن موفرين", "dialog.provider.empty": "لم يتم العثور على موفرين", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 74bfd8707c8..ef3b19f6206 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "Nenhum resultado encontrado", "palette.group.commands": "Comandos", "palette.group.files": "Arquivos", + "palette.group.projects": "Projetos", + "settings.palette.projects": "Mostrar projetos na paleta de comandos", + "settings.palette.projects.description": "Incluir projetos nos resultados de busca do Cmd+P", "dialog.provider.search.placeholder": "Buscar provedores", "dialog.provider.empty": "Nenhum provedor encontrado", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 7242fb5849f..9c9ddfb0252 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "Ingen resultater fundet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", + "palette.group.projects": "Projekter", + "settings.palette.projects": "Vis projekter i kommandopaletten", + "settings.palette.projects.description": "Inkluder projekter i Cmd+P søgeresultater", "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 bd8acae5e8f..bc5aec17c07 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -92,6 +92,9 @@ export const dict = { "palette.empty": "Keine Ergebnisse gefunden", "palette.group.commands": "Befehle", "palette.group.files": "Dateien", + "palette.group.projects": "Projekte", + "settings.palette.projects": "Projekte in der Befehlspalette anzeigen", + "settings.palette.projects.description": "Projekte in Cmd+P Suchergebnisse einbeziehen", "dialog.provider.search.placeholder": "Anbieter durchsuchen", "dialog.provider.empty": "Keine Anbieter gefunden", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 8fba6861b0b..016bf74e6c7 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -90,6 +90,10 @@ export const dict = { "palette.empty": "No results found", "palette.group.commands": "Commands", "palette.group.files": "Files", + "palette.group.projects": "Projects", + + "settings.palette.projects": "Show projects in command palette", + "settings.palette.projects.description": "Include projects in Cmd+P search results", "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 f9b11ade870..a072981c46e 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "No se encontraron resultados", "palette.group.commands": "Comandos", "palette.group.files": "Archivos", + "palette.group.projects": "Proyectos", + "settings.palette.projects": "Mostrar proyectos en la paleta de comandos", + "settings.palette.projects.description": "Incluir proyectos en los resultados de búsqueda de Cmd+P", "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 0cc81e5ea7a..3153c1d306f 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "Aucun résultat trouvé", "palette.group.commands": "Commandes", "palette.group.files": "Fichiers", + "palette.group.projects": "Projets", + "settings.palette.projects": "Afficher les projets dans la palette de commandes", + "settings.palette.projects.description": "Inclure les projets dans les résultats de recherche Cmd+P", "dialog.provider.search.placeholder": "Rechercher des fournisseurs", "dialog.provider.empty": "Aucun fournisseur trouvé", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 337e1b0d349..f77ee5a5085 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "結果が見つかりません", "palette.group.commands": "コマンド", "palette.group.files": "ファイル", + "palette.group.projects": "プロジェクト", + "settings.palette.projects": "コマンドパレットにプロジェクトを表示", + "settings.palette.projects.description": "Cmd+Pの検索結果にプロジェクトを含める", "dialog.provider.search.placeholder": "プロバイダーを検索", "dialog.provider.empty": "プロバイダーが見つかりません", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 283bb6f3bdc..58aeab2d7cc 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -92,6 +92,9 @@ export const dict = { "palette.empty": "결과 없음", "palette.group.commands": "명령어", "palette.group.files": "파일", + "palette.group.projects": "프로젝트", + "settings.palette.projects": "명령 팔레트에 프로젝트 표시", + "settings.palette.projects.description": "Cmd+P 검색 결과에 프로젝트 포함", "dialog.provider.search.placeholder": "공급자 검색", "dialog.provider.empty": "공급자 없음", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index bbffd0083d1..187577531a7 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -91,6 +91,9 @@ export const dict = { "palette.empty": "Ingen resultater funnet", "palette.group.commands": "Kommandoer", "palette.group.files": "Filer", + "palette.group.projects": "Prosjekter", + "settings.palette.projects": "Vis prosjekter i kommandopaletten", + "settings.palette.projects.description": "Inkluder prosjekter i Cmd+P søkeresultater", "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 2d36ca8c180..8d7f0eb6555 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "Brak wyników", "palette.group.commands": "Polecenia", "palette.group.files": "Pliki", + "palette.group.projects": "Projekty", + "settings.palette.projects": "Pokaż projekty w palecie poleceń", + "settings.palette.projects.description": "Uwzględnij projekty w wynikach wyszukiwania Cmd+P", "dialog.provider.search.placeholder": "Szukaj dostawców", "dialog.provider.empty": "Nie znaleziono dostawców", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 18b0ba5f47d..a0d3a07baa6 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "Ничего не найдено", "palette.group.commands": "Команды", "palette.group.files": "Файлы", + "palette.group.projects": "Проекты", + "settings.palette.projects": "Показывать проекты в палитре команд", + "settings.palette.projects.description": "Включить проекты в результаты поиска Cmd+P", "dialog.provider.search.placeholder": "Поиск провайдеров", "dialog.provider.empty": "Провайдеры не найдены", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index d48a7cea665..82a356ab121 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -88,6 +88,9 @@ export const dict = { "palette.empty": "ไม่พบผลลัพธ์", "palette.group.commands": "คำสั่ง", "palette.group.files": "ไฟล์", + "palette.group.projects": "โปรเจกต์", + "settings.palette.projects": "แสดงโปรเจกต์ในพาเลทคำสั่ง", + "settings.palette.projects.description": "รวมโปรเจกต์ในผลการค้นหา Cmd+P", "dialog.provider.search.placeholder": "ค้นหาผู้ให้บริการ", "dialog.provider.empty": "ไม่พบผู้ให้บริการ", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 070064d1c41..27e60d79118 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -92,6 +92,9 @@ export const dict = { "palette.empty": "未找到结果", "palette.group.commands": "命令", "palette.group.files": "文件", + "palette.group.projects": "项目", + "settings.palette.projects": "在命令面板中显示项目", + "settings.palette.projects.description": "在 Cmd+P 搜索结果中包含项目", "dialog.provider.search.placeholder": "搜索提供商", "dialog.provider.empty": "未找到提供商", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 39dcd92e276..c361ae00916 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -92,6 +92,9 @@ export const dict = { "palette.empty": "找不到結果", "palette.group.commands": "命令", "palette.group.files": "檔案", + "palette.group.projects": "專案", + "settings.palette.projects": "在指令面板中顯示專案", + "settings.palette.projects.description": "在 Cmd+P 搜尋結果中包含專案", "dialog.provider.search.placeholder": "搜尋提供者", "dialog.provider.empty": "找不到提供者",