@markdy/renderer-dom 1.0.19 → 1.0.20
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/dist/index.d.ts +2 -1
- package/dist/index.js +341 -46
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BeatRange, Diagnostic, RenderPlan } from '@markdy/core';
|
|
1
|
+
import { BeatRange, ThemeTokens, Diagnostic, RenderPlan } from '@markdy/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Markdy diagram runtime — renders a RenderPlan via WAAPI.
|
|
@@ -50,6 +50,7 @@ interface Diagram {
|
|
|
50
50
|
seekToBeat(name: string): void;
|
|
51
51
|
nextBeat(): void;
|
|
52
52
|
prevBeat(): void;
|
|
53
|
+
setTheme(theme: string | ThemeTokens): void;
|
|
53
54
|
destroy(): void;
|
|
54
55
|
}
|
|
55
56
|
declare function createDiagram(opts: DiagramOptions): Diagram;
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
compressMarkdyToUrlHash,
|
|
10
10
|
parseAndCompile,
|
|
11
|
-
resolvePlayer
|
|
11
|
+
resolvePlayer,
|
|
12
|
+
resolveTheme
|
|
12
13
|
} from "@markdy/core";
|
|
13
14
|
|
|
14
15
|
// src/geometry/rect.ts
|
|
@@ -316,10 +317,112 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
316
317
|
const targetCenter = rectCenter(targetRect);
|
|
317
318
|
const dx = targetCenter.x - sourceCenter.x;
|
|
318
319
|
const dy = targetCenter.y - sourceCenter.y;
|
|
320
|
+
const isDominantVertical = Math.abs(dy) >= Math.abs(dx);
|
|
319
321
|
const stubLen = 18;
|
|
320
322
|
const allObstacles = obstacles;
|
|
321
323
|
const candidates = [];
|
|
322
|
-
if (
|
|
324
|
+
if (dy >= 20) {
|
|
325
|
+
let sX = sourceCenter.x;
|
|
326
|
+
let tX = targetCenter.x;
|
|
327
|
+
if (dx > 16) {
|
|
328
|
+
sX = clamp(sourceCenter.x + (lane > 0 ? Math.abs(laneShift) : 6), sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
329
|
+
tX = clamp(targetCenter.x - (lane > 0 ? Math.abs(laneShift) : 6), targetRect.x1 + 14, targetRect.x2 - 14);
|
|
330
|
+
} else if (dx < -16) {
|
|
331
|
+
sX = clamp(sourceCenter.x - (lane > 0 ? Math.abs(laneShift) : 6), sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
332
|
+
tX = clamp(targetCenter.x + (lane > 0 ? Math.abs(laneShift) : 6), targetRect.x1 + 14, targetRect.x2 - 14);
|
|
333
|
+
} else {
|
|
334
|
+
sX = clamp(sourceCenter.x + laneShift, sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
335
|
+
tX = clamp(targetCenter.x + laneShift, targetRect.x1 + 14, targetRect.x2 - 14);
|
|
336
|
+
}
|
|
337
|
+
const sPort = { x: sX, y: sourceRect.y2 };
|
|
338
|
+
const tPort = { x: tX, y: targetRect.y1 };
|
|
339
|
+
if (Math.abs(sX - tX) < 1) {
|
|
340
|
+
candidates.push([sPort, tPort]);
|
|
341
|
+
} else {
|
|
342
|
+
const midY = round1((sPort.y + tPort.y) / 2 + laneShift);
|
|
343
|
+
candidates.push([
|
|
344
|
+
sPort,
|
|
345
|
+
{ x: sPort.x, y: midY },
|
|
346
|
+
{ x: tPort.x, y: midY },
|
|
347
|
+
tPort
|
|
348
|
+
]);
|
|
349
|
+
}
|
|
350
|
+
let minObstacleX = Math.min(sourceRect.x1, targetRect.x1);
|
|
351
|
+
let maxObstacleX = Math.max(sourceRect.x2, targetRect.x2);
|
|
352
|
+
for (const o of allObstacles) {
|
|
353
|
+
if (o.y2 > sourceRect.y2 && o.y1 < targetRect.y1) {
|
|
354
|
+
minObstacleX = Math.min(minObstacleX, o.x1);
|
|
355
|
+
maxObstacleX = Math.max(maxObstacleX, o.x2);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const bypassXLeft = Math.max(16, minObstacleX - 24 - Math.abs(laneShift));
|
|
359
|
+
const bypassXRight = Math.min(bounds.width - 16, maxObstacleX + 24 + Math.abs(laneShift));
|
|
360
|
+
candidates.push([
|
|
361
|
+
sPort,
|
|
362
|
+
{ x: sPort.x, y: sPort.y + 12 },
|
|
363
|
+
{ x: bypassXLeft, y: sPort.y + 12 },
|
|
364
|
+
{ x: bypassXLeft, y: tPort.y - 12 },
|
|
365
|
+
{ x: tPort.x, y: tPort.y - 12 },
|
|
366
|
+
tPort
|
|
367
|
+
]);
|
|
368
|
+
candidates.push([
|
|
369
|
+
sPort,
|
|
370
|
+
{ x: sPort.x, y: sPort.y + 12 },
|
|
371
|
+
{ x: bypassXRight, y: sPort.y + 12 },
|
|
372
|
+
{ x: bypassXRight, y: tPort.y - 12 },
|
|
373
|
+
{ x: tPort.x, y: tPort.y - 12 },
|
|
374
|
+
tPort
|
|
375
|
+
]);
|
|
376
|
+
} else if (dy <= -20) {
|
|
377
|
+
let sX = sourceCenter.x;
|
|
378
|
+
let tX = targetCenter.x;
|
|
379
|
+
if (dx > 16) {
|
|
380
|
+
sX = clamp(sourceCenter.x + (lane > 0 ? Math.abs(laneShift) : 6), sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
381
|
+
tX = clamp(targetCenter.x - (lane > 0 ? Math.abs(laneShift) : 6), targetRect.x1 + 14, targetRect.x2 - 14);
|
|
382
|
+
} else if (dx < -16) {
|
|
383
|
+
sX = clamp(sourceCenter.x - (lane > 0 ? Math.abs(laneShift) : 6), sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
384
|
+
tX = clamp(targetCenter.x + (lane > 0 ? Math.abs(laneShift) : 6), targetRect.x1 + 14, targetRect.x2 - 14);
|
|
385
|
+
} else {
|
|
386
|
+
sX = clamp(sourceCenter.x + laneShift, sourceRect.x1 + 14, sourceRect.x2 - 14);
|
|
387
|
+
tX = clamp(targetCenter.x + laneShift, targetRect.x1 + 14, targetRect.x2 - 14);
|
|
388
|
+
}
|
|
389
|
+
const sPort = { x: sX, y: sourceRect.y1 };
|
|
390
|
+
const tPort = { x: tX, y: targetRect.y2 };
|
|
391
|
+
if (Math.abs(sX - tX) < 1) {
|
|
392
|
+
candidates.push([sPort, tPort]);
|
|
393
|
+
} else {
|
|
394
|
+
const midY = round1((sPort.y + tPort.y) / 2 + laneShift);
|
|
395
|
+
candidates.push([
|
|
396
|
+
sPort,
|
|
397
|
+
{ x: sPort.x, y: midY },
|
|
398
|
+
{ x: tPort.x, y: midY },
|
|
399
|
+
tPort
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
let minObstacleX = Math.min(sourceRect.x1, targetRect.x1);
|
|
403
|
+
let maxObstacleX = Math.max(sourceRect.x2, targetRect.x2);
|
|
404
|
+
for (const o of allObstacles) {
|
|
405
|
+
if (o.y1 < sourceRect.y2 && o.y2 > targetRect.y1) {
|
|
406
|
+
minObstacleX = Math.min(minObstacleX, o.x1);
|
|
407
|
+
maxObstacleX = Math.max(maxObstacleX, o.x2);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const bypassXLeft = Math.max(16, minObstacleX - 28 - Math.abs(laneShift));
|
|
411
|
+
const bypassXRight = Math.min(bounds.width - 16, maxObstacleX + 28 + Math.abs(laneShift));
|
|
412
|
+
candidates.push([
|
|
413
|
+
{ x: sourceRect.x1, y: sourceCenter.y },
|
|
414
|
+
{ x: bypassXLeft, y: sourceCenter.y },
|
|
415
|
+
{ x: bypassXLeft, y: targetCenter.y },
|
|
416
|
+
{ x: targetRect.x1, y: targetCenter.y }
|
|
417
|
+
]);
|
|
418
|
+
candidates.push([
|
|
419
|
+
{ x: sourceRect.x2, y: sourceCenter.y },
|
|
420
|
+
{ x: bypassXRight, y: sourceCenter.y },
|
|
421
|
+
{ x: bypassXRight, y: targetCenter.y },
|
|
422
|
+
{ x: targetRect.x2, y: targetCenter.y }
|
|
423
|
+
]);
|
|
424
|
+
}
|
|
425
|
+
if (dx >= 20) {
|
|
323
426
|
let sY = sourceCenter.y;
|
|
324
427
|
let tY = targetCenter.y;
|
|
325
428
|
if (Math.abs(dy) > 16) {
|
|
@@ -334,15 +437,16 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
334
437
|
const tPort = { x: targetRect.x1, y: tY };
|
|
335
438
|
if (Math.abs(sY - tY) < 1) {
|
|
336
439
|
candidates.push([sPort, tPort]);
|
|
440
|
+
} else {
|
|
441
|
+
const rawMidX = (sPort.x + tPort.x) / 2 + laneShift;
|
|
442
|
+
const midX = round1(clamp(rawMidX, sourceRect.x2 + 8, targetRect.x1 - 8));
|
|
443
|
+
candidates.push([
|
|
444
|
+
sPort,
|
|
445
|
+
{ x: midX, y: sY },
|
|
446
|
+
{ x: midX, y: tY },
|
|
447
|
+
tPort
|
|
448
|
+
]);
|
|
337
449
|
}
|
|
338
|
-
const rawMidX = (sPort.x + tPort.x) / 2 + laneShift;
|
|
339
|
-
const midX = round1(clamp(rawMidX, sourceRect.x2 + 8, targetRect.x1 - 8));
|
|
340
|
-
candidates.push([
|
|
341
|
-
sPort,
|
|
342
|
-
{ x: midX, y: sY },
|
|
343
|
-
{ x: midX, y: tY },
|
|
344
|
-
tPort
|
|
345
|
-
]);
|
|
346
450
|
let minObstacleY = Math.min(sourceRect.y1, targetRect.y1);
|
|
347
451
|
for (const o of allObstacles) {
|
|
348
452
|
if (o.x2 > sourceRect.x2 && o.x1 < targetRect.x1) {
|
|
@@ -356,7 +460,7 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
356
460
|
{ x: targetCenter.x, y: bypassYTop },
|
|
357
461
|
{ x: targetCenter.x, y: targetRect.y1 }
|
|
358
462
|
]);
|
|
359
|
-
} else if (dx
|
|
463
|
+
} else if (dx <= -20) {
|
|
360
464
|
const sX = clamp(sourceCenter.x + (lane > 0 ? lane % 2 === 1 ? 8 : -8 : 0), sourceRect.x1 + 16, sourceRect.x2 - 16);
|
|
361
465
|
const tX = clamp(targetCenter.x + (lane > 0 ? lane % 2 === 1 ? 8 : -8 : 0), targetRect.x1 + 16, targetRect.x2 - 16);
|
|
362
466
|
let minObstacleY = Math.min(sourceRect.y1, targetRect.y1);
|
|
@@ -401,23 +505,6 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
401
505
|
{ x: midX, y: tY },
|
|
402
506
|
tPortR
|
|
403
507
|
]);
|
|
404
|
-
} else {
|
|
405
|
-
const sX = clamp(sourceCenter.x + laneShift, sourceRect.x1 + 16, sourceRect.x2 - 16);
|
|
406
|
-
const tX = clamp(targetCenter.x + laneShift, targetRect.x1 + 16, targetRect.x2 - 16);
|
|
407
|
-
const sourceDown = dy >= 0;
|
|
408
|
-
const sPort = { x: sX, y: sourceDown ? sourceRect.y2 : sourceRect.y1 };
|
|
409
|
-
const tPort = { x: tX, y: sourceDown ? targetRect.y1 : targetRect.y2 };
|
|
410
|
-
if (Math.abs(sX - tX) < 1) {
|
|
411
|
-
candidates.push([sPort, tPort]);
|
|
412
|
-
} else {
|
|
413
|
-
const midY = round1((sPort.y + tPort.y) / 2 + laneShift);
|
|
414
|
-
candidates.push([
|
|
415
|
-
sPort,
|
|
416
|
-
{ x: sPort.x, y: midY },
|
|
417
|
-
{ x: tPort.x, y: midY },
|
|
418
|
-
tPort
|
|
419
|
-
]);
|
|
420
|
-
}
|
|
421
508
|
}
|
|
422
509
|
for (const obstacle of allObstacles) {
|
|
423
510
|
const bypassYTop = Math.max(16, obstacle.y1 - 18 - Math.abs(laneShift));
|
|
@@ -443,6 +530,12 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
443
530
|
tP
|
|
444
531
|
]);
|
|
445
532
|
}
|
|
533
|
+
if (candidates.length === 0) {
|
|
534
|
+
candidates.push([
|
|
535
|
+
{ x: sourceCenter.x, y: sourceCenter.y },
|
|
536
|
+
{ x: targetCenter.x, y: targetCenter.y }
|
|
537
|
+
]);
|
|
538
|
+
}
|
|
446
539
|
let best = candidates[0];
|
|
447
540
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
448
541
|
for (const candidate of candidates) {
|
|
@@ -450,7 +543,27 @@ function routeOrthogonal(sourceRect, targetRect, obstacles, bounds, lane = 0) {
|
|
|
450
543
|
const hits = countPathIntersections(cleaned, allObstacles);
|
|
451
544
|
const bends = routeBends(cleaned);
|
|
452
545
|
const length = routeLength(cleaned);
|
|
453
|
-
|
|
546
|
+
let portPenalty = 0;
|
|
547
|
+
const startP = cleaned[0];
|
|
548
|
+
const endP = cleaned[cleaned.length - 1];
|
|
549
|
+
if (isDominantVertical) {
|
|
550
|
+
if (dy >= 20) {
|
|
551
|
+
if (Math.abs(startP.y - sourceRect.y1) < 1) portPenalty += 25e3;
|
|
552
|
+
if (Math.abs(endP.y - targetRect.y2) < 1) portPenalty += 25e3;
|
|
553
|
+
} else if (dy <= -20) {
|
|
554
|
+
if (Math.abs(startP.y - sourceRect.y2) < 1) portPenalty += 25e3;
|
|
555
|
+
if (Math.abs(endP.y - targetRect.y1) < 1) portPenalty += 25e3;
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
if (dx >= 20) {
|
|
559
|
+
if (Math.abs(startP.x - sourceRect.x1) < 1) portPenalty += 25e3;
|
|
560
|
+
if (Math.abs(endP.x - targetRect.x2) < 1) portPenalty += 25e3;
|
|
561
|
+
} else if (dx <= -20) {
|
|
562
|
+
if (Math.abs(startP.x - sourceRect.x2) < 1) portPenalty += 25e3;
|
|
563
|
+
if (Math.abs(endP.x - targetRect.x1) < 1) portPenalty += 25e3;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const score = hits * 1e6 + portPenalty + bends * 500 + length;
|
|
454
567
|
if (score < bestScore) {
|
|
455
568
|
bestScore = score;
|
|
456
569
|
best = cleaned;
|
|
@@ -522,9 +635,14 @@ function ensureEdgeLayer(scene) {
|
|
|
522
635
|
}
|
|
523
636
|
function ensureDefs(svg, theme, id) {
|
|
524
637
|
const key = `data-markdy-defs-${id}`;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
638
|
+
let defs = svg.querySelector(`defs[${key}='1']`);
|
|
639
|
+
if (!defs) {
|
|
640
|
+
defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
641
|
+
defs.setAttribute(key, "1");
|
|
642
|
+
svg.prepend(defs);
|
|
643
|
+
} else {
|
|
644
|
+
defs.replaceChildren();
|
|
645
|
+
}
|
|
528
646
|
for (const [kind, color] of Object.entries(theme.edges)) {
|
|
529
647
|
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
530
648
|
arrow.setAttribute("id", `${id}-arrow-${kind}`);
|
|
@@ -571,7 +689,27 @@ function ensureDefs(svg, theme, id) {
|
|
|
571
689
|
filter.appendChild(turb);
|
|
572
690
|
filter.appendChild(disp);
|
|
573
691
|
defs.appendChild(filter);
|
|
574
|
-
|
|
692
|
+
}
|
|
693
|
+
function updateEdgeLayerTheme(svg, edgeRuntimeMap, theme, sceneId) {
|
|
694
|
+
ensureDefs(svg, theme, sceneId);
|
|
695
|
+
const isDark = isDarkTheme(theme);
|
|
696
|
+
for (const runtime of edgeRuntimeMap.values()) {
|
|
697
|
+
const color = theme.edges[runtime.kind];
|
|
698
|
+
runtime.color = color;
|
|
699
|
+
runtime.path.setAttribute("stroke", color);
|
|
700
|
+
if (runtime.kind !== "dependency") {
|
|
701
|
+
runtime.path.style.filter = `drop-shadow(0 0 4px ${translucentColor(color, "44")})`;
|
|
702
|
+
}
|
|
703
|
+
runtime.dot.setAttribute("fill", color);
|
|
704
|
+
runtime.dot.style.filter = `drop-shadow(0 0 6px ${color}) drop-shadow(0 0 12px ${color}88)`;
|
|
705
|
+
if (runtime.labelPlate) {
|
|
706
|
+
runtime.labelPlate.setAttribute("fill", theme.canvas ?? theme.surface ?? "#ffffff");
|
|
707
|
+
runtime.labelPlate.setAttribute("stroke", translucentColor(color, "28"));
|
|
708
|
+
}
|
|
709
|
+
if (runtime.label) {
|
|
710
|
+
runtime.label.setAttribute("fill", computeEdgeLabelColor(color, isDark));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
575
713
|
}
|
|
576
714
|
function isDarkTheme(theme) {
|
|
577
715
|
if (theme.name === "paper" || theme.name === "editorial" || theme.name === "sketchy") {
|
|
@@ -1635,6 +1773,10 @@ function ensureNodeStyles(doc) {
|
|
|
1635
1773
|
border-radius: 4px;
|
|
1636
1774
|
transform: rotate(0deg);
|
|
1637
1775
|
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
|
|
1776
|
+
filter: drop-shadow(0 0 1px var(--md-hairline, rgba(15, 23, 42, 0.2))) drop-shadow(0 4px 12px var(--md-shadow, rgba(2, 6, 23, 0.15)));
|
|
1777
|
+
}
|
|
1778
|
+
.markdy-node[data-shape="diamond"][data-focal="1"] {
|
|
1779
|
+
filter: drop-shadow(0 0 1.5px var(--md-accent)) drop-shadow(0 4px 16px color-mix(in srgb, var(--md-accent) 30%, transparent));
|
|
1638
1780
|
}
|
|
1639
1781
|
.markdy-node[data-shape="diamond"] .markdy-node__body {
|
|
1640
1782
|
justify-content: center;
|
|
@@ -1650,6 +1792,13 @@ function ensureNodeStyles(doc) {
|
|
|
1650
1792
|
border-radius: 999px;
|
|
1651
1793
|
min-height: 54px;
|
|
1652
1794
|
}
|
|
1795
|
+
.markdy-node[data-shape="pill"] .markdy-node__body {
|
|
1796
|
+
justify-content: center;
|
|
1797
|
+
padding: 0 20px;
|
|
1798
|
+
}
|
|
1799
|
+
.markdy-node[data-shape="pill"] .markdy-node__label {
|
|
1800
|
+
text-align: center;
|
|
1801
|
+
}
|
|
1653
1802
|
.markdy-node[data-shape="circle"],
|
|
1654
1803
|
.markdy-node[data-kind="dot"] {
|
|
1655
1804
|
border-radius: 50%;
|
|
@@ -1719,9 +1868,13 @@ function ensureNodeStyles(doc) {
|
|
|
1719
1868
|
background: var(--md-node-surface, var(--md-surface));
|
|
1720
1869
|
}
|
|
1721
1870
|
.markdy-node[data-focal="1"] {
|
|
1722
|
-
background:
|
|
1871
|
+
background:
|
|
1872
|
+
linear-gradient(180deg,
|
|
1873
|
+
color-mix(in srgb, var(--md-accent) 12%, var(--md-node-surface-raised, var(--md-surface-raised, #ffffff))),
|
|
1874
|
+
color-mix(in srgb, var(--md-accent) 6%, var(--md-node-surface, var(--md-surface, #ffffff))));
|
|
1723
1875
|
box-shadow:
|
|
1724
|
-
|
|
1876
|
+
0 2px 8px color-mix(in srgb, var(--md-accent) 20%, transparent),
|
|
1877
|
+
inset 0 0 0 1.5px color-mix(in srgb, var(--md-accent) 60%, transparent);
|
|
1725
1878
|
}
|
|
1726
1879
|
.markdy-scene-root[data-markdy-theme="nebula"] .markdy-node {
|
|
1727
1880
|
border: 1px solid color-mix(in srgb, var(--md-role-color, var(--md-accent)) 34%, transparent);
|
|
@@ -1744,8 +1897,8 @@ function ensureNodeStyles(doc) {
|
|
|
1744
1897
|
box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--md-accent) 60%, transparent);
|
|
1745
1898
|
background:
|
|
1746
1899
|
linear-gradient(180deg,
|
|
1747
|
-
color-mix(in srgb, var(--md-accent-
|
|
1748
|
-
color-mix(in srgb, var(--md-accent-
|
|
1900
|
+
color-mix(in srgb, var(--md-accent) 10%, var(--md-node-surface-raised, var(--md-surface-raised, #ffffff))),
|
|
1901
|
+
color-mix(in srgb, var(--md-accent) 5%, var(--md-node-surface, var(--md-surface, #ffffff))));
|
|
1749
1902
|
}
|
|
1750
1903
|
.markdy-node[data-kind="external"] {
|
|
1751
1904
|
border: 1.2px dashed color-mix(in srgb, var(--md-ink, var(--md-text)) 30%, transparent);
|
|
@@ -1768,17 +1921,32 @@ function ensureNodeStyles(doc) {
|
|
|
1768
1921
|
border-color: #ff5a36;
|
|
1769
1922
|
box-shadow: 0 0 18px -8px rgba(255, 90, 54, 0.45);
|
|
1770
1923
|
}
|
|
1924
|
+
.markdy-scene-root[data-markdy-theme="terminal"] .markdy-node[data-shape="diamond"] {
|
|
1925
|
+
filter: drop-shadow(0 0 1px #2b2b2b);
|
|
1926
|
+
}
|
|
1927
|
+
.markdy-scene-root[data-markdy-theme="terminal"] .markdy-node[data-shape="diamond"][data-focal="1"] {
|
|
1928
|
+
filter: drop-shadow(0 0 2px #ff5a36) drop-shadow(0 0 12px rgba(255, 90, 54, 0.35));
|
|
1929
|
+
}
|
|
1771
1930
|
.markdy-scene-root[data-markdy-theme="sketchy"] .markdy-node {
|
|
1772
1931
|
background: #ffffff;
|
|
1773
1932
|
border: 1.5px solid #2d3142;
|
|
1774
1933
|
box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.10);
|
|
1775
1934
|
border-radius: var(--md-radius-md, 4px);
|
|
1776
1935
|
}
|
|
1936
|
+
.markdy-scene-root[data-markdy-theme="sketchy"] .markdy-node[data-shape="diamond"] {
|
|
1937
|
+
filter: drop-shadow(0 0 1px #2d3142) drop-shadow(3px 3px 0 rgba(45, 49, 66, 0.12));
|
|
1938
|
+
}
|
|
1777
1939
|
.markdy-scene-root[data-markdy-theme="sketchy"] .markdy-node[data-focal="1"] {
|
|
1778
1940
|
border-color: #eb6c36;
|
|
1779
1941
|
background: rgba(235, 108, 54, 0.06);
|
|
1780
1942
|
box-shadow: 3px 3px 0 rgba(235, 108, 54, 0.12);
|
|
1781
1943
|
}
|
|
1944
|
+
.markdy-scene-root[data-markdy-theme="nebula"] .markdy-node[data-shape="diamond"] {
|
|
1945
|
+
filter: drop-shadow(0 0 1px var(--md-hairline)) drop-shadow(0 0 18px -4px color-mix(in srgb, var(--md-role-color, var(--md-accent)) 60%, transparent));
|
|
1946
|
+
}
|
|
1947
|
+
.markdy-scene-root[data-markdy-theme="nebula"] .markdy-node[data-shape="diamond"][data-focal="1"] {
|
|
1948
|
+
filter: drop-shadow(0 0 2px var(--md-accent)) drop-shadow(0 0 24px -2px color-mix(in srgb, var(--md-accent) 70%, transparent));
|
|
1949
|
+
}
|
|
1782
1950
|
`;
|
|
1783
1951
|
doc.head.appendChild(style);
|
|
1784
1952
|
}
|
|
@@ -2309,6 +2477,52 @@ function mountSequenceLayer(layer, nodes, messages, activations, theme, bounds)
|
|
|
2309
2477
|
for (const animation of animations) animation.pause();
|
|
2310
2478
|
return animations;
|
|
2311
2479
|
}
|
|
2480
|
+
function updateSequenceLayerTheme(layer, theme) {
|
|
2481
|
+
const svg = layer.querySelector("svg");
|
|
2482
|
+
if (!svg) return;
|
|
2483
|
+
const defs = svg.querySelector("defs");
|
|
2484
|
+
if (defs) {
|
|
2485
|
+
Array.from(defs.querySelectorAll("marker")).forEach((marker) => {
|
|
2486
|
+
const id = marker.getAttribute("id") || "";
|
|
2487
|
+
const path = marker.querySelector("path");
|
|
2488
|
+
if (path) {
|
|
2489
|
+
if (id.includes("response")) {
|
|
2490
|
+
path.setAttribute("stroke", theme.edges.response);
|
|
2491
|
+
} else if (id.includes("event")) {
|
|
2492
|
+
path.setAttribute("fill", theme.edges.event);
|
|
2493
|
+
} else {
|
|
2494
|
+
path.setAttribute("fill", theme.edges.request);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
Array.from(svg.querySelectorAll(".markdy-sequence-lifeline")).forEach((line) => {
|
|
2500
|
+
line.setAttribute("stroke", theme.hairline ?? theme.border);
|
|
2501
|
+
});
|
|
2502
|
+
Array.from(svg.querySelectorAll(".markdy-sequence-activation")).forEach((bar) => {
|
|
2503
|
+
bar.setAttribute("fill", theme.accent);
|
|
2504
|
+
bar.style.filter = `drop-shadow(0 0 6px ${theme.accent}66)`;
|
|
2505
|
+
});
|
|
2506
|
+
Array.from(svg.querySelectorAll(".markdy-sequence-message")).forEach((group) => {
|
|
2507
|
+
const line = group.querySelector("line");
|
|
2508
|
+
if (line) {
|
|
2509
|
+
const markerEnd = line.getAttribute("marker-end") || "";
|
|
2510
|
+
if (markerEnd.includes("response")) line.setAttribute("stroke", theme.edges.response);
|
|
2511
|
+
else if (markerEnd.includes("event")) line.setAttribute("stroke", theme.edges.event);
|
|
2512
|
+
else line.setAttribute("stroke", theme.edges.request);
|
|
2513
|
+
}
|
|
2514
|
+
const plate = group.querySelector("rect");
|
|
2515
|
+
if (plate) {
|
|
2516
|
+
plate.setAttribute("fill", theme.labelPlate ?? theme.surface);
|
|
2517
|
+
plate.setAttribute("stroke", theme.hairline ?? `color-mix(in srgb, ${theme.border} 70%, transparent)`);
|
|
2518
|
+
}
|
|
2519
|
+
const text = group.querySelector("text");
|
|
2520
|
+
if (text) {
|
|
2521
|
+
text.setAttribute("fill", theme.text);
|
|
2522
|
+
if (theme.fonts?.mono) text.setAttribute("font-family", theme.fonts.mono);
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2312
2526
|
|
|
2313
2527
|
// src/tree.ts
|
|
2314
2528
|
function mountTreeBuses(layer, buses, theme) {
|
|
@@ -2512,10 +2726,10 @@ function ensureSceneStyles(doc) {
|
|
|
2512
2726
|
gap: 8px;
|
|
2513
2727
|
padding: 6px 12px;
|
|
2514
2728
|
color: var(--md-text, #f8fafc);
|
|
2515
|
-
background:
|
|
2516
|
-
backdrop-filter:
|
|
2517
|
-
-webkit-backdrop-filter:
|
|
2518
|
-
border-top:
|
|
2729
|
+
background: transparent;
|
|
2730
|
+
backdrop-filter: none;
|
|
2731
|
+
-webkit-backdrop-filter: none;
|
|
2732
|
+
border-top: none;
|
|
2519
2733
|
z-index: 20;
|
|
2520
2734
|
}
|
|
2521
2735
|
.markdy-controls {
|
|
@@ -3071,14 +3285,15 @@ function applyThemeVariables(element, theme) {
|
|
|
3071
3285
|
element.style.setProperty("--md-node-surface-raised", theme.nodeSurfaceRaised ?? theme.surfaceRaised);
|
|
3072
3286
|
element.style.setProperty("--md-hairline", theme.hairline ?? `color-mix(in srgb, ${theme.border} 50%, transparent)`);
|
|
3073
3287
|
element.style.setProperty("--md-shadow", theme.shadow ?? "rgba(2, 6, 23, 0.55)");
|
|
3074
|
-
element.style.setProperty("--md-footer-bg",
|
|
3075
|
-
element.style.setProperty("--md-footer-border",
|
|
3288
|
+
element.style.setProperty("--md-footer-bg", "transparent");
|
|
3289
|
+
element.style.setProperty("--md-footer-border", "transparent");
|
|
3076
3290
|
element.style.setProperty("--md-control-bg", theme.surfaceRaised);
|
|
3077
3291
|
element.style.setProperty("--md-control-border", theme.border);
|
|
3078
3292
|
element.style.setProperty("--md-control-text", theme.textMuted);
|
|
3079
3293
|
element.style.setProperty("--md-control-hover-bg", theme.canvas);
|
|
3080
3294
|
element.style.setProperty("--md-control-hover-text", theme.text);
|
|
3081
3295
|
element.style.setProperty("--md-control-hover-border", theme.accent);
|
|
3296
|
+
element.style.setProperty("--md-segmented-bg", `color-mix(in srgb, ${theme.surfaceRaised} 60%, transparent)`);
|
|
3082
3297
|
element.style.setProperty("--md-divider", theme.border);
|
|
3083
3298
|
if (theme.fonts?.title) element.style.setProperty("--md-font-title", theme.fonts.title);
|
|
3084
3299
|
if (theme.fonts?.nodeName) element.style.setProperty("--md-font-node", theme.fonts.nodeName);
|
|
@@ -3247,7 +3462,7 @@ function createDiagram(opts) {
|
|
|
3247
3462
|
function updateProgressBar(pct) {
|
|
3248
3463
|
if (!progressEl) return;
|
|
3249
3464
|
if (progressMode === "bar") {
|
|
3250
|
-
progressEl.style.background = customColor ?? "#2563eb";
|
|
3465
|
+
progressEl.style.background = customColor ?? plan.theme.accent ?? "#2563eb";
|
|
3251
3466
|
progressEl.style.transformOrigin = "left center";
|
|
3252
3467
|
progressEl.style.transform = `scaleX(${pct})`;
|
|
3253
3468
|
return;
|
|
@@ -3259,7 +3474,7 @@ function createDiagram(opts) {
|
|
|
3259
3474
|
progressEl.style.webkitMask = progressEl.style.mask;
|
|
3260
3475
|
progressEl.style.maskComposite = "exclude";
|
|
3261
3476
|
progressEl.style.webkitMaskComposite = "xor";
|
|
3262
|
-
progressEl.style.padding = "
|
|
3477
|
+
progressEl.style.padding = "1px";
|
|
3263
3478
|
}
|
|
3264
3479
|
let footer = null;
|
|
3265
3480
|
function ensureFooter() {
|
|
@@ -3275,7 +3490,8 @@ function createDiagram(opts) {
|
|
|
3275
3490
|
gap: "6px",
|
|
3276
3491
|
width: "100%",
|
|
3277
3492
|
boxSizing: "border-box",
|
|
3278
|
-
padding: "4px 6px 0"
|
|
3493
|
+
padding: "4px 6px 0",
|
|
3494
|
+
background: "transparent"
|
|
3279
3495
|
});
|
|
3280
3496
|
if (container.parentNode) container.parentNode.insertBefore(footer, container.nextSibling);
|
|
3281
3497
|
else container.appendChild(footer);
|
|
@@ -3735,6 +3951,84 @@ function createDiagram(opts) {
|
|
|
3735
3951
|
}
|
|
3736
3952
|
diagram.seek(target);
|
|
3737
3953
|
},
|
|
3954
|
+
setTheme(theme) {
|
|
3955
|
+
const newTheme = typeof theme === "string" ? resolveTheme(theme) : theme;
|
|
3956
|
+
plan.theme = newTheme;
|
|
3957
|
+
applyThemeToScene(scene, newTheme);
|
|
3958
|
+
if (footer) {
|
|
3959
|
+
applyThemeVariables(footer, newTheme);
|
|
3960
|
+
}
|
|
3961
|
+
for (const node of plan.nodes) {
|
|
3962
|
+
const el = nodeEls.get(node.id);
|
|
3963
|
+
if (el) {
|
|
3964
|
+
if (newTheme.flatCards) {
|
|
3965
|
+
el.style.setProperty("--md-shadow", "transparent");
|
|
3966
|
+
} else {
|
|
3967
|
+
el.style.removeProperty("--md-shadow");
|
|
3968
|
+
}
|
|
3969
|
+
if (newTheme.accentTint) {
|
|
3970
|
+
el.style.setProperty("--md-accent-tint", newTheme.accentTint);
|
|
3971
|
+
} else {
|
|
3972
|
+
el.style.removeProperty("--md-accent-tint");
|
|
3973
|
+
}
|
|
3974
|
+
const roleColor = newTheme.roles[node.role] ?? newTheme.accent;
|
|
3975
|
+
el.style.setProperty("--md-role-color", roleColor);
|
|
3976
|
+
applyDeclaredNodeStyle(el, node.style);
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
const structuralSvg = structuralEdgeHost.querySelector("svg[data-markdy-edge-layer='1']");
|
|
3980
|
+
if (structuralSvg) {
|
|
3981
|
+
updateEdgeLayerTheme(structuralSvg, edgeRuntimes, newTheme, edgeSceneId);
|
|
3982
|
+
}
|
|
3983
|
+
const cameraSvg = cameraLayer.querySelector("svg[data-markdy-edge-layer='1']");
|
|
3984
|
+
if (cameraSvg) {
|
|
3985
|
+
updateEdgeLayerTheme(cameraSvg, edgeRuntimes, newTheme, edgeSceneId);
|
|
3986
|
+
}
|
|
3987
|
+
if (plan.treeBuses && plan.treeBuses.length > 0) {
|
|
3988
|
+
treeLayer.replaceChildren();
|
|
3989
|
+
mountTreeBuses(treeLayer, plan.treeBuses, newTheme);
|
|
3990
|
+
}
|
|
3991
|
+
if (plan.diagramType === "constellation" || plan.diagramType === "radar" || plan.diagramType === "timeline") {
|
|
3992
|
+
constellationLayer.replaceChildren();
|
|
3993
|
+
if (plan.diagramType === "constellation") {
|
|
3994
|
+
mountConstellationLayer(
|
|
3995
|
+
constellationLayer,
|
|
3996
|
+
plan.nodes,
|
|
3997
|
+
newTheme,
|
|
3998
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
3999
|
+
);
|
|
4000
|
+
} else if (plan.diagramType === "radar") {
|
|
4001
|
+
mountRadarLayer(
|
|
4002
|
+
constellationLayer,
|
|
4003
|
+
plan.nodes,
|
|
4004
|
+
newTheme,
|
|
4005
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4006
|
+
);
|
|
4007
|
+
} else if (plan.diagramType === "timeline") {
|
|
4008
|
+
mountTimelineLayer(
|
|
4009
|
+
constellationLayer,
|
|
4010
|
+
plan.nodes,
|
|
4011
|
+
newTheme,
|
|
4012
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4013
|
+
);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
if (plan.diagramType === "sequence") {
|
|
4017
|
+
updateSequenceLayerTheme(sequenceLayer, newTheme);
|
|
4018
|
+
}
|
|
4019
|
+
if (plan.groupBoundaries && plan.groupBoundaries.length > 0) {
|
|
4020
|
+
groupLayer.replaceChildren();
|
|
4021
|
+
mountGroupBoundaries(groupLayer, plan.groupBoundaries, newTheme);
|
|
4022
|
+
}
|
|
4023
|
+
if (plan.annotations && plan.annotations.length > 0) {
|
|
4024
|
+
annotationLayer.replaceChildren();
|
|
4025
|
+
mountAnnotations(annotationLayer, plan.annotations, plan.nodes, newTheme, {
|
|
4026
|
+
width: plan.meta.width,
|
|
4027
|
+
height: plan.meta.height
|
|
4028
|
+
});
|
|
4029
|
+
}
|
|
4030
|
+
if (totalDurationMs > 0) updateProgressBar(sceneMs / totalDurationMs);
|
|
4031
|
+
},
|
|
3738
4032
|
destroy() {
|
|
3739
4033
|
diagram.pause();
|
|
3740
4034
|
if (keyboardShortcuts && typeof window !== "undefined") {
|
|
@@ -3901,6 +4195,7 @@ function createDiagram(opts) {
|
|
|
3901
4195
|
const targetThemes = isDark ? lightThemes : darkThemes;
|
|
3902
4196
|
const nextTheme = targetThemes[Math.floor(Math.random() * targetThemes.length)];
|
|
3903
4197
|
flashControlLabel(button, nextTheme);
|
|
4198
|
+
diagram.setTheme(nextTheme);
|
|
3904
4199
|
container.dispatchEvent(
|
|
3905
4200
|
new CustomEvent("markdy-theme-switch", {
|
|
3906
4201
|
bubbles: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Browser renderer for diagram-native animated MarkdyScript architecture diagrams, built on the Web Animations API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"html2canvas": "^1.4.1",
|
|
48
|
-
"@markdy/core": "1.0.
|
|
48
|
+
"@markdy/core": "1.0.20"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"jsdom": "^29.1.1",
|
|
52
52
|
"tsup": "^8.5.1",
|
|
53
53
|
"typescript": "^5.9.3",
|
|
54
54
|
"vitest": "^4.1.7",
|
|
55
|
-
"@markdy/stdlib-systems": "1.0.
|
|
55
|
+
"@markdy/stdlib-systems": "1.0.20"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|