Skip to content

Commit

Permalink
Use string builders
Browse files Browse the repository at this point in the history
  • Loading branch information
kognise committed Jul 31, 2024
1 parent b01c387 commit b4884cb
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 58 deletions.
9 changes: 5 additions & 4 deletions ui/animation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ui

import (
"strings"
"time"

"github.com/charmbracelet/lipgloss"
Expand Down Expand Up @@ -30,13 +31,13 @@ const PoggersAnimationInterval = 80 * time.Millisecond

// Render a frame of the cool loading animation I designed.
func PoggersAnimationFrame(t int) string {
frame := ""
var frame strings.Builder

targetLetter := targetLetters[(t/animWidth)%len(targetLetters)]

for y := 0; y < animHeight; y++ {
if y > 0 {
frame += "\n"
frame.WriteByte('\n')
}

// Line equation determined by bruteforce.
Expand Down Expand Up @@ -65,9 +66,9 @@ func PoggersAnimationFrame(t int) string {
}
}

frame += style.Render(string(char))
frame.WriteString(style.Render(string(char)))
}
}

return frame
return frame.String()
}
14 changes: 8 additions & 6 deletions ui/appmenu.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ui

import (
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
Expand Down Expand Up @@ -65,19 +67,19 @@ func (appmenu *Appmenu) Render() string {
return ""
}

s := ""
var s strings.Builder

for i, item := range appmenu.items {
if i > 0 {
s += "\n"
s.WriteByte('\n')
}
s += item.render(i == appmenu.cursor, appmenu.isOpen)
s.WriteString(item.render(i == appmenu.cursor, appmenu.isOpen))
}

// Render the submenu to the right of the appmenu.
s = lipgloss.JoinHorizontal(lipgloss.Top, s, appmenu.items[appmenu.cursor].Submenu.Render(appmenu.isOpen))

return s
return lipgloss.JoinHorizontal(lipgloss.Top,
s.String(),
appmenu.items[appmenu.cursor].Submenu.Render(appmenu.isOpen))
}

// Move the cursor to the next selectable item in the currently active menu.
Expand Down
9 changes: 5 additions & 4 deletions ui/submenu.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ui

import (
"slices"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand Down Expand Up @@ -378,14 +379,14 @@ type Submenu struct {

// Render the submenu to a string.
func (submenu *Submenu) Render(isSubmenuOpen bool) string {
s := ""
var s strings.Builder
for i, item := range submenu.items {
if i > 0 {
s += "\n"
s.WriteByte('\n')
}
s += item.render(i == submenu.cursor && item.isSelectable(), isSubmenuOpen)
s.WriteString(item.render(i == submenu.cursor && item.isSelectable(), isSubmenuOpen))
}
return s
return s.String()
}

// Move the cursor to the next selectable item.
Expand Down
97 changes: 53 additions & 44 deletions view.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,45 +88,56 @@ func renderHeader(m *model) string {
MarginRight(4).
Render(ui.Logo)

status := "Status: "
status += renderStatusButton(m.state.BackendState, m.state.CurrentExitNode != nil)
if m.state.BackendState == ipn.Running.String() {
status += lipgloss.NewStyle().
Faint(true).
PaddingLeft(1).
Render("(press . to disconnect)")
}
status += "\n"
var statusStr string
{
var status strings.Builder
status.WriteString("Status: ")
status.WriteString(renderStatusButton(m.state.BackendState, m.state.CurrentExitNode != nil))
if m.state.BackendState == ipn.Running.String() {
status.WriteString(lipgloss.NewStyle().
Faint(true).
PaddingLeft(1).
Render("(press . to disconnect)"))
}
status.WriteByte('\n')

// Extra info; either auth URL or user login name, depending on the backend state.
if m.state.User == nil || m.state.User.LoginName == "" {
status += lipgloss.NewStyle().
Faint(true).
Render("--")
} else {
status += lipgloss.NewStyle().
Faint(true).
Render(m.state.User.LoginName)
// Extra info; either auth URL or user login name, depending on the backend state.
if m.state.User == nil || m.state.User.LoginName == "" {
status.WriteString(lipgloss.NewStyle().
Faint(true).
Render("--"))
} else {
status.WriteString(lipgloss.NewStyle().
Faint(true).
Render(m.state.User.LoginName))
}

statusStr = status.String()
}

// App versions.
versions := "tsui: " + Version + "\n"
versions += "tailscale: "
if m.state.TSVersion != "" {
versions += m.state.TSVersion
} else {
versions += "(not connected)"
var versionsStr string
{
// App versions.
var versions strings.Builder
versions.WriteString("tsui: " + Version + "\n")
versions.WriteString("tailscale: ")
if m.state.TSVersion != "" {
versions.WriteString(m.state.TSVersion)
} else {
versions.WriteString("(not connected)")
}

versionsStr = lipgloss.NewStyle().
Faint(true).
Render(versions.String())
}
versions = lipgloss.NewStyle().
Faint(true).
Render(versions)

// Spacer between the left content and the right content.
spacer := lipgloss.NewStyle().
Width(m.terminalWidth - lipgloss.Width(versions) - lipgloss.Width(status) - lipgloss.Width(logo)).
Width(m.terminalWidth - lipgloss.Width(versionsStr) - lipgloss.Width(statusStr) - lipgloss.Width(logo)).
Render(" ")

return lipgloss.JoinHorizontal(lipgloss.Center, logo, status, spacer, versions)
return lipgloss.JoinHorizontal(lipgloss.Center, logo, statusStr, spacer, versionsStr)
}

// Render a banner/modal for the middle of the screen.
Expand All @@ -143,28 +154,26 @@ func renderMiddleBanner(m *model, height int, text string) string {
func renderStatusBar(m *model) string {
var text string

if m.statusText == "" && m.canWrite {
// Only show up/down if we have data.
if m.state.BackendState == ipn.Running.String() {
text = ""
} else {
text = lipgloss.NewStyle().
Faint(true).
Render(fmt.Sprintf(
"▲ %s | %s ▼",
ui.FormatBytes(m.state.TxBytes),
ui.FormatBytes(m.state.RxBytes),
))
}
if m.statusText == "" && m.canWrite && m.state.BackendState == ipn.Running.String() {
// If there's no other status, we're running, and we have write access, show up/down.
text = lipgloss.NewStyle().
Faint(true).
Render(fmt.Sprintf(
"▲ %s | %s ▼",
ui.FormatBytes(m.state.TxBytes),
ui.FormatBytes(m.state.RxBytes),
))
} else if m.statusText == "" && !m.canWrite {
// If there's no other status and we don't have write access, show a read-only warning.
text = lipgloss.NewStyle().
Bold(true).
Foreground(ui.Yellow).
Render("Read-only mode.")
text += lipgloss.NewStyle().
Foreground(ui.Yellow).
Render(" To edit preferences, you may have to run tsui as root.")
} else {
} else if m.statusText != "" {
// Otherwise, there's a status message, so render it.
var color lipgloss.Color

switch m.statusType {
Expand Down

0 comments on commit b4884cb

Please sign in to comment.