
Beautiful Mermaid Ascii
- 41 installs
- 49 repo stars
- Updated February 11, 2026
- ratacat/claude-skills
Helps with ai & agent building tasks.
About
beautiful-mermaid-ascii is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- beautiful-mermaid-ascii
- AI & Agent Building
- AI-coding skill
Beautiful Mermaid Ascii by the numbers
- 41 all-time installs (skills.sh)
- Ranked #8,067 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ratacat/claude-skills --skill beautiful-mermaid-asciiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 41 |
|---|---|
| repo stars | ★ 49 |
| Last updated | February 11, 2026 |
| Repository | ratacat/claude-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Beautiful Mermaid ASCII Rendering
Use lukilabs/beautiful-mermaid (a JS library, not a CLI) to turn Mermaid diagrams into terminal-friendly ASCII/Unicode art.
Quick start
Render a Mermaid file:
skills/beautiful-mermaid-ascii/scripts/mermaid-ascii path/to/diagram.mmdInstall a clean mermaid-ascii command on your PATH (symlink into ~/.local/bin by default):
skills/beautiful-mermaid-ascii/scripts/install-mermaid-asciiRender from stdin:
cat path/to/diagram.mmd | skills/beautiful-mermaid-ascii/scripts/mermaid-asciiRender the first Mermaid fenced block from Markdown:
skills/beautiful-mermaid-ascii/scripts/mermaid-ascii --md README.mdSelect a different fenced block (1-based):
skills/beautiful-mermaid-ascii/scripts/mermaid-ascii --md README.md --block 2Installation approach (how this skill “deals with installing”)
scripts/mermaid-ascii auto-installs beautiful-mermaid into a writable cache directory (defaults to $XDG_CACHE_HOME/beautiful-mermaid-ascii, or /tmp/beautiful-mermaid-ascii) when needed, then runs the renderer.
If you want a “real” command on your PATH, prefer the symlink installer:
skills/beautiful-mermaid-ascii/scripts/install-mermaid-asciiYou can also install this folder as a local/global npm package (use a writable npm cache if your ~/.npm is not writable):
# from the repo root
NPM_CONFIG_CACHE=/tmp/npm-cache npm install -g --prefix ~/.local ./skills/beautiful-mermaid-asciiIf you already have beautiful-mermaid installed in the current project, run with:
skills/beautiful-mermaid-ascii/scripts/mermaid-ascii --pkg-dir . path/to/diagram.mmdTroubleshooting
- If installs fail due to permission errors in
~/.npmor~/Library/Caches, run with a writable cache directory: skills/beautiful-mermaid-ascii/scripts/mermaid-ascii --cache-dir /tmp/bm-cache ...- If output is empty, verify the Mermaid text is valid and starts with a diagram type (
flowchart,sequenceDiagram, etc.). - For multiple diagrams in Markdown, use
--listto enumerate fenced blocks and choose one with--block.
Bundled resources
skills/beautiful-mermaid-ascii/scripts/mermaid-ascii: Shell wrapper that ensures dependencies are available, then renders.skills/beautiful-mermaid-ascii/scripts/mermaid-ascii.mjs: Node CLI that extracts Mermaid (raw or from Markdown fences) and callsrenderMermaidAscii.skills/beautiful-mermaid-ascii/references/notes.md: Small notes about Mermaid inputs and common patterns.
{
"name": "beautiful-mermaid-ascii-cli",
"private": true,
"version": "0.0.0",
"description": "Render Mermaid diagrams as terminal-friendly ASCII/Unicode art using lukilabs/beautiful-mermaid.",
"bin": {
"mermaid-ascii": "scripts/mermaid-ascii"
}
}
Mermaid Cheat Sheet
Quick reference for common Mermaid patterns.
---
Flowchart (Quick Start)
flowchart LR
A[Start] --> B{Decision}
B -->|Yes| C[Do thing]
B -->|No| D[Skip]
C --> E[End]
D --> EDirections: TB TD BT LR RL
Shapes:
[rect] (rounded) ((circle)) {diamond} {{hexagon}}
[(cylinder)] [[subroutine]] ([stadium]) >asymmetric]Arrows:
--> Arrow
--- Line
-.-> Dotted arrow
==> Thick arrow
--o Circle end
--x Cross end
<--> BidirectionalWith text: A -->|label| B or A -- label --> B
---
Sequence Diagram (Quick Start)
sequenceDiagram
participant A as Alice
participant B as Bob
A->>B: Hello
B-->>A: Hi there
A->>+B: Request
B-->>-A: ResponseArrows:
-> Solid, no head
->> Solid with head
--> Dotted, no head
-->> Dotted with head
-x Solid with X
-) Async (open arrow)Activation: + activates, - deactivates
Blocks:
loop Label alt Condition opt Optional
... ... ...
end else end
...
end
par Action 1 critical Section
... option Fallback
and Action 2 ...
... end
end---
Class Diagram (Quick Start)
classDiagram
class Animal {
+String name
-int age
+speak()
#move(dist) int
}
Animal <|-- Dog
Animal <|-- CatVisibility: + public, - private, # protected, ~ package
Relationships:
<|-- Inheritance
*-- Composition
o-- Aggregation
--> Association
..> Dependency
..|> Realization---
State Diagram (Quick Start)
stateDiagram-v2
[*] --> Idle
Idle --> Running : start
Running --> Paused : pause
Paused --> Running : resume
Running --> [*] : finishSpecial states: [*] (start/end), <<choice>>, <<fork>>, <<join>>
Composite:
state Active {
[*] --> Running
Running --> Stopped
}---
ER Diagram (Quick Start)
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE_ITEM : contains
CUSTOMER {
int id PK
string name
}Cardinality:
|| Exactly one
o| Zero or one
|{ One or more
o{ Zero or moreRead: left to right. ||--o{ = one-to-many
---
Gantt (Quick Start)
gantt
title Project Plan
dateFormat YYYY-MM-DD
section Phase 1
Research :a1, 2024-01-01, 10d
Design :a2, after a1, 7d
section Phase 2
Build :a3, after a2, 14dTask states: :done, :active, :crit
---
Pie Chart
pie title Market Share
"Chrome" : 65
"Firefox" : 15
"Safari" : 12
"Other" : 8---
Mindmap
mindmap
root((Project))
Frontend
React
CSS
Backend
API
DatabaseShapes: [square] (rounded) ((circle)) {{hexagon}}
---
Timeline
timeline
title Company History
section Founding
2020 : Started
2021 : First product
section Growth
2023 : Series A
2024 : Expansion---
Quadrant Chart
quadrantChart
title Priority Matrix
x-axis Low Effort --> High Effort
y-axis Low Impact --> High Impact
quadrant-1 Do First
quadrant-2 Schedule
quadrant-3 Delegate
quadrant-4 Eliminate
Task A: [0.2, 0.8]
Task B: [0.7, 0.6]
Task C: [0.3, 0.3]---
Git Graph
gitGraph
commit
branch develop
checkout develop
commit
checkout main
merge develop
commit tag: "v1.0"---
User Journey
journey
title User Onboarding
section Sign Up
Visit site: 3: User
Create account: 4: User
section First Use
Complete tutorial: 5: User
Invite team: 2: User, AdminScore: 1 (frustrated) to 5 (happy)
---
Configuration
Theme (in frontmatter):
---
config:
theme: forest
---Theme (inline):
%%{init: {'theme': 'dark'}}%%Themes: default forest dark neutral base
---
Comments
%% This is a comment---
Common Patterns
Decision Tree
flowchart TD
A{Start} --> B{Question 1?}
B -->|Yes| C{Question 2?}
B -->|No| D[Result A]
C -->|Yes| E[Result B]
C -->|No| F[Result C]API Flow
sequenceDiagram
Client->>+Server: POST /api/data
Server->>DB: INSERT
DB-->>Server: OK
Server-->>-Client: 201 CreatedSystem Architecture
flowchart TB
subgraph Frontend
UI[Web App]
end
subgraph Backend
API[REST API]
Worker[Job Queue]
end
subgraph Data
DB[(PostgreSQL)]
Cache[(Redis)]
end
UI --> API
API --> DB
API --> Cache
API --> Worker
Worker --> DBState Machine
stateDiagram-v2
[*] --> Draft
Draft --> Review : submit
Review --> Published : approve
Review --> Draft : reject
Published --> Archived : archive
Archived --> [*]Mermaid Syntax Reference
Complete syntax reference for all Mermaid diagram types.
Diagram Types Overview
| Diagram Type | Keyword | Description |
|---|---|---|
| Flowchart | flowchart | Process flows, algorithms, workflows |
| Sequence | sequenceDiagram | Interactions between participants over time |
| Class | classDiagram | OOP class structures and relationships |
| State | stateDiagram-v2 | State machines and transitions |
| ER Diagram | erDiagram | Entity relationships and database schemas |
| Gantt | gantt | Project timelines and scheduling |
| Pie Chart | pie | Distribution/proportion visualization |
| Mindmap | mindmap | Hierarchical idea mapping |
| Timeline | timeline | Chronological events |
| Quadrant | quadrantChart | 2D categorization (priority matrices) |
| Git Graph | gitGraph | Branch and merge visualization |
| User Journey | journey | User experience mapping |
| Sankey | sankey | Flow quantities between nodes |
| XY Chart | xychart | Line/bar charts with axes |
| Block | block | Block diagrams |
| Kanban | kanban | Kanban boards |
| Architecture | architecture | System architecture |
---
1. Flowchart
Direction
flowchart TD %% Top-Down (default)
flowchart TB %% Top-Bottom (same as TD)
flowchart BT %% Bottom-Top
flowchart LR %% Left-Right
flowchart RL %% Right-LeftNode Shapes
| Shape | Syntax | Example |
|---|---|---|
| Rectangle | A[text] | A[Process] |
| Rounded | A(text) | A(Start) |
| Stadium | A([text]) | A([Terminal]) |
| Subroutine | A[[text]] | A[[Subroutine]] |
| Cylinder | A[(text)] | A[(Database)] |
| Circle | A((text)) | A((Event)) |
| Diamond | A{text} | A{Decision} |
| Hexagon | A{{text}} | A{{Preparation}} |
| Parallelogram | A[/text/] | A[/Input/] |
| Parallelogram Alt | A[\text\] | A[\Output\] |
| Trapezoid | A[/text\] | A[/Manual/] |
| Trapezoid Alt | A[\text/] | A[\Priority/] |
| Double Circle | A(((text))) | A(((Stop))) |
| Asymmetric | A>text] | A>Flag] |
Links/Arrows
| Type | Syntax | Description |
|---|---|---|
| Arrow | A --> B | Solid line with arrow |
| Open | A --- B | Solid line, no arrow |
| Text on arrow | `A --> | text |
| Text on link | A -- text --> B | Alternative label syntax |
| Dotted | A -.-> B | Dotted line with arrow |
| Dotted open | A -.- B | Dotted line, no arrow |
| Thick | A ==> B | Thick line with arrow |
| Thick open | A === B | Thick line, no arrow |
| Invisible | A ~~~ B | Hidden link (for layout) |
| Circle end | A --o B | Line with circle end |
| Cross end | A --x B | Line with X end |
| Multi-directional | A <--> B | Arrows both directions |
Link Length
Add extra dashes for longer links:
A --> B %% Normal
A ---> B %% Longer
A ----> B %% Even longerSubgraphs
flowchart TB
subgraph one[Title One]
A --> B
end
subgraph two[Title Two]
C --> D
end
one --> twoStyling
flowchart LR
A --> B --> C
%% Style individual nodes
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#f66,stroke-dasharray: 5 5
%% Define and apply classes
classDef green fill:#9f6,stroke:#333
classDef red fill:#f66,stroke:#333
class A green
class B,C redComments
%% This is a comment---
2. Sequence Diagram
Participants
sequenceDiagram
participant A as Alice
participant B as Bob
actor U as UserParticipant types:
participant- Box (default)actor- Stick figureboundary- System boundarycontrol- Control flowentity- Data entitydatabase- Cylindercollections- Stacked boxesqueue- Queue symbol
Messages
| Syntax | Description |
|---|---|
A->B: msg | Solid line, no arrow |
A-->B: msg | Dotted line, no arrow |
A->>B: msg | Solid line with arrowhead |
A-->>B: msg | Dotted line with arrowhead |
A-xB: msg | Solid line with X |
A--xB: msg | Dotted line with X |
A-)B: msg | Solid line with open arrow (async) |
A--)B: msg | Dotted line with open arrow (async) |
A<<->>B: msg | Bidirectional |
Activations
sequenceDiagram
Alice->>+John: Request %% + activates
John-->>-Alice: Response %% - deactivates
%% Or explicit
activate John
deactivate JohnNotes
Note right of Alice: Text here
Note left of Bob: Text here
Note over Alice: Text here
Note over Alice,Bob: Spans bothControl Flow
%% Loops
loop Every minute
Alice->>Bob: Ping
end
%% Conditionals
alt Success
Alice->>Bob: OK
else Failure
Alice->>Bob: Error
end
%% Optional
opt Extra processing
Bob->>Alice: Details
end
%% Parallel
par Alice to Bob
Alice->>Bob: Hello
and Alice to John
Alice->>John: Hello
end
%% Critical section
critical Establish connection
Service->>DB: Connect
option Timeout
Service->>Service: Retry
end
%% Break
break When error occurs
Service->>Client: Error
endHighlighting
rect rgb(200, 220, 255)
A->>B: Inside highlight
endAutonumber
sequenceDiagram
autonumber
Alice->>Bob: First (1)
Bob->>Alice: Second (2)---
3. Class Diagram
Basic Class
classDiagram
class Animal {
+String name
+int age
+makeSound()
+move(distance)
}Visibility Modifiers
| Symbol | Meaning |
|---|---|
+ | Public |
- | Private |
# | Protected |
~ | Package/Internal |
Method Return Types
class MyClass {
+getAge() int
+getName() String
+process(data) bool
}Relationships
| Type | Syntax | Description |
|---|---|---|
| Inheritance | `A <\ | -- B` |
| Composition | A *-- B | B is part of A (strong) |
| Aggregation | A o-- B | B is part of A (weak) |
| Association | A --> B | A uses B |
| Dependency | A ..> B | A depends on B |
| Realization | `A ..\ | > B` |
| Link | A -- B | Simple link |
Cardinality
classDiagram
Customer "1" --> "*" Order : places
Order "1" --> "1..*" LineItem : containsAnnotations
classDiagram
class Shape {
<<interface>>
+draw()
}
class Singleton {
<<singleton>>
}
class Utility {
<<abstract>>
}Namespaces
classDiagram
namespace Animals {
class Dog
class Cat
}---
4. State Diagram
Basic States
stateDiagram-v2
[*] --> Idle %% Start state
Idle --> Running : start
Running --> Idle : stop
Running --> [*] %% End stateState Descriptions
stateDiagram-v2
state "Waiting for input" as waiting
[*] --> waitingComposite States
stateDiagram-v2
[*] --> Active
state Active {
[*] --> Running
Running --> Paused : pause
Paused --> Running : resume
}
Active --> [*] : quitForks and Joins
stateDiagram-v2
state fork_state <<fork>>
state join_state <<join>>
[*] --> fork_state
fork_state --> State1
fork_state --> State2
State1 --> join_state
State2 --> join_state
join_state --> [*]Choice
stateDiagram-v2
state check <<choice>>
[*] --> check
check --> Success : valid
check --> Failure : invalidNotes
stateDiagram-v2
State1 : Description
note right of State1
Extended notes here
end note
note left of State2 : Short noteConcurrency
stateDiagram-v2
state Parallel {
[*] --> A
--
[*] --> B
}Direction
stateDiagram-v2
direction LR
[*] --> A --> B --> [*]---
5. ER Diagram
Basic Structure
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE_ITEM : contains
PRODUCT ||--o{ LINE_ITEM : "is in"Cardinality
| Left | Right | Meaning |
|---|---|---|
| `\ | o` | `o\ |
| `\ | \ | ` |
}o | o{ | Zero or more |
| `}\ | ` | `\ |
Full Syntax
<entity1> <relationship> <entity2> : <label>Entity Attributes
erDiagram
CUSTOMER {
int id PK
string name
string email UK
date created_at
}
ORDER {
int id PK
int customer_id FK
decimal total
date order_date
}Attribute modifiers:
PK- Primary KeyFK- Foreign KeyUK- Unique Key
---
6. Gantt Chart
Basic Structure
gantt
title Project Timeline
dateFormat YYYY-MM-DD
section Phase 1
Task 1 :a1, 2024-01-01, 30d
Task 2 :a2, after a1, 20d
section Phase 2
Task 3 :a3, after a2, 15dDate Formats
dateFormat YYYY-MM-DD
dateFormat DD-MM-YYYY
dateFormat MM-DD-YYYYTask Syntax
Task Name :id, start, duration
Task Name :id, start, end
Task Name :id, after otherId, durationTask States
Done task :done, t1, 2024-01-01, 10d
Active task :active, t2, 2024-01-11, 10d
Critical :crit, t3, 2024-01-21, 10d
Milestone :milestone, m1, 2024-02-01, 0dExcludes
gantt
excludes weekends
excludes 2024-01-15, 2024-01-16---
7. Pie Chart
pie title Favorite Pets
"Dogs" : 45
"Cats" : 35
"Fish" : 15
"Other" : 5Show Data
pie showData
"A" : 30
"B" : 70---
8. Mindmap
Basic Structure
mindmap
root((Central Topic))
Topic A
Subtopic 1
Subtopic 2
Topic B
Subtopic 3Node Shapes
mindmap
root
Square[Square]
Rounded(Rounded)
Circle((Circle))
Bang))Bang((
Cloud)Cloud(
Hexagon{{Hexagon}}Icons
mindmap
root((Main))
Topic::icon(fa fa-book)
Another::icon(fa fa-star)Classes
mindmap
root:::important
Normal
Special:::highlight---
9. Timeline
Basic Structure
timeline
title History of Events
section Early Period
2020 : Event A
: Event B
2021 : Event C
section Recent
2023 : Event D
2024 : Event E : Event F---
10. Quadrant Chart
quadrantChart
title Reach vs Engagement
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 Promote
quadrant-2 Review
quadrant-3 Eliminate
quadrant-4 Monitor
Campaign A: [0.3, 0.6]
Campaign B: [0.7, 0.8]
Campaign C: [0.2, 0.2]
Campaign D: [0.8, 0.3]Values are 0-1 coordinates. Quadrants numbered 1-4 starting top-right, going counter-clockwise.
---
11. Git Graph
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
branch feature
commit
checkout main
merge featureCommit Options
gitGraph
commit id: "abc123"
commit id: "Normal"
commit id: "Reverse" type: REVERSE
commit id: "Highlight" type: HIGHLIGHT
commit tag: "v1.0"---
12. User Journey
journey
title My Daily Routine
section Morning
Wake up: 5: Me
Coffee: 4: Me, Cat
Commute: 2: Me
section Work
Meetings: 3: Me, Team
Coding: 5: MeFormat: Task: score: actors where score is 1-5 (1=bad, 5=great).
---
Configuration
Frontmatter
---
title: My Diagram
config:
theme: forest
---
flowchart LR
A --> BThemes
defaultforestdarkneutralbase
Init Directive
%%{init: {'theme': 'forest', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
flowchart LR
A --> B---
Tips for ASCII Rendering
When using beautiful-mermaid for ASCII output:
1. Keep diagrams simple - Complex diagrams may not render well in ASCII 2. Use short labels - Long text may overflow or wrap unexpectedly 3. Prefer LR/TB directions - These render more predictably 4. Avoid nested subgraphs - Stick to single-level subgraphs 5. Test incrementally - Build complex diagrams piece by piece
Notes
Reference Documents
- [mermaid-syntax-reference.md](./mermaid-syntax-reference.md) - Complete syntax for all 17+ diagram types
- [mermaid-cheatsheet.md](./mermaid-cheatsheet.md) - Quick reference with copy-paste examples
Mermaid Inputs
- Mermaid diagrams are plain text that start with a diagram type keyword:
flowchart LR/flowchart TDsequenceDiagramclassDiagramstateDiagram-v2erDiagramganttpiemindmaptimelinequadrantChartgitGraphjourney- And more...
- If rendering a Markdown file, this skill only extracts triple-backtick blocks fenced with
mermaid:
flowchart LR A --> B
Tips for Terminal Previews
- Use a monospace font; Unicode box-drawing characters render best with a font that supports them well
- If diagrams look "squished", try increasing your terminal window width
- Keep diagrams simple for better ASCII rendering
- Use short labels to avoid overflow/wrapping
- Prefer LR (left-right) or TB (top-bottom) directions
- Avoid deeply nested subgraphs
- Test complex diagrams incrementally
Quick Examples
Flowchart
flowchart LR
A[Start] --> B{Decision}
B -->|Yes| C[Action]
B -->|No| D[End]Sequence
sequenceDiagram
Alice->>Bob: Hello
Bob-->>Alice: HiState
stateDiagram-v2
[*] --> Active
Active --> [*]#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(
cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1
pwd
)"
SKILL_DIR="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd)"
usage() {
cat <<'EOF'
Install a "clean" `mermaid-ascii` command into a bin directory on your PATH.
Usage:
install-mermaid-ascii [--prefix DIR]
Default prefix:
~/.local/bin
This creates/overwrites:
<prefix>/mermaid-ascii (a symlink to this skill's scripts/mermaid-ascii)
EOF
}
prefix="${HOME}/.local/bin"
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help) usage; exit 0 ;;
--prefix) prefix="${2:-}"; shift 2 ;;
*) echo "Unknown option: $1" >&2; usage; exit 2 ;;
esac
done
if [[ -z "${prefix}" ]]; then
echo "--prefix must not be empty" >&2
exit 2
fi
mkdir -p "$prefix"
target="$SKILL_DIR/scripts/mermaid-ascii"
link="$prefix/mermaid-ascii"
rm -f "$link"
ln -s "$target" "$link"
echo "Installed: $link -> $target"
echo 'If `mermaid-ascii` is not found, add this to your shell rc:'
echo " export PATH=\"$prefix:\$PATH\""
#!/usr/bin/env bash
set -euo pipefail
SOURCE="${BASH_SOURCE[0]}"
while [[ -h "$SOURCE" ]]; do
DIR="$(
cd "$(dirname "$SOURCE")" >/dev/null 2>&1
pwd
)"
SOURCE="$(readlink "$SOURCE")"
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
done
SCRIPT_DIR="$(
cd "$(dirname "$SOURCE")" >/dev/null 2>&1
pwd
)"
usage() {
cat <<'EOF'
Render Mermaid diagrams as terminal-friendly ASCII/Unicode art using lukilabs/beautiful-mermaid.
Usage:
mermaid-ascii [--md] [--block N] [--list] [--cache-dir DIR] [--pkg-dir DIR] [FILE]
Input:
- If FILE is provided, read from that file.
- Otherwise, read Mermaid text from stdin.
- With --md, the input is treated as Markdown and Mermaid is extracted from ```mermaid fenced blocks.
Options:
--md Parse input as Markdown and extract Mermaid fenced blocks.
--block N Render the Nth Mermaid fenced block (1-based). Default: 1.
--list List Mermaid fenced blocks (prints an index and first line), then exit.
--cache-dir DIR Writable cache dir used for npm cache + package install. Default: $XDG_CACHE_HOME/beautiful-mermaid-ascii (or /tmp/beautiful-mermaid-ascii)
--pkg-dir DIR Directory to resolve node_modules from. Default: <cache-dir>/pkg (auto-installed).
-h, --help Show help.
Examples:
mermaid-ascii diagram.mmd
cat diagram.mmd | mermaid-ascii
mermaid-ascii --md README.md
mermaid-ascii --md README.md --block 2
EOF
}
mode="raw"
block="1"
list="0"
cache_root="${XDG_CACHE_HOME:-${TMPDIR:-/tmp}}"
cache_dir="$cache_root/beautiful-mermaid-ascii"
pkg_dir=""
args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help) usage; exit 0 ;;
--md) mode="md"; shift ;;
--block) block="${2:-}"; shift 2 ;;
--list) list="1"; shift ;;
--cache-dir) cache_dir="${2:-}"; shift 2 ;;
--pkg-dir) pkg_dir="${2:-}"; shift 2 ;;
--) shift; args+=("$@"); break ;;
-*) echo "Unknown option: $1" >&2; usage; exit 2 ;;
*) args+=("$1"); shift ;;
esac
done
if [[ -z "$pkg_dir" ]]; then
pkg_dir="$cache_dir/pkg"
fi
mkdir -p "$cache_dir" "$pkg_dir"
# Ensure a stable base for Node resolution via createRequire(). npm doesn't always create this.
if [[ ! -f "$pkg_dir/package.json" ]]; then
cat >"$pkg_dir/package.json" <<'EOF'
{
"private": true
}
EOF
fi
# Install deps into a writable cache dir. This avoids failing in sandboxes that forbid writing to ~.
need_install="0"
if [[ ! -f "$pkg_dir/node_modules/beautiful-mermaid/package.json" ]]; then
need_install="1"
fi
if [[ "$need_install" == "1" ]]; then
if ! command -v npm >/dev/null 2>&1; then
echo "npm not found. Install Node.js (includes npm), or install beautiful-mermaid in your project and rerun with --pkg-dir ." >&2
exit 1
fi
npm_cache="$cache_dir/npm-cache"
mkdir -p "$npm_cache"
# Use --no-package-lock to keep the cache dir lightweight and avoid extra writes.
NPM_CONFIG_CACHE="$npm_cache" \
npm_config_cache="$npm_cache" \
npm_config_update_notifier=false \
npm_config_fund=false \
npm_config_audit=false \
npm_config_loglevel=error \
npm install --no-package-lock --prefix "$pkg_dir" beautiful-mermaid >/dev/null
fi
input_file=""
if [[ ${#args[@]} -ge 1 ]]; then
input_file="${args[0]}"
if [[ ! -f "$input_file" ]]; then
echo "Input file not found: $input_file" >&2
exit 1
fi
else
input_file="$(mktemp "$cache_dir/mermaid-input.XXXXXX")"
cat >"$input_file"
fi
node "$SCRIPT_DIR/mermaid-ascii.mjs" \
--pkg-dir "$pkg_dir" \
--mode "$mode" \
--block "$block" \
--list "$list" \
--input "$input_file"
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";
function usage() {
process.stdout.write(
[
"mermaid-ascii.mjs (internal)",
"",
"Options:",
" --pkg-dir DIR Directory to chdir into so Node can resolve beautiful-mermaid from DIR/node_modules",
" --mode raw|md Treat input as raw Mermaid or Markdown with ```mermaid fences",
" --block N 1-based mermaid fence index (when --mode md)",
" --list 0|1 List available mermaid fences and exit",
" --input PATH File containing input text",
"",
].join("\n"),
);
}
function parseArgs(argv) {
const out = {
pkgDir: "",
mode: "raw",
block: 1,
list: false,
input: "",
};
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === "--help" || a === "-h") {
usage();
process.exit(0);
}
if (a === "--pkg-dir") out.pkgDir = argv[++i] ?? "";
else if (a === "--mode") out.mode = argv[++i] ?? "raw";
else if (a === "--block") out.block = Number(argv[++i] ?? "1");
else if (a === "--list") out.list = (argv[++i] ?? "0") === "1";
else if (a === "--input") out.input = argv[++i] ?? "";
else {
process.stderr.write(`Unknown arg: ${a}\n`);
usage();
process.exit(2);
}
}
if (!out.pkgDir) {
process.stderr.write("--pkg-dir is required\n");
process.exit(2);
}
if (!out.input) {
process.stderr.write("--input is required\n");
process.exit(2);
}
if (!Number.isFinite(out.block) || out.block < 1) {
process.stderr.write("--block must be a positive integer\n");
process.exit(2);
}
if (out.mode !== "raw" && out.mode !== "md") {
process.stderr.write("--mode must be raw or md\n");
process.exit(2);
}
return out;
}
function extractMermaidFences(md) {
// Minimal Markdown fence parser:
// - Only supports triple-backtick fences
// - Only captures ```mermaid ... ```
// - Keeps inner text verbatim
const lines = md.split(/\r?\n/);
const blocks = [];
let inFence = false;
let buf = [];
for (const line of lines) {
if (!inFence) {
if (line.trim().toLowerCase() === "```mermaid") {
inFence = true;
buf = [];
}
continue;
}
if (line.trim() === "```") {
blocks.push(buf.join("\n").trimEnd());
inFence = false;
buf = [];
continue;
}
buf.push(line);
}
return blocks;
}
function firstNonEmptyLine(s) {
for (const line of s.split(/\r?\n/)) {
const t = line.trim();
if (t) return t;
}
return "";
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const pkgDirAbs = path.resolve(process.cwd(), args.pkgDir);
const requireFromPkg = createRequire(path.join(pkgDirAbs, "package.json"));
const raw = fs.readFileSync(args.input, "utf8");
const resolvedEntry = requireFromPkg.resolve("beautiful-mermaid");
const bm = await import(pathToFileURL(resolvedEntry).href);
const renderMermaidAscii = bm.renderMermaidAscii;
if (typeof renderMermaidAscii !== "function") {
throw new Error(
"beautiful-mermaid did not export renderMermaidAscii() as expected. Check installed version.",
);
}
if (args.mode === "md") {
const blocks = extractMermaidFences(raw);
if (args.list) {
if (blocks.length === 0) {
process.stdout.write("No ```mermaid fenced blocks found.\n");
return;
}
for (let i = 0; i < blocks.length; i++) {
process.stdout.write(`${i + 1}: ${firstNonEmptyLine(blocks[i])}\n`);
}
return;
}
const idx = args.block - 1;
if (!blocks[idx]) {
process.stderr.write(
`Requested block ${args.block}, but only found ${blocks.length} mermaid block(s). Use --list.\n`,
);
process.exit(1);
}
process.stdout.write(renderMermaidAscii(blocks[idx]) + "\n");
return;
}
if (args.list) {
process.stdout.write("--list is only meaningful with --mode md\n");
return;
}
process.stdout.write(renderMermaidAscii(raw) + "\n");
}
main().catch((err) => {
const msg = err && typeof err === "object" && "stack" in err ? err.stack : String(err);
process.stderr.write(`${msg}\n`);
process.exit(1);
});