Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/cmd/dms/commands_matugen.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func init() {
cmd.Flags().Bool("terminals-always-dark", false, "Force terminal themes to dark variant")
cmd.Flags().String("skip-templates", "", "Comma-separated list of templates to skip")
cmd.Flags().Float64("contrast", 0, "Contrast value from -1 to 1 (0 = standard)")
cmd.Flags().String("source-mode", "", "Source color selection: dominant, colorful, darkness, lightness, saturation, less-saturation, value")
}

matugenQueueCmd.Flags().Bool("wait", true, "Wait for completion")
Expand All @@ -96,6 +97,7 @@ func buildMatugenOptions(cmd *cobra.Command) matugen.Options {
terminalsAlwaysDark, _ := cmd.Flags().GetBool("terminals-always-dark")
skipTemplates, _ := cmd.Flags().GetString("skip-templates")
contrast, _ := cmd.Flags().GetFloat64("contrast")
sourceMode, _ := cmd.Flags().GetString("source-mode")

return matugen.Options{
StateDir: stateDir,
Expand All @@ -112,6 +114,7 @@ func buildMatugenOptions(cmd *cobra.Command) matugen.Options {
SyncModeWithPortal: syncModeWithPortal,
TerminalsAlwaysDark: terminalsAlwaysDark,
SkipTemplates: skipTemplates,
SourceMode: sourceMode,
}
}

Expand Down Expand Up @@ -149,6 +152,7 @@ func runMatugenQueue(cmd *cobra.Command, args []string) {
"terminalsAlwaysDark": opts.TerminalsAlwaysDark,
"skipTemplates": opts.SkipTemplates,
"contrast": opts.Contrast,
"sourceMode": opts.SourceMode,
"wait": wait,
},
}
Expand Down
1 change: 1 addition & 0 deletions core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.26.4

