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 }