142 lines
3.3 KiB
Go
142 lines
3.3 KiB
Go
package screens
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/calmcacil/wg-admin/internal/wireguard"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/mdp/qrterminal/v3"
|
|
)
|
|
|
|
// QRScreen displays a QR code for a WireGuard client configuration
|
|
type QRScreen struct {
|
|
clientName string
|
|
configContent string
|
|
qrCode string
|
|
inlineMode bool
|
|
width, height int
|
|
errorMsg string
|
|
}
|
|
|
|
// NewQRScreen creates a new QR screen for displaying client config QR codes
|
|
func NewQRScreen(clientName string) *QRScreen {
|
|
return &QRScreen{
|
|
clientName: clientName,
|
|
inlineMode: true, // Start in inline mode
|
|
}
|
|
}
|
|
|
|
// Init initializes the QR screen
|
|
func (s *QRScreen) Init() tea.Cmd {
|
|
return s.loadConfig
|
|
}
|
|
|
|
// Update handles messages for the QR screen
|
|
func (s *QRScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "q", "escape":
|
|
// Return to list screen (parent should handle this)
|
|
return nil, nil
|
|
case "f":
|
|
// Toggle between inline and fullscreen mode
|
|
s.inlineMode = !s.inlineMode
|
|
s.generateQRCode()
|
|
}
|
|
case tea.WindowSizeMsg:
|
|
// Handle terminal resize
|
|
s.width = msg.Width
|
|
s.height = msg.Height
|
|
s.generateQRCode()
|
|
case configLoadedMsg:
|
|
s.configContent = msg.content
|
|
s.generateQRCode()
|
|
case ErrMsg:
|
|
s.errorMsg = msg.Err.Error()
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// View renders the QR screen
|
|
func (s *QRScreen) View() string {
|
|
if s.errorMsg != "" {
|
|
return s.renderError()
|
|
}
|
|
if s.qrCode == "" {
|
|
return "Loading QR code..."
|
|
}
|
|
return s.renderQR()
|
|
}
|
|
|
|
// loadConfig loads the client configuration
|
|
func (s *QRScreen) loadConfig() tea.Msg {
|
|
content, err := wireguard.GetClientConfigContent(s.clientName)
|
|
if err != nil {
|
|
return ErrMsg{Err: err}
|
|
}
|
|
return configLoadedMsg{content: content}
|
|
}
|
|
|
|
// generateQRCode generates the QR code based on current mode and terminal size
|
|
func (s *QRScreen) generateQRCode() {
|
|
if s.configContent == "" {
|
|
return
|
|
}
|
|
|
|
// Generate QR code and capture output
|
|
var builder strings.Builder
|
|
|
|
// Generate ANSI QR code using half-block characters
|
|
qrterminal.GenerateHalfBlock(s.configContent, qrterminal.L, &builder)
|
|
|
|
s.qrCode = builder.String()
|
|
}
|
|
|
|
// renderQR renders the QR code with styling
|
|
func (s *QRScreen) renderQR() string {
|
|
styleTitle := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("62")).
|
|
Bold(true).
|
|
MarginBottom(1)
|
|
|
|
styleHelp := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("241")).
|
|
MarginTop(1)
|
|
|
|
styleQR := lipgloss.NewStyle().
|
|
MarginLeft(2)
|
|
|
|
title := styleTitle.Render(fmt.Sprintf("QR Code: %s", s.clientName))
|
|
help := "Press [f] to toggle fullscreen • Press [q/Escape] to return"
|
|
|
|
return title + "\n\n" + styleQR.Render(s.qrCode) + "\n" + styleHelp.Render(help)
|
|
}
|
|
|
|
// renderError renders an error message
|
|
func (s *QRScreen) renderError() string {
|
|
styleError := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("196")).
|
|
Bold(true)
|
|
|
|
styleHelp := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("241")).
|
|
MarginTop(1)
|
|
|
|
title := styleError.Render("Error")
|
|
message := s.errorMsg
|
|
help := "Press [q/Escape] to return"
|
|
|
|
return title + "\n\n" + message + "\n" + styleHelp.Render(help)
|
|
}
|
|
|
|
// Messages
|
|
|
|
// configLoadedMsg is sent when the client configuration is loaded
|
|
type configLoadedMsg struct {
|
|
content string
|
|
}
|