From e58e00da1381d1be93e6651016efc03923f001b4 Mon Sep 17 00:00:00 2001 From: Augustin Date: Sun, 19 Apr 2026 23:05:15 +0200 Subject: [PATCH] feat: add mouse support + install pnpm, uv, docker, gh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added tea.WithMouseCellMotion() for mouse interaction in TUI - Added installers for pnpm (npm), uv (astral), docker (pacman/apt/dnf/brew), gh/github-cli (pacman/apt/dnf/brew) 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush --- cmd/muyue/main.go | 2 +- internal/installer/installer.go | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/cmd/muyue/main.go b/cmd/muyue/main.go index 30ab8a9..8acff81 100644 --- a/cmd/muyue/main.go +++ b/cmd/muyue/main.go @@ -102,7 +102,7 @@ func runTUI() { result := scanner.ScanSystem() model := tui.NewModel(cfg, result) - p := tea.NewProgram(model, tea.WithAltScreen()) + p := tea.NewProgram(model, tea.WithAltScreen(), tea.WithMouseCellMotion()) if _, err := p.Run(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) diff --git a/internal/installer/installer.go b/internal/installer/installer.go index b420452..40116a5 100644 --- a/internal/installer/installer.go +++ b/internal/installer/installer.go @@ -47,6 +47,14 @@ func (i *Installer) InstallTool(name string) InstallResult { return i.installPython() case "git": return i.installGit() + case "pnpm": + return i.installPnpm() + case "uv": + return i.installUv() + case "docker": + return i.installDocker() + case "gh": + return i.installGh() default: return InstallResult{Tool: name, Success: false, Message: "unknown tool"} } @@ -317,3 +325,79 @@ func appendLine(file, line string) { defer f.Close() f.WriteString("\n" + line + "\n") } + +func (i *Installer) installPnpm() InstallResult { + if _, err := exec.LookPath("pnpm"); err == nil { + return InstallResult{Tool: "pnpm", Success: true, Message: "already installed"} + } + cmd := exec.Command("npm", "install", "-g", "pnpm") + if output, err := cmd.CombinedOutput(); err != nil { + return InstallResult{Tool: "pnpm", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))} + } + return InstallResult{Tool: "pnpm", Success: true, Message: "installed"} +} + +func (i *Installer) installUv() InstallResult { + if _, err := exec.LookPath("uv"); err == nil { + return InstallResult{Tool: "uv", Success: true, Message: "already installed"} + } + var cmd *exec.Cmd + switch i.system.OS { + case platform.Linux, platform.MacOS: + cmd = exec.Command("bash", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh") + case platform.Windows: + cmd = exec.Command("powershell", "-Command", "irm https://astral.sh/uv/install.ps1 | iex") + default: + return InstallResult{Tool: "uv", Success: false, Message: "unsupported OS"} + } + if output, err := cmd.CombinedOutput(); err != nil { + return InstallResult{Tool: "uv", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))} + } + return InstallResult{Tool: "uv", Success: true, Message: "installed"} +} + +func (i *Installer) installDocker() InstallResult { + if _, err := exec.LookPath("docker"); err == nil { + return InstallResult{Tool: "docker", Success: true, Message: "already installed"} + } + var cmd *exec.Cmd + switch i.system.PackageManager { + case "apt": + cmd = exec.Command("bash", "-c", "apt-get update && apt-get install -y docker.io && systemctl start docker && systemctl enable docker") + case "pacman": + cmd = exec.Command("bash", "-c", "pacman -S --noconfirm docker && systemctl start docker && systemctl enable docker") + case "dnf": + cmd = exec.Command("bash", "-c", "dnf install -y docker && systemctl start docker && systemctl enable docker") + case "brew": + cmd = exec.Command("bash", "-c", "brew install docker") + default: + return InstallResult{Tool: "docker", Success: false, Message: fmt.Sprintf("install via '%s' not supported, install manually", i.system.PackageManager)} + } + if output, err := cmd.CombinedOutput(); err != nil { + return InstallResult{Tool: "docker", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))} + } + return InstallResult{Tool: "docker", Success: true, Message: "installed"} +} + +func (i *Installer) installGh() InstallResult { + if _, err := exec.LookPath("gh"); err == nil { + return InstallResult{Tool: "gh", Success: true, Message: "already installed"} + } + var cmd *exec.Cmd + switch i.system.PackageManager { + case "apt": + cmd = exec.Command("bash", "-c", "curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && echo 'deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main' | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && apt update && apt install gh -y") + case "pacman": + cmd = exec.Command("pacman", "-S", "--noconfirm", "github-cli") + case "dnf": + cmd = exec.Command("dnf", "install", "-y", "gh") + case "brew": + cmd = exec.Command("brew", "install", "gh") + default: + return InstallResult{Tool: "gh", Success: false, Message: fmt.Sprintf("install via '%s' not supported, install manually", i.system.PackageManager)} + } + if output, err := cmd.CombinedOutput(); err != nil { + return InstallResult{Tool: "gh", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))} + } + return InstallResult{Tool: "gh", Success: true, Message: "installed"} +}