If you're like me and completely obsessed with CLIs and Terminal UIs, this post is for you!
Unfortunately, there isn’t a native way to build beautiful Terminal UIs in JavaScript—at least none that I know of! This was a problem I ran into myself, which eventually led me to port one of the most stunning TUI libraries out there: Lipgloss, created by the folks at Charm.
Don’t believe me? Check this out:
Gorgeous, right?
Here’s the catch: Lipgloss is written in Go. While I usually work in Go, I recently needed to write a web monitoring tool in Node.js. I wasn’t willing to give up my sleek, beautiful UIs, so I found myself in a classic developer challenge mode.
You know those magical moments when you’re deep in code, and something unexpected just clicks? That’s how I ended up porting parts of Lipgloss to WebAssembly (Wasm). And that’s how charsm was born.
Charsm is short for Charm CLI Wasm. Cool, right? Let’s dive into how you can use it to build stunning TUIs in JavaScript.
Install charsm with a simple npm command:
npm install charsm
To get started, import charsm and initialize it in your script:
import { initLip, Lipgloss } from "charsm"; (async function () { const ini = await initLip(); })();
The initLip function loads the Wasm file, prepping everything for rendering. Let’s try printing a table:
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); })();
We can also use hex code for colors(check the link to a full example in the outro)
Result:
Simple, right? Now, let’s move on to rendering a list.
Currently, we can render a simple list. Here’s how it works:
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);
Customizing Selected Items
Let’s make it fancier by using a custom enumerator icon (e.g., ✅) for selected items:
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);
The selected items will display the ✅ icon.
Charsm wraps the Glamour library from Charm to handle markdown rendering:
npm install charsm
Think of styles in charsm as CSS for terminals. Here’s how you can create your own style:
import { initLip, Lipgloss } from "charsm"; (async function () { const ini = await initLip(); })();
To apply this style to text:
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); })();
consult the readme on github for more options or better yet here is a "full" example
Want a layout? Charsm supports simple flex-like layout:
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);
And there you have it! With charsm, you can render tables, lists, markdown, and even create custom styles—all within the terminal which by the way you can wrap around the list or markdown since it is text
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);
The table and list will be wrapped in a border, with padding and margins!
This is just the beginning. I’ll be adding interactive components (like forms) soon, so stay tuned. Have fun experimenting and building your own beautiful Terminal UIs in JavaScript!
Cheers!
The above is the detailed content of How To Build Beautiful Terminal UIs (TUIs) in JavaScript!. For more information, please follow other related articles on the PHP Chinese website!