mirror of
https://github.com/anotherhadi/usbguard-tui.git
synced 2026-10-05 18:28:24 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f69373267e | ||
|
|
83fa5622aa | ||
|
|
b3a59c9174 | ||
|
|
658b052307 | ||
|
|
1ba517179e | ||
|
|
2e91b6bc2a | ||
|
|
2d6c337610 | ||
|
|
032397e1fd |
@@ -6,6 +6,7 @@ before:
|
||||
|
||||
builds:
|
||||
- binary: usbguard-tui
|
||||
main: ./cmd/usbguard-tui
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 533 KiB |
@@ -1,8 +1,8 @@
|
||||
Output ./assets/demo.gif
|
||||
Output .github/assets/demo.gif
|
||||
Require usbguard-tui
|
||||
|
||||
Set Shell "zsh"
|
||||
Set FontSize 32
|
||||
Set FontSize 26
|
||||
Set Width 1500
|
||||
Set Height 1000
|
||||
|
||||
|
||||
@@ -23,6 +23,6 @@ jobs:
|
||||
- uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
version: "~> v2"
|
||||
args: release --clean
|
||||
args: release --clean --config .github/.goreleaser.yaml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
/go.work
|
||||
/go.work.sum
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
```
|
||||
▖▖▄▖▄ ▄▖ ▌ ▄▖▖▖▄▖
|
||||
▌▌▚ ▙▘▌ ▌▌▀▌▛▘▛▌ ▐ ▌▌▐
|
||||
▙▌▄▌▙▘▙▌▙▌█▌▌ ▙▌ ▐ ▙▌▟▖
|
||||
_ _ ____ ____ ____ _ _____ _ _ ___
|
||||
| | | / ___|| __ ) / ___|_ _ __ _ _ __ __| | |_ _| | | |_ _|
|
||||
| | | \___ \| _ \| | _| | | |/ _` | '__/ _` | | | | | | || |
|
||||
| |_| |___) | |_) | |_| | |_| | (_| | | | (_| | | | | |_| || |
|
||||
\___/|____/|____/ \____|\__,_|\__,_|_| \__,_| |_| \___/|___|
|
||||
```
|
||||
|
||||
[](go.mod)
|
||||
[](https://github.com/anotherhadi/usbguard-tui/releases)
|
||||
[](LICENSE)
|
||||
[](https://goreportcard.com/report/github.com/anotherhadi/usbguard-tui)
|
||||

|
||||

|
||||

|
||||
[](https://ko-fi.com/anotherhadi)
|
||||
|
||||
# USBGuard TUI
|
||||
|
||||
A terminal UI for managing USB devices via [usbguard](https://usbguard.github.io/).
|
||||
> A terminal UI for managing USB devices via [usbguard](https://usbguard.github.io/).
|
||||
|
||||
USBGuard is a software framework for implementing a USB device authorization policy (allowlisting/blocklisting). It protects your system against rogue USB devices by scanning them and checking their parameters against a set of rules.
|
||||
|
||||
@@ -21,6 +23,8 @@ Built with [bubbletea](https://github.com/charmbracelet/bubbletea) & Golang!
|
||||
|
||||
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
|
||||
|
||||
> This project is NOT affiliated with, endorsed by, or connected to "Red Hat, Inc" in any way. It is an unofficial, community-driven tool.
|
||||
|
||||
## Features
|
||||
|
||||
- List all connected USB devices with their current status
|
||||
@@ -28,7 +32,7 @@ Colors and styles can be customized using [ilovetui](https://github.com/anotherh
|
||||
- Action popup for quick device management
|
||||
- Filter devices by name with `/`
|
||||
- Auto-refresh
|
||||
- Keyboard shortcuts for all actions (`a`/`A`, `b`/`B`, `e`/`E`, ...)
|
||||
- Keyboard shortcuts for all actions (`a`/`A`, `b`/`B`, `e`/`E`, ...) & Mouse support
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -52,7 +56,7 @@ go install github.com/anotherhadi/usbguard-tui@latest
|
||||
```sh
|
||||
git clone https://github.com/anotherhadi/usbguard-tui
|
||||
cd usbguard-tui
|
||||
go build -o usbguard-tui .
|
||||
go build -o usbguard-tui ./cmd/usbguard-tui
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -95,4 +99,4 @@ The device list refreshes automatically every 2 seconds.
|
||||
<a href="https://github.com/anotherhadi/usbguard-tui">github</a> |
|
||||
<a href="https://gitlab.com/anotherhadi_mirror/usbguard-tui">gitlab (mirror)</a> |
|
||||
<a href="https://git.hadi.icu/anotherhadi/usbguard-tui">gitea (mirror)</a>
|
||||
</div
|
||||
</div>
|
||||
|
||||
@@ -2,23 +2,35 @@ package main
|
||||
|
||||
import (
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/anotherhadi/ilovetui/app"
|
||||
"github.com/anotherhadi/ilovetui/minsize"
|
||||
"github.com/anotherhadi/ilovetui/modal"
|
||||
"github.com/anotherhadi/ilovetui/notification"
|
||||
"github.com/anotherhadi/usbguard-tui/ui"
|
||||
zone "github.com/lrstanley/bubblezone/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
minWidth = 40
|
||||
minHeight = 15
|
||||
)
|
||||
|
||||
type appModel struct {
|
||||
core ui.Model
|
||||
modals modal.Model
|
||||
notif notification.Model
|
||||
core ui.Model
|
||||
modals modal.Model
|
||||
notif notification.Model
|
||||
minsize minsize.Model
|
||||
width, height int
|
||||
}
|
||||
|
||||
func newApp() appModel {
|
||||
zone.NewGlobal()
|
||||
return appModel{
|
||||
core: ui.New(),
|
||||
modals: modal.New(),
|
||||
notif: notification.New(),
|
||||
core: ui.New(),
|
||||
modals: modal.New(),
|
||||
notif: notification.New(),
|
||||
minsize: minsize.New(minWidth, minHeight),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +42,9 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if _, ok := msg.(app.QuitMsg); ok {
|
||||
return a, tea.Quit
|
||||
}
|
||||
if sizeMsg, ok := msg.(tea.WindowSizeMsg); ok {
|
||||
a.width, a.height = sizeMsg.Width, sizeMsg.Height
|
||||
}
|
||||
|
||||
a.core.ModalOpen = a.modals.Open()
|
||||
var coreCmd tea.Cmd
|
||||
@@ -43,9 +58,18 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (a appModel) View() tea.View {
|
||||
bg := a.core.View()
|
||||
if !a.minsize.Fits(a.width, a.height) {
|
||||
return tea.View{
|
||||
Content: a.minsize.View(a.width, a.height),
|
||||
AltScreen: true,
|
||||
WindowTitle: "USBGuard TUI",
|
||||
}
|
||||
}
|
||||
|
||||
bg := lipgloss.Place(a.width, a.height, lipgloss.Left, lipgloss.Top, a.core.View())
|
||||
bg = a.modals.Render(bg)
|
||||
bg = a.notif.Render(bg)
|
||||
bg = zone.Scan(bg)
|
||||
return tea.View{
|
||||
Content: bg,
|
||||
AltScreen: true,
|
||||
@@ -21,6 +21,11 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if _, err := guard.ListDevices(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
p := tea.NewProgram(newApp())
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
@@ -14,7 +14,7 @@
|
||||
(system: f system (import nixpkgs {inherit system;}));
|
||||
|
||||
pname = "usbguard-tui";
|
||||
version = "1.2.0";
|
||||
version = "1.3.0";
|
||||
|
||||
ldflags = ["-s" "-w" "-X main.version=${version}"];
|
||||
in {
|
||||
@@ -25,12 +25,12 @@
|
||||
src = ./.;
|
||||
outputs = ["out"];
|
||||
|
||||
vendorHash = "sha256-8QP2FbNmvqrJhFe3Ia7tPhkxMeFWBhvr4Zr9kpV9bR4=";
|
||||
vendorHash = "sha256-O9DG0pxRKt8VwpZdyvoQ4wsfEbsh5npmwocmtcm2IfA=";
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "A terminal UI for managing USB devices via usbguard.";
|
||||
homepage = "https://github.com/anotherhadi/usbguard-tui";
|
||||
platforms = platforms.unix;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
};
|
||||
in {
|
||||
|
||||
@@ -3,42 +3,43 @@ module github.com/anotherhadi/usbguard-tui
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
charm.land/bubbles/v2 v2.1.0
|
||||
charm.land/bubbletea/v2 v2.0.6
|
||||
charm.land/lipgloss/v2 v2.0.3
|
||||
github.com/anotherhadi/ilovetui v0.2.2
|
||||
github.com/charmbracelet/x/ansi v0.11.7
|
||||
charm.land/bubbles/v2 v2.2.0
|
||||
charm.land/bubbletea/v2 v2.0.9
|
||||
charm.land/lipgloss/v2 v2.0.6
|
||||
github.com/anotherhadi/ilovetui v0.2.4
|
||||
github.com/charmbracelet/x/ansi v0.11.8
|
||||
github.com/lrstanley/bubblezone/v2 v2.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
charm.land/glamour/v2 v2.0.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
charm.land/glamour/v2 v2.0.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260812204455-68fa937c71be // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260823001701-96af6d2cb5f6 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/dlclark/regexp2/v2 v2.7.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.28 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.5 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.3 // indirect
|
||||
github.com/xo/terminfo v1.0.0 // indirect
|
||||
github.com/yuin/goldmark v1.8.5 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.6 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
|
||||
charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
|
||||
charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo=
|
||||
charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g=
|
||||
charm.land/glamour/v2 v2.0.0 h1:IDBoqLEy7Hdpb9VOXN+khLP/XSxtJy1VsHuW/yF87+U=
|
||||
charm.land/glamour/v2 v2.0.0/go.mod h1:kjq9WB0s8vuUYZNYey2jp4Lgd9f4cKdzAw88FZtpj/w=
|
||||
charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU=
|
||||
charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA=
|
||||
charm.land/bubbles/v2 v2.2.0 h1:9GEMewcejrNtVIxZ9Y2wSsWJamsWNsXa8MpMLnH4yI4=
|
||||
charm.land/bubbles/v2 v2.2.0/go.mod h1:wdMgn+sje1KNXdwFizIWjbf328fIUBxqEmJ/vYPo8yc=
|
||||
charm.land/bubbletea/v2 v2.0.9 h1:DpJCMWKgzQK8SJv4zbKKFHAI10ymWy/evClPFk0k0f8=
|
||||
charm.land/bubbletea/v2 v2.0.9/go.mod h1:2SkdgoTXluXJHOUwAoRlRXF/28vklb1rFl6GcgV1/ss=
|
||||
charm.land/glamour/v2 v2.0.1 h1:xl+r00A4aJWU0z8fgwKd9fQQ4rsphqGUzuEiXZP5n+c=
|
||||
charm.land/glamour/v2 v2.0.1/go.mod h1:jo9z8XqVKPeEFMVdvCRLGk++RyJ3CdUwgNr7EvXLw3k=
|
||||
charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ=
|
||||
charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anotherhadi/ilovetui v0.2.2 h1:ZOwyZRxFBZKVJeEAR+lg0xeQfsMZuymtKLwBV0MnBYY=
|
||||
github.com/anotherhadi/ilovetui v0.2.2/go.mod h1:gAn69amLM3z0x8PV4obyf5bJntL/PTZusEuhu/xdFIY=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anotherhadi/ilovetui v0.2.4 h1:8D2jp/37vOJBRI3DKIHHt9PFsnIyfRxs3R58HuCh0tA=
|
||||
github.com/anotherhadi/ilovetui v0.2.4/go.mod h1:gAn69amLM3z0x8PV4obyf5bJntL/PTZusEuhu/xdFIY=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||
@@ -26,14 +26,14 @@ github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 h1:Q9fO0y1Zo5KB/5Vu8JZoLGm1N3RzF9bNj3Ao3xoR+Ac=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM=
|
||||
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
|
||||
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260812204455-68fa937c71be h1:qEvkJy1sjJXP+yf8IH2o13NV+Rh4nIUHjaLNJ+pWxpI=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260812204455-68fa937c71be/go.mod h1:nAw0d9PhFp1qdzi2xhQU5YOu5sVpDIHWlaW2Uz/bCro=
|
||||
github.com/charmbracelet/x/ansi v0.11.8 h1:JMFwp0CgDC2+jcOB162HH5k7I3FVbgFSMMYg7dSPBQQ=
|
||||
github.com/charmbracelet/x/ansi v0.11.8/go.mod h1:ZNN+3mXny/516oTQPLMPIBeSINvNJJQ8uQXDgbeJxY0=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260823001701-96af6d2cb5f6 h1:je2x9scjVLOqRwO5JbFyRzyG5aCLg64DSOvd463cm04=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260823001701-96af6d2cb5f6/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||
@@ -44,8 +44,8 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.7.1 h1:yqDtwI1ptXXvEUNpYTk2lad4jLtAcKqkzepn4savSk4=
|
||||
github.com/dlclark/regexp2/v2 v2.7.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
@@ -54,35 +54,36 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/lrstanley/bubblezone/v2 v2.0.0 h1:pMb9fHKs0slJF6OrzQ2hEgWusqyl9VU/S0UZ5hyh7ZA=
|
||||
github.com/lrstanley/bubblezone/v2 v2.0.0/go.mod h1:yV/QTjcm4Zu5cqvGvdHi7xVUfnB36w/SafOuDp57dgY=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.28 h1:rPyg2ybwEKPebvpzVWe1gKBkH8EQFkxO4Y0hjBeLaBU=
|
||||
github.com/mattn/go-runewidth v0.0.28/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
|
||||
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
|
||||
github.com/sahilm/fuzzy v0.1.3 h1:juByESSS32nVD81vr6tHmKmA/8zde7gE+x5CLxrzXPU=
|
||||
github.com/sahilm/fuzzy v0.1.3/go.mod h1:au6//VbVSqu6DFrkL2CfjlJ5iURpNCPeE+1GwY3XsT8=
|
||||
github.com/xo/terminfo v1.0.0 h1:2ZpYzqWzyyytjk3TP6aJVDhkMAkc99/1xKQdA3TDTBY=
|
||||
github.com/xo/terminfo v1.0.0/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
|
||||
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
+25
-8
@@ -8,6 +8,8 @@ import (
|
||||
"charm.land/bubbles/v2/list"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
zone "github.com/lrstanley/bubblezone/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/modal"
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
"github.com/anotherhadi/usbguard-tui/internal/guard"
|
||||
@@ -37,6 +39,15 @@ func actionModalStyle(status guard.Status) modal.Styles {
|
||||
|
||||
func (a actionModal) Init() tea.Cmd { return nil }
|
||||
|
||||
func (a actionModal) confirmCmd(it actionItem) tea.Cmd {
|
||||
if it.nixos {
|
||||
rule := guard.NixOSRule(a.dev, it.status)
|
||||
key := a.dev.VidPid
|
||||
return tea.Batch(modal.Close(), func() tea.Msg { return nixRuleMsg{key: key, rule: rule} })
|
||||
}
|
||||
return tea.Batch(modal.Close(), doAction(a.dev.ID, it.fn, it.permanent))
|
||||
}
|
||||
|
||||
func (a actionModal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyPressMsg:
|
||||
@@ -48,13 +59,7 @@ func (a actionModal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if item == nil {
|
||||
return a, nil
|
||||
}
|
||||
it := item.(actionItem)
|
||||
if it.nixos {
|
||||
rule := guard.NixOSRule(a.dev, it.status)
|
||||
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, a.confirmCmd(item.(actionItem))
|
||||
}
|
||||
|
||||
case tea.MouseWheelMsg:
|
||||
@@ -65,6 +70,18 @@ func (a actionModal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
a.list.CursorDown()
|
||||
}
|
||||
return a, nil
|
||||
|
||||
case tea.MouseClickMsg:
|
||||
if msg.Mouse().Button != tea.MouseLeft {
|
||||
return a, nil
|
||||
}
|
||||
for i, item := range a.list.VisibleItems() {
|
||||
if zone.Get(actionZoneID(i)).InBounds(msg) {
|
||||
a.list.Select(i)
|
||||
return a, a.confirmCmd(item.(actionItem))
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
@@ -76,7 +93,7 @@ func (a actionModal) View() tea.View {
|
||||
hintStyle := lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||
parts := []string{a.list.View(), ""}
|
||||
if a.rulesManaged {
|
||||
parts = append(parts, hintStyle.Render(fmt.Sprintf("[NixOS: perm rules queued — press %s to copy]", listKeys.CopyRules.Help().Key)))
|
||||
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"))
|
||||
return tea.NewView(strings.Join(parts, "\n"))
|
||||
|
||||
+18
-6
@@ -3,14 +3,21 @@ package ui
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"charm.land/bubbles/v2/list"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
zone "github.com/lrstanley/bubblezone/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
"github.com/anotherhadi/usbguard-tui/internal/guard"
|
||||
)
|
||||
|
||||
func deviceZoneID(dev guard.Device) string {
|
||||
return "device-" + strconv.Itoa(dev.ID)
|
||||
}
|
||||
|
||||
var ruleStateIndicators = map[guard.RuleState]string{
|
||||
guard.RulePermanent: "● perm",
|
||||
guard.RuleTemporary: "○ tmp",
|
||||
@@ -65,10 +72,9 @@ func (d deviceDelegate) Render(w io.Writer, m list.Model, index int, item list.I
|
||||
|
||||
permIndicator := ruleStateIndicators[dev.RuleState]
|
||||
width := m.Width()
|
||||
fmt.Fprintf(w, "%s\n%s",
|
||||
clampToWidth(nameStyle.Render(name), width),
|
||||
clampToWidth(descStyle.Render(fmt.Sprintf("id:%-3d %s %s %s", dev.ID, dev.VidPid, string(dev.Status), permIndicator)), width),
|
||||
)
|
||||
line1 := clampToWidth(nameStyle.Render(name), width)
|
||||
line2 := clampToWidth(descStyle.Render(fmt.Sprintf("id:%-3d %s %s %s", dev.ID, dev.VidPid, string(dev.Status), permIndicator)), width)
|
||||
fmt.Fprint(w, zone.Mark(deviceZoneID(dev), line1+"\n"+line2))
|
||||
}
|
||||
|
||||
type actionItem struct {
|
||||
@@ -89,18 +95,24 @@ 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 actionZoneID(index int) string {
|
||||
return "action-" + strconv.Itoa(index)
|
||||
}
|
||||
|
||||
func (d actionDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
|
||||
a, ok := item.(actionItem)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var content string
|
||||
if index == m.Index() {
|
||||
clr, ok := statusColorsSelected[a.status]
|
||||
if !ok {
|
||||
clr = style.S.Primary
|
||||
}
|
||||
fmt.Fprintf(w, " %s", lipgloss.NewStyle().Bold(true).Foreground(clr).Render("> "+a.label))
|
||||
content = fmt.Sprintf(" %s", lipgloss.NewStyle().Bold(true).Foreground(clr).Render("> "+a.label))
|
||||
} else {
|
||||
fmt.Fprintf(w, " %s", a.label)
|
||||
content = fmt.Sprintf(" %s", a.label)
|
||||
}
|
||||
fmt.Fprint(w, zone.Mark(actionZoneID(index), content))
|
||||
}
|
||||
|
||||
+55
-16
@@ -11,6 +11,7 @@ import (
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
zone "github.com/lrstanley/bubblezone/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/app"
|
||||
"github.com/anotherhadi/ilovetui/bubbles"
|
||||
@@ -41,6 +42,7 @@ type deviceSummary struct {
|
||||
|
||||
type Model struct {
|
||||
list list.Model
|
||||
devices []guard.Device
|
||||
help helpbar.Model
|
||||
daemonStatus string
|
||||
defaultPolicy guard.Status
|
||||
@@ -50,7 +52,6 @@ type Model struct {
|
||||
rulesManaged bool
|
||||
rulesWritable bool
|
||||
pendingRules []pendingRule
|
||||
fatalShown bool
|
||||
|
||||
ModalOpen bool
|
||||
}
|
||||
@@ -65,6 +66,10 @@ func New() Model {
|
||||
l.DisableQuitKeybindings()
|
||||
l.KeyMap.CursorUp = key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up"))
|
||||
l.KeyMap.CursorDown = key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down"))
|
||||
l.Styles.TitleBar = l.Styles.TitleBar.PaddingLeft(1)
|
||||
l.Styles.Filter.Focused.Prompt = l.Styles.Filter.Focused.Prompt.Foreground(style.S.Muted)
|
||||
l.Styles.Filter.Blurred.Prompt = l.Styles.Filter.Blurred.Prompt.Foreground(style.S.Muted)
|
||||
l.FilterInput.SetStyles(l.Styles.Filter)
|
||||
|
||||
h := helpbar.New(
|
||||
helpbar.WithToggle(listKeys.Help),
|
||||
@@ -132,10 +137,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
return m, tea.Batch(fetchDevices, fetchDaemonStatus, fetchDefaultPolicy, tickCmd())
|
||||
|
||||
case devicesMsg:
|
||||
items := make([]list.Item, len(msg))
|
||||
summary := deviceSummary{total: len(msg)}
|
||||
for i, d := range msg {
|
||||
items[i] = d
|
||||
for _, d := range msg {
|
||||
switch d.Status {
|
||||
case guard.Allowed:
|
||||
summary.allowed++
|
||||
@@ -146,6 +149,14 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
m.deviceCounts = summary
|
||||
if slices.Equal(m.devices, msg) {
|
||||
return m, nil
|
||||
}
|
||||
m.devices = msg
|
||||
items := make([]list.Item, len(msg))
|
||||
for i, d := range msg {
|
||||
items[i] = d
|
||||
}
|
||||
cmd := m.list.SetItems(items)
|
||||
m.resizeList()
|
||||
return m, cmd
|
||||
@@ -172,16 +183,6 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
|
||||
case actionMsg:
|
||||
if msg.err != nil {
|
||||
|
||||
if msg.err == guard.ErrPermission {
|
||||
|
||||
if m.fatalShown {
|
||||
return m, nil
|
||||
}
|
||||
m.fatalShown = true
|
||||
return m, modal.Show("Permission Error", newPermissionModal(),
|
||||
modal.WithModalStyle(permissionModalStyle()))
|
||||
}
|
||||
return m, errorToast(msg.err)
|
||||
}
|
||||
return m, fetchDevices
|
||||
@@ -203,6 +204,24 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
m.list.CursorDown()
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case tea.MouseClickMsg:
|
||||
if m.ModalOpen || msg.Mouse().Button != tea.MouseLeft {
|
||||
return m, nil
|
||||
}
|
||||
for i, item := range m.list.VisibleItems() {
|
||||
dev := item.(guard.Device)
|
||||
if zone.Get(deviceZoneID(dev)).InBounds(msg) {
|
||||
alreadyFocused := i == m.list.Index()
|
||||
m.list.Select(i)
|
||||
if alreadyFocused {
|
||||
return m, modal.Show(dev.Name, newActionModal(dev, m.rulesManaged),
|
||||
modal.WithModalStyle(actionModalStyle(dev.Status)))
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if m.ModalOpen {
|
||||
@@ -256,8 +275,12 @@ func (m Model) updateList(msg tea.KeyPressMsg) (Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
}
|
||||
prevFilterState := m.list.FilterState()
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
if m.list.FilterState() != prevFilterState {
|
||||
m.resizeList()
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
@@ -265,7 +288,19 @@ func (m Model) View() string {
|
||||
header := m.renderHeader()
|
||||
listView := strings.TrimRight(m.list.View(), "\n")
|
||||
helpView := strings.TrimRight(m.help.View(m.helpBindings()...), "\n")
|
||||
return strings.Join([]string{header, listView, helpView}, "\n")
|
||||
lines := []string{header}
|
||||
if filterLine := m.renderFilterLine(); filterLine != "" {
|
||||
lines = append(lines, filterLine)
|
||||
}
|
||||
lines = append(lines, listView, helpView)
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (m Model) renderFilterLine() string {
|
||||
if m.list.FilterState() != list.FilterApplied {
|
||||
return ""
|
||||
}
|
||||
return filterLabelStyle.Render("Filter: ") + m.list.FilterValue()
|
||||
}
|
||||
|
||||
func (m Model) helpBindings() []key.Binding {
|
||||
@@ -378,7 +413,11 @@ func (m Model) renderPendingRulesLine() string {
|
||||
func (m Model) listHeight() int {
|
||||
headerH := lipgloss.Height(m.renderHeader())
|
||||
helpH := m.help.Height(m.helpBindings()...)
|
||||
return m.height - headerH - helpH
|
||||
h := m.height - headerH - helpH
|
||||
if m.list.FilterState() == list.FilterApplied {
|
||||
h--
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (m *Model) resizeList() {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/anotherhadi/ilovetui/app"
|
||||
"github.com/anotherhadi/ilovetui/modal"
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
type permissionModal struct{}
|
||||
|
||||
func newPermissionModal() tea.Model { return permissionModal{} }
|
||||
|
||||
func permissionModalStyle() modal.Styles {
|
||||
s := modal.DefaultStyles()
|
||||
s.Title = s.Title.Foreground(style.S.Error)
|
||||
s.Border = s.Border.BorderForeground(style.S.Error)
|
||||
return s
|
||||
}
|
||||
|
||||
func (permissionModal) Init() tea.Cmd { return nil }
|
||||
|
||||
func (m permissionModal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg.(type) {
|
||||
case tea.KeyPressMsg, tea.MouseClickMsg:
|
||||
return m, app.Quit()
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (permissionModal) View() tea.View {
|
||||
textStyle := lipgloss.NewStyle().Foreground(style.S.Text)
|
||||
hintStyle := lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||
|
||||
parts := []string{
|
||||
textStyle.Render("Can't reach the usbguard daemon (permission denied)."),
|
||||
textStyle.Render("Join the usbguard group, or use sudo."),
|
||||
hintStyle.Render("press any key to quit"),
|
||||
}
|
||||
return tea.NewView(strings.Join(parts, "\n"))
|
||||
}
|
||||
@@ -36,5 +36,9 @@ var (
|
||||
PaddingLeft(1).
|
||||
Width(16)
|
||||
|
||||
filterLabelStyle = lipgloss.NewStyle().
|
||||
Foreground(style.S.Muted).
|
||||
PaddingLeft(1)
|
||||
|
||||
warnStyle = lipgloss.NewStyle().Foreground(style.S.Warning)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user