Style definitions for nice terminal layouts 👄
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",
Style.Transform
for altering strings at render time by @meowgorithm in https://github.com/charmbracelet/lipgloss/pull/232
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.9.1...v0.10.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Slack.
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
.
[]any
→ []string
by @maaslalani in https://github.com/charmbracelet/lipgloss/pull/234
v1.17
by @maaslalani in https://github.com/charmbracelet/lipgloss/pull/234
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.9.0...v0.9.1
Now you can draw Table
s with Lip Gloss! 💅
View the source code.
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.
Lip Gloss' Border
now supports additional middle border separators.
type Border struct {
// ...
MiddleLeft string
MiddleRight string
Middle string
MiddleTop string
MiddleBottom string
}
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).
This release also includes a bunch of bug fixes. This includes:
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.7.1...v0.8.0
This bugfix release fixes a problem introduced in v0.7.0 where applications could freeze or hang on start-up.
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.7.0...v0.7.1
We're pleased to introduce custom renders for Lip Gloss! Custom renderers allow you to render to a specific outputs, which is particularly important when you want to detect the color profile and dark background status for multiple different outputs at runtime, such as in a server-client situation.
Here's what it looks like:
func myLittleHandler(sess ssh.Session) {
// Create a renderer for the client.
renderer := lipgloss.NewRenderer(sess)
// Create a new style on the renderer.
style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"})
// Render. The color profile and dark background state will be correctly detected.
io.WriteString(sess, style.Render("Heyyyyyyy"))
}
For a full example on using a custom renderer over SSH with Wish see the SSH example.
type Renderer struct
NewRenderer(io.Writer)
DefaultRenderer()
SetDefaultRenderer(*lipgloss.Renderer)
style.Renderer(*lipgloss.Renderer) Style
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.6.0...v0.7.0
In this latest release of Lip Gloss, styles now support vertical alignment! Additionally, we introduced two new color types, CompleteColor
and CompleteAdaptiveColor
, which lets you bypass automatic color interpolation choose colors for each color profile (ANSI, ANSI256, and TrueColor).
Align content in a Style
vertically at the top, center, or bottom. To get started make a style with some Height
and an AlignVertical
property.
lipgloss.NewStyle().Height(5).AlignVertical(lipgloss.Center).Render("Hello, Center!")
or use the Align
shorthand which sets both horizontal and vertical alignments:
lipgloss.NewStyle().Height(5).Align(lipgloss.Left, lipgloss.Bottom).Render("Hello, Center!")
New API:
Note, there are no breaking changes since if the
Align
shorthand will still accept 1 argument (variadic arguments) and will set only the horizontal alignment to maintain original functionality
Align(p ...Position)
AlignVertical(p Position)
AlignHorizontal(p Position)
GetAlignVertical() Position
GetAlignHorizontal() Position
This revision introduces two new color types CompleteColor
and CompleteAdaptiveColor
. Not for the faint of heart, these types are for bypassing automatic color interpolation so you can specify exact colors for all color profiles in cases where the interpolation can benefit from manual adjustment:
cc := CompleteColor{
TrueColor: "#6B51FF",
ANSI256: "63",
ANSI: "5",
}
cac := CompleteAdaptiveColor{
Light: CompleteColor{
TrueColor: "#FF51CE",
ANSI256: "213",
ANSI: "5",
},
Dark: CompleteColor{
TrueColor: "#6B51FF",
ANSI256: "63",
ANSI: "5",
},
}
sync.RWMutex
by @meowgorithm in https://github.com/charmbracelet/lipgloss/pull/68
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.5.0...v0.6.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Slack.
Happy Valentines Day! Have you ever noticed that programming is the one field where laziness is totally awesome?
The big news in this release is that Lip Gloss will now wait until absolutely necessary to query for the terminal's background color. This was formerly a fairly heavy hit on the system, however it's been optimized to the point where we can now run it on demand. This, and some other major performance benefits, come from some acute improvements upstream in Termenv
. For the low level details check out the release notes for the mighty Termenv v0.10.0 and v0.11.0 releases.
Value()
method for getting the underlying string value set with SetString()
(thanks @76creates) https://github.com/charmbracelet/lipgloss/pull/66
JoinVertical
behavior for non-edge non-center alignments (thanks @ryantriangles) https://github.com/charmbracelet/lipgloss/pull/49
Full Changelog: https://github.com/charmbracelet/lipgloss/compare/v0.4.0...v0.5.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Slack.
This release includes a menagerie of small but useful and handy improvements.
Occasionally you’ll have a word (often a URL or a path) that runs wider than the Width()
you set on a style:
// style.Width(5)
╭───────────╮
│Schnurrbart│
╰───────────╯
// ... that is definitely more than 5 cells wide
Such words will now automatically wrap:
// style.Width(5)
╭─────╮
│Schnu│
│rrbar│
│t │
╰─────╯
// There we go
For details, see the corresponding PR.
Sometimes you need to measure borders and whitespace when calculating your layouts and you end up with code like this:
const horizontalPadding = 6
style.Copy().Width(windowWidth-horizontalPadding)
This update includes a multitude of methods for querying your styles so you can do away with unnecessary constants and magic numbers. For example:
style.Copy().Width(windowWidth-style.GetHorizontalPadding())
The most useful of these methods are perhaps the ones that let you query margins, borders and padding all at once:
Style.GetFrameSize() (x, y int)
Style.GetVerticalFrameSize() int
Style.GetHorizontalFrameSize() int
For details see the changelog below.
At first glance a hidden border seems silly. Hidden borders can be useful, however, if you want to remove a border but maintain layout positioning in, say, an interactive TUI. Also note that you can still apply a background color to a hidden border.
To make a hidden border simply call lipgloss.HiddenBorder()
.
Width()
now wrap automaticallyHiddenBorder()
, which renders a border comprised of spacesStyle.GetBorderTopSize() int
Style.GetBorderRightSize() int
Style.GetBorderBottomSize() int
Style.GetBorderLeftSize() int
Style.GetHorizontalBorderSize() int
Style.GetVerticalBorderSize() int
Style.GetMarginTop() int
Style.GetMarginRight() int
Style.GetMarginBottom() int
Style.GetMarginLeft() int
Style.GetHorizontalMargins() int
Style.GetVerticalMargins() int
Style.GetPaddingTop() int
Style.GetPaddingRight() int
Style.GetPaddingBottom() int
Style.GetPaddingLeft() int
Style.GetHorizontalPadding() int
Style.GetVerticalPadding() int
Style.GetVerticalFrameSize() int
Style.GetHorizontalFrameSize() int
Style.GetFrameSize() (x, y int)
Border.GetTopSize() int
Border.GetRightSize() int
Border.GetBottomSize() int
Border.GetLeftSize() int
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse or right here in GitHub Discussions.
This release adds two utility functions to the Lip Gloss tool belt:
StyleRunes
can apply styling to runes at specific indices in a string, perfect for highlighting matched characters in a fuzzy search result, marking a hotkey on a button and so on.
SetColorProfile
sets the color profile on a package-wide basis, which is very useful in testing. Note that outside of testing you probably don’t want to set the color profile as the best available option will be automatically chosen.
StyleRunes
: applying styling to specific runes in a stringSetColorProfile
: set the color profile on a package-wide contextgo-runewidth
to v0.0.13 for improved emoji renderingThoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse or right here in GitHub Discussions.