Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,314 changes: 1,114 additions & 200 deletions CLI/cmux_open.swift

Large diffs are not rendered by default.

778 changes: 764 additions & 14 deletions Resources/Localizable.xcstrings

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Sources/App/ShortcutBareStartRouting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum KeyboardShortcutBareStartCache {
let resolvedKeys = Set(
KeyboardShortcutSettings.Action.allCases.compactMap { action -> String? in
guard action != .showHideAllWindows else { return nil }
guard !action.isBrowserContentShortcut else { return nil }
return KeyboardShortcutSettings.shortcut(for: action).bareShortcutStartKey
}
)
Expand Down
2 changes: 2 additions & 0 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11843,6 +11843,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
// orphaned, breaking that keystroke for the focused terminal/browser
// input.
guard action != .showHideAllWindows && action != .globalSearch else { return false }
guard !action.isBrowserContentShortcut else { return false }
return KeyboardShortcutSettings.shortcut(for: action).hasChord
}
}
Expand Down Expand Up @@ -14353,6 +14354,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
fileprivate func shouldForwardBrowserSurfaceShortcutToTerminal(_ event: NSEvent) -> Bool {
return KeyboardShortcutSettings.Action.allCases.contains {
$0.shortcutContext == .browserPanel &&
!$0.isBrowserContentShortcut &&
matchConfiguredShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: $0))
}
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/KeyboardShortcutContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ extension KeyboardShortcutSettings.Action {

var shortcutContext: ShortcutContext {
switch self {
case .diffViewerScrollDown,
.diffViewerScrollUp,
.diffViewerScrollToBottom,
.diffViewerScrollToTop,
.diffViewerOpenFileSearch:
return .browserPanel
case .switchRightSidebarToFiles, .switchRightSidebarToFind, .switchRightSidebarToSessions, .switchRightSidebarToFeed, .switchRightSidebarToDock:
return .rightSidebarFocus
case .renameTab, .renameWorkspace:
Expand Down
8 changes: 7 additions & 1 deletion Sources/KeyboardShortcutRecorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct KeyboardShortcutRecorder: View {
var onUndoButtonPressed: (() -> Void)? = nil
var hasPendingRejection: Bool = false
var isDisabled: Bool = false
var firstStrokeRequiresModifier: Bool = true
var onRecordingChanged: (Bool) -> Void = { _ in }
var onRecorderFeedbackChanged: (ShortcutRecorderRejectedAttempt?) -> Void = { _ in }
@State private var isRecording = false
Expand All @@ -40,6 +41,7 @@ struct KeyboardShortcutRecorder: View {
shortcut: $shortcut,
isRecording: $isRecording,
hasPendingRejection: hasPendingRejection,
firstStrokeRequiresModifier: firstStrokeRequiresModifier,
displayString: displayString,
transformRecordedShortcut: transformRecordedShortcut,
onRecordingChanged: onRecordingChanged,
Expand Down Expand Up @@ -129,6 +131,7 @@ private struct ShortcutRecorderButton: NSViewRepresentable {
@Binding var shortcut: StoredShortcut
@Binding var isRecording: Bool
var hasPendingRejection: Bool = false
var firstStrokeRequiresModifier: Bool = true
let displayString: (StoredShortcut) -> String
let transformRecordedShortcut: (StoredShortcut) -> KeyboardShortcutSettings.RecordedShortcutResolution
let onRecordingChanged: (Bool) -> Void
Expand All @@ -138,6 +141,7 @@ private struct ShortcutRecorderButton: NSViewRepresentable {
let button = ShortcutRecorderNSButton()
button.shortcut = shortcut
button.displayString = displayString
button.firstStrokeRequiresModifier = firstStrokeRequiresModifier
button.transformRecordedShortcut = transformRecordedShortcut
button.onShortcutRecorded = { newShortcut in
shortcut = newShortcut
Expand All @@ -155,6 +159,7 @@ private struct ShortcutRecorderButton: NSViewRepresentable {
func updateNSView(_ nsView: ShortcutRecorderNSButton, context: Context) {
nsView.shortcut = shortcut
nsView.displayString = displayString
nsView.firstStrokeRequiresModifier = firstStrokeRequiresModifier
nsView.transformRecordedShortcut = transformRecordedShortcut
nsView.onRecordingChanged = { recording in
isRecording = recording
Expand Down Expand Up @@ -182,6 +187,7 @@ final class ShortcutRecorderNSButton: NSButton {
var transformRecordedShortcut: (StoredShortcut) -> KeyboardShortcutSettings.RecordedShortcutResolution = {
.accepted($0)
}
var firstStrokeRequiresModifier = true
var onShortcutRecorded: ((StoredShortcut) -> Void)?
var onRecordingChanged: ((Bool) -> Void)?
var onRecorderFeedbackChanged: ((ShortcutRecorderRejectedAttempt?) -> Void)?
Expand Down Expand Up @@ -308,7 +314,7 @@ final class ShortcutRecorderNSButton: NSButton {
}

if pendingChordStart == nil {
switch ShortcutStroke.recordingResult(from: event, requireModifier: true) {
switch ShortcutStroke.recordingResult(from: event, requireModifier: firstStrokeRequiresModifier) {
case let .accepted(firstStroke):
let firstShortcut = StoredShortcut(first: firstStroke)
switch transformRecordedShortcut(firstShortcut) {
Expand Down
57 changes: 53 additions & 4 deletions Sources/KeyboardShortcutSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ enum KeyboardShortcutSettings {
case toggleBrowserDeveloperTools
case showBrowserJavaScriptConsole
case toggleReactGrab
case diffViewerScrollDown
case diffViewerScrollUp
case diffViewerScrollToBottom
case diffViewerScrollToTop
case diffViewerOpenFileSearch

var id: String { rawValue }

Expand Down Expand Up @@ -230,6 +235,11 @@ enum KeyboardShortcutSettings {
case .toggleBrowserDeveloperTools: return String(localized: "shortcut.toggleBrowserDevTools.label", defaultValue: "Toggle Browser Developer Tools")
case .showBrowserJavaScriptConsole: return String(localized: "shortcut.showBrowserJSConsole.label", defaultValue: "Show Browser JavaScript Console")
case .toggleReactGrab: return String(localized: "shortcut.toggleReactGrab.label", defaultValue: "Toggle React Grab")
case .diffViewerScrollDown: return String(localized: "shortcut.diffViewerScrollDown.label", defaultValue: "Diff Viewer: Scroll Down")
case .diffViewerScrollUp: return String(localized: "shortcut.diffViewerScrollUp.label", defaultValue: "Diff Viewer: Scroll Up")
case .diffViewerScrollToBottom: return String(localized: "shortcut.diffViewerScrollToBottom.label", defaultValue: "Diff Viewer: Scroll to Bottom")
case .diffViewerScrollToTop: return String(localized: "shortcut.diffViewerScrollToTop.label", defaultValue: "Diff Viewer: Scroll to Top")
case .diffViewerOpenFileSearch: return String(localized: "shortcut.diffViewerOpenFileSearch.label", defaultValue: "Diff Viewer: Open File Search")
}
}

Expand Down Expand Up @@ -419,6 +429,19 @@ enum KeyboardShortcutSettings {
return StoredShortcut(key: "c", command: true, shift: false, option: true, control: false)
case .toggleReactGrab:
return StoredShortcut(key: "g", command: true, shift: true, option: false, control: false)
case .diffViewerScrollDown:
return StoredShortcut(key: "j", command: false, shift: false, option: false, control: false)
case .diffViewerScrollUp:
return StoredShortcut(key: "k", command: false, shift: false, option: false, control: false)
case .diffViewerScrollToBottom:
return StoredShortcut(key: "g", command: false, shift: true, option: false, control: false)
case .diffViewerScrollToTop:
return StoredShortcut(
first: ShortcutStroke(key: "g", command: false, shift: false, option: false, control: false),
second: ShortcutStroke(key: "g", command: false, shift: false, option: false, control: false)
)
case .diffViewerOpenFileSearch:
return StoredShortcut(key: "/", command: false, shift: false, option: false, control: false)
}
}

Expand All @@ -435,6 +458,32 @@ enum KeyboardShortcutSettings {
}
}

var allowsBareFirstStroke: Bool {
switch self {
case .diffViewerScrollDown,
.diffViewerScrollUp,
.diffViewerScrollToBottom,
.diffViewerScrollToTop,
.diffViewerOpenFileSearch:
return true
default:
return false
}
}

var isBrowserContentShortcut: Bool {
switch self {
case .diffViewerScrollDown,
.diffViewerScrollUp,
.diffViewerScrollToBottom,
.diffViewerScrollToTop,
.diffViewerOpenFileSearch:
return true
default:
return false
}
}

func displayedShortcutString(for shortcut: StoredShortcut) -> String {
if shortcut.isUnbound {
return shortcut.displayString
Expand Down Expand Up @@ -2293,14 +2342,14 @@ extension ShortcutStroke {
}

extension StoredShortcut {
static func parseConfig(_ rawValue: String) -> StoredShortcut? {
static func parseConfig(_ rawValue: String, allowBareFirstStroke: Bool = false) -> StoredShortcut? {
if isUnboundConfigToken(rawValue) {
return .unbound
}
return parseConfig(strokes: [rawValue])
return parseConfig(strokes: [rawValue], allowBareFirstStroke: allowBareFirstStroke)
}

static func parseConfig(strokes: [String]) -> StoredShortcut? {
static func parseConfig(strokes: [String], allowBareFirstStroke: Bool = false) -> StoredShortcut? {
guard !strokes.isEmpty, strokes.count <= 2 else { return nil }
if strokes.count == 1, let rawValue = strokes.first, isUnboundConfigToken(rawValue) {
return .unbound
Expand All @@ -2309,7 +2358,7 @@ extension StoredShortcut {
guard parsedStrokes.count == strokes.count, let firstStroke = parsedStrokes.first else {
return nil
}
guard !firstStroke.modifierFlags.isEmpty || firstStroke.key == "space" else { return nil }
guard allowBareFirstStroke || !firstStroke.modifierFlags.isEmpty || firstStroke.key == "space" else { return nil }
let secondStroke = parsedStrokes.count == 2 ? parsedStrokes[1] : nil
return StoredShortcut(first: firstStroke, second: secondStroke)
}
Expand Down
1 change: 1 addition & 0 deletions Sources/KeyboardShortcutSettingsControls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ struct ShortcutRecorderSettingsControl: View {
onUndoButtonPressed: rejectedAttempt != nil ? { rejectedAttempt = nil } : nil,
hasPendingRejection: rejectedAttempt != nil,
isDisabled: isDisabled,
firstStrokeRequiresModifier: !action.allowsBareFirstStroke,
onRecorderFeedbackChanged: { rejectedAttempt = $0 }
)
.onChange(of: shortcut) { _, _ in
Expand Down
9 changes: 7 additions & 2 deletions Sources/KeyboardShortcutSettingsFileStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -981,9 +981,14 @@ final class CmuxSettingsFileStore {
) -> StoredShortcut? {
let shortcut: StoredShortcut? = {
if rawValue is NSNull { return .unbound }
if let stroke = jsonString(rawValue) { return StoredShortcut.parseConfig(stroke) }
if let stroke = jsonString(rawValue) {
return StoredShortcut.parseConfig(stroke, allowBareFirstStroke: action.allowsBareFirstStroke)
}
if let strokes = jsonStringArray(rawValue) {
return strokes.isEmpty ? .unbound : StoredShortcut.parseConfig(strokes: strokes)
return strokes.isEmpty ? .unbound : StoredShortcut.parseConfig(
strokes: strokes,
allowBareFirstStroke: action.allowsBareFirstStroke
)
}
return nil
}()
Expand Down
Loading
Loading