feat(theme): add a selectable source color mode for wallpaper palettes - #3020
feat(theme): add a selectable source color mode for wallpaper palettes#3020Conava wants to merge 6 commits into
Conversation
DMS always seeds the palette from matugen's most dominant candidate (--source-color-index 0, hardcoded at both call sites). When a large muted region outvotes a smaller vivid one, a visibly warm wallpaper produces a cold palette: one image here goes to #8ad0ee where the warm subject is at hue 46. Adds a Source Color setting with seven values. Dominant is the default and is byte-identical to today. Colorful is computed in Go before matugen runs: quantize the image (Wu for the starting clusters, then a weighted k-means in Lab), score the result the way caelestia-cli does, and hand matugen the resulting hex. Its scoring walks a chroma/tone bar down from 20 until something clears it, which biases the pick toward a colorful, well-lit color rather than whichever muted color covers the most pixels. The remaining five map to matugen's --prefer. - core/internal/matugen/sourcecolor.go: extraction, scoring, and the one helper both matugen call sites now share - buildOnce resolves the seed once, before the StockColors branch, so the dry run and the real run cannot disagree about it - extraction failure logs a warning and falls through to matugen's own extraction; it never fails a theme build - --prefer arrived in matugen 4.1, so it is gated separately from the existing isV4 check; on 4.0.x those five modes degrade to the dominant color instead of aborting on an unknown argument - --source-mode on generate and queue, plus sourceMode on the socket handler - Source Color dropdown in Theme & Colors, defaulting to Dominant Adds github.com/Nadim147c/material/v3 (Apache-2.0, no dependencies) for HCT conversions, Wu quantization and the dislike analyzer. Its QuantizeCelebi is not used: it returns after one unconverged iteration and its Lab.DistanceSquared is a dot product rather than a distance, so it collapses most of an image into a single cluster.
There was a problem hiding this comment.
Pull request overview
Adds a new Source Color setting that lets users choose how the wallpaper seed color is selected for matugen-based palette generation, including a custom “Colorful” mode that computes a vivid seed color in-core and safely degrades on older matugen versions.
Changes:
- Adds UI + settings plumbing for
matugenSourceMode, and forwards it to the core via--source-modeonly when non-default. - Implements “Colorful” wallpaper seed extraction in Go (image sampling + quantization + scoring) and forwards matugen
--prefervalues via an allowlist with version-gated fallback. - Extends matugen invocation/version detection, plus adds unit tests and a new Go dependency (
github.com/Nadim147c/material/v3).
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| quickshell/translations/settings_search_index.json | Adds search metadata for the new “Source Color” setting. |
| quickshell/Modules/Settings/ThemeColorsTab.qml | Adds the Source Color dropdown UI and mode description display. |
| quickshell/Common/Theme.qml | Defines available source modes and conditionally appends --source-mode to matugen queue args. |
| quickshell/Common/SettingsData.qml | Persists matugenSourceMode and provides a setter used by the UI. |
| quickshell/Common/settings/SettingsSpec.js | Adds matugenSourceMode to the persisted settings spec with regen hook. |
| core/internal/server/matugen_handler.go | Plumbs sourceMode through the request handler into matugen options. |
| core/internal/matugen/sourcecolor.go | New implementation for “Colorful” source color extraction and --prefer allowlisting. |
| core/internal/matugen/matugen.go | Wires SourceMode into matugen arg building with version-gated --prefer behavior. |
| core/internal/matugen/matugen_test.go | Adds tests for source-mode arg selection and “Colorful” extraction behavior. |
| core/go.sum | Records checksums for the new material/v3 dependency. |
| core/go.mod | Adds github.com/Nadim147c/material/v3 dependency. |
| core/cmd/dms/commands_matugen.go | Adds the --source-mode CLI flag and forwards it through the queue request. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Two points from review on AvengeMedia#3020. The quantize comment pointed at a FOLLOWUPS file that does not exist in this repository. Replaced the reference with the measurement it was standing in for, so the claim is self-contained. setMatugenSourceMode called Theme.generateSystemThemesFromCurrentTheme() directly on top of the regenSystemThemes onChange hook that Spec.set already dispatches. Both feed a 100ms debounce so it was one generation either way, but the direct call was redundant and is gone.
Colorful resolves the seed itself and calls matugen with a hex source, which
leaves matugen's {{image}} unset: the builtin pywalfox template and any user
template using the keyword render the literal string "Null".
Pass the wallpaper path alongside dank16 in --import-json-string, as suggested
in review, so those templates keep working unedited. The path is made absolute
to match what matugen renders for {{image}} in the other modes.
Per-mode descriptions were seven of the fifteen new terms this branch adds, and five of them differed by a single adjective. Drop them: the dropdown row already describes what the setting does, and the docs cover each mode.
matugen_test.go: both sides added imports to the same block, kept both.
|
/claude review |
Claude reviewNo issues found. Checked: the Colorful extraction path (decode/downscale/quantize/score, determinism, empty/transparent/all-black/1px edge cases, no panics on nil population), the |
Description
DMS builds your whole color scheme out of a single color picked from the wallpaper. Today that's always the color covering the most pixels. Often this is a background color and not the color of the main subject of the image, for example a blue background from the sky. This can lead to many images resulting in a blue color palette.
There's no way to change that pick. This PR adds a Source Color setting in Theme & Colors that lets the user decide. DMS hands the picked color to matugen, the external tool that generates the actual scheme. Five of the options below are matugen's own, Colorful is not.
On one of the wallpapers below that's the difference between a
#8ad0eecold cyan accent and a#ffb690warm peach one, from the same image.The five matugen options map to its
--preferflag, through an exact-match allowlist so a hand-editedsettings.jsoncan't inject arbitrary arguments. Colorful has no matugen equivalent, so DMS computes it: the image is reduced to 128 representative colors, then each is scored on how much of the image it covers against how colorful it is. That scoring is inspired by caelestia-cli, which produces noticeably warmer palettes than DMS on the same images. Either way matugen receives a single color, so nothing downstream changes.Notes for review
--preferarrived in matugen 4.1. On older versions those five options fall back to Dominant instead of failing on an unknown flag. Colorful works on any matugen version, since matugen only ever sees a single colorWhy this doesn't just call the library's own image quantizer: its
QuantizeCelebiis broken: it stops after one unconverged pass, and its distance function returns a dot product rather than a distance, so "nearest color" is meaningless. On a 3840x2160 wallpaper it grouped 7,979,139 of 8,292,604 pixels into a single cluster. This PR does that step itself instead, with the reasoning in a comment at the call site.Type of change
Related issues
None.
Screenshots / video
Validation
54 theme generations through the built binary against a scratch config, matugen 4.1.0.
What each option produces, across six wallpapers. Accent color, dark mode,
scheme-tonal-spot:#8ad0ee#ffb690#8ad0ee#ffb690#ffb690#95cdf7#8ad0ee#81d3df#faba73#cdca75#fdb976#fdb976#91d5ac#cdca75#edb4eb#bec2ff#edb4eb#90cef4#b8c4ff#edb4eb#edb4eb#b9c3ff#bdc2ff#b9c3ff#9acbfa#9acbfa#f5b2e1#b9c3ff#edc06c#e7c26c#edc06c#89d6b8#edc06c#a2d399#a2d399#feb877#fcb974#ffb692#f1be6d#ffb692#f1be6d#ffb692Every option produced a complete palette on every wallpaper, nothing failed or came back empty. The seven collapse to 4-5 distinct results per image, because matugen's candidate list is short and several
--prefervalues land on the same candidate.Wallpapers 1 and 2 are the case this PR is for: Dominant picks a cold cyan, Colorful a warm peach or amber. On 4 to 6 nothing much moves. Across my own wallpaper collection of 207 images, which is whatever I happen to have rather than a set curated for this, the median hue shift between Dominant and Colorful is 7° on a 0-180° scale, with 14 wallpapers above 160°. It's a targeted fix for a specific failure, not a global repaint.
Which
--prefervalue lands closest to Colorful also changes per wallpaper (lightness, lightness, saturation, darkness, saturation, lightness), so it works as a per-image knob, not as one setting the user leaves alone.The default is inert. Five generations of one wallpaper, differing only in the Source Color setting, all produced a byte-identical colors file (sha256
d615235985ca): setting absent,dominant,"",nonsense, and--exec. The last two confirm a hand-edited settings value can't inject arguments into matugen's command line.Deterministic. The same image always produces the same color. This matters because DMS compares generated colors to decide whether to re-theme; a color that drifted between runs would re-theme the desktop on every wallpaper event.
Commands run:
Checklist
I18n.tr()with translator context, reusing existing terms where possiblemake fmt, added/updated tests,make testpasses, andgo mod tidyis cleanmake lint-qmlwith no new warnings