mog
开源地址:https://github.com/thatstoasty/mog

漂亮的终端布局的样式定义。构建时考虑了 TUI。移植自/启发自:https://github.com/charmbracelet/lipgloss/tree/master
如果您是 Go 开发人员,请查看他们的 CLI 工具和库。他们无与伦比!
有关错误和待办事项,请参阅自述文件的底部。目前,不支持可打印长度大于 1 的字符。
您应该能够通过运行来构建包mojo package mog -I external
。对于最简单的使用方法,我建议将整个外部文件夹复制到存储库中,然后将该mog
文件夹也复制到外部文件夹中。
注意:似乎.mojopkg
文件不喜欢成为另一个包的一部分,例如。将所有外部部门放在一个external
或vendor
包中。我让 mojopkg 文件工作的唯一方法是与正在执行的文件位于同一目录中,并且该目录不能是 mojo 包。

Mog 采用富有表现力的声明性方法进行终端渲染。熟悉 CSS 的用户会对 Mog 感到宾至如归。
from mog import Style
var style = Style.new() \
.bold(True) \
.foreground(mog.Color("#FAFAFA")) \
.background(mog.Color("#7D56F4")) \
.padding_top(2) \
.padding_left(4) \
.width(22)
print(style.render("Hello, kitty"))
颜色
Mog 支持以下颜色配置文件:
ANSI 16 色(4 位)
mog.Color("5") # magenta
mog.Color("9") # red
mog.Color("12") # light blue
ANSI 256 色(8 位)
mog.Color("86") # aqua
mog.Color("201") # hot pink
mog.Color("202") # orange
真彩色(16,777,216 色;24 位)
mog.Color("#0000FF") # good ol' 100% blue
mog.Color("#04B575") # a green
mog.Color("#3C3C3C") # a dark gray
...以及 1 位 ASCII 配置文件,仅是黑白的。
终端的颜色配置文件很快就会被自动检测,当前调色板色域之外的颜色将自动强制为其最接近的可用值。
目前,图书馆采用深色背景。您可以通过修改样式的配置文件字段将其设置为浅色。
自适应颜色
您还可以指定浅色和深色背景的颜色选项:
mog.AdaptiveColor(light="236", dark="248")
将自动检测终端的背景颜色,并在运行时选择适当的颜色。
颜色齐全
CompleteColor 指定真彩色、ANSI256 和 ANSI 颜色配置文件的精确值。
mog.CompleteColor(true_color="#0000FF", ansi256="86", ansi="5")
在这种情况下,不会执行自动颜色降级,它将基于指定的颜色。
完整的自适应颜色
您可以将 CompleteColor 与 AdaptiveColor 结合使用来指定浅色和深色背景的精确值,而不会自动降低颜色。
mog.CompleteAdaptiveColor(
light = mog.CompleteColor(true_color: "#d7ffae", ansi256: "193", ansi: "11"),
dark = mog.CompleteColor(true_color: "#d75fee", ansi256: "163", ansi: "5"),
)
内联格式
Mog 支持常用的 ANSI 文本格式选项:
var style = Style.new() \
.bold(True) \
.italic(True) \
.faint(True) \
.blink(True) \
.crossout(True) \
.underline(True) \
.reverse(True)
块级格式化
Mog还支持块级格式化规则:
# Padding
var style = Style.new() \
.padding_top(2) \
.padding_right(4) \
.padding_bottom(2) \
.padding_left(4)
# Margins
var style = Style.new() \
.margin_top(2) \
.margin_right(4) \
.margin_bottom(2) \
.margin_left(4)
还有边距和填充的简写语法,其格式与 CSS 相同:
# 2 cells on all sides
Style.new().padding(2)
# 2 cells on the top and bottom, 4 cells on the left and right
Style.new().margin(2, 4)
# 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
Style.new().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
Style.new().margin(2, 4, 3, 1)
对齐文本
您可以将文本段落左对齐、右对齐或居中对齐。
var style = Style.new() \
.width(24) \
.align(position.left) \ # align it left
.align(position.right) \ # no wait, align it right
.align(position.center) # just kidding, align it in the center
宽度和高度
设置最小宽度和高度非常简单明了。
var style = Style.new() \
.set_string("What’s for lunch?") \
.width(24) \
.height(32) \
.foreground(mog.Color("63"))
边框
添加边框很简单:
# Add a purple, rectangular border
var style = Style.new() \
.border(normal_border()) \
.border_foreground(mog.Color("63"))
# Set a rounded, yellow-on-purple border to the top and left
var another_style = Style.new() \
.border(rounded_border()) \
.border_foreground(mog.Color("228")) \
.border_background(mog.Color("63")) \
.border_top(True) \
.border_left(True)
# Make your own border
var my_cute_border = Border(
top = "._.:*:",
bottom = "._.:*:",
left = "|*",
right = "|*",
top_left = "*",
top_right = "*",
bottom_left = "*",
bottom_right = "*",
)
还有用于定义边框的简写函数,它们遵循与边距和填充简写函数类似的模式。
# Add a thick border to the top and bottom
Style.new().border(thick_border(), True, False)
# Add a double border to the top and left sides. Rules are set clockwise
# from top.
Style.new().border(double_border(), True, False, False, True)
复制样式
只需使用copy()
:
var style = Style.new().foreground(mog.Color("219"))
var wild_style = style.copy().blink(True)
copy()
对底层数据结构执行复制,确保您获得样式的真实的、解除引用的副本。无需复制,就可以改变样式。
取消规则
所有规则都可以取消设置:
var style = Style.new() \
.bold(True) \ # make it bold
.unset_bold() \ # jk don't make it bold
.background(mog.Color("227")) \ # yellow background
.unset_background() # never mind
当规则被取消设置时,它不会被继承或复制。
执行规则
有时,例如在开发组件时,您希望确保样式定义尊重其在 UI 中的预期用途。这就是inline
和max_width
、 和max_height
出现的地方:
# Force rendering onto a single line, ignoring margins, padding, and borders.
some_style.inline(True).render("yadda yadda")
# Also limit rendering to five cells
some_style.inline(True).max_width(5).render("yadda yadda")
# Limit rendering to a 5x5 cell block
some_style.max_width(5).max_height(5).render("yadda yadda")
选项卡
制表符 ( \t
) 在不同终端中的呈现方式不同(通常为 8 个空格,有时为 4 个)。由于这种不一致,Mog 在渲染时将制表符转换为 4 个空格。但是,可以根据每种样式更改此行为:
style = Style.new() # tabs will render as 4 spaces, the default
style = style.tab_width(2) # render tabs as 2 spaces
style = style.tab_width(0) # remove tabs entirely
style = style.tab_width(mog.NO_TAB_CONVERSION) # leave tabs intact
渲染
一般来说,您只需调用render(string)
以下方法mog.Style
:
style = Style.new().bold(True).set_string("Hello,")
print(style.render("kitty.")) # Hello, kitty.
print(style.render("puppy.")) # Hello, puppy.
print(style.render("my", "puppy.")) # Hello, my puppy.
但您也可以使用 Stringer 接口:
var style = Style.new().set_string("你好,猫咪。").bold(True)
print(style) # 你好,猫咪。
自定义渲染器
自定义渲染器允许您渲染到特定的输出。当您想要渲染到不同的输出并正确检测每个输出的颜色配置文件和深色背景状态时(例如在服务器-客户端情况下),这一点尤其重要。
fn my_little_handler():
# Create a renderer for the client.
renderer = lipgloss.new_renderer()
# Create a new style on the renderer.
style = renderer.new_style().background(mog.AdaptiveColor(light="63", dark="228"))
# render. The color profile and dark background state will be correctly detected.
style.render("Heyyyyyyy")
公用事业
除了纯粹的样式之外,Mog 还附带了一些实用程序来帮助组装您的布局。
连接段落
水平和垂直连接段落轻而易举。
# Horizontally join three paragraphs along their bottom edges
join_horizontal(bottom, paragraph_a, paragraph_b, paragraph_c)
# Vertically join two paragraphs along their center axes
join_vertical(center, paragraph_a, paragraph_b)
# Horizontally join three paragraphs, with the shorter ones aligning 20%
# from the top of the tallest
join_horizontal(0.2, paragraph_a, paragraph_b, paragraph_c)
测量宽度和高度
有时,您在构建布局时会想知道文本块的宽度和高度。
# render a block of text.
var style = Style.new() \
.width(40) \
.padding(2)
var block string = style.render(some_long_string)
# Get the actual, physical dimensions of the text block.
width = mog.get_width(block)
height = mog.get_height(block)
# Here's a shorthand function.
var width = 0
var height = 0
width, height = mog.get_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 = place_horizontal(80, mog.center, fancy_styled_paragraph)
# 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 = place_vertical(30, mog.bottom, fancy_styled_paragraph)
# Place a paragraph in the bottom right corner of a 30x80 cell space.
block = place(30, 80, mog.right, mog.bottom, fancy_styled_paragraph)
渲染表
Mog 附带了一个表格渲染子包。
import mog.table
定义一些数据行。
rows = List[List[String]](
List[String]("Chinese", "您好", "你好"),
List[String]("Japanese", "こんにちは", "やあ"),
List[String]("Arabic", "أهلين", "أهلا"),
List[String]("Russian", "Здравствуйте", "Привет"),
List[String]("Spanish", "Hola", "¿Qué tal?"),
)
使用 table 包来设置表格的样式和呈现。
t = table.new_table().
.border(normal_border()) \
.border_style(Style.new().foreground(mog.Color("99"))) \
.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(t)
去做
- 分解样式将大型功能和大型类渲染为较小的功能和大型类。
- 弄清楚在表样式函数中捕获变量。使用转义和捕获崩溃,并且每次调用函数时创建样式都很慢。
- 代码无法处理可打印长度大于 1 的字符。
- 在 Ubuntu 测试容器中,renderer.place_vertical 渲染的空白宽度似乎太长。需要调查为什么会发生这种情况。可能是因为执行环境不一定是终端。
笔记
- ANSI256 对设置前景色和背景色的支持是有限的。可以同时设置两者,但前景色通常会被忽略。