feat(shortcuts): add rename-workspace keybind

This commit is contained in:
Brian Le 2026-02-22 18:38:34 -05:00
parent 1809b06867
commit 1824d1a8f1
No known key found for this signature in database
6 changed files with 70 additions and 0 deletions

View file

@ -114,6 +114,7 @@ Everything is scriptable through the CLI and socket API — create workspaces/ta
| ⌃ ⌘ ] | Next workspace |
| ⌃ ⌘ [ | Previous workspace |
| ⌘ ⇧ W | Close workspace |
| ⌘ ⇧ R | Rename workspace |
| ⌘ B | Toggle sidebar |
### Surfaces

View file

@ -1673,6 +1673,36 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return true
}
func promptRenameSelectedWorkspace() -> Bool {
guard let tabManager,
let tabId = tabManager.selectedTabId,
let tab = tabManager.tabs.first(where: { $0.id == tabId }) else {
NSSound.beep()
return false
}
let alert = NSAlert()
alert.messageText = "Rename Workspace"
alert.informativeText = "Enter a custom name for this workspace."
let input = NSTextField(string: tab.customTitle ?? tab.title)
input.placeholderString = "Workspace name"
input.frame = NSRect(x: 0, y: 0, width: 240, height: 22)
alert.accessoryView = input
alert.addButton(withTitle: "Rename")
alert.addButton(withTitle: "Cancel")
let alertWindow = alert.window
alertWindow.initialFirstResponder = input
DispatchQueue.main.async {
alertWindow.makeFirstResponder(input)
input.selectText(nil)
}
let response = alert.runModal()
guard response == .alertFirstButtonReturn else { return true }
tabManager.setCustomTitle(tabId: tab.id, title: input.stringValue)
return true
}
private func handleCustomShortcut(event: NSEvent) -> Bool {
// `charactersIgnoringModifiers` can be nil for some synthetic NSEvents and certain special keys.
// Most shortcuts below use keyCode fallbacks, so treat nil as "" rather than bailing out.
@ -1877,6 +1907,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return true
}
if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .renameWorkspace)) {
_ = promptRenameSelectedWorkspace()
return true
}
// Numeric shortcuts for specific sidebar tabs: Cmd+1-9 (9 = last workspace)
if flags == [.command],
let manager = tabManager,

View file

@ -17,6 +17,7 @@ enum KeyboardShortcutSettings {
case prevSurface
case nextSidebarTab
case prevSidebarTab
case renameWorkspace
case newSurface
// Panes / splits
@ -48,6 +49,7 @@ enum KeyboardShortcutSettings {
case .prevSurface: return "Previous Surface"
case .nextSidebarTab: return "Next Workspace"
case .prevSidebarTab: return "Previous Workspace"
case .renameWorkspace: return "Rename Workspace"
case .newSurface: return "New Surface"
case .focusLeft: return "Focus Pane Left"
case .focusRight: return "Focus Pane Right"
@ -73,6 +75,7 @@ enum KeyboardShortcutSettings {
case .triggerFlash: return "shortcut.triggerFlash"
case .nextSidebarTab: return "shortcut.nextSidebarTab"
case .prevSidebarTab: return "shortcut.prevSidebarTab"
case .renameWorkspace: return "shortcut.renameWorkspace"
case .focusLeft: return "shortcut.focusLeft"
case .focusRight: return "shortcut.focusRight"
case .focusUp: return "shortcut.focusUp"
@ -108,6 +111,8 @@ enum KeyboardShortcutSettings {
return StoredShortcut(key: "]", command: true, shift: false, option: false, control: true)
case .prevSidebarTab:
return StoredShortcut(key: "[", command: true, shift: false, option: false, control: true)
case .renameWorkspace:
return StoredShortcut(key: "r", command: true, shift: true, option: false, control: false)
case .focusLeft:
return StoredShortcut(key: "", command: true, shift: false, option: true, control: false)
case .focusRight:
@ -190,6 +195,7 @@ enum KeyboardShortcutSettings {
static func nextSidebarTabShortcut() -> StoredShortcut { shortcut(for: .nextSidebarTab) }
static func prevSidebarTabShortcut() -> StoredShortcut { shortcut(for: .prevSidebarTab) }
static func renameWorkspaceShortcut() -> StoredShortcut { shortcut(for: .renameWorkspace) }
static func focusLeftShortcut() -> StoredShortcut { shortcut(for: .focusLeft) }
static func focusRightShortcut() -> StoredShortcut { shortcut(for: .focusRight) }

View file

@ -478,6 +478,10 @@ struct cmuxApp: App {
(AppDelegate.shared?.tabManager ?? tabManager).selectPreviousTab()
}
Button("Rename Workspace…") {
_ = AppDelegate.shared?.promptRenameSelectedWorkspace()
}
Divider()
splitCommandButton(title: "Split Right", shortcut: splitRightMenuShortcut) {

View file

@ -318,6 +318,25 @@ final class BrowserDeveloperToolsShortcutDefaultsTests: XCTestCase {
}
}
final class WorkspaceRenameShortcutDefaultsTests: XCTestCase {
func testRenameWorkspaceShortcutDefaultsAndMetadata() {
XCTAssertEqual(KeyboardShortcutSettings.Action.renameWorkspace.label, "Rename Workspace")
XCTAssertEqual(KeyboardShortcutSettings.Action.renameWorkspace.defaultsKey, "shortcut.renameWorkspace")
let shortcut = KeyboardShortcutSettings.Action.renameWorkspace.defaultShortcut
XCTAssertEqual(shortcut.key, "r")
XCTAssertTrue(shortcut.command)
XCTAssertTrue(shortcut.shift)
XCTAssertFalse(shortcut.option)
XCTAssertFalse(shortcut.control)
}
func testShortcutDefaultsKeysRemainUnique() {
let keys = KeyboardShortcutSettings.Action.allCases.map(\.defaultsKey)
XCTAssertEqual(Set(keys).count, keys.count)
}
}
@MainActor
final class BrowserDeveloperToolsConfigurationTests: XCTestCase {
func testBrowserPanelEnablesInspectableWebViewAndDeveloperExtras() {

View file

@ -38,6 +38,11 @@ const CATEGORIES: ShortcutCategory[] = [
combos: [["⌘", "⇧", "W"]],
description: "Close workspace",
},
{
id: "ws-rename",
combos: [["⌘", "⇧", "R"]],
description: "Rename workspace",
},
],
},
{