sketchmark 0.2.7 → 0.2.8

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.
Files changed (62) hide show
  1. package/README.md +1041 -1066
  2. package/dist/animation/index.d.ts +6 -1
  3. package/dist/animation/index.d.ts.map +1 -1
  4. package/dist/ast/types.d.ts +6 -17
  5. package/dist/ast/types.d.ts.map +1 -1
  6. package/dist/config.d.ts +150 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/export/index.d.ts.map +1 -1
  9. package/dist/index.cjs +1566 -1352
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +1566 -1352
  12. package/dist/index.js.map +1 -1
  13. package/dist/layout/entity-rect.d.ts +9 -0
  14. package/dist/layout/entity-rect.d.ts.map +1 -0
  15. package/dist/layout/index.d.ts.map +1 -1
  16. package/dist/markdown/parser.d.ts.map +1 -1
  17. package/dist/parser/index.d.ts.map +1 -1
  18. package/dist/parser/tokenizer.d.ts.map +1 -1
  19. package/dist/renderer/canvas/index.d.ts.map +1 -1
  20. package/dist/renderer/roughChart.d.ts.map +1 -1
  21. package/dist/renderer/shapes/box.d.ts +3 -0
  22. package/dist/renderer/shapes/box.d.ts.map +1 -0
  23. package/dist/renderer/shapes/circle.d.ts +3 -0
  24. package/dist/renderer/shapes/circle.d.ts.map +1 -0
  25. package/dist/renderer/shapes/cylinder.d.ts +3 -0
  26. package/dist/renderer/shapes/cylinder.d.ts.map +1 -0
  27. package/dist/renderer/shapes/diamond.d.ts +3 -0
  28. package/dist/renderer/shapes/diamond.d.ts.map +1 -0
  29. package/dist/renderer/shapes/hexagon.d.ts +3 -0
  30. package/dist/renderer/shapes/hexagon.d.ts.map +1 -0
  31. package/dist/renderer/shapes/icon.d.ts +3 -0
  32. package/dist/renderer/shapes/icon.d.ts.map +1 -0
  33. package/dist/renderer/shapes/image.d.ts +3 -0
  34. package/dist/renderer/shapes/image.d.ts.map +1 -0
  35. package/dist/renderer/shapes/index.d.ts +4 -0
  36. package/dist/renderer/shapes/index.d.ts.map +1 -0
  37. package/dist/renderer/shapes/line.d.ts +3 -0
  38. package/dist/renderer/shapes/line.d.ts.map +1 -0
  39. package/dist/renderer/shapes/note.d.ts +3 -0
  40. package/dist/renderer/shapes/note.d.ts.map +1 -0
  41. package/dist/renderer/shapes/parallelogram.d.ts +3 -0
  42. package/dist/renderer/shapes/parallelogram.d.ts.map +1 -0
  43. package/dist/renderer/shapes/path.d.ts +3 -0
  44. package/dist/renderer/shapes/path.d.ts.map +1 -0
  45. package/dist/renderer/shapes/registry.d.ts +5 -0
  46. package/dist/renderer/shapes/registry.d.ts.map +1 -0
  47. package/dist/renderer/shapes/text-shape.d.ts +3 -0
  48. package/dist/renderer/shapes/text-shape.d.ts.map +1 -0
  49. package/dist/renderer/shapes/triangle.d.ts +3 -0
  50. package/dist/renderer/shapes/triangle.d.ts.map +1 -0
  51. package/dist/renderer/shapes/types.d.ts +50 -0
  52. package/dist/renderer/shapes/types.d.ts.map +1 -0
  53. package/dist/renderer/shared.d.ts +26 -0
  54. package/dist/renderer/shared.d.ts.map +1 -0
  55. package/dist/renderer/svg/index.d.ts.map +1 -1
  56. package/dist/renderer/svg/roughChartSVG.d.ts.map +1 -1
  57. package/dist/renderer/typography.d.ts +27 -0
  58. package/dist/renderer/typography.d.ts.map +1 -0
  59. package/dist/scene/index.d.ts +5 -13
  60. package/dist/scene/index.d.ts.map +1 -1
  61. package/dist/sketchmark.iife.js +1567 -1353
  62. package/package.json +1 -1
