helpers.go (3162B)
1 package tui 2 3 import ( 4 "strings" 5 6 "git.prime8.dev/moncheeta/prime8dev/internal/config" 7 "git.prime8.dev/moncheeta/prime8dev/internal/utils" 8 "github.com/charmbracelet/lipgloss" 9 ) 10 11 func wrapIndex(v int, size int) int { 12 if size == 0 { 13 return 0 14 } 15 for v < 0 { 16 v += size 17 } 18 return v % size 19 } 20 21 func fitRunes(s string, width int) string { 22 if width <= 0 { 23 return "" 24 } 25 26 r := []rune(s) 27 if len(r) <= width { 28 return s 29 } 30 31 if width == 1 { 32 return "…" 33 } 34 35 return string(r[:width-1]) + "…" 36 } 37 38 func padRunesRight(s string, width int) string { 39 r := []rune(s) 40 if len(r) >= width { 41 return s 42 } 43 return s + strings.Repeat(" ", width-len(r)) 44 } 45 46 func clipLines(s string, width int, maxLines int) string { 47 if maxLines <= 0 || width <= 0 { 48 return "" 49 } 50 return lipgloss.NewStyle().MaxWidth(width).MaxHeight(maxLines).Render(s) 51 } 52 53 func renderPanelWithStyle(style lipgloss.Style, width int, totalHeight int, content string) string { 54 borderX := style.GetHorizontalBorderSize() 55 borderY := style.GetVerticalBorderSize() 56 paddingX := style.GetHorizontalPadding() 57 paddingY := style.GetVerticalPadding() 58 59 innerWidth := max(width-borderX-paddingX, 1) 60 innerHeight := max(totalHeight-borderY-paddingY, 1) 61 clipped := clipLines(content, innerWidth, innerHeight) 62 return style. 63 Width(max(width-borderX, 1)). 64 Height(max(totalHeight-borderY, 1)). 65 Render(clipped) 66 } 67 68 func panelStyleSharedLeft(style lipgloss.Style) lipgloss.Style { 69 b := lipgloss.NormalBorder() 70 b.TopLeft = "┬" 71 b.BottomLeft = "┴" 72 return style.BorderStyle(b) 73 } 74 75 func panelStyleSharedTop(style lipgloss.Style) lipgloss.Style { 76 b := lipgloss.NormalBorder() 77 b.TopLeft = "├" 78 b.TopRight = "┤" 79 return style.BorderStyle(b) 80 } 81 82 // panelStyleSharedRight draws a full border whose right edge is the divider 83 // shared with a pane to its right (which then omits its left border). 84 func panelStyleSharedRight(style lipgloss.Style) lipgloss.Style { 85 b := lipgloss.NormalBorder() 86 b.TopRight = "┬" 87 b.BottomRight = "┴" 88 return style.BorderStyle(b) 89 } 90 91 // panelStyleSharedBottom draws a full border whose bottom edge is the divider 92 // shared with a pane below it (which then omits its top border). 93 func panelStyleSharedBottom(style lipgloss.Style) lipgloss.Style { 94 b := lipgloss.NormalBorder() 95 b.BottomLeft = "├" 96 b.BottomRight = "┤" 97 return style.BorderStyle(b) 98 } 99 100 func panelStyleWithoutLeft(style lipgloss.Style) lipgloss.Style { 101 return style.Border(lipgloss.NormalBorder(), true, true, true, false) 102 } 103 104 func panelStyleWithoutTop(style lipgloss.Style) lipgloss.Style { 105 return style.Border(lipgloss.NormalBorder(), false, true, true, true) 106 } 107 108 func panelStyleWithoutRight(style lipgloss.Style) lipgloss.Style { 109 return style.Border(lipgloss.NormalBorder(), true, false, true, true) 110 } 111 112 func panelStyleWithoutBottom(style lipgloss.Style) lipgloss.Style { 113 return style.Border(lipgloss.NormalBorder(), true, true, false, true) 114 } 115 116 // dateRangeLabel is "oct 2024 – feb 2026 · 1 yr 5 mo". 117 func dateRangeLabel(exp config.Experience) string { 118 return utils.FormatRange(exp.StartDate, exp.EndDate, exp.Current) + " · " + 119 utils.FormatDurationShort(exp.StartDate, exp.EndDate, exp.Current) 120 }