Alias Cmd+Shift+R to rename-tab command palette

This commit is contained in:
Lawrence Chen 2026-02-25 04:30:44 -08:00
parent eaabaad3d3
commit 86bbe4c727
4 changed files with 84 additions and 12 deletions

View file

@ -4559,6 +4559,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return true
}
// Alias Cmd+Shift+R to the same rename-tab command palette flow as Cmd+R.
if normalizedFlags == [.command, .shift], (chars == "r" || event.keyCode == 15) {
return handleRenameTabCommandPaletteShortcut(
event: event,
commandPaletteTargetWindow: commandPaletteTargetWindow
)
}
if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .renameWorkspace)) {
_ = promptRenameSelectedWorkspace()
return true
@ -4629,13 +4637,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}
if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .renameTab)) {
// Keep Cmd+R browser reload behavior when a browser panel is focused.
if tabManager?.focusedBrowserPanel != nil {
return false
}
let targetWindow = commandPaletteTargetWindow ?? event.window ?? NSApp.keyWindow ?? NSApp.mainWindow
NotificationCenter.default.post(name: .commandPaletteRenameTabRequested, object: targetWindow)
return true
return handleRenameTabCommandPaletteShortcut(
event: event,
commandPaletteTargetWindow: commandPaletteTargetWindow
)
}
// Numeric shortcuts for specific sidebar tabs: Cmd+1-9 (9 = last workspace)
@ -4864,6 +4869,19 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return false
}
private func handleRenameTabCommandPaletteShortcut(
event: NSEvent,
commandPaletteTargetWindow: NSWindow?
) -> Bool {
// Keep Cmd+R browser reload behavior when a browser panel is focused.
if tabManager?.focusedBrowserPanel != nil {
return false
}
let targetWindow = commandPaletteTargetWindow ?? event.window ?? NSApp.keyWindow ?? NSApp.mainWindow
NotificationCenter.default.post(name: .commandPaletteRenameTabRequested, object: targetWindow)
return true
}
private func shouldSuppressSplitShortcutForTransientTerminalFocusState(direction: SplitDirection) -> Bool {
guard let tabManager,
let workspace = tabManager.selectedWorkspace,

View file

@ -125,7 +125,7 @@ enum KeyboardShortcutSettings {
case .renameTab:
return StoredShortcut(key: "r", command: true, shift: false, option: false, control: false)
case .renameWorkspace:
return StoredShortcut(key: "r", command: true, shift: true, option: false, control: false)
return StoredShortcut(key: "r", command: true, shift: false, option: false, control: true)
case .closeWorkspace:
return StoredShortcut(key: "w", command: true, shift: true, option: false, control: false)
case .focusLeft:

View file

@ -311,6 +311,60 @@ final class AppDelegateShortcutRoutingTests: XCTestCase {
XCTAssertTrue(appDelegate.tabManager === secondManager, "Shortcut routing should retarget active manager to event window")
}
func testCmdShiftRAliasesToRenameTabCommandPaletteRequest() {
guard let appDelegate = AppDelegate.shared else {
XCTFail("Expected AppDelegate.shared")
return
}
let windowId = appDelegate.createMainWindow()
defer {
closeWindow(withId: windowId)
KeyboardShortcutSettings.resetShortcut(for: .renameWorkspace)
}
guard let window = window(withId: windowId) else {
XCTFail("Expected test window")
return
}
KeyboardShortcutSettings.setShortcut(
StoredShortcut(key: "r", command: true, shift: false, option: true, control: false),
for: .renameWorkspace
)
let expectation = expectation(description: "Expected rename tab command palette notification")
var observedWindow: NSWindow?
let token = NotificationCenter.default.addObserver(
forName: .commandPaletteRenameTabRequested,
object: nil,
queue: nil
) { notification in
observedWindow = notification.object as? NSWindow
expectation.fulfill()
}
defer { NotificationCenter.default.removeObserver(token) }
guard let event = makeKeyDownEvent(
key: "r",
modifiers: [.command, .shift],
keyCode: 15, // kVK_ANSI_R
windowNumber: window.windowNumber
) else {
XCTFail("Failed to construct Cmd+Shift+R event")
return
}
#if DEBUG
XCTAssertTrue(appDelegate.debugHandleCustomShortcut(event: event))
#else
XCTFail("debugHandleCustomShortcut is only available in DEBUG")
#endif
wait(for: [expectation], timeout: 1.0)
XCTAssertEqual(observedWindow?.windowNumber, window.windowNumber)
}
func testCmdDigitDoesNotFallbackToOtherWindowWhenEventWindowContextIsMissing() {
guard let appDelegate = AppDelegate.shared else {
XCTFail("Expected AppDelegate.shared")

View file

@ -966,18 +966,18 @@ final class WorkspaceRenameShortcutDefaultsTests: XCTestCase {
let shortcut = KeyboardShortcutSettings.Action.renameWorkspace.defaultShortcut
XCTAssertEqual(shortcut.key, "r")
XCTAssertTrue(shortcut.command)
XCTAssertTrue(shortcut.shift)
XCTAssertFalse(shortcut.shift)
XCTAssertFalse(shortcut.option)
XCTAssertFalse(shortcut.control)
XCTAssertTrue(shortcut.control)
}
func testRenameWorkspaceShortcutConvertsToMenuShortcut() {
let shortcut = KeyboardShortcutSettings.Action.renameWorkspace.defaultShortcut
XCTAssertNotNil(shortcut.keyEquivalent)
XCTAssertTrue(shortcut.eventModifiers.contains(.command))
XCTAssertTrue(shortcut.eventModifiers.contains(.shift))
XCTAssertFalse(shortcut.eventModifiers.contains(.shift))
XCTAssertFalse(shortcut.eventModifiers.contains(.option))
XCTAssertFalse(shortcut.eventModifiers.contains(.control))
XCTAssertTrue(shortcut.eventModifiers.contains(.control))
}
func testCloseWorkspaceShortcutDefaultsAndMetadata() {