Boxes
Draw a box as a string with box(), or hoist boxComponent() so a pipe gets "title: text" instead of a border.
flagstaff/box has two exports: box(), which returns the drawing as a string, and
boxComponent(), which hoists on the loop like any other component. Most callers want the
string.
box(text, options)
import { box } from 'flagstaff/box';
console.log(box('Ready on :3000', { title: 'dev', width: 30 }));
console.log(box('Ready on :3000', { border: 'double', padding: { x: 2, y: 1 }, width: 30 }));╭─ dev ──────────────────────╮
│ Ready on :3000 │
╰────────────────────────────╯
╔════════════════════════════╗
║ ║
║ Ready on :3000 ║
║ ║
╚════════════════════════════╝| option | default | |
|---|---|---|
border | round | a registered border's name, or a BorderStyle object of your own |
padding | { x: 1, y: 0 } | cells left and right of the text, rows above and below |
title | — | written into the top border, cut with … when the box is too narrow |
width | 80 | columns for the whole box, borders included; the text wraps to fit |
href | — | a URL, or a file:// path, that the text links to |
terminal | the real process | the terminal href is rendered for; pass one to make the call pure |
The built-in borders are round, single, double, bold, classic and none, registered
through the same register() a plugin uses. cli-boxes' eight sets are not bundled; if you
depend on cli-boxes, fromCliBoxes() turns it into a
plugin, after which box('…', { border: 'arrow' }) draws with one. A name nobody registered is
refused with E_UNKNOWN_BORDER and the names that exist.
Width is measured, not counted
Every row is measured in terminal columns with linegauge,
so a double-width character takes two columns and the border stays in line. A box with a CJK
body fits exactly the width it was given at 10, 16, 24 and 40 columns — that is a test,
builtins.test.ts.
There is no layout engine: box measures, wraps and joins strings.
Links
With href, the text becomes an OSC 8 hyperlink on a terminal that supports one, through
paratext. Where the link cannot be emitted — and in
every static projection — the destination is written out after the text instead, so it is not
lost:
import { box } from 'flagstaff/box';
const pipe = { env: {}, isTTY: { stdout: false } };
console.log(box('docs', { width: 20, href: 'https://example.com', terminal: pipe }));╭──────────────────╮
│ docs (https://ex │
│ ample.com) │
╰──────────────────╯boxComponent(): a box with a text form
A box printed with console.log is a drawing everywhere, including in a log file and in front
of a screen reader. Hoisted as a component, the drawing is only for a terminal; everywhere
else the box is its title and its text:
import { boxComponent } from 'flagstaff/box';
import { hoist } from 'flagstaff/loop';
import { json, rt } from './rt.mjs';
hoist(boxComponent({ width: 40 }), rt, { title: 'deploy', text: 'Live at https://example.com' }, { json }).lower();deploy: Live at https://example.comThe state is { text, title?, href? }; an href in the state overrides the component's.
Coming from boxen
flagstaff/boxen is boxen 8's API, graded by boxen's own test suite, every case of which is a
snapshot of the exact characters. It carries cli-boxes' table itself rather than reading the
plugin registry, so registering a plugin never changes what a boxen caller draws. See
Coming from boxen.