= ({ x, y, onClose, actions
const disabled = !!action.disabled;
const label = action.label || '';
+ const baseColor =
+ action.danger
+ ? 'var(--vscode-errorForeground)'
+ : action.tone === 'warning'
+ ? 'var(--vscode-editorWarning-foreground, #d19a66)'
+ : action.tone === 'success'
+ ? 'var(--vscode-gitDecoration-addedResourceForeground, #73c991)'
+ : 'inherit';
+ const isToned = !!action.danger || !!action.tone;
return (
= ({ x, y, onClose, actions
padding: '6px 12px',
cursor: disabled ? 'default' : 'pointer',
opacity: disabled ? 0.45 : 1,
- color: action.danger ? 'var(--vscode-errorForeground)' : 'inherit',
+ color: baseColor,
fontSize: '12px',
display: 'flex',
alignItems: 'center',
@@ -75,11 +85,11 @@ export const ContextMenu: React.FC = ({ x, y, onClose, actions
onMouseEnter={(e) => {
if (disabled) return;
e.currentTarget.style.backgroundColor = 'var(--vscode-menu-selectionBackground)';
- e.currentTarget.style.color = action.danger ? 'var(--vscode-errorForeground)' : 'var(--vscode-menu-selectionForeground)';
+ e.currentTarget.style.color = isToned ? baseColor : 'var(--vscode-menu-selectionForeground)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent';
- e.currentTarget.style.color = action.danger ? 'var(--vscode-errorForeground)' : 'inherit';
+ e.currentTarget.style.color = baseColor;
}}
>
{action.icon ? (
diff --git a/src/webview/components/DetailsPane.tsx b/src/webview/components/DetailsPane.tsx
index 1db2041..511de20 100644
--- a/src/webview/components/DetailsPane.tsx
+++ b/src/webview/components/DetailsPane.tsx
@@ -69,6 +69,18 @@ export const DetailsPane: React.FC = ({ sha }) => {
});
};
+ const handleRevealInOS = (change: Change) => {
+ vscode.postMessage({
+ type: 'file/revealInOS',
+ requestId: `reveal-${Date.now()}`,
+ payload: {
+ path: change.path,
+ oldPath: change.oldPath,
+ status: change.status
+ }
+ });
+ };
+
const handleFileClick = (change: Change) => {
if (!details) return;
@@ -128,6 +140,16 @@ export const DetailsPane: React.FC = ({ sha }) => {
}
};
+ const copySubject = () => {
+ if (!details || details.sha === 'UNCOMMITTED') return;
+ const text = String(details.subject || '').trim();
+ if (!text) return;
+ vscode.postMessage({
+ type: 'app/copyToClipboard',
+ payload: { text }
+ });
+ };
+
const isUncommitted = details?.sha === 'UNCOMMITTED';
const allFilePaths = isUncommitted ? changes.map(c => c.path) : [];
const hasExtendedMessage = !!details && !isUncommitted && details.message.trim() !== details.subject.trim();
@@ -182,6 +204,28 @@ export const DetailsPane: React.FC = ({ sha }) => {
return 'No files selected';
}, [selectedPaths.size]);
+ const handleCommitClick = () => {
+ // UX: if the user typed a message but forgot to select files, first click selects all,
+ // second click commits.
+ if (selectedPaths.size === 0) {
+ if (!commitMessage.trim()) return;
+ if (allFilePaths.length === 0) return;
+ selectAll();
+ return;
+ }
+ performCommit();
+ };
+
+ const handleAmendClick = () => {
+ // Same UX for amend (message optional): first click selects all if nothing is selected.
+ if (selectedPaths.size === 0) {
+ if (allFilePaths.length === 0) return;
+ selectAll();
+ return;
+ }
+ performCommit({ amend: true });
+ };
+
const hasCommitBox = isUncommitted || (hasExtendedMessage && showFullMessage);
const formatDateYYYYMMDD = (iso: string) => {
@@ -220,6 +264,25 @@ export const DetailsPane: React.FC = ({ sha }) => {
/>
)}
{details.subject}
+ {!isUncommitted && (
+ {
+ e.stopPropagation();
+ copySubject();
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ e.stopPropagation();
+ copySubject();
+ }
+ }}
+ />
+ )}
{!isUncommitted && (
@@ -277,15 +340,15 @@ export const DetailsPane: React.FC
= ({ sha }) => {