import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress';
import Divider from '@mui/material/Divider';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import { styled } from '@mui/material/styles';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import Typography from '@mui/material/Typography';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { GitLogEntry } from './types';
const Panel = styled(Box)`
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
`;
const TabsWrapper = styled(Box)`
border-bottom: 1px solid ${({ theme }) => theme.palette.divider};
padding: 0 16px;
`;
const TabContent = styled(Box)`
flex: 1;
overflow: auto;
padding: 16px;
`;
const EmptyState = styled(Box)`
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: ${({ theme }) => theme.palette.text.secondary};
`;
const FileListWrapper = styled(Box)`
flex: 1;
overflow: auto;
min-height: 0;
`;
const ActionsWrapper = styled(Box)`
display: flex;
flex-direction: column;
gap: 12px;
`;
interface ICommitDetailsPanelProps {
commit: GitLogEntry | null;
isLatestCommit?: boolean;
onCommitSuccess?: () => void;
onFileSelect?: (file: string | null) => void;
onRevertSuccess?: () => void;
selectedFile?: string | null;
}
export function CommitDetailsPanel(
{ commit, isLatestCommit: _isLatestCommit, onCommitSuccess, onFileSelect, onRevertSuccess, selectedFile }: ICommitDetailsPanelProps,
): React.JSX.Element {
const { t } = useTranslation();
const [currentTab, setCurrentTab] = useState<'details' | 'actions'>('details');
const [isReverting, setIsReverting] = useState(false);
const [isCommitting, setIsCommitting] = useState(false);
// Use files from commit entry (already loaded in useGitLogData)
const fileChanges = commit?.files ?? [];
const handleRevert = async () => {
if (!commit || isReverting) return;
setIsReverting(true);
try {
const meta = window.meta();
const workspaceID = (meta as { workspaceID?: string }).workspaceID;
if (!workspaceID) return;
const workspace = await window.service.workspace.get(workspaceID);
if (!workspace || !('wikiFolderLocation' in workspace)) return;
// Pass the commit message to revertCommit for better revert message
await window.service.git.revertCommit(workspace.wikiFolderLocation, commit.hash, commit.message);
console.log('Revert success');
// Notify parent to select the new revert commit
if (onRevertSuccess) {
onRevertSuccess();
}
} catch (error) {
console.error('Failed to revert commit:', error);
} finally {
setIsReverting(false);
}
};
const handleCommitNow = async () => {
if (isCommitting) return;
setIsCommitting(true);
try {
const meta = window.meta();
const workspaceID = (meta as { workspaceID?: string }).workspaceID;
if (!workspaceID) return;
const workspace = await window.service.workspace.get(workspaceID);
if (!workspace || !('wikiFolderLocation' in workspace)) return;
await window.service.git.commitAndSync(workspace, {
dir: workspace.wikiFolderLocation,
commitOnly: true,
});
console.log('Commit success');
// Notify parent to select the new commit
if (onCommitSuccess) {
onCommitSuccess();
}
} catch (error) {
console.error('Failed to commit:', error);
} finally {
setIsCommitting(false);
}
};
const handleCopyHash = () => {
if (!commit) return;
navigator.clipboard.writeText(commit.hash).then(() => {
console.log('Hash copied');
}).catch((error: unknown) => {
console.error('Failed to copy hash:', error);
});
};
const handleOpenInGitHub = async () => {
if (!commit) return;
try {
const meta = window.meta();
const workspaceID = (meta as { workspaceID?: string }).workspaceID;
if (!workspaceID) return;
const workspace = await window.service.workspace.get(workspaceID);
if (!workspace || !('wikiFolderLocation' in workspace) || !('gitUrl' in workspace)) return;
if (workspace.gitUrl) {
const githubUrl = workspace.gitUrl
.replace(/\.git$/, '')
.replace(/^git@github\.com:/, 'https://github.com/');
const commitUrl = `${githubUrl}/commit/${commit.hash}`;
window.open(commitUrl, '_blank');
}
} catch (error) {
console.error('Failed to open in GitHub:', error);
}
};
if (!commit) {
return (
{t('GitLog.SelectCommit')}
);
}
const renderDetailsTab = () => (
{t('GitLog.Hash')}
{commit.hash}
{t('GitLog.Message')}
{commit.message}
{commit.author && (
{t('GitLog.Author')}
{commit.author.name}
{commit.author.email && ` <${commit.author.email}>`}
)}
{t('GitLog.Date')}
{commit.committerDate}
{t('GitLog.FilesChanged', { count: fileChanges.length })}
{fileChanges.length > 0
? (
{fileChanges.map((file, index) => (
{
onFileSelect?.(file === selectedFile ? null : file);
}}
>
))}
)
: (
{t('GitLog.NoFilesChanged')}
)}
);
const renderActionsTab = () => {
// Check if this is an uncommitted state (hash would be empty or special)
const isUncommitted = !commit || commit.hash === '' || commit.message.includes('未提交') || commit.message.includes('Uncommitted');
return (
{isUncommitted && (
<>
: undefined}
>
{isCommitting ? t('GitLog.Committing') : t('ContextMenu.BackupNow')}
>
)}
{!isUncommitted && (
<>
: undefined}
>
{isReverting ? t('GitLog.Reverting') : t('GitLog.RevertCommit')}
{t('GitLog.WarningMessage')}
>
)}
);
};
return (
{
setCurrentTab(newValue);
}}
>
{currentTab === 'details' ? renderDetailsTab() : renderActionsTab()}
);
}