Releases: charmbracelet/lipgloss
v0.13.0
Woodn’t you know, Lip Gloss has trees!
Lip Gloss ships with a tree rendering sub-package.
import "github.com/charmbracelet/lipgloss/tree"
Define a new tree.
t := tree.Root(".").
Child("A", "B", "C")
Print the tree.
fmt.Println(t)
// .
// ├── A
// ├── B
// └── C
Trees have the ability to nest.
t := tree.Root(".").
Child("macOS").
Child(
tree.New().
Root("Linux").
Child("NixOS").
Child("Arch Linux (btw)").
Child("Void Linux"),
).
Child(
tree.New().
Root("BSD").
Child("FreeBSD").
Child("OpenBSD"),
)
Print the tree.
fmt.Println(t)
Trees can be customized via their enumeration function as well as using
lipgloss.Style
s.
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))
t := tree.
Root("⁜ Makeup").
Child(
"Glossier",
"Fenty Beauty",
tree.New().Child(
"Gloss Bomb Universal Lip Luminizer",
"Hot Cheeks Velour Blushlighter",
),
"Nyx",
"Mac",
"Milk",
).
Enumerator(tree.RoundedEnumerator).
EnumeratorStyle(enumeratorStyle).
RootStyle(rootStyle).
ItemStyle(itemStyle)
Print the tree.
The predefined enumerators for trees are DefaultEnumerator
and RoundedEnumerator
.
If you need, you can also build trees incrementally:
t := tree.New()
for i := 0; i < repeat; i++ {
t.Child("Lip Gloss")
}
There’s more where that came from
Changelog
New Features
- 0618c73: feat(test): add test for
JoinHorizontal
(#346) (@aditipatelpro) - feb42a9: feat: move tree to root (#342) (@caarlos0)
Bug fixes
- 8a0e640: fix: remove unnecessary if (@aymanbagabas)
Documentation updates
- bc0de5c: docs(README): make tree example match output (@bashbunni)
- bb3e339: docs(README): match tree example alignment with list examples (@bashbunni)
- 185fde3: docs(README): update tree images (@bashbunni)
- ed7f56e: docs: fix
CompleteColor
example (#345) (@bashbunni) - cf0a7c6: docs: fix tree screenshot (@caarlos0)
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.
v0.12.1
Border width calcs: back to normal
This release fixes a regression with regard to border calculations introduced in Lip Gloss v0.11.1.
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.
v0.12.0
Lists, Check ✓
This release adds a new sub-package for rendering trees and lists.
import "github.com/charmbracelet/lipgloss/list"
Define a new list.
l := list.New("A", "B", "C")
Print the list.
fmt.Println(l)
// • A
// • B
// • C
Lists have the ability to nest.
l := list.New(
"A", list.New("Artichoke"),
"B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"),
"C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"),
"D", list.New("Dill", "Dragonfruit", "Dried Shrimp"),
"E", list.New("Eggs"),
"F", list.New("Fish Cake", "Furikake"),
"J", list.New("Jicama"),
"K", list.New("Kohlrabi"),
"L", list.New("Leeks", "Lentils", "Licorice Root"),
)
Print the list.
fmt.Println(l)
Lists can be customized via their enumeration function as well as using
lipgloss.Style
s.
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
l := list.New(
"Glossier",
"Claire’s Boutique",
"Nyx",
"Mac",
"Milk",
).
Enumerator(list.Roman).
EnumeratorStyle(enumeratorStyle).
ItemStyle(itemStyle)
Print the list.
In addition to the predefined enumerators (Arabic
, Alphabet
, Roman
, Bullet
, Tree
),
you may also define your own custom enumerator:
l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")
func DuckDuckGooseEnumerator(l list.Items, i int) string {
if l.At(i).Value() == "Goose" {
return "Honk →"
}
return ""
}
l = l.Enumerator(DuckDuckGooseEnumerator)
Print the list:
If you need, you can also build lists incrementally:
l := list.New()
for i := 0; i < repeat; i++ {
l.Item("Lip Gloss")
}
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.
v0.11.1
A lil’ truncation fix
This release is a small patch release to fix text truncation in table cells. For details see: #324.
Other stuff
- chore: remove deprecated Copy() calls by @meowgorithm in #306
- feat: deprecate Style.ColorWhitespace by @meowgorithm in #311
- feat: deprecate Style.ColorWhitespace by @meowgorithm in #314
- fix: Deprecate UnsetBorderTopBackgroundColor in favor of UnsetBorderTopBackground by @nervo in #315
Full Changelog: v0.11.0...v0.11.1
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.11.0
Immutable Styles and Raw Speed, Baby
So! The big news in this release is:
Style
methods will now always return new stylesStyle
and ANSI operations under the hood are faster
There are also a handful of great lil' bug fixes. Read on for more.
Immutable Styles
Every Style
method now returns a completely new style with its own underlying data structure no matter what. This means working with Styles is a lot easier. No more need for Copy()
!
// Before
s := lipgloss.NewStyle().Bold(true)
newStyle := s.Copy()
// After
s := lipgloss.NewStyle().Bold(true)
newStyle := s // this is a true copy
Okay, but why are styles easier to work with now? Consider this:
// Before
baseStyle := lipgloss.NewStyle().Background(lipgloss.Color("59"))
styleAtRuntime := baseStyle.Copy().Width(m.Width)
// After
baseStyle := lipgloss.NewStyle().Padding(1, 2)
styleAtRuntime := baseStyle.Width(m.Width)
It might seem small, but eliminating the risk of mutations in persistent styles in an enormous usability improvement.
How to upgrade
There's nothing to do, however Style.Copy()
is now deprecated and only returns itself, so you can just remove Style.Copy()
calls. If you need to just copy a style without any changes to it you can simply b := a
.
Faster ANSI
Sometimes watch companies brag about their "in-house" watch movement. Well, now we're bragging about our in-house-amazing x/ansi
library by our own @aymanbagabas. It's a fine-tuned, low-level way to manage ANSI sequencing and, because we're pretty nerdy, we’re super excited about it.
What's Changed
New!
- always return copies of styles by @aymanbagabas in #276
Changed
- switch to term/ansi for text manipulation by @aymanbagabas in #268
- replace stripansi with ansi.Strip in table by @aymanbagabas in #271
- test for different GOOS & GOARCH by @aymanbagabas in #292
Fixed
- fix combining both conditional and unconditional wrapping by @aymanbagabas in #275
- fix UnderlineSpaces and StrikethroughSpaces by @Taz03 in #299
- always render horizontal border edges when enabled by @UnseenBook in #211
- fix possible nil panic by @maaslalani in #245
- fix transform operating on ANSI sequences by @meowgorithm in #274
- change propkeys from int to int64 by @hugoleodev in #291
New Contributors
- @benwaffle made their first contribution in #247
- @UnseenBook made their first contribution in #211
- @hugoleodev made their first contribution in #291
- @Taz03 made their first contribution in #299
Full Changelog: v0.10.0...v0.11.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.10.0
String Transforms 💄
Lip Gloss v0.10.0
features a brand new Transform
function for Styles to alter strings at render time. As well as some bug fixes, like ANSI-aware table cell truncation. 🧹
Simply define a Transform
function as func (string) string
and apply it to any style:
// Example:
s := NewStyle().Transform(strings.ToUpper)
fmt.Println(s.Render("raow!") // "RAOW!"
Or, if you prefer:
// Example:
reverse := func(s string) string {
n := 0
rune := make([]rune, len(s))
for _, r := range s {
rune[n] = r
n++
}
rune = rune[0:n]
for i := 0; i < n/2; i++ {
rune[i], rune[n-1-i] = rune[n-1-i], rune[i]
}
return string(rune)
}
s := NewStyle().Transform(reverse)
fmt.Println(s.Render("The quick brown 狐 jumped over the lazy 犬")
// "犬 yzal eht revo depmuj 狐 nworb kciuq ehT",
What's Changed?
- Corrected border shorthand functions explanation by @ReidMason in #237
- Align help by @schmurfy in #239
Style.Transform
for altering strings at render time by @meowgorithm in #232- Adding right padding to empty string by @mikelorant in #253
- Refactor padding functions by @mikelorant in #254
- Fix truncate of table cells containing ANSI by @mikelorant in #256
- Improve maximum width of characters in a string by @mikelorant in #257
New Contributors
- @ReidMason made their first contribution in #237
- @schmurfy made their first contribution in #239
- @mikelorant made their first contribution in #253
Full Changelog: v0.9.1...v0.10.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.9.1
This bugfix release changes the Table Headers
API to accept []string
for consistency with Row
/ Rows
and downgrades Lip Gloss to Go version v1.17
.
What's Changed
- Table Headers type from
[]any
→[]string
by @maaslalani in #234 - Downgrade Lip Gloss to
v1.17
by @maaslalani in #234
Full Changelog: v0.9.0...v0.9.1
v0.9.0
My, how the tables have turned
Now you can draw Table
s with Lip Gloss! 💅
View the source code.
Let's get started
import "github.com/charmbracelet/lipgloss/table"
Define some rows of data.
rows := [][]string{
{"Chinese", "您好", "你好"},
{"Japanese", "こんにちは", "やあ"},
{"Arabic", "أهلين", "أهلا"},
{"Russian", "Здравствуйте", "Привет"},
{"Spanish", "Hola", "¿Qué tal?"},
}
Use the table package to style and render the table.
t := table.New().
Border(lipgloss.NormalBorder()).
BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))).
StyleFunc(func(row, col int) lipgloss.Style {
switch {
case row == 0:
return HeaderStyle
case row%2 == 0:
return EvenRowStyle
default:
return OddRowStyle
}
}).
Headers("LANGUAGE", "FORMAL", "INFORMAL").
Rows(rows...)
// You can also add tables row-by-row
t.Row("English", "You look absolutely fabulous.", "How's it going?")
Print the table.
fmt.Println(t)
For more on tables see the examples.
Additional Borders
Lip Gloss' Border
now supports additional middle border separators.
type Border struct {
// ...
MiddleLeft string
MiddleRight string
Middle string
MiddleTop string
MiddleBottom string
}
v0.8.0
Predictable Tabs
At last: tabs that render the way you want ’em to. With the new Style.TabWidth()
method, you can determine exactly how a \t
will render.
Before this release, Lip Gloss used to mis-measure a tab (i.e. a \t
) at 0 cells wide when they actually render at different widths in different terminals (usually 8 cells, sometimes 4 cells). For these reasons, tabs are almost never what you want when designing layouts for TUIs.
With this release, a tab will get converted to 4 spaces by default—so this is a behavioral change—but you can customize the behavior as well as disable it entirely.
s := lipgloss.NewStyle() // 4 spaces per tab, the default
s = s.TabWidth(2) // 2 spaces per tab
s = s.TabWidth(0) // remove tabs
s = s.TabWidth(-1) // don't convert tabs to spaces
s = s.TabWidth(NoTabConversion) // alias of the above
You can disable the feature with Style.TabWidth(NoTabConversion)
(or Style.TabWidth(-1)
, if you're the pedantic type).
Bug Fixes
This release also includes a bunch of bug fixes. This includes:
- fix: border size calculation by @mieubrisse in #197
- fix: renderer race condition by @aymanbagabas in #210
- fix: cache color profile and background by @aymanbagabas in #212
Full Changelog: v0.7.1...v0.8.0
v0.7.1
This bugfix release fixes a problem introduced in v0.7.0 where applications could freeze or hang on start-up.
What's Changed
- fix(renderer): use termenv default renderer by @aymanbagabas in #179
- chore: bump termenv to v0.15.1 by @muesli in #180
Full Changelog: v0.7.0...v0.7.1