Created
March 24, 2025 00:00
-
-
Save raylee/283c0299e32926334f3ff5bf7f3e280d to your computer and use it in GitHub Desktop.
ghost-rider rotates through user-defined options in a Ghostty config file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ghost-rider rotates options inside a Ghostty config file. | |
// It expects the config file to have a specific format, where options are defined | |
// in a comment line starting with "# gr: " followed by a comma-separated list of | |
// options, and the actual option line that follows it. The script will rotate | |
// the option to the next one in the list each time it is run. | |
// The specific option to rotate can be specified with the -opt flag. | |
// Invoking this with a hotkey manager such as skhd allows for easy | |
// cycling through options like themes or background opacity in ghostty. | |
// The script tells Ghostty to reload its configuration after modifying the file. | |
// The compiled version of this binary and the skhd helper will need to be added | |
// to the assistive access list in System Settings >> Privacy & Security >> Accessibility. | |
// Config for https://github.com/koekeishiya/skhd | |
// cmd + shift - p : ~/.local/bin/ghost-rider -opt theme | |
// cmd + shift - b : ~/.local/bin/ghost-rider -opt background-opacity | |
/* | |
Example ghostty config snippet: | |
# gr: 0.75,0.9,1.0,0.5,0.6 | |
background-opacity = 0.75 | |
# gr: Adwaita,Japanesque | |
theme = Japanesque | |
*/ | |
package main | |
import ( | |
"flag" | |
"fmt" | |
"os" | |
"strings" | |
"github.com/andybrewer/mack" | |
) | |
var ( | |
cfg = flag.String("fn", "~/.config/ghostty/config", "config file path") | |
opt = flag.String("opt", "theme", "option to rotate") | |
) | |
func main() { | |
flag.Parse() | |
// Expand the config file path, subsituting ~ with the home directory | |
homeDir, err := os.UserHomeDir() | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err) | |
os.Exit(1) | |
} | |
*cfg = strings.ReplaceAll(*cfg, "~", homeDir) | |
conf, err := os.ReadFile(*cfg) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error reading config file: %v\n", err) | |
os.Exit(1) | |
} | |
lines := strings.Split(string(conf), "\n") | |
// Example stanza from the ghostty config file: | |
// # gr: 0.75,0.9,1.0,0.5,0.6 | |
// background-opacity = 0.75 | |
// Look for the line that starts with the option. | |
for i := range len(lines) - 1 { | |
if !strings.HasPrefix(lines[i], "# gr: ") { | |
continue | |
} | |
opts := strings.Split(lines[i][6:], ",") | |
parts := strings.Split(lines[i+1], "=") | |
if len(parts) != 2 { | |
continue | |
} | |
parts[0], parts[1] = strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) | |
// check if the line before the comment is the option we want to rotate | |
if parts[0] != *opt { | |
// fmt.Fprintln(os.Stderr, "Skipping line:", lines[i-1]) | |
continue | |
} | |
for j, o := range opts { | |
if o == parts[1] { | |
// rotate to the next option | |
nextOpt := opts[(j+1)%len(opts)] | |
lines[i+1] = fmt.Sprintf("%s = %s", parts[0], nextOpt) | |
break | |
} | |
} | |
} | |
// Write the modified config back to the file | |
err = os.WriteFile(*cfg, []byte(strings.Join(lines, "\n")), 0o644) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error writing config file: %v\n", err) | |
os.Exit(1) | |
} | |
mack.Tell("System Events", | |
`tell process "Ghostty"`, | |
`click menu item "Reload Configuration" of menu "Ghostty" of menu bar item "Ghostty" of menu bar 1`, | |
`end tell`) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment