Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-04-30 17:33:42 +02:00
commit 09b054cc5c
16 changed files with 1037 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
package ui
import (
"fmt"
"io"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/anotherhadi/usbguard-tui/internal/guard"
)
// deviceDelegate renders device list items with status colors.
type deviceDelegate struct{}
func (d deviceDelegate) Height() int { return 2 }
func (d deviceDelegate) Spacing() int { return 0 }
func (d deviceDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
func (d deviceDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
dev, ok := item.(guard.Device)
if !ok {
return
}
selected := index == m.Index()
var color lipgloss.Color
if selected {
var ok bool
color, ok = statusColorsSelected[dev.Status]
if !ok {
color = colorMuted
}
} else {
var ok bool
color, ok = statusColors[dev.Status]
if !ok {
color = colorMuted
}
}
var nameStyle, descStyle lipgloss.Style
if selected {
nameStyle = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(colorAccent).
Foreground(color).
Bold(true).
PaddingLeft(1)
descStyle = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(colorAccent).
Foreground(colorMuted).
PaddingLeft(1)
} else {
nameStyle = lipgloss.NewStyle().Foreground(color).PaddingLeft(2)
descStyle = lipgloss.NewStyle().Foreground(colorMuted).PaddingLeft(2)
}
fmt.Fprintf(w, "%s\n%s",
nameStyle.Render(dev.Name),
descStyle.Render(fmt.Sprintf("id:%-3d %s %s", dev.ID, dev.VidPid, string(dev.Status))),
)
}
// actionItem represents a device policy action in the select popup.
type actionItem struct {
label string
fn func(int, bool) error
permanent bool
status guard.Status
}
func (a actionItem) Title() string { return a.label }
func (a actionItem) Description() string { return "" }
func (a actionItem) FilterValue() string { return a.label }
// actionDelegate renders single-line action items.
type actionDelegate struct{}
func (d actionDelegate) Height() int { return 1 }
func (d actionDelegate) Spacing() int { return 0 }
func (d actionDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
func (d actionDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
a, ok := item.(actionItem)
if !ok {
return
}
if index == m.Index() {
color, ok := statusColorsSelected[a.status]
if !ok {
color = colorAccent
}
fmt.Fprintf(w, " %s", lipgloss.NewStyle().Bold(true).Foreground(color).Render("> "+a.label))
} else {
fmt.Fprintf(w, " %s", a.label)
}
}