feat(ui): add command palette and commit selection sync
Introduce a global command palette for quick access to common repository actions, branches, files, and commits. Wire it into the main shell so it can open settings, help, and other dialogs while keeping keyboard access consistent. Also add commit selection state to the history view so the active commit is highlighted and brought into view when chosen from either the palette or the history panel.
This commit is contained in:
+70
-2
@@ -17,6 +17,7 @@
|
||||
import BranchDeleteConfirmDialog from "./lib/components/BranchDeleteConfirmDialog.svelte";
|
||||
import BranchPanel from "./lib/components/BranchPanel.svelte";
|
||||
import CloneRepositoryDialog from "./lib/components/CloneRepositoryDialog.svelte";
|
||||
import CommandPalette from "./lib/components/CommandPalette.svelte";
|
||||
import CommitPanel from "./lib/components/CommitPanel.svelte";
|
||||
import CompareSelectDialog from "./lib/components/CompareSelectDialog.svelte";
|
||||
import CredentialDialog from "./lib/components/CredentialDialog.svelte";
|
||||
@@ -287,6 +288,7 @@
|
||||
let selectedExplorerKind: ExplorerNodeKind = "file";
|
||||
let expandedExplorerPaths = new Set<string>();
|
||||
let expandedCommitHashes = new Set<string>();
|
||||
let selectedCommitHash = "";
|
||||
let fileHistory: GitCommit[] = [];
|
||||
let fileHistoryLoading = false;
|
||||
let fileHistoryError = "";
|
||||
@@ -312,6 +314,7 @@
|
||||
let aiSettingsOpen = false;
|
||||
let appSettingsOpen = false;
|
||||
let helpOpen = false;
|
||||
let commandPaletteOpen = false;
|
||||
let analyticsNoticeOpen = false;
|
||||
let analyticsSettings: AnalyticsSettings = defaultAnalyticsSettings();
|
||||
let appTheme: AppTheme = loadThemePreference();
|
||||
@@ -3749,6 +3752,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function openFileFromCommandPalette(file: GitRepositoryFile) {
|
||||
if (!activeRepoPath) return;
|
||||
selectedExplorerPath = file.path;
|
||||
selectedExplorerKind = "file";
|
||||
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
||||
try {
|
||||
await openRepositoryFile(activeRepoPath, file.path);
|
||||
trackEvent("explorer_file_opened", { source: "command_palette", tracked: file.tracked ? 1 : 0 });
|
||||
} catch (error) {
|
||||
errorMessage = errorToMessage(error);
|
||||
}
|
||||
}
|
||||
|
||||
function selectCommitFromCommandPalette(target: GitCommit) {
|
||||
selectedCommitHash = target.hash;
|
||||
trackEvent("commit_selected", { source: "command_palette" });
|
||||
}
|
||||
|
||||
async function restoreSelectedFileFromCommit(target: GitCommit) {
|
||||
if (!activeRepoPath || !selectedExplorerPath) return;
|
||||
const kind = selectedExplorerKind === "folder" ? "folder" : "file";
|
||||
@@ -3781,6 +3802,14 @@
|
||||
trackEvent("help_opened");
|
||||
}
|
||||
|
||||
function openAppSettings() {
|
||||
appSettingsOpen = true;
|
||||
}
|
||||
|
||||
function openAiSettings() {
|
||||
aiSettingsOpen = true;
|
||||
}
|
||||
|
||||
async function compareSelectedCommits() {
|
||||
if (!canCompare) return;
|
||||
await runOperation("Comparing commits", async () => {
|
||||
@@ -3970,11 +3999,20 @@
|
||||
// ── Event handlers ─────────────────────────────────────────────────────────
|
||||
|
||||
function handleWindowKeydown(event: KeyboardEvent) {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
|
||||
event.preventDefault();
|
||||
if (!event.repeat) commandPaletteOpen = !commandPaletteOpen;
|
||||
return;
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "/") {
|
||||
event.preventDefault();
|
||||
openHelp();
|
||||
return;
|
||||
}
|
||||
if (event.key === "Escape" && commandPaletteOpen) {
|
||||
commandPaletteOpen = false;
|
||||
return;
|
||||
}
|
||||
if (event.key === "Escape" && helpOpen) {
|
||||
helpOpen = false;
|
||||
return;
|
||||
@@ -4012,7 +4050,7 @@
|
||||
|
||||
<main class="shell">
|
||||
<TitleBar
|
||||
onOpenSettings={() => { appSettingsOpen = true; }}
|
||||
onOpenSettings={openAppSettings}
|
||||
onOpenHelp={openHelp}
|
||||
language={appLanguage}
|
||||
/>
|
||||
@@ -4544,7 +4582,7 @@
|
||||
onGenerateCommitMessage={generateCommitMessageWithAi}
|
||||
onReviewStaged={reviewStagedWithAi}
|
||||
onSplitStaged={splitStagedWithAi}
|
||||
onOpenAiSettings={() => { aiSettingsOpen = true; }}
|
||||
onOpenAiSettings={openAiSettings}
|
||||
onToggleAmend={toggleAmendMode}
|
||||
onUndoLastCommit={undoLastCommitChange}
|
||||
/>
|
||||
@@ -4574,6 +4612,7 @@
|
||||
<aside class="history-aside" aria-label="Commit history">
|
||||
<HistoryPanel
|
||||
{commits}
|
||||
{selectedCommitHash}
|
||||
{localBranchNames}
|
||||
activeBranch={status?.current_branch ?? ""}
|
||||
activeUpstream={status?.upstream ?? ""}
|
||||
@@ -4590,6 +4629,7 @@
|
||||
onCreateBranchFromCommit={openNewBranchDialog}
|
||||
onCherryPickCommit={cherryPickFromCommit}
|
||||
onRevertCommit={revertHistoryCommit}
|
||||
onSelectCommit={(commit) => { selectedCommitHash = commit.hash; }}
|
||||
onToggleCommitFiles={(hash) => {
|
||||
const next = new Set(expandedCommitHashes);
|
||||
if (next.has(hash)) next.delete(hash); else next.add(hash);
|
||||
@@ -4615,6 +4655,34 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{#if commandPaletteOpen}
|
||||
<CommandPalette
|
||||
language={appLanguage}
|
||||
{hasRepository}
|
||||
{isBusy}
|
||||
{branches}
|
||||
files={repoFiles}
|
||||
{commits}
|
||||
onClose={() => { commandPaletteOpen = false; }}
|
||||
onCheckoutBranch={checkout}
|
||||
onOpenFile={openFileFromCommandPalette}
|
||||
onSelectCommit={selectCommitFromCommandPalette}
|
||||
onFetch={fetchRepo}
|
||||
onPull={pullRepo}
|
||||
onPush={pushRepo}
|
||||
onRefresh={refreshRepo}
|
||||
onOpenSearch={openGlobalSearchDialog}
|
||||
onOpenCompare={openCompareSelect}
|
||||
onOpenReflog={openReflog}
|
||||
onOpenInteractiveRebase={openInteractiveRebase}
|
||||
onOpenWorktrees={openWorktreeDialog}
|
||||
onOpenSyncSettings={openSyncOptions}
|
||||
onOpenSettings={openAppSettings}
|
||||
onOpenAiSettings={openAiSettings}
|
||||
onOpenHelp={openHelp}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if updateToastOpen}
|
||||
<UpdateToast
|
||||
state={updateToastState}
|
||||
|
||||
Reference in New Issue
Block a user