Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
45fd80b418
130 files changed
+26739
No files matched your search
@@ -0,0 +1,100 @@
|
||||
package linkhandler
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// macOS apps: where a link was clicked (the frontmost app; the link
|
||||
// handler runs in the background, so it doesn't change it), and which app
|
||||
// a process's terminal belongs to. Elsewhere these know nothing.
|
||||
|
||||
// App is a running macOS app: its bundle path and process ID.
|
||||
type App struct {
|
||||
Bundle string
|
||||
PID int
|
||||
}
|
||||
|
||||
// FrontApp is the frontmost app.
|
||||
func FrontApp() App {
|
||||
if runtime.GOOS != "darwin" {
|
||||
return App{}
|
||||
}
|
||||
asn, err := exec.Command("lsappinfo", "front").Output()
|
||||
if err != nil {
|
||||
return App{}
|
||||
}
|
||||
out, err := exec.Command("lsappinfo", "info", "-only", "pid", "-only", "bundlepath", strings.TrimSpace(string(asn))).Output()
|
||||
if err != nil {
|
||||
return App{}
|
||||
}
|
||||
var a App
|
||||
for _, l := range strings.Split(string(out), "\n") {
|
||||
k, v, _ := strings.Cut(strings.TrimSpace(l), "=")
|
||||
switch strings.Trim(k, `"`) {
|
||||
case "pid":
|
||||
a.PID, _ = strconv.Atoi(v)
|
||||
case "LSBundlePath":
|
||||
a.Bundle = strings.Trim(v, `"`)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// HostApp is the app bundle among pid's ancestors (the terminal it runs
|
||||
// in), "" if none.
|
||||
func HostApp(pid int) string {
|
||||
if runtime.GOOS != "darwin" {
|
||||
return ""
|
||||
}
|
||||
for range 32 {
|
||||
out, err := exec.Command("ps", "-o", "ppid=,comm=", "-p", strconv.Itoa(pid)).Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
ppid, comm, _ := strings.Cut(strings.TrimSpace(string(out)), " ")
|
||||
// The innermost bundle: kitty's quick access terminal is one
|
||||
// inside kitty.app.
|
||||
if i := strings.LastIndex(comm, ".app/Contents/MacOS/"); i >= 0 {
|
||||
return comm[:i+len(".app")]
|
||||
}
|
||||
if pid, err = strconv.Atoi(ppid); err != nil || pid <= 1 {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsTerminal reports whether bundle is a terminal app.
|
||||
func IsTerminal(bundle string) bool {
|
||||
name := strings.ToLower(filepath.Base(bundle))
|
||||
for _, t := range []string{"kitty", "ghostty", "wezterm", "iterm", "terminal.app", "alacritty", "warp"} {
|
||||
if strings.Contains(name, t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDropdown reports whether bundle is a terminal shown only by a hotkey
|
||||
// (kitty's quick access terminal): activating it doesn't show it.
|
||||
func IsDropdown(bundle string) bool { return strings.Contains(bundle, "quick-access") }
|
||||
|
||||
// ShowDropdown shows a hidden dropdown terminal (it toggles: call it only
|
||||
// when the dropdown isn't frontmost).
|
||||
func ShowDropdown(bundle string) {
|
||||
if i := strings.Index(bundle, "kitty.app"); i >= 0 {
|
||||
kitten := bundle[:i+len("kitty.app")] + "/Contents/MacOS/kitten"
|
||||
_ = exec.Command(kitten, "quick-access-terminal").Start()
|
||||
}
|
||||
}
|
||||
|
||||
// Activate brings the app to the front.
|
||||
func Activate(bundle string) {
|
||||
if runtime.GOOS == "darwin" && bundle != "" {
|
||||
_ = exec.Command("open", "-a", bundle).Run()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user