feat(ui): add session history navigation, permission keys, and review shortcuts
mod+alt+arrows step through this window's session-open history (or between neighbouring tabs when session tabs are on), mod+k r renames the current session inline, and mod+k a toggles permission auto-accept. Pending permission cards respond to alt+enter / alt+shift+enter / alt+backspace with the keys printed on the buttons. The commit message box commits on mod+enter, alt+arrows step the diff review between changed files, and the command palette gains search-only commands for rare actions so the initial list stays short.
This commit is contained in:
@@ -1768,6 +1768,37 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
scrollToFile(value);
|
||||
}, [cancelPendingScrollAlignment, expandStackedFile, scrollToFile]);
|
||||
|
||||
// Step review to the adjacent changed file (alt+arrow): selects, expands
|
||||
// a collapsed section, and scrolls to it. Window-level because the diff
|
||||
// surface has no persistent focus target; guarded off editable fields.
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (!event.altKey || event.metaKey || event.ctrlKey || event.shiftKey) return;
|
||||
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLElement && (
|
||||
target.isContentEditable
|
||||
|| target.tagName === 'INPUT'
|
||||
|| target.tagName === 'TEXTAREA'
|
||||
|| target.closest('[role="dialog"]')
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
if (changedFiles.length === 0) return;
|
||||
const delta = event.key === 'ArrowDown' ? 1 : -1;
|
||||
const index = displayFile ? changedFiles.findIndex((file) => file.path === displayFile) : -1;
|
||||
const nextIndex = index === -1
|
||||
? (delta > 0 ? 0 : changedFiles.length - 1)
|
||||
: index + delta;
|
||||
const next = changedFiles[nextIndex];
|
||||
if (!next) return;
|
||||
event.preventDefault();
|
||||
handleSelectFileAndScroll(next.path);
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [changedFiles, displayFile, handleSelectFileAndScroll]);
|
||||
|
||||
const handleHeaderLayoutChange = React.useCallback((mode: DiffViewMode) => {
|
||||
const nextLayout: 'inline' | 'side-by-side' =
|
||||
mode === 'side-by-side' ? 'side-by-side' : 'inline';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useI18n } from '@/lib/i18n';
|
||||
interface CommitInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onSubmit?: () => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
hasTouchInput?: boolean;
|
||||
@@ -18,6 +19,7 @@ const MAX_HEIGHT = 200;
|
||||
export const CommitInput: React.FC<CommitInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
onSubmit,
|
||||
placeholder,
|
||||
disabled = false,
|
||||
hasTouchInput = false,
|
||||
@@ -58,6 +60,12 @@ export const CommitInput: React.FC<CommitInputProps> = ({
|
||||
ref={textareaRef}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
onSubmit?.();
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder ?? t('gitView.commit.messagePlaceholder')}
|
||||
rows={1}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -68,6 +68,9 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
<CommitInput
|
||||
value={commitMessage}
|
||||
onChange={onCommitMessageChange}
|
||||
onSubmit={() => {
|
||||
if (canCommit && !isGeneratingMessage) onCommit();
|
||||
}}
|
||||
placeholder={t('gitView.commit.messagePlaceholder')}
|
||||
disabled={commitAction !== null}
|
||||
hasTouchInput={hasTouchInput}
|
||||
|
||||
Reference in New Issue
Block a user