mirror of
https://github.com/Valkyrie00/bold-brew.git
synced 2026-03-14 14:25:53 +01:00
* feat: add leaves filter to show explicitly installed packages (#25) Add new filter [L] to display only "leaf" packages - those installed explicitly by the user and not as dependencies of other packages. * refactor: Migrate to Podman with OCI Containerfile and enhanced Makefile (#26) * refactor: migrate from Docker to Podman with OCI Containerfile Replace Docker with Podman for better security and OCI compliance. Switch from Dockerfile to standard Containerfile format. * chore: upgrade Go from 1.24 to 1.25 Update Go version to 1.25 to support latest goreleaser v2 and benefit from improved performance and language features. * refactor: migrate to Podman and enhance Makefile Replace Docker with Podman and upgrade Makefile with help system and new developer-friendly targets. * chore: upgrade to Go 1.25 and golangci-lint v2.5.0 Update Go to 1.25 and golangci-lint to v2.5.0 for better tooling support. * feat: add security scanning with govulncheck and gosec (#27) Add comprehensive security scanning to the project with vulnerability checks and static analysis tools. * feat: Add complete Casks support with unified UI (#28) * feat(cask): add backend support for Homebrew casks Implement complete backend infrastructure for managing Homebrew casks alongside formulae, preparing for unified UI. * feat(cask): add complete Homebrew casks support with unified UI Implement full backend and UI support for managing Homebrew casks alongside formulae in a unified interface. * fix(cask): parse cask analytics correctly Fix cask analytics not being displayed (showing 0 for all casks). * feat(cask): add complete Homebrew casks support with unified UI Implement full backend and UI support for managing Homebrew casks alongside formulae in a unified interface. * fix: create copy to avoid implicit memory aliasing * feat: implement XDG Base Directory Specification with github.com/adrg/xdg (#29) Implement XDG Base Directory Specification using the github.com/adrg/xdg package for robust cross-platform support.
192 lines
4.5 KiB
Go
192 lines
4.5 KiB
Go
package components
|
|
|
|
import (
|
|
"bbrew/internal/models"
|
|
"bbrew/internal/ui/theme"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/rivo/tview"
|
|
"golang.org/x/text/language"
|
|
"golang.org/x/text/message"
|
|
)
|
|
|
|
type Details struct {
|
|
view *tview.TextView
|
|
theme *theme.Theme
|
|
}
|
|
|
|
func NewDetails(theme *theme.Theme) *Details {
|
|
details := &Details{
|
|
view: tview.NewTextView(),
|
|
theme: theme,
|
|
}
|
|
|
|
details.view.SetDynamicColors(true)
|
|
details.view.SetTextAlign(tview.AlignLeft)
|
|
details.view.SetTitle("Details")
|
|
details.view.SetTitleColor(theme.TitleColor)
|
|
details.view.SetTitleAlign(tview.AlignLeft)
|
|
details.view.SetBorder(true)
|
|
details.view.SetBorderPadding(0, 0, 1, 1)
|
|
return details
|
|
}
|
|
|
|
func (d *Details) SetContent(pkg *models.Package) {
|
|
if pkg == nil {
|
|
d.view.SetText("")
|
|
return
|
|
}
|
|
|
|
// Installation status with colors
|
|
installedStatus := "[red]Not installed[-]"
|
|
installedIcon := "✗"
|
|
if pkg.LocallyInstalled {
|
|
installedStatus = "[green]Installed[-]"
|
|
installedIcon = "✓"
|
|
|
|
if pkg.Outdated {
|
|
installedStatus = "[orange]Update available[-]"
|
|
installedIcon = "⟳"
|
|
}
|
|
}
|
|
|
|
// Type tag with escaped brackets
|
|
typeTag := tview.Escape("[F]") // Formula
|
|
typeLabel := "Formula"
|
|
if pkg.Type == models.PackageTypeCask {
|
|
typeTag = tview.Escape("[C]") // Cask
|
|
typeLabel = "Cask"
|
|
}
|
|
|
|
// Basic information with status
|
|
basicInfo := fmt.Sprintf(
|
|
"[yellow::b]%s %s[-]\n\n"+
|
|
"[blue]• Type:[-] %s %s\n"+
|
|
"[blue]• Name:[-] %s\n"+
|
|
"[blue]• Display Name:[-] %s\n"+
|
|
"[blue]• Version:[-] %s\n"+
|
|
"[blue]• Status:[-] %s\n\n"+
|
|
"[yellow::b]Description[-]\n%s\n\n"+
|
|
"[blue]• Homepage:[-] %s",
|
|
pkg.Name, installedIcon,
|
|
typeTag, typeLabel,
|
|
pkg.Name,
|
|
pkg.DisplayName,
|
|
pkg.Version,
|
|
installedStatus,
|
|
pkg.Description,
|
|
pkg.Homepage,
|
|
)
|
|
|
|
// Installation details
|
|
installDetails := d.getPackageInstallationDetails(pkg)
|
|
|
|
// Dependencies (only for formulae)
|
|
dependenciesInfo := ""
|
|
if pkg.Type == models.PackageTypeFormula && pkg.Formula != nil {
|
|
dependenciesInfo = d.getDependenciesInfo(pkg.Formula)
|
|
}
|
|
|
|
analyticsInfo := d.getAnalyticsInfo(pkg)
|
|
|
|
parts := []string{basicInfo, installDetails}
|
|
if dependenciesInfo != "" {
|
|
parts = append(parts, dependenciesInfo)
|
|
}
|
|
parts = append(parts, analyticsInfo)
|
|
|
|
d.view.SetText(strings.Join(parts, "\n\n"))
|
|
}
|
|
|
|
func (d *Details) getPackageInstallationDetails(pkg *models.Package) string {
|
|
if !pkg.LocallyInstalled {
|
|
return "[yellow::b]Installation[-]\nNot installed"
|
|
}
|
|
|
|
// For formulae, show detailed installation info
|
|
if pkg.Type == models.PackageTypeFormula && pkg.Formula != nil && len(pkg.Formula.Installed) > 0 {
|
|
packagePrefix := pkg.Formula.LocalPath
|
|
|
|
installedOnRequest := "No"
|
|
if pkg.Formula.Installed[0].InstalledOnRequest {
|
|
installedOnRequest = "Yes"
|
|
}
|
|
|
|
installedAsDependency := "No"
|
|
if pkg.Formula.Installed[0].InstalledAsDependency {
|
|
installedAsDependency = "Yes"
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"[yellow::b]Installation Details[-]\n"+
|
|
"[blue]• Path:[-] %s\n"+
|
|
"[blue]• Installed on request:[-] %s\n"+
|
|
"[blue]• Installed as dependency:[-] %s\n"+
|
|
"[blue]• Installed version:[-] %s",
|
|
packagePrefix,
|
|
installedOnRequest,
|
|
installedAsDependency,
|
|
pkg.Formula.Installed[0].Version,
|
|
)
|
|
}
|
|
|
|
// For casks, show simpler installation info
|
|
if pkg.Type == models.PackageTypeCask && pkg.Cask != nil {
|
|
installedVersion := "Unknown"
|
|
if pkg.Cask.Installed != nil {
|
|
installedVersion = *pkg.Cask.Installed
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"[yellow::b]Installation Details[-]\n"+
|
|
"[blue]• Type:[-] macOS Application\n"+
|
|
"[blue]• Installed version:[-] %s",
|
|
installedVersion,
|
|
)
|
|
}
|
|
|
|
return "[yellow::b]Installation[-]\nInstalled"
|
|
}
|
|
|
|
func (d *Details) getDependenciesInfo(info *models.Formula) string {
|
|
title := "[yellow::b]Dependencies[-]\n"
|
|
|
|
if len(info.Dependencies) == 0 {
|
|
return title + "No dependencies"
|
|
}
|
|
|
|
// Format dependencies in multiple columns or with separators
|
|
deps := ""
|
|
for i, dep := range info.Dependencies {
|
|
deps += dep
|
|
if i < len(info.Dependencies)-1 {
|
|
if (i+1)%3 == 0 {
|
|
deps += "\n"
|
|
} else {
|
|
deps += ", "
|
|
}
|
|
}
|
|
}
|
|
|
|
return title + deps
|
|
}
|
|
|
|
func (d *Details) getAnalyticsInfo(pkg *models.Package) string {
|
|
title := "[yellow::b]Analytics[-]\n"
|
|
|
|
p := message.NewPrinter(language.English)
|
|
|
|
title += fmt.Sprintf("[blue]• 90d Global Rank:[-] %s\n", p.Sprintf("%d", pkg.Analytics90dRank))
|
|
title += fmt.Sprintf("[blue]• 90d Downloads:[-] %s\n", p.Sprintf("%d", pkg.Analytics90dDownloads))
|
|
|
|
return title
|
|
}
|
|
|
|
func (d *Details) View() *tview.TextView {
|
|
return d.view
|
|
}
|
|
|
|
func (d *Details) Clear() {
|
|
d.view.Clear()
|
|
}
|