Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ A simple shortcut menu for macOS
1. Download [Shuttle](http://fitztrev.github.io/shuttle/)
2. Copy to Applications

This fork also supports Ghostty.app. Set `"terminal": "Ghostty.app"` in your Shuttle JSON settings to use it.

## Help
See the [Wiki](https://github.com/fitztrev/shuttle/wiki) pages.

Expand Down
12 changes: 12 additions & 0 deletions Shuttle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */; };
A124BA1A1D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */; };
A124BA1B1D45572B00218F2F /* iTerm2-stable-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */; };
A124BA1C1D45572B00218F2F /* ghostty-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA1F1D45572B00218F2F /* ghostty-current-window.scpt */; };
A124BA1D1D45572B00218F2F /* ghostty-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA201D45572B00218F2F /* ghostty-new-tab-default.scpt */; };
A124BA1E1D45572B00218F2F /* ghostty-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA211D45572B00218F2F /* ghostty-new-window.scpt */; };
A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */; };
A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */; };
A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */; };
Expand Down Expand Up @@ -48,6 +51,9 @@
A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-current-window.scpt"; sourceTree = "<group>"; };
A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-new-tab-default.scpt"; sourceTree = "<group>"; };
A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-new-window.scpt"; sourceTree = "<group>"; };
A124BA1F1D45572B00218F2F /* ghostty-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ghostty-current-window.scpt"; sourceTree = "<group>"; };
A124BA201D45572B00218F2F /* ghostty-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ghostty-new-tab-default.scpt"; sourceTree = "<group>"; };
A124BA211D45572B00218F2F /* ghostty-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ghostty-new-window.scpt"; sourceTree = "<group>"; };
A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-current-window.scpt"; sourceTree = "<group>"; };
A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-tab-default.scpt"; sourceTree = "<group>"; };
A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-window.scpt"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -120,6 +126,9 @@
A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */,
A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */,
A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */,
A124BA1F1D45572B00218F2F /* ghostty-current-window.scpt */,
A124BA201D45572B00218F2F /* ghostty-new-tab-default.scpt */,
A124BA211D45572B00218F2F /* ghostty-new-window.scpt */,
A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */,
A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */,
A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */,
Expand Down Expand Up @@ -256,6 +265,7 @@
buildActionMask = 2147483647;
files = (
A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */,
A124BA1C1D45572B00218F2F /* ghostty-current-window.scpt in Resources */,
A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */,
A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */,
A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */,
Expand All @@ -267,12 +277,14 @@
0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */,
0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */,
A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */,
A124BA1D1D45572B00218F2F /* ghostty-new-tab-default.scpt in Resources */,
A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */,
C149EC0E15D5214600B1F558 /* Credits.rtf in Resources */,
A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */,
C149EC1415D5214600B1F558 /* MainMenu.xib in Resources */,
A124BA1B1D45572B00218F2F /* iTerm2-stable-new-window.scpt in Resources */,
A124BA1A1D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt in Resources */,
A124BA1E1D45572B00218F2F /* ghostty-new-window.scpt in Resources */,
C159DC2815D5DE8000F5DE24 /* shuttle.icns in Resources */,
A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */,
7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */,
Expand Down
22 changes: 21 additions & 1 deletion Shuttle/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ - (void) openHost:(NSMenuItem *) sender {
NSString *terminalNewWindow = [[NSBundle mainBundle] pathForResource:@"terminal-new-window" ofType:@"scpt"];
NSString *terminalCurrentWindow = [[NSBundle mainBundle] pathForResource:@"terminal-current-window" ofType:@"scpt"];
NSString *terminalNewTabDefault = [[NSBundle mainBundle] pathForResource:@"terminal-new-tab-default" ofType:@"scpt"];

//Set Paths to Ghostty AppleScripts
NSString *ghosttyNewWindow = [[NSBundle mainBundle] pathForResource:@"ghostty-new-window" ofType:@"scpt"];
NSString *ghosttyCurrentWindow = [[NSBundle mainBundle] pathForResource:@"ghostty-current-window" ofType:@"scpt"];
NSString *ghosttyNewTabDefault = [[NSBundle mainBundle] pathForResource:@"ghostty-new-tab-default" ofType:@"scpt"];

//Set Path to virtual with screen AppleScripts
NSString *terminalVirtualWithScreen = [[NSBundle mainBundle] pathForResource:@"virtual-with-screen" ofType:@"scpt"];
Expand All @@ -564,11 +569,26 @@ - (void) openHost:(NSMenuItem *) sender {
passParameters = @[escapedObject, terminalTitle];
}
// Check if Url
if (url)
if (url && [url scheme])
{
[[NSWorkspace sharedWorkspace] openURL:url];

}
//If the JSON file is set to use Ghostty
else if ( [terminalPref rangeOfString: @"ghostty"].location !=NSNotFound ) {
if ( [terminalWindow isEqualToString:@"new"] ) {
[self runScript:ghosttyNewWindow handler:handlerName parameters:passParameters];
}
if ( [terminalWindow isEqualToString:@"current"] ) {
[self runScript:ghosttyCurrentWindow handler:handlerName parameters:passParameters];
}
if ( [terminalWindow isEqualToString:@"tab"] ) {
[self runScript:ghosttyNewTabDefault handler:handlerName parameters:passParameters];
}
if ( [terminalWindow isEqualToString:@"virtual"] ) {
[self runScript:terminalVirtualWithScreen handler:handlerName parameters:passParameters];
}
}
//If the JSON file is set to use iTerm
else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound ) {

Expand Down
1 change: 1 addition & 0 deletions Shuttle/Shuttle.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<array>
<string>com.apple.terminal</string>
<string>com.googlecode.iterm2</string>
<string>com.mitchellh.ghostty</string>
</array>
</dict>
</plist>
Binary file added Shuttle/apple-scpt/ghostty-current-window.scpt
Binary file not shown.
Binary file added Shuttle/apple-scpt/ghostty-new-tab-default.scpt
Binary file not shown.
Binary file added Shuttle/apple-scpt/ghostty-new-window.scpt
Binary file not shown.
4 changes: 2 additions & 2 deletions Shuttle/shuttle.default.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"_comments": [
"Valid terminals include: 'Terminal.app' or 'iTerm'",
"Valid terminals include: 'Terminal.app', 'iTerm', or 'Ghostty.app'",
"In the editor value change 'default' to 'nano', 'vi', or another terminal based editor.",
"Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available",
"For more information on how to configure, please see http://fitztrev.github.io/shuttle/"
Expand Down Expand Up @@ -57,4 +57,4 @@
]
}



28 changes: 28 additions & 0 deletions apple-scripts/ghostty/ghostty-current-window.applescript
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--for testing uncomment the "on run" block
--on run
-- set argsCmd to "ps aux | grep [s]sh"
-- set argsTheme to "Homebrew"
-- set argsTitle to "Custom title"
-- scriptRun(argsCmd, argsTheme, argsTitle)
--end run

on scriptRun(argsCmd, argsTheme, argsTitle)
set withCmd to (argsCmd)
CommandRun(withCmd)
end scriptRun

on CommandRun(withCmd)
tell application "/Applications/Ghostty.app"
if it is not running or (count windows) is 0 then
set surfaceConfig to new surface configuration
set targetWindow to new window with configuration surfaceConfig
set targetTerminal to focused terminal of selected tab of targetWindow
else
set targetWindow to front window
set targetTerminal to focused terminal of selected tab of targetWindow
end if
input text withCmd to targetTerminal
send key "enter" to targetTerminal
focus targetTerminal
end tell
end CommandRun
29 changes: 29 additions & 0 deletions apple-scripts/ghostty/ghostty-new-tab-default.applescript
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--for testing uncomment the "on run" block
--on run
-- set argsCmd to "ps aux | grep [s]sh"
-- set argsTheme to "Homebrew"
-- set argsTitle to "Custom title"
-- scriptRun(argsCmd, argsTheme, argsTitle)
--end run

on scriptRun(argsCmd, argsTheme, argsTitle)
set withCmd to (argsCmd)
CommandRun(withCmd)
end scriptRun

on CommandRun(withCmd)
tell application "/Applications/Ghostty.app"
set surfaceConfig to new surface configuration
if it is not running or (count windows) is 0 then
set targetWindow to new window with configuration surfaceConfig
set targetTerminal to focused terminal of selected tab of targetWindow
else
set targetWindow to front window
set targetTab to new tab in targetWindow with configuration surfaceConfig
set targetTerminal to focused terminal of targetTab
end if
input text withCmd to targetTerminal
send key "enter" to targetTerminal
focus targetTerminal
end tell
end CommandRun
23 changes: 23 additions & 0 deletions apple-scripts/ghostty/ghostty-new-window.applescript
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--for testing uncomment the "on run" block
--on run
-- set argsCmd to "ps aux | grep [s]sh"
-- set argsTheme to "Homebrew"
-- set argsTitle to "Custom title"
-- scriptRun(argsCmd, argsTheme, argsTitle)
--end run

on scriptRun(argsCmd, argsTheme, argsTitle)
set withCmd to (argsCmd)
CommandRun(withCmd)
end scriptRun

on CommandRun(withCmd)
tell application "/Applications/Ghostty.app"
set surfaceConfig to new surface configuration
set newWindow to new window with configuration surfaceConfig
set newTerminal to focused terminal of selected tab of newWindow
input text withCmd to newTerminal
send key "enter" to newTerminal
focus newTerminal
end tell
end CommandRun
59 changes: 59 additions & 0 deletions tests/test_ghostty_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def read(path):
return (ROOT / path).read_text()


def test_default_config_mentions_ghostty():
config = read("Shuttle/shuttle.default.json")
assert "Ghostty.app" in config


def test_app_delegate_routes_ghostty_to_scripts():
app_delegate = read("Shuttle/AppDelegate.m")
assert 'rangeOfString: @"ghostty"' in app_delegate
assert 'pathForResource:@"ghostty-new-window"' in app_delegate
assert 'pathForResource:@"ghostty-current-window"' in app_delegate
assert 'pathForResource:@"ghostty-new-tab-default"' in app_delegate


def test_shell_commands_are_not_treated_as_urls():
app_delegate = read("Shuttle/AppDelegate.m")
assert "if (url && [url scheme])" in app_delegate


def test_ghostty_scripts_are_bundled_resources():
project = read("Shuttle.xcodeproj/project.pbxproj")
for script in [
"ghostty-new-window.scpt",
"ghostty-current-window.scpt",
"ghostty-new-tab-default.scpt",
]:
assert script in project


def test_ghostty_has_apple_event_entitlement():
entitlements = read("Shuttle/Shuttle.entitlements")
assert "com.mitchellh.ghostty" in entitlements


def test_ghostty_source_scripts_use_applescript_api():
for source in [
"apple-scripts/ghostty/ghostty-new-window.applescript",
"apple-scripts/ghostty/ghostty-current-window.applescript",
"apple-scripts/ghostty/ghostty-new-tab-default.applescript",
]:
script = read(source)
assert 'tell application "/Applications/Ghostty.app"' in script
assert "input text" in script
assert 'send key "enter"' in script


if __name__ == "__main__":
for name, value in sorted(globals().items()):
if name.startswith("test_") and callable(value):
value()