1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

compositor+matugen: border override, hypr/mango layout overrides, new

templates, respect XDG paths
- Add Hyprland and MangoWC templates
- Add GUI gaps, window radius, and border thickness overrides for niri,
  Hyprland, and MangoWC
- Add replacement support in matugen templates for DATA_DIR, CACHE_DIR,
  CONFIG_DIR
fixes #1274
fixes #1273
This commit is contained in:
bbedward
2026-01-05 11:25:13 -05:00
parent 4005a55bf2
commit 64310854a6
49 changed files with 1017 additions and 207 deletions

View File

@@ -18,3 +18,4 @@ This file is more of a quick reference so I know what to account for before next
- Notification persistence & history
- **BREAKING** vscode theme needs re-installed
- dms doctor cmd
- niri/hypr/mango gaps/window/border overrides

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/json"
"fmt"
"time"
@@ -29,9 +30,16 @@ var matugenQueueCmd = &cobra.Command{
Run: runMatugenQueue,
}
var matugenCheckCmd = &cobra.Command{
Use: "check",
Short: "Check which template apps are detected",
Run: runMatugenCheck,
}
func init() {
matugenCmd.AddCommand(matugenGenerateCmd)
matugenCmd.AddCommand(matugenQueueCmd)
matugenCmd.AddCommand(matugenCheckCmd)
for _, cmd := range []*cobra.Command{matugenGenerateCmd, matugenQueueCmd} {
cmd.Flags().String("state-dir", "", "State directory for cache files")
@@ -162,3 +170,12 @@ func runMatugenQueue(cmd *cobra.Command, args []string) {
log.Fatalf("Timeout waiting for theme generation")
}
}
func runMatugenCheck(cmd *cobra.Command, args []string) {
checks := matugen.CheckTemplates(nil)
data, err := json.Marshal(checks)
if err != nil {
log.Fatalf("Failed to marshal check results: %v", err)
}
fmt.Println(string(data))
}

View File

@@ -176,7 +176,7 @@ func (cd *ConfigDeployer) deployNiriConfig(terminal deps.Terminal, useSystemd bo
}
if existingConfig != "" {
mergedConfig, err := cd.mergeNiriOutputSections(newConfig, existingConfig)
mergedConfig, err := cd.mergeNiriOutputSections(newConfig, existingConfig, dmsDir)
if err != nil {
cd.log(fmt.Sprintf("Warning: Failed to merge output sections: %v", err))
} else {
@@ -209,6 +209,7 @@ func (cd *ConfigDeployer) deployNiriDmsConfigs(dmsDir, terminalCommand string) e
{"layout.kdl", NiriLayoutConfig},
{"alttab.kdl", NiriAlttabConfig},
{"binds.kdl", strings.ReplaceAll(NiriBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)},
{"outputs.kdl", ""},
}
for _, cfg := range configs {
@@ -421,24 +422,31 @@ func (cd *ConfigDeployer) deployAlacrittyConfig() ([]DeploymentResult, error) {
return results, nil
}
// mergeNiriOutputSections extracts output sections from existing config and merges them into the new config
func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig string) (string, error) {
// Regular expression to match output sections (including commented ones)
func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig, dmsDir string) (string, error) {
outputRegex := regexp.MustCompile(`(?m)^(/-)?\s*output\s+"[^"]+"\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`)
// Find all output sections in the existing config
existingOutputs := outputRegex.FindAllString(existingConfig, -1)
if len(existingOutputs) == 0 {
// No output sections to merge
return newConfig, nil
}
// Remove the example output section from the new config
outputsPath := filepath.Join(dmsDir, "outputs.kdl")
if _, err := os.Stat(outputsPath); err != nil {
var outputsContent strings.Builder
for _, output := range existingOutputs {
outputsContent.WriteString(output)
outputsContent.WriteString("\n\n")
}
if err := os.WriteFile(outputsPath, []byte(outputsContent.String()), 0644); err != nil {
cd.log(fmt.Sprintf("Warning: Failed to migrate outputs to %s: %v", outputsPath, err))
} else {
cd.log("Migrated output sections to dms/outputs.kdl")
}
}
exampleOutputRegex := regexp.MustCompile(`(?m)^/-output "eDP-2" \{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`)
mergedConfig := exampleOutputRegex.ReplaceAllString(newConfig, "")
// Find where to insert the output sections (after the input section)
inputEndRegex := regexp.MustCompile(`(?m)^}$`)
inputMatches := inputEndRegex.FindAllStringIndex(newConfig, -1)
@@ -446,7 +454,6 @@ func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig stri
return "", fmt.Errorf("could not find insertion point for output sections")
}
// Insert after the first closing brace (end of input section)
insertPos := inputMatches[0][1]
var builder strings.Builder
@@ -476,6 +483,12 @@ func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal, useSystem
return result, result.Error
}
dmsDir := filepath.Join(configDir, "dms")
if err := os.MkdirAll(dmsDir, 0755); err != nil {
result.Error = fmt.Errorf("failed to create dms directory: %w", err)
return result, result.Error
}
var existingConfig string
if _, err := os.Stat(result.Path); err == nil {
cd.log("Found existing Hyprland configuration")
@@ -515,7 +528,7 @@ func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal, useSystem
}
if existingConfig != "" {
mergedConfig, err := cd.mergeHyprlandMonitorSections(newConfig, existingConfig)
mergedConfig, err := cd.mergeHyprlandMonitorSections(newConfig, existingConfig, dmsDir)
if err != nil {
cd.log(fmt.Sprintf("Warning: Failed to merge monitor sections: %v", err))
} else {
@@ -529,13 +542,42 @@ func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal, useSystem
return result, result.Error
}
if err := cd.deployHyprlandDmsConfigs(dmsDir); err != nil {
result.Error = fmt.Errorf("failed to deploy dms configs: %w", err)
return result, result.Error
}
result.Deployed = true
cd.log("Successfully deployed Hyprland configuration")
return result, nil
}
// mergeHyprlandMonitorSections extracts monitor sections from existing config and merges them into the new config
func (cd *ConfigDeployer) mergeHyprlandMonitorSections(newConfig, existingConfig string) (string, error) {
func (cd *ConfigDeployer) deployHyprlandDmsConfigs(dmsDir string) error {
configs := []struct {
name string
content string
}{
{"colors.conf", HyprColorsConfig},
{"layout.conf", HyprLayoutConfig},
{"outputs.conf", ""},
}
for _, cfg := range configs {
path := filepath.Join(dmsDir, cfg.name)
if _, err := os.Stat(path); err == nil {
cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name))
continue
}
if err := os.WriteFile(path, []byte(cfg.content), 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", cfg.name, err)
}
cd.log(fmt.Sprintf("Deployed %s", cfg.name))
}
return nil
}
func (cd *ConfigDeployer) mergeHyprlandMonitorSections(newConfig, existingConfig, dmsDir string) (string, error) {
monitorRegex := regexp.MustCompile(`(?m)^#?\s*monitor\s*=.*$`)
existingMonitors := monitorRegex.FindAllString(existingConfig, -1)
@@ -543,6 +585,20 @@ func (cd *ConfigDeployer) mergeHyprlandMonitorSections(newConfig, existingConfig
return newConfig, nil
}
outputsPath := filepath.Join(dmsDir, "outputs.conf")
if _, err := os.Stat(outputsPath); err != nil {
var outputsContent strings.Builder
for _, monitor := range existingMonitors {
outputsContent.WriteString(monitor)
outputsContent.WriteString("\n")
}
if err := os.WriteFile(outputsPath, []byte(outputsContent.String()), 0644); err != nil {
cd.log(fmt.Sprintf("Warning: Failed to migrate monitors to %s: %v", outputsPath, err))
} else {
cd.log("Migrated monitor sections to dms/outputs.conf")
}
}
exampleMonitorRegex := regexp.MustCompile(`(?m)^# monitor = eDP-2.*$`)
mergedConfig := exampleMonitorRegex.ReplaceAllString(newConfig, "")

View File

@@ -161,7 +161,8 @@ layout {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := cd.mergeNiriOutputSections(tt.newConfig, tt.existingConfig)
tmpDir := t.TempDir()
result, err := cd.mergeNiriOutputSections(tt.newConfig, tt.existingConfig, tmpDir)
if tt.wantError {
assert.Error(t, err)
@@ -362,7 +363,8 @@ input {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := cd.mergeHyprlandMonitorSections(tt.newConfig, tt.existingConfig)
tmpDir := t.TempDir()
result, err := cd.mergeHyprlandMonitorSections(tt.newConfig, tt.existingConfig, tmpDir)
if tt.wantError {
assert.Error(t, err)
@@ -462,7 +464,6 @@ func TestHyprlandConfigStructure(t *testing.T) {
assert.Contains(t, HyprlandConfig, "# KEYBINDINGS")
assert.Contains(t, HyprlandConfig, "bind = $mod, T, exec, {{TERMINAL_COMMAND}}")
assert.Contains(t, HyprlandConfig, "bind = $mod, space, exec, dms ipc call spotlight toggle")
assert.Contains(t, HyprlandConfig, "windowrule = border_size 0, match:class ^(com\\.mitchellh\\.ghostty)$")
}
func TestGhosttyConfigStructure(t *testing.T) {

View File

@@ -0,0 +1,25 @@
# ! Auto-generated file. Do not edit directly.
# Remove source = ./dms/colors.conf from your config to override.
$primary = rgb(d0bcff)
$outline = rgb(948f99)
$error = rgb(f2b8b5)
general {
col.active_border = $primary
col.inactive_border = $outline
}
group {
col.border_active = $primary
col.border_inactive = $outline
col.border_locked_active = $error
col.border_locked_inactive = $outline
groupbar {
col.active = $primary
col.inactive = $outline
col.locked_active = $error
col.locked_inactive = $outline
}
}

View File

@@ -0,0 +1,11 @@
# Auto-generated by DMS - do not edit manually
general {
gaps_in = 4
gaps_out = 4
border_size = 2
}
decoration {
rounding = 12
}

View File

@@ -27,10 +27,7 @@ input {
general {
gaps_in = 5
gaps_out = 5
border_size = 0 # off in niri
col.active_border = rgba(707070ff)
col.inactive_border = rgba(d0d0d0ff)
border_size = 2
layout = dwindle
}
@@ -42,7 +39,7 @@ decoration {
rounding = 12
active_opacity = 1.0
inactive_opacity = 0.9
inactive_opacity = 1.0
shadow {
enabled = true
@@ -93,7 +90,6 @@ misc {
windowrule = tile on, match:class ^(org\.wezfurlong\.wezterm)$
windowrule = rounding 12, match:class ^(org\.gnome\.)
windowrule = border_size 0, match:class ^(org\.gnome\.)
windowrule = tile on, match:class ^(gnome-control-center)$
windowrule = tile on, match:class ^(pavucontrol)$
@@ -106,12 +102,6 @@ windowrule = float on, match:class ^(org\.gnome\.Nautilus)$
windowrule = float on, match:class ^(steam)$
windowrule = float on, match:class ^(xdg-desktop-portal)$
windowrule = border_size 0, match:class ^(org\.wezfurlong\.wezterm)$
windowrule = border_size 0, match:class ^(Alacritty)$
windowrule = border_size 0, match:class ^(zen)$
windowrule = border_size 0, match:class ^(com\.mitchellh\.ghostty)$
windowrule = border_size 0, match:class ^(kitty)$
windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$
windowrule = float on, match:class ^(zoom)$
@@ -281,3 +271,7 @@ bind = ALT, Print, exec, dms screenshot window
# === System Controls ===
bind = $mod SHIFT, P, dpms, toggle
source = ./dms/colors.conf
source = ./dms/outputs.conf
source = ./dms/layout.conf

View File

@@ -1,21 +1,19 @@
// ! DO NOT EDIT !
// ! AUTO-GENERATED BY DMS !
// ! CHANGES WILL BE OVERWRITTEN !
// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE !
// ! Auto-generated file. Do not edit directly.
// Remove `include "dms/colors.kdl"` from your config to override.
layout {
background-color "transparent"
focus-ring {
active-color "#9dcbfb"
inactive-color "#8c9199"
urgent-color "#ffb4ab"
active-color "#d0bcff"
inactive-color "#948f99"
urgent-color "#f2b8b5"
}
border {
active-color "#9dcbfb"
inactive-color "#8c9199"
urgent-color "#ffb4ab"
active-color "#d0bcff"
inactive-color "#948f99"
urgent-color "#f2b8b5"
}
shadow {
@@ -23,19 +21,19 @@ layout {
}
tab-indicator {
active-color "#9dcbfb"
inactive-color "#8c9199"
urgent-color "#ffb4ab"
active-color "#d0bcff"
inactive-color "#948f99"
urgent-color "#f2b8b5"
}
insert-hint {
color "#9dcbfb80"
color "#d0bcff80"
}
}
recent-windows {
highlight {
active-color "#124a73"
urgent-color "#ffb4ab"
active-color "#4f378b"
urgent-color "#f2b8b5"
}
}

View File

@@ -240,10 +240,6 @@ window-rule {
match app-id="kitty"
draw-border-with-background false
}
window-rule {
match is-active=false
opacity 0.9
}
window-rule {
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
match app-id="zoom"
@@ -273,3 +269,4 @@ include "dms/colors.kdl"
include "dms/layout.kdl"
include "dms/alttab.kdl"
include "dms/binds.kdl"
include "dms/outputs.kdl"

View File

@@ -4,3 +4,9 @@ import _ "embed"
//go:embed embedded/hyprland.conf
var HyprlandConfig string
//go:embed embedded/hypr-colors.conf
var HyprColorsConfig string
//go:embed embedded/hypr-layout.conf
var HyprLayoutConfig string

View File

@@ -23,6 +23,47 @@ const (
ColorModeLight ColorMode = "light"
)
type TemplateKind int
const (
TemplateKindNormal TemplateKind = iota
TemplateKindTerminal
TemplateKindGTK
TemplateKindVSCode
)
type TemplateDef struct {
ID string
Commands []string
Flatpaks []string
ConfigFile string
Kind TemplateKind
RunUnconditionally bool
}
var templateRegistry = []TemplateDef{
{ID: "gtk", Kind: TemplateKindGTK, RunUnconditionally: true},
{ID: "niri", Commands: []string{"niri"}, ConfigFile: "niri.toml"},
{ID: "hyprland", Commands: []string{"Hyprland"}, ConfigFile: "hyprland.toml"},
{ID: "mangowc", Commands: []string{"mango"}, ConfigFile: "mangowc.toml"},
{ID: "qt5ct", Commands: []string{"qt5ct"}, ConfigFile: "qt5ct.toml"},
{ID: "qt6ct", Commands: []string{"qt6ct"}, ConfigFile: "qt6ct.toml"},
{ID: "firefox", Commands: []string{"firefox"}, ConfigFile: "firefox.toml"},
{ID: "pywalfox", Commands: []string{"pywalfox"}, ConfigFile: "pywalfox.toml"},
{ID: "zenbrowser", Commands: []string{"zen", "zen-browser"}, Flatpaks: []string{"app.zen_browser.zen"}, ConfigFile: "zenbrowser.toml"},
{ID: "vesktop", Commands: []string{"vesktop"}, Flatpaks: []string{"dev.vencord.Vesktop"}, ConfigFile: "vesktop.toml"},
{ID: "equibop", Commands: []string{"equibop"}, ConfigFile: "equibop.toml"},
{ID: "ghostty", Commands: []string{"ghostty"}, ConfigFile: "ghostty.toml", Kind: TemplateKindTerminal},
{ID: "kitty", Commands: []string{"kitty"}, ConfigFile: "kitty.toml", Kind: TemplateKindTerminal},
{ID: "foot", Commands: []string{"foot"}, ConfigFile: "foot.toml", Kind: TemplateKindTerminal},
{ID: "alacritty", Commands: []string{"alacritty"}, ConfigFile: "alacritty.toml", Kind: TemplateKindTerminal},
{ID: "wezterm", Commands: []string{"wezterm"}, ConfigFile: "wezterm.toml", Kind: TemplateKindTerminal},
{ID: "nvim", Commands: []string{"nvim"}, ConfigFile: "neovim.toml", Kind: TemplateKindTerminal},
{ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"},
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
{ID: "vscode", Kind: TemplateKindVSCode},
}
func (c *ColorMode) GTKTheme() string {
switch *c {
case ColorModeDark:
@@ -240,7 +281,7 @@ func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error {
if strings.TrimSpace(line) == "[config]" {
continue
}
cfgFile.WriteString(substituteShellDir(line, opts.ShellDir) + "\n")
cfgFile.WriteString(substituteVars(line, opts.ShellDir) + "\n")
}
cfgFile.WriteString("\n")
}
@@ -251,73 +292,31 @@ output_path = '%s'
`, opts.ShellDir, opts.ColorsOutput())
if !opts.ShouldSkipTemplate("gtk") {
homeDir, _ := os.UserHomeDir()
for _, tmpl := range templateRegistry {
if opts.ShouldSkipTemplate(tmpl.ID) {
continue
}
switch tmpl.Kind {
case TemplateKindGTK:
switch opts.Mode {
case "light":
case ColorModeLight:
appendConfig(opts, cfgFile, nil, nil, "gtk3-light.toml")
default:
appendConfig(opts, cfgFile, nil, nil, "gtk3-dark.toml")
}
}
if !opts.ShouldSkipTemplate("niri") {
appendConfig(opts, cfgFile, []string{"niri"}, nil, "niri.toml")
}
if !opts.ShouldSkipTemplate("qt5ct") {
appendConfig(opts, cfgFile, []string{"qt5ct"}, nil, "qt5ct.toml")
}
if !opts.ShouldSkipTemplate("qt6ct") {
appendConfig(opts, cfgFile, []string{"qt6ct"}, nil, "qt6ct.toml")
}
if !opts.ShouldSkipTemplate("firefox") {
appendConfig(opts, cfgFile, []string{"firefox"}, nil, "firefox.toml")
}
if !opts.ShouldSkipTemplate("pywalfox") {
appendConfig(opts, cfgFile, []string{"pywalfox"}, nil, "pywalfox.toml")
}
if !opts.ShouldSkipTemplate("zenbrowser") {
appendConfig(opts, cfgFile, []string{"zen", "zen-browser"}, []string{"app.zen_browser.zen"}, "zenbrowser.toml")
}
if !opts.ShouldSkipTemplate("vesktop") {
appendConfig(opts, cfgFile, []string{"vesktop"}, []string{"dev.vencord.Vesktop"}, "vesktop.toml")
}
if !opts.ShouldSkipTemplate("equibop") {
appendConfig(opts, cfgFile, []string{"equibop"}, nil, "equibop.toml")
}
if !opts.ShouldSkipTemplate("ghostty") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"ghostty"}, nil, "ghostty.toml")
}
if !opts.ShouldSkipTemplate("kitty") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"kitty"}, nil, "kitty.toml")
}
if !opts.ShouldSkipTemplate("foot") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"foot"}, nil, "foot.toml")
}
if !opts.ShouldSkipTemplate("alacritty") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"alacritty"}, nil, "alacritty.toml")
}
if !opts.ShouldSkipTemplate("wezterm") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"wezterm"}, nil, "wezterm.toml")
}
if !opts.ShouldSkipTemplate("nvim") {
appendTerminalConfig(opts, cfgFile, tmpDir, []string{"nvim"}, nil, "neovim.toml")
}
if !opts.ShouldSkipTemplate("dgop") {
appendConfig(opts, cfgFile, []string{"dgop"}, nil, "dgop.toml")
}
if !opts.ShouldSkipTemplate("kcolorscheme") {
appendConfig(opts, cfgFile, nil, nil, "kcolorscheme.toml")
}
if !opts.ShouldSkipTemplate("vscode") {
homeDir, _ := os.UserHomeDir()
case TemplateKindTerminal:
appendTerminalConfig(opts, cfgFile, tmpDir, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
case TemplateKindVSCode:
appendVSCodeConfig(cfgFile, "vscode", filepath.Join(homeDir, ".vscode/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "codium", filepath.Join(homeDir, ".vscode-oss/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "codeoss", filepath.Join(homeDir, ".config/Code - OSS/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir)
default:
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
}
}
if opts.RunUserTemplates {
@@ -364,7 +363,7 @@ func appendConfig(
if err != nil {
return
}
cfgFile.WriteString(substituteShellDir(string(data), opts.ShellDir))
cfgFile.WriteString(substituteVars(string(data), opts.ShellDir))
cfgFile.WriteString("\n")
}
@@ -384,7 +383,7 @@ func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir string, checkC
content := string(data)
if !opts.TerminalsAlwaysDark {
cfgFile.WriteString(substituteShellDir(content, opts.ShellDir))
cfgFile.WriteString(substituteVars(content, opts.ShellDir))
cfgFile.WriteString("\n")
return
}
@@ -422,7 +421,7 @@ func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir string, checkC
fmt.Sprintf("'%s'", tmpPath))
}
cfgFile.WriteString(substituteShellDir(content, opts.ShellDir))
cfgFile.WriteString(substituteVars(content, opts.ShellDir))
cfgFile.WriteString("\n")
}
@@ -467,8 +466,12 @@ output_path = '%s/themes/dankshell-light.json'
log.Infof("Added %s theme config (extension found at %s)", name, extDir)
}
func substituteShellDir(content, shellDir string) string {
return strings.ReplaceAll(content, "'SHELL_DIR/", "'"+shellDir+"/")
func substituteVars(content, shellDir string) string {
result := strings.ReplaceAll(content, "'SHELL_DIR/", "'"+shellDir+"/")
result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/")
result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/")
result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/")
return result
}
func extractTOMLSection(content, startMarker, endMarker string) string {
@@ -678,3 +681,52 @@ func syncColorScheme(mode ColorMode) {
exec.Command("dconf", "write", "/org/gnome/desktop/interface/color-scheme", "'"+scheme+"'").Run()
}
}
type TemplateCheck struct {
ID string `json:"id"`
Detected bool `json:"detected"`
}
func CheckTemplates(checker utils.AppChecker) []TemplateCheck {
if checker == nil {
checker = utils.DefaultAppChecker{}
}
homeDir, _ := os.UserHomeDir()
checks := make([]TemplateCheck, 0, len(templateRegistry))
for _, tmpl := range templateRegistry {
detected := false
switch {
case tmpl.RunUnconditionally:
detected = true
case tmpl.Kind == TemplateKindVSCode:
detected = checkVSCodeExtension(homeDir)
default:
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks)
}
checks = append(checks, TemplateCheck{ID: tmpl.ID, Detected: detected})
}
return checks
}
func checkVSCodeExtension(homeDir string) bool {
extDirs := []string{
filepath.Join(homeDir, ".vscode/extensions"),
filepath.Join(homeDir, ".vscode-oss/extensions"),
filepath.Join(homeDir, ".config/Code - OSS/extensions"),
filepath.Join(homeDir, ".cursor/extensions"),
filepath.Join(homeDir, ".windsurf/extensions"),
}
for _, extDir := range extDirs {
pattern := filepath.Join(extDir, "danklinux.dms-theme-*")
if matches, err := filepath.Glob(pattern); err == nil && len(matches) > 0 {
return true
}
}
return false
}

View File

@@ -6,6 +6,8 @@ import (
"testing"
mocks_utils "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/utils"
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
"github.com/stretchr/testify/assert"
)
func TestAppendConfigBinaryExists(t *testing.T) {
@@ -321,3 +323,72 @@ func TestAppendConfigFileDoesNotExist(t *testing.T) {
t.Errorf("expected no config when file doesn't exist, got: %q", string(output))
}
}
func TestSubstituteVars(t *testing.T) {
configDir := utils.XDGConfigHome()
dataDir := utils.XDGDataHome()
cacheDir := utils.XDGCacheHome()
tests := []struct {
name string
input string
shellDir string
expected string
}{
{
name: "substitutes SHELL_DIR",
input: "input_path = 'SHELL_DIR/matugen/templates/foo.conf'",
shellDir: "/home/user/shell",
expected: "input_path = '/home/user/shell/matugen/templates/foo.conf'",
},
{
name: "substitutes CONFIG_DIR",
input: "output_path = 'CONFIG_DIR/kitty/theme.conf'",
shellDir: "/home/user/shell",
expected: "output_path = '" + configDir + "/kitty/theme.conf'",
},
{
name: "substitutes DATA_DIR",
input: "output_path = 'DATA_DIR/color-schemes/theme.colors'",
shellDir: "/home/user/shell",
expected: "output_path = '" + dataDir + "/color-schemes/theme.colors'",
},
{
name: "substitutes CACHE_DIR",
input: "output_path = 'CACHE_DIR/wal/colors.json'",
shellDir: "/home/user/shell",
expected: "output_path = '" + cacheDir + "/wal/colors.json'",
},
{
name: "substitutes all dir types",
input: "'SHELL_DIR/a' 'CONFIG_DIR/b' 'DATA_DIR/c' 'CACHE_DIR/d'",
shellDir: "/shell",
expected: "'/shell/a' '" + configDir + "/b' '" + dataDir + "/c' '" + cacheDir + "/d'",
},
{
name: "no substitution when no placeholders",
input: "input_path = '/absolute/path/foo.conf'",
shellDir: "/home/user/shell",
expected: "input_path = '/absolute/path/foo.conf'",
},
{
name: "multiple SHELL_DIR occurrences",
input: "'SHELL_DIR/a' and 'SHELL_DIR/b'",
shellDir: "/shell",
expected: "'/shell/a' and '/shell/b'",
},
{
name: "only substitutes quoted paths",
input: "SHELL_DIR/unquoted and 'SHELL_DIR/quoted'",
shellDir: "/shell",
expected: "SHELL_DIR/unquoted and '/shell/quoted'",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := substituteVars(tc.input, tc.shellDir)
assert.Equal(t, tc.expected, result)
})
}
}

View File

@@ -10,9 +10,32 @@ func XDGStateHome() string {
if dir := os.Getenv("XDG_STATE_HOME"); dir != "" {
return dir
}
home, _ := os.UserHomeDir()
return filepath.Join(append([]string{home}, ".local", "state")...)
return filepath.Join(home, ".local", "state")
}
func XDGDataHome() string {
if dir := os.Getenv("XDG_DATA_HOME"); dir != "" {
return dir
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".local", "share")
}
func XDGCacheHome() string {
if dir, err := os.UserCacheDir(); err == nil {
return dir
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".cache")
}
func XDGConfigHome() string {
if dir, err := os.UserConfigDir(); err == nil {
return dir
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".config")
}
func ExpandPath(path string) (string, error) {

View File

@@ -118,8 +118,56 @@ Singleton {
parseSettings(greeterSessionFile.text());
return;
}
parseSettings(settingsFile.text());
try {
const txt = settingsFile.text();
let obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
if (obj?.brightnessLogarithmicDevices && !obj?.brightnessExponentialDevices)
obj.brightnessExponentialDevices = obj.brightnessLogarithmicDevices;
if (obj?.nightModeStartTime !== undefined) {
const parts = obj.nightModeStartTime.split(":");
obj.nightModeStartHour = parseInt(parts[0]) || 18;
obj.nightModeStartMinute = parseInt(parts[1]) || 0;
}
if (obj?.nightModeEndTime !== undefined) {
const parts = obj.nightModeEndTime.split(":");
obj.nightModeEndHour = parseInt(parts[0]) || 6;
obj.nightModeEndMinute = parseInt(parts[1]) || 0;
}
const oldVersion = obj?.configVersion ?? 0;
if (obj && oldVersion === 0)
migrateFromUndefinedToV1(obj);
if (obj && oldVersion < sessionConfigVersion) {
const settingsDataRef = (typeof SettingsData !== "undefined") ? SettingsData : null;
const migrated = Store.migrateToVersion(obj, sessionConfigVersion, settingsDataRef);
if (migrated) {
_pendingMigration = migrated;
obj = migrated;
}
}
Store.parse(root, obj);
_loadedSessionSnapshot = getCurrentSessionJson();
_hasLoaded = true;
if (!isGreeterMode && typeof Theme !== "undefined")
Theme.generateSystemThemesFromCurrentTheme();
if (typeof WallpaperCyclingService !== "undefined")
WallpaperCyclingService.updateCyclingState();
_checkSessionWritable();
} catch (e) {
_parseError = true;
const msg = e.message;
console.error("SessionData: Failed to parse session.json - file will not be overwritten. Error:", msg);
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse session.json"), msg));
}
}
function _checkSessionWritable() {
@@ -158,29 +206,27 @@ Singleton {
function parseSettings(content) {
_parseError = false;
try {
let obj = (content && content.trim()) ? JSON.parse(content) : {};
let obj = (content && content.trim()) ? JSON.parse(content) : null;
if (obj.brightnessLogarithmicDevices && !obj.brightnessExponentialDevices) {
if (obj?.brightnessLogarithmicDevices && !obj?.brightnessExponentialDevices)
obj.brightnessExponentialDevices = obj.brightnessLogarithmicDevices;
}
if (obj.nightModeStartTime !== undefined) {
if (obj?.nightModeStartTime !== undefined) {
const parts = obj.nightModeStartTime.split(":");
obj.nightModeStartHour = parseInt(parts[0]) || 18;
obj.nightModeStartMinute = parseInt(parts[1]) || 0;
}
if (obj.nightModeEndTime !== undefined) {
if (obj?.nightModeEndTime !== undefined) {
const parts = obj.nightModeEndTime.split(":");
obj.nightModeEndHour = parseInt(parts[0]) || 6;
obj.nightModeEndMinute = parseInt(parts[1]) || 0;
}
const oldVersion = obj.configVersion ?? 0;
if (oldVersion === 0) {
const oldVersion = obj?.configVersion ?? 0;
if (obj && oldVersion === 0)
migrateFromUndefinedToV1(obj);
}
if (oldVersion < sessionConfigVersion) {
if (obj && oldVersion < sessionConfigVersion) {
const settingsDataRef = (typeof SettingsData !== "undefined") ? SettingsData : null;
const migrated = Store.migrateToVersion(obj, sessionConfigVersion, settingsDataRef);
if (migrated) {
@@ -191,22 +237,14 @@ Singleton {
Store.parse(root, obj);
if (wallpaperPath && wallpaperPath.startsWith("we:")) {
console.warn("WallpaperEngine wallpaper detected, resetting wallpaper");
wallpaperPath = "";
Quickshell.execDetached(["notify-send", "-u", "critical", "-a", "DMS", "-i", "dialog-warning", "WallpaperEngine Support Moved", "WallpaperEngine support has been moved to a plugin. Please enable the Linux Wallpaper Engine plugin in Settings → Plugins to continue using WallpaperEngine."]);
}
_hasLoaded = true;
_loadedSessionSnapshot = getCurrentSessionJson();
_hasLoaded = true;
if (!isGreeterMode && typeof Theme !== "undefined") {
if (!isGreeterMode && typeof Theme !== "undefined")
Theme.generateSystemThemesFromCurrentTheme();
}
if (typeof WallpaperCyclingService !== "undefined") {
if (typeof WallpaperCyclingService !== "undefined")
WallpaperCyclingService.updateCyclingState();
}
} catch (e) {
_parseError = true;
const msg = e.message;

View File

@@ -81,6 +81,13 @@ Singleton {
property real cornerRadius: 12
property int niriLayoutGapsOverride: -1
property int niriLayoutRadiusOverride: -1
property int niriLayoutBorderSize: -1
property int hyprlandLayoutGapsOverride: -1
property int hyprlandLayoutRadiusOverride: -1
property int hyprlandLayoutBorderSize: -1
property int mangoLayoutGapsOverride: -1
property int mangoLayoutRadiusOverride: -1
property int mangoLayoutBorderSize: -1
property bool use24HourClock: true
property bool showSeconds: false
@@ -295,6 +302,8 @@ Singleton {
property bool runDmsMatugenTemplates: true
property bool matugenTemplateGtk: true
property bool matugenTemplateNiri: true
property bool matugenTemplateHyprland: true
property bool matugenTemplateMangowc: true
property bool matugenTemplateQt5ct: true
property bool matugenTemplateQt6ct: true
property bool matugenTemplateFirefox: true
@@ -699,10 +708,15 @@ Singleton {
}
}
function updateNiriLayout() {
if (typeof NiriService !== "undefined" && typeof CompositorService !== "undefined" && CompositorService.isNiri) {
function updateCompositorLayout() {
if (typeof CompositorService === "undefined")
return;
if (CompositorService.isNiri && typeof NiriService !== "undefined")
NiriService.generateNiriLayoutConfig();
}
if (CompositorService.isHyprland && typeof HyprlandService !== "undefined")
HyprlandService.generateLayoutConfig();
if (CompositorService.isDwl && typeof DwlService !== "undefined")
DwlService.generateLayoutConfig();
}
function applyStoredIconTheme() {
@@ -778,7 +792,7 @@ Singleton {
readonly property var _hooks: ({
"applyStoredTheme": applyStoredTheme,
"regenSystemThemes": regenSystemThemes,
"updateNiriLayout": updateNiriLayout,
"updateCompositorLayout": updateCompositorLayout,
"applyStoredIconTheme": applyStoredIconTheme,
"updateBarConfigs": updateBarConfigs
})
@@ -1459,7 +1473,7 @@ Singleton {
function setCornerRadius(radius) {
set("cornerRadius", radius);
NiriService.generateNiriLayoutConfig();
updateCompositorLayout();
}
function setWeatherLocation(displayName, coordinates) {
@@ -1541,9 +1555,7 @@ Singleton {
"spacing": spacing
});
}
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
NiriService.generateNiriLayoutConfig();
}
updateCompositorLayout();
}
function setDankBarPosition(position) {

View File

@@ -910,6 +910,10 @@ Singleton {
skipTemplates.push("gtk");
if (!SettingsData.matugenTemplateNiri)
skipTemplates.push("niri");
if (!SettingsData.matugenTemplateHyprland)
skipTemplates.push("hyprland");
if (!SettingsData.matugenTemplateMangowc)
skipTemplates.push("mangowc");
if (!SettingsData.matugenTemplateQt5ct)
skipTemplates.push("qt5ct");
if (!SettingsData.matugenTemplateQt6ct)

View File

@@ -19,9 +19,16 @@ var SPEC = {
widgetBackgroundColor: { def: "sch" },
widgetColorMode: { def: "default" },
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
niriLayoutGapsOverride: { def: -1, onChange: "updateNiriLayout" },
niriLayoutRadiusOverride: { def: -1, onChange: "updateNiriLayout" },
cornerRadius: { def: 12, onChange: "updateCompositorLayout" },
niriLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
niriLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
niriLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
hyprlandLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
hyprlandLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
hyprlandLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
mangoLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
mangoLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
mangoLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
use24HourClock: { def: true },
showSeconds: { def: false },
@@ -185,6 +192,8 @@ var SPEC = {
runDmsMatugenTemplates: { def: true },
matugenTemplateGtk: { def: true },
matugenTemplateNiri: { def: true },
matugenTemplateHyprland: { def: true },
matugenTemplateMangowc: { def: true },
matugenTemplateQt5ct: { def: true },
matugenTemplateQt6ct: { def: true },
matugenTemplateFirefox: { def: true },

View File

@@ -727,7 +727,7 @@ FocusScope {
id: gridScrollbar
}
ScrollBar.horizontal: ScrollBar {
ScrollBar.horizontal: DankScrollbar {
policy: ScrollBar.AlwaysOff
}

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Modals.FileBrowser
import qs.Services
@@ -13,6 +14,27 @@ Item {
property var cachedIconThemes: SettingsData.availableIconThemes
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
property var installedRegistryThemes: []
property var templateDetection: ({})
function isTemplateDetected(templateId) {
if (!templateDetection || Object.keys(templateDetection).length === 0)
return true;
return templateDetection[templateId] !== false;
}
function getTemplateDescription(templateId, baseDescription) {
if (isTemplateDetected(templateId))
return baseDescription;
if (baseDescription)
return baseDescription + " · " + I18n.tr("Not detected");
return I18n.tr("Not detected");
}
function getTemplateDescriptionColor(templateId) {
if (isTemplateDetected(templateId))
return Theme.surfaceVariantText;
return Theme.warning;
}
Component.onCompleted: {
SettingsData.detectAvailableIconThemes();
@@ -20,6 +42,28 @@ Item {
DMSService.listInstalledThemes();
if (PopoutService.pendingThemeInstall)
Qt.callLater(() => themeBrowser.show());
templateCheckProcess.running = true;
}
Process {
id: templateCheckProcess
command: ["dms", "matugen", "check"]
running: false
stdout: StdioCollector {
onStreamFinished: {
try {
const results = JSON.parse(text);
const detection = {};
for (const item of results) {
detection[item.id] = item.detected;
}
themeColorsTab.templateDetection = detection;
} catch (e) {
console.warn("ThemeColorsTab: Failed to parse template check:", e);
}
}
}
}
Connections {
@@ -918,7 +962,7 @@ Item {
SettingsCard {
tab: "theme"
tags: ["niri", "layout", "gaps", "radius", "window"]
tags: ["niri", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("Niri Layout Overrides")
settingKey: "niriLayout"
iconName: "crop_square"
@@ -986,6 +1030,243 @@ Item {
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("niriLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border/focus-ring width")
checked: SettingsData.niriLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("niriLayoutBorderSize", 2);
return;
}
SettingsData.set("niriLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border and focus ring")
visible: SettingsData.niriLayoutBorderSize >= 0
value: Math.max(0, SettingsData.niriLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("niriLayoutBorderSize", newValue)
}
}
SettingsCard {
tab: "theme"
tags: ["hyprland", "layout", "gaps", "radius", "window", "border", "rounding"]
title: I18n.tr("Hyprland Layout Overrides")
settingKey: "hyprlandLayout"
iconName: "crop_square"
visible: CompositorService.isHyprland
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.hyprlandLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("hyprlandLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("hyprlandLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gaps_in and gaps_out)")
visible: SettingsData.hyprlandLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window rounding instead of theme radius")
checked: SettingsData.hyprlandLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("hyprlandLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverride"
text: I18n.tr("Window Rounding")
description: I18n.tr("Rounded corners for windows (decoration.rounding)")
visible: SettingsData.hyprlandLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.hyprlandLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutBorderSize", 2);
return;
}
SettingsData.set("hyprlandLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (general.border_size)")
visible: SettingsData.hyprlandLayoutBorderSize >= 0
value: Math.max(0, SettingsData.hyprlandLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutBorderSize", newValue)
}
}
SettingsCard {
tab: "theme"
tags: ["mangowc", "mango", "dwl", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("MangoWC Layout Overrides")
settingKey: "mangoLayout"
iconName: "crop_square"
visible: CompositorService.isDwl
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.mangoLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("mangoLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("mangoLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gappih/gappiv/gappoh/gappov)")
visible: SettingsData.mangoLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window radius instead of theme radius")
checked: SettingsData.mangoLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("mangoLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverride"
text: I18n.tr("Window Corner Radius")
description: I18n.tr("Rounded corners for windows (border_radius)")
visible: SettingsData.mangoLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.mangoLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutBorderSize", 2);
return;
}
SettingsData.set("mangoLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (borderpx)")
visible: SettingsData.mangoLayoutBorderSize >= 0
value: Math.max(0, SettingsData.mangoLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutBorderSize", newValue)
}
}
SettingsCard {
@@ -1066,7 +1347,8 @@ Item {
tags: ["matugen", "gtk", "template"]
settingKey: "matugenTemplateGtk"
text: "GTK"
description: ""
description: getTemplateDescription("gtk", "")
descriptionColor: getTemplateDescriptionColor("gtk")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateGtk
onToggled: checked => SettingsData.set("matugenTemplateGtk", checked)
@@ -1077,18 +1359,44 @@ Item {
tags: ["matugen", "niri", "template"]
settingKey: "matugenTemplateNiri"
text: "niri"
description: ""
description: getTemplateDescription("niri", "")
descriptionColor: getTemplateDescriptionColor("niri")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateNiri
onToggled: checked => SettingsData.set("matugenTemplateNiri", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["matugen", "hyprland", "template"]
settingKey: "matugenTemplateHyprland"
text: "Hyprland"
description: getTemplateDescription("hyprland", "")
descriptionColor: getTemplateDescriptionColor("hyprland")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateHyprland
onToggled: checked => SettingsData.set("matugenTemplateHyprland", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["matugen", "mangowc", "template"]
settingKey: "matugenTemplateMangowc"
text: "mangowc"
description: getTemplateDescription("mangowc", "")
descriptionColor: getTemplateDescriptionColor("mangowc")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateMangowc
onToggled: checked => SettingsData.set("matugenTemplateMangowc", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["matugen", "qt5ct", "template"]
settingKey: "matugenTemplateQt5ct"
text: "qt5ct"
description: ""
description: getTemplateDescription("qt5ct", "")
descriptionColor: getTemplateDescriptionColor("qt5ct")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateQt5ct
onToggled: checked => SettingsData.set("matugenTemplateQt5ct", checked)
@@ -1099,7 +1407,8 @@ Item {
tags: ["matugen", "qt6ct", "template"]
settingKey: "matugenTemplateQt6ct"
text: "qt6ct"
description: ""
description: getTemplateDescription("qt6ct", "")
descriptionColor: getTemplateDescriptionColor("qt6ct")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateQt6ct
onToggled: checked => SettingsData.set("matugenTemplateQt6ct", checked)
@@ -1110,7 +1419,8 @@ Item {
tags: ["matugen", "firefox", "template"]
settingKey: "matugenTemplateFirefox"
text: "Firefox"
description: ""
description: getTemplateDescription("firefox", "")
descriptionColor: getTemplateDescriptionColor("firefox")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateFirefox
onToggled: checked => SettingsData.set("matugenTemplateFirefox", checked)
@@ -1121,7 +1431,8 @@ Item {
tags: ["matugen", "pywalfox", "template"]
settingKey: "matugenTemplatePywalfox"
text: "pywalfox"
description: ""
description: getTemplateDescription("pywalfox", "")
descriptionColor: getTemplateDescriptionColor("pywalfox")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplatePywalfox
onToggled: checked => SettingsData.set("matugenTemplatePywalfox", checked)
@@ -1132,7 +1443,8 @@ Item {
tags: ["matugen", "zenbrowser", "template"]
settingKey: "matugenTemplateZenBrowser"
text: "zenbrowser"
description: ""
description: getTemplateDescription("zenbrowser", "")
descriptionColor: getTemplateDescriptionColor("zenbrowser")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateZenBrowser
onToggled: checked => SettingsData.set("matugenTemplateZenBrowser", checked)
@@ -1143,7 +1455,8 @@ Item {
tags: ["matugen", "vesktop", "discord", "template"]
settingKey: "matugenTemplateVesktop"
text: "vesktop"
description: ""
description: getTemplateDescription("vesktop", "")
descriptionColor: getTemplateDescriptionColor("vesktop")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateVesktop
onToggled: checked => SettingsData.set("matugenTemplateVesktop", checked)
@@ -1154,7 +1467,8 @@ Item {
tags: ["matugen", "equibop", "discord", "template"]
settingKey: "matugenTemplateEquibop"
text: "equibop"
description: ""
description: getTemplateDescription("equibop", "")
descriptionColor: getTemplateDescriptionColor("equibop")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateEquibop
onToggled: checked => SettingsData.set("matugenTemplateEquibop", checked)
@@ -1165,7 +1479,8 @@ Item {
tags: ["matugen", "ghostty", "terminal", "template"]
settingKey: "matugenTemplateGhostty"
text: "Ghostty"
description: ""
description: getTemplateDescription("ghostty", "")
descriptionColor: getTemplateDescriptionColor("ghostty")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateGhostty
onToggled: checked => SettingsData.set("matugenTemplateGhostty", checked)
@@ -1176,7 +1491,8 @@ Item {
tags: ["matugen", "kitty", "terminal", "template"]
settingKey: "matugenTemplateKitty"
text: "kitty"
description: ""
description: getTemplateDescription("kitty", "")
descriptionColor: getTemplateDescriptionColor("kitty")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateKitty
onToggled: checked => SettingsData.set("matugenTemplateKitty", checked)
@@ -1187,17 +1503,20 @@ Item {
tags: ["matugen", "foot", "terminal", "template"]
settingKey: "matugenTemplateFoot"
text: "foot"
description: ""
description: getTemplateDescription("foot", "")
descriptionColor: getTemplateDescriptionColor("foot")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateFoot
onToggled: checked => SettingsData.set("matugenTemplateFoot", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["matugen", "neovim", "terminal", "template"]
settingKey: "matugenTemplateNeovim"
text: "neovim"
description: "Requires lazy plugin manager"
description: getTemplateDescription("nvim", "Requires lazy plugin manager")
descriptionColor: getTemplateDescriptionColor("nvim")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateNeovim
onToggled: checked => SettingsData.set("matugenTemplateNeovim", checked)
@@ -1208,7 +1527,8 @@ Item {
tags: ["matugen", "alacritty", "terminal", "template"]
settingKey: "matugenTemplateAlacritty"
text: "Alacritty"
description: ""
description: getTemplateDescription("alacritty", "")
descriptionColor: getTemplateDescriptionColor("alacritty")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateAlacritty
onToggled: checked => SettingsData.set("matugenTemplateAlacritty", checked)
@@ -1219,7 +1539,8 @@ Item {
tags: ["matugen", "wezterm", "terminal", "template"]
settingKey: "matugenTemplateWezterm"
text: "WezTerm"
description: ""
description: getTemplateDescription("wezterm", "")
descriptionColor: getTemplateDescriptionColor("wezterm")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateWezterm
onToggled: checked => SettingsData.set("matugenTemplateWezterm", checked)
@@ -1230,7 +1551,8 @@ Item {
tags: ["matugen", "dgop", "template"]
settingKey: "matugenTemplateDgop"
text: "dgop"
description: ""
description: getTemplateDescription("dgop", "")
descriptionColor: getTemplateDescriptionColor("dgop")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateDgop
onToggled: checked => SettingsData.set("matugenTemplateDgop", checked)
@@ -1241,7 +1563,8 @@ Item {
tags: ["matugen", "kcolorscheme", "kde", "template"]
settingKey: "matugenTemplateKcolorscheme"
text: "KColorScheme"
description: ""
description: getTemplateDescription("kcolorscheme", "")
descriptionColor: getTemplateDescriptionColor("kcolorscheme")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateKcolorscheme
onToggled: checked => SettingsData.set("matugenTemplateKcolorscheme", checked)
@@ -1252,7 +1575,8 @@ Item {
tags: ["matugen", "vscode", "code", "template"]
settingKey: "matugenTemplateVscode"
text: "VS Code"
description: ""
description: getTemplateDescription("vscode", "")
descriptionColor: getTemplateDescriptionColor("vscode")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateVscode
onToggled: checked => SettingsData.set("matugenTemplateVscode", checked)

View File

@@ -137,7 +137,11 @@ Singleton {
Component.onCompleted: {
detectCompositor();
scheduleSort();
Qt.callLater(() => NiriService.generateNiriLayoutConfig());
Qt.callLater(() => {
NiriService.generateNiriLayoutConfig();
HyprlandService.generateLayoutConfig();
DwlService.generateLayoutConfig();
});
}
Connections {
@@ -396,7 +400,11 @@ Singleton {
repeat: false
onTriggered: {
detectCompositor();
Qt.callLater(() => NiriService.generateNiriLayoutConfig());
Qt.callLater(() => {
NiriService.generateNiriLayoutConfig();
HyprlandService.generateLayoutConfig();
DwlService.generateLayoutConfig();
});
}
}

View File

@@ -13,6 +13,9 @@ Singleton {
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
readonly property string mangoDmsDir: configDir + "/mango/dms"
readonly property string outputsPath: mangoDmsDir + "/outputs.conf"
readonly property string layoutPath: mangoDmsDir + "/layout.conf"
property int _lastGapValue: -1
property bool dwlAvailable: false
property var outputs: ({})
@@ -29,6 +32,27 @@ Singleton {
signal stateChanged
Connections {
target: SettingsData
function onBarConfigsChanged() {
if (!CompositorService.isDwl)
return;
const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
if (newGaps === root._lastGapValue)
return;
root._lastGapValue = newGaps;
generateLayoutConfig();
}
}
Connections {
target: CompositorService
function onIsDwlChanged() {
if (CompositorService.isDwl)
generateLayoutConfig();
}
}
Connections {
target: DMSService
function onCapabilitiesReceived() {
@@ -49,12 +73,12 @@ Singleton {
}
Component.onCompleted: {
if (DMSService.dmsAvailable) {
if (DMSService.dmsAvailable)
checkCapabilities();
}
if (dwlAvailable) {
if (dwlAvailable)
refreshOutputScales();
}
if (CompositorService.isDwl)
Qt.callLater(generateLayoutConfig);
}
function checkCapabilities() {
@@ -323,6 +347,37 @@ Singleton {
});
}
function generateLayoutConfig() {
if (!CompositorService.isDwl)
return;
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
const defaultBorderSize = 2;
const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutRadiusOverride >= 0) ? SettingsData.mangoLayoutRadiusOverride : defaultRadius;
const gaps = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutGapsOverride >= 0) ? SettingsData.mangoLayoutGapsOverride : defaultGaps;
const borderSize = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutBorderSize >= 0) ? SettingsData.mangoLayoutBorderSize : defaultBorderSize;
let content = `# Auto-generated by DMS - do not edit manually
border_radius=${cornerRadius}
gappih=${gaps}
gappiv=${gaps}
gappoh=${gaps}
gappov=${gaps}
borderpx=${borderSize}
`;
Proc.runCommand("mango-write-layout", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("DwlService: Failed to write layout config:", output);
return;
}
console.info("DwlService: Generated layout config at", layoutPath);
reloadConfig();
});
}
function transformToMango(transform) {
switch (transform) {
case "Normal":

View File

@@ -12,6 +12,35 @@ Singleton {
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
readonly property string hyprDmsDir: configDir + "/hypr/dms"
readonly property string outputsPath: hyprDmsDir + "/outputs.conf"
readonly property string layoutPath: hyprDmsDir + "/layout.conf"
property int _lastGapValue: -1
Component.onCompleted: {
if (CompositorService.isHyprland)
Qt.callLater(generateLayoutConfig);
}
Connections {
target: SettingsData
function onBarConfigsChanged() {
if (!CompositorService.isHyprland)
return;
const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
if (newGaps === root._lastGapValue)
return;
root._lastGapValue = newGaps;
generateLayoutConfig();
}
}
Connections {
target: CompositorService
function onIsHyprlandChanged() {
if (CompositorService.isHyprland)
generateLayoutConfig();
}
}
function getOutputIdentifier(output, outputName) {
if (SettingsData.displayNameMode === "model" && output.make && output.model)
@@ -132,6 +161,41 @@ Singleton {
});
}
function generateLayoutConfig() {
if (!CompositorService.isHyprland)
return;
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
const defaultBorderSize = 2;
const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutRadiusOverride >= 0) ? SettingsData.hyprlandLayoutRadiusOverride : defaultRadius;
const gaps = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutGapsOverride >= 0) ? SettingsData.hyprlandLayoutGapsOverride : defaultGaps;
const borderSize = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutBorderSize >= 0) ? SettingsData.hyprlandLayoutBorderSize : defaultBorderSize;
let content = `# Auto-generated by DMS - do not edit manually
general {
gaps_in = ${gaps}
gaps_out = ${gaps}
border_size = ${borderSize}
}
decoration {
rounding = ${cornerRadius}
}
`;
Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("HyprlandService: Failed to write layout config:", output);
return;
}
console.info("HyprlandService: Generated layout config at", layoutPath);
reloadConfig();
});
}
function transformToHyprland(transform) {
switch (transform) {
case "Normal":

View File

@@ -1012,9 +1012,11 @@ Singleton {
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
const defaultBorderSize = 2;
const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutRadiusOverride >= 0) ? SettingsData.niriLayoutRadiusOverride : defaultRadius;
const gaps = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutGapsOverride >= 0) ? SettingsData.niriLayoutGapsOverride : defaultGaps;
const borderSize = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutBorderSize >= 0) ? SettingsData.niriLayoutBorderSize : defaultBorderSize;
const dmsWarning = `// ! DO NOT EDIT !
// ! AUTO-GENERATED BY DMS !
@@ -1027,11 +1029,11 @@ Singleton {
gaps ${gaps}
border {
width 2
width ${borderSize}
}
focus-ring {
width 2
width ${borderSize}
}
}
window-rule {

View File

@@ -12,7 +12,7 @@ ScrollBar {
policy: (parent && parent.contentHeight > parent.height) ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
minimumSize: 0.08
implicitWidth: 8
implicitWidth: 10
interactive: true
hoverEnabled: true
z: 1000

View File

@@ -14,6 +14,7 @@ Item {
property bool toggling: false
property string text: ""
property string description: ""
property color descriptionColor: Theme.surfaceVariantText
property bool hideText: false
signal clicked
@@ -79,7 +80,7 @@ Item {
StyledText {
text: toggle.description
font.pixelSize: Appearance.fontSize.small
color: Theme.surfaceVariantText
color: toggle.descriptionColor
wrapMode: Text.WordWrap
width: Math.min(implicitWidth, toggle.width - 120)
visible: toggle.description.length > 0

View File

@@ -1,3 +1,3 @@
[templates.dmsalacritty]
input_path = 'SHELL_DIR/matugen/templates/alacritty.toml'
output_path = '~/.config/alacritty/dank-theme.toml'
output_path = 'CONFIG_DIR/alacritty/dank-theme.toml'

View File

@@ -1,3 +1,3 @@
[templates.dmsdgop]
input_path = 'SHELL_DIR/matugen/templates/dgop.json'
output_path = '~/.config/dgop/colors.json'
output_path = 'CONFIG_DIR/dgop/colors.json'

View File

@@ -1,3 +1,3 @@
[templates.dmsequibop]
input_path = 'SHELL_DIR/matugen/templates/vesktop.css'
output_path = '~/.config/equibop/themes/dank-discord.css'
output_path = 'CONFIG_DIR/equibop/themes/dank-discord.css'

View File

@@ -1,3 +1,3 @@
[templates.dmsfirefox]
input_path = 'SHELL_DIR/matugen/templates/firefox-userchrome.css'
output_path = '~/.config/DankMaterialShell/firefox.css'
output_path = 'CONFIG_DIR/DankMaterialShell/firefox.css'

View File

@@ -1,3 +1,3 @@
[templates.dmsfoot]
input_path = 'SHELL_DIR/matugen/templates/foot.ini'
output_path = '~/.config/foot/dank-colors.ini'
output_path = 'CONFIG_DIR/foot/dank-colors.ini'

View File

@@ -1,3 +1,3 @@
[templates.dmsghostty]
input_path = 'SHELL_DIR/matugen/templates/ghostty.conf'
output_path = '~/.config/ghostty/themes/dankcolors'
output_path = 'CONFIG_DIR/ghostty/themes/dankcolors'

View File

@@ -1,7 +1,7 @@
[templates.dmsgtk3]
input_path = 'SHELL_DIR/matugen/templates/gtk-colors.css'
output_path = '~/.config/gtk-3.0/dank-colors.css'
output_path = 'CONFIG_DIR/gtk-3.0/dank-colors.css'
[templates.dmsgtk4]
input_path = 'SHELL_DIR/matugen/templates/gtk-colors.css'
output_path = '~/.config/gtk-4.0/dank-colors.css'
output_path = 'CONFIG_DIR/gtk-4.0/dank-colors.css'

View File

@@ -1,7 +1,7 @@
[templates.dmsgtk3]
input_path = 'SHELL_DIR/matugen/templates/gtk-light-colors.css'
output_path = '~/.config/gtk-3.0/dank-colors.css'
output_path = 'CONFIG_DIR/gtk-3.0/dank-colors.css'
[templates.dmsgtk4]
input_path = 'SHELL_DIR/matugen/templates/gtk-colors.css'
output_path = '~/.config/gtk-4.0/dank-colors.css'
output_path = 'CONFIG_DIR/gtk-4.0/dank-colors.css'

View File

@@ -0,0 +1,3 @@
[templates.dmshyprland]
input_path = 'SHELL_DIR/matugen/templates/hypr-colors.conf'
output_path = 'CONFIG_DIR/hypr/dms/colors.conf'

View File

@@ -1,11 +1,11 @@
[templates.dmskcolorscheme]
input_path = 'SHELL_DIR/matugen/templates/kcolorscheme.colors'
output_path = '~/.local/share/color-schemes/DankMatugen.colors'
output_path = 'DATA_DIR/color-schemes/DankMatugen.colors'
[templates.dmslightkcolorscheme]
input_path = 'SHELL_DIR/matugen/templates/light-kcolorscheme.colors'
output_path = '~/.local/share/color-schemes/DankMatugenLight.colors'
output_path = 'DATA_DIR/color-schemes/DankMatugenLight.colors'
[templates.dmsdarkkcolorscheme]
input_path = 'SHELL_DIR/matugen/templates/dark-kcolorscheme.colors'
output_path = '~/.local/share/color-schemes/DankMatugenDark.colors'
output_path = 'DATA_DIR/color-schemes/DankMatugenDark.colors'

View File

@@ -1,7 +1,7 @@
[templates.dmskitty]
input_path = 'SHELL_DIR/matugen/templates/kitty.conf'
output_path = '~/.config/kitty/dank-theme.conf'
output_path = 'CONFIG_DIR/kitty/dank-theme.conf'
[templates.dmskittytabs]
input_path = 'SHELL_DIR/matugen/templates/kitty-tabs.conf'
output_path = '~/.config/kitty/dank-tabs.conf'
output_path = 'CONFIG_DIR/kitty/dank-tabs.conf'

View File

@@ -0,0 +1,3 @@
[templates.dmsmango]
input_path = 'SHELL_DIR/matugen/templates/mango-colors.conf'
output_path = 'CONFIG_DIR/mango/dms/colors.conf'

View File

@@ -1,3 +1,3 @@
[templates.dmsneovim]
input_path = 'SHELL_DIR/matugen/templates/neovim.lua'
output_path = '~/.config/nvim/lua/plugins/dankcolors.lua'
output_path = 'CONFIG_DIR/nvim/lua/plugins/dankcolors.lua'

View File

@@ -1,3 +1,3 @@
[templates.dmsniri]
input_path = 'SHELL_DIR/matugen/templates/niri-colors.kdl'
output_path = '~/.config/niri/dms/colors.kdl'
output_path = 'CONFIG_DIR/niri/dms/colors.kdl'

View File

@@ -1,4 +1,4 @@
[templates.dmspywalfox]
input_path = 'SHELL_DIR/matugen/templates/pywalfox-colors.json'
output_path = '~/.cache/wal/dank-pywalfox.json'
output_path = 'CACHE_DIR/wal/dank-pywalfox.json'
post_hook = 'sh -c "command -v pywalfox && test -f ~/.cache/wal/colors.json && pywalfox update"'

View File

@@ -1,3 +1,3 @@
[templates.dmsqt5ct]
input_path = 'SHELL_DIR/matugen/templates/qtct-colors.conf'
output_path = '~/.config/qt5ct/colors/matugen.conf'
output_path = 'CONFIG_DIR/qt5ct/colors/matugen.conf'

View File

@@ -1,3 +1,3 @@
[templates.dmsqt6ct]
input_path = 'SHELL_DIR/matugen/templates/qtct-colors.conf'
output_path = '~/.config/qt6ct/colors/matugen.conf'
output_path = 'CONFIG_DIR/qt6ct/colors/matugen.conf'

View File

@@ -1,3 +1,3 @@
[templates.dmsvesktop]
input_path = 'SHELL_DIR/matugen/templates/vesktop.css'
output_path = '~/.config/vesktop/themes/dank-discord.css'
output_path = 'CONFIG_DIR/vesktop/themes/dank-discord.css'

View File

@@ -1,3 +1,3 @@
[templates.dmswezterm]
input_path = 'SHELL_DIR/matugen/templates/wezterm.toml'
output_path = '~/.config/wezterm/colors/dank-theme.toml'
output_path = 'CONFIG_DIR/wezterm/colors/dank-theme.toml'

View File

@@ -1,3 +1,3 @@
[templates.dmszenbrowser]
input_path = 'SHELL_DIR/matugen/templates/zen-userchrome.css'
output_path = '~/.config/DankMaterialShell/zen.css'
output_path = 'CONFIG_DIR/DankMaterialShell/zen.css'

View File

@@ -0,0 +1,25 @@
# ! Auto-generated file. Do not edit directly.
# Remove source = ./dms/colors.conf from your config to override.
$primary = rgb({{colors.primary.default.hex_stripped}})
$outline = rgb({{colors.outline.default.hex_stripped}})
$error = rgb({{colors.error.default.hex_stripped}})
general {
col.active_border = $primary
col.inactive_border = $outline
}
group {
col.border_active = $primary
col.border_inactive = $outline
col.border_locked_active = $error
col.border_locked_inactive = $outline
groupbar {
col.active = $primary
col.inactive = $outline
col.locked_active = $error
col.locked_inactive = $outline
}
}

View File

@@ -0,0 +1,6 @@
# ! Auto-generated file. Do not edit directly.
# Remove source = ./dms/colors.conf from your config to override.
bordercolor = 0x{{colors.outline.default.hex_stripped}}ff
focuscolor = 0x{{colors.primary.default.hex_stripped}}ff
urgentcolor = 0x{{colors.error.default.hex_stripped}}ff

View File

@@ -1,3 +1,6 @@
// ! Auto-generated file. Do not edit directly.
// Remove `include "dms/colors.kdl"` from your config to override.
layout {
background-color "transparent"

View File

@@ -3,6 +3,7 @@
//@ pragma Env QT_FFMPEG_DECODING_HW_DEVICE_TYPES=vaapi
//@ pragma Env QT_FFMPEG_ENCODING_HW_DEVICE_TYPES=vaapi
//@ pragma Env QT_WAYLAND_DISABLE_WINDOWDECORATION=1
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Material
//@ pragma UseQApplication
import QtQuick