mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-28 15:25:27 +08:00
feat(command-palette): add git fetch/pull/push and branch switch actions
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
|
ArrowDownToLine,
|
||||||
|
ArrowUpFromLine,
|
||||||
Bell,
|
Bell,
|
||||||
Bot,
|
Bot,
|
||||||
FileText,
|
FileText,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
GitCommit,
|
GitCommit,
|
||||||
|
GitMerge,
|
||||||
Info,
|
Info,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
ListChecks,
|
ListChecks,
|
||||||
@@ -13,6 +16,7 @@ import {
|
|||||||
MessageSquarePlus,
|
MessageSquarePlus,
|
||||||
Palette,
|
Palette,
|
||||||
Plug,
|
Plug,
|
||||||
|
RefreshCw,
|
||||||
Settings,
|
Settings,
|
||||||
SunMoon,
|
SunMoon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -35,6 +39,8 @@ import { useSessionsSource } from './sources/useSessionsSource';
|
|||||||
import { useFilesSource } from './sources/useFilesSource';
|
import { useFilesSource } from './sources/useFilesSource';
|
||||||
import { useCommitsSource } from './sources/useCommitsSource';
|
import { useCommitsSource } from './sources/useCommitsSource';
|
||||||
import { useSessionMessageSearch } from './sources/useSessionMessageSearch';
|
import { useSessionMessageSearch } from './sources/useSessionMessageSearch';
|
||||||
|
import { useBranchesSource } from './sources/useBranchesSource';
|
||||||
|
import { useGitActions } from './sources/useGitActions';
|
||||||
|
|
||||||
type Mode = 'mixed' | 'actions' | 'files' | 'commits';
|
type Mode = 'mixed' | 'actions' | 'files' | 'commits';
|
||||||
|
|
||||||
@@ -104,6 +110,8 @@ export default function CommandPalette({
|
|||||||
const { items: messageMatches } = useSessionMessageSearch(projectId, query, open && mode === 'mixed');
|
const { items: messageMatches } = useSessionMessageSearch(projectId, query, open && mode === 'mixed');
|
||||||
const { items: files } = useFilesSource(projectId, open && (mode === 'mixed' || mode === 'files'));
|
const { items: files } = useFilesSource(projectId, open && (mode === 'mixed' || mode === 'files'));
|
||||||
const { items: commits } = useCommitsSource(projectId, open && (mode === 'mixed' || mode === 'commits'));
|
const { items: commits } = useCommitsSource(projectId, open && (mode === 'mixed' || mode === 'commits'));
|
||||||
|
const { items: branches } = useBranchesSource(projectId, open && (mode === 'mixed' || mode === 'actions'));
|
||||||
|
const git = useGitActions(projectId);
|
||||||
|
|
||||||
const showActions = mode === 'mixed' || mode === 'actions';
|
const showActions = mode === 'mixed' || mode === 'actions';
|
||||||
const showSessions = mode === 'mixed';
|
const showSessions = mode === 'mixed';
|
||||||
@@ -206,6 +214,45 @@ export default function CommandPalette({
|
|||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showActions && projectId && (
|
||||||
|
<CommandGroup heading="Git">
|
||||||
|
<CommandItem
|
||||||
|
value="git fetch remote"
|
||||||
|
onSelect={() => run(() => { void git.fetch(); onShowTab?.('git'); })}
|
||||||
|
>
|
||||||
|
<RefreshCw className="h-4 w-4 shrink-0 text-muted-foreground" aria-hidden />
|
||||||
|
<span className="flex-1">Git: Fetch</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem
|
||||||
|
value="git pull merge upstream"
|
||||||
|
onSelect={() => run(() => { void git.pull(); onShowTab?.('git'); })}
|
||||||
|
>
|
||||||
|
<ArrowDownToLine className="h-4 w-4 shrink-0 text-muted-foreground" aria-hidden />
|
||||||
|
<span className="flex-1">Git: Pull</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem
|
||||||
|
value="git push origin remote"
|
||||||
|
onSelect={() => run(() => { void git.push(); onShowTab?.('git'); })}
|
||||||
|
>
|
||||||
|
<ArrowUpFromLine className="h-4 w-4 shrink-0 text-muted-foreground" aria-hidden />
|
||||||
|
<span className="flex-1">Git: Push</span>
|
||||||
|
</CommandItem>
|
||||||
|
{branches
|
||||||
|
.filter((b) => !b.isCurrent && !b.isRemote)
|
||||||
|
.slice(0, 30)
|
||||||
|
.map((b) => (
|
||||||
|
<CommandItem
|
||||||
|
key={`branch-${b.name}`}
|
||||||
|
value={`git switch checkout branch ${b.name}`}
|
||||||
|
onSelect={() => run(() => { void git.checkout(b.name); onShowTab?.('git'); })}
|
||||||
|
>
|
||||||
|
<GitMerge className="h-4 w-4 shrink-0 text-muted-foreground" aria-hidden />
|
||||||
|
<span className="flex-1 truncate">Switch to branch: {b.name}</span>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
)}
|
||||||
|
|
||||||
{showActions && (
|
{showActions && (
|
||||||
<CommandGroup heading="Settings">
|
<CommandGroup heading="Settings">
|
||||||
{SETTINGS_TABS.map(({ id, label, keywords, icon: Icon }) => (
|
{SETTINGS_TABS.map(({ id, label, keywords, icon: Icon }) => (
|
||||||
|
|||||||
47
src/components/command-palette/sources/useBranchesSource.ts
Normal file
47
src/components/command-palette/sources/useBranchesSource.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { authenticatedFetch } from '../../../utils/api';
|
||||||
|
|
||||||
|
export type BranchResult = {
|
||||||
|
name: string;
|
||||||
|
isCurrent: boolean;
|
||||||
|
isRemote: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BranchesResponse {
|
||||||
|
branches?: Array<{ name: string; current?: boolean; isRemote?: boolean }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBranchesSource(projectId: string | undefined, enabled: boolean) {
|
||||||
|
const [items, setItems] = useState<BranchResult[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled || !projectId) {
|
||||||
|
setItems([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
|
const params = new URLSearchParams({ project: projectId });
|
||||||
|
authenticatedFetch(`/api/git/branches?${params.toString()}`)
|
||||||
|
.then((r) => r.json() as Promise<BranchesResponse>)
|
||||||
|
.then((data) => {
|
||||||
|
if (cancelled) return;
|
||||||
|
const list = data.branches ?? [];
|
||||||
|
setItems(
|
||||||
|
list.map<BranchResult>((b) => ({
|
||||||
|
name: b.name,
|
||||||
|
isCurrent: Boolean(b.current),
|
||||||
|
isRemote: Boolean(b.isRemote),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (!cancelled) setItems([]);
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [projectId, enabled]);
|
||||||
|
|
||||||
|
return { items };
|
||||||
|
}
|
||||||
38
src/components/command-palette/sources/useGitActions.ts
Normal file
38
src/components/command-palette/sources/useGitActions.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { authenticatedFetch } from '../../../utils/api';
|
||||||
|
|
||||||
|
async function postGit(path: string, body: Record<string, unknown>) {
|
||||||
|
const res = await authenticatedFetch(path, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGitActions(projectId: string | undefined) {
|
||||||
|
const fetch = useCallback(() => {
|
||||||
|
if (!projectId) return Promise.resolve();
|
||||||
|
return postGit('/api/git/fetch', { project: projectId });
|
||||||
|
}, [projectId]);
|
||||||
|
|
||||||
|
const pull = useCallback(() => {
|
||||||
|
if (!projectId) return Promise.resolve();
|
||||||
|
return postGit('/api/git/pull', { project: projectId });
|
||||||
|
}, [projectId]);
|
||||||
|
|
||||||
|
const push = useCallback(() => {
|
||||||
|
if (!projectId) return Promise.resolve();
|
||||||
|
return postGit('/api/git/push', { project: projectId });
|
||||||
|
}, [projectId]);
|
||||||
|
|
||||||
|
const checkout = useCallback(
|
||||||
|
(branch: string) => {
|
||||||
|
if (!projectId) return Promise.resolve();
|
||||||
|
return postGit('/api/git/checkout', { project: projectId, branch });
|
||||||
|
},
|
||||||
|
[projectId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { fetch, pull, push, checkout };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user