Skip to content

Commit 2acb78f

Browse files
committed
perf(tui): add lazy init and skip terminal detection
- Use sync.Once for lazy style initialization - Force TrueColor profile to skip slow terminal capability detection - Fixes slow cold start on Windows caused by lipgloss background color queries See: charmbracelet/lipgloss#86
1 parent 99eea4b commit 2acb78f

2 files changed

Lines changed: 118 additions & 71 deletions

File tree

src/internal/tui/styles.go

Lines changed: 115 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,156 +4,200 @@
44
package tui
55

66
import (
7+
"sync"
8+
79
"github.com/charmbracelet/lipgloss"
10+
"github.com/muesli/termenv"
811
)
912

10-
// Color palette
13+
// Lazy initialization to avoid cold start penalty from lipgloss terminal detection
1114
var (
12-
colorPrimary = lipgloss.Color("39") // Cyan
13-
colorSecondary = lipgloss.Color("213") // Magenta/Pink
14-
colorSuccess = lipgloss.Color("42") // Green
15-
colorWarning = lipgloss.Color("214") // Orange/Yellow
16-
colorError = lipgloss.Color("196") // Red
17-
colorMuted = lipgloss.Color("245") // Gray
15+
initOnce sync.Once
16+
17+
// Colors
18+
colorPrimary lipgloss.Color
19+
colorSecondary lipgloss.Color
20+
colorSuccess lipgloss.Color
21+
colorWarning lipgloss.Color
22+
colorError lipgloss.Color
23+
colorMuted lipgloss.Color
24+
25+
// Text styles
26+
StyleTitle lipgloss.Style
27+
StyleSubtitle lipgloss.Style
28+
StyleVersion lipgloss.Style
29+
StyleActiveVersion lipgloss.Style
30+
StyleRuntime lipgloss.Style
31+
StyleMuted lipgloss.Style
32+
StyleIndicator lipgloss.Style
33+
StyleWarningIndicator lipgloss.Style
34+
35+
// Box styles
36+
StyleBox lipgloss.Style
37+
StyleInfoBox lipgloss.Style
38+
StyleSuccessBox lipgloss.Style
39+
StyleErrorBox lipgloss.Style
40+
41+
// Table styles
42+
StyleTableHeader lipgloss.Style
43+
StyleTableCell lipgloss.Style
44+
StyleTableRowActive lipgloss.Style
45+
StyleTableBorder lipgloss.Style
46+
47+
// Indicator strings
48+
CheckMark string
49+
CrossMark string
50+
Bullet string
51+
Arrow string
1852
)
1953

