mirror of
https://github.com/lukaszraczylo/lolcathost.git
synced 2026-07-10 04:43:11 +00:00
Add automatic wrapping of the content based on terminal width
This commit is contained in:
+71
-16
@@ -920,8 +920,15 @@ func (m *Model) View() string {
|
|||||||
currentContent := sb.String()
|
currentContent := sb.String()
|
||||||
currentLines := strings.Count(currentContent, "\n") + 1
|
currentLines := strings.Count(currentContent, "\n") + 1
|
||||||
|
|
||||||
// Fill space to push footer to bottom (reserve 3 lines for footer)
|
// Calculate footer height dynamically (help bar lines + status bar + spacing)
|
||||||
footerHeight := 3
|
footerHeight := 2 // status bar + newline before it
|
||||||
|
var helpBarContent string
|
||||||
|
if m.mode == ViewList {
|
||||||
|
helpBarContent = m.helpBar()
|
||||||
|
helpBarLines := strings.Count(helpBarContent, "\n") + 1
|
||||||
|
footerHeight += helpBarLines + 1 // +1 for newline before help bar
|
||||||
|
}
|
||||||
|
|
||||||
remainingLines := m.height - currentLines - footerHeight
|
remainingLines := m.height - currentLines - footerHeight
|
||||||
if remainingLines > 0 {
|
if remainingLines > 0 {
|
||||||
sb.WriteString(strings.Repeat("\n", remainingLines))
|
sb.WriteString(strings.Repeat("\n", remainingLines))
|
||||||
@@ -930,7 +937,7 @@ func (m *Model) View() string {
|
|||||||
// Footer (help bar + status bar)
|
// Footer (help bar + status bar)
|
||||||
if m.mode == ViewList {
|
if m.mode == ViewList {
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(m.helpBar())
|
sb.WriteString(helpBarContent)
|
||||||
}
|
}
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(m.statusBar())
|
sb.WriteString(m.statusBar())
|
||||||
@@ -939,19 +946,67 @@ func (m *Model) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) helpBar() string {
|
func (m *Model) helpBar() string {
|
||||||
return helpBarStyle.Render(fmt.Sprintf("%s/%s: Navigate %s: Toggle %s: New %s: Edit %s: Delete %s: Presets %s: Groups %s: Backups %s: Search %s: Help %s: Quit",
|
// Define help items with their display widths (without ANSI codes)
|
||||||
helpKeyStyle.Render("↑↓"),
|
type helpItem struct {
|
||||||
helpKeyStyle.Render("jk"),
|
key string
|
||||||
helpKeyStyle.Render("Space"),
|
desc string
|
||||||
helpKeyStyle.Render("n"),
|
rawWidth int // width without ANSI escape codes
|
||||||
helpKeyStyle.Render("e"),
|
}
|
||||||
helpKeyStyle.Render("d"),
|
|
||||||
helpKeyStyle.Render("p"),
|
items := []helpItem{
|
||||||
helpKeyStyle.Render("g"),
|
{"↑↓/jk", "Navigate", 13},
|
||||||
helpKeyStyle.Render("b"),
|
{"Space", "Toggle", 13},
|
||||||
helpKeyStyle.Render("/"),
|
{"n", "New", 6},
|
||||||
helpKeyStyle.Render("?"),
|
{"e", "Edit", 7},
|
||||||
helpKeyStyle.Render("q")))
|
{"d", "Delete", 9},
|
||||||
|
{"p", "Presets", 10},
|
||||||
|
{"g", "Groups", 9},
|
||||||
|
{"b", "Backups", 10},
|
||||||
|
{"/", "Search", 9},
|
||||||
|
{"?", "Help", 7},
|
||||||
|
{"q", "Quit", 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
separator := " "
|
||||||
|
sepWidth := 2
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
var currentLine string
|
||||||
|
var currentWidth int
|
||||||
|
|
||||||
|
for i, item := range items {
|
||||||
|
rendered := helpKeyStyle.Render(item.key) + ": " + item.desc
|
||||||
|
|
||||||
|
// Check if adding this item would exceed width
|
||||||
|
newWidth := currentWidth + item.rawWidth
|
||||||
|
if currentWidth > 0 {
|
||||||
|
newWidth += sepWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.width > 0 && newWidth > m.width && currentWidth > 0 {
|
||||||
|
// Start a new line
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
currentLine = rendered
|
||||||
|
currentWidth = item.rawWidth
|
||||||
|
} else {
|
||||||
|
// Add to current line
|
||||||
|
if currentWidth > 0 {
|
||||||
|
currentLine += separator
|
||||||
|
}
|
||||||
|
currentLine += rendered
|
||||||
|
currentWidth = newWidth
|
||||||
|
if i == 0 {
|
||||||
|
currentWidth = item.rawWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last line
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpBarStyle.Render(strings.Join(lines, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) statusBar() string {
|
func (m *Model) statusBar() string {
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func (b *BackupPicker) selectView() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
leftSb.WriteString("\n")
|
leftSb.WriteString("\n")
|
||||||
leftSb.WriteString(helpDescStyle.Render("↑↓ navigate • Enter restore • Esc cancel"))
|
leftSb.WriteString(WrapHelpText("↑↓ navigate • Enter restore • Esc cancel", 40))
|
||||||
|
|
||||||
// Build right panel (preview)
|
// Build right panel (preview)
|
||||||
var rightSb strings.Builder
|
var rightSb strings.Builder
|
||||||
@@ -219,7 +219,8 @@ func (b *BackupPicker) selectView() string {
|
|||||||
// Show scroll indicator
|
// Show scroll indicator
|
||||||
if len(lines) > previewHeight {
|
if len(lines) > previewHeight {
|
||||||
rightSb.WriteString("\n")
|
rightSb.WriteString("\n")
|
||||||
rightSb.WriteString(helpDescStyle.Render(fmt.Sprintf("Lines %d-%d of %d (Shift+↑↓ scroll)", b.previewScroll+1, endLine, len(lines))))
|
scrollText := fmt.Sprintf("%d-%d of %d (↑↓ scroll)", b.previewScroll+1, endLine, len(lines))
|
||||||
|
rightSb.WriteString(helpDescStyle.Render(scrollText))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ func (f *Form) View() string {
|
|||||||
sb.WriteString("\n\n")
|
sb.WriteString("\n\n")
|
||||||
|
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(helpDescStyle.Render("Tab/↓ next • Shift+Tab/↑ prev • ←→ select group • Enter save • Esc cancel"))
|
sb.WriteString(WrapHelpText("Tab/↓ next • Shift+Tab/↑ prev • ←→ select group • Enter save • Esc cancel", f.width-6))
|
||||||
|
|
||||||
return dialogStyle.Render(sb.String())
|
return dialogStyle.Render(sb.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ func (g *GroupPicker) selectView() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString("\n\n")
|
sb.WriteString("\n\n")
|
||||||
sb.WriteString(helpDescStyle.Render("↑↓ navigate • n new • r rename • d delete • Esc back"))
|
sb.WriteString(WrapHelpText("↑↓ navigate • n new • r rename • d delete • Esc back", g.width-6))
|
||||||
|
|
||||||
return dialogStyle.Render(sb.String())
|
return dialogStyle.Render(sb.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ func (p *PresetPicker) selectView() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(helpDescStyle.Render("↑↓ navigate • Enter apply • n new • e edit • d delete • Esc cancel"))
|
sb.WriteString(WrapHelpText("↑↓ navigate • Enter apply • n new • e edit • d delete • Esc cancel", p.width-6))
|
||||||
|
|
||||||
return dialogStyle.Render(sb.String())
|
return dialogStyle.Render(sb.String())
|
||||||
}
|
}
|
||||||
@@ -483,7 +483,7 @@ func (p *PresetPicker) formView() string {
|
|||||||
}
|
}
|
||||||
sb.WriteString("\n\n")
|
sb.WriteString("\n\n")
|
||||||
|
|
||||||
sb.WriteString(helpDescStyle.Render("Tab/↓ next • Enter select/save • Esc cancel"))
|
sb.WriteString(WrapHelpText("Tab/↓ next • Enter select/save • Esc cancel", p.width-6))
|
||||||
|
|
||||||
return dialogStyle.Render(sb.String())
|
return dialogStyle.Render(sb.String())
|
||||||
}
|
}
|
||||||
@@ -536,7 +536,7 @@ func (p *PresetPicker) pickerView() string {
|
|||||||
|
|
||||||
sb.WriteString(titleStyle.Render(title))
|
sb.WriteString(titleStyle.Render(title))
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(helpDescStyle.Render("Space to toggle • Enter to confirm • Esc to cancel"))
|
sb.WriteString(WrapHelpText("Space to toggle • Enter to confirm • Esc to cancel", p.width-6))
|
||||||
sb.WriteString("\n\n")
|
sb.WriteString("\n\n")
|
||||||
|
|
||||||
filtered := p.getFilteredAliases()
|
filtered := p.getFilteredAliases()
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -148,3 +150,71 @@ func StatusText(enabled bool, pending bool, hasError bool) string {
|
|||||||
func HelpItem(key, desc string) string {
|
func HelpItem(key, desc string) string {
|
||||||
return helpKeyStyle.Render(key) + " " + helpDescStyle.Render(desc)
|
return helpKeyStyle.Render(key) + " " + helpDescStyle.Render(desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WrapHelpText wraps help text to fit within maxWidth, splitting on bullet separators.
|
||||||
|
// If maxWidth is 0 or negative, returns the original text.
|
||||||
|
func WrapHelpText(text string, maxWidth int) string {
|
||||||
|
if maxWidth <= 0 {
|
||||||
|
return helpDescStyle.Render(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
separator := " • "
|
||||||
|
parts := splitOnSeparator(text, separator)
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
var currentLine string
|
||||||
|
var currentWidth int
|
||||||
|
|
||||||
|
for i, part := range parts {
|
||||||
|
partWidth := len(part)
|
||||||
|
sepWidth := 3 // len(" • ")
|
||||||
|
|
||||||
|
newWidth := currentWidth + partWidth
|
||||||
|
if currentWidth > 0 {
|
||||||
|
newWidth += sepWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if newWidth > maxWidth && currentWidth > 0 {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
currentLine = part
|
||||||
|
currentWidth = partWidth
|
||||||
|
} else {
|
||||||
|
if currentWidth > 0 {
|
||||||
|
currentLine += separator
|
||||||
|
}
|
||||||
|
currentLine += part
|
||||||
|
if i == 0 {
|
||||||
|
currentWidth = partWidth
|
||||||
|
} else {
|
||||||
|
currentWidth = newWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply style to each line and join
|
||||||
|
var result []string
|
||||||
|
for _, line := range lines {
|
||||||
|
result = append(result, helpDescStyle.Render(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(result, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitOnSeparator splits a string on the given separator.
|
||||||
|
func splitOnSeparator(s, sep string) []string {
|
||||||
|
var parts []string
|
||||||
|
for {
|
||||||
|
idx := strings.Index(s, sep)
|
||||||
|
if idx == -1 {
|
||||||
|
parts = append(parts, s)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parts = append(parts, s[:idx])
|
||||||
|
s = s[idx+len(sep):]
|
||||||
|
}
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user