mirror of
https://github.com/Valkyrie00/bold-brew.git
synced 2026-03-14 14:25:53 +01:00
Merge 0fcc9ea00b into 65824fce99
This commit is contained in:
commit
ee7a1c286d
4 changed files with 154 additions and 8 deletions
4
.env
4
.env
|
|
@ -2,5 +2,5 @@ APP_NAME=bbrew
|
|||
APP_VERSION=0.0.1-local
|
||||
CONTAINER_IMAGE_NAME=bbrew
|
||||
BUILD_GOVERSION=1.25
|
||||
BUILD_GOOS=darwin
|
||||
BUILD_GOARCH=arm64
|
||||
BUILD_GOOS=linux
|
||||
BUILD_GOARCH=amd64
|
||||
2
Makefile
2
Makefile
|
|
@ -13,7 +13,7 @@ BUILD_GOOS ?= $(shell go env GOOS)
|
|||
BUILD_GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
# Container runtime command
|
||||
CONTAINER_RUN = podman run --rm -v $(PWD):/app $(CONTAINER_IMAGE_NAME)
|
||||
CONTAINER_RUN = podman run --rm -v $(PWD):/app:Z $(CONTAINER_IMAGE_NAME)
|
||||
|
||||
##############################
|
||||
# HELP
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ type InputService struct {
|
|||
ActionHelp *InputAction
|
||||
ActionBack *InputAction
|
||||
ActionQuit *InputAction
|
||||
ActionToggleSelection *InputAction
|
||||
}
|
||||
|
||||
var NewInputService = func(appService *AppService, brewService BrewServiceInterface) InputServiceInterface {
|
||||
|
|
@ -124,13 +125,17 @@ var NewInputService = func(appService *AppService, brewService BrewServiceInterf
|
|||
Key: tcell.KeyRune, Rune: 'q', KeySlug: "q", Name: "Quit",
|
||||
Action: s.handleQuitEvent, HideFromLegend: true,
|
||||
}
|
||||
s.ActionToggleSelection = &InputAction{
|
||||
Key: tcell.KeyRune, Rune: ' ', KeySlug: "space", Name: "Select",
|
||||
Action: s.handleToggleSelectionEvent, HideFromLegend: true,
|
||||
}
|
||||
|
||||
// Build keyActions slice (InstallAll/RemoveAll added dynamically in Brewfile mode)
|
||||
s.keyActions = []*InputAction{
|
||||
s.ActionSearch, s.ActionFilterInstalled, s.ActionFilterOutdated,
|
||||
s.ActionFilterLeaves, s.ActionFilterCasks, s.ActionInstall,
|
||||
s.ActionUpdate, s.ActionRemove, s.ActionUpdateAll,
|
||||
s.ActionHelp, s.ActionBack, s.ActionQuit,
|
||||
s.ActionHelp, s.ActionBack, s.ActionQuit, s.ActionToggleSelection,
|
||||
}
|
||||
|
||||
// Convert keyActions to legend entries
|
||||
|
|
@ -169,6 +174,12 @@ func (s *InputService) HandleKeyEventInput(event *tcell.EventKey) *tcell.EventKe
|
|||
return event
|
||||
}
|
||||
|
||||
// Handle Space explicitly since it might conflict or need special handling
|
||||
if event.Key() == tcell.KeyRune && event.Rune() == ' ' {
|
||||
s.handleToggleSelectionEvent()
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, input := range s.keyActions {
|
||||
if event.Modifiers() == tcell.ModNone && input.Key == event.Key() && input.Rune == event.Rune() { // Check Rune
|
||||
if input.Action != nil {
|
||||
|
|
@ -188,8 +199,33 @@ func (s *InputService) HandleKeyEventInput(event *tcell.EventKey) *tcell.EventKe
|
|||
|
||||
// handleBack is called when the user presses the back key (Esc).
|
||||
func (s *InputService) handleBack() {
|
||||
s.layout.GetTable().ClearSelection()
|
||||
s.appService.GetApp().SetRoot(s.layout.Root(), true)
|
||||
s.appService.GetApp().SetFocus(s.layout.GetTable().View())
|
||||
// Force redraw of table to remove selection visuals
|
||||
// s.appService.forceRefreshResults() // Might be too heavy?
|
||||
// Actually Table.ToggleSelection updates visual.
|
||||
// ClearSelection needs to update visual too.
|
||||
// But Table.ClearSelection just clears the map. I need to implement visual clear in Table or just force refresh.
|
||||
// For now, let's just assume we need to refresh.
|
||||
s.appService.search(s.layout.GetSearch().Field().GetText(), false)
|
||||
}
|
||||
|
||||
// handleToggleSelectionEvent toggles the selection of the current row.
|
||||
func (s *InputService) handleToggleSelectionEvent() {
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 { // Skip header
|
||||
// Determine highlight color based on package status
|
||||
color := tcell.ColorDarkCyan
|
||||
if row-1 < len(*s.appService.filteredPackages) {
|
||||
pkg := (*s.appService.filteredPackages)[row-1]
|
||||
if pkg.LocallyInstalled {
|
||||
color = tcell.ColorDarkRed // Use DarkRed for installed packages to indicate different state
|
||||
}
|
||||
}
|
||||
|
||||
s.layout.GetTable().ToggleSelection(row, color)
|
||||
}
|
||||
}
|
||||
|
||||
// handleSearchFieldEvent is called when the user presses the search key (/).
|
||||
|
|
@ -306,6 +342,13 @@ func (s *InputService) closeModal() {
|
|||
|
||||
// handleInstallPackageEvent is called when the user presses the installation key (i).
|
||||
func (s *InputService) handleInstallPackageEvent() {
|
||||
if len(s.layout.GetTable().GetSelectedRows()) > 0 {
|
||||
s.processSelectedPackages("install", "INSTALL", func(pkg models.Package) error {
|
||||
return s.brewService.InstallPackage(pkg, s.appService.app, s.layout.GetOutput().View())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.appService.filteredPackages)[row-1]
|
||||
|
|
@ -329,6 +372,13 @@ func (s *InputService) handleInstallPackageEvent() {
|
|||
|
||||
// handleRemovePackageEvent is called when the user presses the removal key (r).
|
||||
func (s *InputService) handleRemovePackageEvent() {
|
||||
if len(s.layout.GetTable().GetSelectedRows()) > 0 {
|
||||
s.processSelectedPackages("remove", "REMOVE", func(pkg models.Package) error {
|
||||
return s.brewService.RemovePackage(pkg, s.appService.app, s.layout.GetOutput().View())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.appService.filteredPackages)[row-1]
|
||||
|
|
@ -352,6 +402,13 @@ func (s *InputService) handleRemovePackageEvent() {
|
|||
|
||||
// handleUpdatePackageEvent is called when the user presses the update key (u).
|
||||
func (s *InputService) handleUpdatePackageEvent() {
|
||||
if len(s.layout.GetTable().GetSelectedRows()) > 0 {
|
||||
s.processSelectedPackages("update", "UPDATE", func(pkg models.Package) error {
|
||||
return s.brewService.UpdatePackage(pkg, s.appService.app, s.layout.GetOutput().View())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.appService.filteredPackages)[row-1]
|
||||
|
|
@ -399,6 +456,53 @@ type batchOperation struct {
|
|||
execute func(pkg models.Package) error
|
||||
}
|
||||
|
||||
// processSelectedPackages processes the selected packages from the table.
|
||||
func (s *InputService) processSelectedPackages(verb, tag string, action func(models.Package) error) {
|
||||
selectedRows := s.layout.GetTable().GetSelectedRows()
|
||||
if len(selectedRows) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
packages := make([]models.Package, 0, len(selectedRows))
|
||||
for _, row := range selectedRows {
|
||||
if row > 0 && row-1 < len(*s.appService.filteredPackages) {
|
||||
packages = append(packages, (*s.appService.filteredPackages)[row-1])
|
||||
}
|
||||
}
|
||||
|
||||
if len(packages) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.showModal(fmt.Sprintf("Are you sure you want to %s %d selected packages?", verb, len(packages)), func() {
|
||||
s.closeModal()
|
||||
s.layout.GetOutput().Clear()
|
||||
go func() {
|
||||
total := len(packages)
|
||||
for i, pkg := range packages {
|
||||
s.layout.GetNotifier().ShowWarning(fmt.Sprintf("[%d/%d] %s %s...", i+1, total, verb, pkg.Name))
|
||||
s.appService.app.QueueUpdateDraw(func() {
|
||||
fmt.Fprintf(s.layout.GetOutput().View(), "\n[%s] %s %s...\n", tag, verb, pkg.Name)
|
||||
})
|
||||
|
||||
if err := action(pkg); err != nil {
|
||||
s.layout.GetNotifier().ShowError(fmt.Sprintf("Failed to %s %s", verb, pkg.Name))
|
||||
s.appService.app.QueueUpdateDraw(func() {
|
||||
fmt.Fprintf(s.layout.GetOutput().View(), "[ERROR] Failed to %s %s: %v\n", verb, pkg.Name, err)
|
||||
})
|
||||
continue
|
||||
}
|
||||
s.appService.app.QueueUpdateDraw(func() {
|
||||
fmt.Fprintf(s.layout.GetOutput().View(), "[SUCCESS] %s processed successfully\n", pkg.Name)
|
||||
})
|
||||
}
|
||||
s.layout.GetNotifier().ShowSuccess(fmt.Sprintf("Completed! Processed %d packages", total))
|
||||
s.layout.GetTable().ClearSelection() // Clear selection after batch operation
|
||||
s.appService.forceRefreshResults()
|
||||
}()
|
||||
}, s.closeModal)
|
||||
}
|
||||
|
||||
// handleBatchPackageOperation processes multiple packages with progress notifications.
|
||||
func (s *InputService) handleBatchPackageOperation(op batchOperation) {
|
||||
if !s.appService.IsBrewfileMode() {
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@ import (
|
|||
)
|
||||
|
||||
type Table struct {
|
||||
view *tview.Table
|
||||
theme *theme.Theme
|
||||
view *tview.Table
|
||||
theme *theme.Theme
|
||||
selectedRows map[int]bool
|
||||
}
|
||||
|
||||
func NewTable(theme *theme.Theme) *Table {
|
||||
table := &Table{
|
||||
view: tview.NewTable(),
|
||||
theme: theme,
|
||||
view: tview.NewTable(),
|
||||
theme: theme,
|
||||
selectedRows: make(map[int]bool),
|
||||
}
|
||||
table.view.SetBorders(false)
|
||||
table.view.SetSelectable(true, false)
|
||||
|
|
@ -37,6 +39,46 @@ func (t *Table) View() *tview.Table {
|
|||
|
||||
func (t *Table) Clear() {
|
||||
t.view.Clear()
|
||||
t.selectedRows = make(map[int]bool)
|
||||
}
|
||||
|
||||
func (t *Table) ClearSelection() {
|
||||
t.selectedRows = make(map[int]bool)
|
||||
}
|
||||
|
||||
func (t *Table) ToggleSelection(row int, highlightColor tcell.Color) {
|
||||
isSelected := false
|
||||
if t.selectedRows[row] {
|
||||
delete(t.selectedRows, row)
|
||||
} else {
|
||||
t.selectedRows[row] = true
|
||||
isSelected = true
|
||||
}
|
||||
|
||||
// Update visual style for the row
|
||||
colCount := t.view.GetColumnCount()
|
||||
for i := 0; i < colCount; i++ {
|
||||
cell := t.view.GetCell(row, i)
|
||||
if cell != nil {
|
||||
if isSelected {
|
||||
cell.SetBackgroundColor(highlightColor)
|
||||
} else {
|
||||
cell.SetBackgroundColor(t.theme.DefaultBgColor) // Or tcell.ColorDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) IsSelected(row int) bool {
|
||||
return t.selectedRows[row]
|
||||
}
|
||||
|
||||
func (t *Table) GetSelectedRows() []int {
|
||||
rows := make([]int, 0, len(t.selectedRows))
|
||||
for row := range t.selectedRows {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (t *Table) SetTableHeaders(headers ...string) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue