Feature: Terminal stay open and shortcuts

This commit is contained in:
Hariharan Mudaliar 2026-03-08 17:48:43 +05:30
commit 2085815ab5
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
7 changed files with 104 additions and 5 deletions

View file

@ -433,7 +433,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N;
@ -475,7 +475,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.0.0;
MARKETING_VERSION = 2.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.Ebullioscopic.Atoll.dev;
PRODUCT_NAME = Atoll;
PROVISIONING_PROFILE_SPECIFIER = "";
@ -505,7 +505,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N;
@ -547,7 +547,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.0.0;
MARKETING_VERSION = 2.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.Ebullioscopic.Atoll;
PRODUCT_NAME = Atoll;
PROVISIONING_PROFILE_SPECIFIER = "";

View file

@ -150,6 +150,7 @@ struct ContentView: View {
@State private var lastHapticTime: Date = Date()
@State private var hoverClickMonitor: Any?
@State private var hoverClickLocalMonitor: Any?
@State private var stickyTerminalClickMonitor: Any?
@State private var gestureProgress: CGFloat = .zero
@State private var skipGestureActiveDirection: MusicManager.SkipDirection?
@ -424,6 +425,9 @@ struct ContentView: View {
isHovering = false
}
}
if newState == .closed {
removeStickyTerminalClickMonitor()
}
}
.onChange(of: vm.isBatteryPopoverActive) { _, newPopoverState in
runAfter(0.1) {
@ -606,6 +610,7 @@ struct ContentView: View {
.onDisappear {
hoverTask?.cancel()
stopHoverClickMonitor()
removeStickyTerminalClickMonitor()
cancelMusicControlWindowSync()
hideMusicControlWindow()
cancelMusicControlVisibilityTimer()
@ -1586,6 +1591,23 @@ struct ContentView: View {
}
}
private func installStickyTerminalClickMonitor() {
guard stickyTerminalClickMonitor == nil else { return }
stickyTerminalClickMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDown]) { [weak vm] _ in
Task { @MainActor in
guard let vm, vm.notchState == .open else { return }
vm.close()
}
}
}
private func removeStickyTerminalClickMonitor() {
if let stickyTerminalClickMonitor {
NSEvent.removeMonitor(stickyTerminalClickMonitor)
self.stickyTerminalClickMonitor = nil
}
}
// MARK: - Hover Management
/// Handle hover state changes with debouncing
@ -1594,6 +1616,7 @@ struct ContentView: View {
if hovering {
startHoverClickMonitor()
removeStickyTerminalClickMonitor()
} else {
stopHoverClickMonitor()
}
@ -1642,6 +1665,12 @@ struct ContentView: View {
if self.vm.notchState == .open && !self.shouldPreventAutoClose() {
self.vm.close()
} else if self.vm.notchState == .open
&& Defaults[.terminalStickyMode]
&& self.coordinator.currentView == .terminal {
// Terminal sticky mode kept the notch open — install a
// global click monitor so a click outside closes it.
self.installStickyTerminalClickMonitor()
}
}
}
@ -1660,7 +1689,7 @@ struct ContentView: View {
}
private func shouldPreventAutoClose() -> Bool {
coordinator.firstLaunch || hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose
coordinator.firstLaunch || hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose || (Defaults[.terminalStickyMode] && coordinator.currentView == .terminal)
}
// Helper to prevent rapid haptic feedback

View file

@ -894,6 +894,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
ColorPickerPanelManager.shared.toggleColorPickerPanel()
}
KeyboardShortcuts.onKeyDown(for: .toggleTerminalTab) { [weak self] in
guard let self else { return }
guard Defaults[.enableShortcuts], Defaults[.enableTerminalFeature] else { return }
if vm.notchState == .closed {
vm.open()
coordinator.currentView = .terminal
} else {
if coordinator.currentView == .terminal {
vm.close()
} else {
coordinator.currentView = .terminal
}
}
}
KeyboardShortcuts.onKeyDown(for: .screenAssistantPanel) { [weak self] in
guard let self else { return }
guard Defaults[.enableShortcuts], Defaults[.enableScreenAssistant] else { return }
@ -920,6 +936,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
updateShortcut(.clipboardHistoryPanel, isEnabled: Defaults[.enableShortcuts] && Defaults[.enableClipboardManager])
updateShortcut(.colorPickerPanel, isEnabled: Defaults[.enableShortcuts] && Defaults[.enableColorPickerFeature])
updateShortcut(.screenAssistantPanel, isEnabled: Defaults[.enableShortcuts] && Defaults[.enableScreenAssistant])
updateShortcut(.toggleTerminalTab, isEnabled: Defaults[.enableShortcuts] && Defaults[.enableTerminalFeature])
}
@MainActor

