漂亮的终端布局的样式定义。构建时考虑了 TUI。
Lip Gloss 采用富有表现力的声明性方法进行终端渲染。熟悉 CSS 的用户会对 Lip Gloss 感到宾至如归。
import "github.com/charmbracelet/lipgloss"
var style = lipgloss . NewStyle ().
Bold ( true ).
Foreground ( lipgloss . Color ( "#FAFAFA" )).
Background ( lipgloss . Color ( "#7D56F4" )).
PaddingTop ( 2 ).
PaddingLeft ( 4 ).
Width ( 22 )
fmt . Println ( style . Render ( "Hello, kitty" ))
唇彩支持以下颜色配置文件:
lipgloss . Color ( "5" ) // magenta
lipgloss . Color ( "9" ) // red
lipgloss . Color ( "12" ) // light blue
lipgloss . Color ( "86" ) // aqua
lipgloss . Color ( "201" ) // hot pink
lipgloss . Color ( "202" ) // orange
lipgloss . Color ( "#0000FF" ) // good ol' 100% blue
lipgloss . Color ( "#04B575" ) // a green
lipgloss . Color ( "#3C3C3C" ) // a dark gray
...以及 1 位 ASCII 配置文件,仅是黑白的。
将自动检测终端的颜色配置文件,当前调色板色域之外的颜色将自动强制为其最接近的可用值。
您还可以指定浅色和深色背景的颜色选项:
lipgloss. AdaptiveColor { Light : "236" , Dark : "248" }
将自动检测终端的背景颜色,并在运行时选择适当的颜色。
CompleteColor 指定真彩色、ANSI256 和 ANSI 颜色配置文件的精确值。
lipgloss. CompleteColor { TrueColor : "#0000FF" , ANSI256 : "86" , ANSI : "5" }
在这种情况下,不会执行自动颜色降级,它将基于指定的颜色。
您可以将CompleteColor
与AdaptiveColor
结合使用来指定浅色和深色背景的精确值,而不会自动降低颜色。
lipgloss. CompleteAdaptiveColor {
Light : CompleteColor { TrueColor : "#d7ffae" , ANSI256 : "193" , ANSI : "11" },
Dark : CompleteColor { TrueColor : "#d75fee" , ANSI256 : "163" , ANSI : "5" },
}
Lip Gloss 支持常用的 ANSI 文本格式选项:
var style = lipgloss . NewStyle ().
Bold ( true ).
Italic ( true ).
Faint ( true ).
Blink ( true ).
Strikethrough ( true ).
Underline ( true ).
Reverse ( true )
Lip Gloss 还支持块级格式化规则:
// Padding
var style = lipgloss . NewStyle ().
PaddingTop ( 2 ).
PaddingRight ( 4 ).
PaddingBottom ( 2 ).
PaddingLeft ( 4 )
// Margins
var style = lipgloss . NewStyle ().
MarginTop ( 2 ).
MarginRight ( 4 ).
MarginBottom ( 2 ).
MarginLeft ( 4 )
还有边距和填充的简写语法,其格式与 CSS 相同:
// 2 cells on all sides
lipgloss . NewStyle (). Padding ( 2 )
// 2 cells on the top and bottom, 4 cells on the left and right
lipgloss . NewStyle (). Margin ( 2 , 4 )
// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
lipgloss . NewStyle (). Padding ( 1 , 4 , 2 )
// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
// the bottom, and 1 on the left
lipgloss . NewStyle (). Margin ( 2 , 4 , 3 , 1 )
您可以将文本段落左对齐、右对齐或居中对齐。
var style = lipgloss . NewStyle ().
Width ( 24 ).
Align ( lipgloss . Left ). // align it left
Align ( lipgloss . Right ). // no wait, align it right
Align ( lipgloss . Center ) // just kidding, align it in the center
设置最小宽度和高度非常简单明了。
var style = lipgloss . NewStyle ().
SetString ( "What’s for lunch?" ).
Width ( 24 ).
Height ( 32 ).
Foreground ( lipgloss . Color ( "63" ))
添加边框很简单:
// Add a purple, rectangular border
var style = lipgloss . NewStyle ().
BorderStyle ( lipgloss . NormalBorder ()).
BorderForeground ( lipgloss . Color ( "63" ))
// Set a rounded, yellow-on-purple border to the top and left
var anotherStyle = lipgloss . NewStyle ().
BorderStyle ( lipgloss . RoundedBorder ()).
BorderForeground ( lipgloss . Color ( "228" )).
BorderBackground ( lipgloss . Color ( "63" )).
BorderTop ( true ).
BorderLeft ( true )
// Make your own border
var myCuteBorder = lipgloss. Border {
Top : "._.:*:" ,
Bottom : "._.:*:" ,
Left : "|*" ,
Right : "|*" ,
TopLeft : "*" ,
TopRight : "*" ,
BottomLeft : "*" ,
BottomRight : "*" ,
}
还有用于定义边框的简写函数,它们遵循与边距和填充简写函数类似的模式。
// Add a thick border to the top and bottom
lipgloss . NewStyle ().
Border ( lipgloss . ThickBorder (), true , false )
// Add a double border to the top and left sides. Rules are set clockwise
// from top.
lipgloss . NewStyle ().
Border ( lipgloss . DoubleBorder (), true , false , false , true )
有关边界的更多信息,请参阅文档。
只需使用赋值:
style := lipgloss . NewStyle (). Foreground ( lipgloss . Color ( "219" ))
copiedStyle := style // this is a true copy
wildStyle := style . Blink ( true ) // this is also true copy, with blink added
由于Style
数据结构仅包含原始类型,因此将一个样式分配给另一个样式可以有效地创建该样式的新副本,而不会改变原始样式。
样式可以继承其他样式的规则。继承时,仅继承接收者上未设置的规则。
var styleA = lipgloss . NewStyle ().
Foreground ( lipgloss . Color ( "229" )).
Background ( lipgloss . Color ( "63" ))
// Only the background color will be inherited here, because the foreground
// color will have been already set:
var styleB = lipgloss . NewStyle ().
Foreground ( lipgloss . Color ( "201" )).
Inherit ( styleA )
所有规则都可以取消设置:
var style = lipgloss . NewStyle ().
Bold ( true ). // make it bold
UnsetBold (). // jk don't make it bold
Background ( lipgloss . Color ( "227" )). // yellow background
UnsetBackground () // never mind
当规则被取消设置时,它不会被继承或复制。
有时,例如在开发组件时,您希望确保样式定义尊重其在 UI 中的预期用途。这就是Inline
、 MaxWidth
和MaxHeight
发挥作用的地方:
// Force rendering onto a single line, ignoring margins, padding, and borders.
someStyle . Inline ( true ). Render ( "yadda yadda" )
// Also limit rendering to five cells
someStyle . Inline ( true ). MaxWidth ( 5 ). Render ( "yadda yadda" )
// Limit rendering to a 5x5 cell block
someStyle . MaxWidth ( 5 ). MaxHeight ( 5 ). Render ( "yadda yadda" )
制表符 ( t
) 在不同终端中的呈现方式不同(通常为 8 个空格,有时为 4 个)。由于这种不一致,Lip Gloss 在渲染时将制表符转换为 4 个空格。但是,可以根据每种样式更改此行为:
style := lipgloss . NewStyle () // tabs will render as 4 spaces, the default
style = style . TabWidth ( 2 ) // render tabs as 2 spaces
style = style . TabWidth ( 0 ) // remove tabs entirely
style = style . TabWidth ( lipgloss . NoTabConversion ) // leave tabs intact
通常,您只需在lipgloss.Style
上调用Render(string...)
方法:
style := lipgloss . NewStyle (). Bold ( true ). SetString ( "Hello," )
fmt . Println ( style . Render ( "kitty." )) // Hello, kitty.
fmt . Println ( style . Render ( "puppy." )) // Hello, puppy.
但您也可以使用 Stringer 接口:
var style = lipgloss . NewStyle (). SetString ( "你好,猫咪。" ). Bold ( true )
fmt . Println ( style ) // 你好,猫咪。
自定义渲染器允许您渲染到特定的输出。当您想要渲染到不同的输出并正确检测每个输出的颜色配置文件和深色背景状态时(例如在服务器-客户端情况下),这一点尤其重要。
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" ))
}
有关通过 SSH 与 Wish 使用自定义渲染器的示例,请参阅 SSH 示例。
除了纯粹的造型之外,唇彩还附带一些实用程序来帮助您组装布局。
水平和垂直连接段落轻而易举。
// Horizontally join three paragraphs along their bottom edges
lipgloss . JoinHorizontal ( lipgloss . Bottom , paragraphA , paragraphB , paragraphC )
// Vertically join two paragraphs along their center axes
lipgloss . JoinVertical ( lipgloss . Center , paragraphA , paragraphB )
// Horizontally join three paragraphs, with the shorter ones aligning 20%
// from the top of the tallest
lipgloss . JoinHorizontal ( 0.2 , paragraphA , paragraphB , paragraphC )
有时,您在构建布局时会想知道文本块的宽度和高度。
// Render a block of text.
var style = lipgloss . NewStyle ().
Width ( 40 ).
Padding ( 2 )
var block string = style . Render ( someLongString )
// Get the actual, physical dimensions of the text block.
width := lipgloss . Width ( block )
height := lipgloss . Height ( block )
// Here's a shorthand function.
w , h := lipgloss . Size ( block )
有时您只是想在空白处放置一段文本。
// Center a paragraph horizontally in a space 80 cells wide. The height of
// the block returned will be as tall as the input paragraph.
block := lipgloss . PlaceHorizontal ( 80 , lipgloss . Center , fancyStyledParagraph )
// Place a paragraph at the bottom of a space 30 cells tall. The width of
// the text block returned will be as wide as the input paragraph.
block := lipgloss . PlaceVertical ( 30 , lipgloss . Bottom , fancyStyledParagraph )
// Place a paragraph in the bottom right corner of a 30x80 cell space.
block := lipgloss . Place ( 30 , 80 , lipgloss . Right , lipgloss . Bottom , fancyStyledParagraph )
您还可以设置空白的样式。有关详细信息,请参阅文档。
唇彩附带一个表格渲染子包。
import "github.com/charmbracelet/lipgloss/table"
定义一些数据行。
rows := [][] string {
{ "Chinese" , "您好" , "你好" },
{ "Japanese" , "こんにちは" , "やあ" },
{ "Arabic" , "أهلين" , "أهلا" },
{ "Russian" , "Здравствуйте" , "Привет" },
{ "Spanish" , "Hola" , "¿Qué tal?" },
}
使用 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?" )
打印表格。
fmt . Println ( t )
警告
Table Rows
需要在Offset
之前声明,否则什么也不做。
有关表格的更多信息,请参阅文档和示例。
唇彩附带一个列表渲染子包。
import "github.com/charmbracelet/lipgloss/list"
定义一个新列表。
l := list . New ( "A" , "B" , "C" )
打印列表。
fmt . Println ( l )
// • A
// • B
// • C
列表具有嵌套的能力。
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" ),
)
打印列表。
fmt . Println ( l )
列表可以通过其枚举函数以及使用lipgloss.Style
进行自定义。
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 )
打印列表。
除了预定义的枚举器( Arabic
、 Alphabet
、 Roman
、 Bullet
、 Tree
)之外,您还可以定义自己的自定义枚举器:
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 )
打印列表:
如果需要,您还可以逐步构建列表:
l := list . New ()
for i := 0 ; i < repeat ; i ++ {
l . Item ( "Lip Gloss" )
}
唇彩附带一个树渲染子包。
import "github.com/charmbracelet/lipgloss/tree"
定义一棵新树。
t := tree . Root ( "." ).
Child ( "A" , "B" , "C" )
打印树。
fmt . Println ( t )
// .
// ├── A
// ├── B
// └── C
树木有筑巢的能力。
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" ),
)
打印树。
fmt . Println ( t )
树可以通过其枚举函数以及使用lipgloss.Style
进行自定义。
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 )
打印树。
树的预定义枚举器是DefaultEnumerator
和RoundedEnumerator
。
如果需要,您还可以增量构建树:
t := tree . New ()
for i := 0 ; i < repeat ; i ++ {
t . Child ( "Lip Gloss" )
}
这很可能是由于您的区域设置和编码造成的,特别是中文、日文和韩文(例如zh_CN.UTF-8
或ja_JP.UTF-8
)。解决此问题的最直接方法是在您的环境中设置RUNEWIDTH_EASTASIAN=0
。
有关详细信息,请参阅 https://github.com/charmbracelet/lipgloss/issues/40。
Lip Gloss 会自动将颜色降级为给定终端中的最佳可用选项,如果输出不是 TTY,它将完全删除颜色输出。这在运行测试、CI 或在其他地方进行管道输出时很常见。
如有必要,您可以使用SetColorProfile
在测试中强制使用颜色配置文件。
import (
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
)
lipgloss . SetColorProfile ( termenv . TrueColor )
注意:此选项限制了应用程序的灵活性,并且可能会导致在不需要的情况下输出 ANSI 转义码。在选择强制使用颜色配置文件之前,请仔细记下您的用例和环境。
唇彩并不能取代珍珠奶茶。相反,它是绝佳的珍珠奶茶伴侣。它旨在使组装终端用户界面视图尽可能简单和有趣,以便您可以专注于构建应用程序,而不用担心底层布局细节。
简而言之,您可以使用唇彩来帮助构建您的珍珠奶茶视图。
Lip Gloss 建立在优秀的 Termenv 和 Reflow 库之上,它们分别处理颜色和 ANSI 感知文本操作。对于许多用例,Termenv 和 Reflow 足以满足您的需求。
要获得更加以文档为中心的渲染解决方案,并支持列表、表格和语法突出显示代码等内容,请查看 Glamour,它是基于样式表的 Markdown 渲染器。
请参阅贡献。
我们很想听听您对此项目的想法。请随时给我们留言!
麻省理工学院
魅力的一部分。
Charm热爱开源 • Charm热爱开源