mirror of
https://github.com/anotherhadi/usbguard-tui.git
synced 2026-08-21 12:05:50 +02:00
Print on exit -> copy to clipboard
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"charm.land/bubbles/v2/key"
|
"charm.land/bubbles/v2/key"
|
||||||
@@ -50,7 +51,8 @@ func (a actionModal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
it := item.(actionItem)
|
it := item.(actionItem)
|
||||||
if it.nixos {
|
if it.nixos {
|
||||||
rule := guard.NixOSRule(a.dev, it.status)
|
rule := guard.NixOSRule(a.dev, it.status)
|
||||||
return a, tea.Batch(modal.Close(), func() tea.Msg { return nixRuleMsg{rule: rule} })
|
key := a.dev.VidPid
|
||||||
|
return a, tea.Batch(modal.Close(), func() tea.Msg { return nixRuleMsg{key: key, rule: rule} })
|
||||||
}
|
}
|
||||||
return a, tea.Batch(modal.Close(), doAction(a.dev.ID, it.fn, it.permanent))
|
return a, tea.Batch(modal.Close(), doAction(a.dev.ID, it.fn, it.permanent))
|
||||||
}
|
}
|
||||||
@@ -74,7 +76,7 @@ func (a actionModal) View() tea.View {
|
|||||||
hintStyle := lipgloss.NewStyle().Foreground(style.S.Muted)
|
hintStyle := lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||||
parts := []string{a.list.View(), ""}
|
parts := []string{a.list.View(), ""}
|
||||||
if a.rulesManaged {
|
if a.rulesManaged {
|
||||||
parts = append(parts, hintStyle.Render("[NixOS: perm rules printed on exit]"))
|
parts = append(parts, hintStyle.Render(fmt.Sprintf("[NixOS: perm rules queued — press %s to copy]", listKeys.CopyRules.Help().Key)))
|
||||||
}
|
}
|
||||||
parts = append(parts, hintStyle.Render("↑↓ navigate enter confirm esc cancel"))
|
parts = append(parts, hintStyle.Render("↑↓ navigate enter confirm esc cancel"))
|
||||||
return tea.NewView(strings.Join(parts, "\n"))
|
return tea.NewView(strings.Join(parts, "\n"))
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@ type listKeyMap struct {
|
|||||||
|
|
||||||
AllowAll key.Binding
|
AllowAll key.Binding
|
||||||
AllowAllPerm key.Binding
|
AllowAllPerm key.Binding
|
||||||
PrintAll key.Binding
|
CopyRules key.Binding
|
||||||
|
|
||||||
Up key.Binding
|
Up key.Binding
|
||||||
Down key.Binding
|
Down key.Binding
|
||||||
@@ -32,7 +32,7 @@ func (k listKeyMap) globalBindings() []key.Binding {
|
|||||||
return []key.Binding{
|
return []key.Binding{
|
||||||
k.Open, k.Filter, k.Refresh, k.Quit,
|
k.Open, k.Filter, k.Refresh, k.Quit,
|
||||||
k.Allow, k.AllowPerm, k.Block, k.BlockPerm, k.Reject, k.RejectPerm,
|
k.Allow, k.AllowPerm, k.Block, k.BlockPerm, k.Reject, k.RejectPerm,
|
||||||
k.AllowAll, k.AllowAllPerm, k.PrintAll,
|
k.AllowAll, k.AllowAllPerm, k.CopyRules,
|
||||||
k.Up, k.Down, k.GoToStart, k.GoToEnd, k.PrevPage, k.NextPage,
|
k.Up, k.Down, k.GoToStart, k.GoToEnd, k.PrevPage, k.NextPage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ var listKeys = listKeyMap{
|
|||||||
RejectPerm: key.NewBinding(key.WithKeys("E"), key.WithHelp("E", "reject (perm)")),
|
RejectPerm: key.NewBinding(key.WithKeys("E"), key.WithHelp("E", "reject (perm)")),
|
||||||
AllowAll: key.NewBinding(key.WithKeys("w"), key.WithHelp("w", "allow all")),
|
AllowAll: key.NewBinding(key.WithKeys("w"), key.WithHelp("w", "allow all")),
|
||||||
AllowAllPerm: key.NewBinding(key.WithKeys("W"), key.WithHelp("W", "allow all (perm)")),
|
AllowAllPerm: key.NewBinding(key.WithKeys("W"), key.WithHelp("W", "allow all (perm)")),
|
||||||
PrintAll: key.NewBinding(key.WithKeys("p"), key.WithHelp("p", "print all rules")),
|
CopyRules: key.NewBinding(key.WithKeys("y"), key.WithHelp("y", "copy rules")),
|
||||||
|
|
||||||
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up")),
|
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up")),
|
||||||
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
||||||
|
|||||||
+52
-16
@@ -54,14 +54,6 @@ type Model struct {
|
|||||||
fatalShown bool
|
fatalShown bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) PendingRules() []string {
|
|
||||||
rules := make([]string, len(m.pendingRules))
|
|
||||||
for i, r := range m.pendingRules {
|
|
||||||
rules[i] = r.rule
|
|
||||||
}
|
|
||||||
return rules
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() Model {
|
func New() Model {
|
||||||
l := bubbles.NewList(nil, 0, 0)
|
l := bubbles.NewList(nil, 0, 0)
|
||||||
l.SetDelegate(deviceDelegate{})
|
l.SetDelegate(deviceDelegate{})
|
||||||
@@ -261,8 +253,15 @@ func (m Model) updateList(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
|||||||
return m, queueNixOSRules(m.visibleDevices(), guard.Allowed)
|
return m, queueNixOSRules(m.visibleDevices(), guard.Allowed)
|
||||||
}
|
}
|
||||||
return m, doBulkAction(m.visibleDeviceIDs(), guard.AllowDevice, true)
|
return m, doBulkAction(m.visibleDeviceIDs(), guard.AllowDevice, true)
|
||||||
case key.Matches(msg, listKeys.PrintAll):
|
case key.Matches(msg, listKeys.CopyRules):
|
||||||
return m, queueCurrentStateRules(m.visibleDevices())
|
toCopy := m.pendingRules
|
||||||
|
if len(toCopy) == 0 {
|
||||||
|
toCopy = mergeCurrentStateRules(nil, m.visibleDevices())
|
||||||
|
}
|
||||||
|
cmd := copyRulesCmd(toCopy, m.rulesManaged)
|
||||||
|
m.pendingRules = nil
|
||||||
|
m.resizeList()
|
||||||
|
return m, cmd
|
||||||
}
|
}
|
||||||
if hasSelection {
|
if hasSelection {
|
||||||
if cmd := m.deviceActionCmd(msg, dev); cmd != nil {
|
if cmd := m.deviceActionCmd(msg, dev); cmd != nil {
|
||||||
@@ -392,7 +391,7 @@ func (m Model) renderPendingRulesLine() string {
|
|||||||
noun = "rules"
|
noun = "rules"
|
||||||
}
|
}
|
||||||
label := infoLabelStyle.Render("Pending rules")
|
label := infoLabelStyle.Render("Pending rules")
|
||||||
return label + warnStyle.Render(fmt.Sprintf("%d %s queued (printed on exit)", count, noun))
|
return label + warnStyle.Render(fmt.Sprintf("%d %s queued (press %s to copy)", count, noun, listKeys.CopyRules.Help().Key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) listHeight() int {
|
func (m Model) listHeight() int {
|
||||||
@@ -488,14 +487,51 @@ func queueNixOSRules(devices []guard.Device, status guard.Status) tea.Cmd {
|
|||||||
return tea.Batch(cmds...)
|
return tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func queueCurrentStateRules(devices []guard.Device) tea.Cmd {
|
func mergeCurrentStateRules(pending []pendingRule, devices []guard.Device) []pendingRule {
|
||||||
cmds := make([]tea.Cmd, len(devices))
|
for _, d := range devices {
|
||||||
for i, d := range devices {
|
|
||||||
key := d.VidPid
|
key := d.VidPid
|
||||||
rule := guard.NixOSRule(d, d.Status)
|
rule := guard.NixOSRule(d, d.Status)
|
||||||
cmds[i] = func() tea.Msg { return nixRuleMsg{key: key, rule: rule} }
|
if i := slices.IndexFunc(pending, func(r pendingRule) bool { return r.key == key }); i >= 0 {
|
||||||
|
pending[i].rule = rule
|
||||||
|
} else {
|
||||||
|
pending = append(pending, pendingRule{key: key, rule: rule})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tea.Batch(cmds...)
|
return pending
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyRulesCmd(pending []pendingRule, nixos bool) tea.Cmd {
|
||||||
|
if len(pending) == 0 {
|
||||||
|
return notification.Show("Copy rules", "No rules to copy.", notification.Warning)
|
||||||
|
}
|
||||||
|
rules := make([]string, len(pending))
|
||||||
|
for i, r := range pending {
|
||||||
|
rules[i] = r.rule
|
||||||
|
}
|
||||||
|
text := formatRulesForClipboard(rules, nixos)
|
||||||
|
noun := "rule"
|
||||||
|
if len(rules) > 1 {
|
||||||
|
noun = "rules"
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf("%d %s copied to clipboard.", len(rules), noun)
|
||||||
|
return tea.Batch(
|
||||||
|
tea.SetClipboard(text),
|
||||||
|
notification.Show("Copy rules", msg, notification.Success),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatRulesForClipboard(rules []string, nixos bool) string {
|
||||||
|
if !nixos {
|
||||||
|
return strings.Join(rules, "\n")
|
||||||
|
}
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("# Add to your NixOS configuration:\n")
|
||||||
|
b.WriteString("services.usbguard.rules = lib.mkAfter ''\n")
|
||||||
|
for _, r := range rules {
|
||||||
|
b.WriteString(" " + r + "\n")
|
||||||
|
}
|
||||||
|
b.WriteString("'';")
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type actionBinding struct {
|
type actionBinding struct {
|
||||||
|
|||||||
@@ -23,19 +23,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := tea.NewProgram(ui.New())
|
p := tea.NewProgram(ui.New())
|
||||||
m, err := p.Run()
|
if _, err := p.Run(); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if fm, ok := m.(ui.Model); ok {
|
|
||||||
if rules := fm.PendingRules(); len(rules) > 0 {
|
|
||||||
fmt.Println("# Add to your NixOS configuration:")
|
|
||||||
fmt.Println("services.usbguard.rules = lib.mkAfter ''")
|
|
||||||
for _, rule := range rules {
|
|
||||||
fmt.Println(" ", rule)
|
|
||||||
}
|
|
||||||
fmt.Println("'';")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user