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 && ( <> )} {!isUncommitted && ( <> {t('GitLog.WarningMessage')} )} ); }; return ( { setCurrentTab(newValue); }} > {currentTab === 'details' ? renderDetailsTab() : renderActionsTab()} ); }