20-
// Text styles
21-
var (
22-
// StyleTitle is for main headers and titles
23-
StyleTitle = lipgloss.NewStyle().
54+
// initStyles initializes all lipgloss styles lazily
55+
func initStyles() {
56+
initOnce.Do(func() {
57+
// Force TrueColor profile to skip slow terminal capability detection
58+
// See: https://github.com/charmbracelet/lipgloss/issues/86
59+
lipgloss.SetColorProfile(termenv.TrueColor)
60+
61+
// Color palette
62+
colorPrimary = lipgloss.Color("39") // Cyan
63+
colorSecondary = lipgloss.Color("213") // Magenta/Pink
64+
colorSuccess = lipgloss.Color("42") // Green
65+
colorWarning = lipgloss.Color("214") // Orange/Yellow
66+
colorError = lipgloss.Color("196") // Red
67+
colorMuted = lipgloss.Color("245") // Gray
68+
69+
// Text styles
70+
StyleTitle = lipgloss.NewStyle().
2471
Bold(true).
2572
Foreground(colorPrimary).
2673
MarginBottom(1)
2774

28-
// StyleSubtitle is for secondary headers
29-
StyleSubtitle = lipgloss.NewStyle().
75+
StyleSubtitle = lipgloss.NewStyle().
3076
Bold(true).
3177
Foreground(colorSecondary)
3278

33-
// StyleVersion is for version numbers
34-
StyleVersion = lipgloss.NewStyle().
79+
StyleVersion = lipgloss.NewStyle().
3580
Bold(true).
3681
Foreground(colorSecondary)
3782

38-
// StyleActiveVersion is for currently active versions
39-
StyleActiveVersion = lipgloss.NewStyle().
40-
Bold(true).
41-
Foreground(colorSuccess)
83+
StyleActiveVersion = lipgloss.NewStyle().
84+
Bold(true).
85+
Foreground(colorSuccess)
4286

43-
// StyleRuntime is for runtime names (Python, Node.js, etc.)
44-
StyleRuntime = lipgloss.NewStyle().
87+
StyleRuntime = lipgloss.NewStyle().
4588
Bold(true).
4689
Foreground(colorPrimary)
4790

48-
// StyleMuted is for dimmed/secondary text
49-
StyleMuted = lipgloss.NewStyle().
91+
StyleMuted = lipgloss.NewStyle().
5092
Foreground(colorMuted)
5193

52-
// StyleIndicator is for status indicators (checkmarks, etc.)
53-
StyleIndicator = lipgloss.NewStyle().
94+
StyleIndicator = lipgloss.NewStyle().
5495
Foreground(colorSuccess)
5596

56-
// StyleWarningIndicator is for warning indicators
57-
StyleWarningIndicator = lipgloss.NewStyle().
58-
Foreground(colorWarning)
59-
)
97+
StyleWarningIndicator = lipgloss.NewStyle().
98+
Foreground(colorWarning)
6099

61-
// Box styles
62-
var (
63-
// StyleBox is a generic rounded box
64-
StyleBox = lipgloss.NewStyle().
100+
// Box styles
101+
StyleBox = lipgloss.NewStyle().
65102
Border(lipgloss.RoundedBorder()).
66103
BorderForeground(colorMuted).
67104
Padding(0, 1)
68105

69-
// StyleInfoBox is for informational content
70-
StyleInfoBox = lipgloss.NewStyle().
106+
StyleInfoBox = lipgloss.NewStyle().
71107
Border(lipgloss.RoundedBorder()).
72108
BorderForeground(colorPrimary).
73109
Padding(0, 1)
74110

75-
// StyleSuccessBox is for success messages
76-
StyleSuccessBox = lipgloss.NewStyle().
111+
StyleSuccessBox = lipgloss.NewStyle().
77112
Border(lipgloss.RoundedBorder()).
78113
BorderForeground(colorSuccess).
79114
Padding(0, 1)
80115

81-
// StyleErrorBox is for error messages
82-
StyleErrorBox = lipgloss.NewStyle().
116+
StyleErrorBox = lipgloss.NewStyle().
83117
Border(lipgloss.RoundedBorder()).
84118
BorderForeground(colorError).
85119
Padding(0, 1)
86-
)
87120

88-
// Table styles
89-
var (
90-
// StyleTableHeader is for table column headers
91-
StyleTableHeader = lipgloss.NewStyle().
92-
Bold(true).
93-
Foreground(colorPrimary).
94-
PaddingRight(2)
95-
96-
// StyleTableCell is for regular table cells
97-
StyleTableCell = lipgloss.NewStyle().
121+
// Table styles
122+
StyleTableHeader = lipgloss.NewStyle().
123+
Bold(true).
124+
Foreground(colorPrimary).
98125
PaddingRight(2)
99126

100-
// StyleTableRowActive is for highlighted/active rows
101-
StyleTableRowActive = lipgloss.NewStyle().
102-
Foreground(colorSuccess)
103-
104-
// StyleTableBorder wraps the table in a rounded border
105-
StyleTableBorder = lipgloss.NewStyle().
106-
Border(lipgloss.RoundedBorder()).
107-
BorderForeground(colorMuted).
108-
Padding(0, 1)
109-
)
127+
StyleTableCell = lipgloss.NewStyle().
128+
PaddingRight(2)
110129

111-
// Indicator constants with styles applied
112-
var (
113-
// CheckMark is a styled checkmark indicator
114-
CheckMark = StyleIndicator.Render("✓")
130+
StyleTableRowActive = lipgloss.NewStyle().
131+
Foreground(colorSuccess)
115132

116-
// CrossMark is a styled cross/X indicator
117-
CrossMark = lipgloss.NewStyle().Foreground(colorError).Render("✗")
133+
StyleTableBorder = lipgloss.NewStyle().
134+
Border(lipgloss.RoundedBorder()).
135+
BorderForeground(colorMuted).
136+
Padding(0, 1)
118137

119-
// Bullet is a styled bullet point
120-
Bullet = StyleMuted.Render("•")
138+
// Indicator constants with styles applied
139+
CheckMark = StyleIndicator.Render("✓")
140+
CrossMark = lipgloss.NewStyle().Foreground(colorError).Render("✗")
141+
Bullet = StyleMuted.Render("•")
142+
Arrow = lipgloss.NewStyle().Foreground(colorPrimary).Render("→")
143+
})
144+
}
121145

122-
// Arrow is a styled arrow indicator
123-
Arrow = lipgloss.NewStyle().Foreground(colorPrimary).Render("→")
124-
)
146+
// Init ensures styles are initialized. Call this before using any styles.
147+
func Init() {
148+
initStyles()
149+
}
125150

126151
// RenderTitle renders a styled title
127152
func RenderTitle(text string) string {
153+
initStyles()
128154
return StyleTitle.Render(text)
129155
}
130156

131157
// RenderRuntime renders a runtime name with styling
132158
func RenderRuntime(name string) string {
159+
initStyles()
133160
return StyleRuntime.Render(name)
134161
}
135162

136163
// RenderVersion renders a version string with styling
137164
func RenderVersion(version string) string {
165+
initStyles()
138166
return StyleVersion.Render(version)
139167
}
140168

141169
// RenderActiveVersion renders an active version string with styling
142170
func RenderActiveVersion(version string) string {
171+
initStyles()
143172
return StyleActiveVersion.Render(version)
144173
}
145174

146175
// RenderMuted renders text in a muted/dim style
147176
func RenderMuted(text string) string {
177+
initStyles()
148178
return StyleMuted.Render(text)
149179
}
150180

151181
// RenderBox renders content in a rounded box
152182
func RenderBox(content string) string {
183+
initStyles()
153184
return StyleBox.Render(content)
154185
}
155186

156187
// RenderInfoBox renders content in an info-styled box
157188
func RenderInfoBox(content string) string {
189+
initStyles()
158190
return StyleInfoBox.Render(content)
159191
}
192+
193+
// GetCheckMark returns the styled checkmark indicator
194+
func GetCheckMark() string {
195+
initStyles()
196+
return CheckMark
197+
}
198+
199+
// GetCrossMark returns the styled cross indicator
200+
func GetCrossMark() string {
201+
initStyles()
202+
return CrossMark
203+
}

src/internal/tui/table.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ func (t *Table) Render() string {
8181
return ""
8282
}
8383

84+
// Ensure styles are initialized
85+
initStyles()
86+
8487
// Calculate total width for title
8588
totalWidth := 0
8689
for _, w := range t.widths {

0 commit comments

Comments
 (0)