mirror of
https://github.com/anotherhadi/proton-vpn-tui.git
synced 2026-10-05 11:18:27 +02:00
150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
package settings
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"io"
|
|
"strings"
|
|
|
|
"charm.land/bubbles/v2/list"
|
|
tea "charm.land/bubbletea/v2"
|
|
"charm.land/lipgloss/v2"
|
|
"github.com/charmbracelet/x/ansi"
|
|
|
|
"github.com/anotherhadi/ilovetui/style"
|
|
|
|
"github.com/anotherhadi/proton-vpn-tui/internal/backend"
|
|
"github.com/anotherhadi/proton-vpn-tui/internal/icons"
|
|
)
|
|
|
|
func clampToWidth(s string, width int) string {
|
|
if width <= 0 {
|
|
return s
|
|
}
|
|
return ansi.Truncate(s, width, "…")
|
|
}
|
|
|
|
type delegate struct{}
|
|
|
|
func (d delegate) Height() int { return 2 }
|
|
func (d delegate) Spacing() int { return 1 }
|
|
func (d delegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
|
|
|
|
func (d delegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
|
|
it, ok := listItem.(item)
|
|
if !ok {
|
|
return
|
|
}
|
|
selected := index == m.Index()
|
|
|
|
var titleStyle, descStyle, valueStyle lipgloss.Style
|
|
if selected {
|
|
titleStyle = lipgloss.NewStyle().
|
|
Border(lipgloss.NormalBorder(), false, false, false, true).
|
|
BorderForeground(style.S.Primary).
|
|
Foreground(style.S.Primary).
|
|
Bold(true).
|
|
PaddingLeft(1)
|
|
descStyle = lipgloss.NewStyle().
|
|
Border(lipgloss.NormalBorder(), false, false, false, true).
|
|
BorderForeground(style.S.Primary).
|
|
Foreground(style.S.Muted).
|
|
PaddingLeft(1)
|
|
valueStyle = lipgloss.NewStyle().Foreground(style.S.Success).Bold(true)
|
|
} else {
|
|
titleStyle = lipgloss.NewStyle().Foreground(style.S.Text).PaddingLeft(2)
|
|
descStyle = lipgloss.NewStyle().Foreground(style.S.Muted).PaddingLeft(2)
|
|
valueStyle = lipgloss.NewStyle().Foreground(style.S.Success)
|
|
}
|
|
|
|
value := renderCycleValue(valueStyle.Render(humanizeKebab(it.setting.Value)))
|
|
if isOnOffSetting(it.setting) {
|
|
value = renderToggle(it.setting.Value == "on")
|
|
}
|
|
|
|
width := m.Width()
|
|
title := titleStyle.Render(humanizeKebab(it.key))
|
|
line1 := padLine(justifyBetween(title, value, width), width)
|
|
line2 := padLine(descStyle.Render(it.setting.Description), width)
|
|
fmt.Fprintf(w, "%s\n%s", line1, line2)
|
|
}
|
|
|
|
func justifyBetween(left, right string, width int) string {
|
|
gap := width - lipgloss.Width(left) - lipgloss.Width(right)
|
|
if gap < 1 {
|
|
gap = 1
|
|
}
|
|
return left + strings.Repeat(" ", gap) + right
|
|
}
|
|
|
|
func padLine(s string, width int) string {
|
|
s = clampToWidth(s, width)
|
|
if pad := width - lipgloss.Width(s); pad > 0 {
|
|
s += strings.Repeat(" ", pad)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func isOnOffSetting(s backend.Setting) bool {
|
|
if len(s.Options) != 2 {
|
|
return false
|
|
}
|
|
var hasOn, hasOff bool
|
|
for _, o := range s.Options {
|
|
hasOn = hasOn || o.ID == "on"
|
|
hasOff = hasOff || o.ID == "off"
|
|
}
|
|
return hasOn && hasOff
|
|
}
|
|
|
|
func renderCycleValue(value string) string {
|
|
arrow := lipgloss.NewStyle().Foreground(style.S.Muted)
|
|
return arrow.Render("< ") + value + arrow.Render(" >")
|
|
}
|
|
|
|
func renderToggle(on bool) string {
|
|
if on {
|
|
return renderBadge(" ●", style.S.Success)
|
|
}
|
|
return renderBadge("● ", style.S.Subtle)
|
|
}
|
|
|
|
func renderBadge(label string, fill color.Color) string {
|
|
body := lipgloss.NewStyle().
|
|
Background(fill).
|
|
Foreground(style.S.Background).
|
|
Bold(true).
|
|
Render(label)
|
|
|
|
if icons.I.PillLeft == "" && icons.I.PillRight == "" {
|
|
return body
|
|
}
|
|
|
|
cap := lipgloss.NewStyle().Foreground(fill).Background(style.S.Background)
|
|
return cap.Render(icons.I.PillLeft) + body + cap.Render(icons.I.PillRight)
|
|
}
|
|
|
|
var settingWordOverrides = map[string]string{
|
|
"nat": "NAT",
|
|
"dns": "DNS",
|
|
"vpn": "VPN",
|
|
"ip": "IP",
|
|
"ipv6": "IPv6",
|
|
"netshield": "NetShield",
|
|
}
|
|
|
|
func humanizeKebab(s string) string {
|
|
words := strings.Split(s, "-")
|
|
for i, w := range words {
|
|
if override, ok := settingWordOverrides[strings.ToLower(w)]; ok {
|
|
words[i] = override
|
|
continue
|
|
}
|
|
if w == "" {
|
|
continue
|
|
}
|
|
words[i] = strings.ToUpper(w[:1]) + w[1:]
|
|
}
|
|
return strings.Join(words, " ")
|
|
}
|