@markdy/renderer-dom 0.8.2 → 0.8.4
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 +11 -11
- package/dist/index.d.ts +11 -5
- package/dist/index.js +220 -90
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -37,12 +37,12 @@ This package consumes parsed AST and drives DOM + Web Animations API timelines.
|
|
|
37
37
|
## Usage
|
|
38
38
|
|
|
39
39
|
```typescript
|
|
40
|
-
import {
|
|
40
|
+
import { createDiagram } from "@markdy/renderer-dom";
|
|
41
41
|
|
|
42
|
-
const
|
|
42
|
+
const diagram = createDiagram({
|
|
43
43
|
container: document.getElementById("scene")!,
|
|
44
44
|
code: `
|
|
45
|
-
scene "Request" theme=
|
|
45
|
+
scene "Request" theme=paper
|
|
46
46
|
browser Web
|
|
47
47
|
service API
|
|
48
48
|
beat main:
|
|
@@ -53,15 +53,15 @@ const player = createPlayer({
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
// Playback control
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
diagram.pause();
|
|
57
|
+
diagram.seek(1.5); // jump to 1.5 seconds
|
|
58
|
+
diagram.play();
|
|
59
|
+
diagram.destroy(); // clean up DOM + cancel animations
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## API
|
|
63
63
|
|
|
64
|
-
### `
|
|
64
|
+
### `createDiagram(options: DiagramOptions): Diagram`
|
|
65
65
|
|
|
66
66
|
| Option | Type | Default | Description |
|
|
67
67
|
|---|---|---|---|
|
|
@@ -77,7 +77,7 @@ player.destroy(); // clean up DOM + cancel animations
|
|
|
77
77
|
| `onTimeUpdate` | `(seconds: number, durationSeconds: number) => void` | — | Called whenever playback or seek changes the current time |
|
|
78
78
|
| `onPlayStateChange` | `(playing: boolean) => void` | — | Called when playback starts or pauses |
|
|
79
79
|
|
|
80
|
-
### `
|
|
80
|
+
### `Diagram`
|
|
81
81
|
|
|
82
82
|
| Method | Description |
|
|
83
83
|
|---|---|
|
|
@@ -97,8 +97,8 @@ player.destroy(); // clean up DOM + cancel animations
|
|
|
97
97
|
|
|
98
98
|
```
|
|
99
99
|
src/
|
|
100
|
-
index.ts — Barrel exports (
|
|
101
|
-
|
|
100
|
+
index.ts — Barrel exports (createDiagram)
|
|
101
|
+
diagram.ts — Public API, rAF loop, progress bar, responsive scaling
|
|
102
102
|
nodes.ts — Node element factory + scene title
|
|
103
103
|
edges.ts — Flow-edge SVG runtime, routing, and cue animations
|
|
104
104
|
geometry/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { BeatRange, Diagnostic } from '@markdy/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Markdy diagram runtime — renders a RenderPlan via WAAPI.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
interface
|
|
7
|
+
interface DiagramOptions {
|
|
8
8
|
container: HTMLElement;
|
|
9
9
|
code: string;
|
|
10
10
|
autoplay?: boolean;
|
|
11
11
|
loop?: boolean;
|
|
12
12
|
copyright?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Maps node `image=`/`logo=` values to resolved URLs. Lets a host (Astro,
|
|
15
|
+
* MDX) remap DSL asset references to CDN/data URLs. Values not present here
|
|
16
|
+
* are used verbatim as the image `src`.
|
|
17
|
+
*/
|
|
18
|
+
assets?: Record<string, string>;
|
|
13
19
|
/** @deprecated Prefer sceneBoundaryProgress. */
|
|
14
20
|
progressBar?: boolean;
|
|
15
21
|
/** Show rainbow progress around scene boundary. Defaults to true. */
|
|
@@ -20,7 +26,7 @@ interface PlayerOptions {
|
|
|
20
26
|
onTimeUpdate?: (seconds: number, durationSeconds: number) => void;
|
|
21
27
|
onPlayStateChange?: (playing: boolean) => void;
|
|
22
28
|
}
|
|
23
|
-
interface
|
|
29
|
+
interface Diagram {
|
|
24
30
|
play(): void;
|
|
25
31
|
pause(): void;
|
|
26
32
|
seek(seconds: number): void;
|
|
@@ -33,6 +39,6 @@ interface Player {
|
|
|
33
39
|
seekToBeat(name: string): void;
|
|
34
40
|
destroy(): void;
|
|
35
41
|
}
|
|
36
|
-
declare function
|
|
42
|
+
declare function createDiagram(opts: DiagramOptions): Diagram;
|
|
37
43
|
|
|
38
|
-
export { type
|
|
44
|
+
export { type Diagram, type DiagramOptions, createDiagram };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/diagram.ts
|
|
2
2
|
import { parseAndCompile } from "@markdy/core";
|
|
3
3
|
|
|
4
4
|
// src/geometry/rect.ts
|
|
@@ -233,13 +233,13 @@ function ensureDefs(svg, theme, id) {
|
|
|
233
233
|
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
234
234
|
arrow.setAttribute("id", `${id}-arrow-${kind}`);
|
|
235
235
|
arrow.setAttribute("viewBox", "0 0 10 10");
|
|
236
|
-
arrow.setAttribute("refX", "8");
|
|
236
|
+
arrow.setAttribute("refX", "8.5");
|
|
237
237
|
arrow.setAttribute("refY", "5");
|
|
238
|
-
arrow.setAttribute("markerWidth", "
|
|
239
|
-
arrow.setAttribute("markerHeight", "
|
|
238
|
+
arrow.setAttribute("markerWidth", "7");
|
|
239
|
+
arrow.setAttribute("markerHeight", "7");
|
|
240
240
|
arrow.setAttribute("orient", "auto-start-reverse");
|
|
241
241
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
242
|
-
path.setAttribute("d", "M 1 1 L 9 5 L 1
|
|
242
|
+
path.setAttribute("d", "M 1.5 1.6 L 9 5 L 1.5 8.4 L 3.4 5 Z");
|
|
243
243
|
path.setAttribute("fill", color);
|
|
244
244
|
arrow.appendChild(path);
|
|
245
245
|
defs.appendChild(arrow);
|
|
@@ -260,19 +260,21 @@ function createEdgeRuntime(svg, from, to, kind, label, theme, sceneId, routeObst
|
|
|
260
260
|
path.setAttribute("d", d);
|
|
261
261
|
path.setAttribute("fill", "none");
|
|
262
262
|
path.setAttribute("stroke", color);
|
|
263
|
-
path.setAttribute("stroke-width", kind === "dependency" ? "1.5" : "2
|
|
263
|
+
path.setAttribute("stroke-width", kind === "dependency" ? "1.5" : "2");
|
|
264
264
|
path.setAttribute("stroke-linejoin", "round");
|
|
265
265
|
path.setAttribute("stroke-linecap", "round");
|
|
266
266
|
if (style.dash) path.setAttribute("stroke-dasharray", style.dash);
|
|
267
267
|
if (style.marker !== "none") {
|
|
268
268
|
path.setAttribute("marker-end", `url(#${sceneId}-arrow-${kind})`);
|
|
269
269
|
}
|
|
270
|
-
|
|
270
|
+
if (kind !== "dependency") {
|
|
271
|
+
path.style.filter = `drop-shadow(0 0 3px ${color}33)`;
|
|
272
|
+
}
|
|
271
273
|
const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
272
|
-
dot.setAttribute("r", "
|
|
274
|
+
dot.setAttribute("r", "3.5");
|
|
273
275
|
dot.setAttribute("fill", color);
|
|
274
276
|
dot.style.opacity = "0";
|
|
275
|
-
dot.style.filter = `drop-shadow(0 0
|
|
277
|
+
dot.style.filter = `drop-shadow(0 0 4px ${color}aa)`;
|
|
276
278
|
group.append(path, dot);
|
|
277
279
|
let labelEl;
|
|
278
280
|
let labelRect;
|
|
@@ -280,6 +282,20 @@ function createEdgeRuntime(svg, from, to, kind, label, theme, sceneId, routeObst
|
|
|
280
282
|
const textWidth = label.length * 6.6 + 10;
|
|
281
283
|
const placement = placeFlowLabel(points, textWidth, labelObstacles, bounds);
|
|
282
284
|
labelRect = placement.rect;
|
|
285
|
+
const plate = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
286
|
+
const padX = 7;
|
|
287
|
+
const halfW = textWidth / 2;
|
|
288
|
+
plate.setAttribute("x", String(placement.x - halfW - padX));
|
|
289
|
+
plate.setAttribute("y", String(placement.y - 9));
|
|
290
|
+
plate.setAttribute("width", String(textWidth + padX * 2));
|
|
291
|
+
plate.setAttribute("height", "18");
|
|
292
|
+
plate.setAttribute("rx", "6");
|
|
293
|
+
plate.setAttribute("fill", theme.labelPlate ?? theme.surface);
|
|
294
|
+
plate.setAttribute("fill-opacity", "0.9");
|
|
295
|
+
plate.setAttribute("stroke", theme.hairline ?? `color-mix(in srgb, ${theme.border} 60%, transparent)`);
|
|
296
|
+
plate.setAttribute("stroke-width", "1");
|
|
297
|
+
plate.style.opacity = "0";
|
|
298
|
+
group.appendChild(plate);
|
|
283
299
|
labelEl = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
284
300
|
labelEl.setAttribute("x", String(placement.x));
|
|
285
301
|
labelEl.setAttribute("y", String(placement.y));
|
|
@@ -287,14 +303,11 @@ function createEdgeRuntime(svg, from, to, kind, label, theme, sceneId, routeObst
|
|
|
287
303
|
labelEl.setAttribute("dominant-baseline", "middle");
|
|
288
304
|
labelEl.setAttribute("font-size", "11");
|
|
289
305
|
labelEl.setAttribute("font-family", "ui-monospace, SFMono-Regular, Menlo, monospace");
|
|
290
|
-
labelEl.setAttribute("fill", theme.
|
|
291
|
-
labelEl.setAttribute("stroke", theme.canvas);
|
|
292
|
-
labelEl.setAttribute("stroke-width", "3");
|
|
293
|
-
labelEl.setAttribute("paint-order", "stroke");
|
|
294
|
-
labelEl.setAttribute("stroke-linejoin", "round");
|
|
306
|
+
labelEl.setAttribute("fill", theme.text);
|
|
295
307
|
labelEl.textContent = label;
|
|
296
308
|
labelEl.style.opacity = "0";
|
|
297
309
|
group.appendChild(labelEl);
|
|
310
|
+
labelEl.__plate = plate;
|
|
298
311
|
}
|
|
299
312
|
svg.appendChild(group);
|
|
300
313
|
return { group, path, label: labelEl, dot, pathLen: len, points, labelRect };
|
|
@@ -332,6 +345,10 @@ function animateEdgeReveal(runtime, startMs, durMs) {
|
|
|
332
345
|
);
|
|
333
346
|
if (label) {
|
|
334
347
|
anims.push(label.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 180, delay: startMs + 80, fill: "forwards" }));
|
|
348
|
+
const plate = label.__plate;
|
|
349
|
+
if (plate) {
|
|
350
|
+
anims.push(plate.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 180, delay: startMs + 80, fill: "forwards" }));
|
|
351
|
+
}
|
|
335
352
|
}
|
|
336
353
|
if (points.length >= 2) {
|
|
337
354
|
anims.push(
|
|
@@ -448,42 +465,46 @@ function ensureNodeStyles(doc) {
|
|
|
448
465
|
box-sizing: border-box;
|
|
449
466
|
width: var(--md-node-w, 184px);
|
|
450
467
|
height: var(--md-node-h, 88px);
|
|
451
|
-
border-radius:
|
|
452
|
-
|
|
453
|
-
|
|
468
|
+
border-radius: 12px;
|
|
469
|
+
background:
|
|
470
|
+
linear-gradient(180deg,
|
|
471
|
+
var(--md-node-surface-raised, color-mix(in srgb, var(--md-surface-raised) 88%, #ffffff 12%)),
|
|
472
|
+
var(--md-node-surface, var(--md-surface)));
|
|
454
473
|
color: var(--md-text);
|
|
455
|
-
box-shadow:
|
|
474
|
+
box-shadow:
|
|
475
|
+
0 1px 1px color-mix(in srgb, var(--md-shadow, rgba(2, 6, 23, 0.5)) 50%, transparent),
|
|
476
|
+
0 10px 22px -12px var(--md-shadow, rgba(2, 6, 23, 0.55)),
|
|
477
|
+
inset 0 0 0 1px var(--md-hairline, color-mix(in srgb, var(--md-border) 50%, transparent)),
|
|
478
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
|
456
479
|
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
457
480
|
overflow: hidden;
|
|
458
481
|
opacity: 0;
|
|
459
482
|
transform: translateY(8px);
|
|
460
|
-
transition: box-shadow 0.
|
|
483
|
+
transition: box-shadow 0.25s ease, transform 0.25s ease;
|
|
461
484
|
}
|
|
462
485
|
.markdy-node[data-visible="1"] {
|
|
463
486
|
opacity: 1;
|
|
464
487
|
transform: translateY(0);
|
|
465
488
|
}
|
|
466
489
|
.markdy-node[data-focused="1"] {
|
|
467
|
-
box-shadow:
|
|
490
|
+
box-shadow:
|
|
491
|
+
0 2px 4px rgba(2, 6, 23, 0.32),
|
|
492
|
+
0 16px 34px -14px rgba(2, 6, 23, 0.6),
|
|
493
|
+
inset 0 0 0 1px color-mix(in srgb, var(--md-accent) 65%, transparent),
|
|
494
|
+
0 0 0 3px color-mix(in srgb, var(--md-accent) 20%, transparent);
|
|
468
495
|
}
|
|
469
496
|
.markdy-node[data-glow="1"] {
|
|
470
|
-
box-shadow:
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
top: 0;
|
|
476
|
-
bottom: 0;
|
|
477
|
-
width: 4px;
|
|
478
|
-
background: var(--md-role-color, var(--md-accent));
|
|
479
|
-
border-radius: 16px 0 0 16px;
|
|
480
|
-
}
|
|
481
|
-
.markdy-node__type {
|
|
482
|
-
display: none;
|
|
497
|
+
box-shadow:
|
|
498
|
+
0 2px 4px rgba(2, 6, 23, 0.32),
|
|
499
|
+
0 0 0 1px color-mix(in srgb, var(--md-glow-color, var(--md-accent)) 55%, transparent),
|
|
500
|
+
0 0 20px -2px color-mix(in srgb, var(--md-glow-color, var(--md-accent)) 42%, transparent),
|
|
501
|
+
inset 0 0 18px -8px color-mix(in srgb, var(--md-glow-color, var(--md-accent)) 45%, transparent);
|
|
483
502
|
}
|
|
503
|
+
.markdy-node__rail { display: none; }
|
|
504
|
+
.markdy-node__type { display: none; }
|
|
484
505
|
.markdy-node__body {
|
|
485
506
|
height: 100%;
|
|
486
|
-
padding: 0
|
|
507
|
+
padding: 0 13px;
|
|
487
508
|
display: flex;
|
|
488
509
|
align-items: center;
|
|
489
510
|
gap: 10px;
|
|
@@ -493,43 +514,69 @@ function ensureNodeStyles(doc) {
|
|
|
493
514
|
flex: 0 0 auto;
|
|
494
515
|
width: 30px;
|
|
495
516
|
height: 30px;
|
|
496
|
-
border-radius:
|
|
517
|
+
border-radius: 8px;
|
|
497
518
|
display: flex;
|
|
498
519
|
align-items: center;
|
|
499
520
|
justify-content: center;
|
|
500
521
|
color: var(--md-role-color, var(--md-accent));
|
|
501
|
-
background:
|
|
502
|
-
|
|
522
|
+
background:
|
|
523
|
+
linear-gradient(180deg,
|
|
524
|
+
color-mix(in srgb, var(--md-role-color, var(--md-accent)) 24%, transparent),
|
|
525
|
+
color-mix(in srgb, var(--md-role-color, var(--md-accent)) 11%, transparent));
|
|
526
|
+
box-shadow:
|
|
527
|
+
inset 0 0 0 1px color-mix(in srgb, var(--md-role-color, var(--md-accent)) 34%, transparent),
|
|
528
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
|
503
529
|
}
|
|
504
530
|
.markdy-node__icon svg {
|
|
505
|
-
width:
|
|
506
|
-
height:
|
|
531
|
+
width: 17px;
|
|
532
|
+
height: 17px;
|
|
507
533
|
display: block;
|
|
508
534
|
stroke: currentColor;
|
|
509
535
|
}
|
|
536
|
+
.markdy-node__icon[data-media="image"] {
|
|
537
|
+
background: color-mix(in srgb, var(--md-surface-raised) 68%, #ffffff 32%);
|
|
538
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--md-border) 60%, transparent);
|
|
539
|
+
padding: 3px;
|
|
540
|
+
overflow: hidden;
|
|
541
|
+
}
|
|
542
|
+
.markdy-node__icon img {
|
|
543
|
+
width: 100%;
|
|
544
|
+
height: 100%;
|
|
545
|
+
object-fit: contain;
|
|
546
|
+
border-radius: 6px;
|
|
547
|
+
display: block;
|
|
548
|
+
}
|
|
549
|
+
.markdy-node__icon[data-fit="cover"] img {
|
|
550
|
+
object-fit: cover;
|
|
551
|
+
}
|
|
510
552
|
.markdy-node[data-icon="decision"] .markdy-node__icon,
|
|
511
553
|
.markdy-node[data-icon="flow"] .markdy-node__icon {
|
|
512
554
|
border-radius: 999px;
|
|
513
555
|
}
|
|
514
556
|
.markdy-node__label {
|
|
557
|
+
flex: 1 1 auto;
|
|
515
558
|
min-width: 0;
|
|
516
559
|
padding: 0;
|
|
517
|
-
font-size:
|
|
518
|
-
font-weight:
|
|
519
|
-
|
|
520
|
-
|
|
560
|
+
font-size: 14px;
|
|
561
|
+
font-weight: 600;
|
|
562
|
+
letter-spacing: -0.01em;
|
|
563
|
+
line-height: 1.18;
|
|
564
|
+
display: -webkit-box;
|
|
565
|
+
-webkit-box-orient: vertical;
|
|
566
|
+
-webkit-line-clamp: 3;
|
|
567
|
+
line-clamp: 3;
|
|
521
568
|
overflow: hidden;
|
|
522
|
-
|
|
569
|
+
overflow-wrap: anywhere;
|
|
570
|
+
word-break: break-word;
|
|
571
|
+
text-wrap: balance;
|
|
523
572
|
}
|
|
524
|
-
.markdy-node[data-role="client"] { border-radius:
|
|
525
|
-
.markdy-node[data-role="data"] { border-radius:
|
|
526
|
-
.markdy-node[data-role="flow"] { transform: rotate(0deg); clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }
|
|
527
|
-
.markdy-node[data-role="network"] { clip-path: polygon(8% 0%, 92% 0%, 100% 50%, 92% 100%, 8% 100%, 0% 50%); }
|
|
573
|
+
.markdy-node[data-role="client"] { border-radius: 15px 15px 9px 9px; }
|
|
574
|
+
.markdy-node[data-role="data"] { border-radius: 12px 12px 20px 20px; }
|
|
528
575
|
.markdy-scene-title {
|
|
529
576
|
position: absolute;
|
|
530
|
-
left:
|
|
531
|
-
top:
|
|
532
|
-
right:
|
|
577
|
+
left: 44px;
|
|
578
|
+
top: 26px;
|
|
579
|
+
right: 44px;
|
|
533
580
|
font-size: 32px;
|
|
534
581
|
font-weight: 700;
|
|
535
582
|
line-height: 1.2;
|
|
@@ -636,6 +683,49 @@ var ICONS = {
|
|
|
636
683
|
["circle", { cx: "18", cy: "18", r: "2.5" }],
|
|
637
684
|
["path", { d: "M8.4 11 15.6 7M8.4 13 15.6 17" }]
|
|
638
685
|
],
|
|
686
|
+
server: [
|
|
687
|
+
["rect", { x: "4", y: "4.5", width: "16", height: "6.5", rx: "2" }],
|
|
688
|
+
["rect", { x: "4", y: "13", width: "16", height: "6.5", rx: "2" }],
|
|
689
|
+
["path", { d: "M7.5 7.75h.01M7.5 16.25h.01" }],
|
|
690
|
+
["path", { d: "M11 7.75h6M11 16.25h6" }]
|
|
691
|
+
],
|
|
692
|
+
scheduler: [
|
|
693
|
+
["circle", { cx: "12", cy: "12", r: "8" }],
|
|
694
|
+
["path", { d: "M12 7.5V12l3.2 2" }]
|
|
695
|
+
],
|
|
696
|
+
key: [
|
|
697
|
+
["circle", { cx: "8.5", cy: "8.5", r: "3.6" }],
|
|
698
|
+
["path", { d: "M11.1 11.1 19 19" }],
|
|
699
|
+
["path", { d: "M16.4 16.4l1.8 1.8M18.2 13.6l1.8 1.8" }]
|
|
700
|
+
],
|
|
701
|
+
pod: [
|
|
702
|
+
["path", { d: "M12 3 20 7.5v9L12 21 4 16.5v-9L12 3Z" }],
|
|
703
|
+
["path", { d: "M8.75 10.5h6.5v5h-6.5z" }]
|
|
704
|
+
],
|
|
705
|
+
function: [
|
|
706
|
+
["rect", { x: "4", y: "4", width: "16", height: "16", rx: "4.5" }],
|
|
707
|
+
["path", { d: "M13.2 7.5 9.5 12.4h3.4L11 16.5" }]
|
|
708
|
+
],
|
|
709
|
+
lock: [
|
|
710
|
+
["rect", { x: "5", y: "11", width: "14", height: "9", rx: "2.2" }],
|
|
711
|
+
["path", { d: "M8 11V8a4 4 0 0 1 8 0v3" }],
|
|
712
|
+
["path", { d: "M12 14.8v2.4" }]
|
|
713
|
+
],
|
|
714
|
+
metrics: [
|
|
715
|
+
["path", { d: "M4 20h16" }],
|
|
716
|
+
["rect", { x: "5", y: "11", width: "2.6", height: "6", rx: "0.6" }],
|
|
717
|
+
["rect", { x: "10.7", y: "6.5", width: "2.6", height: "10.5", rx: "0.6" }],
|
|
718
|
+
["rect", { x: "16.4", y: "13", width: "2.6", height: "4", rx: "0.6" }]
|
|
719
|
+
],
|
|
720
|
+
mobile: [
|
|
721
|
+
["rect", { x: "7", y: "3", width: "10", height: "18", rx: "2.6" }],
|
|
722
|
+
["path", { d: "M10.5 18h3" }]
|
|
723
|
+
],
|
|
724
|
+
registry: [
|
|
725
|
+
["rect", { x: "3.5", y: "13", width: "7", height: "6", rx: "1" }],
|
|
726
|
+
["rect", { x: "13.5", y: "13", width: "7", height: "6", rx: "1" }],
|
|
727
|
+
["rect", { x: "8.5", y: "5", width: "7", height: "6", rx: "1" }]
|
|
728
|
+
],
|
|
639
729
|
flow: [
|
|
640
730
|
["path", { d: "M5 12h14" }],
|
|
641
731
|
["path", { d: "m13 6 6 6-6 6" }]
|
|
@@ -647,41 +737,81 @@ var ICONS = {
|
|
|
647
737
|
]
|
|
648
738
|
};
|
|
649
739
|
function iconKeyForNode(node) {
|
|
740
|
+
const override = typeof node.props?.icon === "string" ? node.props.icon.toLowerCase() : void 0;
|
|
741
|
+
if (override && ICONS[override]) return override;
|
|
650
742
|
if (node.kind === "api_gateway" || node.kind === "gateway" || node.kind === "load_balancer" || node.kind === "ingress") return "gateway";
|
|
651
743
|
if (node.kind === "db" || node.kind === "database" || node.kind === "sql" || node.kind === "nosql" || node.kind === "warehouse") return "database";
|
|
652
744
|
if (node.kind === "bucket" || node.kind === "object_store" || node.kind === "blob" || node.kind === "volume" || node.kind === "disk") return "storage";
|
|
653
745
|
if (node.kind === "cdn" || node.kind === "dns" || node.kind === "internet") return "cdn";
|
|
654
746
|
if (node.kind === "queue" || node.kind === "topic" || node.kind === "stream" || node.kind === "event_bus" || node.kind === "broker") return "queue";
|
|
655
|
-
if (node.kind === "
|
|
747
|
+
if (node.kind === "scheduler" || node.kind === "cron") return "scheduler";
|
|
748
|
+
if (node.kind === "worker" || node.kind === "job" || node.kind === "batch") return "worker";
|
|
749
|
+
if (node.kind === "function" || node.kind === "lambda") return "function";
|
|
750
|
+
if (node.kind === "pod" || node.kind === "container" || node.kind === "sidecar") return "pod";
|
|
751
|
+
if (node.kind === "secret" || node.kind === "key" || node.kind === "certificate" || node.kind === "vault") return "key";
|
|
752
|
+
if (node.kind === "auth" || node.kind === "oauth" || node.kind === "oidc" || node.kind === "jwt" || node.kind === "lock") return "lock";
|
|
753
|
+
if (node.kind === "firewall" || node.kind === "waf" || node.kind === "vpn" || node.kind === "bastion") return "security";
|
|
754
|
+
if (node.kind === "proxy" || node.kind === "reverse_proxy" || node.kind === "router" || node.kind === "nat" || node.kind === "switch") return "gateway";
|
|
755
|
+
if (node.kind === "monitor" || node.kind === "metrics" || node.kind === "dashboard" || node.kind === "slo" || node.kind === "probe") return "metrics";
|
|
756
|
+
if (node.kind === "registry" || node.kind === "artifact") return "registry";
|
|
757
|
+
if (node.kind === "mobile") return "mobile";
|
|
758
|
+
if (node.kind === "api" || node.kind === "service" || node.kind === "microservice" || node.kind === "backend" || node.kind === "server" || node.kind === "handler" || node.kind === "controller") return "server";
|
|
656
759
|
if (node.kind === "browser" || node.kind === "web" || node.kind === "frontend" || node.kind === "app") return "browser";
|
|
657
760
|
if (node.kind === "user" || node.kind === "client") return "user";
|
|
658
761
|
if (node.kind === "decision" || node.kind === "condition") return "decision";
|
|
659
762
|
if (node.kind === "cache") return "cache";
|
|
660
763
|
return ICONS[node.kind] ? node.kind : node.role;
|
|
661
764
|
}
|
|
662
|
-
function
|
|
663
|
-
const wrap = doc.createElement("div");
|
|
664
|
-
wrap.className = "markdy-node__icon";
|
|
665
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
765
|
+
function appendGlyph(doc, wrap, spec) {
|
|
666
766
|
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
667
767
|
svg.setAttribute("viewBox", "0 0 24 24");
|
|
668
768
|
svg.setAttribute("fill", "none");
|
|
669
|
-
svg.setAttribute("stroke-width", "
|
|
769
|
+
svg.setAttribute("stroke-width", "1.75");
|
|
670
770
|
svg.setAttribute("stroke-linecap", "round");
|
|
671
771
|
svg.setAttribute("stroke-linejoin", "round");
|
|
672
|
-
const spec = ICONS[iconKeyForNode(node)] ?? ICONS.service;
|
|
673
772
|
for (const [tag, attrs] of spec) {
|
|
674
773
|
const child = doc.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
675
774
|
for (const [name, value] of Object.entries(attrs)) child.setAttribute(name, value);
|
|
676
775
|
svg.appendChild(child);
|
|
677
776
|
}
|
|
678
777
|
wrap.appendChild(svg);
|
|
778
|
+
}
|
|
779
|
+
function resolveImageSrc(node, assets) {
|
|
780
|
+
const raw = node.props?.image ?? node.props?.logo;
|
|
781
|
+
if (typeof raw !== "string" || raw.length === 0) return void 0;
|
|
782
|
+
return assets?.[raw] ?? raw;
|
|
783
|
+
}
|
|
784
|
+
function createNodeMediaEl(doc, node, assets) {
|
|
785
|
+
const wrap = doc.createElement("div");
|
|
786
|
+
wrap.className = "markdy-node__icon";
|
|
787
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
788
|
+
const imgSrc = resolveImageSrc(node, assets);
|
|
789
|
+
if (imgSrc) {
|
|
790
|
+
wrap.dataset.media = "image";
|
|
791
|
+
const fit = typeof node.props?.imageFit === "string" ? node.props.imageFit.toLowerCase() : "contain";
|
|
792
|
+
wrap.dataset.fit = fit === "cover" ? "cover" : "contain";
|
|
793
|
+
const img = doc.createElement("img");
|
|
794
|
+
img.src = imgSrc;
|
|
795
|
+
img.alt = "";
|
|
796
|
+
img.decoding = "async";
|
|
797
|
+
img.loading = "lazy";
|
|
798
|
+
img.referrerPolicy = "no-referrer";
|
|
799
|
+
img.addEventListener("error", () => {
|
|
800
|
+
wrap.removeAttribute("data-media");
|
|
801
|
+
wrap.removeAttribute("data-fit");
|
|
802
|
+
wrap.textContent = "";
|
|
803
|
+
appendGlyph(doc, wrap, ICONS[iconKeyForNode(node)] ?? ICONS.service);
|
|
804
|
+
});
|
|
805
|
+
wrap.appendChild(img);
|
|
806
|
+
return wrap;
|
|
807
|
+
}
|
|
808
|
+
appendGlyph(doc, wrap, ICONS[iconKeyForNode(node)] ?? ICONS.service);
|
|
679
809
|
return wrap;
|
|
680
810
|
}
|
|
681
|
-
function createNodeEl(node, theme) {
|
|
811
|
+
function createNodeEl(node, theme, assets) {
|
|
682
812
|
const el = document.createElement("div");
|
|
683
|
-
el.className = "markdy-node markdy-scene-
|
|
684
|
-
el.dataset.
|
|
813
|
+
el.className = "markdy-node markdy-scene-node";
|
|
814
|
+
el.dataset.node = node.id;
|
|
685
815
|
el.dataset.role = node.role;
|
|
686
816
|
el.style.left = `${node.x}px`;
|
|
687
817
|
el.style.top = `${node.y}px`;
|
|
@@ -694,19 +824,14 @@ function createNodeEl(node, theme) {
|
|
|
694
824
|
el.dataset.icon = iconKeyForNode(node);
|
|
695
825
|
el.title = `${node.label} (${typeText})`;
|
|
696
826
|
el.setAttribute("aria-label", el.title);
|
|
697
|
-
const rail = document.createElement("div");
|
|
698
|
-
rail.className = "markdy-node__rail";
|
|
699
|
-
const type = document.createElement("div");
|
|
700
|
-
type.className = "markdy-node__type";
|
|
701
|
-
type.textContent = typeText;
|
|
702
827
|
const body = document.createElement("div");
|
|
703
828
|
body.className = "markdy-node__body";
|
|
704
|
-
const icon =
|
|
829
|
+
const icon = createNodeMediaEl(document, node, assets);
|
|
705
830
|
const label = document.createElement("div");
|
|
706
831
|
label.className = "markdy-node__label";
|
|
707
832
|
label.textContent = node.label;
|
|
708
833
|
body.append(icon, label);
|
|
709
|
-
el.append(
|
|
834
|
+
el.append(body);
|
|
710
835
|
return el;
|
|
711
836
|
}
|
|
712
837
|
function createTitleEl(title) {
|
|
@@ -740,18 +865,18 @@ function ensureSceneStyles(doc) {
|
|
|
740
865
|
linear-gradient(90deg, var(--md-grid-minor) 1px, transparent 1px) 0 0 / 32px 32px,
|
|
741
866
|
linear-gradient(var(--md-grid-major) 1px, transparent 1px) 0 0 / 160px 160px,
|
|
742
867
|
linear-gradient(90deg, var(--md-grid-major) 1px, transparent 1px) 0 0 / 160px 160px;
|
|
743
|
-
mask-image:
|
|
744
|
-
opacity: 0.
|
|
868
|
+
mask-image: radial-gradient(ellipse at 50% 42%, #000 55%, transparent 100%);
|
|
869
|
+
opacity: 0.5;
|
|
745
870
|
}
|
|
746
871
|
.markdy-scene-root::after {
|
|
747
872
|
z-index: 1;
|
|
748
873
|
background:
|
|
749
|
-
radial-gradient(ellipse at 50%
|
|
750
|
-
linear-gradient(180deg, transparent 0%, rgba(2, 6, 23, 0.
|
|
751
|
-
opacity: 0.
|
|
874
|
+
radial-gradient(ellipse at 50% -8%, color-mix(in srgb, var(--md-accent) 10%, transparent), transparent 46%),
|
|
875
|
+
linear-gradient(180deg, transparent 0%, rgba(2, 6, 23, 0.05) 62%, var(--md-vignette) 100%);
|
|
876
|
+
opacity: 0.62;
|
|
752
877
|
}
|
|
753
878
|
.markdy-scene-content { z-index: 2; }
|
|
754
|
-
.markdy-scene-
|
|
879
|
+
.markdy-scene-node-layer {
|
|
755
880
|
position: absolute;
|
|
756
881
|
inset: 0;
|
|
757
882
|
overflow: visible;
|
|
@@ -772,17 +897,22 @@ function applyThemeToScene(scene, theme) {
|
|
|
772
897
|
scene.style.setProperty("--md-grid-major", theme.gridMajor);
|
|
773
898
|
scene.style.setProperty("--md-vignette", theme.vignette);
|
|
774
899
|
scene.style.setProperty("--md-accent", theme.accent);
|
|
900
|
+
scene.style.setProperty("--md-node-surface", theme.nodeSurface ?? theme.surface);
|
|
901
|
+
scene.style.setProperty("--md-node-surface-raised", theme.nodeSurfaceRaised ?? theme.surfaceRaised);
|
|
902
|
+
scene.style.setProperty("--md-hairline", theme.hairline ?? `color-mix(in srgb, ${theme.border} 50%, transparent)`);
|
|
903
|
+
scene.style.setProperty("--md-shadow", theme.shadow ?? "rgba(2, 6, 23, 0.55)");
|
|
775
904
|
scene.dataset.markdyTheme = theme.name;
|
|
776
905
|
}
|
|
777
906
|
|
|
778
|
-
// src/
|
|
779
|
-
function
|
|
907
|
+
// src/diagram.ts
|
|
908
|
+
function createDiagram(opts) {
|
|
780
909
|
const {
|
|
781
910
|
container,
|
|
782
911
|
code,
|
|
783
912
|
autoplay = true,
|
|
784
913
|
loop = true,
|
|
785
914
|
copyright = true,
|
|
915
|
+
assets,
|
|
786
916
|
progressBar,
|
|
787
917
|
sceneBoundaryProgress,
|
|
788
918
|
playbackRate: initialPlaybackRate = 1,
|
|
@@ -870,15 +1000,15 @@ function createPlayer(opts) {
|
|
|
870
1000
|
sceneContent.className = "markdy-scene-content";
|
|
871
1001
|
Object.assign(sceneContent.style, { position: "absolute", inset: "0" });
|
|
872
1002
|
scene.appendChild(sceneContent);
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
sceneContent.appendChild(
|
|
1003
|
+
const nodeLayer = document.createElement("div");
|
|
1004
|
+
nodeLayer.className = "markdy-scene-node-layer";
|
|
1005
|
+
sceneContent.appendChild(nodeLayer);
|
|
876
1006
|
const titleEl = createTitleEl(plan.title);
|
|
877
|
-
|
|
1007
|
+
nodeLayer.appendChild(titleEl);
|
|
878
1008
|
const nodeEls = /* @__PURE__ */ new Map();
|
|
879
1009
|
for (const node of plan.nodes) {
|
|
880
|
-
const el = createNodeEl(node, plan.theme);
|
|
881
|
-
|
|
1010
|
+
const el = createNodeEl(node, plan.theme, assets);
|
|
1011
|
+
nodeLayer.appendChild(el);
|
|
882
1012
|
nodeEls.set(node.id, el);
|
|
883
1013
|
}
|
|
884
1014
|
function scaleScene() {
|
|
@@ -924,7 +1054,7 @@ function createPlayer(opts) {
|
|
|
924
1054
|
if (totalDurationMs > 0) updateProgressBar(sceneMs / totalDurationMs);
|
|
925
1055
|
rafId = requestAnimationFrame(rafTick);
|
|
926
1056
|
}
|
|
927
|
-
const
|
|
1057
|
+
const diagram = {
|
|
928
1058
|
play() {
|
|
929
1059
|
if (isPlaying) return;
|
|
930
1060
|
isPlaying = true;
|
|
@@ -966,10 +1096,10 @@ function createPlayer(opts) {
|
|
|
966
1096
|
},
|
|
967
1097
|
seekToBeat(name) {
|
|
968
1098
|
const beat = plan.beats.find((b) => b.name === name);
|
|
969
|
-
if (beat)
|
|
1099
|
+
if (beat) diagram.seek(beat.start);
|
|
970
1100
|
},
|
|
971
1101
|
destroy() {
|
|
972
|
-
|
|
1102
|
+
diagram.pause();
|
|
973
1103
|
for (const anim of allAnims) anim.cancel();
|
|
974
1104
|
resizeObserver.disconnect();
|
|
975
1105
|
if (progressEl?.parentNode === viewport) viewport.removeChild(progressEl);
|
|
@@ -979,15 +1109,15 @@ function createPlayer(opts) {
|
|
|
979
1109
|
};
|
|
980
1110
|
viewport.style.cursor = "pointer";
|
|
981
1111
|
viewport.addEventListener("click", () => {
|
|
982
|
-
if (isPlaying)
|
|
1112
|
+
if (isPlaying) diagram.pause();
|
|
983
1113
|
else {
|
|
984
1114
|
if (!loop && sceneMs >= totalDurationMs) sceneMs = 0;
|
|
985
|
-
|
|
1115
|
+
diagram.play();
|
|
986
1116
|
}
|
|
987
1117
|
});
|
|
988
|
-
if (autoplay)
|
|
989
|
-
return
|
|
1118
|
+
if (autoplay) diagram.play();
|
|
1119
|
+
return diagram;
|
|
990
1120
|
}
|
|
991
1121
|
export {
|
|
992
|
-
|
|
1122
|
+
createDiagram
|
|
993
1123
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
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,19 +43,20 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@markdy/core": "0.8.
|
|
46
|
+
"@markdy/core": "0.8.4"
|
|
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.4"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsup",
|
|
57
57
|
"typecheck": "tsc --noEmit",
|
|
58
58
|
"lint": "tsc --noEmit",
|
|
59
|
-
"test": "vitest run"
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"harness": "pnpm --filter @markdy/core run build && pnpm run build && node harness/serve.mjs"
|
|
60
61
|
}
|
|
61
62
|
}
|