package main import ( "fmt" "log" "os" "regexp" "strings" ) var ( property = regexp.MustCompile(`^\s*--([a-z0-9-]+):\s*#([0-9a-f]{6});\s*$`) darkOpen = regexp.MustCompile(`^@media \(prefers-color-scheme: dark\) \{$`) ) func block(lines []string, start int) (map[string]string, []string) { colors := map[string]string{} order := []string{} for _, line := range lines[start+1:] { if strings.TrimSpace(line) == "}" { return colors, order } m := property.FindStringSubmatch(line) if m == nil { continue } colors[m[1]] = m[2] order = append(order, m[1]) } log.Fatalf("unterminated block at line %d", start+1) return nil, nil } func swiftName(prop string) string { parts := strings.Split(prop, "-") for i := 1; i < len(parts); i++ { parts[i] = strings.ToUpper(parts[i][:1]) + parts[i][1:] } return strings.Join(parts, "") } func main() { css, err := os.ReadFile("../web/style.css") if err != nil { log.Fatal(err) } lines := strings.Split(string(css), "\n") lightAt, darkAt := -1, -1 for i, line := range lines { if line == ":root {" && lightAt < 0 { lightAt = i } if darkOpen.MatchString(line) && i+1 < len(lines) && strings.TrimSpace(lines[i+1]) == ":root {" { darkAt = i + 1 } } if lightAt < 0 || darkAt < 0 { log.Fatal("web/style.css has no :root block or no prefers-color-scheme: dark :root block") } light, order := block(lines, lightAt) dark, _ := block(lines, darkAt) var b strings.Builder b.WriteString("// Code generated by go generate ./mac from web/style.css. DO NOT EDIT.\n\n") b.WriteString("import AppKit\nimport SwiftUI\n\n") b.WriteString("enum Palette {\n") for _, prop := range order { d, ok := dark[prop] if !ok { log.Fatalf("--%s has no dark value", prop) } fmt.Fprintf(&b, " static let %s = dynamic(0x%s, 0x%s)\n", swiftName(prop), light[prop], d) } for prop := range dark { if _, ok := light[prop]; !ok { log.Fatalf("--%s has no light value", prop) } } b.WriteString(` private static func rgb(_ hex: UInt32) -> NSColor { NSColor(srgbRed: CGFloat(hex >> 16 & 0xff) / 255, green: CGFloat(hex >> 8 & 0xff) / 255, blue: CGFloat(hex & 0xff) / 255, alpha: 1) } private static func dynamic(_ light: UInt32, _ dark: UInt32) -> Color { Color(nsColor: NSColor(name: nil) { appearance in appearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua ? rgb(dark) : rgb(light) }) } } `) err = os.WriteFile("Sources/Byre/Palette.swift", []byte(b.String()), 0o644) if err != nil { log.Fatal(err) } }