mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-20 16:02:05 +08:00
* feat(git-panel): add Branches tab, Fetch always visible, inline error banners - Add dedicated Branches tab (local/remote sections, switch with confirmation, delete branch, create branch) - Rename History tab to Commits; add change-count badge on Changes tab - Fetch button always visible when remote exists (not only when both ahead & behind) - Inline error banner below header for failed push/pull/fetch, with dismiss button - Server: /api/git/branches now returns localBranches + remoteBranches separately - Server: add /api/git/delete-branch endpoint (prevents deleting current branch) - Controller: expose operationError, clearOperationError, deleteBranch, localBranches, remoteBranches - Constants: add deleteBranch to all ConfirmActionType record maps Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: git log datetime * feat(git-panel): add staged/unstaged sections and enhanced commit details --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Haile <118998054+blackmammoth@users.noreply.github.com>
185 lines
5.7 KiB
TypeScript
185 lines
5.7 KiB
TypeScript
import { useCallback, useState } from 'react';
|
|
import { useGitPanelController } from '../hooks/useGitPanelController';
|
|
import { useRevertLocalCommit } from '../hooks/useRevertLocalCommit';
|
|
import type { ConfirmationRequest, GitPanelProps, GitPanelView } from '../types/types';
|
|
import { getChangedFileCount } from '../utils/gitPanelUtils';
|
|
import ChangesView from '../view/changes/ChangesView';
|
|
import HistoryView from '../view/history/HistoryView';
|
|
import BranchesView from '../view/branches/BranchesView';
|
|
import GitPanelHeader from '../view/GitPanelHeader';
|
|
import GitRepositoryErrorState from '../view/GitRepositoryErrorState';
|
|
import GitViewTabs from '../view/GitViewTabs';
|
|
import ConfirmActionModal from '../view/modals/ConfirmActionModal';
|
|
|
|
export default function GitPanel({ selectedProject, isMobile = false, onFileOpen }: GitPanelProps) {
|
|
const [activeView, setActiveView] = useState<GitPanelView>('changes');
|
|
const [wrapText, setWrapText] = useState(true);
|
|
const [hasExpandedFiles, setHasExpandedFiles] = useState(false);
|
|
const [confirmAction, setConfirmAction] = useState<ConfirmationRequest | null>(null);
|
|
|
|
const {
|
|
gitStatus,
|
|
gitDiff,
|
|
isLoading,
|
|
currentBranch,
|
|
branches,
|
|
localBranches,
|
|
remoteBranches,
|
|
recentCommits,
|
|
commitDiffs,
|
|
remoteStatus,
|
|
isCreatingBranch,
|
|
isFetching,
|
|
isPulling,
|
|
isPushing,
|
|
isPublishing,
|
|
isCreatingInitialCommit,
|
|
operationError,
|
|
clearOperationError,
|
|
refreshAll,
|
|
switchBranch,
|
|
createBranch,
|
|
deleteBranch,
|
|
handleFetch,
|
|
handlePull,
|
|
handlePush,
|
|
handlePublish,
|
|
discardChanges,
|
|
deleteUntrackedFile,
|
|
fetchCommitDiff,
|
|
generateCommitMessage,
|
|
commitChanges,
|
|
createInitialCommit,
|
|
openFile,
|
|
} = useGitPanelController({
|
|
selectedProject,
|
|
activeView,
|
|
onFileOpen,
|
|
});
|
|
|
|
const { isRevertingLocalCommit, revertLatestLocalCommit } = useRevertLocalCommit({
|
|
projectName: selectedProject?.name ?? null,
|
|
onSuccess: refreshAll,
|
|
});
|
|
|
|
const executeConfirmedAction = useCallback(async () => {
|
|
if (!confirmAction) return;
|
|
const actionToExecute = confirmAction;
|
|
setConfirmAction(null);
|
|
try {
|
|
await actionToExecute.onConfirm();
|
|
} catch (error) {
|
|
console.error('Error executing confirmation action:', error);
|
|
}
|
|
}, [confirmAction]);
|
|
|
|
const changeCount = getChangedFileCount(gitStatus);
|
|
|
|
if (!selectedProject) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center text-muted-foreground">
|
|
<p>Select a project to view source control</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-full flex-col bg-background">
|
|
<GitPanelHeader
|
|
isMobile={isMobile}
|
|
currentBranch={currentBranch}
|
|
branches={branches}
|
|
remoteStatus={remoteStatus}
|
|
isLoading={isLoading}
|
|
isCreatingBranch={isCreatingBranch}
|
|
isFetching={isFetching}
|
|
isPulling={isPulling}
|
|
isPushing={isPushing}
|
|
isPublishing={isPublishing}
|
|
isRevertingLocalCommit={isRevertingLocalCommit}
|
|
operationError={operationError}
|
|
onRefresh={refreshAll}
|
|
onRevertLocalCommit={revertLatestLocalCommit}
|
|
onSwitchBranch={switchBranch}
|
|
onCreateBranch={createBranch}
|
|
onFetch={handleFetch}
|
|
onPull={handlePull}
|
|
onPush={handlePush}
|
|
onPublish={handlePublish}
|
|
onClearError={clearOperationError}
|
|
onRequestConfirmation={setConfirmAction}
|
|
/>
|
|
|
|
{gitStatus?.error ? (
|
|
<GitRepositoryErrorState error={gitStatus.error} details={gitStatus.details} />
|
|
) : (
|
|
<>
|
|
<GitViewTabs
|
|
activeView={activeView}
|
|
isHidden={hasExpandedFiles}
|
|
changeCount={changeCount}
|
|
onChange={setActiveView}
|
|
/>
|
|
|
|
{activeView === 'changes' && (
|
|
<ChangesView
|
|
key={selectedProject.fullPath}
|
|
isMobile={isMobile}
|
|
projectPath={selectedProject.fullPath}
|
|
gitStatus={gitStatus}
|
|
gitDiff={gitDiff}
|
|
isLoading={isLoading}
|
|
wrapText={wrapText}
|
|
isCreatingInitialCommit={isCreatingInitialCommit}
|
|
onWrapTextChange={setWrapText}
|
|
onCreateInitialCommit={createInitialCommit}
|
|
onOpenFile={openFile}
|
|
onDiscardFile={discardChanges}
|
|
onDeleteFile={deleteUntrackedFile}
|
|
onCommitChanges={commitChanges}
|
|
onGenerateCommitMessage={generateCommitMessage}
|
|
onRequestConfirmation={setConfirmAction}
|
|
onExpandedFilesChange={setHasExpandedFiles}
|
|
/>
|
|
)}
|
|
|
|
{activeView === 'history' && (
|
|
<HistoryView
|
|
isMobile={isMobile}
|
|
isLoading={isLoading}
|
|
recentCommits={recentCommits}
|
|
commitDiffs={commitDiffs}
|
|
wrapText={wrapText}
|
|
onFetchCommitDiff={fetchCommitDiff}
|
|
/>
|
|
)}
|
|
|
|
{activeView === 'branches' && (
|
|
<BranchesView
|
|
isMobile={isMobile}
|
|
isLoading={isLoading}
|
|
currentBranch={currentBranch}
|
|
localBranches={localBranches}
|
|
remoteBranches={remoteBranches}
|
|
remoteStatus={remoteStatus}
|
|
isCreatingBranch={isCreatingBranch}
|
|
onSwitchBranch={switchBranch}
|
|
onCreateBranch={createBranch}
|
|
onDeleteBranch={deleteBranch}
|
|
onRequestConfirmation={setConfirmAction}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
|
|
<ConfirmActionModal
|
|
action={confirmAction}
|
|
onCancel={() => setConfirmAction(null)}
|
|
onConfirm={() => {
|
|
void executeConfirmedAction();
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|