@markdy/renderer-dom 0.8.1 → 0.8.2
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 +7 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +192 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@ Web Animations API renderer for [MarkdyScript](../../docs/SYNTAX.md) scenes. Tra
|
|
|
8
8
|
- **Auto-layout diagrams** — renders positioned nodes and orthogonal, obstacle-aware edges from a compiled `RenderPlan`
|
|
9
9
|
- **Flow edges** — `->` request, `<-` response, `~>` event, `--` dependency, each with its own stroke, plus a pulse that travels the edge as it draws
|
|
10
10
|
- **Beat-driven cues** — `show`, `hide`, `glow`, and `focus`, sequenced by named beats
|
|
11
|
+
- **Semantic node cards** — compact SVG glyphs for browsers, services, gateways, queues, workers, databases, storage, CDN, security, platform, and more
|
|
11
12
|
- **Seek-safe** — manual `currentTime` control enables reliable `seek()` in any direction
|
|
13
|
+
- **Playback-rate controls** — set timeline speed to slow down or speed up diagrams without rebuilding animations
|
|
12
14
|
- **Semantic themes** — `midnight` and `paper`, with per-role node colors
|
|
13
15
|
- **Single dependency** — only `@markdy/core`
|
|
14
16
|
|
|
@@ -68,7 +70,9 @@ player.destroy(); // clean up DOM + cancel animations
|
|
|
68
70
|
| `autoplay` | `boolean` | `true` | Start playing immediately |
|
|
69
71
|
| `loop` | `boolean` | `true` | Loop the animation when it reaches the end |
|
|
70
72
|
| `copyright` | `boolean` | `true` | Show a small "Powered by Markdy" badge below the animation |
|
|
71
|
-
| `progressBar` | `boolean` | `true` |
|
|
73
|
+
| `progressBar` | `boolean` | `true` | Deprecated compatibility flag for the rainbow scene-boundary progress bar |
|
|
74
|
+
| `sceneBoundaryProgress` | `boolean` | `progressBar ?? true` | Preferred flag for the rainbow scene-boundary progress bar |
|
|
75
|
+
| `playbackRate` | `number` | `1` | Timeline speed multiplier |
|
|
72
76
|
| `onWarning` | `(warning: Diagnostic) => void` | `console.warn` | Called for each soft parse warning |
|
|
73
77
|
| `onTimeUpdate` | `(seconds: number, durationSeconds: number) => void` | — | Called whenever playback or seek changes the current time |
|
|
74
78
|
| `onPlayStateChange` | `(playing: boolean) => void` | — | Called when playback starts or pauses |
|
|
@@ -80,6 +84,8 @@ player.destroy(); // clean up DOM + cancel animations
|
|
|
80
84
|
| `play()` | Start or resume playback |
|
|
81
85
|
| `pause()` | Pause at current position |
|
|
82
86
|
| `seek(seconds)` | Jump to a specific time |
|
|
87
|
+
| `setPlaybackRate(rate)` | Change timeline speed; ignores non-positive or non-finite values |
|
|
88
|
+
| `playbackRate()` | Current timeline speed multiplier |
|
|
83
89
|
| `currentTime()` | Current playback position in seconds |
|
|
84
90
|
| `duration()` | Total scene duration in seconds |
|
|
85
91
|
| `isPlaying()` | Whether the scene is currently playing |
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,12 @@ interface PlayerOptions {
|
|
|
10
10
|
autoplay?: boolean;
|
|
11
11
|
loop?: boolean;
|
|
12
12
|
copyright?: boolean;
|
|
13
|
+
/** @deprecated Prefer sceneBoundaryProgress. */
|
|
13
14
|
progressBar?: boolean;
|
|
15
|
+
/** Show rainbow progress around scene boundary. Defaults to true. */
|
|
16
|
+
sceneBoundaryProgress?: boolean;
|
|
17
|
+
/** Playback speed multiplier. Defaults to 1. */
|
|
18
|
+
playbackRate?: number;
|
|
14
19
|
onWarning?: (warning: Diagnostic) => void;
|
|
15
20
|
onTimeUpdate?: (seconds: number, durationSeconds: number) => void;
|
|
16
21
|
onPlayStateChange?: (playing: boolean) => void;
|
|
@@ -19,6 +24,8 @@ interface Player {
|
|
|
19
24
|
play(): void;
|
|
20
25
|
pause(): void;
|
|
21
26
|
seek(seconds: number): void;
|
|
27
|
+
setPlaybackRate(rate: number): void;
|
|
28
|
+
playbackRate(): number;
|
|
22
29
|
currentTime(): number;
|
|
23
30
|
duration(): number;
|
|
24
31
|
isPlaying(): boolean;
|
package/dist/index.js
CHANGED
|
@@ -448,7 +448,7 @@ function ensureNodeStyles(doc) {
|
|
|
448
448
|
box-sizing: border-box;
|
|
449
449
|
width: var(--md-node-w, 184px);
|
|
450
450
|
height: var(--md-node-h, 88px);
|
|
451
|
-
border-radius:
|
|
451
|
+
border-radius: 14px;
|
|
452
452
|
border: 1px solid var(--md-border);
|
|
453
453
|
background: linear-gradient(145deg, var(--md-surface-raised), var(--md-surface));
|
|
454
454
|
color: var(--md-text);
|
|
@@ -479,15 +479,41 @@ function ensureNodeStyles(doc) {
|
|
|
479
479
|
border-radius: 16px 0 0 16px;
|
|
480
480
|
}
|
|
481
481
|
.markdy-node__type {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
482
|
+
display: none;
|
|
483
|
+
}
|
|
484
|
+
.markdy-node__body {
|
|
485
|
+
height: 100%;
|
|
486
|
+
padding: 0 14px 0 18px;
|
|
487
|
+
display: flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
gap: 10px;
|
|
490
|
+
min-width: 0;
|
|
491
|
+
}
|
|
492
|
+
.markdy-node__icon {
|
|
493
|
+
flex: 0 0 auto;
|
|
494
|
+
width: 30px;
|
|
495
|
+
height: 30px;
|
|
496
|
+
border-radius: 10px;
|
|
497
|
+
display: flex;
|
|
498
|
+
align-items: center;
|
|
499
|
+
justify-content: center;
|
|
500
|
+
color: var(--md-role-color, var(--md-accent));
|
|
501
|
+
background: color-mix(in srgb, var(--md-role-color, var(--md-accent)) 16%, transparent);
|
|
502
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--md-role-color, var(--md-accent)) 28%, transparent) inset;
|
|
503
|
+
}
|
|
504
|
+
.markdy-node__icon svg {
|
|
505
|
+
width: 18px;
|
|
506
|
+
height: 18px;
|
|
507
|
+
display: block;
|
|
508
|
+
stroke: currentColor;
|
|
509
|
+
}
|
|
510
|
+
.markdy-node[data-icon="decision"] .markdy-node__icon,
|
|
511
|
+
.markdy-node[data-icon="flow"] .markdy-node__icon {
|
|
512
|
+
border-radius: 999px;
|
|
488
513
|
}
|
|
489
514
|
.markdy-node__label {
|
|
490
|
-
|
|
515
|
+
min-width: 0;
|
|
516
|
+
padding: 0;
|
|
491
517
|
font-size: 15px;
|
|
492
518
|
font-weight: 650;
|
|
493
519
|
line-height: 1.25;
|
|
@@ -519,6 +545,139 @@ function ensureNodeStyles(doc) {
|
|
|
519
545
|
`;
|
|
520
546
|
doc.head.appendChild(style);
|
|
521
547
|
}
|
|
548
|
+
var ICONS = {
|
|
549
|
+
compute: [
|
|
550
|
+
["rect", { x: "5", y: "5", width: "14", height: "14", rx: "3" }],
|
|
551
|
+
["path", { d: "M9 9h6v6H9zM9 2.5v2.5M15 2.5v2.5M9 19v2.5M15 19v2.5M2.5 9h2.5M2.5 15h2.5M19 9h2.5M19 15h2.5" }]
|
|
552
|
+
],
|
|
553
|
+
user: [
|
|
554
|
+
["circle", { cx: "12", cy: "8", r: "3.4" }],
|
|
555
|
+
["path", { d: "M5.5 20a6.5 6.5 0 0 1 13 0" }]
|
|
556
|
+
],
|
|
557
|
+
browser: [
|
|
558
|
+
["rect", { x: "3", y: "5", width: "18", height: "14", rx: "2.5" }],
|
|
559
|
+
["path", { d: "M3 9h18" }],
|
|
560
|
+
["path", { d: "M7 7h.01M10 7h.01" }]
|
|
561
|
+
],
|
|
562
|
+
service: [
|
|
563
|
+
["path", { d: "M12 3 4.5 7.2 12 11.4l7.5-4.2L12 3Z" }],
|
|
564
|
+
["path", { d: "M4.5 12 12 16.2 19.5 12" }],
|
|
565
|
+
["path", { d: "M4.5 16.8 12 21l7.5-4.2" }]
|
|
566
|
+
],
|
|
567
|
+
gateway: [
|
|
568
|
+
["circle", { cx: "5", cy: "12", r: "2" }],
|
|
569
|
+
["circle", { cx: "19", cy: "6", r: "2" }],
|
|
570
|
+
["circle", { cx: "19", cy: "18", r: "2" }],
|
|
571
|
+
["path", { d: "M7 12h4l5.5-5" }],
|
|
572
|
+
["path", { d: "M11 12l5.5 5" }]
|
|
573
|
+
],
|
|
574
|
+
queue: [
|
|
575
|
+
["rect", { x: "5", y: "5", width: "14", height: "4", rx: "1.4" }],
|
|
576
|
+
["rect", { x: "5", y: "10", width: "14", height: "4", rx: "1.4" }],
|
|
577
|
+
["rect", { x: "5", y: "15", width: "14", height: "4", rx: "1.4" }]
|
|
578
|
+
],
|
|
579
|
+
worker: [
|
|
580
|
+
["circle", { cx: "12", cy: "12", r: "3.2" }],
|
|
581
|
+
["path", { d: "M12 2.8v3M12 18.2v3M2.8 12h3M18.2 12h3M5.5 5.5l2.1 2.1M16.4 16.4l2.1 2.1M18.5 5.5l-2.1 2.1M7.6 16.4l-2.1 2.1" }]
|
|
582
|
+
],
|
|
583
|
+
database: [
|
|
584
|
+
["ellipse", { cx: "12", cy: "5.5", rx: "7", ry: "3" }],
|
|
585
|
+
["path", { d: "M5 5.5v10c0 1.7 3.1 3 7 3s7-1.3 7-3v-10" }],
|
|
586
|
+
["path", { d: "M5 10.5c0 1.7 3.1 3 7 3s7-1.3 7-3" }]
|
|
587
|
+
],
|
|
588
|
+
storage: [
|
|
589
|
+
["path", { d: "M4 8.5 12 4l8 4.5v7L12 20l-8-4.5v-7Z" }],
|
|
590
|
+
["path", { d: "M4 8.5 12 13l8-4.5" }],
|
|
591
|
+
["path", { d: "M12 13v7" }]
|
|
592
|
+
],
|
|
593
|
+
cdn: [
|
|
594
|
+
["circle", { cx: "12", cy: "12", r: "8" }],
|
|
595
|
+
["path", { d: "M4 12h16M12 4c2.2 2.1 3.3 4.8 3.3 8S14.2 17.9 12 20M12 4c-2.2 2.1-3.3 4.8-3.3 8S9.8 17.9 12 20" }]
|
|
596
|
+
],
|
|
597
|
+
cache: [
|
|
598
|
+
["path", { d: "M13 2 5 13h6l-1 9 9-13h-6l0-7Z" }]
|
|
599
|
+
],
|
|
600
|
+
code: [
|
|
601
|
+
["path", { d: "m9 18-6-6 6-6" }],
|
|
602
|
+
["path", { d: "m15 6 6 6-6 6" }],
|
|
603
|
+
["path", { d: "m14 4-4 16" }]
|
|
604
|
+
],
|
|
605
|
+
messaging: [
|
|
606
|
+
["path", { d: "M4 6h16v10H8l-4 4V6Z" }],
|
|
607
|
+
["path", { d: "M8 10h8M8 13h5" }]
|
|
608
|
+
],
|
|
609
|
+
network: [
|
|
610
|
+
["circle", { cx: "6", cy: "7", r: "2" }],
|
|
611
|
+
["circle", { cx: "18", cy: "7", r: "2" }],
|
|
612
|
+
["circle", { cx: "12", cy: "18", r: "2" }],
|
|
613
|
+
["path", { d: "M8 8.5 11 16M16 8.5 13 16M8 7h8" }]
|
|
614
|
+
],
|
|
615
|
+
platform: [
|
|
616
|
+
["rect", { x: "4", y: "5", width: "16", height: "14", rx: "2.5" }],
|
|
617
|
+
["path", { d: "M8 9h8M8 13h8M8 17h4" }]
|
|
618
|
+
],
|
|
619
|
+
security: [
|
|
620
|
+
["path", { d: "M12 3 5 6v5c0 4.5 3 7.7 7 10 4-2.3 7-5.5 7-10V6l-7-3Z" }],
|
|
621
|
+
["path", { d: "M9.5 12.5 11.2 14 15 10" }]
|
|
622
|
+
],
|
|
623
|
+
delivery: [
|
|
624
|
+
["path", { d: "M4 7h10" }],
|
|
625
|
+
["path", { d: "M4 12h16" }],
|
|
626
|
+
["path", { d: "M4 17h10" }],
|
|
627
|
+
["path", { d: "m16 7 4 5-4 5" }]
|
|
628
|
+
],
|
|
629
|
+
observability: [
|
|
630
|
+
["path", { d: "M4 14s2.5-5 8-5 8 5 8 5-2.5 5-8 5-8-5-8-5Z" }],
|
|
631
|
+
["circle", { cx: "12", cy: "14", r: "2.5" }]
|
|
632
|
+
],
|
|
633
|
+
distributed: [
|
|
634
|
+
["circle", { cx: "6", cy: "12", r: "2.5" }],
|
|
635
|
+
["circle", { cx: "18", cy: "6", r: "2.5" }],
|
|
636
|
+
["circle", { cx: "18", cy: "18", r: "2.5" }],
|
|
637
|
+
["path", { d: "M8.4 11 15.6 7M8.4 13 15.6 17" }]
|
|
638
|
+
],
|
|
639
|
+
flow: [
|
|
640
|
+
["path", { d: "M5 12h14" }],
|
|
641
|
+
["path", { d: "m13 6 6 6-6 6" }]
|
|
642
|
+
],
|
|
643
|
+
decision: [
|
|
644
|
+
["path", { d: "M12 3 21 12 12 21 3 12 12 3Z" }],
|
|
645
|
+
["path", { d: "M12 8v4" }],
|
|
646
|
+
["path", { d: "M12 16h.01" }]
|
|
647
|
+
]
|
|
648
|
+
};
|
|
649
|
+
function iconKeyForNode(node) {
|
|
650
|
+
if (node.kind === "api_gateway" || node.kind === "gateway" || node.kind === "load_balancer" || node.kind === "ingress") return "gateway";
|
|
651
|
+
if (node.kind === "db" || node.kind === "database" || node.kind === "sql" || node.kind === "nosql" || node.kind === "warehouse") return "database";
|
|
652
|
+
if (node.kind === "bucket" || node.kind === "object_store" || node.kind === "blob" || node.kind === "volume" || node.kind === "disk") return "storage";
|
|
653
|
+
if (node.kind === "cdn" || node.kind === "dns" || node.kind === "internet") return "cdn";
|
|
654
|
+
if (node.kind === "queue" || node.kind === "topic" || node.kind === "stream" || node.kind === "event_bus" || node.kind === "broker") return "queue";
|
|
655
|
+
if (node.kind === "worker" || node.kind === "job" || node.kind === "scheduler" || node.kind === "cron" || node.kind === "batch") return "worker";
|
|
656
|
+
if (node.kind === "browser" || node.kind === "web" || node.kind === "frontend" || node.kind === "app") return "browser";
|
|
657
|
+
if (node.kind === "user" || node.kind === "client") return "user";
|
|
658
|
+
if (node.kind === "decision" || node.kind === "condition") return "decision";
|
|
659
|
+
if (node.kind === "cache") return "cache";
|
|
660
|
+
return ICONS[node.kind] ? node.kind : node.role;
|
|
661
|
+
}
|
|
662
|
+
function createIconEl(doc, node) {
|
|
663
|
+
const wrap = doc.createElement("div");
|
|
664
|
+
wrap.className = "markdy-node__icon";
|
|
665
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
666
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
667
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
668
|
+
svg.setAttribute("fill", "none");
|
|
669
|
+
svg.setAttribute("stroke-width", "2");
|
|
670
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
671
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
672
|
+
const spec = ICONS[iconKeyForNode(node)] ?? ICONS.service;
|
|
673
|
+
for (const [tag, attrs] of spec) {
|
|
674
|
+
const child = doc.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
675
|
+
for (const [name, value] of Object.entries(attrs)) child.setAttribute(name, value);
|
|
676
|
+
svg.appendChild(child);
|
|
677
|
+
}
|
|
678
|
+
wrap.appendChild(svg);
|
|
679
|
+
return wrap;
|
|
680
|
+
}
|
|
522
681
|
function createNodeEl(node, theme) {
|
|
523
682
|
const el = document.createElement("div");
|
|
524
683
|
el.className = "markdy-node markdy-scene-actor";
|
|
@@ -530,15 +689,24 @@ function createNodeEl(node, theme) {
|
|
|
530
689
|
el.style.setProperty("--md-node-h", `${node.height}px`);
|
|
531
690
|
const roleColor = theme.roles[node.role] ?? theme.accent;
|
|
532
691
|
el.style.setProperty("--md-role-color", roleColor);
|
|
692
|
+
const typeText = node.kind.replace(/_/g, " ");
|
|
693
|
+
el.dataset.kind = node.kind;
|
|
694
|
+
el.dataset.icon = iconKeyForNode(node);
|
|
695
|
+
el.title = `${node.label} (${typeText})`;
|
|
696
|
+
el.setAttribute("aria-label", el.title);
|
|
533
697
|
const rail = document.createElement("div");
|
|
534
698
|
rail.className = "markdy-node__rail";
|
|
535
699
|
const type = document.createElement("div");
|
|
536
700
|
type.className = "markdy-node__type";
|
|
537
|
-
type.textContent =
|
|
701
|
+
type.textContent = typeText;
|
|
702
|
+
const body = document.createElement("div");
|
|
703
|
+
body.className = "markdy-node__body";
|
|
704
|
+
const icon = createIconEl(document, node);
|
|
538
705
|
const label = document.createElement("div");
|
|
539
706
|
label.className = "markdy-node__label";
|
|
540
707
|
label.textContent = node.label;
|
|
541
|
-
|
|
708
|
+
body.append(icon, label);
|
|
709
|
+
el.append(rail, type, body);
|
|
542
710
|
return el;
|
|
543
711
|
}
|
|
544
712
|
function createTitleEl(title) {
|
|
@@ -615,11 +783,14 @@ function createPlayer(opts) {
|
|
|
615
783
|
autoplay = true,
|
|
616
784
|
loop = true,
|
|
617
785
|
copyright = true,
|
|
618
|
-
progressBar
|
|
786
|
+
progressBar,
|
|
787
|
+
sceneBoundaryProgress,
|
|
788
|
+
playbackRate: initialPlaybackRate = 1,
|
|
619
789
|
onWarning = (w) => console.warn(`[markdy] line ${w.line}: ${w.message}`),
|
|
620
790
|
onTimeUpdate,
|
|
621
791
|
onPlayStateChange
|
|
622
792
|
} = opts;
|
|
793
|
+
const showSceneBoundaryProgress = sceneBoundaryProgress ?? progressBar ?? true;
|
|
623
794
|
const { ast, plan } = parseAndCompile(code);
|
|
624
795
|
for (const w of ast.diagnostics) {
|
|
625
796
|
if (w.severity === "warning") onWarning(w);
|
|
@@ -635,7 +806,7 @@ function createPlayer(opts) {
|
|
|
635
806
|
});
|
|
636
807
|
container.appendChild(viewport);
|
|
637
808
|
let progressEl = null;
|
|
638
|
-
if (
|
|
809
|
+
if (showSceneBoundaryProgress) {
|
|
639
810
|
progressEl = document.createElement("div");
|
|
640
811
|
Object.assign(progressEl.style, {
|
|
641
812
|
position: "absolute",
|
|
@@ -726,6 +897,7 @@ function createPlayer(opts) {
|
|
|
726
897
|
anim.currentTime = 0;
|
|
727
898
|
}
|
|
728
899
|
let sceneMs = 0;
|
|
900
|
+
let playbackRate = Number.isFinite(initialPlaybackRate) && initialPlaybackRate > 0 ? initialPlaybackRate : 1;
|
|
729
901
|
let lastRafTs = null;
|
|
730
902
|
let isPlaying = false;
|
|
731
903
|
let rafId = null;
|
|
@@ -734,7 +906,7 @@ function createPlayer(opts) {
|
|
|
734
906
|
onTimeUpdate?.(sceneMs / 1e3, durationSeconds);
|
|
735
907
|
}
|
|
736
908
|
function rafTick(timestamp) {
|
|
737
|
-
if (lastRafTs !== null) sceneMs += timestamp - lastRafTs;
|
|
909
|
+
if (lastRafTs !== null) sceneMs += (timestamp - lastRafTs) * playbackRate;
|
|
738
910
|
lastRafTs = timestamp;
|
|
739
911
|
if (totalDurationMs > 0 && sceneMs >= totalDurationMs) {
|
|
740
912
|
if (loop) sceneMs = sceneMs % totalDurationMs;
|
|
@@ -773,6 +945,13 @@ function createPlayer(opts) {
|
|
|
773
945
|
applyCurrentTime();
|
|
774
946
|
if (totalDurationMs > 0) updateProgressBar(sceneMs / totalDurationMs);
|
|
775
947
|
},
|
|
948
|
+
setPlaybackRate(rate) {
|
|
949
|
+
if (!Number.isFinite(rate) || rate <= 0) return;
|
|
950
|
+
playbackRate = rate;
|
|
951
|
+
},
|
|
952
|
+
playbackRate() {
|
|
953
|
+
return playbackRate;
|
|
954
|
+
},
|
|
776
955
|
currentTime() {
|
|
777
956
|
return sceneMs / 1e3;
|
|
778
957
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Browser renderer for animated MarkdyScript architecture diagrams, built on the Web Animations API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,14 +43,14 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@markdy/core": "0.8.
|
|
46
|
+
"@markdy/core": "0.8.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"jsdom": "^29.1.1",
|
|
50
50
|
"tsup": "^8.5.1",
|
|
51
51
|
"typescript": "^5.9.3",
|
|
52
52
|
"vitest": "^4.1.7",
|
|
53
|
-
"@markdy/stdlib-systems": "0.8.
|
|
53
|
+
"@markdy/stdlib-systems": "0.8.2"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsup",
|