diff --git a/include/ghostty.h b/include/ghostty.h
index e66ce08ea..c64ce0160 100644
--- a/include/ghostty.h
+++ b/include/ghostty.h
@@ -508,6 +508,7 @@ typedef enum {
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL,
+ GHOSTTY_ACTION_TOGGLE_VISIBILITY,
GHOSTTY_ACTION_GOTO_TAB,
GHOSTTY_ACTION_GOTO_SPLIT,
GHOSTTY_ACTION_RESIZE_SPLIT,
diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift
index 5980b8d66..e926e380b 100644
--- a/macos/Sources/App/macOS/AppDelegate.swift
+++ b/macos/Sources/App/macOS/AppDelegate.swift
@@ -86,6 +86,11 @@ class AppDelegate: NSObject,
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
}
+ /// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually
+ /// brings each window one by one to the front. But at worst its off by one set of toggles and this
+ /// makes our logic very easy.
+ private var isVisible: Bool = true
+
override init() {
terminalManager = TerminalManager(ghostty)
updaterController = SPUStandardUpdaterController(
@@ -251,6 +256,7 @@ class AppDelegate: NSObject,
// Ghostty will validate as well but we can avoid creating an entirely new
// surface by doing our own validation here. We can also show a useful error
// this way.
+
var isDirectory = ObjCBool(true)
guard FileManager.default.fileExists(atPath: filename, isDirectory: &isDirectory) else { return false }
@@ -315,6 +321,7 @@ class AppDelegate: NSObject,
syncMenuShortcut(action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
syncMenuShortcut(action: "reset_font_size", menuItem: self.menuResetFontSize)
syncMenuShortcut(action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal)
+ syncMenuShortcut(action: "toggle_visibility", menuItem: self.menuQuickTerminal)
syncMenuShortcut(action: "inspector:toggle", menuItem: self.menuTerminalInspector)
syncMenuShortcut(action: "toggle_secure_input", menuItem: self.menuSecureInput)
@@ -564,4 +571,23 @@ class AppDelegate: NSObject,
self.menuQuickTerminal?.state = if (quickController.visible) { .on } else { .off }
}
+
+ /// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
+ @IBAction func toggleVisibility(_ sender: Any) {
+ // We only care about terminal windows.
+ for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) {
+ if isVisible {
+ window.orderOut(nil)
+ } else {
+ window.makeKeyAndOrderFront(nil)
+ }
+ }
+
+ // After bringing them all to front we make sure our app is active too.
+ if !isVisible {
+ NSApp.activate(ignoringOtherApps: true)
+ }
+
+ isVisible.toggle()
+ }
}
diff --git a/macos/Sources/App/macOS/MainMenu.xib b/macos/Sources/App/macOS/MainMenu.xib
index 63aae4c60..2982d6cfc 100644
--- a/macos/Sources/App/macOS/MainMenu.xib
+++ b/macos/Sources/App/macOS/MainMenu.xib
@@ -217,10 +217,16 @@
-