box-of-rain
v1.1.2
Published
Generate beautiful ASCII box diagrams with borders, arrows, shadows, and nesting
Maintainers
Readme
box-of-rain
Quickstart
$ echo 'flowchart LR
web([Frontend])
subgraph platform [Cloud Platform]
api([API Server])
db[(Database)]
api --> db
end
web -->|HTTPS| api' | npx box-of-rain --mermaidGenerate ASCII box diagrams from JSON, YAML, or Mermaid. Supports nested boxes, arrow connections, auto-layout, multiple border styles, and shadows. Can output as plain text or SVG. CLI or programmatic.
This code is entirely AI generated. Be warned, take it for what you will. No promises.
It's inspired by turbopuffer's, planetscale's, and oxide computing's ascii diagrams. It's meant to be a simple and effective way to generate diagrams through a rote interface, largely self-layouting.
Of course, it's named as an homage to the inimitable Robert Hunter. Though he'd likely be disgusted, everything written below here is AI generated as well. But tiny, useful, isolated, and non-hosted libraries are probably the best use-case for AI.
Examples
These examples are SVGs because Github doesn't allow for customizing line-height of code blocks. If you put this on a website, you might want to set line-height: 1 to avoid clipping.
Install
pnpm install box-of-rainCLI Usage
npx box-of-rain --example # run the built-in example
npx box-of-rain diagram.json # render a diagram from JSON
npx box-of-rain diagram.yaml # render a diagram from YAML
npx box-of-rain diagram.mmd # render a diagram from Mermaid
npx box-of-rain --mermaid diagram.txt # force Mermaid parsing
npx box-of-rain --svg diagram.json # SVG outputStdin
You can pipe input via stdin instead of passing a file path. JSON is the default format; use --mermaid or --yaml to override.
cat diagram.json | npx box-of-rain
cat diagram.mmd | npx box-of-rain --mermaid
cat diagram.yaml | npx box-of-rain --yaml
echo '{"children":[{"id":"a","children":["Hello"]},{"id":"b","children":["World"]}],"connections":[{"from":"a","to":"b"}]}' | npx box-of-rainSchema
A diagram is a recursive tree of nodes. Each node can contain text or nested child nodes. Connections can be defined at any level.
{
"children": [
{
"id": "web",
"children": ["Frontend"],
"border": "rounded"
},
{
"id": "platform",
"title": "Cloud Platform",
"border": "double",
"shadow": true,
"children": [
{ "id": "api", "children": ["API Server"], "border": "bold" },
{ "id": "db", "children": ["Database"] }
],
"connections": [
{ "from": "api", "to": "db" }
]
}
],
"connections": [
{ "from": "web", "to": "api", "label": "HTTPS" }
]
}children is polymorphic
"children": "Hello"— single line of text"children": ["Line 1", "Line 2"]— multi-line text"children": [{ "id": "a", ... }]— nested child boxes (recursive)
Node properties
| Property | Type | Default | Description |
|------------------|-----------------------------|------------|------------------------------------------------|
| id | string | — | Unique identifier (required for connections) |
| children | string | string[] | Node[] | — | Text content or nested boxes |
| border | string | "single" | single, double, bold, rounded, or dashed |
| title | string | — | Text on the top border |
| shadow | boolean | false | Adds a ░ shadow on the right and bottom |
| disabled | boolean | false | Shade with ░ and strikethrough title |
| childDirection | string | "horizontal" | horizontal or vertical child layout |
| x, y | number | auto | Position (top-left corner) |
| width, height | number | auto | Box dimensions in characters |
| connections | Connection[] | — | Connections between child IDs at this level |
Connection properties
| Property | Type | Default | Description |
|------------|--------|-----------|----------------------------------------|
| from | string | — | Source box id |
| to | string | — | Target box id |
| label | string | — | Text label on the arrow |
| fromSide | string | auto | Which side the arrow exits from |
| toSide | string | auto | Which side the arrow enters |
Sides are right, left, top, or bottom. When omitted, sides are auto-detected based on relative box positions.
Mermaid Support
You can write diagrams in Mermaid syntax instead of JSON/YAML. Flowcharts and sequence diagrams are supported.
Flowchart
flowchart LR
subgraph home1[Your Home WiFi]:::shadow
iphone((Your iPhone))
robot((Your Robot))
end
subgraph cloud[China]:::shadow
server{Company Servers}
end
iphone -->|data| server
server -->|commands| robot
%% @route server-->robot toSide=rightSupported flowchart features:
Direction:
flowchart LR,RL,TD/TB,BT(alsograph)Node shapes map to border styles:
| Mermaid | Border | |---------|--------| |
A[text]|single| |A(text)/A([text])|rounded| |A[[text]]/A[(text)]|double| |A((text))/A{{text}}|bold| |A{text}|dashed|Edges:
-->,---,-.->,==>with optional|label|or-- label -->syntaxChained edges:
A --> B --> CSubgraphs:
subgraph id[Title] ... end, including nestingComments:
%%Semicolons:
A[One]; B[Two]on the same lineQuoted text:
A["Hello World"]Line breaks:
A[Line 1<br>Line 2]
Sequence diagrams
sequenceDiagram
participant A as Alice
participant B as Bob
participant C as Server
A->>B: Hello Bob
B->>C: Auth request
C-->>B: Auth response
B-->>A: Hi AliceSupported sequence features:
participantandactordeclarations (actors getroundedborders)- Aliases:
participant A as Alice - Message types:
->>,-->>,-x,--x,-),--) - Implicit participants from messages
Extensions
Two library-specific extensions are available using Mermaid-compatible syntax:
:::shadow — adds a shadow to a node or subgraph:
A[Server]:::shadow
subgraph cloud[Cloud]:::shadow%% @route — controls connection routing with fromSide/toSide:
%% @route server-->robot toSide=right
%% @route A-->B fromSide=bottom toSide=topArrow routing
Arrows are routed automatically based on the anchor positions:
- Straight — when source and target are on the same row
- L-shaped — horizontal, corner, vertical, corner, horizontal
- U-shaped — when
fromSideandtoSideare the same (e.g. both"right"), the arrow extends past all boxes, turns vertical, and comes back
Border styles
single: ┌──────┐ double: ╔══════╗ bold: ┏━━━━━━┓ rounded: ╭──────╮ dashed: ┌┄┄┄┄┄┄┐
│ │ ║ ║ ┃ ┃ │ │ ┆ ┆
└──────┘ ╚══════╝ ┗━━━━━━┛ ╰──────╯ └┄┄┄┄┄┄┘Auto-layout
When boxes don't have explicit x/y positions, the layout engine:
- Sizes each box to fit its content
- Lays out children inside their parent containers
- Assigns top-level boxes to horizontal layers based on connection flow
- Orders boxes within each layer to minimize edge crossings
- Handles cycles in the dependency graph
You can mix auto and manual positioning — set x/y/width/height on specific boxes and leave the rest to auto-layout.
Programmatic usage
import { render, renderSvg } from 'box-of-rain';
const diagram = {
children: [
{ id: 'a', children: ['Hello'], border: 'double' },
{ id: 'b', children: ['World'], border: 'bold' },
],
connections: [
{ from: 'a', to: 'b' },
],
};
console.log(render(diagram));
// Or as SVG:
const svg = renderSvg(render(diagram));From Mermaid
Mermaid support lives in a separate subpath export (box-of-rain/mermaid) so that the main entry point stays lightweight and doesn't pull in the chevrotain parser.
import { renderMermaid, parseMermaid } from 'box-of-rain/mermaid';
// One-step render:
console.log(renderMermaid(`
flowchart LR
A[Frontend] --> B[API] --> C[Database]
`));
// Or parse first, then render:
import { render } from 'box-of-rain';
const nodeDef = parseMermaid(`
sequenceDiagram
Alice->>Bob: Hello
Bob-->>Alice: Hi
`);
console.log(render(nodeDef));Input is validated at runtime with Zod. Invalid schemas throw a ZodError with details.
Development
pnpm install
pnpm test # run tests
pnpm typecheck # type-check
pnpm build # build to dist/More Examples
License
MIT
