Merge pull request #66 from Psypeal/fix/ctrl-r-shortcut
fix: prevent Ctrl+R page reload and show platform-aware shortcuts (#58)
This commit is contained in:
commit
c4975f1874
7 changed files with 45 additions and 10 deletions
|
|
@ -481,7 +481,16 @@ function createWindow(): void {
|
|||
const ZOOM_OUT_KEYS = new Set(['-', '_']);
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
if (!input.meta || input.type !== 'keyDown') return;
|
||||
if (input.type !== 'keyDown') return;
|
||||
|
||||
// Prevent Electron's default Ctrl+R / Cmd+R page reload so the renderer
|
||||
// keyboard handler can use it as "Refresh Session" (fixes #58).
|
||||
if ((input.control || input.meta) && input.key.toLowerCase() === 'r') {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input.meta) return;
|
||||
|
||||
const currentLevel = mainWindow.webContents.getZoomLevel();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||
|
||||
import { api } from '@renderer/api';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { formatShortcut } from '@renderer/utils/stringUtils';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ const CommandSearch = ({ value, onChange }: Readonly<CommandSearchProps>): React
|
|||
<button
|
||||
onClick={() => openCommandPalette()}
|
||||
className="flex shrink-0 items-center gap-1 transition-opacity hover:opacity-80"
|
||||
title={selectedProjectId ? 'Search in sessions (⌘K)' : 'Search projects (⌘K)'}
|
||||
title={selectedProjectId ? `Search in sessions (${formatShortcut('K')})` : `Search projects (${formatShortcut('K')})`}
|
||||
>
|
||||
<kbd className="flex h-5 items-center justify-center rounded border border-border bg-surface-overlay px-1.5 text-[10px] font-medium text-text-muted">
|
||||
<Command className="size-2.5" />
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { useEffect, useRef, useState } from 'react';
|
|||
import { isElectronMode } from '@renderer/api';
|
||||
import { HEADER_ROW1_HEIGHT, HEADER_ROW2_HEIGHT } from '@renderer/constants/layout';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { truncateMiddle } from '@renderer/utils/stringUtils';
|
||||
import { formatShortcut, truncateMiddle } from '@renderer/utils/stringUtils';
|
||||
import { Check, ChevronDown, GitBranch, PanelLeft } from 'lucide-react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
|
|
@ -369,7 +369,7 @@ export const SidebarHeader = (): React.JSX.Element => {
|
|||
backgroundColor: isCollapseHovered ? 'var(--color-surface-raised)' : 'transparent',
|
||||
} as React.CSSProperties
|
||||
}
|
||||
title="Collapse sidebar (⌘B)"
|
||||
title={`Collapse sidebar (${formatShortcut('B')})`}
|
||||
>
|
||||
<PanelLeft className="size-4" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortabl
|
|||
import { isElectronMode } from '@renderer/api';
|
||||
import { HEADER_ROW1_HEIGHT } from '@renderer/constants/layout';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { formatShortcut } from '@renderer/utils/stringUtils';
|
||||
import { Bell, PanelLeft, Plus, RefreshCw, Search, Settings } from 'lucide-react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
|
|
@ -339,7 +340,7 @@ export const TabBar = ({ paneId }: TabBarProps): React.JSX.Element => {
|
|||
onMouseEnter={() => setRefreshHover(true)}
|
||||
onMouseLeave={() => setRefreshHover(false)}
|
||||
onClick={handleRefresh}
|
||||
title="Refresh Session (Cmd+R)"
|
||||
title={`Refresh Session (${formatShortcut('R')})`}
|
||||
>
|
||||
<RefreshCw className="size-4" />
|
||||
</button>
|
||||
|
|
@ -376,7 +377,7 @@ export const TabBar = ({ paneId }: TabBarProps): React.JSX.Element => {
|
|||
color: searchHover ? 'var(--color-text)' : 'var(--color-text-muted)',
|
||||
backgroundColor: searchHover ? 'var(--color-surface-raised)' : 'transparent',
|
||||
}}
|
||||
title="Search (Cmd+K)"
|
||||
title={`Search (${formatShortcut('K')})`}
|
||||
>
|
||||
<Search className="size-4" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { formatShortcut } from '@renderer/utils/stringUtils';
|
||||
|
||||
interface TabContextMenuProps {
|
||||
x: number;
|
||||
y: number;
|
||||
|
|
@ -100,13 +102,13 @@ export const TabContextMenu = ({
|
|||
onClick={handleClick(onCloseSelectedTabs)}
|
||||
/>
|
||||
) : (
|
||||
<MenuItem label="Close Tab" shortcut="⌘W" onClick={handleClick(onCloseTab)} />
|
||||
<MenuItem label="Close Tab" shortcut={formatShortcut('W')} onClick={handleClick(onCloseTab)} />
|
||||
)}
|
||||
<MenuItem label="Close Other Tabs" onClick={handleClick(onCloseOtherTabs)} />
|
||||
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
|
||||
<MenuItem
|
||||
label="Split Right"
|
||||
shortcut="⌘\"
|
||||
shortcut={formatShortcut('\\')}
|
||||
onClick={handleClick(onSplitRight)}
|
||||
disabled={disableSplit}
|
||||
/>
|
||||
|
|
@ -127,7 +129,7 @@ export const TabContextMenu = ({
|
|||
/>
|
||||
)}
|
||||
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
|
||||
<MenuItem label="Close All Tabs" shortcut="⇧⌘W" onClick={handleClick(onCloseAllTabs)} />
|
||||
<MenuItem label="Close All Tabs" shortcut={formatShortcut('W', { shift: true })} onClick={handleClick(onCloseAllTabs)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { MAX_PANES } from '@renderer/types/panes';
|
||||
import { formatShortcut } from '@renderer/utils/stringUtils';
|
||||
import { Check, ClipboardCopy, Eye, EyeOff, Pin, PinOff, Terminal } from 'lucide-react';
|
||||
|
||||
interface SessionContextMenuProps {
|
||||
|
|
@ -98,7 +99,7 @@ export const SessionContextMenu = ({
|
|||
}}
|
||||
>
|
||||
<MenuItem label="Open in Current Pane" onClick={handleClick(onOpenInCurrentPane)} />
|
||||
<MenuItem label="Open in New Tab" shortcut="⌘ Click" onClick={handleClick(onOpenInNewTab)} />
|
||||
<MenuItem label="Open in New Tab" shortcut={`${formatShortcut('')}Click`} onClick={handleClick(onOpenInNewTab)} />
|
||||
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
|
||||
<MenuItem
|
||||
label="Split Right and Open"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,27 @@
|
|||
* String utilities for display formatting.
|
||||
*/
|
||||
|
||||
const isMacPlatform =
|
||||
typeof window !== 'undefined' && window.navigator.userAgent.includes('Macintosh');
|
||||
|
||||
/** Returns '⌘' on macOS, 'Ctrl' on Windows/Linux. */
|
||||
export const modKey = isMacPlatform ? '⌘' : 'Ctrl+';
|
||||
|
||||
/** Returns '⇧' on macOS, 'Shift+' on Windows/Linux. */
|
||||
export const shiftKey = isMacPlatform ? '⇧' : 'Shift+';
|
||||
|
||||
/**
|
||||
* Formats a keyboard shortcut for the current platform.
|
||||
* @example formatShortcut('R') → '⌘R' on Mac, 'Ctrl+R' on Windows/Linux
|
||||
* @example formatShortcut('W', { shift: true }) → '⇧⌘W' on Mac, 'Ctrl+Shift+W' on Windows/Linux
|
||||
*/
|
||||
export function formatShortcut(key: string, opts?: { shift?: boolean }): string {
|
||||
if (opts?.shift) {
|
||||
return isMacPlatform ? `${shiftKey}${modKey}${key}` : `${modKey}${shiftKey}${key}`;
|
||||
}
|
||||
return `${modKey}${key}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates a string in the middle to preserve both the beginning and end.
|
||||
* Useful for branch names where the unique identifier is often at the end.
|
||||
|
|
|
|||
Loading…
Reference in a new issue