Files
Archduino/internal/theming/theming.go

98 lines
2.1 KiB
Go

// Package theming applies a selected colorscheme across starship, zellij,
// yazi, lazygit, and NvChad. Themes are discovered from embedded manifest
// files under assets/<id>/theme.yaml at package init time.
package theming
import (
"embed"
"fmt"
"io/fs"
"path/filepath"
"sort"
"gopkg.in/yaml.v3"
)
//go:embed assets
var assetsFS embed.FS
// Theme describes a colorscheme discovered under assets/<id>/theme.yaml.
type Theme struct {
ID string
DisplayName string
NvChadTheme string
}
type manifest struct {
DisplayName string `yaml:"display_name"`
NvChad string `yaml:"nvchad"`
}
var discovered []Theme
func init() {
themes, err := discover()
if err != nil {
// Package is embed-only; discovery failure is a programmer error,
// not runtime input. Panic so tests surface the mistake early.
panic(fmt.Errorf("theming: discover themes: %w", err))
}
discovered = themes
}
func discover() ([]Theme, error) {
entries, err := fs.ReadDir(assetsFS, "assets")
if err != nil {
return nil, err
}
var out []Theme
for _, e := range entries {
if !e.IsDir() {
continue
}
id := e.Name()
m, err := readManifest(id)
if err != nil {
return nil, fmt.Errorf("theme %q: %w", id, err)
}
out = append(out, Theme{ID: id, DisplayName: m.DisplayName, NvChadTheme: m.NvChad})
}
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
return out, nil
}
func readManifest(id string) (manifest, error) {
data, err := assetsFS.ReadFile(filepath.Join("assets", id, "theme.yaml"))
if err != nil {
return manifest{}, err
}
var m manifest
if err := yaml.Unmarshal(data, &m); err != nil {
return manifest{}, err
}
if m.DisplayName == "" {
return manifest{}, fmt.Errorf("empty display_name")
}
if m.NvChad == "" {
return manifest{}, fmt.Errorf("empty nvchad")
}
return m, nil
}
// List returns the themes sorted by ID.
func List() []Theme {
out := make([]Theme, len(discovered))
copy(out, discovered)
return out
}
// Get returns the theme with the given ID, if present.
func Get(id string) (Theme, bool) {
for _, t := range discovered {
if t.ID == id {
return t, true
}
}
return Theme{}, false
}