如何用 JavaScript 构建漂亮的终端 UI(TUI)!

2025-06-07

如何用 JavaScript 构建漂亮的终端 UI(TUI)!

如果您像我一样,完全痴迷于 CLI 和终端 UI,那么这篇文章适合您!

不幸的是,JavaScript 中没有原生的方式可以构建漂亮的终端 UI——至少我所知道的没有!我自己也遇到了这个问题,最终促使我移植了最令人惊叹的 TUI 库之一:Lipgloss ,由Charm的团队创建

不相信吗?看看这个:

Charm Cli Ligloss

很漂亮吧?

问题在于:Lipgloss 是用 Go 编写的。虽然我通常使用 Go 工作,但最近需要用 Node.js 编写一个 Web 监控工具。我不愿意放弃自己流畅美观的 UI,所以我发现自己陷入了典型的开发者挑战模式

你知道那种当你深入代码时,意想不到的事情突然灵光一闪的神奇时刻吗?正因如此,我最终将 Lipgloss 的部分代码移植到了 WebAssembly(Wasm)。charsm 也由此诞生

魅力是什么?

Charsm 是Charm CLI + Wasm的缩写。很酷吧?让我们深入研究如何使用它在 JavaScript 中构建令人惊叹的 TUI。


入门

使用简单的 npm 命令安装 charsm:

npm install charsm
Enter fullscreen mode Exit fullscreen mode

创建一个简单的表

首先,charsm在脚本中导入并初始化它:

import { initLip, Lipgloss } from "charsm";

(async function () {
  const ini = await initLip();
})();
Enter fullscreen mode Exit fullscreen mode

initLip函数加载 Wasm 文件,为渲染做好一切准备。让我们尝试打印一个表格:

const rows = [
  ["Chinese", "您好", "你好"],
  ["Japanese", "こんにちは", "やあ"],
  ["Arabic", "أهلين", "أهلا"],
  ["Russian", "Здравствуйте", "Привет"],
  ["Spanish", "Hola", "¿Qué tal?"],
];

const tabledata = { 
  headers: ["LANGUAGE", "FORMAL", "INFORMAL"], 
  rows: rows 
};

(async function () {
  const ini = await initLip();
  const lip = new Lipgloss();

  const table = lip.newTable({
    data: tabledata,
    table: { border: "rounded", color: "99", width: 100 },
    header: { color: "212", bold: true },
    rows: { even: { color: "246" } },
  });

  console.log(table);
})();
Enter fullscreen mode Exit fullscreen mode

我们还可以使用十六进制代码来表示颜色(请查看结尾中的完整示例链接)

结果:

桌子

很简单吧?现在,我们来渲染列表。


渲染列表

目前,我们可以渲染一个简单的列表。它的工作原理如下:

const subtle = { Light: "#D9DCCF", Dark: "#383838" };
const special = { Light: "#43BF6D", Dark: "#73F59F" };

const list = lip.List({
  data: ["Grapefruit", "Yuzu", "Citron", "Pomelo", "Kumquat"],
  selected: [],
  listStyle: "alphabet",
  styles: {
    numeratorColor: special.Dark,
    itemColor: subtle.Dark,
    marginRight: 1,
  },
});
const combined = table + "\n\n" + list
console.log(combined);
Enter fullscreen mode Exit fullscreen mode

图片描述

自定义选定项目

让我们通过为选定项目使用自定义枚举器图标(例如✅)使其更加美观:

const customList = lip.List({
  data: ["Grapefruit", "Yuzu", "Citron", "Pomelo", "Kumquat"],
  selected: ["Grapefruit", "Yuzu"],
  listStyle: "custom",
  customEnum: "",
  styles: {
    numeratorColor: special.Dark,
    itemColor: subtle.Dark,
    marginRight: 1,
  },
});

console.log(customList);
Enter fullscreen mode Exit fullscreen mode

选定的项目将显示✅图标。

图片描述


渲染 Markdown

Charsm 包装了Charm 中的Glamour库来处理 markdown 渲染:

const content = `
# Today’s Menu

## Appetizers
| Name        | Price | Notes                           |
| ----------- | ----- | ------------------------------- |
| Tsukemono   | $2    | Just an appetizer               |
| Tomato Soup | $4    | Made with San Marzano tomatoes  |

## Desserts
| Name         | Price | Notes                 |
| ------------ | ----- | --------------------- |
| Dorayaki     | $4    | Looks good on rabbits |
| Cream Puff   | $3    | Pretty creamy!        |

Enjoy your meal!
`;

console.log(lip.RenderMD(content, "tokyo-night"));
Enter fullscreen mode Exit fullscreen mode

自定义样式

可以将 Charsm 中的样式视为终端的 CSS。您可以按照以下步骤创建自己的样式:

  lip.createStyle({
    id: "primary",
    canvasColor: { color: "#7D56F4" },
    border: { type: "rounded", sides: [true] },
    // for both margin and padding top, right, bottom, left
    padding: [6, 8, 6, 8],
    margin: [0, 8, 8, 8],
    bold: true,
    // align: 'center',
    width: 10,
    height: 12,

  });
 lip.createStyle({
    id: "secondary",
    canvasColor: { color: "#7D56F4" },
    border: { type: "rounded", background: "#0056b3", sides: [true, false]},
    padding: [6, 8, 6, 8],
    margin: [0, 0, 8, 1],
    bold: true,

    alignV: "bottom",
    width: 10,
    height: 12,

  });

Enter fullscreen mode Exit fullscreen mode

要将此样式应用于文本:

const styledText = lip.apply({ value: "Hello, charsm!", id: "primary" });
console.log(styledText);
Enter fullscreen mode Exit fullscreen mode

请参阅github 上的自述文件以了解更多选项,或者更好的是,这里有一个“完整”的例子

想要布局吗?Charsm 支持简单的类似 flex 的布局:


  const a = lip.apply({ value: "Charsmmm", id: "secondary" })

  const b = lip.apply({ value: "🔥🦾🍕", id: "primary" })
  const c = lip.apply({ value: 'Charsmmm', id: "secondary" })

const res = lip.join({
  direction: "horizontal",
  elements: [a, b, c],
  position: "left",
});

console.log(res);
Enter fullscreen mode Exit fullscreen mode

总结

就这样!有了 Charsm,你可以渲染表格、列表、Markdown,甚至创建自定义样式——所有这些都可以在终端内完成。顺便说一下,由于列表或 Markdown 是文本,所以你可以将其换行。

  console.log(lip.apply({value: combined, id: "primary"}))
Enter fullscreen mode Exit fullscreen mode

表格和列表将被包裹在边框中,并带有填充和边距!

包装列表和表格

这仅仅是个开始。我很快会添加一些交互式组件(比如表单),敬请期待。祝您使用 JavaScript 构建自己精美的终端 UI 充满乐趣!

您可以在substack上找到我,阅读更短的个人文章,以及x

你可以在x上找到我

再见!🫡

文章来源:https://dev.to/sfundomhlungu/how-to-build-beautiful-terminal-uis-tuis-in-javascript-74j
PREV
适用于你的下一个 React 项目的 10 个 UI 框架
NEXT
如何轻松更改 VSCode 中的终端样式