View file

@ -13239,6 +13239,9 @@
"Just now" : {
"comment" : "A string indicating that an action was performed just recently.",
"isCommentAutoGenerated" : true
},
"Keep terminal open until clicked outside" : {
},
"Keyboard Backlight HUD" : {
"localizations" : {
@ -18201,6 +18204,9 @@
}
}
}
},
"Opens the terminal tab in the notch. Default is Ctrl+`. Only works when terminal feature is enabled." : {
},
"Option as Meta key" : {
@ -18648,6 +18654,9 @@
}
}
}
},
"Prevents the terminal from closing when the cursor accidentally leaves the notch area." : {
},
"Preview" : {
"localizations" : {
@ -23266,6 +23275,9 @@
"Terminal" : {
"comment" : "The title of the settings view related to the terminal.",
"isCommentAutoGenerated" : true
},
"Terminal feature is disabled" : {
},
"Terminal is disabled" : {
"comment" : "A message displayed when the terminal feature is disabled.",
@ -23849,6 +23861,10 @@
}
}
},
"Toggle Terminal Tab:" : {
"comment" : "Name for a keyboard shortcut that toggles the terminal tab in the notch.",
"isCommentAutoGenerated" : true
},
"Toggle the Dynamic Island open or closed from anywhere." : {
"comment" : "A description of the \"Toggle Navigation\" keyboard shortcut.",
"isCommentAutoGenerated" : true,

View file

@ -31,5 +31,6 @@ extension KeyboardShortcuts.Name {
static let increaseBacklight = Self("increaseBacklight", default: .init(.f2, modifiers: [.command]))
static let toggleSneakPeek = Self("toggleSneakPeek", default: .init(.h, modifiers: [.command, .shift]))
static let toggleNotchOpen = Self("toggleNotchOpen", default: .init(.i, modifiers: [.command, .shift]))
static let toggleTerminalTab = Self("toggleTerminalTab", default: .init(.backtick, modifiers: [.control]))
static let startDemoTimer = Self("startDemoTimer", default: .init(.t, modifiers: [.command, .shift]))
}

View file

@ -5473,6 +5473,29 @@ struct Shortcuts: View {
.font(.caption)
}
Section {
HStack {
VStack(alignment: .leading) {
KeyboardShortcuts.Recorder("Toggle Terminal Tab:", name: .toggleTerminalTab)
.disabled(!enableShortcuts || !Defaults[.enableTerminalFeature])
if !Defaults[.enableTerminalFeature] {
Text("Terminal feature is disabled")
.font(.caption)
.foregroundStyle(.secondary)
.padding(.top, 2)
}
}
Spacer()
}
} header: {
Text("Terminal")
} footer: {
Text("Opens the terminal tab in the notch. Default is Ctrl+`. Only works when terminal feature is enabled.")
.multilineTextAlignment(.trailing)
.foregroundStyle(.secondary)
.font(.caption)
}
Section {
HStack {
VStack(alignment: .leading) {
@ -7218,6 +7241,18 @@ struct TerminalSettings: View {
Section {
Defaults.Toggle("Enable terminal", key: .enableTerminalFeature)
.settingsHighlight(id: highlightID("Enable terminal"))
if enableTerminalFeature {
Defaults.Toggle(key: .terminalStickyMode) {
VStack(alignment: .leading, spacing: 2) {
Text("Keep terminal open until clicked outside")
Text("Prevents the terminal from closing when the cursor accidentally leaves the notch area.")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.settingsHighlight(id: highlightID("Keep terminal open"))
}
} header: {
Text("General")
} footer: {

View file

@ -926,6 +926,7 @@ extension Defaults.Keys {
static let terminalBackgroundColor = Key<Color>("terminalBackgroundColor", default: .black)
static let terminalForegroundColor = Key<Color>("terminalForegroundColor", default: .white)
static let terminalCursorColor = Key<Color>("terminalCursorColor", default: Color(.selectedControlColor))
static let terminalStickyMode = Key<Bool>("terminalStickyMode", default: false)
// MARK: Timer Feature
static let enableTimerFeature = Key<Bool>("enableTimerFeature", default: true)