mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
Compare commits
4 Commits
6297b0679c
...
a55ec6416c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a55ec6416c | ||
|
|
b1834b1958 | ||
|
|
1beeb9fb55 | ||
|
|
18d86354ec |
@@ -77,8 +77,6 @@ func runUpdate() {
|
|||||||
switch config.Family {
|
switch config.Family {
|
||||||
case distros.FamilyArch:
|
case distros.FamilyArch:
|
||||||
updateErr = updateArchLinux()
|
updateErr = updateArchLinux()
|
||||||
case distros.FamilyNix:
|
|
||||||
updateErr = updateNixOS()
|
|
||||||
case distros.FamilySUSE:
|
case distros.FamilySUSE:
|
||||||
updateErr = updateOtherDistros()
|
updateErr = updateOtherDistros()
|
||||||
default:
|
default:
|
||||||
@@ -152,27 +150,6 @@ func updateArchLinux() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateNixOS() error {
|
|
||||||
fmt.Println("This will update DankMaterialShell using nix profile.")
|
|
||||||
if !confirmUpdate() {
|
|
||||||
return errdefs.ErrUpdateCancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nRunning: nix profile upgrade github:AvengeMedia/DankMaterialShell")
|
|
||||||
updateCmd := exec.Command("nix", "profile", "upgrade", "github:AvengeMedia/DankMaterialShell")
|
|
||||||
updateCmd.Stdout = os.Stdout
|
|
||||||
updateCmd.Stderr = os.Stderr
|
|
||||||
err := updateCmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error: Failed to update using nix profile: %v\n", err)
|
|
||||||
fmt.Println("Falling back to git-based update method...")
|
|
||||||
return updateOtherDistros()
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("dms successfully updated")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateOtherDistros() error {
|
func updateOtherDistros() error {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,466 +0,0 @@
|
|||||||
package distros
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Register("nixos", "#7EBAE4", FamilyNix, func(config DistroConfig, logChan chan<- string) Distribution {
|
|
||||||
return NewNixOSDistribution(config, logChan)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type NixOSDistribution struct {
|
|
||||||
*BaseDistribution
|
|
||||||
config DistroConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNixOSDistribution(config DistroConfig, logChan chan<- string) *NixOSDistribution {
|
|
||||||
base := NewBaseDistribution(logChan)
|
|
||||||
return &NixOSDistribution{
|
|
||||||
BaseDistribution: base,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) GetID() string {
|
|
||||||
return n.config.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) GetColorHex() string {
|
|
||||||
return n.config.ColorHex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) GetFamily() DistroFamily {
|
|
||||||
return n.config.Family
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) GetPackageManager() PackageManagerType {
|
|
||||||
return PackageManagerNix
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) {
|
|
||||||
return n.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) {
|
|
||||||
var dependencies []deps.Dependency
|
|
||||||
|
|
||||||
// DMS at the top (shell is prominent)
|
|
||||||
dependencies = append(dependencies, n.detectDMS())
|
|
||||||
|
|
||||||
// Terminal with choice support
|
|
||||||
dependencies = append(dependencies, n.detectSpecificTerminal(terminal))
|
|
||||||
|
|
||||||
// Common detections using base methods
|
|
||||||
dependencies = append(dependencies, n.detectGit())
|
|
||||||
dependencies = append(dependencies, n.detectWindowManager(wm))
|
|
||||||
dependencies = append(dependencies, n.detectQuickshell())
|
|
||||||
dependencies = append(dependencies, n.detectXDGPortal())
|
|
||||||
dependencies = append(dependencies, n.detectPolkitAgent())
|
|
||||||
dependencies = append(dependencies, n.detectAccountsService())
|
|
||||||
|
|
||||||
// Hyprland-specific tools
|
|
||||||
if wm == deps.WindowManagerHyprland {
|
|
||||||
dependencies = append(dependencies, n.detectHyprlandTools()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Niri-specific tools
|
|
||||||
if wm == deps.WindowManagerNiri {
|
|
||||||
dependencies = append(dependencies, n.detectXwaylandSatellite())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base detections (common across distros)
|
|
||||||
dependencies = append(dependencies, n.detectMatugen())
|
|
||||||
dependencies = append(dependencies, n.detectDgop())
|
|
||||||
dependencies = append(dependencies, n.detectHyprpicker())
|
|
||||||
dependencies = append(dependencies, n.detectClipboardTools()...)
|
|
||||||
|
|
||||||
return dependencies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectDMS() deps.Dependency {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
|
|
||||||
// For NixOS, check if quickshell can find the dms config
|
|
||||||
cmd := exec.Command("qs", "-c", "dms", "--list")
|
|
||||||
if err := cmd.Run(); err == nil {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
} else if n.packageInstalled("DankMaterialShell") {
|
|
||||||
// Fallback: check if flake is in profile
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "dms (DankMaterialShell)",
|
|
||||||
Status: status,
|
|
||||||
Description: "Desktop Management System configuration (installed as flake)",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectXDGPortal() deps.Dependency {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
if n.packageInstalled("xdg-desktop-portal-gtk") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "xdg-desktop-portal-gtk",
|
|
||||||
Status: status,
|
|
||||||
Description: "Desktop integration portal for GTK",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectWindowManager(wm deps.WindowManager) deps.Dependency {
|
|
||||||
switch wm {
|
|
||||||
case deps.WindowManagerHyprland:
|
|
||||||
status := deps.StatusMissing
|
|
||||||
description := "Dynamic tiling Wayland compositor"
|
|
||||||
if n.commandExists("hyprland") || n.commandExists("Hyprland") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
} else {
|
|
||||||
description = "Install system-wide: programs.hyprland.enable = true; in configuration.nix"
|
|
||||||
}
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "hyprland",
|
|
||||||
Status: status,
|
|
||||||
Description: description,
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
case deps.WindowManagerNiri:
|
|
||||||
status := deps.StatusMissing
|
|
||||||
description := "Scrollable-tiling Wayland compositor"
|
|
||||||
if n.commandExists("niri") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
} else {
|
|
||||||
description = "Install system-wide: environment.systemPackages = [ pkgs.niri ]; in configuration.nix"
|
|
||||||
}
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "niri",
|
|
||||||
Status: status,
|
|
||||||
Description: description,
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "unknown-wm",
|
|
||||||
Status: deps.StatusMissing,
|
|
||||||
Description: "Unknown window manager",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectHyprlandTools() []deps.Dependency {
|
|
||||||
var dependencies []deps.Dependency
|
|
||||||
|
|
||||||
tools := []struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}{
|
|
||||||
{"grim", "Screenshot utility for Wayland"},
|
|
||||||
{"slurp", "Region selection utility for Wayland"},
|
|
||||||
{"hyprctl", "Hyprland control utility (comes with system Hyprland)"},
|
|
||||||
{"hyprpicker", "Color picker for Hyprland"},
|
|
||||||
{"grimblast", "Screenshot script for Hyprland"},
|
|
||||||
{"jq", "JSON processor"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tool := range tools {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
|
|
||||||
// Special handling for hyprctl - it comes with system hyprland
|
|
||||||
if tool.name == "hyprctl" {
|
|
||||||
if n.commandExists("hyprctl") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if n.commandExists(tool.name) {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies = append(dependencies, deps.Dependency{
|
|
||||||
Name: tool.name,
|
|
||||||
Status: status,
|
|
||||||
Description: tool.description,
|
|
||||||
Required: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return dependencies
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectXwaylandSatellite() deps.Dependency {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
if n.commandExists("xwayland-satellite") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "xwayland-satellite",
|
|
||||||
Status: status,
|
|
||||||
Description: "Xwayland support",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectPolkitAgent() deps.Dependency {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
if n.packageInstalled("mate-polkit") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "mate-polkit",
|
|
||||||
Status: status,
|
|
||||||
Description: "PolicyKit authentication agent",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) detectAccountsService() deps.Dependency {
|
|
||||||
status := deps.StatusMissing
|
|
||||||
if n.packageInstalled("accountsservice") {
|
|
||||||
status = deps.StatusInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
return deps.Dependency{
|
|
||||||
Name: "accountsservice",
|
|
||||||
Status: status,
|
|
||||||
Description: "D-Bus interface for user account query and manipulation",
|
|
||||||
Required: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) packageInstalled(pkg string) bool {
|
|
||||||
cmd := exec.Command("nix", "profile", "list")
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strings.Contains(string(output), pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
|
|
||||||
packages := map[string]PackageMapping{
|
|
||||||
"git": {Name: "nixpkgs#git", Repository: RepoTypeSystem},
|
|
||||||
"quickshell": {Name: "github:quickshell-mirror/quickshell", Repository: RepoTypeFlake},
|
|
||||||
"matugen": {Name: "github:InioX/matugen", Repository: RepoTypeFlake},
|
|
||||||
"dgop": {Name: "github:AvengeMedia/dgop", Repository: RepoTypeFlake},
|
|
||||||
"dms (DankMaterialShell)": {Name: "github:AvengeMedia/DankMaterialShell", Repository: RepoTypeFlake},
|
|
||||||
"ghostty": {Name: "nixpkgs#ghostty", Repository: RepoTypeSystem},
|
|
||||||
"alacritty": {Name: "nixpkgs#alacritty", Repository: RepoTypeSystem},
|
|
||||||
"cliphist": {Name: "nixpkgs#cliphist", Repository: RepoTypeSystem},
|
|
||||||
"wl-clipboard": {Name: "nixpkgs#wl-clipboard", Repository: RepoTypeSystem},
|
|
||||||
"xdg-desktop-portal-gtk": {Name: "nixpkgs#xdg-desktop-portal-gtk", Repository: RepoTypeSystem},
|
|
||||||
"mate-polkit": {Name: "nixpkgs#mate.mate-polkit", Repository: RepoTypeSystem},
|
|
||||||
"accountsservice": {Name: "nixpkgs#accountsservice", Repository: RepoTypeSystem},
|
|
||||||
"hyprpicker": {Name: "nixpkgs#hyprpicker", Repository: RepoTypeSystem},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Window managers (hyprland/niri) should be installed system-wide on NixOS
|
|
||||||
// We only install the tools here
|
|
||||||
switch wm {
|
|
||||||
case deps.WindowManagerHyprland:
|
|
||||||
// Skip hyprland itself - should be installed system-wide
|
|
||||||
packages["grim"] = PackageMapping{Name: "nixpkgs#grim", Repository: RepoTypeSystem}
|
|
||||||
packages["slurp"] = PackageMapping{Name: "nixpkgs#slurp", Repository: RepoTypeSystem}
|
|
||||||
packages["grimblast"] = PackageMapping{Name: "github:hyprwm/contrib#grimblast", Repository: RepoTypeFlake}
|
|
||||||
packages["jq"] = PackageMapping{Name: "nixpkgs#jq", Repository: RepoTypeSystem}
|
|
||||||
case deps.WindowManagerNiri:
|
|
||||||
// Skip niri itself - should be installed system-wide
|
|
||||||
packages["xwayland-satellite"] = PackageMapping{Name: "nixpkgs#xwayland-satellite", Repository: RepoTypeFlake}
|
|
||||||
}
|
|
||||||
|
|
||||||
return packages
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhasePrerequisites,
|
|
||||||
Progress: 0.10,
|
|
||||||
Step: "NixOS prerequisites ready",
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: "NixOS package manager is ready to use",
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error {
|
|
||||||
// Phase 1: Check Prerequisites
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhasePrerequisites,
|
|
||||||
Progress: 0.05,
|
|
||||||
Step: "Checking system prerequisites...",
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: "Starting prerequisite check...",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := n.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil {
|
|
||||||
return fmt.Errorf("failed to install prerequisites: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nixpkgsPkgs, flakePkgs, _ := n.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
|
|
||||||
|
|
||||||
// Phase 2: Nixpkgs Packages
|
|
||||||
if len(nixpkgsPkgs) > 0 {
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseSystemPackages,
|
|
||||||
Progress: 0.35,
|
|
||||||
Step: fmt.Sprintf("Installing %d packages from nixpkgs...", len(nixpkgsPkgs)),
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(nixpkgsPkgs, ", ")),
|
|
||||||
}
|
|
||||||
if err := n.installNixpkgsPackages(ctx, nixpkgsPkgs, progressChan); err != nil {
|
|
||||||
return fmt.Errorf("failed to install nixpkgs packages: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 3: Flake Packages
|
|
||||||
if len(flakePkgs) > 0 {
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseAURPackages,
|
|
||||||
Progress: 0.65,
|
|
||||||
Step: fmt.Sprintf("Installing %d packages from flakes...", len(flakePkgs)),
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: fmt.Sprintf("Installing flake packages: %s", strings.Join(flakePkgs, ", ")),
|
|
||||||
}
|
|
||||||
if err := n.installFlakePackages(ctx, flakePkgs, progressChan); err != nil {
|
|
||||||
return fmt.Errorf("failed to install flake packages: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 4: Configuration
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseConfiguration,
|
|
||||||
Progress: 0.90,
|
|
||||||
Step: "Configuring system...",
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: "Starting post-installation configuration...",
|
|
||||||
}
|
|
||||||
if err := n.postInstallConfig(progressChan); err != nil {
|
|
||||||
return fmt.Errorf("failed to configure system: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 5: Complete
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseComplete,
|
|
||||||
Progress: 1.0,
|
|
||||||
Step: "Installation complete!",
|
|
||||||
IsComplete: true,
|
|
||||||
LogOutput: "All packages installed and configured successfully",
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) {
|
|
||||||
nixpkgsPkgs := []string{}
|
|
||||||
flakePkgs := []string{}
|
|
||||||
|
|
||||||
variantMap := make(map[string]deps.PackageVariant)
|
|
||||||
for _, dep := range dependencies {
|
|
||||||
variantMap[dep.Name] = dep.Variant
|
|
||||||
}
|
|
||||||
|
|
||||||
packageMap := n.GetPackageMapping(wm)
|
|
||||||
|
|
||||||
for _, dep := range dependencies {
|
|
||||||
if disabledFlags[dep.Name] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgInfo, exists := packageMap[dep.Name]
|
|
||||||
if !exists {
|
|
||||||
n.log(fmt.Sprintf("Warning: No package mapping found for %s", dep.Name))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch pkgInfo.Repository {
|
|
||||||
case RepoTypeSystem:
|
|
||||||
nixpkgsPkgs = append(nixpkgsPkgs, pkgInfo.Name)
|
|
||||||
case RepoTypeFlake:
|
|
||||||
flakePkgs = append(flakePkgs, pkgInfo.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nixpkgsPkgs, flakePkgs, variantMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) installNixpkgsPackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error {
|
|
||||||
if len(packages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n.log(fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(packages, ", ")))
|
|
||||||
|
|
||||||
args := []string{"profile", "install"}
|
|
||||||
args = append(args, packages...)
|
|
||||||
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseSystemPackages,
|
|
||||||
Progress: 0.40,
|
|
||||||
Step: "Installing nixpkgs packages...",
|
|
||||||
IsComplete: false,
|
|
||||||
CommandInfo: fmt.Sprintf("nix %s", strings.Join(args, " ")),
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "nix", args...)
|
|
||||||
return n.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) installFlakePackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error {
|
|
||||||
if len(packages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n.log(fmt.Sprintf("Installing flake packages: %s", strings.Join(packages, ", ")))
|
|
||||||
|
|
||||||
baseProgress := 0.65
|
|
||||||
progressStep := 0.20 / float64(len(packages))
|
|
||||||
|
|
||||||
for i, pkg := range packages {
|
|
||||||
currentProgress := baseProgress + (float64(i) * progressStep)
|
|
||||||
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseAURPackages,
|
|
||||||
Progress: currentProgress,
|
|
||||||
Step: fmt.Sprintf("Installing flake package %s (%d/%d)...", pkg, i+1, len(packages)),
|
|
||||||
IsComplete: false,
|
|
||||||
CommandInfo: fmt.Sprintf("nix profile install %s", pkg),
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "nix", "profile", "install", pkg)
|
|
||||||
if err := n.runWithProgress(cmd, progressChan, PhaseAURPackages, currentProgress, currentProgress+progressStep); err != nil {
|
|
||||||
return fmt.Errorf("failed to install flake package %s: %w", pkg, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NixOSDistribution) postInstallConfig(progressChan chan<- InstallProgressMsg) error {
|
|
||||||
// For NixOS, DMS is installed as a flake package, so we skip both the binary installation and git clone
|
|
||||||
// The flake installation handles both the binary and config files correctly
|
|
||||||
progressChan <- InstallProgressMsg{
|
|
||||||
Phase: PhaseConfiguration,
|
|
||||||
Progress: 0.95,
|
|
||||||
Step: "NixOS configuration complete",
|
|
||||||
IsComplete: false,
|
|
||||||
LogOutput: "DMS installed via flake - binary and config handled by Nix",
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -122,15 +122,8 @@ func (d *Detector) GetInstalledComponents() []DependencyInfo {
|
|||||||
return []DependencyInfo{}
|
return []DependencyInfo{}
|
||||||
}
|
}
|
||||||
|
|
||||||
isNixOS := d.isNixOS()
|
|
||||||
|
|
||||||
var components []DependencyInfo
|
var components []DependencyInfo
|
||||||
for _, dep := range dependencies {
|
for _, dep := range dependencies {
|
||||||
// On NixOS, filter out the window managers themselves but keep their components
|
|
||||||
if isNixOS && (dep.Name == "hyprland" || dep.Name == "niri") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
components = append(components, DependencyInfo{
|
components = append(components, DependencyInfo{
|
||||||
Name: dep.Name,
|
Name: dep.Name,
|
||||||
Status: dep.Status,
|
Status: dep.Status,
|
||||||
@@ -142,23 +135,6 @@ func (d *Detector) GetInstalledComponents() []DependencyInfo {
|
|||||||
return components
|
return components
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Detector) isNixOS() bool {
|
|
||||||
_, err := os.Stat("/etc/nixos")
|
|
||||||
if err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alternative check
|
|
||||||
if _, err := os.Stat("/nix/store"); err == nil {
|
|
||||||
// Also check for nixos-version command
|
|
||||||
if d.commandExists("nixos-version") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type DependencyInfo struct {
|
type DependencyInfo struct {
|
||||||
Name string
|
Name string
|
||||||
Status deps.DependencyStatus
|
Status deps.DependencyStatus
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m.updateSelectWindowManagerState(msg)
|
return m.updateSelectWindowManagerState(msg)
|
||||||
case StateSelectTerminal:
|
case StateSelectTerminal:
|
||||||
return m.updateSelectTerminalState(msg)
|
return m.updateSelectTerminalState(msg)
|
||||||
case StateMissingWMInstructions:
|
|
||||||
return m.updateMissingWMInstructionsState(msg)
|
|
||||||
case StateDetectingDeps:
|
case StateDetectingDeps:
|
||||||
return m.updateDetectingDepsState(msg)
|
return m.updateDetectingDepsState(msg)
|
||||||
case StateDependencyReview:
|
case StateDependencyReview:
|
||||||
@@ -183,8 +181,6 @@ func (m Model) View() string {
|
|||||||
return m.viewSelectWindowManager()
|
return m.viewSelectWindowManager()
|
||||||
case StateSelectTerminal:
|
case StateSelectTerminal:
|
||||||
return m.viewSelectTerminal()
|
return m.viewSelectTerminal()
|
||||||
case StateMissingWMInstructions:
|
|
||||||
return m.viewMissingWMInstructions()
|
|
||||||
case StateDetectingDeps:
|
case StateDetectingDeps:
|
||||||
return m.viewDetectingDeps()
|
return m.viewDetectingDeps()
|
||||||
case StateDependencyReview:
|
case StateDependencyReview:
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ const (
|
|||||||
StateWelcome ApplicationState = iota
|
StateWelcome ApplicationState = iota
|
||||||
StateSelectWindowManager
|
StateSelectWindowManager
|
||||||
StateSelectTerminal
|
StateSelectTerminal
|
||||||
StateMissingWMInstructions
|
|
||||||
StateDetectingDeps
|
StateDetectingDeps
|
||||||
StateDependencyReview
|
StateDependencyReview
|
||||||
StateGentooUseFlags
|
StateGentooUseFlags
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
package tui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m Model) viewMissingWMInstructions() string {
|
|
||||||
var b strings.Builder
|
|
||||||
|
|
||||||
b.WriteString(m.renderBanner())
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Determine which WM is missing
|
|
||||||
wmName := "Niri"
|
|
||||||
installCmd := `environment.systemPackages = with pkgs; [
|
|
||||||
niri
|
|
||||||
];`
|
|
||||||
alternateCmd := `# Or enable the module if available:
|
|
||||||
# programs.niri.enable = true;`
|
|
||||||
|
|
||||||
if m.selectedWM == 1 {
|
|
||||||
wmName = "Hyprland"
|
|
||||||
installCmd = `programs.hyprland.enable = true;`
|
|
||||||
alternateCmd = `# Or add to systemPackages:
|
|
||||||
# environment.systemPackages = with pkgs; [
|
|
||||||
# hyprland
|
|
||||||
# ];`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Title
|
|
||||||
title := m.styles.Title.Render("⚠️ " + wmName + " Not Installed")
|
|
||||||
b.WriteString(title)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Explanation
|
|
||||||
explanation := m.styles.Normal.Render(wmName + " needs to be installed system-wide on NixOS.")
|
|
||||||
b.WriteString(explanation)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Instructions
|
|
||||||
instructions := m.styles.Subtle.Render("To install " + wmName + ", add this to your /etc/nixos/configuration.nix:")
|
|
||||||
b.WriteString(instructions)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Command box
|
|
||||||
cmdBox := m.styles.CodeBlock.Render(installCmd)
|
|
||||||
b.WriteString(cmdBox)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Alternate command
|
|
||||||
altBox := m.styles.Subtle.Render(alternateCmd)
|
|
||||||
b.WriteString(altBox)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Rebuild instruction
|
|
||||||
rebuildInstruction := m.styles.Normal.Render("Then rebuild your system:")
|
|
||||||
b.WriteString(rebuildInstruction)
|
|
||||||
b.WriteString("\n")
|
|
||||||
|
|
||||||
rebuildCmd := m.styles.CodeBlock.Render("sudo nixos-rebuild switch")
|
|
||||||
b.WriteString(rebuildCmd)
|
|
||||||
b.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Navigation help
|
|
||||||
help := m.styles.Subtle.Render("Press Esc to go back and select a different window manager, or Ctrl+C to exit")
|
|
||||||
b.WriteString(help)
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m Model) updateMissingWMInstructionsState(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
if keyMsg, ok := msg.(tea.KeyMsg); ok {
|
|
||||||
switch keyMsg.String() {
|
|
||||||
case "esc":
|
|
||||||
// Go back to window manager selection
|
|
||||||
m.state = StateSelectWindowManager
|
|
||||||
return m, m.listenForLogs()
|
|
||||||
case "ctrl+c":
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m, m.listenForLogs()
|
|
||||||
}
|
|
||||||
@@ -140,20 +140,6 @@ func (m Model) updateSelectTerminalState(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.selectedTerminal++
|
m.selectedTerminal++
|
||||||
}
|
}
|
||||||
case "enter":
|
case "enter":
|
||||||
if m.osInfo != nil && m.osInfo.Distribution.ID == "nixos" {
|
|
||||||
var wmInstalled bool
|
|
||||||
if m.selectedWM == 0 {
|
|
||||||
wmInstalled = m.commandExists("niri")
|
|
||||||
} else {
|
|
||||||
wmInstalled = m.commandExists("hyprland") || m.commandExists("Hyprland")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wmInstalled {
|
|
||||||
m.state = StateMissingWMInstructions
|
|
||||||
return m, m.listenForLogs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.state = StateDetectingDeps
|
m.state = StateDetectingDeps
|
||||||
m.isLoading = true
|
m.isLoading = true
|
||||||
return m, tea.Batch(m.spinner.Tick, m.detectDependencies())
|
return m, tea.Batch(m.spinner.Tick, m.detectDependencies())
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (m Model) viewWelcome() string {
|
|||||||
case "debian":
|
case "debian":
|
||||||
errorMsg = fmt.Sprintf("Debian %s is not supported.\n\nOnly Debian 13+ (Trixie) is supported.\n\nPlease upgrade to Debian 13 or later.", m.osInfo.VersionID)
|
errorMsg = fmt.Sprintf("Debian %s is not supported.\n\nOnly Debian 13+ (Trixie) is supported.\n\nPlease upgrade to Debian 13 or later.", m.osInfo.VersionID)
|
||||||
case "nixos":
|
case "nixos":
|
||||||
errorMsg = "NixOS is currently not supported, but there is a DankMaterialShell flake available."
|
errorMsg = "See the NixOS documentation for installation instructions: https://danklinux.com/docs/dankmaterialshell/nixos."
|
||||||
default:
|
default:
|
||||||
errorMsg = fmt.Sprintf("%s is not supported.\nFeel free to request on https://github.com/AvengeMedia/DankMaterialShell", m.osInfo.PrettyName)
|
errorMsg = fmt.Sprintf("%s is not supported.\nFeel free to request on https://github.com/AvengeMedia/DankMaterialShell", m.osInfo.PrettyName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,25 @@ Item {
|
|||||||
readonly property string mainPath: generatePathForPosition(width, height)
|
readonly property string mainPath: generatePathForPosition(width, height)
|
||||||
readonly property string borderFullPath: generateBorderFullPath(width, height)
|
readonly property string borderFullPath: generateBorderFullPath(width, height)
|
||||||
readonly property string borderEdgePath: generateBorderEdgePath(width, height)
|
readonly property string borderEdgePath: generateBorderEdgePath(width, height)
|
||||||
|
property bool mainPathCorrectShape: false
|
||||||
|
property bool borderFullPathCorrectShape: false
|
||||||
|
property bool borderEdgePathCorrectShape: false
|
||||||
|
|
||||||
|
onMainPathChanged: {
|
||||||
|
if (width > 0 && height > 0){
|
||||||
|
root:mainPathCorrectShape = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBorderFullPathChanged: {
|
||||||
|
if (width > 0 && height > 0){
|
||||||
|
root:borderFullPathCorrectShape = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBorderEdgePathChanged: {
|
||||||
|
if (width > 0 && height > 0){
|
||||||
|
root:borderEdgePathCorrectShape = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -51,27 +70,31 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape {
|
|
||||||
|
Loader {
|
||||||
id: barShape
|
id: barShape
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
preferredRendererType: Shape.CurveRenderer
|
active: mainPathCorrectShape
|
||||||
|
sourceComponent: Shape {
|
||||||
|
anchors.fill: parent
|
||||||
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
|
||||||
ShapePath {
|
ShapePath {
|
||||||
fillColor: barWindow._bgColor
|
fillColor: barWindow._bgColor
|
||||||
strokeColor: "transparent"
|
strokeColor: "transparent"
|
||||||
strokeWidth: 0
|
strokeWidth: 0
|
||||||
|
|
||||||
PathSvg {
|
PathSvg {
|
||||||
path: root.mainPath
|
path: root.mainPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape {
|
Loader {
|
||||||
id: barBorder
|
id: barBorder
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: barConfig?.borderEnabled ?? false
|
active: borderFullPathCorrectShape && borderEdgePathCorrectShape
|
||||||
preferredRendererType: Shape.CurveRenderer
|
|
||||||
|
|
||||||
readonly property real borderThickness: Math.max(1, barConfig?.borderThickness ?? 1)
|
readonly property real borderThickness: Math.max(1, barConfig?.borderThickness ?? 1)
|
||||||
readonly property real inset: showFullBorder ? Math.ceil(borderThickness / 2) : borderThickness / 2
|
readonly property real inset: showFullBorder ? Math.ceil(borderThickness / 2) : borderThickness / 2
|
||||||
@@ -79,16 +102,22 @@ Item {
|
|||||||
readonly property color baseColor: (borderColorKey === "surfaceText") ? Theme.surfaceText : (borderColorKey === "primary") ? Theme.primary : Theme.secondary
|
readonly property color baseColor: (borderColorKey === "surfaceText") ? Theme.surfaceText : (borderColorKey === "primary") ? Theme.primary : Theme.secondary
|
||||||
readonly property color borderColor: Theme.withAlpha(baseColor, barConfig?.borderOpacity ?? 1.0)
|
readonly property color borderColor: Theme.withAlpha(baseColor, barConfig?.borderOpacity ?? 1.0)
|
||||||
readonly property bool showFullBorder: (barConfig?.spacing ?? 4) > 0
|
readonly property bool showFullBorder: (barConfig?.spacing ?? 4) > 0
|
||||||
|
sourceComponent: Shape {
|
||||||
|
id: barBorderShape
|
||||||
|
anchors.fill: parent
|
||||||
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
visible: barConfig?.borderEnabled ?? false
|
||||||
|
|
||||||
ShapePath {
|
ShapePath {
|
||||||
fillColor: "transparent"
|
fillColor: "transparent"
|
||||||
strokeColor: barBorder.borderColor
|
strokeColor: barBorder.borderColor
|
||||||
strokeWidth: barBorder.borderThickness
|
strokeWidth: barBorder.borderThickness
|
||||||
joinStyle: ShapePath.RoundJoin
|
joinStyle: ShapePath.RoundJoin
|
||||||
capStyle: ShapePath.FlatCap
|
capStyle: ShapePath.FlatCap
|
||||||
|
|
||||||
PathSvg {
|
PathSvg {
|
||||||
path: barBorder.showFullBorder ? root.borderFullPath : root.borderEdgePath
|
path: barBorder.showFullBorder ? root.borderFullPath : root.borderEdgePath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ DankPopout {
|
|||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (root.contentHeight === 0 && item) {
|
if (item) {
|
||||||
root.contentHeight = Qt.binding(() => item.implicitHeight + Theme.spacingS * 2)
|
root.contentHeight = Qt.binding(() => item.implicitHeight + Theme.spacingS * 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,6 @@ Variants {
|
|||||||
asynchronous: true
|
asynchronous: true
|
||||||
smooth: true
|
smooth: true
|
||||||
cache: true
|
cache: true
|
||||||
sourceSize: Qt.size(modelData.width, modelData.height)
|
|
||||||
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +243,6 @@ Variants {
|
|||||||
asynchronous: true
|
asynchronous: true
|
||||||
smooth: true
|
smooth: true
|
||||||
cache: true
|
cache: true
|
||||||
sourceSize: Qt.size(modelData.width, modelData.height)
|
|
||||||
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
@@ -453,16 +451,14 @@ Variants {
|
|||||||
duration: root.actualTransitionType === "none" ? 0 : 1000
|
duration: root.actualTransitionType === "none" ? 0 : 1000
|
||||||
easing.type: Easing.InOutCubic
|
easing.type: Easing.InOutCubic
|
||||||
onFinished: {
|
onFinished: {
|
||||||
const tempSource = nextWallpaper.source;
|
|
||||||
if (tempSource && nextWallpaper.status === Image.Ready && !tempSource.toString().startsWith("#")) {
|
|
||||||
currentWallpaper.source = tempSource;
|
|
||||||
}
|
|
||||||
root.transitionProgress = 0.0;
|
|
||||||
currentWallpaper.visible = root.actualTransitionType === "none";
|
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
|
if (nextWallpaper.source && nextWallpaper.status === Image.Ready && !nextWallpaper.source.toString().startsWith("#")) {
|
||||||
|
currentWallpaper.source = nextWallpaper.source;
|
||||||
|
}
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
nextWallpaper.visible = false;
|
nextWallpaper.visible = false;
|
||||||
|
currentWallpaper.visible = root.actualTransitionType === "none";
|
||||||
|
root.transitionProgress = 0.0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user