require (
github.com/AvengeMedia/dgop v0.2.4-0.20260808141551-f559f2f31d2a
github.com/Nadim147c/material/v3 v3.1.1
github.com/Wifx/gonetworkmanager/v2 v2.2.0
github.com/alecthomas/chroma/v2 v2.27.0
github.com/charmbracelet/bubbles v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions core/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ github.com/AvengeMedia/dgop v0.2.4-0.20260808141551-f559f2f31d2a h1:bGwK4G22ixHU
github.com/AvengeMedia/dgop v0.2.4-0.20260808141551-f559f2f31d2a/go.mod h1:2kt1u9YgHwi2npqB5ago8kwZdFrHlPGXv3PM/vwRHfY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nadim147c/material/v3 v3.1.1 h1:3ZuTPWBJWUpzzWW1rtqe28RMZXhA4f0/mg/Jk3JG94Y=
github.com/Nadim147c/material/v3 v3.1.1/go.mod h1:ThN+dRdC+CDN2qXSGMn32KqtcXwYtb/trDM82cdZI6g=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/Wifx/gonetworkmanager/v2 v2.2.0 h1:kPstgsQtY8CmDOOFZd81ytM9Gi3f6ImzPCKF7nNhQ2U=
Expand Down
84 changes: 65 additions & 19 deletions core/internal/matugen/matugen.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ func (c *ColorMode) GTKTheme() string {
}

var (
matugenVersionMu sync.Mutex
matugenVersionOK bool
matugenSupportsCOE bool
matugenIsV4 bool
matugenVersionMu sync.Mutex
matugenVersionOK bool
matugenSupportsCOE bool
matugenIsV4 bool
matugenSupportsPrefer bool
)

type Options struct {
Expand All @@ -106,6 +107,7 @@ type Options struct {
IconTheme string
MatugenType string
Contrast float64
SourceMode string
RunUserTemplates bool
ColorsOnly bool
StockColors string
Expand Down Expand Up @@ -303,6 +305,28 @@ func buildOnce(opts *Options) (bool, error) {
var primaryDark, primaryLight, surface string
var dank16JSON string
var importArgs []string
var sourceImage string

// Colorful mode resolves the seed here, before matugen is invoked at all,
// by rewriting the source to the extracted hex. Both the dry-run and the
Comment thread
Conava marked this conversation as resolved.
// real run below read opts.Kind/opts.Value, so one rewrite covers both and
// they cannot disagree about the seed. Extraction failure (a format
// image.Decode cannot read, an unreadable file) falls through to matugen's
// own extraction: this must never fail a theme build.
if opts.StockColors == "" && opts.Kind == "image" && opts.SourceMode == SourceModeColorful {
if seed, err := ExtractSourceColor(opts.Value); err != nil {
log.Warnf("Colorful source extraction failed for %s, using matugen's own: %v", opts.Value, err)
} else {
log.Infof("Colorful source color: %s -> %s", opts.Value, seed)
// matugen resolves {{image}} to an absolute path, so match it.
sourceImage = opts.Value
if abs, err := filepath.Abs(sourceImage); err == nil {
sourceImage = abs
}
opts.Kind = "hex"
opts.Value = seed
}
}

if opts.StockColors != "" {
log.Info("Using stock/custom theme colors with matugen base")
Expand All @@ -325,7 +349,7 @@ func buildOnce(opts *Options) (bool, error) {
args := []string{"color", "hex", primaryDark, "-m", string(opts.Mode), "-t", opts.MatugenType, "-c", cfgFile.Name()}
args = appendContrastArg(args, opts.Contrast)
args = append(args, importArgs...)
if err := runMatugen(args); err != nil {
if err := runMatugen(args, opts.SourceMode); err != nil {
return false, err
}
} else {
Expand All @@ -348,8 +372,7 @@ func buildOnce(opts *Options) (bool, error) {
}

dank16JSON = generateDank16Variants(primaryDark, primaryLight, surface, opts.Mode)
importData := fmt.Sprintf(`{"dank16": %s}`, dank16JSON)
importArgs = []string{"--import-json-string", importData}
importArgs = []string{"--import-json-string", buildImportData(dank16JSON, sourceImage)}

log.Infof("Running matugen %s with dank16 injection", opts.Kind)
var args []string
Expand All @@ -362,7 +385,7 @@ func buildOnce(opts *Options) (bool, error) {
args = append(args, "-m", string(opts.Mode), "-t", opts.MatugenType, "-c", cfgFile.Name())
args = appendContrastArg(args, opts.Contrast)
args = append(args, importArgs...)
if err := runMatugen(args); err != nil {
if err := runMatugen(args, opts.SourceMode); err != nil {
return false, err
}
}
Expand Down Expand Up @@ -425,6 +448,17 @@ func appendContrastArg(args []string, contrast float64) []string {
return append(args, "--contrast", strconv.FormatFloat(contrast, 'f', -1, 64))
}

// buildImportData is the JSON passed to matugen's --import-json-string. image is
// set only when the source was rewritten from a wallpaper to a hex color, where
// matugen leaves {{image}} unset and templates using it would render "Null".
func buildImportData(dank16JSON, image string) string {
if image == "" {
return fmt.Sprintf(`{"dank16": %s}`, dank16JSON)
}
path, _ := json.Marshal(image)
return fmt.Sprintf(`{"dank16": %s, "image": %s}`, dank16JSON, path)
}

func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error {
userConfigPath := filepath.Join(opts.ConfigDir, "matugen", "config.toml")

Expand Down Expand Up @@ -755,16 +789,17 @@ func extractTOMLSection(content, startMarker, endMarker string) string {
}

type matugenFlags struct {
supportsCOE bool
isV4 bool
supportsCOE bool
isV4 bool
supportsPrefer bool
}

func detectMatugenVersion() (matugenFlags, error) {
matugenVersionMu.Lock()
defer matugenVersionMu.Unlock()

if matugenVersionOK {
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
return matugenFlags{matugenSupportsCOE, matugenIsV4, matugenSupportsPrefer}, nil
}

return detectMatugenVersionLocked()
Expand All @@ -779,7 +814,8 @@ func redetectMatugenVersion(old matugenFlags) (matugenFlags, bool) {
if err != nil {
return old, false
}
changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4
changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4 ||
flags.supportsPrefer != old.supportsPrefer
return flags, changed
}

Expand Down Expand Up @@ -811,6 +847,9 @@ func detectMatugenVersionLocked() (matugenFlags, error) {

matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
matugenIsV4 = major >= 4
// --prefer landed in 4.1; 4.0.x has --source-color-index but not --prefer,
// and clap aborts on an unknown argument rather than ignoring it.
matugenSupportsPrefer = major > 4 || (major == 4 && minor >= 1)
matugenVersionOK = true

if matugenSupportsCOE {
Expand All @@ -819,28 +858,34 @@ func detectMatugenVersionLocked() (matugenFlags, error) {
if matugenIsV4 {
log.Debugf("Matugen %s detected: using v4 compatibility flags", versionStr)
}
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
if matugenIsV4 && !matugenSupportsPrefer {
log.Debugf("Matugen %s detected: --prefer unavailable, source modes fall back to the dominant color", versionStr)
}
return matugenFlags{matugenSupportsCOE, matugenIsV4, matugenSupportsPrefer}, nil
}

func buildMatugenArgs(baseArgs []string, flags matugenFlags) []string {
func buildMatugenArgs(baseArgs []string, flags matugenFlags, sourceMode string) []string {
args := make([]string, 0, len(baseArgs)+4)
if flags.supportsCOE {
args = append(args, "--continue-on-error")
}
args = append(args, baseArgs...)
// matugen 3 has neither flag. matugen 4's --source-color-index help notes
// "In earlier versions the default was 0", so omitting both on v3 gives the
// same seed the flag would have asked for.
if flags.isV4 {
args = append(args, "--source-color-index", "0")
args = append(args, sourceSelectionArgs(sourceMode, flags.supportsPrefer)...)
}
return args
}

func runMatugen(baseArgs []string) error {
func runMatugen(baseArgs []string, sourceMode string) error {
flags, err := detectMatugenVersion()
if err != nil {
return err
}

args := buildMatugenArgs(baseArgs, flags)
args := buildMatugenArgs(baseArgs, flags, sourceMode)
cmd := exec.Command("matugen", args...)
cmd.Env = utils.EnvWithUserBinPath(nil)
cmd.Stdout = os.Stdout
Expand All @@ -858,7 +903,7 @@ func runMatugen(baseArgs []string) error {
}

log.Warnf("Matugen version changed (v4: %v -> %v), retrying", flags.isV4, newFlags.isV4)
args = buildMatugenArgs(baseArgs, newFlags)
args = buildMatugenArgs(baseArgs, newFlags, sourceMode)
retryCmd := exec.Command("matugen", args...)
retryCmd.Env = utils.EnvWithUserBinPath(nil)
retryCmd.Stdout = os.Stdout
Expand Down Expand Up @@ -899,7 +944,8 @@ func execDryRun(opts *Options, flags matugenFlags) (string, error) {
baseArgs = append(baseArgs, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
baseArgs = appendContrastArg(baseArgs, opts.Contrast)
if flags.isV4 {
baseArgs = append(baseArgs, "--source-color-index", "0", "--old-json-output")
baseArgs = append(baseArgs, sourceSelectionArgs(opts.SourceMode, flags.supportsPrefer)...)
baseArgs = append(baseArgs, "--old-json-output")
}

cmd := exec.Command("matugen", baseArgs...)
Expand Down
Loading
Loading