diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config')
25 files changed, 2765 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go new file mode 100644 index 0000000..7cc513f --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go @@ -0,0 +1,718 @@ +package config + +import ( + "context" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" +) + +type ConfigDeployer struct { + logChan chan<- string +} + +type DeploymentResult struct { + ConfigType string + Path string + BackupPath string + Deployed bool + Error error +} + +func NewConfigDeployer(logChan chan<- string) *ConfigDeployer { + return &ConfigDeployer{ + logChan: logChan, + } +} + +func (cd *ConfigDeployer) log(message string) { + if cd.logChan != nil { + cd.logChan <- message + } +} + +// DeployConfigurations deploys all necessary configurations based on the chosen window manager +func (cd *ConfigDeployer) DeployConfigurations(ctx context.Context, wm deps.WindowManager) ([]DeploymentResult, error) { + return cd.DeployConfigurationsWithTerminal(ctx, wm, deps.TerminalGhostty) +} + +// DeployConfigurationsWithTerminal deploys all necessary configurations based on chosen window manager and terminal +func (cd *ConfigDeployer) DeployConfigurationsWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]DeploymentResult, error) { + return cd.DeployConfigurationsSelective(ctx, wm, terminal, nil, nil) +} + +// DeployConfigurationsWithSystemd deploys configurations with systemd option +func (cd *ConfigDeployer) DeployConfigurationsWithSystemd(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, useSystemd bool) ([]DeploymentResult, error) { + return cd.deployConfigurationsInternal(ctx, wm, terminal, nil, nil, nil, useSystemd) +} + +func (cd *ConfigDeployer) DeployConfigurationsSelective(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool) ([]DeploymentResult, error) { + return cd.DeployConfigurationsSelectiveWithReinstalls(ctx, wm, terminal, installedDeps, replaceConfigs, nil) +} + +func (cd *ConfigDeployer) DeployConfigurationsSelectiveWithReinstalls(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool, reinstallItems map[string]bool) ([]DeploymentResult, error) { + return cd.deployConfigurationsInternal(ctx, wm, terminal, installedDeps, replaceConfigs, reinstallItems, true) +} + +func (cd *ConfigDeployer) deployConfigurationsInternal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool, reinstallItems map[string]bool, useSystemd bool) ([]DeploymentResult, error) { + var results []DeploymentResult + + // Primary config file paths used to detect fresh installs. + configPrimaryPaths := map[string]string{ + "Niri": filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl"), + "Hyprland": filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf"), + "Ghostty": filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config"), + "Kitty": filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf"), + "Alacritty": filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml"), + } + + shouldReplaceConfig := func(configType string) bool { + if replaceConfigs == nil { + return true + } + replace, exists := replaceConfigs[configType] + if !exists || replace { + return true + } + // Config is explicitly set to "don't replace" — but still deploy + // if the config file doesn't exist yet (fresh install scenario). + if primaryPath, ok := configPrimaryPaths[configType]; ok { + if _, err := os.Stat(primaryPath); os.IsNotExist(err) { + return true + } + } + return false + } + + switch wm { + case deps.WindowManagerNiri: + if shouldReplaceConfig("Niri") { + result, err := cd.deployNiriConfig(terminal, useSystemd) + results = append(results, result) + if err != nil { + return results, fmt.Errorf("failed to deploy Niri config: %w", err) + } + } + case deps.WindowManagerHyprland: + if shouldReplaceConfig("Hyprland") { + result, err := cd.deployHyprlandConfig(terminal, useSystemd) + results = append(results, result) + if err != nil { + return results, fmt.Errorf("failed to deploy Hyprland config: %w", err) + } + } + } + + switch terminal { + case deps.TerminalGhostty: + if shouldReplaceConfig("Ghostty") { + ghosttyResults, err := cd.deployGhosttyConfig() + results = append(results, ghosttyResults...) + if err != nil { + return results, fmt.Errorf("failed to deploy Ghostty config: %w", err) + } + } + case deps.TerminalKitty: + if shouldReplaceConfig("Kitty") { + kittyResults, err := cd.deployKittyConfig() + results = append(results, kittyResults...) + if err != nil { + return results, fmt.Errorf("failed to deploy Kitty config: %w", err) + } + } + case deps.TerminalAlacritty: + if shouldReplaceConfig("Alacritty") { + alacrittyResults, err := cd.deployAlacrittyConfig() + results = append(results, alacrittyResults...) + if err != nil { + return results, fmt.Errorf("failed to deploy Alacritty config: %w", err) + } + } + } + + return results, nil +} + +func (cd *ConfigDeployer) deployNiriConfig(terminal deps.Terminal, useSystemd bool) (DeploymentResult, error) { + result := DeploymentResult{ + ConfigType: "Niri", + Path: filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl"), + } + + configDir := filepath.Dir(result.Path) + if err := os.MkdirAll(configDir, 0o755); err != nil { + result.Error = fmt.Errorf("failed to create config directory: %w", err) + return result, result.Error + } + + dmsDir := filepath.Join(configDir, "dms") + if err := os.MkdirAll(dmsDir, 0o755); 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 Niri configuration") + + existingData, err := os.ReadFile(result.Path) + if err != nil { + result.Error = fmt.Errorf("failed to read existing config: %w", err) + return result, result.Error + } + existingConfig = string(existingData) + + timestamp := time.Now().Format("2006-01-02_15-04-05") + result.BackupPath = result.Path + ".backup." + timestamp + if err := os.WriteFile(result.BackupPath, existingData, 0o644); err != nil { + result.Error = fmt.Errorf("failed to create backup: %w", err) + return result, result.Error + } + cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) + } + + var terminalCommand string + switch terminal { + case deps.TerminalGhostty: + terminalCommand = "ghostty" + case deps.TerminalKitty: + terminalCommand = "kitty" + case deps.TerminalAlacritty: + terminalCommand = "alacritty" + default: + terminalCommand = "ghostty" + } + + newConfig := strings.ReplaceAll(NiriConfig, "{{TERMINAL_COMMAND}}", terminalCommand) + + if !useSystemd { + newConfig = cd.transformNiriConfigForNonSystemd(newConfig, terminalCommand) + } + + if existingConfig != "" { + mergedConfig, err := cd.mergeNiriOutputSections(newConfig, existingConfig, dmsDir) + if err != nil { + cd.log(fmt.Sprintf("Warning: Failed to merge output sections: %v", err)) + } else { + newConfig = mergedConfig + cd.log("Successfully merged existing output sections") + } + } + + if err := os.WriteFile(result.Path, []byte(newConfig), 0o644); err != nil { + result.Error = fmt.Errorf("failed to write config: %w", err) + return result, result.Error + } + + if err := cd.deployNiriDmsConfigs(dmsDir, terminalCommand); err != nil { + result.Error = fmt.Errorf("failed to deploy dms configs: %w", err) + return result, result.Error + } + + result.Deployed = true + cd.log("Successfully deployed Niri configuration") + return result, nil +} + +func (cd *ConfigDeployer) deployNiriDmsConfigs(dmsDir, terminalCommand string) error { + configs := []struct { + name string + content string + }{ + {"colors.kdl", NiriColorsConfig}, + {"layout.kdl", NiriLayoutConfig}, + {"alttab.kdl", NiriAlttabConfig}, + {"binds.kdl", strings.ReplaceAll(NiriBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)}, + {"outputs.kdl", ""}, + {"cursor.kdl", ""}, + {"windowrules.kdl", ""}, + } + + for _, cfg := range configs { + path := filepath.Join(dmsDir, cfg.name) + // Skip if file already exists and is not empty to preserve user modifications + if info, err := os.Stat(path); err == nil && info.Size() > 0 { + cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name)) + continue + } + if err := os.WriteFile(path, []byte(cfg.content), 0o644); 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) deployGhosttyConfig() ([]DeploymentResult, error) { + var results []DeploymentResult + + mainResult := DeploymentResult{ + ConfigType: "Ghostty", + Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config"), + } + + configDir := filepath.Dir(mainResult.Path) + if err := os.MkdirAll(configDir, 0o755); err != nil { + mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + if _, err := os.Stat(mainResult.Path); err == nil { + cd.log("Found existing Ghostty configuration") + + existingData, err := os.ReadFile(mainResult.Path) + if err != nil { + mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + timestamp := time.Now().Format("2006-01-02_15-04-05") + mainResult.BackupPath = mainResult.Path + ".backup." + timestamp + if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to create backup: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) + } + + if err := os.WriteFile(mainResult.Path, []byte(GhosttyConfig), 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to write config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + mainResult.Deployed = true + cd.log("Successfully deployed Ghostty configuration") + results = append(results, mainResult) + + colorResult := DeploymentResult{ + ConfigType: "Ghostty Colors", + Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "themes", "dankcolors"), + } + + themesDir := filepath.Dir(colorResult.Path) + if err := os.MkdirAll(themesDir, 0o755); err != nil { + mainResult.Error = fmt.Errorf("failed to create themes directory: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + if err := os.WriteFile(colorResult.Path, []byte(GhosttyColorConfig), 0o644); err != nil { + colorResult.Error = fmt.Errorf("failed to write color config: %w", err) + return results, colorResult.Error + } + + colorResult.Deployed = true + cd.log("Successfully deployed Ghostty color configuration") + results = append(results, colorResult) + + return results, nil +} + +func (cd *ConfigDeployer) deployKittyConfig() ([]DeploymentResult, error) { + var results []DeploymentResult + + mainResult := DeploymentResult{ + ConfigType: "Kitty", + Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf"), + } + + configDir := filepath.Dir(mainResult.Path) + if err := os.MkdirAll(configDir, 0o755); err != nil { + mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + if _, err := os.Stat(mainResult.Path); err == nil { + cd.log("Found existing Kitty configuration") + + existingData, err := os.ReadFile(mainResult.Path) + if err != nil { + mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + timestamp := time.Now().Format("2006-01-02_15-04-05") + mainResult.BackupPath = mainResult.Path + ".backup." + timestamp + if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to create backup: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) + } + + if err := os.WriteFile(mainResult.Path, []byte(KittyConfig), 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to write config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + mainResult.Deployed = true + cd.log("Successfully deployed Kitty configuration") + results = append(results, mainResult) + + themeResult := DeploymentResult{ + ConfigType: "Kitty Theme", + Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-theme.conf"), + } + + if err := os.WriteFile(themeResult.Path, []byte(KittyThemeConfig), 0o644); err != nil { + themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) + return results, themeResult.Error + } + + themeResult.Deployed = true + cd.log("Successfully deployed Kitty theme configuration") + results = append(results, themeResult) + + tabsResult := DeploymentResult{ + ConfigType: "Kitty Tabs", + Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-tabs.conf"), + } + + if err := os.WriteFile(tabsResult.Path, []byte(KittyTabsConfig), 0o644); err != nil { + tabsResult.Error = fmt.Errorf("failed to write tabs config: %w", err) + return results, tabsResult.Error + } + + tabsResult.Deployed = true + cd.log("Successfully deployed Kitty tabs configuration") + results = append(results, tabsResult) + + return results, nil +} + +func (cd *ConfigDeployer) deployAlacrittyConfig() ([]DeploymentResult, error) { + var results []DeploymentResult + + mainResult := DeploymentResult{ + ConfigType: "Alacritty", + Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml"), + } + + configDir := filepath.Dir(mainResult.Path) + if err := os.MkdirAll(configDir, 0o755); err != nil { + mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + if _, err := os.Stat(mainResult.Path); err == nil { + cd.log("Found existing Alacritty configuration") + + existingData, err := os.ReadFile(mainResult.Path) + if err != nil { + mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + timestamp := time.Now().Format("2006-01-02_15-04-05") + mainResult.BackupPath = mainResult.Path + ".backup." + timestamp + if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to create backup: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) + } + + if err := os.WriteFile(mainResult.Path, []byte(AlacrittyConfig), 0o644); err != nil { + mainResult.Error = fmt.Errorf("failed to write config: %w", err) + return []DeploymentResult{mainResult}, mainResult.Error + } + + mainResult.Deployed = true + cd.log("Successfully deployed Alacritty configuration") + results = append(results, mainResult) + + themeResult := DeploymentResult{ + ConfigType: "Alacritty Theme", + Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "dank-theme.toml"), + } + + if err := os.WriteFile(themeResult.Path, []byte(AlacrittyThemeConfig), 0o644); err != nil { + themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) + return results, themeResult.Error + } + + themeResult.Deployed = true + cd.log("Successfully deployed Alacritty theme configuration") + results = append(results, themeResult) + + return results, nil +} + +func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig, dmsDir string) (string, error) { + outputRegex := regexp.MustCompile(`(?m)^(/-)?\s*output\s+"[^"]+"\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`) + existingOutputs := outputRegex.FindAllString(existingConfig, -1) + + if len(existingOutputs) == 0 { + return newConfig, nil + } + + 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()), 0o644); 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, "") + + inputEndRegex := regexp.MustCompile(`(?m)^}$`) + inputMatches := inputEndRegex.FindAllStringIndex(newConfig, -1) + + if len(inputMatches) < 1 { + return "", fmt.Errorf("could not find insertion point for output sections") + } + + insertPos := inputMatches[0][1] + + var builder strings.Builder + builder.WriteString(mergedConfig[:insertPos]) + builder.WriteString("\n// Outputs from existing configuration\n") + + for _, output := range existingOutputs { + builder.WriteString(output) + builder.WriteString("\n") + } + + builder.WriteString(mergedConfig[insertPos:]) + + return builder.String(), nil +} + +// deployHyprlandConfig handles Hyprland configuration deployment with backup and merging +func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal, useSystemd bool) (DeploymentResult, error) { + result := DeploymentResult{ + ConfigType: "Hyprland", + Path: filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf"), + } + + configDir := filepath.Dir(result.Path) + if err := os.MkdirAll(configDir, 0o755); err != nil { + result.Error = fmt.Errorf("failed to create config directory: %w", err) + return result, result.Error + } + + dmsDir := filepath.Join(configDir, "dms") + if err := os.MkdirAll(dmsDir, 0o755); 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") + + existingData, err := os.ReadFile(result.Path) + if err != nil { + result.Error = fmt.Errorf("failed to read existing config: %w", err) + return result, result.Error + } + existingConfig = string(existingData) + + timestamp := time.Now().Format("2006-01-02_15-04-05") + result.BackupPath = result.Path + ".backup." + timestamp + if err := os.WriteFile(result.BackupPath, existingData, 0o644); err != nil { + result.Error = fmt.Errorf("failed to create backup: %w", err) + return result, result.Error + } + cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) + } + + var terminalCommand string + switch terminal { + case deps.TerminalGhostty: + terminalCommand = "ghostty" + case deps.TerminalKitty: + terminalCommand = "kitty" + case deps.TerminalAlacritty: + terminalCommand = "alacritty" + default: + terminalCommand = "ghostty" + } + + newConfig := strings.ReplaceAll(HyprlandConfig, "{{TERMINAL_COMMAND}}", terminalCommand) + + if !useSystemd { + newConfig = cd.transformHyprlandConfigForNonSystemd(newConfig, terminalCommand) + } + + if existingConfig != "" { + mergedConfig, err := cd.mergeHyprlandMonitorSections(newConfig, existingConfig, dmsDir) + if err != nil { + cd.log(fmt.Sprintf("Warning: Failed to merge monitor sections: %v", err)) + } else { + newConfig = mergedConfig + cd.log("Successfully merged existing monitor sections") + } + } + + if err := os.WriteFile(result.Path, []byte(newConfig), 0o644); err != nil { + result.Error = fmt.Errorf("failed to write config: %w", err) + return result, result.Error + } + + if err := cd.deployHyprlandDmsConfigs(dmsDir, terminalCommand); 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 +} + +func (cd *ConfigDeployer) deployHyprlandDmsConfigs(dmsDir string, terminalCommand string) error { + configs := []struct { + name string + content string + }{ + {"colors.conf", HyprColorsConfig}, + {"layout.conf", HyprLayoutConfig}, + {"binds.conf", strings.ReplaceAll(HyprBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)}, + {"outputs.conf", ""}, + {"cursor.conf", ""}, + {"windowrules.conf", ""}, + } + + for _, cfg := range configs { + path := filepath.Join(dmsDir, cfg.name) + // Skip if file already exists and is not empty to preserve user modifications + if info, err := os.Stat(path); err == nil && info.Size() > 0 { + cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name)) + continue + } + if err := os.WriteFile(path, []byte(cfg.content), 0o644); 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) + + if len(existingMonitors) == 0 { + 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()), 0o644); 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, "") + + monitorHeaderRegex := regexp.MustCompile(`(?m)^# MONITOR CONFIG\n# ==================$`) + headerMatch := monitorHeaderRegex.FindStringIndex(mergedConfig) + + if headerMatch == nil { + return "", fmt.Errorf("could not find MONITOR CONFIG section") + } + + insertPos := headerMatch[1] + 1 + + var builder strings.Builder + builder.WriteString(mergedConfig[:insertPos]) + builder.WriteString("# Monitors from existing configuration\n") + + for _, monitor := range existingMonitors { + builder.WriteString(monitor) + builder.WriteString("\n") + } + + builder.WriteString(mergedConfig[insertPos:]) + + return builder.String(), nil +} + +func (cd *ConfigDeployer) transformHyprlandConfigForNonSystemd(config, terminalCommand string) string { + lines := strings.Split(config, "\n") + var result []string + startupSectionFound := false + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "exec-once = dbus-update-activation-environment") { + continue + } + if strings.HasPrefix(trimmed, "exec-once = systemctl --user start") { + startupSectionFound = true + result = append(result, "exec-once = dms run") + result = append(result, "env = QT_QPA_PLATFORM,wayland;xcb") + result = append(result, "env = ELECTRON_OZONE_PLATFORM_HINT,auto") + result = append(result, "env = QT_QPA_PLATFORMTHEME,gtk3") + result = append(result, "env = QT_QPA_PLATFORMTHEME_QT6,gtk3") + result = append(result, fmt.Sprintf("env = TERMINAL,%s", terminalCommand)) + continue + } + result = append(result, line) + } + + if !startupSectionFound { + for i, line := range result { + if strings.Contains(line, "STARTUP APPS") { + insertLines := []string{ + "exec-once = dms run", + "env = QT_QPA_PLATFORM,wayland;xcb", + "env = ELECTRON_OZONE_PLATFORM_HINT,auto", + "env = QT_QPA_PLATFORMTHEME,gtk3", + "env = QT_QPA_PLATFORMTHEME_QT6,gtk3", + fmt.Sprintf("env = TERMINAL,%s", terminalCommand), + } + result = append(result[:i+2], append(insertLines, result[i+2:]...)...) + break + } + } + } + + return strings.Join(result, "\n") +} + +func (cd *ConfigDeployer) transformNiriConfigForNonSystemd(config, terminalCommand string) string { + envVars := fmt.Sprintf(`environment { + XDG_CURRENT_DESKTOP "niri" + QT_QPA_PLATFORM "wayland;xcb" + ELECTRON_OZONE_PLATFORM_HINT "auto" + QT_QPA_PLATFORMTHEME "gtk3" + QT_QPA_PLATFORMTHEME_QT6 "gtk3" + TERMINAL "%s" +}`, terminalCommand) + + config = regexp.MustCompile(`environment \{[^}]*\}`).ReplaceAllString(config, envVars) + + spawnDms := `spawn-at-startup "dms" "run"` + if !strings.Contains(config, spawnDms) { + // Insert spawn-at-startup for dms after the environment block + envBlockEnd := regexp.MustCompile(`environment \{[^}]*\}`) + if loc := envBlockEnd.FindStringIndex(config); loc != nil { + config = config[:loc[1]] + "\n" + spawnDms + config[loc[1]:] + } + } + + return config +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go new file mode 100644 index 0000000..3726634 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go @@ -0,0 +1,792 @@ +package config + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMergeNiriOutputSections(t *testing.T) { + cd := &ConfigDeployer{} + + tests := []struct { + name string + newConfig string + existingConfig string + wantError bool + wantContains []string + }{ + { + name: "no existing outputs", + newConfig: `input { + keyboard { + xkb { + } + } +} +layout { + gaps 5 +}`, + existingConfig: `input { + keyboard { + xkb { + } + } +} +layout { + gaps 10 +}`, + wantError: false, + wantContains: []string{"gaps 5"}, // Should keep new config + }, + { + name: "merge single output", + newConfig: `input { + keyboard { + xkb { + } + } +} +/-output "eDP-2" { + mode "2560x1600@239.998993" + position x=2560 y=0 +} +layout { + gaps 5 +}`, + existingConfig: `input { + keyboard { + xkb { + } + } +} +output "eDP-1" { + mode "1920x1080@60.000000" + position x=0 y=0 + scale 1.0 +} +layout { + gaps 10 +}`, + wantError: false, + wantContains: []string{ + "gaps 5", // New config preserved + `output "eDP-1"`, // Existing output merged + "1920x1080@60.000000", // Existing output details + "Outputs from existing configuration", // Comment added + }, + }, + { + name: "merge multiple outputs", + newConfig: `input { + keyboard { + xkb { + } + } +} +/-output "eDP-2" { + mode "2560x1600@239.998993" + position x=2560 y=0 +} +layout { + gaps 5 +}`, + existingConfig: `input { + keyboard { + xkb { + } + } +} +output "eDP-1" { + mode "1920x1080@60.000000" + position x=0 y=0 + scale 1.0 +} +/-output "HDMI-1" { + mode "1920x1080@60.000000" + position x=1920 y=0 +} +layout { + gaps 10 +}`, + wantError: false, + wantContains: []string{ + "gaps 5", // New config preserved + `output "eDP-1"`, // First existing output + `/-output "HDMI-1"`, // Second existing output (commented) + "1920x1080@60.000000", // Output details + }, + }, + { + name: "merge commented outputs", + newConfig: `input { + keyboard { + xkb { + } + } +} +/-output "eDP-2" { + mode "2560x1600@239.998993" + position x=2560 y=0 +} +layout { + gaps 5 +}`, + existingConfig: `input { + keyboard { + xkb { + } + } +} +/-output "eDP-1" { + mode "1920x1080@60.000000" + position x=0 y=0 + scale 1.0 +} +layout { + gaps 10 +}`, + wantError: false, + wantContains: []string{ + "gaps 5", // New config preserved + `/-output "eDP-1"`, // Commented output preserved + "1920x1080@60.000000", // Output details + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := t.TempDir() + result, err := cd.mergeNiriOutputSections(tt.newConfig, tt.existingConfig, tmpDir) + + if tt.wantError { + assert.Error(t, err) + return + } + + require.NoError(t, err) + + for _, want := range tt.wantContains { + assert.Contains(t, result, want, "merged config should contain: %s", want) + } + + assert.NotContains(t, result, `/-output "eDP-2"`, "example output should be removed") + }) + } +} + +func TestConfigDeploymentFlow(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + t.Run("deploy ghostty config to empty directory", func(t *testing.T) { + results, err := cd.deployGhosttyConfig() + require.NoError(t, err) + require.Len(t, results, 2) + + mainResult := results[0] + assert.Equal(t, "Ghostty", mainResult.ConfigType) + assert.True(t, mainResult.Deployed) + assert.Empty(t, mainResult.BackupPath) + assert.FileExists(t, mainResult.Path) + + content, err := os.ReadFile(mainResult.Path) + require.NoError(t, err) + assert.Contains(t, string(content), "window-decoration = false") + + colorResult := results[1] + assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) + assert.True(t, colorResult.Deployed) + assert.FileExists(t, colorResult.Path) + + colorContent, err := os.ReadFile(colorResult.Path) + require.NoError(t, err) + assert.Contains(t, string(colorContent), "background = #101418") + }) + + t.Run("deploy ghostty config with existing file", func(t *testing.T) { + existingContent := "# Old config\nfont-size = 14\n" + ghosttyPath := getGhosttyPath() + err := os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(ghosttyPath, []byte(existingContent), 0o644) + require.NoError(t, err) + + results, err := cd.deployGhosttyConfig() + require.NoError(t, err) + require.Len(t, results, 2) + + mainResult := results[0] + assert.Equal(t, "Ghostty", mainResult.ConfigType) + assert.True(t, mainResult.Deployed) + assert.NotEmpty(t, mainResult.BackupPath) + assert.FileExists(t, mainResult.Path) + assert.FileExists(t, mainResult.BackupPath) + + backupContent, err := os.ReadFile(mainResult.BackupPath) + require.NoError(t, err) + assert.Equal(t, existingContent, string(backupContent)) + + newContent, err := os.ReadFile(mainResult.Path) + require.NoError(t, err) + assert.NotContains(t, string(newContent), "# Old config") + + colorResult := results[1] + assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) + assert.True(t, colorResult.Deployed) + assert.FileExists(t, colorResult.Path) + }) +} + +func getGhosttyPath() string { + return filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") +} + +func TestMergeHyprlandMonitorSections(t *testing.T) { + cd := &ConfigDeployer{} + + tests := []struct { + name string + newConfig string + existingConfig string + wantError bool + wantContains []string + wantNotContains []string + }{ + { + name: "no existing monitors", + newConfig: `# ================== +# MONITOR CONFIG +# ================== +# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 + +# ================== +# ENVIRONMENT VARS +# ================== +env = XDG_CURRENT_DESKTOP,niri`, + existingConfig: `# Some other config +input { + kb_layout = us +}`, + wantError: false, + wantContains: []string{"MONITOR CONFIG", "ENVIRONMENT VARS"}, + }, + { + name: "merge single monitor", + newConfig: `# ================== +# MONITOR CONFIG +# ================== +# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 + +# ================== +# ENVIRONMENT VARS +# ==================`, + existingConfig: `# My config +monitor = DP-1, 1920x1080@144, 0x0, 1 +input { + kb_layout = us +}`, + wantError: false, + wantContains: []string{ + "MONITOR CONFIG", + "monitor = DP-1, 1920x1080@144, 0x0, 1", + "Monitors from existing configuration", + }, + wantNotContains: []string{ + "monitor = eDP-2", // Example monitor should be removed + }, + }, + { + name: "merge multiple monitors", + newConfig: `# ================== +# MONITOR CONFIG +# ================== +# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 + +# ================== +# ENVIRONMENT VARS +# ==================`, + existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1 +# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1 +monitor = eDP-1, 2560x1440@165, auto, 1.25`, + wantError: false, + wantContains: []string{ + "monitor = DP-1", + "# monitor = HDMI-A-1", // Commented monitor preserved + "monitor = eDP-1", + "Monitors from existing configuration", + }, + wantNotContains: []string{ + "monitor = eDP-2", // Example monitor should be removed + }, + }, + { + name: "preserve commented monitors", + newConfig: `# ================== +# MONITOR CONFIG +# ================== +# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 + +# ==================`, + existingConfig: `# monitor = DP-1, 1920x1080@144, 0x0, 1 +# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1`, + wantError: false, + wantContains: []string{ + "# monitor = DP-1", + "# monitor = HDMI-A-1", + "Monitors from existing configuration", + }, + }, + { + name: "no monitor config section", + newConfig: `# Some config without monitor section +input { + kb_layout = us +}`, + existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1`, + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := t.TempDir() + result, err := cd.mergeHyprlandMonitorSections(tt.newConfig, tt.existingConfig, tmpDir) + + if tt.wantError { + assert.Error(t, err) + return + } + + require.NoError(t, err) + + for _, want := range tt.wantContains { + assert.Contains(t, result, want, "merged config should contain: %s", want) + } + + for _, notWant := range tt.wantNotContains { + assert.NotContains(t, result, notWant, "merged config should NOT contain: %s", notWant) + } + }) + } +} + +func TestHyprlandConfigDeployment(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-hyprland-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + t.Run("deploy hyprland config to empty directory", func(t *testing.T) { + result, err := cd.deployHyprlandConfig(deps.TerminalGhostty, true) + require.NoError(t, err) + + assert.Equal(t, "Hyprland", result.ConfigType) + assert.True(t, result.Deployed) + assert.Empty(t, result.BackupPath) + assert.FileExists(t, result.Path) + + content, err := os.ReadFile(result.Path) + require.NoError(t, err) + assert.Contains(t, string(content), "# MONITOR CONFIG") + assert.Contains(t, string(content), "source = ./dms/binds.conf") + assert.Contains(t, string(content), "exec-once = ") + }) + + t.Run("deploy hyprland config with existing monitors", func(t *testing.T) { + existingContent := `# My existing Hyprland config +monitor = DP-1, 1920x1080@144, 0x0, 1 +monitor = HDMI-A-1, 3840x2160@60, 1920x0, 1.5 + +general { + gaps_in = 10 +} +` + hyprPath := filepath.Join(tempDir, ".config", "hypr", "hyprland.conf") + err := os.MkdirAll(filepath.Dir(hyprPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(hyprPath, []byte(existingContent), 0o644) + require.NoError(t, err) + + result, err := cd.deployHyprlandConfig(deps.TerminalKitty, true) + require.NoError(t, err) + + assert.Equal(t, "Hyprland", result.ConfigType) + assert.True(t, result.Deployed) + assert.NotEmpty(t, result.BackupPath) + assert.FileExists(t, result.Path) + assert.FileExists(t, result.BackupPath) + + backupContent, err := os.ReadFile(result.BackupPath) + require.NoError(t, err) + assert.Equal(t, existingContent, string(backupContent)) + + newContent, err := os.ReadFile(result.Path) + require.NoError(t, err) + assert.Contains(t, string(newContent), "monitor = DP-1, 1920x1080@144") + assert.Contains(t, string(newContent), "monitor = HDMI-A-1, 3840x2160@60") + assert.Contains(t, string(newContent), "source = ./dms/binds.conf") + assert.NotContains(t, string(newContent), "monitor = eDP-2") + }) +} + +func TestNiriConfigStructure(t *testing.T) { + assert.Contains(t, NiriConfig, "input {") + assert.Contains(t, NiriConfig, "layout {") + + assert.Contains(t, NiriBindsConfig, "binds {") + assert.Contains(t, NiriBindsConfig, `spawn "{{TERMINAL_COMMAND}}"`) +} + +func TestHyprlandConfigStructure(t *testing.T) { + assert.Contains(t, HyprlandConfig, "# MONITOR CONFIG") + assert.Contains(t, HyprlandConfig, "# STARTUP APPS") + assert.Contains(t, HyprlandConfig, "# INPUT CONFIG") + assert.Contains(t, HyprlandConfig, "source = ./dms/binds.conf") +} + +func TestGhosttyConfigStructure(t *testing.T) { + assert.Contains(t, GhosttyConfig, "window-decoration = false") + assert.Contains(t, GhosttyConfig, "background-opacity = 1.0") + assert.Contains(t, GhosttyConfig, "theme = dankcolors") +} + +func TestGhosttyColorConfigStructure(t *testing.T) { + assert.Contains(t, GhosttyColorConfig, "background = #101418") + assert.Contains(t, GhosttyColorConfig, "foreground = #e0e2e8") + assert.Contains(t, GhosttyColorConfig, "cursor-color = #9dcbfb") + assert.Contains(t, GhosttyColorConfig, "palette = 0=#101418") + assert.Contains(t, GhosttyColorConfig, "palette = 15=#ffffff") +} + +func TestKittyConfigStructure(t *testing.T) { + assert.Contains(t, KittyConfig, "font_size 12.0") + assert.Contains(t, KittyConfig, "window_padding_width 12") + assert.Contains(t, KittyConfig, "background_opacity 1.0") + assert.Contains(t, KittyConfig, "include dank-tabs.conf") + assert.Contains(t, KittyConfig, "include dank-theme.conf") +} + +func TestKittyThemeConfigStructure(t *testing.T) { + assert.Contains(t, KittyThemeConfig, "foreground #e0e2e8") + assert.Contains(t, KittyThemeConfig, "background #101418") + assert.Contains(t, KittyThemeConfig, "cursor #e0e2e8") + assert.Contains(t, KittyThemeConfig, "color0 #101418") + assert.Contains(t, KittyThemeConfig, "color15 #ffffff") +} + +func TestKittyTabsConfigStructure(t *testing.T) { + assert.Contains(t, KittyTabsConfig, "tab_bar_style powerline") + assert.Contains(t, KittyTabsConfig, "tab_powerline_style slanted") + assert.Contains(t, KittyTabsConfig, "active_tab_background #124a73") + assert.Contains(t, KittyTabsConfig, "inactive_tab_background #101418") +} + +func TestAlacrittyConfigStructure(t *testing.T) { + assert.Contains(t, AlacrittyConfig, "[general]") + assert.Contains(t, AlacrittyConfig, "~/.config/alacritty/dank-theme.toml") + assert.Contains(t, AlacrittyConfig, "[window]") + assert.Contains(t, AlacrittyConfig, "decorations = \"None\"") + assert.Contains(t, AlacrittyConfig, "padding = { x = 12, y = 12 }") + assert.Contains(t, AlacrittyConfig, "[cursor]") + assert.Contains(t, AlacrittyConfig, "[keyboard]") +} + +func TestAlacrittyThemeConfigStructure(t *testing.T) { + assert.Contains(t, AlacrittyThemeConfig, "[colors.primary]") + assert.Contains(t, AlacrittyThemeConfig, "background = '#101418'") + assert.Contains(t, AlacrittyThemeConfig, "foreground = '#e0e2e8'") + assert.Contains(t, AlacrittyThemeConfig, "[colors.cursor]") + assert.Contains(t, AlacrittyThemeConfig, "cursor = '#9dcbfb'") + assert.Contains(t, AlacrittyThemeConfig, "[colors.normal]") + assert.Contains(t, AlacrittyThemeConfig, "[colors.bright]") +} + +func TestKittyConfigDeployment(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-kitty-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + t.Run("deploy kitty config to empty directory", func(t *testing.T) { + results, err := cd.deployKittyConfig() + require.NoError(t, err) + require.Len(t, results, 3) + + mainResult := results[0] + assert.Equal(t, "Kitty", mainResult.ConfigType) + assert.True(t, mainResult.Deployed) + assert.FileExists(t, mainResult.Path) + + content, err := os.ReadFile(mainResult.Path) + require.NoError(t, err) + assert.Contains(t, string(content), "include dank-theme.conf") + + themeResult := results[1] + assert.Equal(t, "Kitty Theme", themeResult.ConfigType) + assert.True(t, themeResult.Deployed) + assert.FileExists(t, themeResult.Path) + + tabsResult := results[2] + assert.Equal(t, "Kitty Tabs", tabsResult.ConfigType) + assert.True(t, tabsResult.Deployed) + assert.FileExists(t, tabsResult.Path) + }) +} + +func TestAlacrittyConfigDeployment(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-alacritty-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + t.Run("deploy alacritty config to empty directory", func(t *testing.T) { + results, err := cd.deployAlacrittyConfig() + require.NoError(t, err) + require.Len(t, results, 2) + + mainResult := results[0] + assert.Equal(t, "Alacritty", mainResult.ConfigType) + assert.True(t, mainResult.Deployed) + assert.FileExists(t, mainResult.Path) + + content, err := os.ReadFile(mainResult.Path) + require.NoError(t, err) + assert.Contains(t, string(content), "~/.config/alacritty/dank-theme.toml") + assert.Contains(t, string(content), "[window]") + + themeResult := results[1] + assert.Equal(t, "Alacritty Theme", themeResult.ConfigType) + assert.True(t, themeResult.Deployed) + assert.FileExists(t, themeResult.Path) + + themeContent, err := os.ReadFile(themeResult.Path) + require.NoError(t, err) + assert.Contains(t, string(themeContent), "[colors.primary]") + assert.Contains(t, string(themeContent), "background = '#101418'") + }) + + t.Run("deploy alacritty config with existing file", func(t *testing.T) { + existingContent := "# Old alacritty config\n[window]\nopacity = 0.9\n" + alacrittyPath := filepath.Join(tempDir, ".config", "alacritty", "alacritty.toml") + err := os.MkdirAll(filepath.Dir(alacrittyPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(alacrittyPath, []byte(existingContent), 0o644) + require.NoError(t, err) + + results, err := cd.deployAlacrittyConfig() + require.NoError(t, err) + require.Len(t, results, 2) + + mainResult := results[0] + assert.True(t, mainResult.Deployed) + assert.NotEmpty(t, mainResult.BackupPath) + assert.FileExists(t, mainResult.BackupPath) + + backupContent, err := os.ReadFile(mainResult.BackupPath) + require.NoError(t, err) + assert.Equal(t, existingContent, string(backupContent)) + + newContent, err := os.ReadFile(mainResult.Path) + require.NoError(t, err) + assert.NotContains(t, string(newContent), "# Old alacritty config") + assert.Contains(t, string(newContent), "decorations = \"None\"") + }) +} + +func TestShouldReplaceConfigDeployIfMissing(t *testing.T) { + allFalse := map[string]bool{ + "Niri": false, + "Hyprland": false, + "Ghostty": false, + "Kitty": false, + "Alacritty": false, + } + + t.Run("replaceConfigs nil deploys config", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-replace-nil-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + results, err := cd.DeployConfigurationsSelectiveWithReinstalls( + context.Background(), + deps.WindowManagerNiri, + deps.TerminalGhostty, + nil, // installedDeps + nil, // replaceConfigs + nil, // reinstallItems + ) + require.NoError(t, err) + + // With replaceConfigs=nil, all configs should be deployed + hasDeployed := false + for _, r := range results { + if r.Deployed { + hasDeployed = true + break + } + } + assert.True(t, hasDeployed, "expected at least one config to be deployed when replaceConfigs is nil") + }) + + t.Run("replaceConfigs all false and config missing deploys config", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-replace-missing-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + results, err := cd.DeployConfigurationsSelectiveWithReinstalls( + context.Background(), + deps.WindowManagerNiri, + deps.TerminalGhostty, + nil, // installedDeps + allFalse, // replaceConfigs — all false + nil, // reinstallItems + ) + require.NoError(t, err) + + // Config files don't exist on disk, so they should still be deployed + hasDeployed := false + for _, r := range results { + if r.Deployed { + hasDeployed = true + break + } + } + assert.True(t, hasDeployed, "expected configs to be deployed when files are missing, even with replaceConfigs all false") + }) + + t.Run("replaceConfigs false and config exists skips config", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-replace-exists-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + // Create the Ghostty primary config file so shouldReplaceConfig returns false + ghosttyPath := filepath.Join(tempDir, ".config", "ghostty", "config") + err = os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(ghosttyPath, []byte("# existing ghostty config\n"), 0o644) + require.NoError(t, err) + + // Also create the Niri primary config file + niriPath := filepath.Join(tempDir, ".config", "niri", "config.kdl") + err = os.MkdirAll(filepath.Dir(niriPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(niriPath, []byte("// existing niri config\n"), 0o644) + require.NoError(t, err) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + results, err := cd.DeployConfigurationsSelectiveWithReinstalls( + context.Background(), + deps.WindowManagerNiri, + deps.TerminalGhostty, + nil, // installedDeps + allFalse, // replaceConfigs — all false + nil, // reinstallItems + ) + require.NoError(t, err) + + // Both Niri and Ghostty config files exist, so with all false they should be skipped + for _, r := range results { + assert.Fail(t, "expected no configs to be deployed", "got deployed config: %s", r.ConfigType) + } + }) + + t.Run("replaceConfigs true and config exists deploys config", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "dankinstall-replace-true-test") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", originalHome) + + // Create the Ghostty primary config file + ghosttyPath := filepath.Join(tempDir, ".config", "ghostty", "config") + err = os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) + require.NoError(t, err) + err = os.WriteFile(ghosttyPath, []byte("# existing ghostty config\n"), 0o644) + require.NoError(t, err) + + logChan := make(chan string, 100) + cd := NewConfigDeployer(logChan) + + replaceConfigs := map[string]bool{ + "Niri": false, + "Hyprland": false, + "Ghostty": true, // explicitly true + "Kitty": false, + "Alacritty": false, + } + + results, err := cd.DeployConfigurationsSelectiveWithReinstalls( + context.Background(), + deps.WindowManagerNiri, + deps.TerminalGhostty, + nil, // installedDeps + replaceConfigs, // Ghostty=true, rest=false + nil, // reinstallItems + ) + require.NoError(t, err) + + // Ghostty should be deployed because replaceConfigs["Ghostty"]=true + foundGhostty := false + for _, r := range results { + if r.ConfigType == "Ghostty" && r.Deployed { + foundGhostty = true + } + } + assert.True(t, foundGhostty, "expected Ghostty config to be deployed when replaceConfigs is true") + }) +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go new file mode 100644 index 0000000..7842fa5 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go @@ -0,0 +1,57 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +func LocateDMSConfig() (string, error) { + var primaryPaths []string + + configHome, err := os.UserConfigDir() + if err == nil && configHome != "" { + primaryPaths = append(primaryPaths, filepath.Join(configHome, "quickshell", "dms")) + } + + // System data directories + dataDirs := os.Getenv("XDG_DATA_DIRS") + if dataDirs == "" { + dataDirs = "/usr/local/share:/usr/share" + } + + for dir := range strings.SplitSeq(dataDirs, ":") { + if dir != "" { + primaryPaths = append(primaryPaths, filepath.Join(dir, "quickshell", "dms")) + } + } + + // System config directories (fallback) + configDirs := os.Getenv("XDG_CONFIG_DIRS") + if configDirs == "" { + configDirs = "/etc/xdg" + } + + for dir := range strings.SplitSeq(configDirs, ":") { + if dir != "" { + primaryPaths = append(primaryPaths, filepath.Join(dir, "quickshell", "dms")) + } + } + + // Build search paths with secondary (monorepo) paths interleaved + var searchPaths []string + for _, path := range primaryPaths { + searchPaths = append(searchPaths, path) + searchPaths = append(searchPaths, filepath.Join(path, "quickshell")) + } + + for _, path := range searchPaths { + shellPath := filepath.Join(path, "shell.qml") + if info, err := os.Stat(shellPath); err == nil && !info.IsDir() { + return path, nil + } + } + + return "", fmt.Errorf("could not find DMS config (shell.qml) in any valid config path") +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml new file mode 100644 index 0000000..4048457 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml @@ -0,0 +1,31 @@ +[colors.primary] +background = '#101418' +foreground = '#e0e2e8' + +[colors.selection] +text = '#e0e2e8' +background = '#124a73' + +[colors.cursor] +text = '#101418' +cursor = '#9dcbfb' + +[colors.normal] +black = '#101418' +red = '#d75a59' +green = '#8ed88c' +yellow = '#e0d99d' +blue = '#4087bc' +magenta = '#839fbc' +cyan = '#9dcbfb' +white = '#abb2bf' + +[colors.bright] +black = '#5c6370' +red = '#e57e7e' +green = '#a2e5a0' +yellow = '#efe9b3' +blue = '#a7d9ff' +magenta = '#3d8197' +cyan = '#5c7ba3' +white = '#ffffff' diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml new file mode 100644 index 0000000..b666177 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml @@ -0,0 +1,37 @@ +[general] +import = [ + "~/.config/alacritty/dank-theme.toml" +] + +[window] +decorations = "None" +padding = { x = 12, y = 12 } +opacity = 1.0 + +[scrolling] +history = 3023 + +[cursor] +style = { shape = "Block", blinking = "On" } +blink_interval = 500 +unfocused_hollow = true + +[mouse] +hide_when_typing = true + +[selection] +save_to_clipboard = false + +[bell] +duration = 0 + +[keyboard] +bindings = [ + { key = "C", mods = "Control|Shift", action = "Copy" }, + { key = "V", mods = "Control|Shift", action = "Paste" }, + { key = "N", mods = "Control|Shift", action = "SpawnNewInstance" }, + { key = "Equals", mods = "Control|Shift", action = "IncreaseFontSize" }, + { key = "Minus", mods = "Control", action = "DecreaseFontSize" }, + { key = "Key0", mods = "Control", action = "ResetFontSize" }, + { key = "Enter", mods = "Shift", chars = "\n" }, +] diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf new file mode 100644 index 0000000..56dca5f --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf @@ -0,0 +1,21 @@ +background = #101418 +foreground = #e0e2e8 +cursor-color = #9dcbfb +selection-background = #124a73 +selection-foreground = #e0e2e8 +palette = 0=#101418 +palette = 1=#d75a59 +palette = 2=#8ed88c +palette = 3=#e0d99d +palette = 4=#4087bc +palette = 5=#839fbc +palette = 6=#9dcbfb +palette = 7=#abb2bf +palette = 8=#5c6370 +palette = 9=#e57e7e +palette = 10=#a2e5a0 +palette = 11=#efe9b3 +palette = 12=#a7d9ff +palette = 13=#3d8197 +palette = 14=#5c7ba3 +palette = 15=#ffffff diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf new file mode 100644 index 0000000..8ca05a2 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf @@ -0,0 +1,51 @@ +# Font Configuration +font-size = 12 + +# Window Configuration +window-decoration = false +window-padding-x = 12 +window-padding-y = 12 +background-opacity = 1.0 +background-blur-radius = 32 + +# Cursor Configuration +cursor-style = block +cursor-style-blink = true + +# Scrollback +scrollback-limit = 3023 + +# Terminal features +mouse-hide-while-typing = true +copy-on-select = false +confirm-close-surface = false + +# Disable annoying copied to clipboard +app-notifications = no-clipboard-copy,no-config-reload + +# Key bindings for common actions +#keybind = ctrl+c=copy_to_clipboard +#keybind = ctrl+v=paste_from_clipboard +keybind = ctrl+shift+n=new_window +keybind = ctrl+t=new_tab +keybind = ctrl+plus=increase_font_size:1 +keybind = ctrl+minus=decrease_font_size:1 +keybind = ctrl+zero=reset_font_size + +# Material 3 UI elements +unfocused-split-opacity = 0.7 +unfocused-split-fill = #44464f + +# Tab configuration +gtk-titlebar = false + +# Shell integration +shell-integration = detect +shell-integration-features = cursor,sudo,title,no-cursor +keybind = shift+enter=text:\n + +# Rando stuff +gtk-single-instance = true + +# Dank color generation +theme = dankcolors diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf new file mode 100644 index 0000000..dfe5630 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf @@ -0,0 +1,162 @@ +# === Application Launchers === +bind = SUPER, T, exec, {{TERMINAL_COMMAND}} +bind = SUPER, space, exec, dms ipc call spotlight toggle +bind = SUPER, V, exec, dms ipc call clipboard toggle +bind = SUPER, M, exec, dms ipc call processlist focusOrToggle +bind = SUPER, comma, exec, dms ipc call settings focusOrToggle +bind = SUPER, N, exec, dms ipc call notifications toggle +bind = SUPER SHIFT, N, exec, dms ipc call notepad toggle +bind = SUPER, Y, exec, dms ipc call dankdash wallpaper +bind = SUPER, TAB, exec, dms ipc call hypr toggleOverview +bind = SUPER, X, exec, dms ipc call powermenu toggle + +# === Cheat sheet +bind = SUPER SHIFT, Slash, exec, dms ipc call keybinds toggle hyprland + +# === Security === +bind = SUPER ALT, L, exec, dms ipc call lock lock +bind = SUPER SHIFT, E, exit +bind = CTRL ALT, Delete, exec, dms ipc call processlist focusOrToggle + +# === Audio Controls === +bindel = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3 +bindel = , XF86AudioLowerVolume, exec, dms ipc call audio decrement 3 +bindl = , XF86AudioMute, exec, dms ipc call audio mute +bindl = , XF86AudioMicMute, exec, dms ipc call audio micmute +bindl = , XF86AudioPause, exec, dms ipc call mpris playPause +bindl = , XF86AudioPlay, exec, dms ipc call mpris playPause +bindl = , XF86AudioPrev, exec, dms ipc call mpris previous +bindl = , XF86AudioNext, exec, dms ipc call mpris next +bindel = CTRL, XF86AudioRaiseVolume, exec, dms ipc call mpris increment 3 +bindel = CTRL, XF86AudioLowerVolume, exec, dms ipc call mpris decrement 3 + +# === Brightness Controls === +bindel = , XF86MonBrightnessUp, exec, dms ipc call brightness increment 5 "" +bindel = , XF86MonBrightnessDown, exec, dms ipc call brightness decrement 5 "" + +# === Window Management === +bind = SUPER, Q, killactive +bind = SUPER, F, fullscreen, 1 +bind = SUPER SHIFT, F, fullscreen, 0 +bind = SUPER SHIFT, T, togglefloating +bind = SUPER, W, togglegroup +bind = SUPER SHIFT, W, exec, dms ipc call window-rules toggle + +# === Focus Navigation === +bind = SUPER, left, movefocus, l +bind = SUPER, down, movefocus, d +bind = SUPER, up, movefocus, u +bind = SUPER, right, movefocus, r +bind = SUPER, H, movefocus, l +bind = SUPER, J, movefocus, d +bind = SUPER, K, movefocus, u +bind = SUPER, L, movefocus, r + +# === Window Movement === +bind = SUPER SHIFT, left, movewindow, l +bind = SUPER SHIFT, down, movewindow, d +bind = SUPER SHIFT, up, movewindow, u +bind = SUPER SHIFT, right, movewindow, r +bind = SUPER SHIFT, H, movewindow, l +bind = SUPER SHIFT, J, movewindow, d +bind = SUPER SHIFT, K, movewindow, u +bind = SUPER SHIFT, L, movewindow, r + +# === Column Navigation === +bind = SUPER, Home, focuswindow, first +bind = SUPER, End, focuswindow, last + +# === Monitor Navigation === +bind = SUPER CTRL, left, focusmonitor, l +bind = SUPER CTRL, right, focusmonitor, r +bind = SUPER CTRL, H, focusmonitor, l +bind = SUPER CTRL, J, focusmonitor, d +bind = SUPER CTRL, K, focusmonitor, u +bind = SUPER CTRL, L, focusmonitor, r + +# === Move to Monitor === +bind = SUPER SHIFT CTRL, left, movewindow, mon:l +bind = SUPER SHIFT CTRL, down, movewindow, mon:d +bind = SUPER SHIFT CTRL, up, movewindow, mon:u +bind = SUPER SHIFT CTRL, right, movewindow, mon:r +bind = SUPER SHIFT CTRL, H, movewindow, mon:l +bind = SUPER SHIFT CTRL, J, movewindow, mon:d +bind = SUPER SHIFT CTRL, K, movewindow, mon:u +bind = SUPER SHIFT CTRL, L, movewindow, mon:r + +# === Workspace Navigation === +bind = SUPER, Page_Down, workspace, e+1 +bind = SUPER, Page_Up, workspace, e-1 +bind = SUPER, U, workspace, e+1 +bind = SUPER, I, workspace, e-1 +bind = SUPER CTRL, down, movetoworkspace, e+1 +bind = SUPER CTRL, up, movetoworkspace, e-1 +bind = SUPER CTRL, U, movetoworkspace, e+1 +bind = SUPER CTRL, I, movetoworkspace, e-1 + +# === Workspace Management === +bind = CTRL SHIFT, R, exec, dms ipc call workspace-rename open + +# === Move Workspaces === +bind = SUPER SHIFT, Page_Down, movetoworkspace, e+1 +bind = SUPER SHIFT, Page_Up, movetoworkspace, e-1 +bind = SUPER SHIFT, U, movetoworkspace, e+1 +bind = SUPER SHIFT, I, movetoworkspace, e-1 + +# === Mouse Wheel Navigation === +bind = SUPER, mouse_down, workspace, e+1 +bind = SUPER, mouse_up, workspace, e-1 +bind = SUPER CTRL, mouse_down, movetoworkspace, e+1 +bind = SUPER CTRL, mouse_up, movetoworkspace, e-1 + +# === Numbered Workspaces === +bind = SUPER, 1, workspace, 1 +bind = SUPER, 2, workspace, 2 +bind = SUPER, 3, workspace, 3 +bind = SUPER, 4, workspace, 4 +bind = SUPER, 5, workspace, 5 +bind = SUPER, 6, workspace, 6 +bind = SUPER, 7, workspace, 7 +bind = SUPER, 8, workspace, 8 +bind = SUPER, 9, workspace, 9 + +# === Move to Numbered Workspaces === +bind = SUPER SHIFT, 1, movetoworkspace, 1 +bind = SUPER SHIFT, 2, movetoworkspace, 2 +bind = SUPER SHIFT, 3, movetoworkspace, 3 +bind = SUPER SHIFT, 4, movetoworkspace, 4 +bind = SUPER SHIFT, 5, movetoworkspace, 5 +bind = SUPER SHIFT, 6, movetoworkspace, 6 +bind = SUPER SHIFT, 7, movetoworkspace, 7 +bind = SUPER SHIFT, 8, movetoworkspace, 8 +bind = SUPER SHIFT, 9, movetoworkspace, 9 + +# === Column Management === +bind = SUPER, bracketleft, layoutmsg, preselect l +bind = SUPER, bracketright, layoutmsg, preselect r + +# === Sizing & Layout === +bind = SUPER, R, layoutmsg, togglesplit +bind = SUPER CTRL, F, resizeactive, exact 100% 100% + +# === Move/resize windows with mainMod + LMB/RMB and dragging === +bindmd = SUPER, mouse:272, Move window, movewindow +bindmd = SUPER, mouse:273, Resize window, resizewindow + +# === Move/resize windows with mainMod + LMB/RMB and dragging === +bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 +bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 + +# === Manual Sizing === +binde = SUPER, minus, resizeactive, -10% 0 +binde = SUPER, equal, resizeactive, 10% 0 +binde = SUPER SHIFT, minus, resizeactive, 0 -10% +binde = SUPER SHIFT, equal, resizeactive, 0 10% + +# === Screenshots === +bind = , Print, exec, dms screenshot +bind = CTRL, Print, exec, dms screenshot full +bind = ALT, Print, exec, dms screenshot window + +# === System Controls === +bind = SUPER SHIFT, P, dpms, toggle diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf new file mode 100644 index 0000000..38bcb0e --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf @@ -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 + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf new file mode 100644 index 0000000..b9a8ad9 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf @@ -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 +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf new file mode 100644 index 0000000..f4e7a03 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf @@ -0,0 +1,121 @@ +# Hyprland Configuration +# https://wiki.hypr.land/Configuring/ + +# ================== +# MONITOR CONFIG +# ================== +# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 +monitor = , preferred,auto,auto + +# ================== +# STARTUP APPS +# ================== +exec-once = dbus-update-activation-environment --systemd --all +exec-once = systemctl --user start hyprland-session.target + +# ================== +# INPUT CONFIG +# ================== +input { + kb_layout = us + numlock_by_default = true +} + +# ================== +# GENERAL LAYOUT +# ================== +general { + gaps_in = 5 + gaps_out = 5 + border_size = 2 + + layout = dwindle +} + +# ================== +# DECORATION +# ================== +decoration { + rounding = 12 + + active_opacity = 1.0 + inactive_opacity = 1.0 + + shadow { + enabled = true + range = 30 + render_power = 5 + offset = 0 5 + color = rgba(00000070) + } +} + +# ================== +# ANIMATIONS +# ================== +animations { + enabled = true + + animation = windowsIn, 1, 3, default + animation = windowsOut, 1, 3, default + animation = workspaces, 1, 5, default + animation = windowsMove, 1, 4, default + animation = fade, 1, 3, default + animation = border, 1, 3, default +} + +# ================== +# LAYOUTS +# ================== +dwindle { + preserve_split = true +} + +master { + mfact = 0.5 +} + +# ================== +# MISC +# ================== +misc { + disable_hyprland_logo = true + disable_splash_rendering = true +} + +# ================== +# WINDOW RULES +# ================== +windowrule = tile on, match:class ^(org\.wezfurlong\.wezterm)$ + +windowrule = rounding 12, match:class ^(org\.gnome\.) + +windowrule = tile on, match:class ^(gnome-control-center)$ +windowrule = tile on, match:class ^(pavucontrol)$ +windowrule = tile on, match:class ^(nm-connection-editor)$ + +windowrule = float on, match:class ^(org\.gnome\.Calculator)$ +windowrule = float on, match:class ^(gnome-calculator)$ +windowrule = float on, match:class ^(galculator)$ +windowrule = float on, match:class ^(blueman-manager)$ +windowrule = float on, match:class ^(org\.gnome\.Nautilus)$ +windowrule = float on, match:class ^(xdg-desktop-portal)$ + +windowrule = no_initial_focus on, match:class ^(steam)$, match:title ^(notificationtoasts) +windowrule = pin on, match:class ^(steam)$, match:title ^(notificationtoasts) + +windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$ +windowrule = float on, match:class ^(zoom)$ + +# DMS windows floating by default +# ! Hyprland doesn't size these windows correctly so disabling by default here +# windowrule = float on, match:class ^(org.quickshell)$ + +layerrule = no_anim on, match:namespace ^(quickshell)$ +layerrule = no_anim on, match:namespace ^dms:.* + +source = ./dms/colors.conf +source = ./dms/outputs.conf +source = ./dms/layout.conf +source = ./dms/cursor.conf +source = ./dms/binds.conf diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf new file mode 100644 index 0000000..b2305c8 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf @@ -0,0 +1,24 @@ +tab_bar_edge top +tab_bar_style powerline +tab_powerline_style slanted +tab_bar_align left +tab_bar_min_tabs 2 +tab_bar_margin_width 0.0 +tab_bar_margin_height 2.5 1.5 +tab_bar_margin_color #101418 + +tab_bar_background #101418 + +active_tab_foreground #cfe5ff +active_tab_background #124a73 +active_tab_font_style bold + +inactive_tab_foreground #c2c7cf +inactive_tab_background #101418 +inactive_tab_font_style normal + +tab_activity_symbol " ● " +tab_numbers_style 1 + +tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" +active_tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf new file mode 100644 index 0000000..2587bcf --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf @@ -0,0 +1,24 @@ +cursor #e0e2e8 +cursor_text_color #c2c7cf + +foreground #e0e2e8 +background #101418 +selection_foreground #243240 +selection_background #b9c8da +url_color #9dcbfb +color0 #101418 +color1 #d75a59 +color2 #8ed88c +color3 #e0d99d +color4 #4087bc +color5 #839fbc +color6 #9dcbfb +color7 #abb2bf +color8 #5c6370 +color9 #e57e7e +color10 #a2e5a0 +color11 #efe9b3 +color12 #a7d9ff +color13 #3d8197 +color14 #5c7ba3 +color15 #ffffff diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf new file mode 100644 index 0000000..2ae96a0 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf @@ -0,0 +1,37 @@ +# Font Configuration +font_size 12.0 + +# Window Configuration +window_padding_width 12 +background_opacity 1.0 +background_blur 32 +hide_window_decorations yes + +# Cursor Configuration +cursor_shape block +cursor_blink_interval 1 + +# Scrollback +scrollback_lines 3000 + +# Terminal features +copy_on_select yes +strip_trailing_spaces smart + +# Key bindings for common actions +map ctrl+shift+n new_window +map ctrl+t new_tab +map ctrl+plus change_font_size all +1.0 +map ctrl+minus change_font_size all -1.0 +map ctrl+0 change_font_size all 0 + +# Tab configuration +tab_bar_style powerline +tab_bar_align left + +# Shell integration +shell_integration enabled + +# Dank color generation +include dank-tabs.conf +include dank-theme.conf diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl new file mode 100644 index 0000000..5f9bdcd --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl @@ -0,0 +1,10 @@ +// ! DO NOT EDIT ! +// ! AUTO-GENERATED BY DMS ! +// ! CHANGES WILL BE OVERWRITTEN ! +// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! + +recent-windows { + highlight { + corner-radius 12 + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl new file mode 100644 index 0000000..27cb860 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl @@ -0,0 +1,221 @@ +binds { + // === System & Overview === + Mod+D repeat=false { toggle-overview; } + Mod+Tab repeat=false { toggle-overview; } + Mod+Shift+Slash { show-hotkey-overlay; } + + // === Application Launchers === + Mod+T hotkey-overlay-title="Open Terminal" { spawn "{{TERMINAL_COMMAND}}"; } + Mod+Space hotkey-overlay-title="Application Launcher" { + spawn "dms" "ipc" "call" "spotlight" "toggle"; + } + Mod+V hotkey-overlay-title="Clipboard Manager" { + spawn "dms" "ipc" "call" "clipboard" "toggle"; + } + Mod+M hotkey-overlay-title="Task Manager" { + spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; + } + + Super+X hotkey-overlay-title="Power Menu: Toggle" { spawn "dms" "ipc" "call" "powermenu" "toggle"; } + Mod+Comma hotkey-overlay-title="Settings" { + spawn "dms" "ipc" "call" "settings" "focusOrToggle"; + } + Mod+Y hotkey-overlay-title="Browse Wallpapers" { + spawn "dms" "ipc" "call" "dankdash" "wallpaper"; + } + Mod+N hotkey-overlay-title="Notification Center" { spawn "dms" "ipc" "call" "notifications" "toggle"; } + Mod+Shift+N hotkey-overlay-title="Notepad" { spawn "dms" "ipc" "call" "notepad" "toggle"; } + + // === Security === + Mod+Alt+L hotkey-overlay-title="Lock Screen" { + spawn "dms" "ipc" "call" "lock" "lock"; + } + Mod+Shift+E { quit; } + Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" { + spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; + } + + // === Audio Controls === + XF86AudioRaiseVolume allow-when-locked=true { + spawn "dms" "ipc" "call" "audio" "increment" "3"; + } + XF86AudioLowerVolume allow-when-locked=true { + spawn "dms" "ipc" "call" "audio" "decrement" "3"; + } + XF86AudioMute allow-when-locked=true { + spawn "dms" "ipc" "call" "audio" "mute"; + } + XF86AudioMicMute allow-when-locked=true { + spawn "dms" "ipc" "call" "audio" "micmute"; + } + XF86AudioPause allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "playPause"; + } + XF86AudioPlay allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "playPause"; + } + XF86AudioPrev allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "previous"; + } + XF86AudioNext allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "next"; + } + Ctrl+XF86AudioRaiseVolume allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "increment" "3"; + } + Ctrl+XF86AudioLowerVolume allow-when-locked=true { + spawn "dms" "ipc" "call" "mpris" "decrement" "3"; + } + + // === Brightness Controls === + XF86MonBrightnessUp allow-when-locked=true { + spawn "dms" "ipc" "call" "brightness" "increment" "5" ""; + } + XF86MonBrightnessDown allow-when-locked=true { + spawn "dms" "ipc" "call" "brightness" "decrement" "5" ""; + } + + // === Window Management === + Mod+Q repeat=false { close-window; } + Mod+F { maximize-column; } + Mod+Shift+F { fullscreen-window; } + Mod+Shift+T { toggle-window-floating; } + Mod+Shift+V { switch-focus-between-floating-and-tiling; } + Mod+W { toggle-column-tabbed-display; } + Mod+Shift+W hotkey-overlay-title="Create window rule" { spawn "dms" "ipc" "call" "window-rules" "toggle"; } + + // === Focus Navigation === + Mod+Left { focus-column-left; } + Mod+Down { focus-window-down; } + Mod+Up { focus-window-up; } + Mod+Right { focus-column-right; } + Mod+H { focus-column-left; } + Mod+J { focus-window-down; } + Mod+K { focus-window-up; } + Mod+L { focus-column-right; } + + // === Window Movement === + Mod+Shift+Left { move-column-left; } + Mod+Shift+Down { move-window-down; } + Mod+Shift+Up { move-window-up; } + Mod+Shift+Right { move-column-right; } + Mod+Shift+H { move-column-left; } + Mod+Shift+J { move-window-down; } + Mod+Shift+K { move-window-up; } + Mod+Shift+L { move-column-right; } + + // === Column Navigation === + Mod+Home { focus-column-first; } + Mod+End { focus-column-last; } + Mod+Ctrl+Home { move-column-to-first; } + Mod+Ctrl+End { move-column-to-last; } + + // === Monitor Navigation === + Mod+Ctrl+Left { focus-monitor-left; } + //Mod+Ctrl+Down { focus-monitor-down; } + //Mod+Ctrl+Up { focus-monitor-up; } + Mod+Ctrl+Right { focus-monitor-right; } + Mod+Ctrl+H { focus-monitor-left; } + Mod+Ctrl+J { focus-monitor-down; } + Mod+Ctrl+K { focus-monitor-up; } + Mod+Ctrl+L { focus-monitor-right; } + + // === Move to Monitor === + Mod+Shift+Ctrl+Left { move-column-to-monitor-left; } + Mod+Shift+Ctrl+Down { move-column-to-monitor-down; } + Mod+Shift+Ctrl+Up { move-column-to-monitor-up; } + Mod+Shift+Ctrl+Right { move-column-to-monitor-right; } + Mod+Shift+Ctrl+H { move-column-to-monitor-left; } + Mod+Shift+Ctrl+J { move-column-to-monitor-down; } + Mod+Shift+Ctrl+K { move-column-to-monitor-up; } + Mod+Shift+Ctrl+L { move-column-to-monitor-right; } + + // === Workspace Navigation === + Mod+Page_Down { focus-workspace-down; } + Mod+Page_Up { focus-workspace-up; } + Mod+U { focus-workspace-down; } + Mod+I { focus-workspace-up; } + Mod+Ctrl+Down { move-column-to-workspace-down; } + Mod+Ctrl+Up { move-column-to-workspace-up; } + Mod+Ctrl+U { move-column-to-workspace-down; } + Mod+Ctrl+I { move-column-to-workspace-up; } + + // === Workspace Management === + Ctrl+Shift+R hotkey-overlay-title="Rename Workspace" { + spawn "dms" "ipc" "call" "workspace-rename" "open"; + } + + // === Move Workspaces === + Mod+Shift+Page_Down { move-workspace-down; } + Mod+Shift+Page_Up { move-workspace-up; } + Mod+Shift+U { move-workspace-down; } + Mod+Shift+I { move-workspace-up; } + + // === Mouse Wheel Navigation === + Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } + Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } + Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } + Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } + + Mod+WheelScrollRight { focus-column-right; } + Mod+WheelScrollLeft { focus-column-left; } + Mod+Ctrl+WheelScrollRight { move-column-right; } + Mod+Ctrl+WheelScrollLeft { move-column-left; } + + Mod+Shift+WheelScrollDown { focus-column-right; } + Mod+Shift+WheelScrollUp { focus-column-left; } + Mod+Ctrl+Shift+WheelScrollDown { move-column-right; } + Mod+Ctrl+Shift+WheelScrollUp { move-column-left; } + + // === Numbered Workspaces === + Mod+1 { focus-workspace 1; } + Mod+2 { focus-workspace 2; } + Mod+3 { focus-workspace 3; } + Mod+4 { focus-workspace 4; } + Mod+5 { focus-workspace 5; } + Mod+6 { focus-workspace 6; } + Mod+7 { focus-workspace 7; } + Mod+8 { focus-workspace 8; } + Mod+9 { focus-workspace 9; } + + // === Move to Numbered Workspaces === + Mod+Shift+1 { move-column-to-workspace 1; } + Mod+Shift+2 { move-column-to-workspace 2; } + Mod+Shift+3 { move-column-to-workspace 3; } + Mod+Shift+4 { move-column-to-workspace 4; } + Mod+Shift+5 { move-column-to-workspace 5; } + Mod+Shift+6 { move-column-to-workspace 6; } + Mod+Shift+7 { move-column-to-workspace 7; } + Mod+Shift+8 { move-column-to-workspace 8; } + Mod+Shift+9 { move-column-to-workspace 9; } + + // === Column Management === + Mod+BracketLeft { consume-or-expel-window-left; } + Mod+BracketRight { consume-or-expel-window-right; } + Mod+Period { expel-window-from-column; } + + // === Sizing & Layout === + Mod+R { switch-preset-column-width; } + Mod+Shift+R { switch-preset-window-height; } + Mod+Ctrl+R { reset-window-height; } + Mod+Ctrl+F { expand-column-to-available-width; } + Mod+C { center-column; } + Mod+Ctrl+C { center-visible-columns; } + + // === Manual Sizing === + Mod+Minus { set-column-width "-10%"; } + Mod+Equal { set-column-width "+10%"; } + Mod+Shift+Minus { set-window-height "-10%"; } + Mod+Shift+Equal { set-window-height "+10%"; } + + // === Screenshots === + XF86Launch1 { screenshot; } + Ctrl+XF86Launch1 { screenshot-screen; } + Alt+XF86Launch1 { screenshot-window; } + Print { screenshot; } + Ctrl+Print { screenshot-screen; } + Alt+Print { screenshot-window; } + // === System Controls === + Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } + Mod+Shift+P { power-off-monitors; } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl new file mode 100644 index 0000000..145a179 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl @@ -0,0 +1,39 @@ +// ! 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 "#d0bcff" + inactive-color "#948f99" + urgent-color "#f2b8b5" + } + + border { + active-color "#d0bcff" + inactive-color "#948f99" + urgent-color "#f2b8b5" + } + + shadow { + color "#00000070" + } + + tab-indicator { + active-color "#d0bcff" + inactive-color "#948f99" + urgent-color "#f2b8b5" + } + + insert-hint { + color "#d0bcff80" + } +} + +recent-windows { + highlight { + active-color "#4f378b" + urgent-color "#f2b8b5" + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl new file mode 100644 index 0000000..5653a16 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl @@ -0,0 +1,17 @@ +hotkey-overlay { + skip-at-startup +} + +environment { + DMS_RUN_GREETER "1" +} + +gestures { + hot-corners { + off + } +} + +layout { + background-color "#000000" +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl new file mode 100644 index 0000000..1951500 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl @@ -0,0 +1,22 @@ +// ! DO NOT EDIT ! +// ! AUTO-GENERATED BY DMS ! +// ! CHANGES WILL BE OVERWRITTEN ! +// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! + +layout { + gaps 4 + + border { + width 2 + } + + focus-ring { + width 2 + } +} +window-rule { + geometry-corner-radius 12 + clip-to-geometry true + tiled-state true + draw-border-with-background false +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl new file mode 100644 index 0000000..0a759e4 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl @@ -0,0 +1,279 @@ +// This config is in the KDL format: https://kdl.dev +// "/-" comments out the following node. +// Check the wiki for a full description of the configuration: +// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction +config-notification { + disable-failed +} + +gestures { + hot-corners { + off + } +} + +// Input device configuration. +// Find the full list of options on the wiki: +// https://github.com/YaLTeR/niri/wiki/Configuration:-Input +input { + keyboard { + xkb { + // You can set rules, model, layout, variant and options. + // For more information, see xkeyboard-config(7). + + // For example: + // layout "us,ru" + // options "grp:win_space_toggle,compose:ralt,ctrl:nocaps" + + // If this section is empty, niri will fetch xkb settings + // from org.freedesktop.locale1. You can control these using + // localectl set-x11-keymap. + } + + // Enable numlock on startup, omitting this setting disables it. + numlock + } + + // Next sections include libinput settings. + // Omitting settings disables them, or leaves them at their default values. + // All commented-out settings here are examples, not defaults. + touchpad { + // off + tap + // dwt + // dwtp + // drag false + // drag-lock + natural-scroll + // accel-speed 0.2 + // accel-profile "flat" + // scroll-method "two-finger" + // disabled-on-external-mouse + } + + mouse { + // off + // natural-scroll + // accel-speed 0.2 + // accel-profile "flat" + // scroll-method "no-scroll" + } + + trackpoint { + // off + // natural-scroll + // accel-speed 0.2 + // accel-profile "flat" + // scroll-method "on-button-down" + // scroll-button 273 + // scroll-button-lock + // middle-emulation + } + + // Uncomment this to make the mouse warp to the center of newly focused windows. + // warp-mouse-to-focus + + // Focus windows and outputs automatically when moving the mouse into them. + // Setting max-scroll-amount="0%" makes it work only on windows already fully on screen. + // focus-follows-mouse max-scroll-amount="0%" +} +// You can configure outputs by their name, which you can find +// by running `niri msg outputs` while inside a niri instance. +// The built-in laptop monitor is usually called "eDP-1". +// Find more information on the wiki: +// https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs +// Remember to uncomment the node by removing "/-"! +/-output "eDP-2" { + mode "2560x1600@239.998993" + position x=2560 y=0 + variable-refresh-rate +} +// Settings that influence how windows are positioned and sized. +// Find more information on the wiki: +// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout +layout { + // Set gaps around windows in logical pixels. + background-color "transparent" + // When to center a column when changing focus, options are: + // - "never", default behavior, focusing an off-screen column will keep at the left + // or right edge of the screen. + // - "always", the focused column will always be centered. + // - "on-overflow", focusing a column will center it if it doesn't fit + // together with the previously focused column. + center-focused-column "never" + // You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between. + preset-column-widths { + // Proportion sets the width as a fraction of the output width, taking gaps into account. + // For example, you can perfectly fit four windows sized "proportion 0.25" on an output. + // The default preset widths are 1/3, 1/2 and 2/3 of the output. + proportion 0.33333 + proportion 0.5 + proportion 0.66667 + // Fixed sets the width in logical pixels exactly. + // fixed 1920 + } + // You can also customize the heights that "switch-preset-window-height" (Mod+Shift+R) toggles between. + // preset-window-heights { } + // You can change the default width of the new windows. + default-column-width { proportion 0.5; } + // If you leave the brackets empty, the windows themselves will decide their initial width. + // default-column-width {} + // By default focus ring and border are rendered as a solid background rectangle + // behind windows. That is, they will show up through semitransparent windows. + // This is because windows using client-side decorations can have an arbitrary shape. + // + // If you don't like that, you should uncomment `prefer-no-csd` below. + // Niri will draw focus ring and border *around* windows that agree to omit their + // client-side decorations. + // + // Alternatively, you can override it with a window rule called + // `draw-border-with-background`. + border { + off + width 4 + active-color "#707070" // Neutral gray + inactive-color "#d0d0d0" // Light gray + urgent-color "#cc4444" // Softer red + } + shadow { + softness 30 + spread 5 + offset x=0 y=5 + color "#0007" + } + struts { + } +} +layer-rule { + match namespace="^quickshell$" + place-within-backdrop true +} +overview { + workspace-shadow { + off + } +} +// Add lines like this to spawn processes at startup. +// Note that running niri as a session supports xdg-desktop-autostart, +// which may be more convenient to use. +// See the binds section below for more spawn examples. +// This line starts waybar, a commonly used bar for Wayland compositors. +environment { + XDG_CURRENT_DESKTOP "niri" +} +hotkey-overlay { + skip-at-startup +} +prefer-no-csd +screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" +animations { + workspace-switch { + spring damping-ratio=0.80 stiffness=523 epsilon=0.0001 + } + window-open { + duration-ms 150 + curve "ease-out-expo" + } + window-close { + duration-ms 150 + curve "ease-out-quad" + } + horizontal-view-movement { + spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 + } + window-movement { + spring damping-ratio=0.75 stiffness=323 epsilon=0.0001 + } + window-resize { + spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 + } + config-notification-open-close { + spring damping-ratio=0.65 stiffness=923 epsilon=0.001 + } + screenshot-ui-open { + duration-ms 200 + curve "ease-out-quad" + } + overview-open-close { + spring damping-ratio=0.85 stiffness=800 epsilon=0.0001 + } +} +// Window rules let you adjust behavior for individual windows. +// Find more information on the wiki: +// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules +// Work around WezTerm's initial configure bug +// by setting an empty default-column-width. +window-rule { + // This regular expression is intentionally made as specific as possible, + // since this is the default config, and we want no false positives. + // You can get away with just app-id="wezterm" if you want. + match app-id=r#"^org\.wezfurlong\.wezterm$"# + default-column-width {} +} +window-rule { + match app-id=r#"^org\.gnome\."# + draw-border-with-background false + geometry-corner-radius 12 + clip-to-geometry true +} +window-rule { + match app-id=r#"^gnome-control-center$"# + match app-id=r#"^pavucontrol$"# + match app-id=r#"^nm-connection-editor$"# + default-column-width { proportion 0.5; } + open-floating false +} +window-rule { + match app-id=r#"^org\.gnome\.Calculator$"# + match app-id=r#"^gnome-calculator$"# + match app-id=r#"^galculator$"# + match app-id=r#"^blueman-manager$"# + match app-id=r#"^org\.gnome\.Nautilus$"# + match app-id=r#"^xdg-desktop-portal$"# + open-floating true +} +window-rule { + match app-id=r#"^steam$"# title=r#"^notificationtoasts_\d+_desktop$"# + default-floating-position x=10 y=10 relative-to="bottom-right" + open-focused false +} +window-rule { + match app-id=r#"^org\.wezfurlong\.wezterm$"# + match app-id="Alacritty" + match app-id="zen" + match app-id="com.mitchellh.ghostty" + match app-id="kitty" + draw-border-with-background false +} +window-rule { + match app-id=r#"firefox$"# title="^Picture-in-Picture$" + match app-id="zoom" + open-floating true +} +// Open dms windows as floating by default +window-rule { + match app-id=r#"org.quickshell$"# + match app-id=r#"com.danklinux.dms$"# + open-floating true +} +debug { + honor-xdg-activation-with-invalid-serial +} + +// Override to disable super+tab +recent-windows { + binds { + Alt+Tab { next-window scope="output"; } + Alt+Shift+Tab { previous-window scope="output"; } + Alt+grave { next-window filter="app-id"; } + Alt+Shift+grave { previous-window filter="app-id"; } + } +} + +// Include dms files +include "dms/colors.kdl" +include "dms/layout.kdl" +include "dms/alttab.kdl" +include "dms/binds.kdl" +include "dms/outputs.kdl" +include "dms/cursor.kdl" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf Binary files differnew file mode 100644 index 0000000..03e05c4 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go new file mode 100644 index 0000000..f6bb964 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go @@ -0,0 +1,15 @@ +package config + +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 + +//go:embed embedded/hypr-binds.conf +var HyprBindsConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go new file mode 100644 index 0000000..01801ca --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go @@ -0,0 +1,21 @@ +package config + +import _ "embed" + +//go:embed embedded/niri.kdl +var NiriConfig string + +//go:embed embedded/niri-colors.kdl +var NiriColorsConfig string + +//go:embed embedded/niri-layout.kdl +var NiriLayoutConfig string + +//go:embed embedded/niri-alttab.kdl +var NiriAlttabConfig string + +//go:embed embedded/niri-binds.kdl +var NiriBindsConfig string + +//go:embed embedded/niri-greeter.kdl +var NiriGreeterConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go new file mode 100644 index 0000000..a5bba04 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go @@ -0,0 +1,24 @@ +package config + +import _ "embed" + +//go:embed embedded/ghostty.conf +var GhosttyConfig string + +//go:embed embedded/ghostty-colors.conf +var GhosttyColorConfig string + +//go:embed embedded/kitty.conf +var KittyConfig string + +//go:embed embedded/kitty-theme.conf +var KittyThemeConfig string + +//go:embed embedded/kitty-tabs.conf +var KittyTabsConfig string + +//go:embed embedded/alacritty.toml +var AlacrittyConfig string + +//go:embed embedded/alacritty-theme.toml +var AlacrittyThemeConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go new file mode 100644 index 0000000..2c5bbc7 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go @@ -0,0 +1,6 @@ +package config + +import _ "embed" + +//go:embed embedded/testpage.pdf +var TestPage string |