package/README.md CHANGED
@@ -1,1066 +1,1041 @@
1
- # sketchmark
2
-
3
- A text-based diagram DSL that renders hand-drawn SVG and Canvas diagrams using [rough.js](https://roughjs.com). Write diagrams as plain text, get sketchy, expressive visuals with a full animation system.
4
-
5
- ```
6
- diagram
7
- title label="System Architecture"
8
-
9
- box client label="Client App" theme=primary
10
- box gateway label="API Gateway" theme=warning
11
- box db label="PostgreSQL" theme=success
12
-
13
- client --> gateway label="HTTPS"
14
- gateway --> db label="SQL"
15
-
16
- step highlight client
17
- step draw client-->gateway
18
- step highlight gateway
19
- step draw gateway-->db
20
- end
21
- ```
22
-
23
- ---
24
-
25
- ## Table of Contents
26
-
27
- - [Installation](#installation)
28
- - [Quick Start](#quick-start)
29
- - [DSL Reference](#dsl-reference)
30
- - [Diagram Header](#diagram-header)
31
- - [Node Shapes](#node-shapes)
32
- - [Icons](#icon-shape)
33
- - [Edges](#edges)
34
- - [Groups](#groups)
35
- - [Bare Groups](#bare-groups)
36
- - [Tables](#tables)
37
- - [Notes](#notes)
38
- - [Charts](#charts)
39
- - [Markdown Blocks](#markdown-blocks)
40
- - [Themes](#themes)
41
- - [Style Directive](#style-directive)
42
- - [Typography](#typography)
43
- - [Animation Steps](#animation-steps)
44
- - [Layout System](#layout-system)
45
- - [Animation System](#animation-system)
46
- - [Theme Palettes](#theme-palettes)
47
- - [Font System](#font-system)
48
- - [API Reference](#api-reference)
49
- - [Export](#export)
50
- - [Examples](#examples)
51
-
52
- ---
53
-
54
- ## Installation
55
-
56
- ```bash
57
- npm install sketchmark
58
- ```
59
-
60
- rough.js is a peer dependency — it must be available at runtime.
61
-
62
- ---
63
-
64
- ## Quick Start
65
-
66
- **With a bundler (Vite, webpack, Next.js):**
67
-
68
- ```typescript
69
- import { render } from 'sketchmark';
70
-
71
- const instance = render({
72
- container: document.getElementById('diagram'),
73
- dsl: `
74
- diagram
75
- box a label="Hello"
76
- box b label="World"
77
- a --> b label="connects"
78
- `,
79
- renderer: 'svg',
80
- svgOptions: { showTitle: true, interactive: true, transparent: true },
81
- });
82
-
83
- // Step through animation
84
- instance.anim.next();
85
- instance.anim.play(800);
86
- ```
87
-
88
- **CDN / no bundler with import map:**
89
-
90
- ```html
91
-
92
-
93
- { "imports": { "sketchmark": "https://unpkg.com/sketchmark/dist/index.js" } }
94
-
95
-
96
- import { render } from 'sketchmark';
97
- render({
98
- container: document.getElementById('diagram'),
99
- dsl: `diagram\nbox a label="Hello"\nbox b label="World"\na --> b`,
100
- });
101
-
102
- ```
103
-
104
- **CommonJS:**
105
-
106
- ```javascript
107
- const { parse, buildSceneGraph, layout } = require('sketchmark');
108
- ```
109
-
110
- ---
111
-
112
- ## DSL Reference
113
-
114
- Every diagram starts with `diagram` and ends with `end`.
115
-
116
- ```
117
- diagram
118
- title label="My Diagram"
119
- layout row
120
- config gap=60
121
-
122
- ... nodes, edges, groups, steps ...
123
-
124
- end
125
- ```
126
-
127
- ### Diagram Header
128
-
129
- | Keyword | Example | Description |
130
- |---|---|---|
131
- | `title` | `title label="My Diagram"` | Title shown above the diagram |
132
- | `description` | `description "A brief summary"` | Diagram description (metadata) |
133
- | `layout` | `layout row` | Root layout direction: `row`, `column`, `grid` |
134
- | `config gap` | `config gap=60` | Gap between root-level items (default: 80) |
135
- | `config margin` | `config margin=40` | Outer canvas margin (default: 60) |
136
- | `config theme` | `config theme=ocean` | Global palette (see [Theme Palettes](#theme-palettes)) |
137
- | `config font` | `config font=caveat` | Diagram-wide font (see [Font System](#font-system)) |
138
- | `config title-color` | `config title-color=#333` | Title text color |
139
- | `config title-size` | `config title-size=20` | Title font size in px |
140
- | `config title-weight` | `config title-weight=700` | Title font weight |
141
-
142
- ---
143
-
144
- ### Node Shapes
145
-
146
- ```
147
- box id label="..." [theme=X] [width=N] [height=N]
148
- circle id label="..."
149
- diamond id label="..."
150
- hexagon id label="..."
151
- triangle id label="..."
152
- cylinder id label="..."
153
- parallelogram id label="..."
154
- text id label="..."
155
- image id label="..." url="https://..."
156
- icon id label="..." name="prefix:name"
157
- ```
158
-
159
- **Common properties:**
160
-
161
- | Property | Example | Description |
162
- |---|---|---|
163
- | `label` | `label="API Gateway"` | Display text. Use `\n` for line breaks |
164
- | `theme` | `theme=primary` | Named theme |
165
- | `width` | `width=140` | Override auto-width in px |
166
- | `height` | `height=55` | Override auto-height in px |
167
- | `fill` | `fill="#e8f4ff"` | Background fill color |
168
- | `stroke` | `stroke="#0044cc"` | Border color |
169
- | `stroke-width` | `stroke-width=2` | Border thickness in px |
170
- | `stroke-dash` | `stroke-dash=5,3` | Dashed border pattern (dash, gap) |
171
- | `opacity` | `opacity=0.5` | Element opacity (0 to 1) |
172
- | `color` | `color="#003399"` | Text color |
173
- | `font` | `font=caveat` | Font family or built-in name |
174
- | `font-size` | `font-size=12` | Label font size in px |
175
- | `font-weight` | `font-weight=600` | Font weight |
176
- | `letter-spacing` | `letter-spacing=2` | Letter spacing in px |
177
- | `text-align` | `text-align=left` | `left`, `center`, `right` |
178
- | `vertical-align` | `vertical-align=top` | `top`, `middle`, `bottom` |
179
- | `line-height` | `line-height=1.6` | Line height multiplier |
180
-
181
- > **`text` shape:** No border or background. Long labels auto word-wrap. Use `width=` to control the wrap width.
182
-
183
- > **`image` shape:** Renders an image clipped to a rounded rect. Requires `url=` property. Label renders below the image. Border only shown when `stroke=` is set.
184
-
185
- > **`icon` shape:** Renders an icon from [Iconify](https://iconify.design/) (200,000+ open source icons). Requires `name=` property in `prefix:name` format (e.g. `mdi:database`). Defaults to `mdi` prefix if omitted. Use `color=` to tint the icon. Label renders below the icon. Border only shown when `stroke=` is set. Default size: 48x48.
186
-
187
- **Example:**
188
- ```
189
- box gateway label="API Gateway" theme=warning width=150 height=55
190
- circle user label="User" fill="#e8f4ff" stroke="#0044cc" color="#003399"
191
- cylinder db label="PostgreSQL" theme=success width=140 height=65
192
- image logo label="Logo" url="https://example.com/logo.png" width=80 height=80
193
- icon db label="Database" name="mdi:database" color="#1976D2"
194
- icon cloud name="mdi:cloud" width=64 height=64
195
- text caption label="This auto-wraps across multiple lines." width=300
196
- ```
197
-
198
- ---
199
-
200
- ### Icon Shape
201
-
202
- Render any of 200,000+ open source vector icons from [Iconify](https://iconify.design/).
203
-
204
- ```
205
- icon id [label="..."] name="prefix:name" [color="#hex"] [width=N] [height=N]
206
- ```
207
-
208
- | Property | Example | Description |
209
- |---|---|---|
210
- | `name` | `name="mdi:database"` | Icon identifier in `prefix:name` format. Defaults to `mdi` prefix if omitted |
211
- | `color` | `color="#1976D2"` | Icon tint color |
212
- | `stroke` | `stroke="#333"` | Optional border (not shown by default) |
213
- | `label` | `label="DB"` | Label shown below the icon (defaults to id) |
214
- | `width` | `width=64` | Icon width (default: 48) |
215
- | `height` | `height=64` | Icon height (default: 48) |
216
-
217
- Browse available icons at [icon-sets.iconify.design](https://icon-sets.iconify.design/). Common prefixes: `mdi` (Material Design), `lucide`, `heroicons`, `tabler`, `ph` (Phosphor), `ri` (Remix), `carbon`.
218
-
219
- **Example:**
220
- ```
221
- icon db label="Database" name="mdi:database" color="#1976D2"
222
- icon cloud label="Cloud" name="mdi:cloud-outline" color="#FF9800" width=64 height=64
223
- icon lock name="mdi:lock" color="#E53935"
224
- icon user name="lucide:user"
225
- ```
226
-
227
- ---
228
-
229
- ### Edges
230
-
231
- ```
232
- fromId connector toId [label="..."] [stroke="#color"] [stroke-width=N]
233
- ```
234
-
235
- **Connectors:**
236
-
237
- | Connector | Arrow | Line |
238
- |---|---|---|
239
- | `->` | end | solid |
240
- | `<-` | start | solid |
241
- | `<->` | both | solid |
242
- | `-->` | end | dashed |
243
- | `<-->` | both | dashed |
244
- | `--` | none | solid |
245
- | `---` | none | dashed |
246
-
247
- **Edge style properties:**
248
-
249
- | Property | Description |
250
- |---|---|
251
- | `label` | Text label on the edge |
252
- | `stroke` | Line and arrowhead color |
253
- | `stroke-width` | Line thickness |
254
- | `color` | Label text color |
255
- | `font` | Label font |
256
- | `font-size` | Label font size |
257
- | `theme` | Apply a named theme to the edge |
258
-
259
- **Example:**
260
- ```
261
- client --> gateway label="HTTPS"
262
- gateway <-> auth label="verify"
263
- a -- b
264
- db --- replica
265
- api --> db stroke="#0044cc" stroke-width=2 theme=primary
266
- ```
267
-
268
- ---
269
-
270
- ### Groups
271
-
272
- Groups are containers that arrange children using a flexbox-style layout.
273
-
274
- ```
275
- group id [label="..."] [layout=row|column|grid] [gap=N] [padding=N]
276
- [justify=start|center|end|space-between|space-around]
277
- [align=start|center|end]
278
- [columns=N] [width=N] [height=N] [theme=X]
279
- [font=X] [font-size=N] [letter-spacing=N]
280
- {
281
- ...children...
282
- }
283
- ```
284
-
285
- **Properties:**
286
-
287
- | Property | Default | Description |
288
- |---|---|---|
289
- | `label` | | Label at top-left. Omit or `""` for no label and no reserved space |
290
- | `layout` | `column` | `row`, `column`, or `grid` |
291
- | `gap` | `10` | Space between children in px |
292
- | `padding` | `26` | Inner padding in px |
293
- | `justify` | `start` | Main-axis distribution |
294
- | `align` | `start` | Cross-axis alignment |
295
- | `columns` | `1` | Columns when `layout=grid` |
296
- | `width` | auto | Minimum width enables `justify` |
297
- | `height` | auto | Minimum height |
298
- | `theme` | | Named theme for border/background |
299
-
300
- > **`justify`** requires an explicit `width` larger than total child width to have a visible effect.
301
-
302
- **Example:**
303
- ```
304
- group services label="Microservices" layout=column gap=16 padding=30 theme=muted
305
- {
306
- box auth label="Auth Service" theme=primary width=140 height=55
307
- box billing label="Billing Service" theme=primary width=140 height=55
308
- }
309
- ```
310
-
311
- **Grid:**
312
- ```
313
- group icons layout=grid columns=3 gap=20 padding=24
314
- {
315
- box a label="A" width=100 height=60
316
- box b label="B" width=100 height=60
317
- box c label="C" width=100 height=60
318
- }
319
- ```
320
-
321
- **Space-between with explicit width:**
322
- ```
323
- group nav layout=row justify=space-between width=500 padding=20 gap=10
324
- {
325
- box home label="Home" width=80 height=40
326
- box about label="About" width=80 height=40
327
- box contact label="Contact" width=80 height=40
328
- }
329
- ```
330
-
331
- ---
332
-
333
- ### Bare Groups
334
-
335
- `bare` is a layout-only container — no label, no border, no background, no padding by default. All group layout properties apply.
336
-
337
- ```
338
- bare id [layout=...] [gap=N] [padding=N] [justify=...] [align=...] [columns=N]
339
- {
340
- ...children...
341
- }
342
- ```
343
-
344
- Any explicitly set style overrides the bare defaults:
345
-
346
- ```
347
- # fully invisible layout container
348
- bare page layout=row gap=60
349
- {
350
- markdown intro width=340
351
- """
352
- # Title
353
- Some prose here.
354
- """
355
-
356
- group diagram layout=column gap=20 padding=30 theme=muted
357
- {
358
- box a label="A" theme=primary width=130 height=52
359
- }
360
- }
361
-
362
- # bare with a visible border
363
- bare wrapper layout=row gap=20 stroke="#cccccc" padding=16
364
- {
365
- box x label="X" width=100 height=50
366
- box y label="Y" width=100 height=50
367
- }
368
- ```
369
-
370
- ---
371
-
372
- ### Tables
373
-
374
- ```
375
- table id [label="..."] [theme=X] [font=X] [font-size=N] [text-align=left|center|right]
376
- {
377
- header Col1 Col2 Col3
378
- row val1 val2 val3
379
- }
380
- ```
381
-
382
- **Example:**
383
- ```
384
- table pricing label="Pricing Plans" font=dm-mono text-align=right
385
- {
386
- header Plan Price Requests
387
- row Free $0 1k/day
388
- row Pro $29 100k/day
389
- row Enterprise $299 Unlimited
390
- }
391
- ```
392
-
393
- ---
394
-
395
- ### Notes
396
-
397
- Sticky notes with a folded corner.
398
-
399
- ```
400
- note id label="Single line" [theme=X]
401
- note id label="Line one\nLine two\nLine three"
402
- note id label="..." [width=N] [height=N]
403
- [font=X] [font-size=N] [letter-spacing=N]
404
- [text-align=left|center|right]
405
- [vertical-align=top|middle|bottom]
406
- [line-height=1.4]
407
- ```
408
-
409
- **Example:**
410
- ```
411
- note n1 label="Rate limited\nto 1000 req/s\nper tenant"
412
- note n2 label="Postgres 16\nwith pg_vector" width=200 height=100 font-size=13
413
- note n3 label="Centered" text-align=center vertical-align=middle width=180 height=80
414
- ```
415
-
416
- ---
417
-
418
- ### Charts
419
-
420
- ```
421
- bar-chart id [label="..."] [width=N] [height=N] [theme=X]
422
- line-chart id ...
423
- area-chart id ...
424
- pie-chart id ...
425
- donut-chart id ...
426
- scatter-chart id ...
427
-
428
- data
429
- [
430
- ["Label", "Series1", "Series2"],
431
- ["Jan", 120, 80 ],
432
- ["Feb", 150, 95 ]
433
- ]
434
- ```
435
-
436
- **Pie / donut:**
437
- ```
438
- pie-chart revenue label="Revenue Split" width=280 height=240
439
- data
440
- [
441
- ["Product A", 42],
442
- ["Services", 30],
443
- ["Support", 25]
444
- ]
445
- ```
446
-
447
- ---
448
-
449
- ### Markdown Blocks
450
-
451
- Prose content with Markdown-style formatting, rendered inside the diagram layout.
452
-
453
- ```
454
- markdown id [width=N] [padding=N] [font=X]
455
- [text-align=left|center|right] [color=X]
456
- """
457
- # Heading 1
458
- ## Heading 2
459
- ### Heading 3
460
-
461
- Paragraph with **bold** and *italic* text.
462
-
463
- Another paragraph.
464
- """
465
- ```
466
-
467
- **Supported syntax:**
468
-
469
- | Syntax | Result |
470
- |---|---|
471
- | `# text` | H1 heading |
472
- | `## text` | H2 heading |
473
- | `### text` | H3 heading |
474
- | `**text**` | Bold |
475
- | `*text*` | Italic |
476
- | blank line | Vertical spacing |
477
-
478
- **Example:**
479
- ```
480
- bare page layout=row gap=60
481
- {
482
- markdown intro width=320 font=caveat padding=0
483
- """
484
- # Sketchmark
485
-
486
- A text-based diagram DSL that renders
487
- **hand-drawn** SVG diagrams.
488
-
489
- ## Animation
490
-
491
- Every element supports *step-by-step*
492
- animation — **draw**, highlight, fade, move.
493
- """
494
-
495
- group diagram layout=column gap=20 padding=30 theme=muted
496
- {
497
- box parser label="Parser" theme=primary width=130 height=52
498
- box scene label="Scene" theme=success width=130 height=52
499
- }
500
- }
501
-
502
- parser --> scene label="AST"
503
-
504
- step draw intro
505
- step highlight parser
506
- step draw parser-->scene
507
- step highlight scene
508
- end
509
- ```
510
-
511
- ---
512
-
513
- ### Themes
514
-
515
- Define reusable style presets and apply them to any element.
516
-
517
- ```
518
- theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
519
- theme success fill="#e8ffe8" stroke="#007700" color="#004400"
520
- theme warning fill="#fff9e6" stroke="#f0a500" color="#7a5000"
521
- theme danger fill="#ffe8e8" stroke="#cc0000" color="#900000"
522
- theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
523
- ```
524
-
525
- Apply to any element: `box a theme=primary`, `group g theme=muted`, `note n theme=warning`, `a --> b theme=danger`
526
-
527
- ---
528
-
529
- ### Style Directive
530
-
531
- Apply styles to any element after it's defined, by targeting its id:
532
-
533
- ```
534
- box a label="Hello"
535
- style a fill="#ff0000" stroke="#cc0000" font-size=16
536
- ```
537
-
538
- This merges with any existing styles on the element. Useful for separating layout from styling.
539
-
540
- ---
541
-
542
- ### Typography
543
-
544
- Typography properties work on all text-bearing elements.
545
-
546
- | Property | Applies to | Description |
547
- |---|---|---|
548
- | `font` | all | Font family or built-in name |
549
- | `font-size` | all | Font size in px |
550
- | `font-weight` | all | `400`, `500`, `600`, `700` |
551
- | `letter-spacing` | all | Letter spacing in px |
552
- | `text-align` | nodes, notes, table cells, markdown | `left`, `center`, `right` |
553
- | `vertical-align` | nodes, notes | `top`, `middle`, `bottom` |
554
- | `line-height` | nodes, notes, markdown | Multiplier e.g. `1.4` |
555
-
556
- ```
557
- # diagram-wide font
558
- config font=caveat
559
-
560
- # per-element overrides
561
- box title label="Heading" font=playfair font-size=20 text-align=left
562
- box body label="Body text" font=system vertical-align=top
563
- note n label="Annotation" font=caveat font-size=13 line-height=1.6
564
- ```
565
-
566
- ---
567
-
568
- ### Animation Steps
569
-
570
- ```
571
- step action target [options]
572
- ```
573
-
574
- All actions work on **all element types** — nodes, groups, tables, notes, charts, and edges.
575
-
576
- **Actions:**
577
-
578
- | Action | Syntax | Description |
579
- |---|---|---|
580
- | `highlight` | `step highlight id` | Pulsing glow |
581
- | `fade` | `step fade id` | Fade to 22% opacity |
582
- | `unfade` | `step unfade id` | Restore full opacity |
583
- | `draw` | `step draw id` | Stroke-draw reveal |
584
- | `draw` | `step draw a-->b` | Animate edge in |
585
- | `erase` | `step erase id` | Fade to invisible |
586
- | `show` | `step show id` | Make hidden element visible |
587
- | `hide` | `step hide id` | Hide element |
588
- | `pulse` | `step pulse id` | Single brightness flash |
589
- | `color` | `step color id fill="#ff0000"` | Change fill color |
590
- | `move` | `step move id dx=50 dy=0` | Translate by dx/dy px |
591
- | `scale` | `step scale id factor=1.5` | Scale (absolute) |
592
- | `rotate` | `step rotate id deg=45` | Rotate (cumulative) |
593
-
594
- **Options:**
595
-
596
- | Option | Description |
597
- |---|---|
598
- | `duration=600` | Animation duration in ms |
599
- | `delay=100` | Delay before animation starts in ms |
600
- | `dx=100` | X offset for `move` |
601
- | `dy=-80` | Y offset for `move` |
602
- | `factor=1.5` | Scale multiplier |
603
- | `deg=45` | Rotation degrees |
604
-
605
- **Behaviour:**
606
- - `move` cumulative. `dx=50` twice = 100px total
607
- - `scale` absolute. `factor=1.0` always resets to normal
608
- - `rotate` — cumulative. `deg=-45` rotates back
609
- - `color` — use `fill=` syntax: `step color id fill="#ff0000"`
610
-
611
- **Slide-in entrance:**
612
- ```
613
- step move id dx=0 dy=80 # snap below final position
614
- step draw id # reveal at offset
615
- step move id dx=0 dy=-80 # animate up into place
616
- ```
617
-
618
- **Wobble-and-fail:**
619
- ```
620
- step rotate id deg=8
621
- step rotate id deg=-8
622
- step rotate id deg=8
623
- step rotate id deg=25 # cumulative = 33°
624
- step fade id
625
- ```
626
-
627
- **Edge animations:**
628
- ```
629
- step highlight a-->b
630
- step color a-->b fill="#ff0000"
631
- step fade a-->b
632
- step move a-->b dx=0 dy=-20 duration=400
633
- ```
634
-
635
- ---
636
-
637
- ## Layout System
638
-
639
- ### Root layout
640
-
641
- ```
642
- layout row # items flow left to right (default)
643
- layout column # items flow top to bottom
644
- layout grid # grid — set columns with: config columns=N
645
- ```
646
-
647
- ### Group layout
648
-
649
- Each group is an independent flex container:
650
-
651
- ```
652
- group g layout=row justify=space-between width=500 gap=16 padding=20
653
- ```
654
-
655
- ### `justify` values
656
-
657
- | Value | Effect |
658
- |---|---|
659
- | `start` | Pack to start (default) |
660
- | `center` | Center in container |
661
- | `end` | Pack to end |
662
- | `space-between` | First at start, last at end, equal gaps between |
663
- | `space-around` | Equal space around each child |
664
-
665
- ### `align` values
666
-
667
- | Value | Effect |
668
- |---|---|
669
- | `start` | Cross-axis start (default) |
670
- | `center` | Cross-axis center |
671
- | `end` | Cross-axis end |
672
-
673
- ---
674
-
675
- ## Animation System
676
-
677
- ```typescript
678
- const { anim } = render({ container, dsl });
679
-
680
- anim.next(); // advance one step
681
- anim.prev(); // go back one step
682
- anim.reset(); // return to initial state
683
- anim.goTo(3); // jump to step index
684
- await anim.play(800); // auto-play, 800ms per step
685
-
686
- anim.currentStep // current index (-1 = not started)
687
- anim.total // total step count
688
- anim.canNext // boolean
689
- anim.canPrev // boolean
690
-
691
- anim.on((event) => {
692
- // event.type: 'step-change' | 'animation-reset' |
693
- // 'animation-start' | 'animation-end' | 'step-complete'
694
- });
695
- ```
696
-
697
- ---
698
-
699
- ## Theme Palettes
700
-
701
- | Name | Description |
702
- |---|---|
703
- | `light` | Warm parchment (default) |
704
- | `dark` | Dark warm background |
705
- | `ocean` | Cool blues |
706
- | `forest` | Greens |
707
- | `sunset` | Warm oranges and reds |
708
- | `slate` | Cool grays |
709
- | `rose` | Pinks and magentas |
710
- | `midnight` | GitHub-dark style blues |
711
- | `sketch` | Graphite pencil-on-paper |
712
-
713
- ```
714
- config theme=sketch
715
- ```
716
-
717
- List all palette names at runtime:
718
-
719
- ```typescript
720
- import { THEME_NAMES } from 'sketchmark';
721
- // ['light', 'dark', 'ocean', 'forest', 'sunset', 'slate', 'rose', 'midnight', 'sketch']
722
- ```
723
-
724
- ---
725
-
726
- ## Font System
727
-
728
- ### Built-in fonts
729
-
730
- | Name | Style |
731
- |---|---|
732
- | `caveat` | Hand-drawn, casual |
733
- | `handlee` | Hand-drawn, friendly |
734
- | `indie-flower` | Hand-drawn, playful |
735
- | `patrick-hand` | Hand-drawn, clean |
736
- | `dm-mono` | Monospace, refined |
737
- | `jetbrains` | Monospace, code-like |
738
- | `instrument` | Serif, editorial |
739
- | `playfair` | Serif, elegant |
740
- | `system` | System UI sans-serif |
741
- | `mono` | Courier New |
742
- | `serif` | Georgia |
743
-
744
- Built-in fonts load automatically from Google Fonts on first use.
745
-
746
- ```
747
- config font=caveat # diagram-wide
748
-
749
- box a label="Hand-drawn" font=caveat
750
- box b label="Code style" font=dm-mono font-size=11
751
- ```
752
-
753
- ### Custom fonts
754
-
755
- ```typescript
756
- import { registerFont } from 'sketchmark';
757
-
758
- // font already loaded in the page via or @import
759
- registerFont('brand', '"Brand Sans", sans-serif');
760
-
761
- // then use in DSL
762
- // config font=brand
763
- // box a font=brand
764
- ```
765
-
766
- Or pass a full CSS family directly in DSL (must be quoted):
767
-
768
- ```
769
- box a label="Hello" font="'Pacifico', cursive"
770
- ```
771
-
772
- ---
773
-
774
- ## API Reference
775
-
776
- ### `render(options): DiagramInstance`
777
-
778
- ```typescript
779
- import { render } from 'sketchmark';
780
-
781
- const instance = render({
782
- container: '#my-div',
783
- dsl: '...',
784
- renderer: 'svg', // 'svg' (default) | 'canvas'
785
- injectCSS: true,
786
- svgOptions: {
787
- showTitle: true,
788
- interactive: true,
789
- roughness: 1.3,
790
- bowing: 0.7,
791
- theme: 'light', // 'light' | 'dark' | 'auto'
792
- transparent: false, // remove background rect
793
- onNodeClick: (nodeId) => {},
794
- },
795
- canvasOptions: {
796
- scale: 2,
797
- roughness: 1.3,
798
- transparent: false,
799
- },
800
- onNodeClick: (nodeId) => {},
801
- onReady: (anim, svg) => {},
802
- });
803
- ```
804
-
805
- `theme: 'auto'` follows OS `prefers-color-scheme`. `transparent: true` removes the background so the diagram floats over the page.
806
-
807
- **`DiagramInstance`:**
808
-
809
- ```typescript
810
- instance.scene // SceneGraph
811
- instance.anim // AnimationController
812
- instance.svg // SVGSVGElement
813
- instance.canvas // HTMLCanvasElement
814
- instance.update(dsl) // re-render with new DSL
815
- instance.exportSVG() // download SVG
816
- instance.exportPNG() // download PNG
817
- ```
818
-
819
- ---
820
-
821
- ### Pipeline API
822
-
823
- ```typescript
824
- import { parse, buildSceneGraph, layout, renderToSVG } from 'sketchmark';
825
-
826
- const ast = parse(dslString);
827
- const scene = buildSceneGraph(ast);
828
- layout(scene);
829
- const svg = renderToSVG(scene, containerEl, options);
830
- ```
831
-
832
- ---
833
-
834
- ### `parse(dsl: string): DiagramAST`
835
-
836
- ```typescript
837
- import { parse, ParseError } from 'sketchmark';
838
-
839
- try {
840
- const ast = parse(dsl);
841
- } catch (e) {
842
- if (e instanceof ParseError) {
843
- console.error(`Line ${e.line}, Col ${e.col}: ${e.message}`);
844
- }
845
- }
846
- ```
847
-
848
- ---
849
-
850
- ## Export
851
-
852
- ```typescript
853
- import { exportSVG, exportPNG, exportHTML, getSVGBlob } from 'sketchmark';
854
-
855
- exportSVG(svgEl, { filename: 'diagram.svg' });
856
- await exportPNG(svgEl, { filename: 'diagram.png', scale: 2 });
857
- exportHTML(svgEl, dslSource, { filename: 'diagram.html' });
858
-
859
- const blob = getSVGBlob(svgEl);
860
- ```
861
-
862
- Via instance:
863
-
864
- ```typescript
865
- instance.exportSVG('diagram.svg');
866
- await instance.exportPNG('diagram.png');
867
- ```
868
-
869
- ---
870
-
871
- ## Examples
872
-
873
- ### Basic architecture
874
-
875
- ```
876
- diagram
877
- title label="System Architecture"
878
- layout row
879
- config gap=60
880
-
881
- theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
882
- theme success fill="#e8ffe8" stroke="#007700" color="#004400"
883
- theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
884
-
885
- box client label="Client App" theme=primary width=140 height=55
886
- box gateway label="API Gateway" theme=muted width=140 height=55
887
-
888
- group services label="Services" layout=column gap=16 padding=30 theme=muted
889
- {
890
- box auth label="Auth Service" theme=primary width=130 height=50
891
- box data label="Data Service" theme=primary width=130 height=50
892
- }
893
-
894
- cylinder db label="PostgreSQL" theme=success width=140 height=65
895
-
896
- client --> gateway label="HTTPS"
897
- gateway --> auth
898
- gateway --> data
899
- auth --> db label="SQL"
900
- data --> db label="SQL"
901
- end
902
- ```
903
-
904
- ---
905
-
906
- ### Animated deployment
907
-
908
- ```
909
- diagram
910
- title label="Blue-Green Deployment"
911
- layout row
912
- config gap=50
913
-
914
- theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
915
- theme success fill="#e8ffe8" stroke="#007700" color="#004400"
916
- theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
917
-
918
- box lb label="Load Balancer" theme=muted width=150 height=55
919
-
920
- group blue label="Blue (live)" layout=column gap=16 padding=26 theme=primary
921
- {
922
- box b1 label="API v1" theme=primary width=120 height=50
923
- box b2 label="Worker v1" theme=primary width=120 height=50
924
- }
925
-
926
- group green label="Green (new)" layout=column gap=16 padding=26 theme=success
927
- {
928
- box g1 label="API v2" theme=success width=120 height=50
929
- box g2 label="Worker v2" theme=success width=120 height=50
930
- }
931
-
932
- lb --> b1 label="100%"
933
- lb --> g1 label="0%"
934
-
935
- step highlight lb
936
- step draw lb-->b1
937
- step highlight b1
938
- step move g1 dx=0 dy=60
939
- step move g2 dx=0 dy=60
940
- step draw g1
941
- step move g1 dx=0 dy=-60 duration=500
942
- step draw g2
943
- step move g2 dx=0 dy=-60 duration=500
944
- step fade b1
945
- step fade b2
946
- step draw lb-->g1
947
- step highlight g1
948
- end
949
- ```
950
-
951
- ---
952
-
953
- ### Markdown with diagram
954
-
955
- ```
956
- diagram
957
- layout row
958
- config gap=60
959
-
960
- theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
961
- theme success fill="#e8ffe8" stroke="#007700" color="#004400"
962
- theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
963
-
964
- bare page layout=row gap=60
965
- {
966
- markdown intro width=320 font=caveat padding=0
967
- """
968
- # Sketchmark
969
-
970
- A text-based diagram DSL that renders
971
- **hand-drawn** SVG diagrams using rough.js.
972
-
973
- ## Animation
974
-
975
- Every element supports **step-by-step**
976
- animation draw, highlight, fade, move.
977
- """
978
-
979
- group diagram layout=column gap=20 padding=30 theme=muted
980
- {
981
- box parser label="Parser" theme=primary width=130 height=52
982
- box scene label="Scene" theme=success width=130 height=52
983
- box render label="Renderer" theme=muted width=130 height=52
984
- }
985
- }
986
-
987
- parser --> scene label="AST"
988
- scene --> render label="SceneGraph"
989
-
990
- step draw intro
991
- step highlight parser
992
- step draw parser-->scene
993
- step highlight scene
994
- step draw scene-->render
995
- step highlight render
996
- end
997
- ```
998
-
999
- ---
1000
-
1001
- ### Charts
1002
-
1003
- ```
1004
- diagram
1005
- layout row
1006
- config gap=40
1007
-
1008
- bar-chart revenue label="Monthly Revenue" width=340 height=240
1009
- data
1010
- [
1011
- ["Month", "2023", "2024"],
1012
- ["Jan", 42000, 58000 ],
1013
- ["Feb", 38000, 61000 ],
1014
- ["Mar", 51000, 67000 ],
1015
- ["Apr", 46000, 72000 ]
1016
- ]
1017
-
1018
- pie-chart share label="Market Share" width=280 height=240
1019
- data
1020
- [
1021
- ["Product A", 42],
1022
- ["Product B", 31],
1023
- ["Product C", 27]
1024
- ]
1025
- end
1026
- ```
1027
-
1028
- ---
1029
-
1030
- ### Sketch theme
1031
-
1032
- ```
1033
- diagram
1034
- title label="System Architecture"
1035
- config theme=sketch
1036
- config font=caveat
1037
- layout row
1038
- config gap=60
1039
-
1040
- group root layout=row gap=60 padding=0
1041
- {
1042
- box client label="Client App" width=140 height=55
1043
- box gateway label="API Gateway" width=140 height=55
1044
-
1045
- group services layout=column gap=16 padding=30
1046
- {
1047
- box auth label="Auth Service" width=140 height=55
1048
- box billing label="Billing Service" width=140 height=55
1049
- }
1050
-
1051
- cylinder db label="PostgreSQL" width=140 height=65
1052
- }
1053
-
1054
- client --> gateway label="HTTPS"
1055
- gateway --> auth
1056
- gateway --> billing
1057
- auth --> db label="SQL"
1058
- billing --> db label="SQL"
1059
- end
1060
- ```
1061
-
1062
- ---
1063
-
1064
- ## License
1065
-
1066
- MIT
1
+ # Sketchmark
2
+
3
+ > Hand-drawn, rough-style diagrams from a plain-text DSL.
4
+ > Live examples: **https://sketchmark.dev/examples**
5
+
6
+ ---
7
+
8
+ ## Table of Contents
9
+
10
+ 1. [Installation](#installation)
11
+ 2. [Quick Start](#quick-start)
12
+ 3. [Framework Setup](#framework-setup)
13
+ - [Plain HTML (CDN)](#plain-html-cdn)
14
+ - [Vite / Vanilla TS](#vite--vanilla-ts)
15
+ - [Next.js (App Router)](#nextjs-app-router)
16
+ 4. [DSL Syntax Overview](#dsl-syntax-overview)
17
+ 5. [Nodes](#nodes)
18
+ - [Shapes](#shapes)
19
+ - [Node Properties](#node-properties)
20
+ 6. [Edges (Connectors)](#edges-connectors)
21
+ 7. [Groups](#groups)
22
+ 8. [Tables](#tables)
23
+ 9. [Charts](#charts)
24
+ 10. [Markdown Blocks](#markdown-blocks)
25
+ 11. [Themes](#themes)
26
+ 12. [Fonts](#fonts)
27
+ 13. [Animation System](#animation-system)
28
+ 14. [Config Options](#config-options)
29
+ 15. [Export](#export)
30
+ 16. [Supported vs Unsupported Features](#supported-vs-unsupported-features)
31
+ 17. [Full DSL Reference Table](#full-dsl-reference-table)
32
+ 18. [Complete Example](#complete-example)
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install sketchmark
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Quick Start
45
+
46
+ ```javascript
47
+ import { render } from 'sketchmark';
48
+
49
+ const dsl = `
50
+ diagram
51
+ box a label="Hello"
52
+ box b label="World"
53
+ a --> b label="greets"
54
+ end
55
+ `.trim();
56
+
57
+ const instance = render({
58
+ container: document.getElementById('diagram'),
59
+ dsl,
60
+ renderer: 'svg',
61
+ svgOptions: { showTitle: true, theme: 'light', transparent: true },
62
+ });
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Framework Setup
68
+
69
+ ### Plain HTML (CDN)
70
+
71
+ ```html
72
+ <!DOCTYPE html>
73
+ <html>
74
+ <head>
75
+ <!-- rough.js MUST load before sketchmark -->
76
+ <script src="https://unpkg.com/roughjs@4.6.6/bundled/rough.js"></script>
77
+ </head>
78
+ <body>
79
+ <div id="diagram"></div>
80
+
81
+ <script type="module">
82
+ import { render } from 'https://unpkg.com/sketchmark/dist/index.js';
83
+
84
+ const dsl = `
85
+ diagram
86
+ box a label="Client"
87
+ box b label="Server"
88
+ a --> b label="HTTP"
89
+ end
90
+ `.trim();
91
+
92
+ const instance = render({
93
+ container: document.getElementById('diagram'),
94
+ dsl,
95
+ renderer: 'svg',
96
+ svgOptions: { showTitle: true, interactive: true, theme: 'light', transparent: true },
97
+ });
98
+
99
+ // Animation controls
100
+ const { anim } = instance;
101
+ document.getElementById('btn-next').addEventListener('click', () => anim.next());
102
+ document.getElementById('btn-play').addEventListener('click', () => anim.play(700));
103
+ </script>
104
+ </body>
105
+ </html>
106
+ ```
107
+
108
+ ---
109
+
110
+ ### Vite / Vanilla TS
111
+
112
+ **`src/main.ts`**:
113
+
114
+ ```typescript
115
+ import { render } from 'sketchmark';
116
+
117
+
118
+ const dsl = `
119
+ diagram
120
+ title label="My Architecture"
121
+ layout row
122
+ config gap=60
123
+
124
+ box client label="Client App" width=140 height=55
125
+ box server label="API Server" width=140 height=55
126
+ cylinder db label="PostgreSQL" width=140 height=65
127
+
128
+ client --> server label="HTTPS"
129
+ server --> db label="SQL"
130
+
131
+ step highlight client
132
+ step draw client-->server
133
+ step highlight server
134
+ step draw server-->db
135
+ end
136
+ `.trim();
137
+
138
+ const instance = render({
139
+ container: document.getElementById('diagram') as HTMLElement,
140
+ dsl,
141
+ renderer: 'svg',
142
+ svgOptions: { showTitle: true, interactive: true, theme: 'light', transparent: true },
143
+ });
144
+
145
+ const { anim } = instance;
146
+ // Wire up buttons
147
+ document.getElementById('btn-next')!.addEventListener('click', () => { anim.next(); });
148
+ document.getElementById('btn-play')!.addEventListener('click', async () => { await anim.play(700); });
149
+ document.getElementById('btn-reset')!.addEventListener('click', () => { anim.reset(); });
150
+ ```
151
+
152
+ ---
153
+
154
+ ### Next.js (App Router)
155
+
156
+ Three rules that **must** all be followed:
157
+
158
+ **Rule 1 — Component must be `'use client'`**:
159
+
160
+ ```tsx
161
+ // src/components/SketchmarkDiagram.tsx
162
+ 'use client';
163
+
164
+ import { useEffect, useRef } from 'react';
165
+ import { render } from 'sketchmark';
166
+
167
+ interface Props {
168
+ dsl: string;
169
+ showTitle?: boolean;
170
+ showControls?: boolean;
171
+ theme?: 'light' | 'dark';
172
+ }
173
+
174
+ export default function SketchmarkDiagram({ dsl, showTitle = true, theme = 'light' }: Props) {
175
+ const containerRef = useRef<HTMLDivElement>(null);
176
+
177
+ useEffect(() => {
178
+ const el = containerRef.current;
179
+ if (!el) return;
180
+ el.innerHTML = '';
181
+ render({
182
+ container: el,
183
+ dsl,
184
+ renderer: 'svg',
185
+ svgOptions: { showTitle, theme, transparent: true, interactive: true },
186
+ });
187
+ }, [dsl, showTitle, theme]);
188
+
189
+ return <div ref={containerRef} />;
190
+ }
191
+ ```
192
+
193
+ **Rule 3 DSL strings must never have leading whitespace**:
194
+
195
+ ```typescript
196
+ // ✅ Correct — use .trim()
197
+ const dsl = `
198
+ diagram
199
+ box a label="Hello"
200
+ box b label="World"
201
+ a --> b
202
+ end
203
+ `.trim();
204
+
205
+ // Wrong leading spaces break the parser
206
+ const dsl = `
207
+ diagram
208
+ box a label="Hello"
209
+ `.trim();
210
+ ```
211
+ ---
212
+
213
+ ## DSL Syntax Overview
214
+
215
+ Every diagram follows this structure:
216
+
217
+ ```
218
+ diagram
219
+ [title label="My Title"]
220
+ [layout row|column|grid]
221
+ [config key=value ...]
222
+ [theme name fill="..." stroke="..." color="..."]
223
+
224
+ [nodes, edges, groups, tables, charts, markdown blocks]
225
+
226
+ [step action target ...]
227
+ end
228
+ ```
229
+
230
+ - Lines starting with `#` or `//` are comments.
231
+ - All key-value attributes use the `key=value` or `key="quoted value"` syntax.
232
+ - The DSL is **whitespace-sensitive** — do not indent lines.
233
+
234
+ ---
235
+
236
+ ## Nodes
237
+
238
+ ### Shapes
239
+
240
+ Every node has the form:
241
+ ```
242
+ <shape> <id> [label="..."] [property=value ...]
243
+ ```
244
+
245
+ | Shape | DSL Keyword | Description | Auto-sizes to |
246
+ |-------|-------------|-------------|---------------|
247
+ | Box (default) | `box` | Rectangle | label width |
248
+ | Circle | `circle` | Ellipse | label width |
249
+ | Diamond | `diamond` | Rhombus (decision) | label width + padding |
250
+ | Hexagon | `hexagon` | Six-sided polygon | label width + padding |
251
+ | Triangle | `triangle` | Triangle | label width + padding |
252
+ | Cylinder | `cylinder` | Database drum shape | label width, fixed height 66px |
253
+ | Parallelogram | `parallelogram` | Slanted rectangle | label width + skew |
254
+ | Text | `text` | Label only, no border | wraps to width |
255
+ | Image | `image` | URL-loaded image | label width |
256
+ | Icon | `icon` | Iconify icon | 48×48 + label |
257
+ | Line | `line` | Horizontal rule | label width |
258
+ | Path | `path` | Custom SVG path data | user-specified |
259
+ | Note | `note` | Sticky-note shape | line count × line height |
260
+
261
+ ```
262
+ # Examples of each shape
263
+ box myBox label="A Box" width=140 height=55
264
+ circle myCirc label="A Circle" width=100
265
+ diamond myDia label="Decision?" width=150
266
+ hexagon myHex label="Process"
267
+ triangle myTri label="Start"
268
+ cylinder myDb label="PostgreSQL" height=65
269
+ parallelogram myPara label="I/O"
270
+ text myTxt label="Some prose" width=300
271
+ image myImg label="Logo" url="https://example.com/logo.png" width=120 height=60
272
+ icon myIcon label="Settings" name="mdi:cog"
273
+ line myLine label="Section" width=200
274
+ path myPath value="M 0 0 L 50 50 L 100 0 Z" width=100 height=60
275
+ note myNote label="Remember this!"
276
+ ```
277
+
278
+ ---
279
+
280
+ ### Node Properties
281
+
282
+ | Property | Type | Description | Example |
283
+ |----------|------|-------------|---------|
284
+ | `label` | string | Display text (required) | `label="Hello World"` |
285
+ | `width` | number | Override auto-width (px) | `width=140` |
286
+ | `height` | number | Override auto-height (px) | `height=55` |
287
+ | `theme` | string | Named theme preset | `theme=primary` |
288
+ | `fill` | CSS color | Background fill color | `fill="#e8f4ff"` |
289
+ | `stroke` | CSS color | Border/outline color | `stroke="#0044cc"` |
290
+ | `stroke-width` | number | Border thickness | `stroke-width=2` |
291
+ | `color` | CSS color | Text color | `color="#003399"` |
292
+ | `opacity` | 0–1 | Element opacity | `opacity=0.5` |
293
+ | `font-size` | number | Text size in px | `font-size=16` |
294
+ | `font-weight` | number/string | Font weight | `font-weight=700` |
295
+ | `font` | string | Font name (see Fonts section) | `font=caveat` |
296
+ | `text-align` | left/center/right | Horizontal text alignment | `text-align=left` |
297
+ | `vertical-align` | top/middle/bottom | Vertical text alignment | `vertical-align=top` |
298
+ | `line-height` | number | Line height multiplier | `line-height=1.5` |
299
+ | `letter-spacing` | number | Letter spacing in px | `letter-spacing=2` |
300
+ | `padding` | number | Inner padding (px) | `padding=12` |
301
+ | `dash` | numbers | Stroke dash pattern | `dash="6,3"` |
302
+ | `url` | URL string | Image URL (for `image` shape) | `url="https://..."` |
303
+ | `name` | string | Iconify icon name (for `icon`) | `name="mdi:cog"` |
304
+ | `value` | string | SVG path data (for `path`) | `value="M 0 0 L 50 0"` |
305
+ | `deg` | number | Static rotation (degrees) | `deg=45` |
306
+ | `dx` | number | Static X translation (px) | `dx=20` |
307
+ | `dy` | number | Static Y translation (px) | `dy=-10` |
308
+ | `factor` | number | Static scale factor | `factor=1.2` |
309
+
310
+ ---
311
+
312
+ ## Edges (Connectors)
313
+
314
+ Edges connect two node/group IDs:
315
+
316
+ ```
317
+ <from> <connector> <to> [label="..."] [style properties]
318
+ ```
319
+
320
+ ### Connector Syntax
321
+
322
+ | Connector | Direction | Dashed | Arrowhead |
323
+ |-----------|-----------|--------|-----------|
324
+ | `->` | Forward | No | Single end |
325
+ | `-->` | Forward | Yes | Single end |
326
+ | `<-` | Backward | No | Single start |
327
+ | `<--` | Backward | Yes | Single start |
328
+ | `<->` | Bidirectional | No | Both ends |
329
+ | `<-->` | Bidirectional | Yes | Both ends |
330
+ | `--` | None | No | No arrow |
331
+ | `---` | None | Yes | No arrow |
332
+
333
+ ```
334
+ # Edge examples
335
+ a -> b
336
+ a --> b label="Async call"
337
+ a <-> b
338
+ a <--> b label="Sync"
339
+ a -- b # line, no arrow
340
+ a --- b # dashed line, no arrow
341
+
342
+ # With style overrides
343
+ a --> b label="HTTPS" stroke="#cc0000" stroke-width=2 color="#aa0000" font-size=10
344
+ ```
345
+
346
+ ### Edge Style Properties
347
+
348
+ | Property | Description |
349
+ |----------|-------------|
350
+ | `label` | Text label floating on the edge |
351
+ | `stroke` | Line color |
352
+ | `stroke-width` | Line thickness |
353
+ | `color` | Label text color |
354
+ | `font-size` | Label font size |
355
+ | `font` | Label font family |
356
+ | `letter-spacing` | Label letter spacing |
357
+
358
+ ---
359
+
360
+ ## Groups
361
+
362
+ Groups visually contain one or more nodes, tables, or nested groups.
363
+
364
+ ```
365
+ group <id> [label="..."] [layout=row|column|grid] [gap=N] [padding=N]
366
+ [columns=N] [align=start|center|end]
367
+ [justify=start|center|end|space-between|space-around]
368
+ [theme=...] [fill="..."] [stroke="..."] [width=N] [height=N]
369
+ {
370
+ box a label="Node A"
371
+ box b label="Node B"
372
+ # nested groups allowed:
373
+ group inner label="Inner Group" layout=row { box c label="C" }
374
+ }
375
+ ```
376
+
377
+ ### Group Properties
378
+
379
+ | Property | Type | Description |
380
+ |----------|------|-------------|
381
+ | `label` | string | Group title (shown at top) |
382
+ | `layout` | row / column / grid | Child arrangement direction |
383
+ | `gap` | number | Space between children (px) |
384
+ | `padding` | number | Inner padding (px) |
385
+ | `columns` | number | Column count (for `layout=grid`) |
386
+ | `align` | start/center/end | Cross-axis alignment (align-items) |
387
+ | `justify` | start/center/end/space-between/space-around | Main-axis alignment |
388
+ | `width` | number | Minimum width override |
389
+ | `height` | number | Minimum height override |
390
+ | `theme` | string | Named theme preset |
391
+ | `fill` | CSS color | Background color |
392
+ | `stroke` | CSS color | Border color |
393
+ | `stroke-width` | number | Border thickness |
394
+
395
+ #### `bare` keyword
396
+
397
+ `bare` is an alias for a group with no visible border or fill:
398
+
399
+ ```
400
+ bare myContainer {
401
+ box a label="Floating A"
402
+ box b label="Floating B"
403
+ }
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Tables
409
+
410
+ ```
411
+ table <id> [label="..."] [theme=...] [fill="..."] [stroke="..."]
412
+ {
413
+ header Col1 Col2 Col3
414
+ row "Value A" "Value B" "Value C"
415
+ row "Value D" "Value E" "Value F"
416
+ }
417
+ ```
418
+
419
+ - `header` rows get a shaded background and bold text.
420
+ - `row` rows use regular styling.
421
+ - `"value"` must use a double-quoted string literal.
422
+ - Column widths auto-size to content.
423
+ - Tables support `fill`, `stroke`, `color`, `font-size`, `font`, `text-align`, `letter-spacing`, `theme`, `opacity` style props (same as nodes).
424
+
425
+ ---
426
+
427
+ ## Charts
428
+
429
+ ```
430
+ <chart-type> <id> [label="Title"] [width=N] [height=N] [theme=...] [style props]
431
+ data
432
+ [["Category", "Series1", "Series2"],
433
+ ["Jan", 120, 80],
434
+ ["Feb", 150, 90]]
435
+ ```
436
+
437
+ ### Chart Types
438
+
439
+ | DSL Keyword | Chart | Notes |
440
+ |-------------|-------|-------|
441
+ | `bar-chart` | Grouped bar chart | Multiple series supported |
442
+ | `line-chart` | Line chart | Multiple series supported |
443
+ | `area-chart` | Area/filled line chart | Multiple series supported |
444
+ | `pie-chart` | Pie chart | `["Label", value]` rows |
445
+ | `donut-chart` | Donut chart | Same data as pie |
446
+ | `scatter-chart` | Scatter plot | `["Label", x, y]` rows |
447
+
448
+ ```
449
+ # Bar chart example
450
+ bar-chart sales label="Monthly Sales" width=400 height=280
451
+ data
452
+ [["Month", "Revenue", "Cost"],
453
+ ["Jan", 1200, 800],
454
+ ["Feb", 1500, 900],
455
+ ["Mar", 1100, 750]]
456
+
457
+ # Pie chart example
458
+ pie-chart share label="Market Share" width=300 height=240
459
+ data
460
+ [["Company", "Share"],
461
+ ["Alpha", 42],
462
+ ["Beta", 31],
463
+ ["Gamma", 27]]
464
+ ```
465
+
466
+ ---
467
+
468
+ ## Markdown Blocks
469
+
470
+ Renders inline rich text with headings and bold/italic:
471
+
472
+ ```
473
+ markdown <id> [width=N] [height=N] [theme=...] [style props]
474
+ """
475
+ # Heading 1
476
+ ## Heading 2
477
+ ### Heading 3
478
+
479
+ Normal paragraph with **bold** and *italic* text.
480
+
481
+ Another paragraph here.
482
+ """
483
+ ```
484
+
485
+ - Triple-quote `"""` delimiters for the content block.
486
+ - Supported formatting: `# H1`, `## H2`, `### H3`, `**bold**`, `*italic*`, blank lines.
487
+ - Style props: `color`, `font`, `font-size`, `text-align`, `padding`, `fill`, `stroke`, `opacity`, `letter-spacing`.
488
+
489
+ ---
490
+
491
+ ## Themes
492
+
493
+ ### Global Palette Themes
494
+
495
+ Activate via `config theme=<name>` in the DSL.
496
+
497
+ | Theme Name | Description |
498
+ |------------|-------------|
499
+ | `light` | Warm parchment (default) |
500
+ | `dark` | Dark warm brown |
501
+ | `sketch` | Neutral grey pencil |
502
+ | `ocean` | Blue tones |
503
+ | `forest` | Green tones |
504
+ | `sunset` | Orange/red warm |
505
+ | `slate` | Cool blue-grey (like Tailwind) |
506
+ | `rose` | Pink/rose |
507
+ | `midnight` | GitHub dark-style |
508
+
509
+ ```
510
+ # Activate in DSL
511
+ config theme=ocean
512
+
513
+ # Or pass as render option
514
+ render({ ..., svgOptions: { theme: 'dark' } });
515
+ # 'auto' follows system prefers-color-scheme
516
+ render({ ..., svgOptions: { theme: 'auto' } });
517
+ ```
518
+
519
+ ### Named Custom Themes (per-element)
520
+
521
+ Define a named theme and apply it to any element with `theme=<name>`:
522
+
523
+ ```
524
+ theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
525
+ theme success fill="#e8ffe8" stroke="#007700" color="#004400"
526
+ theme warning fill="#fff9e6" stroke="#f0a500" color="#7a5000"
527
+ theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
528
+
529
+ box client label="Client" theme=primary
530
+ box server label="Server" theme=warning
531
+ cylinder db label="DB" theme=success
532
+ group services label="Services" theme=muted { ... }
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Fonts
538
+
539
+ Set globally with `config font=<name>` or per-element with `font=<name>`.
540
+
541
+ | Font Name | Family | Type |
542
+ |-----------|--------|------|
543
+ | `caveat` | Caveat | Hand-drawn cursive |
544
+ | `handlee` | Handlee | Hand-drawn cursive |
545
+ | `indie-flower` | Indie Flower | Hand-drawn cursive |
546
+ | `patrick-hand` | Patrick Hand | Hand-drawn cursive |
547
+ | `dm-mono` | DM Mono | Monospace |
548
+ | `jetbrains` | JetBrains Mono | Monospace |
549
+ | `instrument` | Instrument Serif | Serif |
550
+ | `playfair` | Playfair Display | Serif |
551
+ | `system` | system-ui, sans-serif | System (default) |
552
+ | `mono` | Courier New | Monospace |
553
+ | `serif` | Georgia | Serif |
554
+
555
+ ```
556
+ # Global font
557
+ config font=caveat
558
+
559
+ # Per-element font
560
+ box a label="Handwritten" font=caveat
561
+ box b label="Mono" font=dm-mono
562
+ ```
563
+
564
+ You can also pass any valid CSS font-family string directly.
565
+
566
+ ---
567
+
568
+ ## Animation System
569
+
570
+ ### Step Syntax
571
+
572
+ ```
573
+ step <action> <target> [options]
574
+ ```
575
+
576
+ - `<target>` is a node/group/table/chart/markdown ID, or an edge in `from-->to` format.
577
+ - Steps play in sequence via `anim.next()` or `anim.play(msPerStep)`.
578
+
579
+ ### Animation Actions
580
+
581
+ | Action | Target | Options | Description |
582
+ |--------|--------|---------|-------------|
583
+ | `highlight` | node/edge/group | | Pulsing glow highlight (only one element at a time) |
584
+ | `draw` | node/edge/group/table/chart/markdown | `duration=N` | Animated stroke-drawing reveal |
585
+ | `fade` | node/edge/group | | Fade to 22% opacity |
586
+ | `unfade` | node/edge/group | | Restore from fade |
587
+ | `erase` | node/edge/group | `duration=N` | Fade to invisible (opacity 0) |
588
+ | `show` | node/edge/group | `duration=N` | Fade to visible |
589
+ | `hide` | node/edge/group | `duration=N` | Fade to hidden |
590
+ | `pulse` | node/edge/group | `duration=N` | One-shot brightness pulse |
591
+ | `move` | node | `dx=N dy=N duration=N` | Translate by (dx, dy) px |
592
+ | `scale` | node | `factor=N duration=N` | Scale to factor (absolute) |
593
+ | `rotate` | node | `deg=N duration=N` | Rotate by deg (cumulative) |
594
+ | `color` | node/edge | `fill="#..."` or `color="#..."` | Change fill/stroke color |
595
+
596
+ ### Step Options
597
+
598
+ | Option | Description | Default |
599
+ |--------|-------------|---------|
600
+ | `duration=N` | Animation duration in ms | varies by type |
601
+ | `delay=N` | Delay before this step fires (ms) | 0 |
602
+ | `dx=N` | X translation for `move` | 0 |
603
+ | `dy=N` | Y translation for `move` | 0 |
604
+ | `factor=N` | Scale factor for `scale` | 1 |
605
+ | `deg=N` | Rotation degrees for `rotate` | 0 |
606
+ | `fill="..."` | New color for `color` action | |
607
+ | `color="..."` | Alias for fill in `color` action | |
608
+
609
+ ### Animation Examples
610
+
611
+ ```
612
+ # Draw edges incrementally
613
+ step draw client-->server
614
+ step highlight server
615
+ step draw server-->db
616
+ step highlight db
617
+
618
+ # Move a node
619
+ step move myBox dx=100 dy=0 duration=500
620
+
621
+ # Scale up then back
622
+ step scale myBox factor=1.5 duration=300
623
+ step scale myBox factor=1 duration=300
624
+
625
+ # Rotate
626
+ step rotate myBox deg=45 duration=400
627
+
628
+ # Change color
629
+ step color myBox fill="#ff0000"
630
+
631
+ # Show/hide
632
+ step hide myBox duration=400
633
+ step show myBox duration=400
634
+
635
+ # Fade background nodes
636
+ step fade nodeA
637
+ step unfade nodeA
638
+
639
+ # Pulse a node
640
+ step pulse myBox duration=600
641
+
642
+ # Delay before a step
643
+ step highlight server delay=500
644
+ ```
645
+
646
+ ### JavaScript Animation API
647
+
648
+ ```javascript
649
+ const { anim } = render({ ... });
650
+
651
+ // Properties
652
+ anim.total // number of steps
653
+ anim.currentStep // current step index (-1 = before start)
654
+ anim.canNext // boolean
655
+ anim.canPrev // boolean
656
+ anim.atEnd // boolean
657
+
658
+ // Methods
659
+ anim.next() // advance one step (returns bool)
660
+ anim.prev() // go back one step (returns bool)
661
+ anim.reset() // reset to before step 0
662
+ anim.goTo(index) // jump to step N
663
+ await anim.play(700) // play all remaining steps (700ms between)
664
+
665
+ // Event listener
666
+ const unsub = anim.on((event) => {
667
+ console.log(event.type); // 'step-change' | 'animation-start' | 'animation-end' | 'animation-reset'
668
+ console.log(event.stepIndex); // number
669
+ console.log(event.step); // ASTStep object
670
+ console.log(event.total); // total steps
671
+ });
672
+ unsub(); // unsubscribe
673
+ ```
674
+
675
+ ### Pre-hidden Elements (Draw Targets)
676
+
677
+ Any element targeted by a `step draw` action starts **hidden** and only appears when that step fires. Elements NOT targeted by `draw` are always visible.
678
+
679
+ ---
680
+
681
+ ## Config Options
682
+
683
+ Set in DSL with `config key=value`:
684
+
685
+ | Key | Description | Default |
686
+ |-----|-------------|---------|
687
+ | `theme` | Global palette name | `light` |
688
+ | `font` | Global font name | `system` |
689
+ | `gap` | Space between root-level items (px) | `80` |
690
+ | `margin` | Canvas outer margin (px) | `60` |
691
+ | `columns` | Column count for `layout=grid` | `1` |
692
+ | `title-color` | Diagram title text color | palette default |
693
+ | `title-size` | Diagram title font size | `18` |
694
+ | `title-weight` | Diagram title font weight | `600` |
695
+
696
+ ```
697
+ diagram
698
+ title label="My System"
699
+ layout row
700
+ config gap=60
701
+ config margin=40
702
+ config theme=ocean
703
+ config font=caveat
704
+ config title-size=24
705
+ config title-color="#001f5b"
706
+ ```
707
+
708
+ ---
709
+
710
+ ## Export
711
+
712
+ ### From JavaScript API
713
+
714
+ ```javascript
715
+ const instance = render({ ... });
716
+
717
+ // SVG file download
718
+ instance.exportSVG('my-diagram.svg');
719
+
720
+ // PNG file download
721
+ await instance.exportPNG('my-diagram.png');
722
+
723
+ // Advanced: get SVG string
724
+ import { getSVGString } from 'sketchmark';
725
+ const svgString = getSVGString(instance.svg);
726
+
727
+ // Advanced: get PNG data URL
728
+ import { svgToPNGDataURL } from 'sketchmark';
729
+ const dataUrl = await svgToPNGDataURL(instance.svg, { scale: 2, background: '#ffffff' });
730
+
731
+ // Self-contained HTML file
732
+ import { exportHTML } from 'sketchmark';
733
+ exportHTML(instance.svg, dslSource, { filename: 'diagram.html' });
734
+ ```
735
+
736
+ ### Export Options
737
+
738
+ | Option | Type | Description |
739
+ |--------|------|-------------|
740
+ | `filename` | string | Download filename |
741
+ | `scale` | number | PNG pixel density (default: 2 = @2x) |
742
+ | `background` | CSS color | PNG background color (default: `#f8f4ea`) |
743
+
744
+ ### Available Export Functions
745
+
746
+ | Function | Output | Status |
747
+ |----------|--------|--------|
748
+ | `exportSVG(svg, opts)` | `.svg` file download | ✅ Stable |
749
+ | `exportPNG(svg, opts)` | `.png` file download | ✅ Stable |
750
+ | `exportHTML(svg, dsl, opts)` | Self-contained `.html` | ✅ Stable |
751
+ | `exportCanvasPNG(canvas, opts)` | `.png` from canvas renderer | ✅ Stable |
752
+ | `getSVGString(svg)` | SVG string | ✅ Stable |
753
+ | `getSVGBlob(svg)` | SVG Blob | ✅ Stable |
754
+ | `svgToPNGDataURL(svg, opts)` | PNG data URL string | ✅ Stable |
755
+ | `exportGIF(frames, opts)` | GIF (requires gifshot) | ⚠️ Stub only |
756
+ | `exportMP4(canvas, dur, opts)` | WebM via MediaRecorder | ⚠️ Stub only |
757
+
758
+ ---
759
+
760
+ ## Supported vs Unsupported Features
761
+
762
+ ### Nodes
763
+
764
+ | Feature | Supported | Notes |
765
+ |---------|-----------|-------|
766
+ | box | | Default shape |
767
+ | circle | ✅ | |
768
+ | diamond | ✅ | |
769
+ | hexagon | | |
770
+ | triangle | ✅ | |
771
+ | cylinder | ✅ | |
772
+ | parallelogram | ✅ | |
773
+ | text | ✅ | Auto word-wraps |
774
+ | image (URL) | ✅ | Cross-origin images |
775
+ | icon (Iconify) | ✅ | Uses Iconify API |
776
+ | line | ✅ | Horizontal rule with label |
777
+ | path (SVG path data) | ✅ | Raw SVG `d` attribute |
778
+ | note | ✅ | Sticky-note shape |
779
+ | Multiline label (`\n`) | ✅ | Use `\n` in label strings |
780
+ | Per-node font override | ✅ | |
781
+ | Per-node opacity | ✅ | |
782
+ | Per-node static transform (deg/dx/dy/factor) | ✅ | Set at parse time |
783
+ | Rounded corners | ❌ | Not configurable |
784
+ | Custom SVG shapes | ❌ | Only `path` workaround |
785
+ | Rich text inside nodes | ❌ | Plain text only |
786
+
787
+ ### Edges
788
+
789
+ | Feature | Supported | Notes |
790
+ |---------|-----------|-------|
791
+ | Single arrow `->` / `-->` | | |
792
+ | Reverse arrow `<-` / `<--` | ✅ | |
793
+ | Bidirectional `<->` / `<-->` | ✅ | |
794
+ | No arrow `--` / `---` | ✅ | |
795
+ | Dashed lines (`--`, `---`, `<-->`) | ✅ | |
796
+ | Edge labels | ✅ | |
797
+ | Edge color/stroke override | ✅ | |
798
+ | Self-loops | ❌ | |
799
+ | Curved/bezier edges | ❌ | Straight lines only |
800
+ | Waypoints / routing control | ❌ | Auto-routed |
801
+ | Multiple edges between same nodes | ✅ | Stack visually |
802
+ | Edge from/to groups | ✅ | Uses group center |
803
+
804
+ ### Groups
805
+
806
+ | Feature | Supported | Notes |
807
+ |---------|-----------|-------|
808
+ | Nested groups | ✅ | Unlimited depth |
809
+ | Row / column / grid layout | ✅ | |
810
+ | justify-content variants | ✅ | start, center, end, space-between, space-around |
811
+ | align-items variants | ✅ | start, center, end |
812
+ | Fixed width/height | ✅ | Minimum size override |
813
+ | `bare` (invisible group) | ✅ | |
814
+ | Scrolling | | |
815
+
816
+ ### Charts
817
+
818
+ | Feature | Supported | Notes |
819
+ |---------|-----------|-------|
820
+ | Bar chart (grouped) | ✅ | Multiple series |
821
+ | Line chart | ✅ | Multiple series |
822
+ | Area chart | ✅ | Multiple series |
823
+ | Pie chart | ✅ | |
824
+ | Donut chart | | |
825
+ | Scatter plot | ✅ | |
826
+ | Axes and tick labels | ✅ | Auto-generated Y axis |
827
+ | Legend | ✅ | Auto-generated |
828
+ | X-axis labels | ✅ | |
829
+ | Interactive tooltips | ❌ | |
830
+ | Stacked bars | ❌ | |
831
+ | Custom colors per series | ❌ | Uses built-in palette |
832
+ | Logarithmic scale | ❌ | |
833
+
834
+ ### Animation
835
+
836
+ | Feature | Supported | Notes |
837
+ |---------|-----------|-------|
838
+ | highlight | ✅ | Pulsing glow |
839
+ | draw (nodes) | ✅ | Stroke-path reveal |
840
+ | draw (edges) | ✅ | Animated line draw |
841
+ | draw (groups) | ✅ | |
842
+ | draw (tables) | | |
843
+ | draw (charts) | | Fade-in |
844
+ | draw (markdown) | ✅ | Fade-in |
845
+ | fade / unfade | ✅ | |
846
+ | show / hide | ✅ | |
847
+ | erase | ✅ | |
848
+ | pulse | ✅ | |
849
+ | move | ✅ | CSS transform translate |
850
+ | scale | ✅ | CSS transform scale |
851
+ | rotate | ✅ | CSS transform rotate |
852
+ | color | ✅ | Dynamic fill/stroke color change |
853
+ | delay per step | | `delay=N` ms |
854
+ | custom duration | ✅ | `duration=N` ms |
855
+ | Canvas renderer animation | ❌ | SVG renderer only |
856
+ | Click-triggered steps | | Parsed but not implemented |
857
+ | Parallel steps | | Sequential only |
858
+
859
+ ### Export
860
+
861
+ | Feature | Supported |
862
+ |---------|-----------|
863
+ | SVG download | ✅ |
864
+ | PNG download (via canvas) | ✅ |
865
+ | HTML (self-contained) | ✅ |
866
+ | SVG string / Blob | ✅ |
867
+ | GIF | ❌ (stub) |
868
+ | MP4/WebM | ❌ (stub) |
869
+
870
+ ---
871
+
872
+ ## Full DSL Reference Table
873
+
874
+ | Keyword | Category | Example |
875
+ |---------|----------|---------|
876
+ | `diagram` | Structure | `diagram` |
877
+ | `end` | Structure | `end` |
878
+ | `title` | Meta | `title label="My Diagram"` |
879
+ | `description` | Meta | `description "Some text"` |
880
+ | `layout` | Meta | `layout row` / `layout column` / `layout grid` |
881
+ | `config` | Meta | `config gap=60` |
882
+ | `theme` | Styling | `theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"` |
883
+ | `style` | Styling | `style nodeId fill="#ff0" stroke="#000"` |
884
+ | `box` | Node | `box myId label="Label" width=120 height=50` |
885
+ | `circle` | Node | `circle myId label="Label"` |
886
+ | `diamond` | Node | `diamond myId label="Decision?"` |
887
+ | `hexagon` | Node | `hexagon myId label="Process"` |
888
+ | `triangle` | Node | `triangle myId label="Start"` |
889
+ | `cylinder` | Node | `cylinder myId label="DB" height=65` |
890
+ | `parallelogram` | Node | `parallelogram myId label="I/O"` |
891
+ | `text` | Node | `text myId label="Plain text" width=300` |
892
+ | `image` | Node | `image myId label="Logo" url="https://..."` |
893
+ | `icon` | Node | `icon myId label="Settings" name="mdi:cog"` |
894
+ | `line` | Node | `line myId label="Divider" width=400` |
895
+ | `path` | Node | `path myId value="M 0 0 L 100 0 L 50 80 Z"` |
896
+ | `note` | Node | `note myId label="Sticky note text"` |
897
+ | `->` | Edge | `a -> b label="call"` |
898
+ | `-->` | Edge | `a --> b` |
899
+ | `<-` | Edge | `a <- b` |
900
+ | `<--` | Edge | `a <-- b` |
901
+ | `<->` | Edge | `a <-> b` |
902
+ | `<-->` | Edge | `a <--> b` |
903
+ | `--` | Edge | `a -- b` |
904
+ | `---` | Edge | `a --- b` |
905
+ | `group` | Group | `group myGroup label="Services" layout=column { ... }` |
906
+ | `bare` | Group | `bare myWrap { ... }` |
907
+ | `table` | Table | `table myTable label="Users" { header Name Age }` |
908
+ | `bar-chart` | Chart | `bar-chart sales label="Sales" data [...]` |
909
+ | `line-chart` | Chart | `line-chart trend data [...]` |
910
+ | `pie-chart` | Chart | `pie-chart share data [...]` |
911
+ | `donut-chart` | Chart | `donut-chart share data [...]` |
912
+ | `scatter-chart` | Chart | `scatter-chart pts data [...]` |
913
+ | `area-chart` | Chart | `area-chart filled data [...]` |
914
+ | `markdown` | Markdown | `markdown md1 """ # Title ... """` |
915
+ | `step highlight` | Animation | `step highlight nodeId` |
916
+ | `step draw` | Animation | `step draw nodeId` / `step draw a-->b` |
917
+ | `step fade` | Animation | `step fade nodeId` |
918
+ | `step unfade` | Animation | `step unfade nodeId` |
919
+ | `step erase` | Animation | `step erase nodeId duration=400` |
920
+ | `step show` | Animation | `step show nodeId duration=300` |
921
+ | `step hide` | Animation | `step hide nodeId duration=300` |
922
+ | `step pulse` | Animation | `step pulse nodeId duration=500` |
923
+ | `step move` | Animation | `step move nodeId dx=100 dy=0 duration=400` |
924
+ | `step scale` | Animation | `step scale nodeId factor=1.5 duration=300` |
925
+ | `step rotate` | Animation | `step rotate nodeId deg=90 duration=400` |
926
+ | `step color` | Animation | `step color nodeId fill="#ff0000"` |
927
+
928
+ ---
929
+
930
+ ## Complete Example
931
+
932
+ This example demonstrates most features in a single diagram:
933
+
934
+ ```
935
+ diagram
936
+ title label="Full Stack Architecture"
937
+ layout row
938
+ config gap=50
939
+ config theme=light
940
+ config font=system
941
+
942
+ # Define named themes
943
+ theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
944
+ theme success fill="#e8ffe8" stroke="#007700" color="#004400"
945
+ theme warning fill="#fff9e6" stroke="#f0a500" color="#7a5000"
946
+ theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
947
+
948
+ # Standalone node
949
+ box client label="Browser" theme=primary width=120 height=50
950
+
951
+ # Group of services
952
+ group backend label="Backend" layout=column gap=12 padding=24 theme=muted
953
+ {
954
+ box api label="REST API" theme=warning width=130 height=48
955
+ box auth label="Auth" theme=primary width=130 height=48
956
+ }
957
+
958
+ # Database
959
+ cylinder db label="PostgreSQL" theme=success width=130 height=65
960
+
961
+ # Edges
962
+ client --> api label="HTTPS"
963
+ api --> auth
964
+ api --> db label="SQL"
965
+ auth --> db label="SQL"
966
+
967
+ # Inline table
968
+ table users label="Users Table"
969
+ {
970
+ header ID Name Email
971
+ row "1" "Alice" "alice@example.com"
972
+ row "2" "Bob" "bob@example.com"
973
+ }
974
+
975
+ # A chart
976
+ bar-chart traffic label="Daily Traffic" width=300 height=200
977
+ data
978
+ [["Day", "Requests"],
979
+ ["Mon", 1200],
980
+ ["Tue", 1500],
981
+ ["Wed", 900]]
982
+
983
+ # Animation sequence
984
+ step highlight client
985
+ step draw client-->api
986
+ step highlight api
987
+ step draw api-->auth
988
+ step draw api-->db
989
+ step highlight db
990
+ end
991
+ ```
992
+
993
+ ---
994
+
995
+ ## `render()` API Reference
996
+
997
+ ```typescript
998
+ render(options: RenderOptions): DiagramInstance
999
+
1000
+ interface RenderOptions {
1001
+ container: string | HTMLElement | SVGSVGElement; // CSS selector or element
1002
+ dsl: string; // DSL source text
1003
+ renderer?: 'svg' | 'canvas'; // default: 'svg'
1004
+ injectCSS?: boolean; // inject animation CSS (default: true)
1005
+ svgOptions?: SVGRendererOptions;
1006
+ canvasOptions?: CanvasRendererOptions;
1007
+ onNodeClick?: (nodeId: string) => void; // click handler
1008
+ onReady?: (anim, svg?) => void; // callback after render
1009
+ }
1010
+
1011
+ interface SVGRendererOptions {
1012
+ roughness?: number; // default 1.3
1013
+ bowing?: number; // default 0.7
1014
+ showTitle?: boolean; // show diagram title
1015
+ interactive?: boolean; // enable hover/click
1016
+ onNodeClick?: (id: string) => void;
1017
+ theme?: 'light' | 'dark' | 'auto';
1018
+ transparent?: boolean; // transparent background
1019
+ }
1020
+
1021
+ interface DiagramInstance {
1022
+ scene: SceneGraph;
1023
+ anim: AnimationController;
1024
+ svg?: SVGSVGElement;
1025
+ canvas?: HTMLCanvasElement;
1026
+ update: (dsl: string) => DiagramInstance; // re-render with new DSL
1027
+ exportSVG: (filename?: string) => void;
1028
+ exportPNG: (filename?: string) => Promise<void>;
1029
+ }
1030
+ ```
1031
+
1032
+ ---
1033
+
1034
+ ## Important Gotchas
1035
+
1036
+ 1. **DSL must not be indented** — every DSL line must start at column 0 (no leading spaces/tabs).
1037
+ 2. **Always call `.trim()`** on template literals to strip the leading newline from the opening backtick.
1038
+ 3. **Animation only works with SVG renderer** — the canvas renderer does not support animated steps.
1039
+ 4. **`step draw` makes elements start hidden** — any element you intend to `draw` will be invisible until its step fires.
1040
+ 5. **Node IDs must be unique** — duplicate IDs are silently deduplicated (only first definition kept).
1041
+ 6. **Group children inherit group's coordinate space** — edges can connect across group boundaries using the node/group ID directly.