diff --git a/packages/desktop/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs index 16182c8c..9497d2f3 100644 --- a/packages/desktop/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -115,7 +115,6 @@ const MENU_ITEM_SETTINGS_ID: &str = "menu_settings"; #[cfg(target_os = "macos")] const MENU_ITEM_COMMAND_PALETTE_ID: &str = "menu_command_palette"; #[cfg(target_os = "macos")] -const MENU_ITEM_QUICK_OPEN_ID: &str = "menu_quick_open"; #[cfg(target_os = "macos")] const MENU_ITEM_NEW_SESSION_ID: &str = "menu_new_session"; #[cfg(target_os = "macos")] @@ -394,14 +393,6 @@ fn build_macos_menu( MENU_ITEM_COMMAND_PALETTE_ID, "Command Palette", true, - Some("Cmd+K"), - )?; - - let quick_open = MenuItem::with_id( - app, - MENU_ITEM_QUICK_OPEN_ID, - "Quick Open…", - true, Some("Cmd+P"), )?; @@ -592,7 +583,6 @@ fn build_macos_menu( &PredefinedMenuItem::separator(app)?, &settings, &command_palette, - &quick_open, &PredefinedMenuItem::separator(app)?, &PredefinedMenuItem::services(app, None)?, &PredefinedMenuItem::separator(app)?, @@ -3885,11 +3875,6 @@ fn main() { dispatch_menu_action(app, "command-palette"); return; } - if id == MENU_ITEM_QUICK_OPEN_ID { - dispatch_menu_action(app, "quick-open"); - return; - } - if id == MENU_ITEM_NEW_SESSION_ID { dispatch_menu_action(app, "new-session"); return; diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index ec7cb608..22185e8d 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -2148,8 +2148,7 @@ const buildMacMenu = () => { }, { type: 'separator' }, { label: 'Settings', accelerator: 'Cmd+,', click: () => dispatchAction('settings') }, - { label: 'Command Palette', accelerator: 'Cmd+K', click: () => dispatchAction('command-palette') }, - { label: 'Quick Open…', accelerator: 'Cmd+P', click: () => dispatchAction('quick-open') }, + { label: 'Command Palette', accelerator: 'Cmd+P', click: () => dispatchAction('command-palette') }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 6c8d7949..48ca7ef3 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -54,7 +54,6 @@ import { useGitHubAuthStore } from '@/stores/useGitHubAuthStore'; import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore'; import type { RuntimeAPIs } from '@/lib/api/types'; import { TooltipProvider } from '@/components/ui/tooltip'; -import { QuickOpenDialog } from '@/components/ui/QuickOpenDialog'; import { McpOAuthCallbackPage } from '@/components/sections/mcp/McpOAuthCallbackPage'; import { MCP_OAUTH_CALLBACK_PATH } from '@/components/sections/mcp/mcpOAuth'; import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery'; @@ -925,7 +924,6 @@ function App({ apis }: AppProps) { {!isBootShell && ( <> - {showMemoryDebug && ( setShowMemoryDebug(false)} /> diff --git a/packages/ui/src/components/ui/CommandPalette.tsx b/packages/ui/src/components/ui/CommandPalette.tsx index ca428555..8b43c775 100644 --- a/packages/ui/src/components/ui/CommandPalette.tsx +++ b/packages/ui/src/components/ui/CommandPalette.tsx @@ -1,315 +1,530 @@ import React from 'react'; import { - CommandDialog, + Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, - CommandSeparator, CommandShortcut, } from '@/components/ui/command'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; import { useUIStore } from '@/stores/useUIStore'; import { useSessionUIStore } from '@/sync/session-ui-store'; +import { useGlobalSessionsStore, resolveGlobalSessionDirectory } from '@/stores/useGlobalSessionsStore'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; -import { useThemeSystem } from '@/contexts/useThemeSystem'; +import { useGitAllBranches, useGitStore } from '@/stores/useGitStore'; +import { useFileSearchStore } from '@/stores/useFileSearchStore'; import { useDeviceInfo } from '@/lib/device'; +import { useDebouncedValue } from '@/hooks/useDebouncedValue'; +import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; +import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; +import { getContextFileOpenFailureMessage, validateContextFileOpen } from '@/lib/contextFileOpenGuard'; +import { toast } from '@/components/ui'; +import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; import { RiAddLine, RiChatAi3Line, - RiCheckLine, - RiClipboardLine, - RiComputerLine, - RiFileLine, - RiFolderLine, RiGitBranchLine, RiLayoutLeftLine, RiLayoutRightLine, - RiMoonLine, RiPieChartLine, - RiQuestionLine, RiSettings3Line, - RiSunLine, RiTerminalBoxLine, } from '@remixicon/react'; +import type { Session } from '@opencode-ai/sdk/v2'; import { createWorktreeSession } from '@/lib/worktreeSessionCreator'; import { formatShortcutForDisplay, getEffectiveShortcutCombo } from '@/lib/shortcuts'; import { isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop'; -import { SETTINGS_PAGE_METADATA, SETTINGS_GROUP_LABELS, type SettingsRuntimeContext } from '@/lib/settings/metadata'; +import { SETTINGS_PAGE_METADATA, type SettingsRuntimeContext } from '@/lib/settings/metadata'; import { getSettingsNavIcon } from '@/components/views/SettingsView'; +import { rankByFuzzyQuery, scoreByFuzzyQuery } from '@/lib/search/fuzzySearch'; +import { truncatePathMiddle } from '@/lib/utils'; import { useI18n } from '@/lib/i18n'; +type CommandEntry = { + id: string; + title: string; + icon: React.ReactNode; + shortcutId?: string; + searchText: string; + onSelect: () => void; +}; + +type FileHit = { path: string; name: string; relativePath: string }; + +const normalizePath = (value: string): string => { + if (!value) return ''; + const raw = value.replace(/\\/g, '/'); + const hadUncPrefix = raw.startsWith('//'); + let normalized = raw.replace(/\/+/g, '/'); + if (hadUncPrefix && !normalized.startsWith('//')) normalized = `/${normalized}`; + const isUnixRoot = normalized === '/'; + const isWindowsDriveRoot = /^[A-Za-z]:\/$/.test(normalized); + if (!isUnixRoot && !isWindowsDriveRoot) normalized = normalized.replace(/\/+$/, ''); + return normalized; +}; + export const CommandPalette: React.FC = () => { const { t } = useI18n(); + const isCommandPaletteOpen = useUIStore((s) => s.isCommandPaletteOpen); const setCommandPaletteOpen = useUIStore((s) => s.setCommandPaletteOpen); - const setHelpDialogOpen = useUIStore((s) => s.setHelpDialogOpen); - const setQuickOpenOpen = useUIStore((s) => s.setQuickOpenOpen); const setActiveMainTab = useUIStore((s) => s.setActiveMainTab); const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen); const setSettingsPage = useUIStore((s) => s.setSettingsPage); const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen); const toggleSidebar = useUIStore((s) => s.toggleSidebar); const toggleRightSidebar = useUIStore((s) => s.toggleRightSidebar); - const setRightSidebarOpen = useUIStore((s) => s.setRightSidebarOpen); - const setRightSidebarTab = useUIStore((s) => s.setRightSidebarTab); const toggleBottomTerminal = useUIStore((s) => s.toggleBottomTerminal); - const setBottomTerminalExpanded = useUIStore((s) => s.setBottomTerminalExpanded); - const isBottomTerminalExpanded = useUIStore((s) => s.isBottomTerminalExpanded); const openContextOverview = useUIStore((s) => s.openContextOverview); - const openContextPlan = useUIStore((s) => s.openContextPlan); + const openContextFile = useUIStore((s) => s.openContextFile); const shortcutOverrides = useUIStore((s) => s.shortcutOverrides); const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft); const setCurrentSession = useSessionUIStore((s) => s.setCurrentSession); - const getSessionsByDirectory = useSessionUIStore((s) => s.getSessionsByDirectory); + const activeSessions = useGlobalSessionsStore((s) => s.activeSessions); const currentDirectory = useDirectoryStore((s) => s.currentDirectory); - const { themeMode, setThemeMode } = useThemeSystem(); + const effectiveDirectory = useEffectiveDirectory(); + const searchFiles = useFileSearchStore((s) => s.searchFiles); + const { files: filesApi, git: gitApi } = useRuntimeAPIs(); + const ensureGitStatus = useGitStore((s) => s.ensureStatus); const { isMobile } = useDeviceInfo(); + const currentRoot = React.useMemo( + () => (effectiveDirectory ? normalizePath(effectiveDirectory) : null), + [effectiveDirectory], + ); + + const [query, setQuery] = React.useState(''); + const debouncedQuery = useDebouncedValue(query, 200); + const trimmedQuery = debouncedQuery.trim(); + const liveTrimmed = query.trim(); + + React.useEffect(() => { + if (!isCommandPaletteOpen) setQuery(''); + }, [isCommandPaletteOpen]); + + // Lazy-load git status for every session directory we plan to display so that + // branch labels become available across all projects, not only the active one. + // Deferred to idle to keep the first render (and the file-search effect) free + // from a flood of git store updates. + React.useEffect(() => { + if (!isCommandPaletteOpen || !gitApi) return; + const handle = setTimeout(() => { + const seen = new Set(); + for (const session of activeSessions) { + const dir = resolveGlobalSessionDirectory(session); + if (!dir || seen.has(dir)) continue; + seen.add(dir); + void ensureGitStatus(dir, gitApi); + } + }, 0); + return () => clearTimeout(handle); + }, [isCommandPaletteOpen, activeSessions, gitApi, ensureGitStatus]); + const close = React.useCallback(() => setCommandPaletteOpen(false), [setCommandPaletteOpen]); + const run = React.useCallback( + (fn: () => void | Promise) => () => { + close(); + void fn(); + }, + [close], + ); - const run = React.useCallback((fn: () => void | Promise) => async () => { - close(); - await fn(); - }, [close]); - - const handleCreateSession = run(() => { - setActiveMainTab('chat'); - setSessionSwitcherOpen(false); - openNewSessionDraft(); - }); - - const handleOpenQuickOpen = run(() => setQuickOpenOpen(true)); - - const handleCreateWorktreeSession = run(() => { - createWorktreeSession(); - }); - - const handleOpenSessionList = run(() => { - if (isMobile) { - const { isSessionSwitcherOpen } = useUIStore.getState(); - setSessionSwitcherOpen(!isSessionSwitcherOpen); - } else { - toggleSidebar(); - } - }); - - const handleToggleRightSidebar = run(() => toggleRightSidebar()); - const handleOpenRightSidebarGit = run(() => { setRightSidebarOpen(true); setRightSidebarTab('git'); }); - const handleOpenRightSidebarFiles = run(() => { setRightSidebarOpen(true); setRightSidebarTab('files'); }); - const handleToggleTerminalDock = run(() => toggleBottomTerminal()); - const handleToggleTerminalExpanded = run(() => setBottomTerminalExpanded(!isBottomTerminalExpanded)); - const handleShowHelp = run(() => setHelpDialogOpen(true)); - const handleOpenSettings = run(() => setSettingsDialogOpen(true)); - const handleShowContextUsage = run(() => { - if (currentDirectory) openContextOverview(currentDirectory); - }); - const handleShowPlan = run(() => { - if (currentDirectory) openContextPlan(currentDirectory); - }); - - const handleOpenSettingsPage = (slug: string) => run(() => { - setSettingsPage(slug); - setSettingsDialogOpen(true); - }); - - const handleOpenSession = (sessionId: string, directoryHint?: string | null) => run(() => { - setCurrentSession(sessionId, directoryHint ?? null); - }); - - const handleSetThemeMode = (mode: 'light' | 'dark' | 'system') => run(() => { - setThemeMode(mode); - }); + // --------------------------------------------------------------------------- + // Commands + // --------------------------------------------------------------------------- + const commands = React.useMemo(() => { + const list: CommandEntry[] = [ + { + id: 'new-session', + title: t('commandPalette.item.newSession'), + icon: , + shortcutId: 'new_chat', + searchText: t('commandPalette.item.newSession'), + onSelect: run(() => { + setActiveMainTab('chat'); + setSessionSwitcherOpen(false); + openNewSessionDraft(); + }), + }, + { + id: 'new-worktree', + title: t('commandPalette.item.newWorktreeDraft'), + icon: , + shortcutId: 'new_chat_worktree', + searchText: t('commandPalette.item.newWorktreeDraft'), + onSelect: run(() => { + void createWorktreeSession(); + }), + }, + { + id: 'toggle-sidebar', + title: isMobile + ? t('commandPalette.item.showSessionSwitcher') + : t('commandPalette.item.toggleSidebar'), + icon: , + shortcutId: 'toggle_sidebar', + searchText: isMobile + ? t('commandPalette.item.showSessionSwitcher') + : t('commandPalette.item.toggleSidebar'), + onSelect: run(() => { + if (isMobile) { + const { isSessionSwitcherOpen } = useUIStore.getState(); + setSessionSwitcherOpen(!isSessionSwitcherOpen); + } else { + toggleSidebar(); + } + }), + }, + { + id: 'toggle-right-sidebar', + title: t('commandPalette.item.toggleRightSidebar'), + icon: , + shortcutId: 'toggle_right_sidebar', + searchText: t('commandPalette.item.toggleRightSidebar'), + onSelect: run(() => toggleRightSidebar()), + }, + { + id: 'toggle-terminal', + title: t('commandPalette.item.toggleTerminal'), + icon: , + shortcutId: 'toggle_terminal', + searchText: t('commandPalette.item.toggleTerminal'), + onSelect: run(() => toggleBottomTerminal()), + }, + { + id: 'context-usage', + title: t('commandPalette.item.showContextUsage'), + icon: , + searchText: t('commandPalette.item.showContextUsage'), + onSelect: run(() => { + if (currentDirectory) openContextOverview(currentDirectory); + }), + }, + { + id: 'open-settings', + title: t('commandPalette.item.openSettings'), + icon: , + shortcutId: 'open_settings', + searchText: t('commandPalette.item.openSettings'), + onSelect: run(() => setSettingsDialogOpen(true)), + }, + ]; + return list; + }, [ + t, + run, + isMobile, + setActiveMainTab, + setSessionSwitcherOpen, + openNewSessionDraft, + toggleSidebar, + toggleRightSidebar, + toggleBottomTerminal, + currentDirectory, + openContextOverview, + setSettingsDialogOpen, + ]); + // --------------------------------------------------------------------------- + // Settings sub-pages (only show when there's a query) + // --------------------------------------------------------------------------- const settingsRuntimeCtx = React.useMemo(() => { const isDesktop = isDesktopShell(); return { isVSCode: isVSCodeRuntime(), isWeb: !isDesktop && isWebRuntime(), isDesktop }; }, []); - const settingsItems = React.useMemo(() => { - const groupLabel = (g: string) => (SETTINGS_GROUP_LABELS as Record)[g] ?? g; + const settingsEntries = React.useMemo(() => { return SETTINGS_PAGE_METADATA .filter((p) => p.slug !== 'home') .filter((p) => (p.isAvailable ? p.isAvailable(settingsRuntimeCtx) : true)) - .slice() - .sort((a, b) => { - const g = groupLabel(a.group).localeCompare(groupLabel(b.group)); - if (g !== 0) return g; - return a.title.localeCompare(b.title); + .map((page) => { + const Icon = getSettingsNavIcon(page.slug) ?? RiSettings3Line; + const keywords = (page.keywords ?? []).join(' '); + return { + id: `settings:${page.slug}`, + title: page.title, + icon: , + searchText: `${page.title} ${page.group} ${keywords}`, + onSelect: run(() => { + setSettingsPage(page.slug); + setSettingsDialogOpen(true); + }), + } satisfies CommandEntry; }); - }, [settingsRuntimeCtx]); + }, [settingsRuntimeCtx, run, setSettingsPage, setSettingsDialogOpen]); - const recentSessions = React.useMemo(() => { - return getSessionsByDirectory(currentDirectory ?? '').slice(0, 5); - }, [getSessionsByDirectory, currentDirectory]); + // --------------------------------------------------------------------------- + // Sessions + // --------------------------------------------------------------------------- + const sortedActiveSessions = React.useMemo(() => { + const getUpdated = (s: Session) => + (typeof s.time?.updated === 'number' ? s.time.updated : 0) || + (typeof s.time?.created === 'number' ? s.time.created : 0); + return [...activeSessions].sort((a, b) => getUpdated(b) - getUpdated(a)); + }, [activeSessions]); - const shortcut = React.useCallback((actionId: string) => { - return formatShortcutForDisplay(getEffectiveShortcutCombo(actionId, shortcutOverrides)); - }, [shortcutOverrides]); + const allBranches = useGitAllBranches(); + const worktreeMetadata = useSessionUIStore((s) => s.worktreeMetadata); - const settingsGroupLabelMap: Record = { - appearance: t('commandPalette.settingsGroup.appearance'), - projects: t('commandPalette.settingsGroup.projects'), - general: t('commandPalette.settingsGroup.general'), - opencode: t('commandPalette.settingsGroup.opencode'), - git: t('commandPalette.settingsGroup.git'), - skills: t('commandPalette.settingsGroup.skills'), - usage: t('commandPalette.settingsGroup.usage'), - advanced: t('commandPalette.settingsGroup.advanced'), - }; + const branchForSession = React.useCallback( + (sessionId: string, dir: string | null): string | null => { + const meta = worktreeMetadata.get(sessionId); + if (meta?.branch) return meta.branch.trim() || null; + if (dir) return allBranches.get(dir)?.trim() || null; + return null; + }, + [worktreeMetadata, allBranches], + ); - const settingsPageLabelMap: Record = { - home: t('commandPalette.settingsPage.home'), - projects: t('commandPalette.settingsPage.projects'), - 'remote-instances': t('commandPalette.settingsPage.remoteInstances'), - providers: t('commandPalette.settingsPage.providers'), - usage: t('commandPalette.settingsPage.usage'), - agents: t('commandPalette.settingsPage.agents'), - commands: t('commandPalette.settingsPage.commands'), - mcp: t('commandPalette.settingsPage.mcp'), - 'skills.installed': t('commandPalette.settingsPage.skillsInstalled'), - 'skills.catalog': t('commandPalette.settingsPage.skillsCatalog'), - git: t('commandPalette.settingsPage.git'), - appearance: t('commandPalette.settingsPage.appearance'), - chat: t('commandPalette.settingsPage.chat'), - shortcuts: t('commandPalette.settingsPage.shortcuts'), - sessions: t('commandPalette.settingsPage.sessions'), - 'magic-prompts': t('commandPalette.settingsPage.magicPrompts'), - notifications: t('commandPalette.settingsPage.notifications'), - voice: t('commandPalette.settingsPage.voice'), - tunnel: t('commandPalette.settingsPage.tunnel'), - }; + // --------------------------------------------------------------------------- + // File search + // --------------------------------------------------------------------------- + const [fileResults, setFileResults] = React.useState([]); + const [isSearchingFiles, setIsSearchingFiles] = React.useState(false); + + React.useEffect(() => { + if (!isCommandPaletteOpen) { + setFileResults([]); + setIsSearchingFiles(false); + return; + } + if (!currentRoot || trimmedQuery.length === 0) { + setFileResults([]); + setIsSearchingFiles(false); + return; + } + let cancelled = false; + setIsSearchingFiles(true); + void searchFiles(currentRoot, trimmedQuery, 10, { type: 'file' }) + .then((results) => { + if (cancelled) return; + setFileResults( + results.map((file) => ({ + path: normalizePath(file.path), + name: file.name, + relativePath: file.relativePath, + })), + ); + }) + .catch(() => { + if (!cancelled) setFileResults([]); + }) + .finally(() => { + if (!cancelled) setIsSearchingFiles(false); + }); + return () => { + cancelled = true; + }; + }, [isCommandPaletteOpen, currentRoot, trimmedQuery, searchFiles]); + + // --------------------------------------------------------------------------- + // Filter visible items + // --------------------------------------------------------------------------- + const hasQuery = liveTrimmed.length > 0; + + const scoredCommands = React.useMemo(() => { + if (!hasQuery) return commands.map((item) => ({ item, score: 0 })); + return scoreByFuzzyQuery(commands, liveTrimmed, (c) => c.searchText, { + limit: 7, + noFuzzy: true, + }); + }, [commands, liveTrimmed, hasQuery]); + + const scoredSettings = React.useMemo(() => { + if (!hasQuery) return []; + return scoreByFuzzyQuery(settingsEntries, liveTrimmed, (c) => c.searchText, { + limit: 7, + noFuzzy: true, + }); + }, [settingsEntries, liveTrimmed, hasQuery]); + + const scoredSessions = React.useMemo(() => { + if (!hasQuery) return sortedActiveSessions.slice(0, 5).map((item) => ({ item, score: 0 })); + return scoreByFuzzyQuery(sortedActiveSessions, liveTrimmed, (s) => s.title || '', { + limit: 7, + threshold: 0.2, + }); + }, [sortedActiveSessions, liveTrimmed, hasQuery]); + + const scoredFiles = React.useMemo(() => { + if (!hasQuery || fileResults.length === 0) return []; + // Server already ranked by relevance; compute a comparable client score on + // basename so we can decide file group placement vs sessions/commands. + return scoreByFuzzyQuery(fileResults, liveTrimmed, (f) => f.name, { + limit: 10, + threshold: 0.4, + }); + }, [fileResults, liveTrimmed, hasQuery]); + + const visibleCommands = scoredCommands.map((x) => x.item); + const visibleSettings = scoredSettings.map((x) => x.item); + const visibleSessions = scoredSessions.map((x) => x.item); + const visibleFiles = hasQuery ? scoredFiles.map((x) => x.item) : []; + + const groupOrder = React.useMemo<('commands' | 'settings' | 'sessions' | 'files')[]>(() => { + if (!hasQuery) return ['commands', 'sessions']; + const best = (arr: { score: number }[]): number => (arr.length ? arr[0].score : Infinity); + const groups: { key: 'commands' | 'settings' | 'sessions' | 'files'; score: number }[] = [ + { key: 'commands', score: best(scoredCommands) }, + { key: 'settings', score: best(scoredSettings) }, + { key: 'sessions', score: best(scoredSessions) }, + { key: 'files', score: best(scoredFiles) }, + ]; + groups.sort((a, b) => a.score - b.score); + return groups.map((g) => g.key); + }, [hasQuery, scoredCommands, scoredSettings, scoredSessions, scoredFiles]); + + const handleOpenSession = React.useCallback( + (session: Session) => { + close(); + setCurrentSession(session.id, resolveGlobalSessionDirectory(session)); + }, + [close, setCurrentSession], + ); + + const handleOpenFile = React.useCallback( + async (filePath: string) => { + if (!currentRoot) return; + const validation = await validateContextFileOpen(filesApi, filePath); + if (!validation.ok) { + toast.error(getContextFileOpenFailureMessage(validation.reason)); + return; + } + openContextFile(currentRoot, filePath); + close(); + }, + [currentRoot, filesApi, openContextFile, close], + ); + + const shortcut = React.useCallback( + (actionId: string) => + formatShortcutForDisplay(getEffectiveShortcutCombo(actionId, shortcutOverrides)), + [shortcutOverrides], + ); return ( - - - - {t('commandPalette.empty.noResults')} + + + {t('commandPalette.title')} + {t('commandPalette.description')} + + + + + + {t('commandPalette.empty.noResults')} - - - - {t('commandPalette.item.quickOpen')} - {shortcut('open_quick_open')} - - - - {t('commandPalette.item.newSession')} - {shortcut('new_chat')} - - - - {t('commandPalette.item.newWorktreeDraft')} - {shortcut('new_chat_worktree')} - - - - {isMobile ? t('commandPalette.item.showSessionSwitcher') : t('commandPalette.item.toggleSidebar')} - {shortcut('toggle_sidebar')} - - + {groupOrder.map((groupKey) => { + if (groupKey === 'commands' && visibleCommands.length > 0) { + return ( + + {visibleCommands.map((cmd) => ( + + {cmd.icon} + {cmd.title} + {cmd.shortcutId ? ( + {shortcut(cmd.shortcutId)} + ) : null} + + ))} + + ); + } + if (groupKey === 'settings' && visibleSettings.length > 0) { + return ( + + {visibleSettings.map((cmd) => ( + + {cmd.icon} + {cmd.title} + + ))} + + ); + } + if (groupKey === 'sessions' && visibleSessions.length > 0) { + return ( + + {visibleSessions.map((session) => { + const title = session.title || t('commandPalette.session.untitled'); + const dir = resolveGlobalSessionDirectory(session); + const branch = branchForSession(session.id, dir); + return ( + handleOpenSession(session)} + > + + {title} + {branch ? ( + + + {branch} + + ) : null} + + ); + })} + + ); + } + if (groupKey === 'files' && visibleFiles.length > 0) { + return ( + + {visibleFiles.map((file) => { + const display = truncatePathMiddle(file.relativePath || file.name, { + maxLength: 80, + }); + return ( + { + void handleOpenFile(file.path); + }} + > + + + {display} + + + ); + })} + + ); + } + return null; + })} - - - - - - {t('commandPalette.item.toggleRightSidebar')} - {shortcut('toggle_right_sidebar')} - - - - {t('commandPalette.item.showGitRightSidebar')} - {shortcut('open_right_sidebar_git')} - - - - {t('commandPalette.item.showFilesRightSidebar')} - {shortcut('open_right_sidebar_files')} - - - - {t('commandPalette.item.showContextUsage')} - - - - {t('commandPalette.item.showPlan')} - - - - {t('commandPalette.item.toggleTerminal')} - {shortcut('toggle_terminal')} - - - - {isBottomTerminalExpanded ? t('commandPalette.item.collapseTerminal') : t('commandPalette.item.expandTerminal')} - {shortcut('toggle_terminal_expanded')} - - - - {t('commandPalette.item.keyboardShortcuts')} - {shortcut('open_help')} - - - - - - - handleSetThemeMode('light')()}> - - {t('commandPalette.item.themeLight')} - {themeMode === 'light' && } - - handleSetThemeMode('dark')()}> - - {t('commandPalette.item.themeDark')} - {themeMode === 'dark' && } - - handleSetThemeMode('system')()}> - - {t('commandPalette.item.themeSystem')} - {themeMode === 'system' && } - - - - - - - - - {t('commandPalette.item.openSettings')} - {shortcut('open_settings')} - - {settingsItems.map((page) => { - const Icon = getSettingsNavIcon(page.slug) ?? RiSettings3Line; - return ( - handleOpenSettingsPage(page.slug)()}> - - {settingsGroupLabelMap[page.group] ?? SETTINGS_GROUP_LABELS[page.group]}: {settingsPageLabelMap[page.slug] ?? page.title} - - ); - })} - - - {recentSessions.length > 0 && ( - <> - - - {recentSessions.map((session) => ( - handleOpenSession(session.id, currentDirectory ?? null)()} - > - - {session.title || t('commandPalette.session.untitled')} - - ))} - - - )} - - + {hasQuery && isSearchingFiles && visibleFiles.length === 0 ? ( +
+ {t('commandPalette.empty.searchingFiles')} +
+ ) : null} + + + + ); }; diff --git a/packages/ui/src/components/ui/HelpDialog.tsx b/packages/ui/src/components/ui/HelpDialog.tsx index 2ceba4d0..b21f96af 100644 --- a/packages/ui/src/components/ui/HelpDialog.tsx +++ b/packages/ui/src/components/ui/HelpDialog.tsx @@ -70,12 +70,6 @@ export const HelpDialog: React.FC = () => { icon: RiCommandLine, keys: '', }, - { - id: 'open_quick_open', - descriptionKey: 'helpDialog.item.quickOpenFile', - icon: RiFileLine, - keys: '', - }, { id: 'open_help', descriptionKey: "helpDialog.item.showKeyboardShortcuts", @@ -298,7 +292,7 @@ export const HelpDialog: React.FC = () => {
  • • {t('helpDialog.proTips.commandPalette', { - shortcut: renderShortcut('open_command_palette', `${mod} K`, shortcutOverrides), + shortcut: renderShortcut('open_command_palette', `${mod} P`, shortcutOverrides), })}
  • diff --git a/packages/ui/src/components/ui/QuickOpenDialog.tsx b/packages/ui/src/components/ui/QuickOpenDialog.tsx deleted file mode 100644 index a18210d3..00000000 --- a/packages/ui/src/components/ui/QuickOpenDialog.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import React from 'react'; -import { toast } from '@/components/ui'; -import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from '@/components/ui/command'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { useDebouncedValue } from '@/hooks/useDebouncedValue'; -import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; -import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; -import { getContextFileOpenFailureMessage, validateContextFileOpen } from '@/lib/contextFileOpenGuard'; -import { useDirectoryShowHidden } from '@/lib/directoryShowHidden'; -import { useFilesViewShowGitignored } from '@/lib/filesViewShowGitignored'; -import { useFileSearchStore } from '@/stores/useFileSearchStore'; -import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore'; -import { useUIStore } from '@/stores/useUIStore'; -import { useI18n } from '@/lib/i18n'; - -type RecentQuickOpenFile = { - path: string; - name: string; - relativePath: string; -}; - -const normalizePath = (value: string): string => { - if (!value) return ''; - - const raw = value.replace(/\\/g, '/'); - const hadUncPrefix = raw.startsWith('//'); - - let normalized = raw.replace(/\/+/g, '/'); - if (hadUncPrefix && !normalized.startsWith('//')) { - normalized = `/${normalized}`; - } - - const isUnixRoot = normalized === '/'; - const isWindowsDriveRoot = /^[A-Za-z]:\/$/.test(normalized); - if (!isUnixRoot && !isWindowsDriveRoot) { - normalized = normalized.replace(/\/+$/, ''); - } - - return normalized; -}; - -const getRelativePath = (root: string, filePath: string): string => { - const normalizedRoot = normalizePath(root); - const normalizedPath = normalizePath(filePath); - - if (!normalizedRoot || !normalizedPath) { - return normalizedPath; - } - - if (normalizedPath === normalizedRoot) { - return normalizedPath.split('/').filter(Boolean).pop() || normalizedPath; - } - - if (normalizedPath.startsWith(`${normalizedRoot}/`)) { - return normalizedPath.slice(normalizedRoot.length + 1); - } - - return normalizedPath; -}; - -export const QuickOpenDialog: React.FC = () => { - const { t } = useI18n(); - const { files } = useRuntimeAPIs(); - const isQuickOpenOpen = useUIStore((state) => state.isQuickOpenOpen); - const setQuickOpenOpen = useUIStore((state) => state.setQuickOpenOpen); - const openContextFile = useUIStore((state) => state.openContextFile); - const effectiveDirectory = useEffectiveDirectory(); - const showHidden = useDirectoryShowHidden(); - const showGitignored = useFilesViewShowGitignored(); - const searchFiles = useFileSearchStore((state) => state.searchFiles); - const currentRoot = React.useMemo( - () => (effectiveDirectory ? normalizePath(effectiveDirectory) : undefined), - [effectiveDirectory], - ); - const [query, setQuery] = React.useState(''); - const debouncedQuery = useDebouncedValue(query, 200); - const [searchResults, setSearchResults] = React.useState([]); - const [isSearching, setIsSearching] = React.useState(false); - const rootTabs = useFilesViewTabsStore( - React.useCallback( - (state) => (currentRoot ? state.byRoot[currentRoot] : undefined), - [currentRoot], - ), - ); - - const recentFiles = React.useMemo(() => { - if (!currentRoot || !rootTabs) { - return [] as RecentQuickOpenFile[]; - } - - const orderedPaths = [ - rootTabs.selectedPath, - ...rootTabs.openPaths, - ].filter((value): value is string => typeof value === 'string' && value.length > 0); - - const seen = new Set(); - - return orderedPaths - .map((filePath) => normalizePath(filePath)) - .filter((filePath) => { - if (!filePath || seen.has(filePath)) { - return false; - } - - seen.add(filePath); - return true; - }) - .slice(0, 10) - .map((filePath) => { - const name = filePath.split('/').filter(Boolean).pop() || filePath; - return { - path: filePath, - name, - relativePath: getRelativePath(currentRoot, filePath), - } satisfies RecentQuickOpenFile; - }); - }, [currentRoot, rootTabs]); - - const trimmedQuery = debouncedQuery.trim(); - - React.useEffect(() => { - if (!isQuickOpenOpen) { - setQuery(''); - setSearchResults([]); - setIsSearching(false); - } - }, [isQuickOpenOpen]); - - React.useEffect(() => { - if (!currentRoot || trimmedQuery.length === 0) { - setSearchResults([]); - setIsSearching(false); - return; - } - - let cancelled = false; - setIsSearching(true); - - void searchFiles(currentRoot, trimmedQuery, 150, { - includeHidden: showHidden, - respectGitignore: !showGitignored, - type: 'file', - }) - .then((results) => { - if (cancelled) { - return; - } - - setSearchResults(results.map((file) => ({ - path: normalizePath(file.path), - name: file.name, - relativePath: file.relativePath, - }))); - }) - .catch(() => { - if (cancelled) { - return; - } - - setSearchResults([]); - }) - .finally(() => { - if (!cancelled) { - setIsSearching(false); - } - }); - - return () => { - cancelled = true; - }; - }, [currentRoot, searchFiles, showGitignored, showHidden, trimmedQuery]); - - const handleSelectFile = React.useCallback(async (filePath: string) => { - if (!currentRoot) { - return; - } - - const openValidation = await validateContextFileOpen(files, filePath); - if (!openValidation.ok) { - toast.error(getContextFileOpenFailureMessage(openValidation.reason)); - return; - } - - openContextFile(currentRoot, filePath); - setQuickOpenOpen(false); - }, [currentRoot, files, openContextFile, setQuickOpenOpen]); - - const hasTypedQuery = query.trim().length > 0; - const visibleFiles = hasTypedQuery ? searchResults : recentFiles; - const emptyMessage = !currentRoot - ? t('quickOpenDialog.empty.openProjectFirst') - : hasTypedQuery - ? (isSearching ? t('quickOpenDialog.empty.searchingFiles') : t('quickOpenDialog.empty.noMatchingFiles')) - : t('quickOpenDialog.empty.noMatchingRecentFiles'); - - return ( - - - {t('quickOpenDialog.title')} - {t('quickOpenDialog.description')} - - - - - - {emptyMessage} - - {currentRoot && visibleFiles.length > 0 && ( - - {visibleFiles.map((file) => ( - { - void handleSelectFile(file.path); - }} - > - -
    - {file.name} - {file.relativePath} -
    -
    - ))} -
    - )} -
    -
    -
    -
    - ); -}; diff --git a/packages/ui/src/hooks/useKeyboardShortcuts.ts b/packages/ui/src/hooks/useKeyboardShortcuts.ts index 6d070539..bb633088 100644 --- a/packages/ui/src/hooks/useKeyboardShortcuts.ts +++ b/packages/ui/src/hooks/useKeyboardShortcuts.ts @@ -18,7 +18,6 @@ export const useKeyboardShortcuts = () => { const currentSessionId = useSessionUIStore((s) => s.currentSessionId); const abortCurrentOperation = sessionActions.abortCurrentOperation;; const toggleCommandPalette = useUIStore((s) => s.toggleCommandPalette); - const setQuickOpenOpen = useUIStore((s) => s.setQuickOpenOpen); const toggleHelpDialog = useUIStore((s) => s.toggleHelpDialog); const toggleSidebar = useUIStore((s) => s.toggleSidebar); const toggleRightSidebar = useUIStore((s) => s.toggleRightSidebar); @@ -62,12 +61,6 @@ export const useKeyboardShortcuts = () => { return; } - if (eventMatchesShortcut(e, combo('open_quick_open'))) { - e.preventDefault(); - setQuickOpenOpen(true); - return; - } - if (eventMatchesShortcut(e, combo('open_status'))) { e.preventDefault(); void showOpenCodeStatus(); @@ -425,7 +418,6 @@ export const useKeyboardShortcuts = () => { openNewSessionDraft, abortCurrentOperation, toggleCommandPalette, - setQuickOpenOpen, toggleHelpDialog, toggleSidebar, toggleRightSidebar, diff --git a/packages/ui/src/hooks/useMenuActions.ts b/packages/ui/src/hooks/useMenuActions.ts index e1f75a51..6db3ac60 100644 --- a/packages/ui/src/hooks/useMenuActions.ts +++ b/packages/ui/src/hooks/useMenuActions.ts @@ -90,7 +90,7 @@ export const useMenuActions = ( ) => { const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft); const toggleCommandPalette = useUIStore((s) => s.toggleCommandPalette); - const setQuickOpenOpen = useUIStore((s) => s.setQuickOpenOpen); + const setCommandPaletteOpen = useUIStore((s) => s.setCommandPaletteOpen); const toggleHelpDialog = useUIStore((s) => s.toggleHelpDialog); const toggleSidebar = useUIStore((s) => s.toggleSidebar); const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen); @@ -146,7 +146,7 @@ export const useMenuActions = ( break; case 'quick-open': - setQuickOpenOpen(true); + setCommandPaletteOpen(true); break; case 'new-session': @@ -237,7 +237,7 @@ export const useMenuActions = ( setAboutDialogOpen, setActiveMainTab, setSessionSwitcherOpen, - setQuickOpenOpen, + setCommandPaletteOpen, setSettingsDialogOpen, setThemeMode, toggleCommandPalette, diff --git a/packages/ui/src/lib/i18n/messages/en.settings.ts b/packages/ui/src/lib/i18n/messages/en.settings.ts index 50a1ed8e..65c16b2b 100644 --- a/packages/ui/src/lib/i18n/messages/en.settings.ts +++ b/packages/ui/src/lib/i18n/messages/en.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.field.pressKeys': 'Press keys...', 'settings.openchamber.keyboardShortcuts.error.captureFirst': 'Capture a shortcut first.', 'settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut': 'This shortcut can conflict with browser defaults. It is still saved.', - 'settings.openchamber.keyboardShortcuts.action.open_quick_open.label': 'Open quick open', 'settings.openchamber.keyboardShortcuts.action.open_go_to_line.label': 'Go to line (files editor)', 'settings.openchamber.keyboardShortcuts.action.open_command_palette.label': 'Open command palette', 'settings.openchamber.keyboardShortcuts.action.focus_input.label': 'Focus input', diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index ad40bf60..f754245e 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -1103,7 +1103,6 @@ export const dict = { 'helpDialog.section.panels': 'Panels', 'helpDialog.section.interface': 'Interface', 'helpDialog.item.openCommandPalette': 'Open Command Palette', - 'helpDialog.item.quickOpenFile': 'Quick open file', 'helpDialog.item.showKeyboardShortcuts': 'Show Keyboard Shortcuts (this dialog)', 'helpDialog.item.toggleSessionSidebar': 'Toggle Session Sidebar', 'helpDialog.item.cycleAgent': 'Cycle Agent (chat input)', @@ -1586,68 +1585,20 @@ export const dict = { 'chat.messageBody.shellCommand.showOutput': 'Show output', 'chat.messageBody.shellCommand.copied': 'Copied', 'chat.messageBody.shellCommand.copyOutput': 'Copy output', - 'commandPalette.input.placeholder': 'Type a command or search...', + 'commandPalette.title': 'Command Palette', + 'commandPalette.description': 'Search files, sessions, and commands.', + 'commandPalette.input.placeholder': 'Search files, sessions, commands...', 'commandPalette.empty.noResults': 'No results found.', - 'commandPalette.section.sessions': 'Sessions', - 'commandPalette.section.view': 'View', - 'commandPalette.section.theme': 'Theme', - 'commandPalette.section.settings': 'Settings', - 'commandPalette.section.recentSessions': 'Recent Sessions', - 'commandPalette.item.quickOpen': 'Quick Open', + 'commandPalette.empty.searchingFiles': 'Searching files...', 'commandPalette.item.newSession': 'New Session', 'commandPalette.item.newWorktreeDraft': 'New Worktree Draft', 'commandPalette.item.showSessionSwitcher': 'Show Session Switcher', 'commandPalette.item.toggleSidebar': 'Toggle Sidebar', 'commandPalette.item.toggleRightSidebar': 'Toggle Right Sidebar', - 'commandPalette.item.showGitRightSidebar': 'Show Git in Right Sidebar', - 'commandPalette.item.showFilesRightSidebar': 'Show Files in Right Sidebar', 'commandPalette.item.showContextUsage': 'Show Context Usage', - 'commandPalette.item.showPlan': 'Show Plan', 'commandPalette.item.toggleTerminal': 'Toggle Terminal', - 'commandPalette.item.collapseTerminal': 'Collapse Terminal', - 'commandPalette.item.expandTerminal': 'Expand Terminal', - 'commandPalette.item.keyboardShortcuts': 'Keyboard Shortcuts', - 'commandPalette.item.themeLight': 'Light', - 'commandPalette.item.themeDark': 'Dark', - 'commandPalette.item.themeSystem': 'System', 'commandPalette.item.openSettings': 'Open Settings...', 'commandPalette.session.untitled': 'Untitled Session', - 'commandPalette.settingsGroup.appearance': 'Appearance', - 'commandPalette.settingsGroup.projects': 'Projects', - 'commandPalette.settingsGroup.general': 'General', - 'commandPalette.settingsGroup.opencode': 'OpenChamber', - 'commandPalette.settingsGroup.git': 'Git', - 'commandPalette.settingsGroup.skills': 'Skills', - 'commandPalette.settingsGroup.usage': 'Usage', - 'commandPalette.settingsGroup.advanced': 'Advanced', - 'commandPalette.settingsPage.home': 'Home', - 'commandPalette.settingsPage.projects': 'Projects', - 'commandPalette.settingsPage.remoteInstances': 'Remote Instances', - 'commandPalette.settingsPage.providers': 'Providers', - 'commandPalette.settingsPage.usage': 'Usage', - 'commandPalette.settingsPage.agents': 'Agents', - 'commandPalette.settingsPage.commands': 'Commands', - 'commandPalette.settingsPage.mcp': 'MCP', - 'commandPalette.settingsPage.skillsInstalled': 'Installed Skills', - 'commandPalette.settingsPage.skillsCatalog': 'Skills Catalog', - 'commandPalette.settingsPage.git': 'Git', - 'commandPalette.settingsPage.appearance': 'Appearance', - 'commandPalette.settingsPage.chat': 'Chat', - 'commandPalette.settingsPage.shortcuts': 'Keyboard Shortcuts', - 'commandPalette.settingsPage.sessions': 'Sessions', - 'commandPalette.settingsPage.magicPrompts': 'Magic Prompts', - 'commandPalette.settingsPage.notifications': 'Notifications', - 'commandPalette.settingsPage.voice': 'Voice', - 'commandPalette.settingsPage.tunnel': 'Remote Tunnel', - 'quickOpenDialog.title': 'Quick Open', - 'quickOpenDialog.description': 'Quickly open a file in the current project.', - 'quickOpenDialog.input.placeholder': 'Search files...', - 'quickOpenDialog.empty.openProjectFirst': 'Open a project first', - 'quickOpenDialog.empty.searchingFiles': 'Searching files...', - 'quickOpenDialog.empty.noMatchingFiles': 'No matching files found', - 'quickOpenDialog.empty.noMatchingRecentFiles': 'No recent files found', - 'quickOpenDialog.group.files': 'Files', - 'quickOpenDialog.group.recentFiles': 'Recent Files', 'openCodeStatusDialog.title': 'OpenCode Status', 'openCodeStatusDialog.description': 'Inspect current OpenCode status and diagnostics.', 'openCodeStatusDialog.actions.copy': 'Copy', diff --git a/packages/ui/src/lib/i18n/messages/es.settings.ts b/packages/ui/src/lib/i18n/messages/es.settings.ts index 84d51980..b959325b 100644 --- a/packages/ui/src/lib/i18n/messages/es.settings.ts +++ b/packages/ui/src/lib/i18n/messages/es.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.field.pressKeys": "Pulsa las teclas...", "settings.openchamber.keyboardShortcuts.error.captureFirst": "Captura un atajo primero.", "settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut": "Este atajo puede entrar en conflicto con los predeterminados del navegador. Todavía se guarda.", - "settings.openchamber.keyboardShortcuts.action.open_quick_open.label": "Abrir búsqueda rápida", "settings.openchamber.keyboardShortcuts.action.open_go_to_line.label": "Ir a línea (editor de archivos)", "settings.openchamber.keyboardShortcuts.action.open_command_palette.label": "Abrir paleta de comandos", "settings.openchamber.keyboardShortcuts.action.focus_input.label": "Enfocar entrada", diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index d159b9d9..3d48db64 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -1069,7 +1069,6 @@ export const dict: Record = { "helpDialog.section.panels": "Paneles", "helpDialog.section.interface": "Interfaz", "helpDialog.item.openCommandPalette": "Abrir paleta de comandos", - "helpDialog.item.quickOpenFile": "Abrir archivo rápido", "helpDialog.item.showKeyboardShortcuts": "Mostrar atajos de teclado (este diálogo)", "helpDialog.item.toggleSessionSidebar": "Mostrar u ocultar barra lateral de sesión", "helpDialog.item.cycleAgent": "Cambiar agente (entrada de chat)", @@ -1552,68 +1551,20 @@ export const dict: Record = { "chat.messageBody.shellCommand.showOutput": "Mostrar salida", "chat.messageBody.shellCommand.copied": "Copiado", "chat.messageBody.shellCommand.copyOutput": "Copiar salida", - "commandPalette.input.placeholder": "Escribe un comando o busca...", + "commandPalette.title": "Paleta de comandos", + "commandPalette.description": "Buscar archivos, sesiones y comandos.", + "commandPalette.input.placeholder": "Buscar archivos, sesiones, comandos...", "commandPalette.empty.noResults": "No se encontraron resultados.", - "commandPalette.section.sessions": "Sesiones", - "commandPalette.section.view": "Ver", - "commandPalette.section.theme": "Tema", - "commandPalette.section.settings": "Configuración", - "commandPalette.section.recentSessions": "Sesiones recientes", - "commandPalette.item.quickOpen": "Abrir rápido", + "commandPalette.empty.searchingFiles": "Buscando archivos...", "commandPalette.item.newSession": "Nueva sesión", "commandPalette.item.newWorktreeDraft": "Nuevo borrador de worktree", "commandPalette.item.showSessionSwitcher": "Mostrar cambiador de sesiones", "commandPalette.item.toggleSidebar": "Mostrar u ocultar barra lateral", "commandPalette.item.toggleRightSidebar": "Mostrar u ocultar barra lateral derecha", - "commandPalette.item.showGitRightSidebar": "Mostrar Git en barra lateral derecha", - "commandPalette.item.showFilesRightSidebar": "Mostrar archivos en barra lateral derecha", "commandPalette.item.showContextUsage": "Mostrar uso del contexto", - "commandPalette.item.showPlan": "Mostrar plan", "commandPalette.item.toggleTerminal": "Mostrar u ocultar terminal", - "commandPalette.item.collapseTerminal": "Colapsar terminal", - "commandPalette.item.expandTerminal": "Expandir terminal", - "commandPalette.item.keyboardShortcuts": "Atajos de teclado", - "commandPalette.item.themeLight": "Claro", - "commandPalette.item.themeDark": "Oscuro", - "commandPalette.item.themeSystem": "Sistema", "commandPalette.item.openSettings": "Abrir configuración...", "commandPalette.session.untitled": "Sesión sin título", - "commandPalette.settingsGroup.appearance": "Apariencia", - "commandPalette.settingsGroup.projects": "Proyectos", - "commandPalette.settingsGroup.general": "General", - "commandPalette.settingsGroup.opencode": "OpenChamber", - "commandPalette.settingsGroup.git": "Git", - "commandPalette.settingsGroup.skills": "Habilidades", - "commandPalette.settingsGroup.usage": "Uso", - "commandPalette.settingsGroup.advanced": "Avanzado", - "commandPalette.settingsPage.home": "Inicio", - "commandPalette.settingsPage.projects": "Proyectos", - "commandPalette.settingsPage.remoteInstances": "Instancias remotas", - "commandPalette.settingsPage.providers": "Proveedores", - "commandPalette.settingsPage.usage": "Uso", - "commandPalette.settingsPage.agents": "Agentes", - "commandPalette.settingsPage.commands": "Comandos", - "commandPalette.settingsPage.mcp": "MCP", - "commandPalette.settingsPage.skillsInstalled": "Habilidades instaladas", - "commandPalette.settingsPage.skillsCatalog": "Catálogo de habilidades", - "commandPalette.settingsPage.git": "Git", - "commandPalette.settingsPage.appearance": "Apariencia", - "commandPalette.settingsPage.chat": "Chat", - "commandPalette.settingsPage.shortcuts": "Atajos de teclado", - "commandPalette.settingsPage.sessions": "Sesiones", - "commandPalette.settingsPage.magicPrompts": "Prompts mágicos", - "commandPalette.settingsPage.notifications": "Notificaciones", - "commandPalette.settingsPage.voice": "Voz", - "commandPalette.settingsPage.tunnel": "Túnel remoto", - "quickOpenDialog.title": "Apertura rápida", - "quickOpenDialog.description": "Abre rápidamente un archivo del proyecto actual.", - "quickOpenDialog.input.placeholder": "Buscar archivos...", - "quickOpenDialog.empty.openProjectFirst": "Abre un proyecto primero", - "quickOpenDialog.empty.searchingFiles": "Buscando archivos...", - "quickOpenDialog.empty.noMatchingFiles": "No se encontraron archivos coincidentes", - "quickOpenDialog.empty.noMatchingRecentFiles": "No se encontraron archivos recientes", - "quickOpenDialog.group.files": "Archivos", - "quickOpenDialog.group.recentFiles": "Archivos recientes", "openCodeStatusDialog.title": "Estado de OpenCode", "openCodeStatusDialog.description": "Inspecciona el estado actual de OpenCode y diagnósticos.", "openCodeStatusDialog.actions.copy": "Copiar", diff --git a/packages/ui/src/lib/i18n/messages/ko.settings.ts b/packages/ui/src/lib/i18n/messages/ko.settings.ts index 742899f6..72a40b92 100644 --- a/packages/ui/src/lib/i18n/messages/ko.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ko.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.field.pressKeys': '키를 누르세요...', 'settings.openchamber.keyboardShortcuts.error.captureFirst': '먼저 단축키를 입력하세요.', 'settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut': '이 단축키는 브라우저 기본값과 충돌할 수 있습니다. 그래도 저장됩니다.', - 'settings.openchamber.keyboardShortcuts.action.open_quick_open.label': '빠른 열기 열기', 'settings.openchamber.keyboardShortcuts.action.open_go_to_line.label': '줄로 이동(파일 편집기)', 'settings.openchamber.keyboardShortcuts.action.open_command_palette.label': '명령 팔레트 열기', 'settings.openchamber.keyboardShortcuts.action.focus_input.label': '입력에 포커스', diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index a0e02de2..44d4d53d 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -1105,7 +1105,6 @@ export const dict: Record = { 'helpDialog.section.panels': '패널', 'helpDialog.section.interface': '인터페이스', 'helpDialog.item.openCommandPalette': '명령 팔레트 열기', - 'helpDialog.item.quickOpenFile': '빠른 파일 열기', 'helpDialog.item.showKeyboardShortcuts': '키보드 단축키 보기(이 대화상자)', 'helpDialog.item.toggleSessionSidebar': '토글 세션 사이드바', 'helpDialog.item.cycleAgent': '에이전트 순환(채팅 입력)', @@ -1586,68 +1585,20 @@ export const dict: Record = { 'chat.messageBody.shellCommand.showOutput': '표시 출력', 'chat.messageBody.shellCommand.copied': '복사됨', 'chat.messageBody.shellCommand.copyOutput': '출력 복사', - 'commandPalette.input.placeholder': '명령 입력 또는 검색…', + 'commandPalette.title': '명령 팔레트', + 'commandPalette.description': '파일, 세션, 명령을 검색합니다.', + 'commandPalette.input.placeholder': '파일, 세션, 명령 검색…', 'commandPalette.empty.noResults': '결과 없음', - 'commandPalette.section.sessions': '세션', - 'commandPalette.section.view': '보기', - 'commandPalette.section.theme': '테마', - 'commandPalette.section.settings': '설정', - 'commandPalette.section.recentSessions': '최근 세션', - 'commandPalette.item.quickOpen': '빠른 열기', + 'commandPalette.empty.searchingFiles': '파일 검색 중…', 'commandPalette.item.newSession': '새 세션', 'commandPalette.item.newWorktreeDraft': '새 워크트리 드래프트', 'commandPalette.item.showSessionSwitcher': '세션 전환기 표시', 'commandPalette.item.toggleSidebar': '토글 사이드바', 'commandPalette.item.toggleRightSidebar': '오른쪽 사이드바 전환', - 'commandPalette.item.showGitRightSidebar': '오른쪽 사이드바에 Git 표시', - 'commandPalette.item.showFilesRightSidebar': '오른쪽 사이드바에 파일 표시', 'commandPalette.item.showContextUsage': '컨텍스트 사용량 표시', - 'commandPalette.item.showPlan': '플랜 표시', 'commandPalette.item.toggleTerminal': '토글 터미널', - 'commandPalette.item.collapseTerminal': '접기 터미널', - 'commandPalette.item.expandTerminal': '펼치기 터미널', - 'commandPalette.item.keyboardShortcuts': '키보드 단축키', - 'commandPalette.item.themeLight': '라이트', - 'commandPalette.item.themeDark': '다크', - 'commandPalette.item.themeSystem': '시스템', 'commandPalette.item.openSettings': '설정... 열기', 'commandPalette.session.untitled': '제목 없는 세션', - 'commandPalette.settingsGroup.appearance': '외관', - 'commandPalette.settingsGroup.projects': '프로젝트', - 'commandPalette.settingsGroup.general': '일반', - 'commandPalette.settingsGroup.opencode': 'OpenChamber', - 'commandPalette.settingsGroup.git': 'Git', - 'commandPalette.settingsGroup.skills': '스킬', - 'commandPalette.settingsGroup.usage': '사용량', - 'commandPalette.settingsGroup.advanced': '고급', - 'commandPalette.settingsPage.home': '홈', - 'commandPalette.settingsPage.projects': '프로젝트', - 'commandPalette.settingsPage.remoteInstances': '원격 인스턴스', - 'commandPalette.settingsPage.providers': '프로바이더', - 'commandPalette.settingsPage.usage': '사용량', - 'commandPalette.settingsPage.agents': '에이전트', - 'commandPalette.settingsPage.commands': '명령', - 'commandPalette.settingsPage.mcp': 'MCP', - 'commandPalette.settingsPage.skillsInstalled': '설치된 스킬', - 'commandPalette.settingsPage.skillsCatalog': '스킬 카탈로그', - 'commandPalette.settingsPage.git': 'Git', - 'commandPalette.settingsPage.appearance': '외관', - 'commandPalette.settingsPage.chat': '채팅', - 'commandPalette.settingsPage.shortcuts': '키보드 단축키', - 'commandPalette.settingsPage.sessions': '세션', - 'commandPalette.settingsPage.magicPrompts': 'Magic 프롬프트', - 'commandPalette.settingsPage.notifications': '알림', - 'commandPalette.settingsPage.voice': '음성', - 'commandPalette.settingsPage.tunnel': '리모트 터널', - 'quickOpenDialog.title': '빠른 열기', - 'quickOpenDialog.description': '현재 프로젝트에서 파일을 빠르게 여세요.', - 'quickOpenDialog.input.placeholder': '파일 검색…', - 'quickOpenDialog.empty.openProjectFirst': '먼저 프로젝트를 여세요', - 'quickOpenDialog.empty.searchingFiles': '파일 검색 중…', - 'quickOpenDialog.empty.noMatchingFiles': '일치하는 파일이 없습니다', - 'quickOpenDialog.empty.noMatchingRecentFiles': '최근 파일이 없습니다', - 'quickOpenDialog.group.files': '파일', - 'quickOpenDialog.group.recentFiles': '최근 파일', 'openCodeStatusDialog.title': 'OpenCode 상태', 'openCodeStatusDialog.description': '현재 OpenCode 상태와 진단 정보를 확인합니다.', 'openCodeStatusDialog.actions.copy': '복사', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts index dcd6f16d..cc79b5c5 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.field.pressKeys": "Pressione as teclas...", "settings.openchamber.keyboardShortcuts.error.captureFirst": "Captura um atalho primeiro.", "settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut": "Este atalho pode entrar em conflito com os padrões do navegador. Ainda assim, ele será salvo.", - "settings.openchamber.keyboardShortcuts.action.open_quick_open.label": "Abrir busca rápida", "settings.openchamber.keyboardShortcuts.action.open_go_to_line.label": "Ir para linha (editor de arquivos)", "settings.openchamber.keyboardShortcuts.action.open_command_palette.label": "Abrir paleta de comandos", "settings.openchamber.keyboardShortcuts.action.focus_input.label": "Focar entrada", diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index 70b8b6d2..3059aa70 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -1069,7 +1069,6 @@ export const dict: Record = { "helpDialog.section.panels": "Painéis", "helpDialog.section.interface": "Interface", "helpDialog.item.openCommandPalette": "Abrir paleta de comandos", - "helpDialog.item.quickOpenFile": "Abrir arquivo rápido", "helpDialog.item.showKeyboardShortcuts": "Mostrar atalhos de teclado (este diálogo)", "helpDialog.item.toggleSessionSidebar": "Mostrar ou ocultar barra lateral de sessão", "helpDialog.item.cycleAgent": "Alternar agente (entrada do chat)", @@ -1552,68 +1551,20 @@ export const dict: Record = { "chat.messageBody.shellCommand.showOutput": "Mostrar saída", "chat.messageBody.shellCommand.copied": "Copiado", "chat.messageBody.shellCommand.copyOutput": "Copiar saída", - "commandPalette.input.placeholder": "Digite um comando ou pesquise...", + "commandPalette.title": "Paleta de comandos", + "commandPalette.description": "Buscar arquivos, sessões e comandos.", + "commandPalette.input.placeholder": "Buscar arquivos, sessões, comandos...", "commandPalette.empty.noResults": "Nenhum resultado encontrado.", - "commandPalette.section.sessions": "Sessões", - "commandPalette.section.view": "Ver", - "commandPalette.section.theme": "Tema", - "commandPalette.section.settings": "Configurações", - "commandPalette.section.recentSessions": "Sessões recentes", - "commandPalette.item.quickOpen": "Abrir rápido", + "commandPalette.empty.searchingFiles": "Buscando arquivos...", "commandPalette.item.newSession": "Nova sessão", "commandPalette.item.newWorktreeDraft": "Novo rascunho de worktree", "commandPalette.item.showSessionSwitcher": "Mostrar seletor de sessões", "commandPalette.item.toggleSidebar": "Mostrar ou ocultar barra lateral", "commandPalette.item.toggleRightSidebar": "Mostrar ou ocultar barra lateral direita", - "commandPalette.item.showGitRightSidebar": "Mostrar Git em barra lateral direita", - "commandPalette.item.showFilesRightSidebar": "Mostrar arquivos em barra lateral direita", "commandPalette.item.showContextUsage": "Mostrar uso do contexto", - "commandPalette.item.showPlan": "Mostrar plano", "commandPalette.item.toggleTerminal": "Mostrar ou ocultar terminal", - "commandPalette.item.collapseTerminal": "Recolher terminal", - "commandPalette.item.expandTerminal": "Expandir terminal", - "commandPalette.item.keyboardShortcuts": "Atalhos de teclado", - "commandPalette.item.themeLight": "Claro", - "commandPalette.item.themeDark": "Escuro", - "commandPalette.item.themeSystem": "Sistema", "commandPalette.item.openSettings": "Abrir configurações...", "commandPalette.session.untitled": "Sessão sem título", - "commandPalette.settingsGroup.appearance": "Aparência", - "commandPalette.settingsGroup.projects": "Projetos", - "commandPalette.settingsGroup.general": "Geral", - "commandPalette.settingsGroup.opencode": "OpenChamber", - "commandPalette.settingsGroup.git": "Git", - "commandPalette.settingsGroup.skills": "Habilidades", - "commandPalette.settingsGroup.usage": "Uso", - "commandPalette.settingsGroup.advanced": "Avançado", - "commandPalette.settingsPage.home": "Início", - "commandPalette.settingsPage.projects": "Projetos", - "commandPalette.settingsPage.remoteInstances": "Instâncias remotas", - "commandPalette.settingsPage.providers": "Provedores", - "commandPalette.settingsPage.usage": "Uso", - "commandPalette.settingsPage.agents": "Agentes", - "commandPalette.settingsPage.commands": "Comandos", - "commandPalette.settingsPage.mcp": "MCP", - "commandPalette.settingsPage.skillsInstalled": "Habilidades instaladas", - "commandPalette.settingsPage.skillsCatalog": "Catálogo de habilidades", - "commandPalette.settingsPage.git": "Git", - "commandPalette.settingsPage.appearance": "Aparência", - "commandPalette.settingsPage.chat": "Chat", - "commandPalette.settingsPage.shortcuts": "Atalhos de teclado", - "commandPalette.settingsPage.sessions": "Sessões", - "commandPalette.settingsPage.magicPrompts": "Prompts mágicos", - "commandPalette.settingsPage.notifications": "Notificações", - "commandPalette.settingsPage.voice": "Voz", - "commandPalette.settingsPage.tunnel": "Túnel remoto", - "quickOpenDialog.title": "Abertura rápida", - "quickOpenDialog.description": "Abra rapidamente um arquivo do projeto atual.", - "quickOpenDialog.input.placeholder": "Pesquisar arquivos...", - "quickOpenDialog.empty.openProjectFirst": "Abra um projeto primeiro", - "quickOpenDialog.empty.searchingFiles": "Buscando arquivos...", - "quickOpenDialog.empty.noMatchingFiles": "Nenhum arquivo correspondente encontrado", - "quickOpenDialog.empty.noMatchingRecentFiles": "Nenhum arquivo recente correspondente encontrado", - "quickOpenDialog.group.files": "Arquivos", - "quickOpenDialog.group.recentFiles": "Arquivos recentes", "openCodeStatusDialog.title": "Status do OpenCode", "openCodeStatusDialog.description": "Inspecione o status atual do OpenCode e os diagnósticos.", "openCodeStatusDialog.actions.copy": "Copiar", diff --git a/packages/ui/src/lib/i18n/messages/uk.settings.ts b/packages/ui/src/lib/i18n/messages/uk.settings.ts index bff60512..9f39d7dc 100644 --- a/packages/ui/src/lib/i18n/messages/uk.settings.ts +++ b/packages/ui/src/lib/i18n/messages/uk.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.field.pressKeys": "Натисніть клавіші...", "settings.openchamber.keyboardShortcuts.error.captureFirst": "Спочатку запишіть комбінацію клавіш.", "settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut": "Ця комбінація клавіш може конфліктувати зі стандартними скороченнями браузера. Її все одно збережено.", - "settings.openchamber.keyboardShortcuts.action.open_quick_open.label": "Швидке відкриття", "settings.openchamber.keyboardShortcuts.action.open_go_to_line.label": "Перейти до рядка (редактор файлів)", "settings.openchamber.keyboardShortcuts.action.open_command_palette.label": "Відкрити палітру команд", "settings.openchamber.keyboardShortcuts.action.focus_input.label": "Фокус на полі вводу", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index c4e9bda5..2176a499 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -1069,7 +1069,6 @@ export const dict: Record = { "helpDialog.section.panels": "Панелі", "helpDialog.section.interface": "Інтерфейс", "helpDialog.item.openCommandPalette": "Відкрити палітру команд", - "helpDialog.item.quickOpenFile": "Швидке відкриття файлу", "helpDialog.item.showKeyboardShortcuts": "Показати комбінації клавіш (це діалогове вікно)", "helpDialog.item.toggleSessionSidebar": "Перемкнути бічну панель сесій", "helpDialog.item.cycleAgent": "Перемкнути агента (введення в чат)", @@ -1552,68 +1551,20 @@ export const dict: Record = { "chat.messageBody.shellCommand.showOutput": "Показати результат", "chat.messageBody.shellCommand.copied": "Скопійовано", "chat.messageBody.shellCommand.copyOutput": "Копіювати вивід", - "commandPalette.input.placeholder": "Введіть команду або знайдіть...", + "commandPalette.title": "Палітра команд", + "commandPalette.description": "Пошук файлів, сесій і команд.", + "commandPalette.input.placeholder": "Пошук файлів, сесій, команд...", "commandPalette.empty.noResults": "Результатів не знайдено.", - "commandPalette.section.sessions": "Сесії", - "commandPalette.section.view": "Переглянути", - "commandPalette.section.theme": "Тема", - "commandPalette.section.settings": "Налаштування", - "commandPalette.section.recentSessions": "Останні сесії", - "commandPalette.item.quickOpen": "Швидке відкриття", + "commandPalette.empty.searchingFiles": "Пошук файлів...", "commandPalette.item.newSession": "Нова сесія", "commandPalette.item.newWorktreeDraft": "Чернетка нового worktree", "commandPalette.item.showSessionSwitcher": "Показати перемикач сесій", "commandPalette.item.toggleSidebar": "Перемкнути бічну панель", "commandPalette.item.toggleRightSidebar": "Перемкнути праву бічну панель", - "commandPalette.item.showGitRightSidebar": "Показати Git на правій бічній панелі", - "commandPalette.item.showFilesRightSidebar": "Показати файли на правій бічній панелі", "commandPalette.item.showContextUsage": "Показати використання контексту", - "commandPalette.item.showPlan": "Показати план", "commandPalette.item.toggleTerminal": "Перемкнути термінал", - "commandPalette.item.collapseTerminal": "Згорнути термінал", - "commandPalette.item.expandTerminal": "Розгорнути термінал", - "commandPalette.item.keyboardShortcuts": "Комбінації клавіш", - "commandPalette.item.themeLight": "Світла тема", - "commandPalette.item.themeDark": "Темна тема", - "commandPalette.item.themeSystem": "Системна тема", "commandPalette.item.openSettings": "Відкрити налаштування...", "commandPalette.session.untitled": "Сесія без назви", - "commandPalette.settingsGroup.appearance": "Зовнішній вигляд", - "commandPalette.settingsGroup.projects": "Проєкти", - "commandPalette.settingsGroup.general": "Загальний", - "commandPalette.settingsGroup.opencode": "OpenChamber", - "commandPalette.settingsGroup.git": "Git", - "commandPalette.settingsGroup.skills": "Навички", - "commandPalette.settingsGroup.usage": "Використання", - "commandPalette.settingsGroup.advanced": "Просунутий", - "commandPalette.settingsPage.home": "додому", - "commandPalette.settingsPage.projects": "Проєкти", - "commandPalette.settingsPage.remoteInstances": "Віддалені інстанси", - "commandPalette.settingsPage.providers": "Провайдери", - "commandPalette.settingsPage.usage": "Використання", - "commandPalette.settingsPage.agents": "Агенти", - "commandPalette.settingsPage.commands": "Команди", - "commandPalette.settingsPage.mcp": "MCP", - "commandPalette.settingsPage.skillsInstalled": "Встановлені навички", - "commandPalette.settingsPage.skillsCatalog": "Каталог навичок", - "commandPalette.settingsPage.git": "Git", - "commandPalette.settingsPage.appearance": "Зовнішній вигляд", - "commandPalette.settingsPage.chat": "Чат", - "commandPalette.settingsPage.shortcuts": "Комбінації клавіш", - "commandPalette.settingsPage.sessions": "Сесії", - "commandPalette.settingsPage.magicPrompts": "Магічні промпти", - "commandPalette.settingsPage.notifications": "Сповіщення", - "commandPalette.settingsPage.voice": "Голос", - "commandPalette.settingsPage.tunnel": "Віддалений тунель", - "quickOpenDialog.title": "Швидке відкриття", - "quickOpenDialog.description": "Швидко відкрити файл у поточному проєкті.", - "quickOpenDialog.input.placeholder": "Пошук файлів...", - "quickOpenDialog.empty.openProjectFirst": "Спочатку відкрийте проєкт", - "quickOpenDialog.empty.searchingFiles": "Пошук файлів...", - "quickOpenDialog.empty.noMatchingFiles": "Відповідних файлів не знайдено", - "quickOpenDialog.empty.noMatchingRecentFiles": "Останні файли не знайдено", - "quickOpenDialog.group.files": "Файли", - "quickOpenDialog.group.recentFiles": "Останні файли", "openCodeStatusDialog.title": "Статус OpenCode", "openCodeStatusDialog.description": "Перегляньте поточний стан OpenCode і діагностику.", "openCodeStatusDialog.actions.copy": "Копіювати", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts index 5e0ec20a..5f9494ba 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts @@ -718,7 +718,6 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.field.pressKeys': '按下按键...', 'settings.openchamber.keyboardShortcuts.error.captureFirst': '请先录入一个快捷键。', 'settings.openchamber.keyboardShortcuts.warning.riskyBrowserShortcut': '该快捷键可能与浏览器默认快捷键冲突,但仍已保存。', - 'settings.openchamber.keyboardShortcuts.action.open_quick_open.label': '打开快速打开', 'settings.openchamber.keyboardShortcuts.action.open_go_to_line.label': '跳转到行(文件编辑器)', 'settings.openchamber.keyboardShortcuts.action.open_command_palette.label': '打开命令面板', 'settings.openchamber.keyboardShortcuts.action.focus_input.label': '聚焦输入框', diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 38c8126d..49a16899 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -1069,7 +1069,6 @@ export const dict: Record = { 'helpDialog.section.panels': '面板', 'helpDialog.section.interface': '界面', 'helpDialog.item.openCommandPalette': '打开命令面板', - 'helpDialog.item.quickOpenFile': '快速打开文件', 'helpDialog.item.showKeyboardShortcuts': '显示键盘快捷键(此对话框)', 'helpDialog.item.toggleSessionSidebar': '切换会话侧边栏', 'helpDialog.item.cycleAgent': '循环切换智能体(聊天输入)', @@ -1552,68 +1551,20 @@ export const dict: Record = { 'chat.messageBody.shellCommand.showOutput': '显示输出', 'chat.messageBody.shellCommand.copied': '已复制', 'chat.messageBody.shellCommand.copyOutput': '复制输出', - 'commandPalette.input.placeholder': '输入命令或搜索...', + 'commandPalette.title': '命令面板', + 'commandPalette.description': '搜索文件、会话和命令。', + 'commandPalette.input.placeholder': '搜索文件、会话、命令...', 'commandPalette.empty.noResults': '未找到结果。', - 'commandPalette.section.sessions': '会话', - 'commandPalette.section.view': '视图', - 'commandPalette.section.theme': '主题', - 'commandPalette.section.settings': '设置', - 'commandPalette.section.recentSessions': '最近会话', - 'commandPalette.item.quickOpen': '快速打开', + 'commandPalette.empty.searchingFiles': '正在搜索文件...', 'commandPalette.item.newSession': '新建会话', 'commandPalette.item.newWorktreeDraft': '新建工作树草稿', 'commandPalette.item.showSessionSwitcher': '显示会话切换器', 'commandPalette.item.toggleSidebar': '切换侧边栏', 'commandPalette.item.toggleRightSidebar': '切换右侧边栏', - 'commandPalette.item.showGitRightSidebar': '在右侧边栏显示 Git', - 'commandPalette.item.showFilesRightSidebar': '在右侧边栏显示文件', 'commandPalette.item.showContextUsage': '显示上下文用量', - 'commandPalette.item.showPlan': '显示计划', 'commandPalette.item.toggleTerminal': '切换终端', - 'commandPalette.item.collapseTerminal': '收起终端', - 'commandPalette.item.expandTerminal': '展开终端', - 'commandPalette.item.keyboardShortcuts': '键盘快捷键', - 'commandPalette.item.themeLight': '浅色', - 'commandPalette.item.themeDark': '深色', - 'commandPalette.item.themeSystem': '跟随系统', 'commandPalette.item.openSettings': '打开设置...', 'commandPalette.session.untitled': '未命名会话', - 'commandPalette.settingsGroup.appearance': '外观', - 'commandPalette.settingsGroup.projects': '项目', - 'commandPalette.settingsGroup.general': '通用', - 'commandPalette.settingsGroup.opencode': 'OpenChamber', - 'commandPalette.settingsGroup.git': 'Git', - 'commandPalette.settingsGroup.skills': '技能', - 'commandPalette.settingsGroup.usage': '用量', - 'commandPalette.settingsGroup.advanced': '高级', - 'commandPalette.settingsPage.home': '首页', - 'commandPalette.settingsPage.projects': '项目', - 'commandPalette.settingsPage.remoteInstances': '远程实例', - 'commandPalette.settingsPage.providers': '提供商', - 'commandPalette.settingsPage.usage': '用量', - 'commandPalette.settingsPage.agents': 'Agents', - 'commandPalette.settingsPage.commands': '命令', - 'commandPalette.settingsPage.mcp': 'MCP', - 'commandPalette.settingsPage.skillsInstalled': '已安装技能', - 'commandPalette.settingsPage.skillsCatalog': '技能目录', - 'commandPalette.settingsPage.git': 'Git', - 'commandPalette.settingsPage.appearance': '外观', - 'commandPalette.settingsPage.chat': '聊天', - 'commandPalette.settingsPage.shortcuts': '键盘快捷键', - 'commandPalette.settingsPage.sessions': '会话', - 'commandPalette.settingsPage.magicPrompts': '魔法提示词', - 'commandPalette.settingsPage.notifications': '通知', - 'commandPalette.settingsPage.voice': '语音', - 'commandPalette.settingsPage.tunnel': '远程隧道', - 'quickOpenDialog.title': '快速打开', - 'quickOpenDialog.description': '在当前项目中快速打开文件。', - 'quickOpenDialog.input.placeholder': '搜索文件...', - 'quickOpenDialog.empty.openProjectFirst': '请先打开项目', - 'quickOpenDialog.empty.searchingFiles': '正在搜索文件...', - 'quickOpenDialog.empty.noMatchingFiles': '未找到匹配文件', - 'quickOpenDialog.empty.noMatchingRecentFiles': '未找到最近文件', - 'quickOpenDialog.group.files': '文件', - 'quickOpenDialog.group.recentFiles': '最近文件', 'openCodeStatusDialog.title': 'OpenCode 状态', 'openCodeStatusDialog.description': '查看当前 OpenCode 状态与诊断信息。', 'openCodeStatusDialog.actions.copy': '复制', diff --git a/packages/ui/src/lib/search/fuzzySearch.ts b/packages/ui/src/lib/search/fuzzySearch.ts index c047ca64..d9713be8 100644 --- a/packages/ui/src/lib/search/fuzzySearch.ts +++ b/packages/ui/src/lib/search/fuzzySearch.ts @@ -107,6 +107,67 @@ export function filterByFuzzyQuery( return matching; } +/** + * Score-sorted fuzzy ranking. Strict (low threshold), prioritizes substring + * matches (especially prefix matches), and returns the top N. + * + * Use this for command-palette-style result ranking where order matters more + * than recall. + */ +export function scoreByFuzzyQuery( + items: T[], + query: string, + getText: (item: T) => string, + options?: { limit?: number; threshold?: number; noFuzzy?: boolean }, +): { item: T; score: number }[] { + if (!query) return items.map((item) => ({ item, score: 0 })); + const limit = options?.limit ?? items.length; + const threshold = options?.threshold ?? 0.3; + const noFuzzy = options?.noFuzzy ?? false; + const queryLower = query.toLowerCase(); + + const scored: { item: T; score: number }[] = []; + const fuzzyCandidates: { item: T; text: string }[] = []; + + for (const item of items) { + const text = getText(item); + if (!text) continue; + const lower = text.toLowerCase(); + const idx = lower.indexOf(queryLower); + if (idx === 0) { + scored.push({ item, score: -1 }); + continue; + } + if (idx > 0) { + scored.push({ item, score: idx / 1000 }); + continue; + } + if (!noFuzzy) fuzzyCandidates.push({ item, text }); + } + + if (fuzzyCandidates.length > 0) { + const fuse = new Fuse( + fuzzyCandidates.map((c) => c.text), + { threshold, ignoreLocation: true, distance: 100, includeScore: true, minMatchCharLength: 2 }, + ); + for (const result of fuse.search(query)) { + scored.push({ item: fuzzyCandidates[result.refIndex].item, score: result.score ?? 1 }); + } + } + + scored.sort((a, b) => a.score - b.score); + return scored.slice(0, limit); +} + +export function rankByFuzzyQuery( + items: T[], + query: string, + getText: (item: T) => string, + options?: { limit?: number; threshold?: number; noFuzzy?: boolean }, +): T[] { + return scoreByFuzzyQuery(items, query, getText, options).map((x) => x.item); +} + export function partitionByFuzzyQuery( items: T[], query: string, diff --git a/packages/ui/src/lib/shortcuts.ts b/packages/ui/src/lib/shortcuts.ts index 731e698d..2e925ade 100644 --- a/packages/ui/src/lib/shortcuts.ts +++ b/packages/ui/src/lib/shortcuts.ts @@ -104,13 +104,6 @@ export function keyToShortcutToken(key: string): string { } const SHORTCUT_ACTIONS: ReadonlyArray = [ - { - id: 'open_quick_open', - defaultCombo: 'mod+p', - label: 'Open quick open', - description: 'Open the quick open dialog', - customizable: true, - }, { id: 'open_go_to_line', defaultCombo: 'alt+g', @@ -120,7 +113,7 @@ const SHORTCUT_ACTIONS: ReadonlyArray = [ }, { id: 'open_command_palette', - defaultCombo: 'mod+k', + defaultCombo: 'mod+p', label: 'Open command palette', description: 'Open the command palette', customizable: true, diff --git a/packages/ui/src/stores/useUIStore.ts b/packages/ui/src/stores/useUIStore.ts index cda78712..0ddc8d3b 100644 --- a/packages/ui/src/stores/useUIStore.ts +++ b/packages/ui/src/stores/useUIStore.ts @@ -487,7 +487,6 @@ interface UIStore { pendingFileNavigation: PendingFileNavigation | null; pendingFileFocusPath: string | null; isMobile: boolean; - isQuickOpenOpen: boolean; isCommandPaletteOpen: boolean; isHelpDialogOpen: boolean; isAboutDialogOpen: boolean; @@ -613,8 +612,6 @@ interface UIStore { navigateToDiff: (filePath: string) => void; consumePendingDiffFile: () => string | null; setIsMobile: (isMobile: boolean) => void; - setQuickOpenOpen: (open: boolean) => void; - toggleQuickOpen: () => void; toggleCommandPalette: () => void; setCommandPaletteOpen: (open: boolean) => void; toggleHelpDialog: () => void; @@ -743,7 +740,6 @@ export const useUIStore = create()( pendingFileNavigation: null, pendingFileFocusPath: null, isMobile: false, - isQuickOpenOpen: false, isCommandPaletteOpen: false, isHelpDialogOpen: false, isAboutDialogOpen: false, @@ -1286,14 +1282,6 @@ export const useUIStore = create()( set({ isMobile }); }, - setQuickOpenOpen: (open) => { - set({ isQuickOpenOpen: open }); - }, - - toggleQuickOpen: () => { - set((state) => ({ isQuickOpenOpen: !state.isQuickOpenOpen })); - }, - toggleCommandPalette: () => { set((state) => ({ isCommandPaletteOpen: !state.isCommandPaletteOpen })); },