sketchmark 0.1.1 → 0.1.3

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.
package/README.md CHANGED
@@ -31,14 +31,18 @@ end
31
31
  - [Node Shapes](#node-shapes)
32
32
  - [Edges](#edges)
33
33
  - [Groups](#groups)
34
+ - [Bare Groups](#bare-groups)
34
35
  - [Tables](#tables)
35
36
  - [Notes](#notes)
36
37
  - [Charts](#charts)
38
+ - [Markdown Blocks](#markdown-blocks)
37
39
  - [Themes](#themes)
40
+ - [Typography](#typography)
38
41
  - [Animation Steps](#animation-steps)
39
42
  - [Layout System](#layout-system)
40
43
  - [Animation System](#animation-system)
41
44
  - [Theme Palettes](#theme-palettes)
45
+ - [Font System](#font-system)
42
46
  - [API Reference](#api-reference)
43
47
  - [Export](#export)
44
48
  - [Examples](#examples)
@@ -71,7 +75,7 @@ const instance = render({
71
75
  a --> b label="connects"
72
76
  `,
73
77
  renderer: 'svg',
74
- svgOptions: { showTitle: true, interactive: true,transparent: true },
78
+ svgOptions: { showTitle: true, interactive: true, transparent: true },
75
79
  });
76
80
 
77
81
  // Step through animation
@@ -79,18 +83,20 @@ instance.anim.next();
79
83
  instance.anim.play(800);
80
84
  ```
81
85
 
82
- **CDN / no bundler:**
86
+ **CDN / no bundler with import map:**
83
87
 
84
88
  ```html
85
- <script src="https://unpkg.com/roughjs@4.6.6/bundled/rough.js"></script>
86
- <script type="module">
87
- import { render } from 'https://unpkg.com/sketchmark/dist/index.js';
88
89
 
90
+
91
+ { "imports": { "sketchmark": "https://unpkg.com/sketchmark/dist/index.js" } }
92
+
93
+
94
+ import { render } from 'sketchmark';
89
95
  render({
90
96
  container: document.getElementById('diagram'),
91
97
  dsl: `diagram\nbox a label="Hello"\nbox b label="World"\na --> b`,
92
98
  });
93
- </script>
99
+
94
100
  ```
95
101
 
96
102
  **CommonJS:**
@@ -125,41 +131,54 @@ end
125
131
  | `config gap` | `config gap=60` | Gap between root-level items (default: 80) |
126
132
  | `config margin` | `config margin=40` | Outer canvas margin (default: 60) |
127
133
  | `config theme` | `config theme=ocean` | Global palette (see [Theme Palettes](#theme-palettes)) |
134
+ | `config font` | `config font=caveat` | Diagram-wide font (see [Font System](#font-system)) |
128
135
 
129
136
  ---
130
137
 
131
138
  ### Node Shapes
132
139
 
133
140
  ```
134
- box id label="..." [theme=X] [width=N] [height=N]
135
- circle id label="..."
136
- diamond id label="..."
137
- hexagon id label="..."
138
- triangle id label="..."
139
- cylinder id label="..."
140
- parallelogram id label="..."
141
- text id label="..."
142
- image id label="..." url="https://..."
141
+ box id label="..." [theme=X] [width=N] [height=N]
142
+ circle id label="..."
143
+ diamond id label="..."
144
+ hexagon id label="..."
145
+ triangle id label="..."
146
+ cylinder id label="..."
147
+ parallelogram id label="..."
148
+ text id label="..."
149
+ image id label="..." url="https://..."
143
150
  ```
144
151
 
145
152
  **Common properties:**
146
153
 
147
154
  | Property | Example | Description |
148
155
  |---|---|---|
149
- | `label` | `label="API Gateway"` | Display text |
150
- | `theme` | `theme=primary` | Named theme (defined with `theme` keyword) |
156
+ | `label` | `label="API Gateway"` | Display text. Use `\n` for line breaks |
157
+ | `theme` | `theme=primary` | Named theme |
151
158
  | `width` | `width=140` | Override auto-width in px |
152
159
  | `height` | `height=55` | Override auto-height in px |
153
160
  | `fill` | `fill="#e8f4ff"` | Background fill color |
154
161
  | `stroke` | `stroke="#0044cc"` | Border color |
155
162
  | `color` | `color="#003399"` | Text color |
156
- | `font-size` | `font-size=12` | Label font size |
163
+ | `font` | `font=caveat` | Font family or built-in name |
164
+ | `font-size` | `font-size=12` | Label font size in px |
165
+ | `font-weight` | `font-weight=600` | Font weight |
166
+ | `letter-spacing` | `letter-spacing=2` | Letter spacing in px |
167
+ | `text-align` | `text-align=left` | `left`, `center`, `right` |
168
+ | `vertical-align` | `vertical-align=top` | `top`, `middle`, `bottom` |
169
+ | `line-height` | `line-height=1.6` | Line height multiplier |
170
+
171
+ > **`text` shape:** No border or background. Long labels auto word-wrap. Use `width=` to control the wrap width.
172
+
173
+ > **`image` shape:** Renders an image clipped to a rounded rect. Requires `url=` property.
157
174
 
158
175
  **Example:**
159
176
  ```
160
177
  box gateway label="API Gateway" theme=warning width=150 height=55
161
178
  circle user label="User" fill="#e8f4ff" stroke="#0044cc" color="#003399"
162
179
  cylinder db label="PostgreSQL" theme=success width=140 height=65
180
+ image logo label="Logo" url="https://example.com/logo.png" width=80 height=80
181
+ text caption label="This auto-wraps across multiple lines." width=300
163
182
  ```
164
183
 
165
184
  ---
@@ -167,7 +186,7 @@ cylinder db label="PostgreSQL" theme=success width=140 height=65
167
186
  ### Edges
168
187
 
169
188
  ```
170
- fromId connector toId [label="..."]
189
+ fromId connector toId [label="..."] [stroke="#color"] [stroke-width=N]
171
190
  ```
172
191
 
173
192
  **Connectors:**
@@ -182,12 +201,25 @@ fromId connector toId [label="..."]
182
201
  | `--` | none | solid |
183
202
  | `---` | none | dashed |
184
203
 
204
+ **Edge style properties:**
205
+
206
+ | Property | Description |
207
+ |---|---|
208
+ | `label` | Text label on the edge |
209
+ | `stroke` | Line and arrowhead color |
210
+ | `stroke-width` | Line thickness |
211
+ | `color` | Label text color |
212
+ | `font` | Label font |
213
+ | `font-size` | Label font size |
214
+ | `theme` | Apply a named theme to the edge |
215
+
185
216
  **Example:**
186
217
  ```
187
218
  client --> gateway label="HTTPS"
188
219
  gateway <-> auth label="verify"
189
- db --- replica label="sync"
190
- a --- b
220
+ a -- b
221
+ db --- replica
222
+ api --> db stroke="#0044cc" stroke-width=2 theme=primary
191
223
  ```
192
224
 
193
225
  ---
@@ -200,13 +232,10 @@ Groups are containers that arrange children using a flexbox-style layout.
200
232
  group id [label="..."] [layout=row|column|grid] [gap=N] [padding=N]
201
233
  [justify=start|center|end|space-between|space-around]
202
234
  [align=start|center|end]
203
- [columns=N]
204
- [width=N] [height=N]
205
- [theme=X]
235
+ [columns=N] [width=N] [height=N] [theme=X]
236
+ [font=X] [font-size=N] [letter-spacing=N]
206
237
  {
207
- box child1 label="..."
208
- box child2 label="..."
209
- group nested { ... }
238
+ ...children...
210
239
  }
211
240
  ```
212
241
 
@@ -214,17 +243,18 @@ group id [label="..."] [layout=row|column|grid] [gap=N] [padding=N]
214
243
 
215
244
  | Property | Default | Description |
216
245
  |---|---|---|
246
+ | `label` | — | Label at top-left. Omit or `""` for no label and no reserved space |
217
247
  | `layout` | `column` | `row`, `column`, or `grid` |
218
248
  | `gap` | `10` | Space between children in px |
219
249
  | `padding` | `26` | Inner padding in px |
220
- | `justify` | `start` | Main-axis distribution of children |
221
- | `align` | `start` | Cross-axis alignment of children |
222
- | `columns` | `1` | Number of columns when `layout=grid` |
223
- | `width` | auto | Minimum width — enables `justify` distribution |
250
+ | `justify` | `start` | Main-axis distribution |
251
+ | `align` | `start` | Cross-axis alignment |
252
+ | `columns` | `1` | Columns when `layout=grid` |
253
+ | `width` | auto | Minimum width — enables `justify` |
224
254
  | `height` | auto | Minimum height |
225
255
  | `theme` | — | Named theme for border/background |
226
256
 
227
- > **Note on `justify`:** Requires an explicit `width` larger than the children's total width to have visible effect. Without extra space, all five values look identical.
257
+ > **`justify`** requires an explicit `width` larger than total child width to have a visible effect.
228
258
 
229
259
  **Example:**
230
260
  ```
@@ -235,48 +265,85 @@ group services label="Microservices" layout=column gap=16 padding=30 theme=muted
235
265
  }
236
266
  ```
237
267
 
238
- **Grid layout:**
268
+ **Grid:**
239
269
  ```
240
- group icons layout=grid columns=3 gap=20 padding=24 width=400
270
+ group icons layout=grid columns=3 gap=20 padding=24
241
271
  {
242
- box a label="A" width=100 height=60
243
- box b label="B" width=100 height=60
244
- box c label="C" width=100 height=60
245
- box d label="D" width=100 height=60
272
+ box a label="A" width=100 height=60
273
+ box b label="B" width=100 height=60
274
+ box c label="C" width=100 height=60
246
275
  }
247
276
  ```
248
277
 
249
- **Justify example:**
278
+ **Space-between with explicit width:**
250
279
  ```
251
280
  group nav layout=row justify=space-between width=500 padding=20 gap=10
252
281
  {
253
- box home label="Home" width=80 height=40
254
- box about label="About" width=80 height=40
282
+ box home label="Home" width=80 height=40
283
+ box about label="About" width=80 height=40
255
284
  box contact label="Contact" width=80 height=40
256
285
  }
257
286
  ```
258
287
 
259
288
  ---
260
289
 
290
+ ### Bare Groups
291
+
292
+ `bare` is a layout-only container — no label, no border, no background, no padding by default. All group layout properties apply.
293
+
294
+ ```
295
+ bare id [layout=...] [gap=N] [padding=N] [justify=...] [align=...] [columns=N]
296
+ {
297
+ ...children...
298
+ }
299
+ ```
300
+
301
+ Any explicitly set style overrides the bare defaults:
302
+
303
+ ```
304
+ # fully invisible layout container
305
+ bare page layout=row gap=60
306
+ {
307
+ markdown intro width=340
308
+ """
309
+ # Title
310
+ Some prose here.
311
+ """
312
+
313
+ group diagram layout=column gap=20 padding=30 theme=muted
314
+ {
315
+ box a label="A" theme=primary width=130 height=52
316
+ }
317
+ }
318
+
319
+ # bare with a visible border
320
+ bare wrapper layout=row gap=20 stroke="#cccccc" padding=16
321
+ {
322
+ box x label="X" width=100 height=50
323
+ box y label="Y" width=100 height=50
324
+ }
325
+ ```
326
+
327
+ ---
328
+
261
329
  ### Tables
262
330
 
263
331
  ```
264
- table id [label="..."] [theme=X]
332
+ table id [label="..."] [theme=X] [font=X] [font-size=N] [text-align=left|center|right]
265
333
  {
266
334
  header Col1 Col2 Col3
267
335
  row val1 val2 val3
268
- row val4 val5 val6
269
336
  }
270
337
  ```
271
338
 
272
339
  **Example:**
273
340
  ```
274
- table pricing label="Pricing Plans"
341
+ table pricing label="Pricing Plans" font=dm-mono text-align=right
275
342
  {
276
- header Plan Price Requests
277
- row Free $0 1k/day
278
- row Pro $29 100k/day
279
- row Enterprise $299 Unlimited
343
+ header Plan Price Requests
344
+ row Free $0 1k/day
345
+ row Pro $29 100k/day
346
+ row Enterprise $299 Unlimited
280
347
  }
281
348
  ```
282
349
 
@@ -284,11 +351,23 @@ table pricing label="Pricing Plans"
284
351
 
285
352
  ### Notes
286
353
 
287
- Single or multiline sticky notes.
354
+ Sticky notes with a folded corner.
288
355
 
289
356
  ```
290
- note id label="Single line note" [theme=X]
357
+ note id label="Single line" [theme=X]
291
358
  note id label="Line one\nLine two\nLine three"
359
+ note id label="..." [width=N] [height=N]
360
+ [font=X] [font-size=N] [letter-spacing=N]
361
+ [text-align=left|center|right]
362
+ [vertical-align=top|middle|bottom]
363
+ [line-height=1.4]
364
+ ```
365
+
366
+ **Example:**
367
+ ```
368
+ note n1 label="Rate limited\nto 1000 req/s\nper tenant"
369
+ note n2 label="Postgres 16\nwith pg_vector" width=200 height=100 font-size=13
370
+ note n3 label="Centered" text-align=center vertical-align=middle width=180 height=80
292
371
  ```
293
372
 
294
373
  ---
@@ -307,38 +386,90 @@ data
307
386
  [
308
387
  ["Label", "Series1", "Series2"],
309
388
  ["Jan", 120, 80 ],
310
- ["Feb", 150, 95 ],
311
- ["Mar", 130, 110 ]
389
+ ["Feb", 150, 95 ]
312
390
  ]
313
391
  ```
314
392
 
315
- **Pie / donut data format:**
393
+ **Pie / donut:**
316
394
  ```
317
- pie-chart revenue title="Revenue Split"
395
+ pie-chart revenue title="Revenue Split" width=280 height=240
318
396
  data
319
397
  [
320
- ["Product", 45],
321
- ["Services", 30],
322
- ["Support", 25]
398
+ ["Product A", 42],
399
+ ["Services", 30],
400
+ ["Support", 25]
323
401
  ]
324
402
  ```
325
403
 
326
- **Scatter data format:**
404
+ ---
405
+
406
+ ### Markdown Blocks
407
+
408
+ Prose content with Markdown-style formatting, rendered inside the diagram layout.
409
+
327
410
  ```
328
- scatter-chart perf title="Performance"
329
- data
330
- [
331
- ["headers", "x", "y"],
332
- ["Server A", 10, 95],
333
- ["Server B", 20, 87]
334
- ]
411
+ markdown id [width=N] [padding=N] [font=X]
412
+ [text-align=left|center|right] [color=X]
413
+ """
414
+ # Heading 1
415
+ ## Heading 2
416
+ ### Heading 3
417
+
418
+ Paragraph with **bold** and *italic* text.
419
+
420
+ Another paragraph.
421
+ """
422
+ ```
423
+
424
+ **Supported syntax:**
425
+
426
+ | Syntax | Result |
427
+ |---|---|
428
+ | `# text` | H1 heading |
429
+ | `## text` | H2 heading |
430
+ | `### text` | H3 heading |
431
+ | `**text**` | Bold |
432
+ | `*text*` | Italic |
433
+ | blank line | Vertical spacing |
434
+
435
+ **Example:**
436
+ ```
437
+ bare page layout=row gap=60
438
+ {
439
+ markdown intro width=320 font=caveat padding=0
440
+ """
441
+ # Sketchmark
442
+
443
+ A text-based diagram DSL that renders
444
+ **hand-drawn** SVG diagrams.
445
+
446
+ ## Animation
447
+
448
+ Every element supports *step-by-step*
449
+ animation — **draw**, highlight, fade, move.
450
+ """
451
+
452
+ group diagram layout=column gap=20 padding=30 theme=muted
453
+ {
454
+ box parser label="Parser" theme=primary width=130 height=52
455
+ box scene label="Scene" theme=success width=130 height=52
456
+ }
457
+ }
458
+
459
+ parser --> scene label="AST"
460
+
461
+ step draw intro
462
+ step highlight parser
463
+ step draw parser-->scene
464
+ step highlight scene
465
+ end
335
466
  ```
336
467
 
337
468
  ---
338
469
 
339
470
  ### Themes
340
471
 
341
- Define reusable style presets with `theme`, then apply them to any node or group.
472
+ Define reusable style presets and apply them to any element.
342
473
 
343
474
  ```
344
475
  theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
@@ -348,7 +479,33 @@ theme danger fill="#ffe8e8" stroke="#cc0000" color="#900000"
348
479
  theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
349
480
  ```
350
481
 
351
- Apply: `box myNode label="..." theme=primary`
482
+ Apply to any element: `box a theme=primary`, `group g theme=muted`, `note n theme=warning`, `a --> b theme=danger`
483
+
484
+ ---
485
+
486
+ ### Typography
487
+
488
+ Typography properties work on all text-bearing elements.
489
+
490
+ | Property | Applies to | Description |
491
+ |---|---|---|
492
+ | `font` | all | Font family or built-in name |
493
+ | `font-size` | all | Font size in px |
494
+ | `font-weight` | all | `400`, `500`, `600`, `700` |
495
+ | `letter-spacing` | all | Letter spacing in px |
496
+ | `text-align` | nodes, notes, table cells, markdown | `left`, `center`, `right` |
497
+ | `vertical-align` | nodes, notes | `top`, `middle`, `bottom` |
498
+ | `line-height` | nodes, notes, markdown | Multiplier e.g. `1.4` |
499
+
500
+ ```
501
+ # diagram-wide font
502
+ config font=caveat
503
+
504
+ # per-element overrides
505
+ box title label="Heading" font=playfair font-size=20 text-align=left
506
+ box body label="Body text" font=system vertical-align=top
507
+ note n label="Annotation" font=caveat font-size=13 line-height=1.6
508
+ ```
352
509
 
353
510
  ---
354
511
 
@@ -358,38 +515,65 @@ Apply: `box myNode label="..." theme=primary`
358
515
  step action target [options]
359
516
  ```
360
517
 
518
+ All actions work on **all element types** — nodes, groups, tables, notes, charts, and edges.
519
+
361
520
  **Actions:**
362
521
 
363
522
  | Action | Syntax | Description |
364
523
  |---|---|---|
365
- | `highlight` | `step highlight nodeId` | Pulsing glow on a node |
366
- | `fade` | `step fade nodeId` | Fade node to 22% opacity |
367
- | `unfade` | `step unfade nodeId` | Restore full opacity |
368
- | `draw` | `step draw nodeId` | Animate node appearing (stroke-draw) |
369
- | `draw` | `step draw a-->b` | Animate edge appearing |
370
- | `erase` | `step erase nodeId` | Fade element to invisible |
371
- | `show` | `step show nodeId` | Make hidden element visible |
372
- | `hide` | `step hide nodeId` | Hide element |
373
- | `pulse` | `step pulse nodeId` | Single brightness flash |
374
- | `color` | `step color nodeId #ff0000` | Change fill color |
375
- | `move` | `step move nodeId dx=50 dy=0` | Translate by dx/dy px |
376
- | `scale` | `step scale nodeId factor=1.5` | Scale (absolute, 1.0 = normal) |
377
- | `rotate` | `step rotate nodeId deg=45` | Rotate (cumulative degrees) |
524
+ | `highlight` | `step highlight id` | Pulsing glow |
525
+ | `fade` | `step fade id` | Fade to 22% opacity |
526
+ | `unfade` | `step unfade id` | Restore full opacity |
527
+ | `draw` | `step draw id` | Stroke-draw reveal |
528
+ | `draw` | `step draw a-->b` | Animate edge in |
529
+ | `erase` | `step erase id` | Fade to invisible |
530
+ | `show` | `step show id` | Make hidden element visible |
531
+ | `hide` | `step hide id` | Hide element |
532
+ | `pulse` | `step pulse id` | Single brightness flash |
533
+ | `color` | `step color id fill="#ff0000"` | Change fill color |
534
+ | `move` | `step move id dx=50 dy=0` | Translate by dx/dy px |
535
+ | `scale` | `step scale id factor=1.5` | Scale (absolute) |
536
+ | `rotate` | `step rotate id deg=45` | Rotate (cumulative) |
378
537
 
379
538
  **Options:**
380
539
 
381
- | Option | Example | Description |
382
- |---|---|---|
383
- | `duration` | `duration=600` | Animation duration in ms |
384
- | `dx` | `dx=100` | X offset for `move` |
385
- | `dy` | `dy=-80` | Y offset for `move` |
386
- | `factor` | `factor=1.5` | Scale multiplier for `scale` |
387
- | `deg` | `deg=45` | Degrees for `rotate` (cumulative) |
540
+ | Option | Description |
541
+ |---|---|
542
+ | `duration=600` | Animation duration in ms |
543
+ | `dx=100` | X offset for `move` |
544
+ | `dy=-80` | Y offset for `move` |
545
+ | `factor=1.5` | Scale multiplier |
546
+ | `deg=45` | Rotation degrees |
547
+
548
+ **Behaviour:**
549
+ - `move` — cumulative. `dx=50` twice = 100px total
550
+ - `scale` — absolute. `factor=1.0` always resets to normal
551
+ - `rotate` — cumulative. `deg=-45` rotates back
552
+ - `color` — use `fill=` syntax: `step color id fill="#ff0000"`
553
+
554
+ **Slide-in entrance:**
555
+ ```
556
+ step move id dx=0 dy=80 # snap below final position
557
+ step draw id # reveal at offset
558
+ step move id dx=0 dy=-80 # animate up into place
559
+ ```
560
+
561
+ **Wobble-and-fail:**
562
+ ```
563
+ step rotate id deg=8
564
+ step rotate id deg=-8
565
+ step rotate id deg=8
566
+ step rotate id deg=25 # cumulative = 33°
567
+ step fade id
568
+ ```
388
569
 
389
- **Notes on `move` / `scale` / `rotate`:**
390
- - `move` is cumulative — `dx=50` twice = 100px total
391
- - `scale` is absolute — `factor=1.5` always means 1.5×, `factor=1.0` resets to normal
392
- - `rotate` is cumulative — `deg=45` twice = 90° total, `deg=-45` rotates back
570
+ **Edge animations:**
571
+ ```
572
+ step highlight a-->b
573
+ step color a-->b fill="#ff0000"
574
+ step fade a-->b
575
+ step move a-->b dx=0 dy=-20 duration=400
576
+ ```
393
577
 
394
578
  ---
395
579
 
@@ -397,12 +581,10 @@ step action target [options]
397
581
 
398
582
  ### Root layout
399
583
 
400
- Controls how top-level items (groups, standalone nodes) are arranged:
401
-
402
584
  ```
403
- layout row # left to right (default)
404
- layout column # top to bottom
405
- layout grid # grid, use config columns=N
585
+ layout row # items flow left to right (default)
586
+ layout column # items flow top to bottom
587
+ layout grid # grid set columns with: config columns=N
406
588
  ```
407
589
 
408
590
  ### Group layout
@@ -417,73 +599,48 @@ group g layout=row justify=space-between width=500 gap=16 padding=20
417
599
 
418
600
  | Value | Effect |
419
601
  |---|---|
420
- | `start` | Pack children to the start (default) |
421
- | `center` | Center children in the container |
422
- | `end` | Pack children to the end |
602
+ | `start` | Pack to start (default) |
603
+ | `center` | Center in container |
604
+ | `end` | Pack to end |
423
605
  | `space-between` | First at start, last at end, equal gaps between |
424
606
  | `space-around` | Equal space around each child |
425
607
 
426
- > Requires `width` wider than total child width to be visible.
427
-
428
608
  ### `align` values
429
609
 
430
610
  | Value | Effect |
431
611
  |---|---|
432
- | `start` | Align to the start of the cross-axis (default) |
433
- | `center` | Center on the cross-axis |
434
- | `end` | Align to the end of the cross-axis |
612
+ | `start` | Cross-axis start (default) |
613
+ | `center` | Cross-axis center |
614
+ | `end` | Cross-axis end |
435
615
 
436
616
  ---
437
617
 
438
618
  ## Animation System
439
619
 
440
- The `AnimationController` is returned as `instance.anim` from `render()`.
441
-
442
620
  ```typescript
443
- const instance = render({ container, dsl });
444
- const { anim } = instance;
621
+ const { anim } = render({ container, dsl });
445
622
 
446
623
  anim.next(); // advance one step
447
624
  anim.prev(); // go back one step
448
625
  anim.reset(); // return to initial state
449
- anim.goTo(3); // jump to specific step index
626
+ anim.goTo(3); // jump to step index
450
627
  await anim.play(800); // auto-play, 800ms per step
451
628
 
452
- anim.currentStep // current step index (-1 = not started)
453
- anim.total // total number of steps
629
+ anim.currentStep // current index (-1 = not started)
630
+ anim.total // total step count
454
631
  anim.canNext // boolean
455
632
  anim.canPrev // boolean
456
633
 
457
- // listen to events
458
634
  anim.on((event) => {
459
635
  // event.type: 'step-change' | 'animation-reset' |
460
636
  // 'animation-start' | 'animation-end' | 'step-complete'
461
- console.log(event.stepIndex, event.step);
462
637
  });
463
638
  ```
464
639
 
465
- **Slide-in entrance pattern:**
466
- ```
467
- step move nodeId dx=0 dy=80 # push below final position
468
- step draw nodeId # reveal at offset
469
- step move nodeId dx=0 dy=-80 # animate into final position
470
- ```
471
-
472
- **Wobble then fail pattern:**
473
- ```
474
- step rotate nodeId deg=8
475
- step rotate nodeId deg=-8
476
- step rotate nodeId deg=8
477
- step rotate nodeId deg=25 # cumulative = 33°, looks like toppling
478
- step fade nodeId
479
- ```
480
-
481
640
  ---
482
641
 
483
642
  ## Theme Palettes
484
643
 
485
- Set a global palette with `config theme=NAME`. Available palettes:
486
-
487
644
  | Name | Description |
488
645
  |---|---|
489
646
  | `light` | Warm parchment (default) |
@@ -494,9 +651,65 @@ Set a global palette with `config theme=NAME`. Available palettes:
494
651
  | `slate` | Cool grays |
495
652
  | `rose` | Pinks and magentas |
496
653
  | `midnight` | GitHub-dark style blues |
654
+ | `sketch` | Graphite pencil-on-paper |
497
655
 
498
656
  ```
499
- config theme=ocean
657
+ config theme=sketch
658
+ ```
659
+
660
+ List all palette names at runtime:
661
+
662
+ ```typescript
663
+ import { THEME_NAMES } from 'sketchmark';
664
+ // ['light', 'dark', 'ocean', 'forest', 'sunset', 'slate', 'rose', 'midnight', 'sketch']
665
+ ```
666
+
667
+ ---
668
+
669
+ ## Font System
670
+
671
+ ### Built-in fonts
672
+
673
+ | Name | Style |
674
+ |---|---|
675
+ | `caveat` | Hand-drawn, casual |
676
+ | `handlee` | Hand-drawn, friendly |
677
+ | `indie-flower` | Hand-drawn, playful |
678
+ | `patrick-hand` | Hand-drawn, clean |
679
+ | `dm-mono` | Monospace, refined |
680
+ | `jetbrains` | Monospace, code-like |
681
+ | `instrument` | Serif, editorial |
682
+ | `playfair` | Serif, elegant |
683
+ | `system` | System UI sans-serif |
684
+ | `mono` | Courier New |
685
+ | `serif` | Georgia |
686
+
687
+ Built-in fonts load automatically from Google Fonts on first use.
688
+
689
+ ```
690
+ config font=caveat # diagram-wide
691
+
692
+ box a label="Hand-drawn" font=caveat
693
+ box b label="Code style" font=dm-mono font-size=11
694
+ ```
695
+
696
+ ### Custom fonts
697
+
698
+ ```typescript
699
+ import { registerFont } from 'sketchmark';
700
+
701
+ // font already loaded in the page via or @import
702
+ registerFont('brand', '"Brand Sans", sans-serif');
703
+
704
+ // then use in DSL
705
+ // config font=brand
706
+ // box a font=brand
707
+ ```
708
+
709
+ Or pass a full CSS family directly in DSL (must be quoted):
710
+
711
+ ```
712
+ box a label="Hello" font="'Pacifico', cursive"
500
713
  ```
501
714
 
502
715
  ---
@@ -505,57 +718,57 @@ config theme=ocean
505
718
 
506
719
  ### `render(options): DiagramInstance`
507
720
 
508
- One-call API. Parses DSL, builds scene, lays out, renders, and returns a controller.
509
-
510
721
  ```typescript
511
722
  import { render } from 'sketchmark';
512
723
 
513
724
  const instance = render({
514
- container: '#my-div', // CSS selector, HTMLElement, or SVGSVGElement
515
- dsl: '...', // DSL source text
516
- renderer: 'svg', // 'svg' (default) | 'canvas'
517
- injectCSS: true, // inject animation CSS into <head>
725
+ container: '#my-div',
726
+ dsl: '...',
727
+ renderer: 'svg', // 'svg' (default) | 'canvas'
728
+ injectCSS: true,
518
729
  svgOptions: {
519
- showTitle: true,
520
- interactive: true, // hover effects + click handlers
521
- roughness: 1.3,
522
- bowing: 0.7,
523
- theme: 'light', // 'light' | 'dark'
524
- onNodeClick: (nodeId) => {},
730
+ showTitle: true,
731
+ interactive: true,
732
+ roughness: 1.3,
733
+ bowing: 0.7,
734
+ theme: 'light', // 'light' | 'dark' | 'auto'
735
+ transparent: false, // remove background rect
736
+ onNodeClick: (nodeId) => {},
525
737
  },
526
738
  canvasOptions: {
527
- scale: 2, // pixel density
528
- roughness: 1.3,
739
+ scale: 2,
740
+ roughness: 1.3,
741
+ transparent: false,
529
742
  },
530
743
  onNodeClick: (nodeId) => {},
531
744
  onReady: (anim, svg) => {},
532
745
  });
533
746
  ```
534
747
 
748
+ `theme: 'auto'` follows OS `prefers-color-scheme`. `transparent: true` removes the background so the diagram floats over the page.
749
+
535
750
  **`DiagramInstance`:**
536
751
 
537
752
  ```typescript
538
- instance.scene // SceneGraph — all positioned nodes, edges, groups
753
+ instance.scene // SceneGraph
539
754
  instance.anim // AnimationController
540
- instance.svg // SVGSVGElement (if renderer='svg')
541
- instance.canvas // HTMLCanvasElement (if renderer='canvas')
755
+ instance.svg // SVGSVGElement
756
+ instance.canvas // HTMLCanvasElement
542
757
  instance.update(dsl) // re-render with new DSL
543
- instance.exportSVG() // download as SVG file
544
- instance.exportPNG() // download as PNG file
758
+ instance.exportSVG() // download SVG
759
+ instance.exportPNG() // download PNG
545
760
  ```
546
761
 
547
762
  ---
548
763
 
549
- ### Pipeline API (low-level)
550
-
551
- Use these if you need to control each step manually:
764
+ ### Pipeline API
552
765
 
553
766
  ```typescript
554
767
  import { parse, buildSceneGraph, layout, renderToSVG } from 'sketchmark';
555
768
 
556
- const ast = parse(dslString); // DSL → AST
557
- const scene = buildSceneGraph(ast); // AST → SceneGraph
558
- layout(scene); // compute x/y positions
769
+ const ast = parse(dslString);
770
+ const scene = buildSceneGraph(ast);
771
+ layout(scene);
559
772
  const svg = renderToSVG(scene, containerEl, options);
560
773
  ```
561
774
 
@@ -563,9 +776,6 @@ const svg = renderToSVG(scene, containerEl, options);
563
776
 
564
777
  ### `parse(dsl: string): DiagramAST`
565
778
 
566
- Tokenizes and parses DSL source into an AST.
567
- Throws `ParseError` with line/col information on invalid syntax.
568
-
569
779
  ```typescript
570
780
  import { parse, ParseError } from 'sketchmark';
571
781
 
@@ -580,54 +790,30 @@ try {
580
790
 
581
791
  ---
582
792
 
583
- ### `buildSceneGraph(ast): SceneGraph`
584
-
585
- Converts AST to a SceneGraph with unpositioned nodes and groups.
586
-
587
- ---
588
-
589
- ### `layout(scene): SceneGraph`
590
-
591
- Computes x/y positions for all elements. Mutates and returns the SceneGraph.
592
-
593
- ---
594
-
595
- ### `renderToSVG(scene, container, options?): SVGSVGElement`
596
-
597
- Renders scene to SVG using rough.js.
598
-
599
- ---
600
-
601
- ### `renderToCanvas(scene, canvas, options?): void`
602
-
603
- Renders scene to an HTML Canvas element using rough.js.
604
-
605
- ---
606
-
607
793
  ## Export
608
794
 
609
795
  ```typescript
610
796
  import { exportSVG, exportPNG, exportHTML, getSVGBlob } from 'sketchmark';
611
797
 
612
- exportSVG(svgElement, { filename: 'diagram.svg' });
613
- await exportPNG(svgElement, { filename: 'diagram.png', scale: 2 });
614
- exportHTML(svgElement, dslSource, { filename: 'diagram.html' });
798
+ exportSVG(svgEl, { filename: 'diagram.svg' });
799
+ await exportPNG(svgEl, { filename: 'diagram.png', scale: 2 });
800
+ exportHTML(svgEl, dslSource, { filename: 'diagram.html' });
615
801
 
616
- // Get blob without downloading
617
- const blob = getSVGBlob(svgElement);
802
+ const blob = getSVGBlob(svgEl);
618
803
  ```
619
804
 
620
- Or via the instance:
805
+ Via instance:
806
+
621
807
  ```typescript
622
- instance.exportSVG('my-diagram.svg');
623
- await instance.exportPNG('my-diagram.png');
808
+ instance.exportSVG('diagram.svg');
809
+ await instance.exportPNG('diagram.png');
624
810
  ```
625
811
 
626
812
  ---
627
813
 
628
814
  ## Examples
629
815
 
630
- ### Basic architecture diagram
816
+ ### Basic architecture
631
817
 
632
818
  ```
633
819
  diagram
@@ -644,8 +830,8 @@ box gateway label="API Gateway" theme=muted width=140 height=55
644
830
 
645
831
  group services label="Services" layout=column gap=16 padding=30 theme=muted
646
832
  {
647
- box auth label="Auth Service" theme=primary width=130 height=50
648
- box data label="Data Service" theme=primary width=130 height=50
833
+ box auth label="Auth Service" theme=primary width=130 height=50
834
+ box data label="Data Service" theme=primary width=130 height=50
649
835
  }
650
836
 
651
837
  cylinder db label="PostgreSQL" theme=success width=140 height=65
@@ -655,7 +841,6 @@ gateway --> auth
655
841
  gateway --> data
656
842
  auth --> db label="SQL"
657
843
  data --> db label="SQL"
658
-
659
844
  end
660
845
  ```
661
846
 
@@ -693,21 +878,64 @@ lb --> g1 label="0%"
693
878
  step highlight lb
694
879
  step draw lb-->b1
695
880
  step highlight b1
696
-
697
- # green slides in from below
698
881
  step move g1 dx=0 dy=60
699
882
  step move g2 dx=0 dy=60
700
883
  step draw g1
701
884
  step move g1 dx=0 dy=-60 duration=500
702
885
  step draw g2
703
886
  step move g2 dx=0 dy=-60 duration=500
704
-
705
- # traffic shifts
706
887
  step fade b1
707
888
  step fade b2
708
889
  step draw lb-->g1
709
890
  step highlight g1
891
+ end
892
+ ```
893
+
894
+ ---
895
+
896
+ ### Markdown with diagram
897
+
898
+ ```
899
+ diagram
900
+ layout row
901
+ config gap=60
710
902
 
903
+ theme primary fill="#e8f4ff" stroke="#0044cc" color="#003399"
904
+ theme success fill="#e8ffe8" stroke="#007700" color="#004400"
905
+ theme muted fill="#f5f5f5" stroke="#999999" color="#444444"
906
+
907
+ bare page layout=row gap=60
908
+ {
909
+ markdown intro width=320 font=caveat padding=0
910
+ """
911
+ # Sketchmark
912
+
913
+ A text-based diagram DSL that renders
914
+ **hand-drawn** SVG diagrams using rough.js.
915
+
916
+ ## Animation
917
+
918
+ Every element supports **step-by-step**
919
+ animation — draw, highlight, fade, move.
920
+ """
921
+
922
+ group diagram layout=column gap=20 padding=30 theme=muted
923
+ {
924
+ box parser label="Parser" theme=primary width=130 height=52
925
+ box scene label="Scene" theme=success width=130 height=52
926
+ box render label="Renderer" theme=muted width=130 height=52
927
+ }
928
+ }
929
+
930
+ parser --> scene label="AST"
931
+ scene --> render label="SceneGraph"
932
+
933
+ step draw intro
934
+ step highlight parser
935
+ step draw parser-->scene
936
+ step highlight scene
937
+ step draw scene-->render
938
+ step highlight render
711
939
  end
712
940
  ```
713
941
 
@@ -737,7 +965,40 @@ data
737
965
  ["Product B", 31],
738
966
  ["Product C", 27]
739
967
  ]
968
+ end
969
+ ```
970
+
971
+ ---
972
+
973
+ ### Sketch theme
974
+
975
+ ```
976
+ diagram
977
+ title label="System Architecture"
978
+ config theme=sketch
979
+ config font=caveat
980
+ layout row
981
+ config gap=60
982
+
983
+ group root layout=row gap=60 padding=0
984
+ {
985
+ box client label="Client App" width=140 height=55
986
+ box gateway label="API Gateway" width=140 height=55
987
+
988
+ group services layout=column gap=16 padding=30
989
+ {
990
+ box auth label="Auth Service" width=140 height=55
991
+ box billing label="Billing Service" width=140 height=55
992
+ }
740
993
 
994
+ cylinder db label="PostgreSQL" width=140 height=65
995
+ }
996
+
997
+ client --> gateway label="HTTPS"
998
+ gateway --> auth
999
+ gateway --> billing
1000
+ auth --> db label="SQL"
1001
+ billing --> db label="SQL"
741
1002
  end
742
1003
  ```
743
1004