on main help sign in
Merge pull request #124 from charmbracelet/bump-tea
e7d6b41Kujtim Hoxha
go.mod+3−3
@@ -2,9 +2,9 @@ module github.com/charmbracelet/crush
2
2
3go 1.24.3
3go 1.24.3
4
4
5−replace github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250708152737-144080f3d891
5+replace github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
6
6
7−replace github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250708152830-0fa4ef151093
7+replace github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb
8
8
9require (
9require (
10 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
10 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
@@ -75,7 +75,7 @@ require (
75 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
75 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
76 github.com/aymerick/douceur v0.2.0 // indirect
76 github.com/aymerick/douceur v0.2.0 // indirect
77 github.com/charmbracelet/colorprofile v0.3.1 // indirect
77 github.com/charmbracelet/colorprofile v0.3.1 // indirect
78− github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9
78+ github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9 // indirect
79 github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
79 github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
80 github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef
80 github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef
81 github.com/charmbracelet/x/term v0.2.1
81 github.com/charmbracelet/x/term v0.2.1
go.sum+4−6
@@ -70,16 +70,16 @@ github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr
70github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
70github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
71github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e h1:99Ugtt633rqauFsXjZobZmtkNpeaWialfj8dl6COC6A=
71github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e h1:99Ugtt633rqauFsXjZobZmtkNpeaWialfj8dl6COC6A=
72github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
72github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
73−github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250708152737-144080f3d891 h1:wh6N1dR4XkDh6XsiZh1/tImJAZvYB0yVLmaUKvJXvK0=
73+github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595 h1:wLMjzOqrwoM7Em9UR9sGbn4375G8WuxcwFB3kjZiqHo=
74−github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250708152737-144080f3d891/go.mod h1:SwBB+WoaQVMMOM9hknbN/7FNT86kgKG0LSHGTmLphX8=
74+github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595/go.mod h1:+Tl7rePElw6OKt382t04zXwtPFoPXxAaJzNrYmtsLds=
75github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
75github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
76github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
76github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
77github.com/charmbracelet/fang v0.1.0 h1:SlZS2crf3/zQh7Mr4+W+7QR1k+L08rrPX5rm5z3d7Wg=
77github.com/charmbracelet/fang v0.1.0 h1:SlZS2crf3/zQh7Mr4+W+7QR1k+L08rrPX5rm5z3d7Wg=
78github.com/charmbracelet/fang v0.1.0/go.mod h1:Zl/zeUQ8EtQuGyiV0ZKZlZPDowKRTzu8s/367EpN/fc=
78github.com/charmbracelet/fang v0.1.0/go.mod h1:Zl/zeUQ8EtQuGyiV0ZKZlZPDowKRTzu8s/367EpN/fc=
79github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe h1:i6ce4CcAlPpTj2ER69m1DBeLZ3RRcHnKExuwhKa3GfY=
79github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe h1:i6ce4CcAlPpTj2ER69m1DBeLZ3RRcHnKExuwhKa3GfY=
80github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe/go.mod h1:p3Q+aN4eQKeM5jhrmXPMgPrlKbmc59rWSnMsSA3udhk=
80github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe/go.mod h1:p3Q+aN4eQKeM5jhrmXPMgPrlKbmc59rWSnMsSA3udhk=
81−github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250708152830-0fa4ef151093 h1:c9vOmNJQUwy/lp/pNOB5ZDMhOuXJ3Y2LL9uZMYGgJxQ=
81+github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb h1:lswj7CYZVYbLn2OhYJsXOMRQQGdRIfyuSnh5FdVSMr0=
82−github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250708152830-0fa4ef151093/go.mod h1:XmxjFJcMEfYIHa4Mw4ra+uMjploDkTlkKIs7wLt9v4Q=
82+github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb/go.mod h1:wEc/TRrTAIDJYjVCg3+y8WeKaN+F88gpYfGbUuP6W3A=
83github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
83github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
84github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
84github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
85github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9 h1:+LLFCLxtb/sHegwY3zYdFAbaOgI/I9pv/pxdUlI1Q9s=
85github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9 h1:+LLFCLxtb/sHegwY3zYdFAbaOgI/I9pv/pxdUlI1Q9s=
@@ -88,8 +88,6 @@ github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh
88github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
88github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
89github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa h1:lphz0Z3rsiOtMYiz8axkT24i9yFiueDhJbzyNUADmME=
89github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa h1:lphz0Z3rsiOtMYiz8axkT24i9yFiueDhJbzyNUADmME=
90github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa/go.mod h1:xBlh2Yi3DL3zy/2n15kITpg0YZardf/aa/hgUaIM6Rk=
90github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa/go.mod h1:xBlh2Yi3DL3zy/2n15kITpg0YZardf/aa/hgUaIM6Rk=
91−github.com/charmbracelet/x/exp/charmtone v0.0.0-20250627134340-c144409e381c h1:2GELBLPgfSbHU53bsQhR9XIgNuVZ6w+Rz8RWV5Lq+A4=
92−github.com/charmbracelet/x/exp/charmtone v0.0.0-20250627134340-c144409e381c/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
93github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3 h1:1xwHZg6eMZ9Wv5TE1UGub6ARubyOd1Lo5kPUI/6VL50=
91github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3 h1:1xwHZg6eMZ9Wv5TE1UGub6ARubyOd1Lo5kPUI/6VL50=
94github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
92github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
95github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
93github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
vendor/github.com/charmbracelet/bubbletea/v2/tea.go+15−4
@@ -624,6 +624,10 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
624 switch msg.Mode {
624 switch msg.Mode {
625 case ansi.AltScreenSaveCursorMode:
625 case ansi.AltScreenSaveCursorMode:
626 p.renderer.enterAltScreen()
626 p.renderer.enterAltScreen()
627+ // Main and alternate screen have their own Kitty keyboard
628+ // stack. We need to request keyboard enhancements again
629+ // when entering/exiting the alternate screen.
630+ p.requestKeyboardEnhancements()
627 case ansi.TextCursorEnableMode:
631 case ansi.TextCursorEnableMode:
628 p.renderer.showCursor()
632 p.renderer.showCursor()
629 case ansi.GraphemeClusteringMode:
633 case ansi.GraphemeClusteringMode:
@@ -645,6 +649,10 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
645 switch msg.Mode {
649 switch msg.Mode {
646 case ansi.AltScreenSaveCursorMode:
650 case ansi.AltScreenSaveCursorMode:
647 p.renderer.exitAltScreen()
651 p.renderer.exitAltScreen()
652+ // Main and alternate screen have their own Kitty keyboard
653+ // stack. We need to request keyboard enhancements again
654+ // when entering/exiting the alternate screen.
655+ p.requestKeyboardEnhancements()
648 case ansi.TextCursorEnableMode:
656 case ansi.TextCursorEnableMode:
649 p.renderer.hideCursor()
657 p.renderer.hideCursor()
650 default:
658 default:
@@ -1410,13 +1418,16 @@ func (p *Program) stopRenderer(kill bool) {
1410// requestKeyboardEnhancements tries to enable keyboard enhancements and read
1418// requestKeyboardEnhancements tries to enable keyboard enhancements and read
1411// the active keyboard enhancements from the terminal.
1419// the active keyboard enhancements from the terminal.
1412func (p *Program) requestKeyboardEnhancements() {
1420func (p *Program) requestKeyboardEnhancements() {
1421+ // XXX: We write to the renderer directly so that we synchronize with the
1422+ // alt-screen state of the renderer. This is because the main screen and
1423+ // alternate screen have their own Kitty keyboard state stack.
1413 if p.requestedEnhancements.modifyOtherKeys > 0 {
1424 if p.requestedEnhancements.modifyOtherKeys > 0 {
1414− p.execute(ansi.KeyModifierOptions(4, p.requestedEnhancements.modifyOtherKeys)) //nolint:mnd
1425+ _, _ = p.renderer.writeString(ansi.KeyModifierOptions(4, p.requestedEnhancements.modifyOtherKeys)) //nolint:mnd
1415− p.execute(ansi.QueryModifyOtherKeys)
1426+ _, _ = p.renderer.writeString(ansi.QueryModifyOtherKeys)
1416 }
1427 }
1417 if p.requestedEnhancements.kittyFlags > 0 {
1428 if p.requestedEnhancements.kittyFlags > 0 {
1418− p.execute(ansi.PushKittyKeyboard(p.requestedEnhancements.kittyFlags))
1429+ _, _ = p.renderer.writeString(ansi.PushKittyKeyboard(p.requestedEnhancements.kittyFlags))
1419− p.execute(ansi.RequestKittyKeyboard)
1430+ _, _ = p.renderer.writeString(ansi.RequestKittyKeyboard)
1420 }
1431 }
1421}
1432}
1422
1433
vendor/github.com/charmbracelet/lipgloss/v2/get.go+33
@@ -135,6 +135,16 @@ func (s Style) GetPaddingLeft() int {
135 return s.getAsInt(paddingLeftKey)
135 return s.getAsInt(paddingLeftKey)
136}
136}
137
137
138+// GetPaddingChar returns the style's padding character. If no value is set a
139+// space (`\u0020`) is returned.
140+func (s Style) GetPaddingChar() rune {
141+ char := s.getAsRune(paddingCharKey)
142+ if char == 0 {
143+ return ' '
144+ }
145+ return char
146+}
147+
138// GetHorizontalPadding returns the style's left and right padding. Unset
148// GetHorizontalPadding returns the style's left and right padding. Unset
139// values are measured as 0.
149// values are measured as 0.
140func (s Style) GetHorizontalPadding() int {
150func (s Style) GetHorizontalPadding() int {
@@ -186,6 +196,16 @@ func (s Style) GetMarginLeft() int {
186 return s.getAsInt(marginLeftKey)
196 return s.getAsInt(marginLeftKey)
187}
197}
188
198
199+// GetMarginChar returns the style's padding character. If no value is set a
200+// space (`\u0020`) is returned.
201+func (s Style) GetMarginChar() rune {
202+ char := s.getAsRune(marginCharKey)
203+ if char == 0 {
204+ return ' '
205+ }
206+ return char
207+}
208+
189// GetHorizontalMargins returns the style's left and right margins. Unset
209// GetHorizontalMargins returns the style's left and right margins. Unset
190// values are measured as 0.
210// values are measured as 0.
191func (s Style) GetHorizontalMargins() int {
211func (s Style) GetHorizontalMargins() int {
@@ -432,6 +452,19 @@ func (s Style) isSet(k propKey) bool {
432 return s.props.has(k)
452 return s.props.has(k)
433}
453}
434
454
455+func (s Style) getAsRune(k propKey) rune {
456+ if !s.isSet(k) {
457+ return 0
458+ }
459+ switch k { //nolint:exhaustive
460+ case paddingCharKey:
461+ return s.paddingChar
462+ case marginCharKey:
463+ return s.marginChar
464+ }
465+ return 0
466+}
467+
435func (s Style) getAsBool(k propKey, defaultVal bool) bool {
468func (s Style) getAsBool(k propKey, defaultVal bool) bool {
436 if !s.isSet(k) {
469 if !s.isSet(k) {
437 return defaultVal
470 return defaultVal
vendor/github.com/charmbracelet/lipgloss/v2/set.go+27
@@ -29,6 +29,8 @@ func (s *Style) set(key propKey, value any) {
29 s.paddingBottom = max(0, value.(int))
29 s.paddingBottom = max(0, value.(int))
30 case paddingLeftKey:
30 case paddingLeftKey:
31 s.paddingLeft = max(0, value.(int))
31 s.paddingLeft = max(0, value.(int))
32+ case paddingCharKey:
33+ s.paddingChar = value.(rune)
32 case marginTopKey:
34 case marginTopKey:
33 s.marginTop = max(0, value.(int))
35 s.marginTop = max(0, value.(int))
34 case marginRightKey:
36 case marginRightKey:
@@ -39,6 +41,8 @@ func (s *Style) set(key propKey, value any) {
39 s.marginLeft = max(0, value.(int))
41 s.marginLeft = max(0, value.(int))
40 case marginBackgroundKey:
42 case marginBackgroundKey:
41 s.marginBgColor = colorOrNil(value)
43 s.marginBgColor = colorOrNil(value)
44+ case marginCharKey:
45+ s.marginChar = value.(rune)
42 case borderStyleKey:
46 case borderStyleKey:
43 s.borderStyle = value.(Border)
47 s.borderStyle = value.(Border)
44 case borderTopForegroundKey:
48 case borderTopForegroundKey:
@@ -111,6 +115,8 @@ func (s *Style) setFrom(key propKey, i Style) {
111 s.set(paddingBottomKey, i.paddingBottom)
115 s.set(paddingBottomKey, i.paddingBottom)
112 case paddingLeftKey:
116 case paddingLeftKey:
113 s.set(paddingLeftKey, i.paddingLeft)
117 s.set(paddingLeftKey, i.paddingLeft)
118+ case paddingCharKey:
119+ s.set(paddingCharKey, i.paddingChar)
114 case marginTopKey:
120 case marginTopKey:
115 s.set(marginTopKey, i.marginTop)
121 s.set(marginTopKey, i.marginTop)
116 case marginRightKey:
122 case marginRightKey:
@@ -121,6 +127,8 @@ func (s *Style) setFrom(key propKey, i Style) {
121 s.set(marginLeftKey, i.marginLeft)
127 s.set(marginLeftKey, i.marginLeft)
122 case marginBackgroundKey:
128 case marginBackgroundKey:
123 s.set(marginBackgroundKey, i.marginBgColor)
129 s.set(marginBackgroundKey, i.marginBgColor)
130+ case marginCharKey:
131+ s.set(marginCharKey, i.marginChar)
124 case borderStyleKey:
132 case borderStyleKey:
125 s.set(borderStyleKey, i.borderStyle)
133 s.set(borderStyleKey, i.borderStyle)
126 case borderTopForegroundKey:
134 case borderTopForegroundKey:
@@ -320,6 +328,18 @@ func (s Style) PaddingBottom(i int) Style {
320 return s
328 return s
321}
329}
322
330
331+// PaddingChar sets the character used for padding. This is useful for
332+// rendering blocks with a specific character, such as a space or a dot.
333+// Example of using [NBSP] as padding to prevent line breaks:
334+//
335+// ```go
336+// s := lipgloss.NewStyle().PaddingChar(lipgloss.NBSP)
337+// ```
338+func (s Style) PaddingChar(r rune) Style {
339+ s.set(paddingCharKey, r)
340+ return s
341+}
342+
323// ColorWhitespace determines whether or not the background color should be
343// ColorWhitespace determines whether or not the background color should be
324// applied to the padding. This is true by default as it's more than likely the
344// applied to the padding. This is true by default as it's more than likely the
325// desired and expected behavior, but it can be disabled for certain graphic
345// desired and expected behavior, but it can be disabled for certain graphic
@@ -390,6 +410,13 @@ func (s Style) MarginBackground(c color.Color) Style {
390 return s
410 return s
391}
411}
392
412
413+// MarginChar sets the character used for the margin. This is useful for
414+// rendering blocks with a specific character, such as a space or a dot.
415+func (s Style) MarginChar(r rune) Style {
416+ s.set(marginCharKey, r)
417+ return s
418+}
419+
393// Border is shorthand for setting the border style and which sides should
420// Border is shorthand for setting the border style and which sides should
394// have a border at once. The variadic argument sides works as follows:
421// have a border at once. The variadic argument sides works as follows:
395//
422//
vendor/github.com/charmbracelet/lipgloss/v2/style.go+18−8
@@ -10,7 +10,8 @@ import (
10)
10)
11
11
12const (
12const (
13+ // NBSP is the non-breaking space rune.
13− nbsp = '\u00A0'
14+ NBSP = '\u00A0'
14 tabWidthDefault = 4
15 tabWidthDefault = 4
15)
16)
16
17
@@ -44,6 +45,7 @@ const (
44 paddingRightKey
45 paddingRightKey
45 paddingBottomKey
46 paddingBottomKey
46 paddingLeftKey
47 paddingLeftKey
48+ paddingCharKey
47
49
48 // Margins.
50 // Margins.
49 marginTopKey
51 marginTopKey
@@ -51,6 +53,7 @@ const (
51 marginBottomKey
53 marginBottomKey
52 marginLeftKey
54 marginLeftKey
53 marginBackgroundKey
55 marginBackgroundKey
56+ marginCharKey
54
57
55 // Border runes.
58 // Border runes.
56 borderStyleKey
59 borderStyleKey
@@ -128,12 +131,14 @@ type Style struct {
128 paddingRight int
131 paddingRight int
129 paddingBottom int
132 paddingBottom int
130 paddingLeft int
133 paddingLeft int
134+ paddingChar rune
131
135
132 marginTop int
136 marginTop int
133 marginRight int
137 marginRight int
134 marginBottom int
138 marginBottom int
135 marginLeft int
139 marginLeft int
136 marginBgColor color.Color
140 marginBgColor color.Color
141+ marginChar rune
137
142
138 borderStyle Border
143 borderStyle Border
139 borderTopFgColor color.Color
144 borderTopFgColor color.Color
@@ -387,23 +392,24 @@ func (s Style) Render(strs ...string) string {
387
392
388 // Padding
393 // Padding
389 if !inline { //nolint:nestif
394 if !inline { //nolint:nestif
395+ padChar := s.paddingChar
396+ if padChar == 0 {
397+ padChar = ' '
398+ }
390 if leftPadding > 0 {
399 if leftPadding > 0 {
391 var st *ansi.Style
400 var st *ansi.Style
392 if colorWhitespace || styleWhitespace {
401 if colorWhitespace || styleWhitespace {
393 st = &teWhitespace
402 st = &teWhitespace
394 }
403 }
395− str = padLeft(str, leftPadding, st, nbsp)
404+ str = padLeft(str, leftPadding, st, padChar)
396 }
405 }
397
406
398− // XXX: We use a non-breaking space to pad so that the padding is
399− // preserved when the string is copied and pasted.
400−
401 if rightPadding > 0 {
407 if rightPadding > 0 {
402 var st *ansi.Style
408 var st *ansi.Style
403 if colorWhitespace || styleWhitespace {
409 if colorWhitespace || styleWhitespace {
404 st = &teWhitespace
410 st = &teWhitespace
405 }
411 }
406− str = padRight(str, rightPadding, st, nbsp)
412+ str = padRight(str, rightPadding, st, padChar)
407 }
413 }
408
414
409 if topPadding > 0 {
415 if topPadding > 0 {
@@ -494,8 +500,12 @@ func (s Style) applyMargins(str string, inline bool) string {
494 }
500 }
495
501
496 // Add left and right margin
502 // Add left and right margin
503+ marginChar := s.marginChar
504+ if marginChar == 0 {
505+ marginChar = ' '
506+ }
497− str = padLeft(str, leftMargin, &style, ' ')
507+ str = padLeft(str, leftMargin, &style, marginChar)
498− str = padRight(str, rightMargin, &style, ' ')
508+ str = padRight(str, rightMargin, &style, marginChar)
499
509
500 // Top/bottom margin
510 // Top/bottom margin
501 if !inline {
511 if !inline {
vendor/github.com/charmbracelet/lipgloss/v2/unset.go+7
@@ -96,6 +96,13 @@ func (s Style) UnsetPadding() Style {
96 s.unset(paddingRightKey)
96 s.unset(paddingRightKey)
97 s.unset(paddingTopKey)
97 s.unset(paddingTopKey)
98 s.unset(paddingBottomKey)
98 s.unset(paddingBottomKey)
99+ s.unset(paddingCharKey)
100+ return s
101+}
102+
103+// UnsetPaddingChar removes the padding character style rule, if set.
104+func (s Style) UnsetPaddingChar() Style {
105+ s.unset(paddingCharKey)
99 return s
106 return s
100}
107}
101
108
vendor/github.com/charmbracelet/ultraviolet/screen/screen.godeleted−109
@@ -1,109 +0,0 @@
1−// Package screen provides functions and helpers to manipulate a [uv.Screen].
2−package screen
3−
4−import uv "github.com/charmbracelet/ultraviolet"
5−
6−// Clear clears the screen with empty cells. This is equivalent to filling the
7−// screen with empty cells.
8−//
9−// If the screen implements a [Clear] method, it will be called instead of
10−// filling the screen with empty cells.
11−func Clear(scr uv.Screen) {
12− if c, ok := scr.(interface {
13− Clear()
14− }); ok {
15− c.Clear()
16− return
17− }
18− Fill(scr, nil)
19−}
20−
21−// ClearArea clears the given area of the screen with empty cells. This is
22−// equivalent to filling the area with empty cells.
23−//
24−// If the screen implements a [ClearArea] method, it will be called instead of
25−// filling the area with empty cells.
26−func ClearArea(scr uv.Screen, area uv.Rectangle) {
27− if c, ok := scr.(interface {
28− ClearArea(area uv.Rectangle)
29− }); ok {
30− c.ClearArea(area)
31− return
32− }
33− FillArea(scr, nil, area)
34−}
35−
36−// Fill fills the screen with the given cell. If the cell is nil, it fills the
37−// screen with empty cells.
38−//
39−// If the screen implements a [Fill] method, it will be called instead of
40−// filling the screen with empty cells.
41−func Fill(scr uv.Screen, cell *uv.Cell) {
42− if f, ok := scr.(interface {
43− Fill(cell *uv.Cell)
44− }); ok {
45− f.Fill(cell)
46− return
47− }
48− FillArea(scr, cell, scr.Bounds())
49−}
50−
51−// FillArea fills the given area of the screen with the given cell. If the cell
52−// is nil, it fills the area with empty cells.
53−//
54−// If the screen implements a [FillArea] method, it will be called instead of
55−// filling the area with empty cells.
56−func FillArea(scr uv.Screen, cell *uv.Cell, area uv.Rectangle) {
57− if f, ok := scr.(interface {
58− FillArea(cell *uv.Cell, area uv.Rectangle)
59− }); ok {
60− f.FillArea(cell, area)
61− return
62− }
63− for y := area.Min.Y; y < area.Max.Y; y++ {
64− for x := area.Min.X; x < area.Max.X; x++ {
65− scr.SetCell(x, y, cell)
66− }
67− }
68−}
69−
70−// CloneArea clones the given area of the screen and returns a new buffer
71−// with the same size as the area. The new buffer will contain the same cells
72−// as the area in the screen.
73−// Use [uv.Buffer.Draw] to draw the cloned buffer to a screen again.
74−//
75−// If the screen implements a [CloneArea] method, it will be called instead of
76−// cloning the area manually.
77−func CloneArea(scr uv.Screen, area uv.Rectangle) *uv.Buffer {
78− if c, ok := scr.(interface {
79− CloneArea(area uv.Rectangle) *uv.Buffer
80− }); ok {
81− return c.CloneArea(area)
82− }
83− buf := uv.NewBuffer(area.Dx(), area.Dy())
84− for y := area.Min.Y; y < area.Max.Y; y++ {
85− for x := area.Min.X; x < area.Max.X; x++ {
86− cell := scr.CellAt(x, y)
87− if cell == nil || cell.IsZero() {
88− continue
89− }
90− buf.SetCell(x-area.Min.X, y-area.Min.Y, cell.Clone())
91− }
92− }
93− return buf
94−}
95−
96−// Clone creates a new [uv.Buffer] clone of the given screen. The new buffer will
97−// have the same size as the screen and will contain the same cells.
98−// Use [uv.Buffer.Draw] to draw the cloned buffer to a screen again.
99−//
100−// If the screen implements a [Clone] method, it will be called instead of
101−// cloning the entire screen manually.
102−func Clone(scr uv.Screen) *uv.Buffer {
103− if c, ok := scr.(interface {
104− Clone() *uv.Buffer
105− }); ok {
106− return c.Clone()
107− }
108− return CloneArea(scr, scr.Bounds())
109−}
vendor/modules.txt+5−6
@@ -254,8 +254,8 @@ github.com/charmbracelet/bubbles/v2/spinner
254github.com/charmbracelet/bubbles/v2/textarea
254github.com/charmbracelet/bubbles/v2/textarea
255github.com/charmbracelet/bubbles/v2/textinput
255github.com/charmbracelet/bubbles/v2/textinput
256github.com/charmbracelet/bubbles/v2/viewport
256github.com/charmbracelet/bubbles/v2/viewport
257−# github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250708152737-144080f3d891
257+# github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
258−## explicit; go 1.24.3
258+## explicit; go 1.24.0
259github.com/charmbracelet/bubbletea/v2
259github.com/charmbracelet/bubbletea/v2
260# github.com/charmbracelet/colorprofile v0.3.1
260# github.com/charmbracelet/colorprofile v0.3.1
261## explicit; go 1.23.0
261## explicit; go 1.23.0
@@ -269,7 +269,7 @@ github.com/charmbracelet/glamour/v2
269github.com/charmbracelet/glamour/v2/ansi
269github.com/charmbracelet/glamour/v2/ansi
270github.com/charmbracelet/glamour/v2/internal/autolink
270github.com/charmbracelet/glamour/v2/internal/autolink
271github.com/charmbracelet/glamour/v2/styles
271github.com/charmbracelet/glamour/v2/styles
272−# github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.2.0.20250703152125-8e1c474f8a71 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250708152830-0fa4ef151093
272+# github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.2.0.20250703152125-8e1c474f8a71 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb
273## explicit; go 1.24.2
273## explicit; go 1.24.2
274github.com/charmbracelet/lipgloss/v2
274github.com/charmbracelet/lipgloss/v2
275github.com/charmbracelet/lipgloss/v2/table
275github.com/charmbracelet/lipgloss/v2/table
@@ -280,7 +280,6 @@ github.com/charmbracelet/log/v2
280# github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9
280# github.com/charmbracelet/ultraviolet v0.0.0-20250708152637-0fe0235c8db9
281## explicit; go 1.24.0
281## explicit; go 1.24.0
282github.com/charmbracelet/ultraviolet
282github.com/charmbracelet/ultraviolet
283−github.com/charmbracelet/ultraviolet/screen
284# github.com/charmbracelet/x/ansi v0.9.3
283# github.com/charmbracelet/x/ansi v0.9.3
285## explicit; go 1.23.0
284## explicit; go 1.23.0
286github.com/charmbracelet/x/ansi
285github.com/charmbracelet/x/ansi
@@ -839,5 +838,5 @@ mvdan.cc/sh/v3/fileutil
839mvdan.cc/sh/v3/interp
838mvdan.cc/sh/v3/interp
840mvdan.cc/sh/v3/pattern
839mvdan.cc/sh/v3/pattern
841mvdan.cc/sh/v3/syntax
840mvdan.cc/sh/v3/syntax
842−# github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250708152737-144080f3d891
841+# github.com/charmbracelet/bubbletea/v2 => github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250710185017-3c0ffd25e595
843−# github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250708152830-0fa4ef151093
842+# github.com/charmbracelet/lipgloss/v2 => github.com/charmbracelet/lipgloss-internal/v2 v2.0.0-20250710185058-03664cb9cecb