Definiciones de estilo para diseños de terminales agradables. Construido pensando en las TUI.
Lip Gloss adopta un enfoque expresivo y declarativo para la renderización de terminales. Los usuarios familiarizados con CSS se sentirán como en casa con 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" ))
Lip Gloss admite los siguientes perfiles de color:
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
...así como un perfil ASCII de 1 bit, que es sólo blanco y negro.
El perfil de color del terminal se detectará automáticamente y los colores fuera de la gama de la paleta actual se forzarán automáticamente a su valor disponible más cercano.
También puede especificar opciones de color para fondos claros y oscuros:
lipgloss. AdaptiveColor { Light : "236" , Dark : "248" }
El color de fondo del terminal se detectará automáticamente y se elegirá el color apropiado en tiempo de ejecución.
CompleteColor especifica valores exactos para los perfiles de color True Color, ANSI256 y ANSI.
lipgloss. CompleteColor { TrueColor : "#0000FF" , ANSI256 : "86" , ANSI : "5" }
En este caso no se realizará la degradación automática del color y se basará en el color especificado.
Puede utilizar CompleteColor
con AdaptiveColor
para especificar los valores exactos para fondos claros y oscuros sin degradación automática del color.
lipgloss. CompleteAdaptiveColor {
Light : CompleteColor { TrueColor : "#d7ffae" , ANSI256 : "193" , ANSI : "11" },
Dark : CompleteColor { TrueColor : "#d75fee" , ANSI256 : "163" , ANSI : "5" },
}
Lip Gloss admite las opciones habituales de formato de texto ANSI:
var style = lipgloss . NewStyle ().
Bold ( true ).
Italic ( true ).
Faint ( true ).
Blink ( true ).
Strikethrough ( true ).
Underline ( true ).
Reverse ( true )
Lip Gloss también admite reglas para el formato a nivel de bloque:
// 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 )
También existe una sintaxis abreviada para márgenes y relleno, que sigue el mismo formato que 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 )
Puede alinear párrafos de texto a la izquierda, a la derecha o al centro.
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
Establecer un ancho y alto mínimos es simple y directo.
var style = lipgloss . NewStyle ().
SetString ( "What’s for lunch?" ).
Width ( 24 ).
Height ( 32 ).
Foreground ( lipgloss . Color ( "63" ))
Agregar bordes es fácil:
// 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 : "*" ,
}
También hay funciones abreviadas para definir bordes, que siguen un patrón similar a las funciones abreviadas de margen y relleno.
// 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 )
Para obtener más información sobre las fronteras, consulte los documentos.
Solo usa la tarea:
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
Dado que las estructuras de datos Style
contienen solo tipos primitivos, asignar un estilo a otro crea efectivamente una nueva copia del estilo sin mutar el original.
Los estilos pueden heredar reglas de otros estilos. Al heredar, solo se heredan las reglas no establecidas en el receptor.
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 )
Todas las reglas se pueden desarmar:
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
Cuando una regla no está establecida, no se heredará ni se copiará.
A veces, como cuando se desarrolla un componente, desea asegurarse de que las definiciones de estilo respeten el propósito previsto en la interfaz de usuario. Aquí es donde entran Inline
, MaxWidth
y 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" )
El carácter de tabulación ( t
) se representa de manera diferente en diferentes terminales (a menudo como 8 espacios, a veces 4). Debido a esta inconsistencia, Lip Gloss convierte las pestañas en 4 espacios en el momento del renderizado. Sin embargo, este comportamiento se puede cambiar según el estilo:
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
Generalmente, simplemente llamas al método Render(string...)
en un lipgloss.Style
:
style := lipgloss . NewStyle (). Bold ( true ). SetString ( "Hello," )
fmt . Println ( style . Render ( "kitty." )) // Hello, kitty.
fmt . Println ( style . Render ( "puppy." )) // Hello, puppy.
Pero también puedes usar la interfaz Stringer:
var style = lipgloss . NewStyle (). SetString ( "你好,猫咪。" ). Bold ( true )
fmt . Println ( style ) // 你好,猫咪。
Los renderizadores personalizados le permiten renderizar en salidas específicas. Esto es particularmente importante cuando desea renderizar en diferentes salidas y detectar correctamente el perfil de color y el estado del fondo oscuro para cada una, como en una situación de servidor-cliente.
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" ))
}
Para ver un ejemplo sobre el uso de un renderizador personalizado a través de SSH con Wish, consulte el ejemplo de SSH.
Además del estilo puro, Lip Gloss también viene con algunas utilidades para ayudar a armar sus diseños.
Unir párrafos horizontal y verticalmente es muy fácil.
// 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 )
A veces querrás saber el ancho y el alto de los bloques de texto al crear tus diseños.
// 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 )
A veces simplemente querrás colocar un bloque de texto en un espacio en blanco.
// 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 )
También puedes diseñar el espacio en blanco. Para más detalles, consulte los documentos.
Lip Gloss se envía con un subpaquete de representación de tablas.
import "github.com/charmbracelet/lipgloss/table"
Defina algunas filas de datos.
rows := [][] string {
{ "Chinese" , "您好" , "你好" },
{ "Japanese" , "こんにちは" , "やあ" },
{ "Arabic" , "أهلين" , "أهلا" },
{ "Russian" , "Здравствуйте" , "Привет" },
{ "Spanish" , "Hola" , "¿Qué tal?" },
}
Utilice el paquete de tabla para diseñar y representar la tabla.
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?" )
Imprime la tabla.
fmt . Println ( t )
Advertencia
Rows
de la tabla deben declararse antes Offset
; de lo contrario, no hace nada.
Para obtener más información sobre las tablas, consulte los documentos y los ejemplos.
Lip Gloss se envía con un subpaquete de representación de listas.
import "github.com/charmbracelet/lipgloss/list"
Definir una nueva lista.
l := list . New ( "A" , "B" , "C" )
Imprime la lista.
fmt . Println ( l )
// • A
// • B
// • C
Las listas tienen la capacidad de anidarse.
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" ),
)
Imprime la lista.
fmt . Println ( l )
Las listas se pueden personalizar a través de su función de enumeración, así como utilizando 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 )
Imprime la lista.
Además de los enumeradores predefinidos ( Arabic
, Alphabet
, Roman
, Bullet
, Tree
), también puede definir su propio enumerador personalizado:
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 )
Imprime la lista:
Si lo necesita, también puede crear listas de forma incremental:
l := list . New ()
for i := 0 ; i < repeat ; i ++ {
l . Item ( "Lip Gloss" )
}
Lip Gloss se envía con un subpaquete de representación de árboles.
import "github.com/charmbracelet/lipgloss/tree"
Definir un nuevo árbol.
t := tree . Root ( "." ).
Child ( "A" , "B" , "C" )
Imprime el árbol.
fmt . Println ( t )
// .
// ├── A
// ├── B
// └── C
Los árboles tienen la capacidad de anidar.
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" ),
)
Imprime el árbol.
fmt . Println ( t )
Los árboles se pueden personalizar mediante su función de enumeración, así como utilizando 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 )
Imprime el árbol.
Los enumeradores predefinidos para árboles son DefaultEnumerator
y RoundedEnumerator
.
Si es necesario, también puedes construir árboles de forma incremental:
t := tree . New ()
for i := 0 ; i < repeat ; i ++ {
t . Child ( "Lip Gloss" )
}
Lo más probable es que esto se deba a su configuración regional y codificación, particularmente con respecto al chino, japonés y coreano (por ejemplo, zh_CN.UTF-8
o ja_JP.UTF-8
). La forma más directa de solucionar este problema es establecer RUNEWIDTH_EASTASIAN=0
en su entorno.
Para obtener más detalles, consulte https://github.com/charmbracelet/lipgloss/issues/40.
Lip Gloss degrada automáticamente los colores a la mejor opción disponible en el terminal determinado y, si la salida no es un TTY, eliminará la salida de color por completo. Esto es común cuando se ejecutan pruebas, CI o cuando se canaliza la salida a otro lugar.
Si es necesario, puedes forzar un perfil de color en tus pruebas con SetColorProfile
.
import (
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
)
lipgloss . SetColorProfile ( termenv . TrueColor )
Nota: esta opción limita la flexibilidad de su aplicación y puede hacer que se generen códigos de escape ANSI en los casos en que eso no sea deseado. Tome nota de su caso de uso y entorno antes de optar por forzar un perfil de color.
Lip Gloss no reemplaza al Bubble Tea. Más bien, es un excelente compañero del Bubble Tea. Fue diseñado para hacer que el ensamblaje de vistas de la interfaz de usuario del terminal sea lo más simple y divertido posible para que usted pueda concentrarse en crear su aplicación en lugar de preocuparse por los detalles de diseño de bajo nivel.
En términos simples, puedes usar Lip Gloss para ayudarte a desarrollar tus vistas de Bubble Tea.
Lip Gloss se basa en las excelentes bibliotecas Termenv y Reflow que se ocupan del color y de las operaciones de texto compatibles con ANSI, respectivamente. Para muchos casos de uso, Termenv y Reflow serán suficientes para sus necesidades.
Para obtener una solución de renderizado más centrada en documentos con soporte para cosas como listas, tablas y código resaltado con sintaxis, eche un vistazo a Glamour, el renderizador Markdown basado en hojas de estilo.
Ver contribuir.
Nos encantaría escuchar tu opinión sobre este proyecto. ¡No dudes en enviarnos una nota!
MIT
Parte del encanto.
Charm热爱开源 • Charm ama el código abierto