mirror of
https://github.com/anotherhadi/proton-vpn-tui.git
synced 2026-10-05 19:28:28 +02:00
init
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
zone "github.com/lrstanley/bubblezone/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
|
||||
"github.com/anotherhadi/proton-vpn-tui/internal/icons"
|
||||
)
|
||||
|
||||
func (m Model) renderHeader() string {
|
||||
title := headerStyle.Render("ProtonVPN")
|
||||
if style.S.NerdFonts {
|
||||
title = icons.I.Vpn + title
|
||||
}
|
||||
lines := []string{
|
||||
title,
|
||||
"",
|
||||
m.renderStatusLine(),
|
||||
m.renderModesLine(),
|
||||
}
|
||||
if searchLine := m.renderSearchLine(); searchLine != "" {
|
||||
lines = append(lines, searchLine)
|
||||
}
|
||||
lines = append(lines, "", m.renderColumnHeader())
|
||||
for i, l := range lines {
|
||||
lines[i] = clampToWidth(l, m.width)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (m Model) renderColumnHeader() string {
|
||||
indent, expandIcon := "", " "
|
||||
prefixWidth := lipgloss.Width(indent) + lipgloss.Width(expandIcon) + 1
|
||||
infoW, gaps := infoColumnLayout(m.showServersColumn())
|
||||
nameWidth := max(m.width-boxOverhead-prefixWidth-infoW-loadColWidth-gaps, minNameWidth)
|
||||
|
||||
hdrStyle := lipgloss.NewStyle().Foreground(style.S.Subtle).Bold(true)
|
||||
|
||||
line := " " + indent + expandIcon + " " + hdrStyle.Render(fixedWidth("Name", nameWidth))
|
||||
if m.showServersColumn() {
|
||||
line += " " + hdrStyle.Render(fixedWidth("Servers", infoColWidth))
|
||||
}
|
||||
return line + " " + hdrStyle.Render(fixedWidth("Load", loadColWidth))
|
||||
}
|
||||
|
||||
func oneLine(s string) string {
|
||||
return strings.Join(strings.Fields(s), " ")
|
||||
}
|
||||
|
||||
func clampToWidth(s string, width int) string {
|
||||
if width <= 0 {
|
||||
return s
|
||||
}
|
||||
return ansi.Truncate(s, width, "…")
|
||||
}
|
||||
|
||||
func (m Model) renderStatusLine() string {
|
||||
label := infoLabelStyle.Render("Status")
|
||||
|
||||
if m.connecting {
|
||||
return label + primaryStyle.Render(icons.I.Connecting+" connecting…")
|
||||
}
|
||||
if m.disconnecting {
|
||||
return label + mutedStyle.Render(icons.I.Loading+" loading…")
|
||||
}
|
||||
if m.statusErr != nil {
|
||||
return label + warnStyle.Render(oneLine(m.statusErr.Error()))
|
||||
}
|
||||
if !m.statusLoaded {
|
||||
if m.quickConnected {
|
||||
return label + mutedStyle.Render(icons.I.Connected+" connected, "+icons.I.Loading+" loading…")
|
||||
}
|
||||
return label + mutedStyle.Render(icons.I.Loading+" loading…")
|
||||
}
|
||||
if !m.status.Connected {
|
||||
return label + mutedStyle.Render(icons.I.Disconnected+" disconnected")
|
||||
}
|
||||
|
||||
loc := m.status.Server
|
||||
if m.status.Location != "" {
|
||||
loc = fmt.Sprintf("%s (%s)", m.status.Server, m.status.Location)
|
||||
}
|
||||
text := fmt.Sprintf("%s connected: %s", icons.I.Connected, loc)
|
||||
if m.status.Load > 0 {
|
||||
text += fmt.Sprintf(" %d%% load", m.status.Load)
|
||||
}
|
||||
return label + connectedStyle.Render(text)
|
||||
}
|
||||
|
||||
func (m Model) renderSearchLine() string {
|
||||
if !m.searching && strings.TrimSpace(m.searchInput.Value()) == "" {
|
||||
return ""
|
||||
}
|
||||
return infoLabelStyle.Render("Search") + m.searchInput.View()
|
||||
}
|
||||
|
||||
func (m Model) renderModesLine() string {
|
||||
label := infoLabelStyle.Render("Mode")
|
||||
parts := []string{
|
||||
zone.Mark(zoneBadgeFastest, modeBadge(icons.I.Fastest+" fastest", m.prefer == "fastest")),
|
||||
zone.Mark(zoneBadgeRandom, modeBadge(icons.I.Random+" random", m.prefer == "random")),
|
||||
zone.Mark(zoneBadgeSecureCore, modeBadge(icons.I.SecureCore+" secure core", m.filters.SecureCore)),
|
||||
zone.Mark(zoneBadgeTor, modeBadge(icons.I.Tor+" tor", m.filters.Tor)),
|
||||
zone.Mark(zoneBadgeP2P, modeBadge(icons.I.P2p+" p2p", m.filters.P2P)),
|
||||
zone.Mark(zoneBadgeFree, modeBadge(icons.I.Free+" free", m.filters.Free)),
|
||||
}
|
||||
return label + strings.Join(parts, mutedStyle.Render(" "))
|
||||
}
|
||||
|
||||
func modeBadge(text string, active bool) string {
|
||||
if active {
|
||||
return primaryStyle.Bold(true).Render(text)
|
||||
}
|
||||
return mutedStyle.Render(text)
|
||||
}
|
||||
Reference in New Issue
Block a user