Skip to content

Commit c63162e

Browse files
darwinzsteipete
authored andcommitted
feat: add Ghostty terminal picker support
1 parent 4f0ac06 commit c63162e

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

Sources/CodexBar/StatusItemController+Actions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,9 +653,9 @@ extension StatusItemController: StatusItemMenuPersistentActionDelegate {
653653
func openTerminal(command: String) {
654654
let terminal = self.settings.terminalApp
655655

656-
if terminal == .iTerm, !terminal.isInstalled {
656+
if terminal != .terminal, !terminal.isInstalled {
657657
CodexBarLog.logger(LogCategories.terminal).warning(
658-
"iTerm is not installed, falling back to Terminal.app",
658+
"\(terminal.label) is not installed, falling back to Terminal.app",
659659
metadata: ["terminal": terminal.rawValue])
660660
Self.openTerminalInDefaultTerminal(command: command)
661661
return

Sources/CodexBar/TerminalApp.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ enum TerminalApp: String, CaseIterable, Identifiable {
55

66
case terminal
77
case iTerm
8+
case ghostty
89

910
var id: String {
1011
self.rawValue
@@ -14,13 +15,15 @@ enum TerminalApp: String, CaseIterable, Identifiable {
1415
switch self {
1516
case .terminal: "Terminal"
1617
case .iTerm: "iTerm"
18+
case .ghostty: "Ghostty"
1719
}
1820
}
1921

2022
var bundleIdentifier: String {
2123
switch self {
2224
case .terminal: "com.apple.Terminal"
2325
case .iTerm: "com.googlecode.iterm2"
26+
case .ghostty: "com.mitchellh.ghostty"
2427
}
2528
}
2629

@@ -112,6 +115,16 @@ enum TerminalApp: String, CaseIterable, Identifiable {
112115
end tell
113116
end tell
114117
"""
118+
case .ghostty:
119+
// Requires Ghostty 1.3.0+ (first release with AppleScript support). `initial input`
120+
// runs the command in the user's shell and keeps the window open afterward, matching
121+
// the Terminal and iTerm behavior; older Ghostty fails here and falls back to Terminal.
122+
"""
123+
tell application "Ghostty"
124+
activate
125+
new window with configuration {initial input:"\(escaped)" & linefeed}
126+
end tell
127+
"""
115128
}
116129
}
117130

Tests/CodexBarTests/TerminalAppTests.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ struct TerminalAppTests {
5050
}
5151

5252
@Test
53-
func `only two cases exist`() {
54-
#expect(TerminalApp.allCases.count == 2)
53+
func `only three cases exist`() {
54+
#expect(TerminalApp.allCases.count == 3)
5555
}
5656

5757
@Test
@@ -63,12 +63,20 @@ struct TerminalAppTests {
6363

6464
#expect(installed == [.terminal, .iTerm])
6565
#expect(TerminalApp.installed { _ in nil } == [.terminal])
66+
67+
let ghosttyURL = URL(fileURLWithPath: "/Applications/Ghostty.app")
68+
let withGhostty = TerminalApp.installed { bundleIdentifier in
69+
bundleIdentifier == TerminalApp.ghostty.bundleIdentifier ? ghosttyURL : nil
70+
}
71+
72+
#expect(withGhostty == [.terminal, .ghostty])
6673
}
6774

6875
@Test
6976
func `picker options preserve an unavailable persisted selection`() {
7077
#expect(TerminalApp.pickerOptions(selected: .terminal) { _ in nil } == [.terminal])
7178
#expect(TerminalApp.pickerOptions(selected: .iTerm) { _ in nil } == [.terminal, .iTerm])
79+
#expect(TerminalApp.pickerOptions(selected: .ghostty) { _ in nil } == [.terminal, .ghostty])
7280
}
7381

7482
@Test
@@ -121,10 +129,13 @@ struct TerminalAppTests {
121129
let command = #"echo "hello""#
122130
let terminalScript = TerminalApp.terminal.appleScript(command: command)
123131
let iTermScript = TerminalApp.iTerm.appleScript(command: command)
132+
let ghosttyScript = TerminalApp.ghostty.appleScript(command: command)
124133

125134
#expect(terminalScript.contains(#"tell application "Terminal""#))
126135
#expect(terminalScript.contains(#"do script "echo \"hello\"""#))
127136
#expect(iTermScript.contains(#"tell application "iTerm""#))
128137
#expect(iTermScript.contains(#"write text "echo \"hello\"""#))
138+
#expect(ghosttyScript.contains(#"tell application "Ghostty""#))
139+
#expect(ghosttyScript.contains(#"new window with configuration {initial input:"echo \"hello\"" & linefeed}"#))
129140
}
130141
}

0 commit comments

Comments
 (0)