appearance.go

 1package macos
 2
 3import (
 4	_ "embed"
 5	"fmt"
 6	"os"
 7	"os/exec"
 8	"path/filepath"
 9	"runtime"
10	"strings"
11)
12
13//go:embed appearance.swift
14var appearanceSwift string
15
16type MacOSAppearance struct {
17	DarkMode    bool
18	AccentColor string
19}
20
21// GetAppearance fetches the current macOS appearance (dark mode and accent color).
22func GetAppearance() (*MacOSAppearance, error) {
23	if runtime.GOOS != "darwin" {
24		return nil, fmt.Errorf("GetAppearance is only supported on macOS")
25	}
26
27	tmpDir, err := os.MkdirTemp("", "matcha-appearance")
28	if err != nil {
29		return nil, err
30	}
31	defer os.RemoveAll(tmpDir) //nolint:errcheck
32
33	swiftFile := filepath.Join(tmpDir, "appearance.swift")
34	if err := os.WriteFile(swiftFile, []byte(appearanceSwift), 0644); err != nil {
35		return nil, err
36	}
37
38	binFile := filepath.Join(tmpDir, "appearance")
39
40	// Compile
41	cmd := exec.Command("swiftc", swiftFile, "-o", binFile) //nolint:noctx
42	if out, err := cmd.CombinedOutput(); err != nil {
43		return nil, fmt.Errorf("failed to compile appearance helper: %w\n%s", err, string(out))
44	}
45
46	// Run
47	out, err := exec.Command(binFile).Output() //nolint:noctx
48	if err != nil {
49		return nil, fmt.Errorf("failed to run appearance helper: %w", err)
50	}
51
52	parts := strings.Fields(string(out))
53	if len(parts) < 2 {
54		return nil, fmt.Errorf("unexpected output from appearance helper: %s", string(out))
55	}
56
57	return &MacOSAppearance{
58		DarkMode:    parts[0] == "true",
59		AccentColor: parts[1],
60	}, nil
61}