Skip to content

Commit c4975f1

Browse files
authored
Merge pull request #66 from Psypeal/fix/ctrl-r-shortcut
fix: prevent Ctrl+R page reload and show platform-aware shortcuts (#58)
2 parents dc38c79 + 258dc6d commit c4975f1

7 files changed

Lines changed: 45 additions & 10 deletions

File tree

src/main/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,16 @@ function createWindow(): void {
481481
const ZOOM_OUT_KEYS = new Set(['-', '_']);
482482
mainWindow.webContents.on('before-input-event', (event, input) => {
483483
if (!mainWindow || mainWindow.isDestroyed()) return;
484-
if (!input.meta || input.type !== 'keyDown') return;
484+
if (input.type !== 'keyDown') return;
485+
486+
// Prevent Electron's default Ctrl+R / Cmd+R page reload so the renderer
487+
// keyboard handler can use it as "Refresh Session" (fixes #58).
488+
if ((input.control || input.meta) && input.key.toLowerCase() === 'r') {
489+
event.preventDefault();
490+
return;
491+
}
492+
493+
if (!input.meta) return;
485494

486495
const currentLevel = mainWindow.webContents.getZoomLevel();
487496

src/renderer/components/dashboard/DashboardView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import React, { useEffect, useMemo, useState } from 'react';
1111

1212
import { api } from '@renderer/api';
1313
import { useStore } from '@renderer/store';
14+
import { formatShortcut } from '@renderer/utils/stringUtils';
1415
import { createLogger } from '@shared/utils/logger';
1516
import { useShallow } from 'zustand/react/shallow';
1617

@@ -75,7 +76,7 @@ const CommandSearch = ({ value, onChange }: Readonly<CommandSearchProps>): React
7576
<button
7677
onClick={() => openCommandPalette()}
7778
className="flex shrink-0 items-center gap-1 transition-opacity hover:opacity-80"
78-
title={selectedProjectId ? 'Search in sessions (⌘K)' : 'Search projects (⌘K)'}
79+
title={selectedProjectId ? `Search in sessions (${formatShortcut('K')})` : `Search projects (${formatShortcut('K')})`}
7980
>
8081
<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">
8182
<Command className="size-2.5" />

src/renderer/components/layout/SidebarHeader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useEffect, useRef, useState } from 'react';
1616
import { isElectronMode } from '@renderer/api';
1717
import { HEADER_ROW1_HEIGHT, HEADER_ROW2_HEIGHT } from '@renderer/constants/layout';
1818
import { useStore } from '@renderer/store';
19-
import { truncateMiddle } from '@renderer/utils/stringUtils';
19+
import { formatShortcut, truncateMiddle } from '@renderer/utils/stringUtils';
2020
import { Check, ChevronDown, GitBranch, PanelLeft } from 'lucide-react';
2121
import { useShallow } from 'zustand/react/shallow';
2222

@@ -369,7 +369,7 @@ export const SidebarHeader = (): React.JSX.Element => {
369369
backgroundColor: isCollapseHovered ? 'var(--color-surface-raised)' : 'transparent',
370370
} as React.CSSProperties
371371
}
372-
title="Collapse sidebar (⌘B)"
372+
title={`Collapse sidebar (${formatShortcut('B')})`}
373373
>
374374
<PanelLeft className="size-4" />
375375
</button>

src/renderer/components/layout/TabBar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortabl
1414
import { isElectronMode } from '@renderer/api';
1515
import { HEADER_ROW1_HEIGHT } from '@renderer/constants/layout';
1616
import { useStore } from '@renderer/store';
17+
import { formatShortcut } from '@renderer/utils/stringUtils';
1718
import { Bell, PanelLeft, Plus, RefreshCw, Search, Settings } from 'lucide-react';
1819
import { useShallow } from 'zustand/react/shallow';
1920

@@ -339,7 +340,7 @@ export const TabBar = ({ paneId }: TabBarProps): React.JSX.Element => {
339340
onMouseEnter={() => setRefreshHover(true)}
340341
onMouseLeave={() => setRefreshHover(false)}
341342
onClick={handleRefresh}
342-
title="Refresh Session (Cmd+R)"
343+
title={`Refresh Session (${formatShortcut('R')})`}
343344
>
344345
<RefreshCw className="size-4" />
345346
</button>
@@ -376,7 +377,7 @@ export const TabBar = ({ paneId }: TabBarProps): React.JSX.Element => {
376377
color: searchHover ? 'var(--color-text)' : 'var(--color-text-muted)',
377378
backgroundColor: searchHover ? 'var(--color-surface-raised)' : 'transparent',
378379
}}
379-
title="Search (Cmd+K)"
380+
title={`Search (${formatShortcut('K')})`}
380381
>
381382
<Search className="size-4" />
382383
</button>

src/renderer/components/layout/TabContextMenu.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import { useEffect, useRef } from 'react';
99

10+
import { formatShortcut } from '@renderer/utils/stringUtils';
11+
1012
interface TabContextMenuProps {
1113
x: number;
1214
y: number;
@@ -100,13 +102,13 @@ export const TabContextMenu = ({
100102
onClick={handleClick(onCloseSelectedTabs)}
101103
/>
102104
) : (
103-
<MenuItem label="Close Tab" shortcut="⌘W" onClick={handleClick(onCloseTab)} />
105+
<MenuItem label="Close Tab" shortcut={formatShortcut('W')} onClick={handleClick(onCloseTab)} />
104106
)}
105107
<MenuItem label="Close Other Tabs" onClick={handleClick(onCloseOtherTabs)} />
106108
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
107109
<MenuItem
108110
label="Split Right"
109-
shortcut="⌘\"
111+
shortcut={formatShortcut('\\')}
110112
onClick={handleClick(onSplitRight)}
111113
disabled={disableSplit}
112114
/>
@@ -127,7 +129,7 @@ export const TabContextMenu = ({
127129
/>
128130
)}
129131
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
130-
<MenuItem label="Close All Tabs" shortcut="⇧⌘W" onClick={handleClick(onCloseAllTabs)} />
132+
<MenuItem label="Close All Tabs" shortcut={formatShortcut('W', { shift: true })} onClick={handleClick(onCloseAllTabs)} />
131133
</div>
132134
);
133135
};

src/renderer/components/sidebar/SessionContextMenu.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { useEffect, useRef, useState } from 'react';
88

99
import { MAX_PANES } from '@renderer/types/panes';
10+
import { formatShortcut } from '@renderer/utils/stringUtils';
1011
import { Check, ClipboardCopy, Eye, EyeOff, Pin, PinOff, Terminal } from 'lucide-react';
1112

1213
interface SessionContextMenuProps {
@@ -98,7 +99,7 @@ export const SessionContextMenu = ({
9899
}}
99100
>
100101
<MenuItem label="Open in Current Pane" onClick={handleClick(onOpenInCurrentPane)} />
101-
<MenuItem label="Open in New Tab" shortcut="⌘ Click" onClick={handleClick(onOpenInNewTab)} />
102+
<MenuItem label="Open in New Tab" shortcut={`${formatShortcut('')}Click`} onClick={handleClick(onOpenInNewTab)} />
102103
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
103104
<MenuItem
104105
label="Split Right and Open"

src/renderer/utils/stringUtils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22
* String utilities for display formatting.
33
*/
44

5+
const isMacPlatform =
6+
typeof window !== 'undefined' && window.navigator.userAgent.includes('Macintosh');
7+
8+
/** Returns '⌘' on macOS, 'Ctrl' on Windows/Linux. */
9+
export const modKey = isMacPlatform ? '⌘' : 'Ctrl+';
10+
11+
/** Returns '⇧' on macOS, 'Shift+' on Windows/Linux. */
12+
export const shiftKey = isMacPlatform ? '⇧' : 'Shift+';
13+
14+
/**
15+
* Formats a keyboard shortcut for the current platform.
16+
* @example formatShortcut('R') → '⌘R' on Mac, 'Ctrl+R' on Windows/Linux
17+
* @example formatShortcut('W', { shift: true }) → '⇧⌘W' on Mac, 'Ctrl+Shift+W' on Windows/Linux
18+
*/
19+
export function formatShortcut(key: string, opts?: { shift?: boolean }): string {
20+
if (opts?.shift) {
21+
return isMacPlatform ? `${shiftKey}${modKey}${key}` : `${modKey}${shiftKey}${key}`;
22+
}
23+
return `${modKey}${key}`;
24+
}
25+
526
/**
627
* Truncates a string in the middle to preserve both the beginning and end.
728
* Useful for branch names where the unique identifier is often at the end.

0 commit comments

Comments
 (0)