Commit af6e956
Changed files (6)
internal/sections/policy/view.go
@@ -1,31 +1,17 @@
package policy
import (
- "strings"
"ufWall/internal/ufw"
"ufWall/internal/ui"
"github.com/charmbracelet/lipgloss"
)
-func (m Model) getPolicyStyle(policy string) lipgloss.Style {
- switch strings.ToUpper(policy) {
- case "ALLOW":
- return m.styles.AllowPolicy
- case "DENY":
- return m.styles.DenyPolicy
- case "REJECT":
- return m.styles.RejectPolicy
- default:
- return m.styles.Value
- }
-}
-
func (m Model) View(policy ufw.Policy) string {
- incomingStyle := m.getPolicyStyle(policy.DefaultIncoming)
- outgoingStyle := m.getPolicyStyle(policy.DefaultOutgoing)
- routedStyle := m.getPolicyStyle(policy.DefaultRouted)
+ incomingStyle := ui.GetPolicyStyle(m.styles, policy.DefaultIncoming)
+ outgoingStyle := ui.GetPolicyStyle(m.styles, policy.DefaultOutgoing)
+ routedStyle := ui.GetPolicyStyle(m.styles, policy.DefaultRouted)
incomingLine := lipgloss.JoinHorizontal(
lipgloss.Left,
internal/sections/rules/view.go
@@ -2,6 +2,7 @@ package rules
import (
"fmt"
+ "regexp"
"strings"
"ufWall/internal/ufw"
"ufWall/internal/ui"
@@ -20,20 +21,21 @@ func (m Model) View(rules []ufw.Rule) string {
line := strings.Repeat("─", lipgloss.Width(headerContent))
rows = append(rows, " "+headerContent, " "+m.styles.Label.UnsetWidth().Render(line))
- sectionActiveNoMenu := m.menu == nil && m.active
+ sectionActiveNoMenu := m.menu == nil && m.active && !m.showDetails && !m.showDeleteConfirm
for i, r := range rules {
+ action := fmt.Sprintf("%-6s", r.Action)
row := fmt.Sprintf(
- "%-3d │ %-6s │ %-5s │ %-16s │ %-5s │ %-16s │ %-5s",
+ "%-3d │ %6s │ %-5s │ %-16s │ %-5s │ %-16s │ %-5s",
r.Num,
- r.Action,
+ action,
r.ToProtocol,
r.FromSource,
r.FromPort,
r.ToDest,
r.ToPort,
)
- rows = append(rows, ui.InsertCursor(row, m.cursorLine == i && sectionActiveNoMenu, m.styles))
+ rows = append(rows, ui.InsertCursorRulesSection(row, m.cursorLine == i && sectionActiveNoMenu, m.styles, r.Action))
}
table := strings.Join(rows, "\n")
content := lipgloss.JoinVertical(
@@ -42,3 +44,95 @@ func (m Model) View(rules []ufw.Rule) string {
)
return ui.TitledBox("Active Rules", content, m.styles, -1, m.active)
}
+
+// DetailView renders the rule detail overlay
+func (m Model) DetailView() string {
+ if m.detailRule == nil {
+ return ""
+ }
+
+ r := m.detailRule
+
+ // Build detail content
+ var lines []string
+ lines = append(lines, "")
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Rule #:"), m.styles.Value.Render(fmt.Sprintf("%d", r.Num))))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Action:"), ui.GetPolicyStyle(m.styles, r.Action).Render(r.Action)))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Protocol:"), m.styles.Value.Render(r.ToProtocol)))
+ lines = append(lines, "")
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Source:"), m.styles.Value.Render(r.FromSource)))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Source Port:"), m.styles.Value.Render(r.FromPort)))
+ lines = append(lines, "")
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Destination:"), m.styles.Value.Render(r.ToDest)))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Dest Port:"), m.styles.Value.Render(r.ToPort)))
+ lines = append(lines, "")
+
+ if r.Comment != "" {
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Comment:"), m.styles.Value.Render(r.Comment)))
+ lines = append(lines, "")
+ }
+
+ re := regexp.MustCompile(`\s+`)
+ rawText := re.ReplaceAllString(r.Raw, " ")
+ lines = append(lines, fmt.Sprintf(" %s", m.styles.Label.Render("Raw:")))
+ lines = append(lines, fmt.Sprintf(" %s", m.styles.Value.Render(rawText)))
+ lines = append(lines, "")
+ text := m.styles.Label.
+ PaddingTop(1).
+ UnsetWidth().
+ Render("[Press any key to close]")
+
+ centered := lipgloss.Place(
+ 50,
+ 1,
+ lipgloss.Center,
+ lipgloss.Center,
+ text,
+ )
+
+ lines = append(lines, centered)
+
+ lines = append(lines, "")
+
+ content := strings.Join(lines, "\n")
+
+ title := fmt.Sprintf("Rule #%d Details", r.Num)
+ return ui.TitledBox(title, content, m.styles, -1, true)
+}
+
+func (m Model) DeleteConfirmView() string {
+ if m.deleteRule == nil {
+ return ""
+ }
+
+ r := m.deleteRule
+
+ var lines []string
+ lines = append(lines, "")
+ lines = append(lines, m.styles.Error.Render(" Are you sure you want to delete this rule?"))
+ lines = append(lines, "")
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("Rule #:"), m.styles.Value.Render(fmt.Sprintf("%d", r.Num))))
+ lines = append(lines, fmt.Sprintf(" %s %s %s", m.styles.Label.Render("Action:"), ui.GetPolicyStyle(m.styles, r.Action).Render(r.Action), m.styles.Value.Render(r.ToPort)))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("From:"), m.styles.Value.Render(r.FromSource)))
+ lines = append(lines, fmt.Sprintf(" %s %s", m.styles.Label.Render("To:"), m.styles.Value.Render(r.ToDest)))
+ lines = append(lines, "")
+ text := m.styles.Label.
+ PaddingTop(1).
+ UnsetWidth().
+ Render("[y] Yes, delete [n/Esc] Cancel")
+
+ centered := lipgloss.Place(
+ 50,
+ 1,
+ lipgloss.Center,
+ lipgloss.Center,
+ text,
+ )
+
+ lines = append(lines, centered)
+ lines = append(lines, "")
+
+ content := strings.Join(lines, "\n")
+
+ return ui.TitledBox("Confirm Delete", content, m.styles, -1, true)
+}
internal/ufw/rules.go
@@ -100,7 +100,7 @@ func parseRuleLine(num int, line string) Rule {
func findActionIndex(parts []string) int {
for i, part := range parts {
upper := strings.ToUpper(strings.Fields(part)[0])
- if upper == "ALLOW" || upper == "DENY" || upper == "REJECT" || upper == "LIMIT" {
+ if upper == ActionAllow || upper == ActionDeny || upper == ActionReject || upper == ActionLimit {
return i
}
}
internal/ui/cursor.go
@@ -1,12 +1,34 @@
package ui
+import (
+ "fmt"
+ "strings"
+)
+
func InsertCursor(line string, selected bool, styles Styles) string {
prefix := " "
style := styles.Value
if selected {
prefix = "▶ "
- style = styles.ActiveStatus.Bold(true)
+ style = styles.ActiveCursorPointer
}
line = prefix + style.Render(line)
return line
}
+
+func InsertCursorRulesSection(line string, selected bool, styles Styles, action string) string {
+ prefix := " "
+
+ if selected {
+ return "▶ " + styles.ActiveCursorPointer.Render(line)
+ }
+
+ policyStyle := GetPolicyStyle(styles, action)
+
+ actionFormatted := fmt.Sprintf("%-6s", action)
+ styledAction := policyStyle.Render(actionFormatted)
+
+ line = strings.Replace(line, actionFormatted, styledAction, 1)
+
+ return prefix + styles.Value.Render(line)
+}
internal/ui/styles.go
@@ -1,6 +1,8 @@
package ui
import (
+ "strings"
+
"github.com/charmbracelet/lipgloss"
)
@@ -16,6 +18,8 @@ type Styles struct {
SectionTitle lipgloss.Style
SectionBorderActive lipgloss.Style
+ ActiveCursorPointer lipgloss.Style
+
Menu lipgloss.Style
AllowPolicy lipgloss.Style
@@ -33,16 +37,16 @@ type Styles struct {
func NewStyles() Styles {
var (
- mauve = lipgloss.Color("#cba6f7")
- red = lipgloss.Color("#f38ba8")
- maroon = lipgloss.Color("#eba0ac")
- peach = lipgloss.Color("#fab387")
- yellow = lipgloss.Color("#f9e2af")
- green = lipgloss.Color("#a6e3a1")
- sky = lipgloss.Color("#89dceb")
+ mauve = lipgloss.Color("#cba6f7")
+ red = lipgloss.Color("#f38ba8")
+ maroon = lipgloss.Color("#eba0ac")
+ peach = lipgloss.Color("#fab387")
+ yellow = lipgloss.Color("#f9e2af")
+ green = lipgloss.Color("#a6e3a1")
+ sky = lipgloss.Color("#89dceb")
lavender = lipgloss.Color("#b4befe")
- text = lipgloss.Color("#cdd6f4")
+ text = lipgloss.Color("#cdd6f4")
subtext0 = lipgloss.Color("#a6adc8")
overlay2 = lipgloss.Color("#9399b2")
overlay1 = lipgloss.Color("#7f849c")
@@ -68,6 +72,10 @@ func NewStyles() Styles {
Bold(true).
Foreground(red),
+ ActiveCursorPointer: lipgloss.NewStyle().
+ Foreground(yellow).
+ Bold(true),
+
StatusLabel: lipgloss.NewStyle().
Foreground(text).
Bold(true),
@@ -127,3 +135,16 @@ func NewStyles() Styles {
Padding(1, 2),
}
}
+
+func GetPolicyStyle(styles Styles, policy string) lipgloss.Style {
+ switch strings.ToUpper(policy) {
+ case "ALLOW":
+ return styles.AllowPolicy
+ case "DENY":
+ return styles.DenyPolicy
+ case "REJECT":
+ return styles.RejectPolicy
+ default:
+ return styles.Value
+ }
+}