Skip to content

Commit 9f48fd1

Browse files
bobzhangclaude
andcommitted
docs: add snapshot-tested README with embedded SVG examples
The uml module gains an executable README.mbt.md (symlinked as its README.md): every example is a `moon test` block that renders PlantUML source through @api.render_svg and snapshots the SVG into __snapshot__/, which the markdown then embeds inline — sequence, class, use case, mindmap, JSON, YAML, TOML, plus a dark ColorScheme demo. The repo README embeds a preview gallery from the same snapshots, and .gitignore now re-includes __snapshot__/*.svg so the images are tracked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c50f504 commit 9f48fd1

13 files changed

Lines changed: 737 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ target/
77
*.mbti
88
*.dot
99
*.svg
10+
!__snapshot__/*.svg
11+
!**/__snapshot__/*.svg
1012
AGENTS.md

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
A MoonBit project that converts UML strings to SVG, aiming to align with PlantUML's behavior, including its layout.
44

5+
| Sequence | Class |
6+
|---|---|
7+
| ![Sequence diagram](./uml/__snapshot__/sequence.svg) | ![Class diagram](./uml/__snapshot__/class.svg) |
8+
9+
| Mindmap | JSON |
10+
|---|---|
11+
| ![Mindmap diagram](./uml/__snapshot__/mindmap.svg) | ![JSON diagram](./uml/__snapshot__/json.svg) |
12+
13+
Every image above is a test snapshot: the [library README](./uml/README.mbt.md)
14+
is an executable document whose code blocks run under `moon test` and write
15+
these SVGs into [`uml/__snapshot__/`](./uml/__snapshot__/). See it for the full
16+
gallery (use case, YAML, TOML, dark theming) and API walkthrough.
17+
518
## Supported diagrams
619

720
Available:

uml/README.mbt.md

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
# uml
2+
3+
A MoonBit library that converts PlantUML source text to SVG, aiming to align
4+
with PlantUML's behavior, including its layout.
5+
6+
This document is executable. Every example below is a real test that `moon test`
7+
runs, and every image is the exact SVG those tests wrote into
8+
[`__snapshot__/`](./__snapshot__/) with `moon test --update`. When rendering
9+
changes, the pictures in this page change in the same commit.
10+
11+
## Quick start
12+
13+
```bash
14+
moon add kokic/uml
15+
```
16+
17+
The facade package is `kokic/uml/api`. Its entry points are:
18+
19+
- `@api.render_svg(source)` — parse a PlantUML source and render it to SVG in
20+
one call.
21+
- `@api.parse(source)` — parse only; returns a `Document` whose `kind()` names
22+
the diagram family the source selected.
23+
- `Document::render_svg` — render an already parsed document.
24+
25+
```mbt check
26+
///|
27+
test "quick start" {
28+
let source =
29+
#|@startuml
30+
#|participant Alice
31+
#|Alice -> Bob : hello
32+
#|@enduml
33+
let svg = @api.render_svg(source)
34+
assert_true(svg.contains("<svg"))
35+
}
36+
```
37+
38+
## Gallery
39+
40+
### Sequence diagram
41+
42+
Participants and actors, activations, `autonumber`, `alt`/`else` groups, and
43+
notes:
44+
45+
```mbt check
46+
///|
47+
test "sequence diagram" (it : @test.Test) {
48+
let source =
49+
#|@startuml
50+
#|autonumber
51+
#|actor User
52+
#|participant "Web App" as App
53+
#|participant "Auth Service" as Auth
54+
#|User -> App : sign in
55+
#|App -> Auth : POST /token
56+
#|activate Auth
57+
#|Auth --> App : access token
58+
#|deactivate Auth
59+
#|alt token granted
60+
#|App --> User : welcome page
61+
#|else invalid credentials
62+
#|App --> User : error message
63+
#|end
64+
#|note right of Auth : stateless issuer
65+
#|@enduml
66+
it.write(@api.render_svg(source))
67+
it.snapshot(filename="sequence.svg")
68+
}
69+
```
70+
71+
![Sequence diagram](./__snapshot__/sequence.svg)
72+
73+
### Class diagram
74+
75+
Interfaces, abstract classes, visibility markers, and relations:
76+
77+
```mbt check
78+
///|
79+
test "class diagram" (it : @test.Test) {
80+
let source =
81+
#|@startuml
82+
#|interface Shape {
83+
#| + area() : Double
84+
#|}
85+
#|abstract class Polygon {
86+
#| # vertices : Array[Point]
87+
#| + area() : Double
88+
#|}
89+
#|class Circle {
90+
#| - radius : Double
91+
#| + area() : Double
92+
#|}
93+
#|class Point {
94+
#| + x : Double
95+
#| + y : Double
96+
#|}
97+
#|Shape <|.. Polygon
98+
#|Shape <|.. Circle
99+
#|Polygon o-- Point
100+
#|@enduml
101+
it.write(@api.render_svg(source))
102+
it.snapshot(filename="class.svg")
103+
}
104+
```
105+
106+
![Class diagram](./__snapshot__/class.svg)
107+
108+
### Use case diagram
109+
110+
Actors, use cases, and dotted relations:
111+
112+
```mbt check
113+
///|
114+
test "use case diagram" (it : @test.Test) {
115+
let source =
116+
#|@startuml
117+
#|:Customer: --> (Browse catalog)
118+
#|:Customer: --> (Place order)
119+
#|:Sales clerk: --> (Approve order)
120+
#|(Place order) ..> (Approve order) : include
121+
#|@enduml
122+
it.write(@api.render_svg(source))
123+
it.snapshot(filename="usecase.svg")
124+
}
125+
```
126+
127+
![Use case diagram](./__snapshot__/usecase.svg)
128+
129+
### Mindmap
130+
131+
`*` levels grow to the right, `--` levels grow to the left:
132+
133+
```mbt check
134+
///|
135+
test "mindmap diagram" (it : @test.Test) {
136+
let source =
137+
#|@startmindmap
138+
#|* uml
139+
#|** Diagrams
140+
#|*** Sequence
141+
#|*** Class
142+
#|*** Use case
143+
#|** Formats
144+
#|*** JSON
145+
#|*** YAML
146+
#|*** TOML
147+
#|-- Backend
148+
#|--- SVG
149+
#|-- Tooling
150+
#|--- moon test
151+
#|@endmindmap
152+
it.write(@api.render_svg(source))
153+
it.snapshot(filename="mindmap.svg")
154+
}
155+
```
156+
157+
![Mindmap diagram](./__snapshot__/mindmap.svg)
158+
159+
### JSON data diagram
160+
161+
```mbt check
162+
///|
163+
test "json diagram" (it : @test.Test) {
164+
let source =
165+
#|@startjson
166+
#|{
167+
#| "name": "kokic/uml",
168+
#| "version": "0.1.2",
169+
#| "targets": ["wasm", "js", "native"],
170+
#| "diagrams": {
171+
#| "available": 7,
172+
#| "planned": 3
173+
#| }
174+
#|}
175+
#|@endjson
176+
it.write(@api.render_svg(source))
177+
it.snapshot(filename="json.svg")
178+
}
179+
```
180+
181+
![JSON diagram](./__snapshot__/json.svg)
182+
183+
### YAML data diagram
184+
185+
```mbt check
186+
///|
187+
test "yaml diagram" (it : @test.Test) {
188+
let source =
189+
#|@startyaml
190+
#|name: uml
191+
#|license: Apache-2.0
192+
#|diagrams:
193+
#| - sequence
194+
#| - class
195+
#| - mindmap
196+
#|render:
197+
#| backend: svg
198+
#| compatible: PlantUML
199+
#|@endyaml
200+
it.write(@api.render_svg(source))
201+
it.snapshot(filename="yaml.svg")
202+
}
203+
```
204+
205+
![YAML diagram](./__snapshot__/yaml.svg)
206+
207+
### TOML data diagram
208+
209+
```mbt check
210+
///|
211+
test "toml diagram" (it : @test.Test) {
212+
let source =
213+
#|@starttoml
214+
#|[package]
215+
#|name = "uml"
216+
#|version = "0.1.2"
217+
#|
218+
#|[render]
219+
#|backend = "svg"
220+
#|targets = ["wasm", "js"]
221+
#|@endtoml
222+
it.write(@api.render_svg(source))
223+
it.snapshot(filename="toml.svg")
224+
}
225+
```
226+
227+
![TOML diagram](./__snapshot__/toml.svg)
228+
229+
## Theming
230+
231+
`render_svg` accepts a `color_scheme`. The two positional roles are the ink
232+
colors (`text` and `line`); every other role is optional and may be a literal
233+
color or a CSS variable such as `var(--uml-text)`, so one scheme can target a
234+
specific light or dark page without touching each diagram. Document-level
235+
`skinparam` lines still win over the scheme.
236+
237+
```mbt check
238+
///|
239+
test "dark sequence diagram" (it : @test.Test) {
240+
let dark = @style.ColorScheme::ColorScheme(
241+
"#e6edf3", // text
242+
"#8b949e", // line
243+
canvas="#0d1117",
244+
participant="#161b22",
245+
activation="#21262d",
246+
lifeline="#30363d",
247+
note="#2d2a1f",
248+
)
249+
let source =
250+
#|@startuml
251+
#|participant "Web App" as App
252+
#|participant "Auth Service" as Auth
253+
#|App -> Auth : POST /token
254+
#|activate Auth
255+
#|Auth --> App : access token
256+
#|deactivate Auth
257+
#|note right of Auth : stateless issuer
258+
#|@enduml
259+
it.write(@api.render_svg(source, color_scheme=dark))
260+
it.snapshot(filename="sequence_dark.svg")
261+
}
262+
```
263+
264+
![Dark sequence diagram](./__snapshot__/sequence_dark.svg)
265+
266+
## Documents and diagram kinds
267+
268+
`parse` chooses the diagram family with the same per-line heuristics PlantUML
269+
uses to pick a diagram factory, and `Document::kind` exposes the choice:
270+
271+
```mbt check
272+
///|
273+
test "documents expose their detected diagram kind" {
274+
let source =
275+
#|@startmindmap
276+
#|* root
277+
#|@endmindmap
278+
let document = @api.parse(source)
279+
assert_true(document.kind() is Mindmap)
280+
assert_true(document.render_svg().contains("<svg"))
281+
}
282+
```
283+
284+
## Collapsible class members
285+
286+
With `class_member_collapsible=true`, class members render inside a
287+
`<details>` disclosure (via `<foreignObject>`) so they can be folded in the
288+
browser:
289+
290+
```mbt check
291+
///|
292+
test "collapsible class members" {
293+
let source =
294+
#|@startuml
295+
#|class User {
296+
#|- secret
297+
#|}
298+
#|@enduml
299+
let svg = @api.render_svg(source, class_member_collapsible=true)
300+
assert_true(svg.contains("<details"))
301+
}
302+
```
303+
304+
## Supported diagrams
305+
306+
Available: sequence, class, usecase, mindmap, yaml, toml, json.
307+
308+
Under construction: object, state, component, activity.
309+
310+
## Snapshot workflow
311+
312+
The images in this page are ordinary snapshot tests:
313+
314+
```bash
315+
moon test # verifies the SVGs are unchanged
316+
moon test --update # regenerates __snapshot__/*.svg after a rendering change
317+
```
318+
319+
## License
320+
321+
Apache-2.0

uml/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
README.mbt.md

uml/__snapshot__/class.svg

Lines changed: 21 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)