merminal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/docs/adr/000-template.md +24 -0
- data/docs/adr/001-runtime.md +19 -0
- data/docs/adr/002-scene.md +18 -0
- data/docs/adr/003-layout.md +20 -0
- data/docs/adr/004-parser.md +19 -0
- data/docs/adr/005-public-api.md +23 -0
- data/docs/adr/README.md +10 -0
- data/docs/syntax/additional.md +58 -0
- data/docs/syntax/class.md +3 -0
- data/docs/syntax/er.md +3 -0
- data/docs/syntax/flowchart.md +16 -0
- data/docs/syntax/gantt.md +3 -0
- data/docs/syntax/mindmap.md +3 -0
- data/docs/syntax/pie.md +3 -0
- data/docs/syntax/sequence.md +14 -0
- data/docs/syntax/state.md +3 -0
- data/docs/syntax/timeline.md +3 -0
- data/docs/syntax/xychart.md +3 -0
- data/exe/merminal +7 -0
- data/lib/merminal/cli.rb +94 -0
- data/lib/merminal/diagrams/additional.rb +257 -0
- data/lib/merminal/diagrams/additional_layouts.rb +1274 -0
- data/lib/merminal/diagrams/base.rb +65 -0
- data/lib/merminal/diagrams/gantt.rb +109 -0
- data/lib/merminal/diagrams/mindmap.rb +94 -0
- data/lib/merminal/diagrams/pie.rb +64 -0
- data/lib/merminal/diagrams/sequence.rb +186 -0
- data/lib/merminal/diagrams/structure.rb +316 -0
- data/lib/merminal/diagrams/timeline.rb +60 -0
- data/lib/merminal/diagrams/xychart.rb +152 -0
- data/lib/merminal/flowchart/layout.rb +734 -0
- data/lib/merminal/flowchart.rb +279 -0
- data/lib/merminal/markdown.rb +37 -0
- data/lib/merminal/output.rb +93 -0
- data/lib/merminal/raster/box_drawing_table.rb +6 -0
- data/lib/merminal/raster.rb +175 -0
- data/lib/merminal/scene.rb +37 -0
- data/lib/merminal/shareable.rb +9 -0
- data/lib/merminal/source.rb +78 -0
- data/lib/merminal/text/east_asian_width_table.rb +8 -0
- data/lib/merminal/text.rb +110 -0
- data/lib/merminal/version.rb +5 -0
- data/lib/merminal.rb +122 -0
- data/sig/merminal.rbs +59 -0
- metadata +88 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: cbe165ab1447fb74478d15f529f288a7693359b49df68e981ace194c9a4d543f
|
|
4
|
+
data.tar.gz: d77e448ca8c2b4300c5468040c259ff7aeaf48b190ba35c66bac413feef88594
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: cd06fee96beec91b0eaee71c387811fac366844213f2e4fa635840b31d2986c27a8f12762410e27d48878867e936f52fb1a83a374d70272b805dca3e0bc195f0
|
|
7
|
+
data.tar.gz: 65b2310ad2a6d7576fb3bc9be2f4748a1db423f0f55e7bc07e2e73f9947d62b136ac569d201dca385b16ef8d006df323d1774c808a51c17f78d0e647a0a8fe93
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# merminal
|
|
2
|
+
|
|
3
|
+
merminal is a pure Ruby terminal renderer for Mermaid diagrams. It runs without Node.js, a browser, external processes, or runtime gem dependencies. The Ruby API namespace is `Merminal`.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
From this checkout:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
gem build merminal.gemspec
|
|
11
|
+
gem install ./merminal-0.1.0.gem
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Ruby 3.3 or newer is required.
|
|
15
|
+
|
|
16
|
+
## CLI
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
printf 'flowchart LR\nA[Start] --> B[Done]\n' | merminal
|
|
20
|
+
merminal --ascii diagram.mmd
|
|
21
|
+
merminal --check --strict diagram.mmd
|
|
22
|
+
merminal --markdown README.md
|
|
23
|
+
merminal --width 80 --theme solarized --color always diagram.mmd
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`merminal --help` lists every option. When a requested width cannot hold the diagram, the CLI reports the actual width and keeps the full drawing.
|
|
27
|
+
|
|
28
|
+
## Ruby API
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require "merminal"
|
|
32
|
+
|
|
33
|
+
puts Merminal.render("flowchart LR\nA --> B")
|
|
34
|
+
document = Merminal.parse("pie\n\"A\" : 3\n\"B\" : 7")
|
|
35
|
+
warn document.diagnostics.map(&:to_s).join("\n")
|
|
36
|
+
puts document.render(charset: :ascii)
|
|
37
|
+
|
|
38
|
+
Merminal.markdown_blocks("```mermaid\ngraph LR\nA-->B\n```").each do |block|
|
|
39
|
+
puts block.line, block.render
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`Merminal.register` accepts a plugin with `diagram_type`, `keywords`, `parse(source)`, and `layout(ast, **options)` methods. `Document#scene` exposes immutable drawing primitives. The Scene and plugin interfaces are experimental.
|
|
44
|
+
|
|
45
|
+
## Supported diagrams
|
|
46
|
+
|
|
47
|
+
The parser accepts the Mermaid diagram declarations listed below. The core types
|
|
48
|
+
have dedicated parsers and layouts; the additional syntax types also have
|
|
49
|
+
diagram-specific semantic handling and terminal layouts. Core parser errors produce
|
|
50
|
+
line numbered diagnostics; additional syntax follows the documented subset and ignores
|
|
51
|
+
browser-only directives that have no terminal representation.
|
|
52
|
+
|
|
53
|
+
| Diagram | Syntax |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Flowchart | [Flowchart](docs/syntax/flowchart.md) |
|
|
56
|
+
| Sequence | [Sequence](docs/syntax/sequence.md) |
|
|
57
|
+
| State | [State](docs/syntax/state.md) |
|
|
58
|
+
| Class | [Class](docs/syntax/class.md) |
|
|
59
|
+
| ER | [ER](docs/syntax/er.md) |
|
|
60
|
+
| Pie | [Pie](docs/syntax/pie.md) |
|
|
61
|
+
| XY chart | [XY chart](docs/syntax/xychart.md) |
|
|
62
|
+
| Gantt | [Gantt](docs/syntax/gantt.md) |
|
|
63
|
+
| Timeline | [Timeline](docs/syntax/timeline.md) |
|
|
64
|
+
| Mindmap | [Mindmap](docs/syntax/mindmap.md) |
|
|
65
|
+
| Additional syntax diagrams | [Use case, requirement, journey, GitGraph, C4, quadrant, Sankey, block, packet, Kanban, architecture, radar, treemap, Venn, Ishikawa, Wardley, TreeView, Cynefin, swimlane, Event Modeling, Agentflow, ZenUML, Railroad syntax diagrams](docs/syntax/additional.md) |
|
|
66
|
+
|
|
67
|
+
The [gallery](gallery.html) shows Unicode, ASCII, and the four color themes for every fixture.
|
|
68
|
+
|
|
69
|
+
Pie charts appear as horizontal bars because cell based terminals communicate proportions more clearly that way. Unicode ambiguous width defaults to one cell; set `ambiguous_width: 2` for terminals that use two. Emoji ZWJ sequences use the first grapheme code point's width, so some terminal fonts may differ. ASCII mode replaces non ASCII labels with `?`.
|
|
70
|
+
|
|
71
|
+
This is an independent implementation. Thanks to [termaid](https://github.com/fasouto/termaid), [mermaid-ascii](https://github.com/AlexanderGrooff/mermaid-ascii), and [beautiful-mermaid](https://github.com/lukilabs/beautiful-mermaid) for showing what terminal Mermaid tools can offer. Mermaid syntax belongs to the [Mermaid project](https://mermaid.js.org/).
|
|
72
|
+
|
|
73
|
+
## Known limitations
|
|
74
|
+
|
|
75
|
+
Self loops use outside lanes, and dense edge labels can still cross lines. [The structural layout decision](docs/adr/003-layout.md) and [scene boundary](docs/adr/002-scene.md) describe the architecture. Scene and plugin interfaces are experimental.
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
bundle install
|
|
81
|
+
bundle exec rake spec
|
|
82
|
+
bundle exec rake gallery
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`rake unicode:generate` accepts `EAW_FILE` and `UCD_FILE` paths to the official Unicode 17.0 files. It regenerates the checked in width and box drawing tables.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ADR NNN: Implementation decision title
|
|
2
|
+
|
|
3
|
+
- Status: Proposed
|
|
4
|
+
- Date: YYYY-MM-DD
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Describe the concrete implementation question and its compatibility, data,
|
|
9
|
+
runtime, or component constraints. Operational workflow and release policy do
|
|
10
|
+
not belong in this directory; see [the scope](README.md).
|
|
11
|
+
|
|
12
|
+
Name the credible alternatives. If there was no meaningful alternative, record
|
|
13
|
+
the behavior in reference documentation instead of creating an ADR.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
Describe the durable boundary or architecture choice. Leave command syntax,
|
|
18
|
+
field-by-field formats, test evidence, benchmark results, and migration steps
|
|
19
|
+
in their authoritative documents.
|
|
20
|
+
|
|
21
|
+
## Consequences
|
|
22
|
+
|
|
23
|
+
Describe the important positive and negative trade-offs, including what would
|
|
24
|
+
make this decision worth revisiting.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ADR 001: Runtime baseline
|
|
2
|
+
|
|
3
|
+
- Status: Accepted
|
|
4
|
+
- Date: 2026-09-21
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Terminal use should work in Ruby environments with limited package access. The
|
|
9
|
+
implementation uses current Ruby language features and its standard library.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Require CRuby 3.3 or newer and keep runtime gem dependencies at zero.
|
|
14
|
+
|
|
15
|
+
## Consequences
|
|
16
|
+
|
|
17
|
+
Users install no additional runtime gems. The implementation stays small by
|
|
18
|
+
targeting one maintained Ruby baseline; other Ruby implementations are tested
|
|
19
|
+
as best effort.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# ADR 002: Scene layer
|
|
2
|
+
|
|
3
|
+
- Status: Accepted
|
|
4
|
+
- Date: 2026-09-21
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Diagram layout should not choose terminal border characters. Letting each
|
|
9
|
+
diagram plugin render terminal cells would duplicate rasterization behavior.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Pass immutable geometric Scene primitives to one shared rasterizer.
|
|
14
|
+
|
|
15
|
+
## Consequences
|
|
16
|
+
|
|
17
|
+
All diagrams share line joining, themes, width fitting, and ASCII fallback.
|
|
18
|
+
Plugins remain independent of terminal cell representation.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# ADR 003: Structural diagram layout
|
|
2
|
+
|
|
3
|
+
- Status: Accepted
|
|
4
|
+
- Date: 2026-09-21
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Flow, state, class, and ER diagrams need predictable placement and routing. A
|
|
9
|
+
shared layout model avoids a separate strategy for each structural diagram.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Use a deterministic layered layout with rank channels. Reserve space for long
|
|
14
|
+
edges and labels before routing, keep nested subgraphs together, and route loops
|
|
15
|
+
through outside lanes.
|
|
16
|
+
|
|
17
|
+
## Consequences
|
|
18
|
+
|
|
19
|
+
Structural diagrams share predictable placement and routing. The exact sweep,
|
|
20
|
+
crossing, and rasterization algorithms remain implementation details.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ADR 004: Parser diagnostics
|
|
2
|
+
|
|
3
|
+
- Status: Accepted
|
|
4
|
+
- Date: 2026-09-21
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Real Mermaid files may contain unsupported statements. Rejecting an entire
|
|
9
|
+
document would hide the parts that can still be rendered.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Collect line-numbered diagnostics and render recognized statements. Raise in
|
|
14
|
+
strict mode so callers can choose partial or validated parsing.
|
|
15
|
+
|
|
16
|
+
## Consequences
|
|
17
|
+
|
|
18
|
+
Partial diagrams remain useful while CI can enforce strict parsing. Syntax
|
|
19
|
+
reference documents define the accepted subset.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# ADR 005: Public API boundary
|
|
2
|
+
|
|
3
|
+
- Status: Accepted
|
|
4
|
+
- Date: 2026-09-21
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The documented render and parse calls need a stable contract. Scene primitives
|
|
9
|
+
and plugin parsing methods expose layout internals that may change as more
|
|
10
|
+
syntax is supported.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
The intended stable v1.0 API is the render, parse, Markdown extraction, diagram
|
|
15
|
+
registration, and diagram type calls documented in the README and RBS. Their
|
|
16
|
+
document and block result fields are stable as documented.
|
|
17
|
+
|
|
18
|
+
Keep `Document#scene`, Scene primitives, and the plugin interface experimental at v1.0. Their names are available for advanced use, but their shape may change in later minor versions.
|
|
19
|
+
|
|
20
|
+
## Consequences
|
|
21
|
+
|
|
22
|
+
RBS describes the stable calls. Scene primitives and plugin interfaces remain
|
|
23
|
+
experimental extension points.
|
data/docs/adr/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Architecture Decision Records
|
|
2
|
+
|
|
3
|
+
This directory records durable implementation choices that affect component or
|
|
4
|
+
runtime boundaries and had credible alternatives. API reference, command syntax,
|
|
5
|
+
field formats, test evidence, benchmark results, migrations, release policy, and
|
|
6
|
+
operational workflow belong in their authoritative documentation instead.
|
|
7
|
+
|
|
8
|
+
Copy [ADR 000](000-template.md) when recording a decision. Decision records use
|
|
9
|
+
three-digit numbers starting at `001`, assigned without gaps. Keep accepted,
|
|
10
|
+
rejected, and superseded records so later entries do not need renumbering.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Additional Mermaid syntax diagrams
|
|
2
|
+
|
|
3
|
+
The parser recognizes the Mermaid syntax declarations below:
|
|
4
|
+
|
|
5
|
+
`journey`, `quadrantChart`, `requirementDiagram`, `usecaseDiagram`,
|
|
6
|
+
`usecase-beta`, `gitGraph`, `C4Context`, `C4Container`, `C4Component`,
|
|
7
|
+
`C4Dynamic`, `C4Deployment`, `zenuml`, `sankey-beta`, `block-beta`,
|
|
8
|
+
`packet-beta`, `kanban`, `architecture-beta`, `radar-beta`, `treemap-beta`,
|
|
9
|
+
`venn-beta`, `ishikawa-beta`, `wardley-beta`, `treeView`, `treeView-beta`,
|
|
10
|
+
`cynefin-beta`, `swimlane-beta`, `eventModeling`, `agentflow-beta`,
|
|
11
|
+
`railroad-ebnf-beta`, `railroad-abnf-beta`, `railroad-peg-beta`, and
|
|
12
|
+
`railroad-beta`.
|
|
13
|
+
|
|
14
|
+
Each declaration has a diagram-specific terminal layout. Relationship diagrams
|
|
15
|
+
use the shared layered routing engine where that matches their semantics;
|
|
16
|
+
journeys, quadrants, packets, Kanban boards, radar charts, treemaps, Venn
|
|
17
|
+
diagrams, Ishikawa causes, Wardley maps, swimlanes, and event models use their
|
|
18
|
+
own terminal geometry. Ishikawa cause indentation and Treemap hierarchy are
|
|
19
|
+
retained in the terminal layout.
|
|
20
|
+
Railroad declarations render grammar rules with distinct terminal
|
|
21
|
+
and non-terminal nodes. Mermaid directives that only affect browser layout or
|
|
22
|
+
SVG styling are ignored.
|
|
23
|
+
|
|
24
|
+
Kanban task metadata written as `@{ ... }` is shown with the task label. Wardley
|
|
25
|
+
maps support `component`, `anchor`, their `inertia` and source-strategy
|
|
26
|
+
decorators, links (including labels and reverse/dashed forms), `evolve`,
|
|
27
|
+
`pipeline`, `evolution`, trends,
|
|
28
|
+
positioned `note`, and numbered `annotation` statements. Event Modeling supports compact and relaxed time frames, reset
|
|
29
|
+
frames (reset frames retain an `rf` marker), `->>` relations, and named `data` blocks; UI/processor,
|
|
30
|
+
command/read model, and event aliases share their corresponding swimlanes.
|
|
31
|
+
|
|
32
|
+
Railroad rules end with `;`: EBNF uses `=` or `::=`, ABNF uses `=`, and PEG
|
|
33
|
+
uses `<-`. Quoted strings are terminal nodes, identifiers are non-terminal
|
|
34
|
+
nodes, and top-level `|` or `/` alternatives become separate paths. The IR
|
|
35
|
+
form (`railroad-beta`) accepts `terminal("text")`, `nonterminal("name")`,
|
|
36
|
+
`special("text")`, `sequence(...)`, `choice(...)`, `optional(...)`,
|
|
37
|
+
`zeroOrMore(...)`, and `oneOrMore(...)`. EBNF/PEG comments and ABNF trailing
|
|
38
|
+
comments are ignored.
|
|
39
|
+
|
|
40
|
+
An `agentflow-beta` flow with no nodes is rendered as a labeled terminal card;
|
|
41
|
+
it does not fall back to the generic additional-syntax renderer.
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
railroad-ebnf-beta
|
|
45
|
+
title "Letter"
|
|
46
|
+
letter = "a" | "b" ;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
usecase-beta
|
|
51
|
+
actor User
|
|
52
|
+
User --> (Use system)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The core diagram types have dedicated syntax references in the neighboring
|
|
56
|
+
files. This page keeps the additional declaration names and terminal subset in
|
|
57
|
+
one place. Browser-only styling and interaction directives are intentionally
|
|
58
|
+
ignored because they have no terminal equivalent.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Class syntax
|
|
2
|
+
|
|
3
|
+
`classDiagram` accepts `class Name`, bodies in `{ ... }`, attributes, operations, `Name : member`, and annotations such as `class Name <<interface>>` or `Name <<interface>>`. Classes have name, attribute, and operation compartments. Relations include inheritance `<|--`, realization `..|>`, composition `*--`, aggregation `o--`, dependency `..>`, association `-->`, plain `--`, and reverse forms. A label can follow `:`.
|
data/docs/syntax/er.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Flowchart syntax
|
|
2
|
+
|
|
3
|
+
Headers: `graph`, `flowchart`, `flowchart-v2`, or `flowchart-elk`, followed by
|
|
4
|
+
`TB`, `TD`, `BT`, `LR`, or `RL`.
|
|
5
|
+
|
|
6
|
+
Nodes: `A`, `A[text]`, `A(text)`, `A([text])`, `A[[text]]`, `A((text))`, `A(((text)))`, `A{text}`, `A{{text}}`, `A[(text)]`, `A>text]`, `A[/text/]`, `A[/text\]`, and `A@{ shape: diamond }`. `<br>` and HTML entities in labels are accepted.
|
|
7
|
+
|
|
8
|
+
Edges: `-->`, `---`, `-.->`, `==>`, `~~~`, longer variants, edge labels `-->|text|`, chains, and `&` groups. `subgraph ...` / `end` creates a frame. `direction` changes the top level orientation.
|
|
9
|
+
|
|
10
|
+
```mermaid
|
|
11
|
+
flowchart LR
|
|
12
|
+
A[Start] -->|yes| B{Ready?}
|
|
13
|
+
B --> C[Done]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`style`, `classDef`, `class`, `linkStyle`, and `:::` accept `fill`, `stroke`, and `color` hex values for ANSI output. `click` produces an information diagnostic and is ignored. Unknown statements produce errors, and valid statements continue rendering.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Gantt syntax
|
|
2
|
+
|
|
3
|
+
`gantt` accepts `title`, `dateFormat`, `section`, `excludes weekends`, and tasks like `Design :done, d1, 2026-01-01, 3d` or `Build :active, b1, after d1, 5d`. `done`, `active`, `crit`, and `milestone` states are accepted. Durations use days (`d`) or weeks (`w`).
|
data/docs/syntax/pie.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Sequence syntax
|
|
2
|
+
|
|
3
|
+
`sequenceDiagram` accepts `participant` and `actor` declarations, optional `as` labels, message operators `->`, `-->`, `->>`, `-->>`, `-x`, `--x`, `-)`, and `--)`, self messages, `Note left of` / `right of` / `over`, `activate`, `deactivate`, and `autonumber`. `+` or `-` before the recipient activates or deactivates a participant. `loop`, `alt` / `else`, `opt`, `par` / `and`, `critical`, `break`, and `rect` create frames.
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
sequenceDiagram
|
|
7
|
+
participant A as Alice
|
|
8
|
+
participant B as Bob
|
|
9
|
+
A->>+B: Request
|
|
10
|
+
B-->>-A: Response
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Unsupported statements produce errors. Participant declaration order determines columns; undeclared participants use first appearance order.
|
|
14
|
+
Pass `repeat_participants: true` to `render` to show participant boxes again at the bottom.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# State syntax
|
|
2
|
+
|
|
3
|
+
`stateDiagram` and `stateDiagram-v2` accept `A --> B`, transition labels after `:`, `[*]` start and end, `state "Label" as ID`, `state ID <<choice>>`, `<<fork>>`, `<<join>>`, and `state ID { ... }` composite frames. `direction` accepts flowchart directions. `note left of ID` and `note right of ID` accept inline text after `:` or a block ending in `end note`.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# XY chart syntax
|
|
2
|
+
|
|
3
|
+
`xychart-beta` or `xychart` accepts `title`, `x-axis [labels]`, numeric `y-axis min --> max`, `bar [values]`, and `line [values]`. Bars use eighth block characters, lines use braille pixels, and ASCII mode uses `#` and `*`. Add `horizontal` to the header for horizontal bars.
|
data/exe/merminal
ADDED
data/lib/merminal/cli.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
module Merminal
|
|
6
|
+
# Command line interface for files, pipes and Markdown fences.
|
|
7
|
+
module CLI
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def run(argv = ARGV, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
|
11
|
+
options = { charset: :unicode, color: :auto, theme: nil, rounded: true, crossings: :plain,
|
|
12
|
+
fit: :compact, check: false, strict: false, markdown: false, compact: false }
|
|
13
|
+
parser = OptionParser.new do |p|
|
|
14
|
+
p.banner = "Usage: merminal [options] [FILE ...]"
|
|
15
|
+
p.on("-a", "--ascii", "Use printable ASCII") { options[:charset] = :ascii }
|
|
16
|
+
p.on("-t", "--theme NAME", "Color theme") { |name| options[:theme] = name.to_sym }
|
|
17
|
+
p.on("--list-themes", "List themes") { options[:list_themes] = true }
|
|
18
|
+
p.on("--color WHEN", %w[auto always never], "auto, always or never") { |value| options[:color] = value.to_sym }
|
|
19
|
+
p.on("-w", "--width N", Integer, "Target terminal width") { |value| options[:width] = value }
|
|
20
|
+
p.on("--no-fit", "Disable width fitting") { options[:fit] = nil }
|
|
21
|
+
p.on("--fit MODE", %w[compact rotate], "compact or rotate") { |value| options[:fit] = value.to_sym }
|
|
22
|
+
p.on("--compact", "Use compact spacing") { options[:compact] = true }
|
|
23
|
+
p.on("--sharp", "Use sharp corners") { options[:rounded] = false }
|
|
24
|
+
p.on("--crossings MODE", %w[plain bridge], "Crossing style") { |value| options[:crossings] = value.to_sym }
|
|
25
|
+
p.on("--markdown", "Read Mermaid fences") { options[:markdown] = true }
|
|
26
|
+
p.on("--check", "Check syntax without rendering") { options[:check] = true }
|
|
27
|
+
p.on("--strict", "Fail on parser errors") { options[:strict] = true }
|
|
28
|
+
p.on("-o", "--output FILE", "Write output to file") { |path| options[:output] = path }
|
|
29
|
+
p.on("-v", "--version", "Print version") { stdout.puts VERSION; return 0 }
|
|
30
|
+
p.on("-h", "--help", "Print help") { stdout.puts p; return 0 }
|
|
31
|
+
end
|
|
32
|
+
files = parser.parse(argv)
|
|
33
|
+
if options[:list_themes]
|
|
34
|
+
stdout.puts Output::THEMES.keys
|
|
35
|
+
return 0
|
|
36
|
+
end
|
|
37
|
+
raise OptionParser::InvalidArgument, "width must be positive" if options[:width] && !options[:width].positive?
|
|
38
|
+
raise OptionParser::InvalidArgument, "unknown theme" if options[:theme] && !Output::THEMES.key?(options[:theme])
|
|
39
|
+
|
|
40
|
+
parts = []
|
|
41
|
+
error_found = false
|
|
42
|
+
(files.empty? ? ["-"] : files).each do |path|
|
|
43
|
+
input = path == "-" ? stdin.read : File.read(path)
|
|
44
|
+
markdown = options[:markdown] || path.match?(/\.(?:md|markdown)\z/i)
|
|
45
|
+
blocks = markdown ? Merminal.markdown_blocks(input) : [MarkdownBlock.new(source: input, line: 1)]
|
|
46
|
+
blocks.each do |block|
|
|
47
|
+
document = Merminal.parse(block.source)
|
|
48
|
+
document.diagnostics.each do |diagnostic|
|
|
49
|
+
error_found ||= diagnostic.severity == :error
|
|
50
|
+
shifted = diagnostic.with(line: diagnostic.line + block.line - 1)
|
|
51
|
+
stderr.puts shifted.format(path, markdown ? input : block.source)
|
|
52
|
+
end
|
|
53
|
+
next if options[:check]
|
|
54
|
+
|
|
55
|
+
parts << fitted_render(document, options, stdout, stderr)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
output = parts.join("\n\n")
|
|
59
|
+
options[:output] ? File.write(options[:output], output + (output.empty? ? "" : "\n")) : stdout.puts(output) unless options[:check]
|
|
60
|
+
error_found && (options[:strict] || options[:check]) ? 1 : 0
|
|
61
|
+
rescue UnsupportedDiagramError => e
|
|
62
|
+
stderr.puts e.message
|
|
63
|
+
3
|
|
64
|
+
rescue OptionParser::ParseError, Errno::ENOENT, Errno::EACCES, IOError, ArgumentError => e
|
|
65
|
+
stderr.puts e.message
|
|
66
|
+
2
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def fitted_render(document, options, stdout, stderr)
|
|
70
|
+
width = options[:width] || (stdout.tty? ? terminal_width : nil)
|
|
71
|
+
rendered = document.render(width: width, fit: options[:fit], compact: options[:compact],
|
|
72
|
+
charset: options[:charset], color: options[:color], theme: options[:theme],
|
|
73
|
+
rounded: options[:rounded], crossings: options[:crossings])
|
|
74
|
+
if width && rendered.lines.any? { |line| Merminal::Text.width(line.gsub(/\e\[[\d;]*m/, "")) > width }
|
|
75
|
+
actual = rendered.lines.map { |line| Merminal::Text.width(line.gsub(/\e\[[\d;]*m/, "")) }.max
|
|
76
|
+
stderr.puts "diagram width #{actual} exceeds target #{width}"
|
|
77
|
+
end
|
|
78
|
+
rendered
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def terminal_width
|
|
82
|
+
return ENV["COLUMNS"].to_i if ENV["COLUMNS"].to_i.positive?
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
require "io/console"
|
|
86
|
+
width = IO.console&.winsize&.last
|
|
87
|
+
return width if width && width.positive?
|
|
88
|
+
rescue LoadError, Errno::ENOTTY, IOError
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
80
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|