@markdy/renderer-dom 1.0.29 → 1.0.30
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 +5 -3
- package/dist/index.js +476 -59
- package/package.json +3 -3
- package/dist/chunk-VIZHA7GI.js +0 -99
- package/dist/svg-exporter-7FW6RIP6.js +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BeatRange, ThemeTokens, Diagnostic, RenderPlan } from '@markdy/core';
|
|
1
|
+
import { BeatRange, ThemeTokens, PlayerControlsConfig, Diagnostic, RenderPlan } from '@markdy/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Markdy diagram runtime — renders a RenderPlan via WAAPI.
|
|
@@ -30,8 +30,10 @@ interface DiagramOptions {
|
|
|
30
30
|
interactiveViewport?: boolean;
|
|
31
31
|
/** Base URL for the Share control. Defaults to the Markdy playground. */
|
|
32
32
|
shareUrl?: string;
|
|
33
|
-
/** Host controls override. `true` enables the legacy default set; `false` suppresses script controls. */
|
|
34
|
-
controls?: boolean
|
|
33
|
+
/** Host controls override. `true` enables the legacy default set; `false` suppresses script controls; or pass a granular controls configuration object. */
|
|
34
|
+
controls?: boolean | (PlayerControlsConfig & {
|
|
35
|
+
playback?: boolean;
|
|
36
|
+
});
|
|
35
37
|
onWarning?: (warning: Diagnostic) => void;
|
|
36
38
|
onTimeUpdate?: (seconds: number, durationSeconds: number) => void;
|
|
37
39
|
onPlayStateChange?: (playing: boolean) => void;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
exportDiagramAsVectorSvg,
|
|
3
|
-
getDiagramSceneElement,
|
|
4
|
-
prepareHtmlSceneForExport
|
|
5
|
-
} from "./chunk-VIZHA7GI.js";
|
|
6
|
-
|
|
7
1
|
// src/diagram.ts
|
|
8
2
|
import {
|
|
9
3
|
compressMarkdyToUrlHash,
|
|
@@ -1491,6 +1485,159 @@ function mountConstellationLayer(layer, nodes, theme, bounds) {
|
|
|
1491
1485
|
}
|
|
1492
1486
|
}
|
|
1493
1487
|
|
|
1488
|
+
// src/quadrant.ts
|
|
1489
|
+
function mountQuadrantLayer(layer, nodes, theme, bounds) {
|
|
1490
|
+
if (nodes.length === 0) return;
|
|
1491
|
+
const doc = layer.ownerDocument;
|
|
1492
|
+
Object.assign(layer.style, {
|
|
1493
|
+
position: "absolute",
|
|
1494
|
+
inset: "0",
|
|
1495
|
+
zIndex: "38",
|
|
1496
|
+
pointerEvents: "none"
|
|
1497
|
+
});
|
|
1498
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1499
|
+
svg.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
|
|
1500
|
+
Object.assign(svg.style, {
|
|
1501
|
+
position: "absolute",
|
|
1502
|
+
inset: "0",
|
|
1503
|
+
width: "100%",
|
|
1504
|
+
height: "100%",
|
|
1505
|
+
overflow: "visible"
|
|
1506
|
+
});
|
|
1507
|
+
layer.appendChild(svg);
|
|
1508
|
+
const SAFE = 40;
|
|
1509
|
+
const TITLE_BAND = 64;
|
|
1510
|
+
const contentW = bounds.width - SAFE * 2;
|
|
1511
|
+
const contentH = bounds.height - SAFE - TITLE_BAND - SAFE;
|
|
1512
|
+
const centerX = SAFE + contentW / 2;
|
|
1513
|
+
const centerY = TITLE_BAND + contentH / 2;
|
|
1514
|
+
const stroke = theme?.edges?.dependency ?? theme?.rule ?? theme?.border ?? "#64748b";
|
|
1515
|
+
const vAxis = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
1516
|
+
vAxis.classList.add("markdy-quadrant-vaxis");
|
|
1517
|
+
vAxis.setAttribute("x1", String(centerX));
|
|
1518
|
+
vAxis.setAttribute("y1", String(TITLE_BAND + 16));
|
|
1519
|
+
vAxis.setAttribute("x2", String(centerX));
|
|
1520
|
+
vAxis.setAttribute("y2", String(bounds.height - SAFE - 16));
|
|
1521
|
+
vAxis.setAttribute("stroke", stroke);
|
|
1522
|
+
vAxis.setAttribute("stroke-width", "1.5");
|
|
1523
|
+
vAxis.setAttribute("stroke-dasharray", "5 5");
|
|
1524
|
+
vAxis.setAttribute("opacity", "0.65");
|
|
1525
|
+
svg.appendChild(vAxis);
|
|
1526
|
+
const hAxis = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
1527
|
+
hAxis.classList.add("markdy-quadrant-haxis");
|
|
1528
|
+
hAxis.setAttribute("x1", String(SAFE + 16));
|
|
1529
|
+
hAxis.setAttribute("y1", String(centerY));
|
|
1530
|
+
hAxis.setAttribute("x2", String(bounds.width - SAFE - 16));
|
|
1531
|
+
hAxis.setAttribute("y2", String(centerY));
|
|
1532
|
+
hAxis.setAttribute("stroke", stroke);
|
|
1533
|
+
hAxis.setAttribute("stroke-width", "1.5");
|
|
1534
|
+
hAxis.setAttribute("stroke-dasharray", "5 5");
|
|
1535
|
+
hAxis.setAttribute("opacity", "0.65");
|
|
1536
|
+
svg.appendChild(hAxis);
|
|
1537
|
+
const hub = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1538
|
+
hub.classList.add("markdy-quadrant-hub");
|
|
1539
|
+
hub.setAttribute("cx", String(centerX));
|
|
1540
|
+
hub.setAttribute("cy", String(centerY));
|
|
1541
|
+
hub.setAttribute("r", "4");
|
|
1542
|
+
hub.setAttribute("fill", stroke);
|
|
1543
|
+
hub.setAttribute("opacity", "0.75");
|
|
1544
|
+
svg.appendChild(hub);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// src/swimlane.ts
|
|
1548
|
+
function mountSwimlaneLayer(layer, nodes, theme, bounds) {
|
|
1549
|
+
if (nodes.length === 0) return;
|
|
1550
|
+
const doc = layer.ownerDocument;
|
|
1551
|
+
Object.assign(layer.style, {
|
|
1552
|
+
position: "absolute",
|
|
1553
|
+
inset: "0",
|
|
1554
|
+
zIndex: "38",
|
|
1555
|
+
pointerEvents: "none"
|
|
1556
|
+
});
|
|
1557
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1558
|
+
svg.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
|
|
1559
|
+
Object.assign(svg.style, {
|
|
1560
|
+
position: "absolute",
|
|
1561
|
+
inset: "0",
|
|
1562
|
+
width: "100%",
|
|
1563
|
+
height: "100%",
|
|
1564
|
+
overflow: "visible"
|
|
1565
|
+
});
|
|
1566
|
+
layer.appendChild(svg);
|
|
1567
|
+
const SAFE = 40;
|
|
1568
|
+
const TITLE_BAND = 64;
|
|
1569
|
+
const contentH = bounds.height - SAFE - TITLE_BAND - SAFE;
|
|
1570
|
+
const nodeYs = [...new Set(nodes.map((n) => Math.round(n.y)))].sort((a, b) => a - b);
|
|
1571
|
+
const laneCount = Math.max(nodeYs.length, 1);
|
|
1572
|
+
const laneHeight = contentH / laneCount;
|
|
1573
|
+
const stroke = theme?.edges?.dependency ?? theme?.rule ?? theme?.border ?? "#64748b";
|
|
1574
|
+
for (let idx = 0; idx <= laneCount; idx++) {
|
|
1575
|
+
const y = TITLE_BAND + idx * laneHeight;
|
|
1576
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
1577
|
+
line.classList.add("markdy-swimlane-divider");
|
|
1578
|
+
line.setAttribute("x1", String(SAFE));
|
|
1579
|
+
line.setAttribute("y1", String(y));
|
|
1580
|
+
line.setAttribute("x2", String(bounds.width - SAFE));
|
|
1581
|
+
line.setAttribute("y2", String(y));
|
|
1582
|
+
line.setAttribute("stroke", stroke);
|
|
1583
|
+
line.setAttribute("stroke-width", "1.2");
|
|
1584
|
+
line.setAttribute("stroke-dasharray", idx === 0 || idx === laneCount ? "none" : "5 5");
|
|
1585
|
+
line.setAttribute("opacity", idx === 0 || idx === laneCount ? "0.6" : "0.4");
|
|
1586
|
+
svg.appendChild(line);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/gantt.ts
|
|
1591
|
+
function mountGanttLayer(layer, nodes, theme, bounds) {
|
|
1592
|
+
if (nodes.length === 0) return;
|
|
1593
|
+
const doc = layer.ownerDocument;
|
|
1594
|
+
Object.assign(layer.style, {
|
|
1595
|
+
position: "absolute",
|
|
1596
|
+
inset: "0",
|
|
1597
|
+
zIndex: "38",
|
|
1598
|
+
pointerEvents: "none"
|
|
1599
|
+
});
|
|
1600
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1601
|
+
svg.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
|
|
1602
|
+
Object.assign(svg.style, {
|
|
1603
|
+
position: "absolute",
|
|
1604
|
+
inset: "0",
|
|
1605
|
+
width: "100%",
|
|
1606
|
+
height: "100%",
|
|
1607
|
+
overflow: "visible"
|
|
1608
|
+
});
|
|
1609
|
+
layer.appendChild(svg);
|
|
1610
|
+
const SAFE = 40;
|
|
1611
|
+
const TITLE_BAND = 64;
|
|
1612
|
+
const contentW = bounds.width - SAFE * 2;
|
|
1613
|
+
const stroke = theme?.edges?.dependency ?? theme?.rule ?? theme?.border ?? "#64748b";
|
|
1614
|
+
const totalPhases = Math.max(
|
|
1615
|
+
...nodes.map((n) => {
|
|
1616
|
+
const p = typeof n.props?.phase === "number" ? n.props.phase : 0;
|
|
1617
|
+
const s = typeof n.props?.span === "number" ? n.props.span : 1;
|
|
1618
|
+
return p + s;
|
|
1619
|
+
}),
|
|
1620
|
+
4
|
|
1621
|
+
);
|
|
1622
|
+
const minY = Math.min(...nodes.map((n) => n.y)) - 16;
|
|
1623
|
+
const maxY = Math.max(...nodes.map((n) => n.y + n.height)) + 16;
|
|
1624
|
+
const unitW = contentW / totalPhases;
|
|
1625
|
+
for (let phase = 0; phase <= totalPhases; phase++) {
|
|
1626
|
+
const x = SAFE + phase * unitW;
|
|
1627
|
+
const colLine = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
1628
|
+
colLine.classList.add("markdy-gantt-column");
|
|
1629
|
+
colLine.setAttribute("x1", String(x));
|
|
1630
|
+
colLine.setAttribute("y1", String(Math.max(TITLE_BAND, minY)));
|
|
1631
|
+
colLine.setAttribute("x2", String(x));
|
|
1632
|
+
colLine.setAttribute("y2", String(Math.min(bounds.height - SAFE, maxY)));
|
|
1633
|
+
colLine.setAttribute("stroke", stroke);
|
|
1634
|
+
colLine.setAttribute("stroke-width", "1");
|
|
1635
|
+
colLine.setAttribute("stroke-dasharray", "4 4");
|
|
1636
|
+
colLine.setAttribute("opacity", phase === 0 || phase === totalPhases ? "0.6" : "0.35");
|
|
1637
|
+
svg.appendChild(colLine);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1494
1641
|
// src/radar.ts
|
|
1495
1642
|
function mountRadarLayer(layer, nodes, theme, bounds) {
|
|
1496
1643
|
if (nodes.length < 3) return;
|
|
@@ -1511,14 +1658,18 @@ function mountRadarLayer(layer, nodes, theme, bounds) {
|
|
|
1511
1658
|
overflow: "visible"
|
|
1512
1659
|
});
|
|
1513
1660
|
layer.appendChild(svg);
|
|
1514
|
-
const
|
|
1515
|
-
const
|
|
1661
|
+
const SAFE = 40;
|
|
1662
|
+
const TITLE_BAND = 64;
|
|
1663
|
+
const contentW = bounds.width - SAFE * 2;
|
|
1664
|
+
const contentH = bounds.height - SAFE - TITLE_BAND - SAFE;
|
|
1665
|
+
const centerX = SAFE + contentW / 2;
|
|
1666
|
+
const centerY = TITLE_BAND + contentH / 2;
|
|
1516
1667
|
const nodeCenters = nodes.map((n) => ({
|
|
1517
1668
|
x: n.x + n.width / 2,
|
|
1518
1669
|
y: n.y + n.height / 2
|
|
1519
1670
|
}));
|
|
1520
|
-
const
|
|
1521
|
-
const
|
|
1671
|
+
const strokeAxis = theme?.edges?.dependency ?? theme?.rule ?? theme?.border ?? "#64748b";
|
|
1672
|
+
const strokeRing = theme?.rule ?? theme?.edges?.dependency ?? theme?.border ?? "#64748b";
|
|
1522
1673
|
const accentColor = theme?.accent ?? "#38bdf8";
|
|
1523
1674
|
for (const nc of nodeCenters) {
|
|
1524
1675
|
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
@@ -1526,10 +1677,10 @@ function mountRadarLayer(layer, nodes, theme, bounds) {
|
|
|
1526
1677
|
line.setAttribute("y1", String(centerY));
|
|
1527
1678
|
line.setAttribute("x2", String(nc.x));
|
|
1528
1679
|
line.setAttribute("y2", String(nc.y));
|
|
1529
|
-
line.setAttribute("stroke",
|
|
1530
|
-
line.setAttribute("stroke-width", "1");
|
|
1680
|
+
line.setAttribute("stroke", strokeAxis);
|
|
1681
|
+
line.setAttribute("stroke-width", "1.2");
|
|
1531
1682
|
line.setAttribute("stroke-dasharray", "4 4");
|
|
1532
|
-
line.setAttribute("opacity", "0.
|
|
1683
|
+
line.setAttribute("opacity", "0.6");
|
|
1533
1684
|
svg.appendChild(line);
|
|
1534
1685
|
}
|
|
1535
1686
|
const fractions = [0.33, 0.66, 1];
|
|
@@ -1542,12 +1693,19 @@ function mountRadarLayer(layer, nodes, theme, bounds) {
|
|
|
1542
1693
|
const poly = doc.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
1543
1694
|
poly.setAttribute("points", points);
|
|
1544
1695
|
poly.setAttribute("fill", "none");
|
|
1545
|
-
poly.setAttribute("stroke",
|
|
1546
|
-
poly.setAttribute("stroke-width", "1");
|
|
1547
|
-
poly.setAttribute("stroke-dasharray", f === 1 ? "none" : "
|
|
1548
|
-
poly.setAttribute("opacity", String(0.
|
|
1696
|
+
poly.setAttribute("stroke", strokeRing);
|
|
1697
|
+
poly.setAttribute("stroke-width", f === 1 ? "1.5" : "1.2");
|
|
1698
|
+
poly.setAttribute("stroke-dasharray", f === 1 ? "none" : "4 4");
|
|
1699
|
+
poly.setAttribute("opacity", String(0.4 + f * 0.3));
|
|
1549
1700
|
svg.appendChild(poly);
|
|
1550
1701
|
}
|
|
1702
|
+
const centerDot = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1703
|
+
centerDot.setAttribute("cx", String(centerX));
|
|
1704
|
+
centerDot.setAttribute("cy", String(centerY));
|
|
1705
|
+
centerDot.setAttribute("r", "3");
|
|
1706
|
+
centerDot.setAttribute("fill", strokeAxis);
|
|
1707
|
+
centerDot.setAttribute("opacity", "0.8");
|
|
1708
|
+
svg.appendChild(centerDot);
|
|
1551
1709
|
const areaPoints = nodeCenters.map((nc, idx) => {
|
|
1552
1710
|
const f = 0.75 + idx % 3 * 0.12;
|
|
1553
1711
|
const px = centerX + (nc.x - centerX) * f;
|
|
@@ -1561,7 +1719,7 @@ function mountRadarLayer(layer, nodes, theme, bounds) {
|
|
|
1561
1719
|
area.setAttribute("stroke", accentColor);
|
|
1562
1720
|
area.setAttribute("stroke-width", "1.5");
|
|
1563
1721
|
area.setAttribute("stroke-dasharray", "4 4");
|
|
1564
|
-
area.setAttribute("opacity", "0.
|
|
1722
|
+
area.setAttribute("opacity", "0.7");
|
|
1565
1723
|
svg.appendChild(area);
|
|
1566
1724
|
}
|
|
1567
1725
|
|
|
@@ -1585,11 +1743,14 @@ function mountTimelineLayer(layer, nodes, theme, bounds) {
|
|
|
1585
1743
|
overflow: "visible"
|
|
1586
1744
|
});
|
|
1587
1745
|
layer.appendChild(svg);
|
|
1588
|
-
const
|
|
1746
|
+
const SAFE = 40;
|
|
1747
|
+
const TITLE_BAND = 64;
|
|
1748
|
+
const contentH = bounds.height - SAFE - TITLE_BAND - SAFE;
|
|
1749
|
+
const baselineY = TITLE_BAND + contentH / 2;
|
|
1589
1750
|
const minX = Math.min(...nodes.map((n) => n.x)) - 20;
|
|
1590
1751
|
const maxX = Math.max(...nodes.map((n) => n.x + n.width)) + 20;
|
|
1591
|
-
const strokeAxis = theme?.
|
|
1592
|
-
const strokeBorder = theme?.border ?? "#
|
|
1752
|
+
const strokeAxis = theme?.edges?.dependency ?? theme?.rule ?? theme?.border ?? "#64748b";
|
|
1753
|
+
const strokeBorder = theme?.edges?.dependency ?? theme?.border ?? "#64748b";
|
|
1593
1754
|
const accentColor = theme?.accent ?? "#38bdf8";
|
|
1594
1755
|
const surfaceFill = theme?.surfaceRaised ?? theme?.surface ?? "#ffffff";
|
|
1595
1756
|
const axis = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
@@ -1599,7 +1760,8 @@ function mountTimelineLayer(layer, nodes, theme, bounds) {
|
|
|
1599
1760
|
axis.setAttribute("y2", String(baselineY));
|
|
1600
1761
|
axis.setAttribute("stroke", strokeAxis);
|
|
1601
1762
|
axis.setAttribute("stroke-width", "2");
|
|
1602
|
-
axis.setAttribute("
|
|
1763
|
+
axis.setAttribute("stroke-linecap", "round");
|
|
1764
|
+
axis.setAttribute("opacity", "0.75");
|
|
1603
1765
|
svg.appendChild(axis);
|
|
1604
1766
|
for (const node of nodes) {
|
|
1605
1767
|
const nodeCenterX = node.x + node.width / 2;
|
|
@@ -1611,17 +1773,17 @@ function mountTimelineLayer(layer, nodes, theme, bounds) {
|
|
|
1611
1773
|
stem.setAttribute("x2", String(nodeCenterX));
|
|
1612
1774
|
stem.setAttribute("y2", String(targetY));
|
|
1613
1775
|
stem.setAttribute("stroke", node.focal ? accentColor : strokeBorder);
|
|
1614
|
-
stem.setAttribute("stroke-width", node.focal ? "
|
|
1615
|
-
stem.setAttribute("stroke-dasharray", node.focal ? "none" : "
|
|
1616
|
-
stem.setAttribute("opacity", node.focal ? "0.
|
|
1776
|
+
stem.setAttribute("stroke-width", node.focal ? "2" : "1.5");
|
|
1777
|
+
stem.setAttribute("stroke-dasharray", node.focal ? "none" : "4 4");
|
|
1778
|
+
stem.setAttribute("opacity", node.focal ? "0.95" : "0.75");
|
|
1617
1779
|
svg.appendChild(stem);
|
|
1618
1780
|
const pip = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1619
1781
|
pip.setAttribute("cx", String(nodeCenterX));
|
|
1620
1782
|
pip.setAttribute("cy", String(baselineY));
|
|
1621
|
-
pip.setAttribute("r", node.focal ? "5" : "
|
|
1783
|
+
pip.setAttribute("r", node.focal ? "5.5" : "4");
|
|
1622
1784
|
pip.setAttribute("fill", node.focal ? accentColor : surfaceFill);
|
|
1623
1785
|
pip.setAttribute("stroke", node.focal ? accentColor : strokeBorder);
|
|
1624
|
-
pip.setAttribute("stroke-width", "
|
|
1786
|
+
pip.setAttribute("stroke-width", "2");
|
|
1625
1787
|
svg.appendChild(pip);
|
|
1626
1788
|
}
|
|
1627
1789
|
}
|
|
@@ -1676,7 +1838,7 @@ function createGroupBoundaryEl(boundary, theme, doc = document) {
|
|
|
1676
1838
|
el.style.top = `${boundary.y}px`;
|
|
1677
1839
|
el.style.width = `${boundary.width}px`;
|
|
1678
1840
|
el.style.height = `${boundary.height}px`;
|
|
1679
|
-
el.style.setProperty("--md-group-border", theme.
|
|
1841
|
+
el.style.setProperty("--md-group-border", theme.border ?? theme.rule ?? "#cbd5e1");
|
|
1680
1842
|
const displayLabel = boundary.label || boundary.id;
|
|
1681
1843
|
if (displayLabel) {
|
|
1682
1844
|
const label = doc.createElement("div");
|
|
@@ -2439,6 +2601,9 @@ function createText(doc, x, y, text, theme) {
|
|
|
2439
2601
|
label.textContent = text;
|
|
2440
2602
|
return label;
|
|
2441
2603
|
}
|
|
2604
|
+
function resolveLifelineStroke(theme) {
|
|
2605
|
+
return theme.edges?.dependency ?? theme.rule ?? theme.border ?? "#64748b";
|
|
2606
|
+
}
|
|
2442
2607
|
function mountSequenceLayer(layer, nodes, messages, activations, theme, bounds) {
|
|
2443
2608
|
Object.assign(layer.style, {
|
|
2444
2609
|
position: "absolute",
|
|
@@ -2470,19 +2635,28 @@ function mountSequenceLayer(layer, nodes, messages, activations, theme, bounds)
|
|
|
2470
2635
|
const node = nodeById.get(id);
|
|
2471
2636
|
return node ? node.x + node.width / 2 : 0;
|
|
2472
2637
|
};
|
|
2638
|
+
const lifelineStroke = resolveLifelineStroke(theme);
|
|
2473
2639
|
for (const node of nodes) {
|
|
2474
2640
|
const x = centerX(node.id);
|
|
2475
2641
|
const lifeline = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
2476
2642
|
lifeline.classList.add("markdy-sequence-lifeline");
|
|
2477
2643
|
lifeline.setAttribute("x1", String(x));
|
|
2478
2644
|
lifeline.setAttribute("x2", String(x));
|
|
2479
|
-
lifeline.setAttribute("y1", String(node.y + node.height
|
|
2480
|
-
lifeline.setAttribute("y2", String(bounds.height -
|
|
2481
|
-
lifeline.setAttribute("stroke",
|
|
2482
|
-
lifeline.setAttribute("stroke-width", "1.
|
|
2483
|
-
lifeline.setAttribute("stroke-dasharray", "
|
|
2645
|
+
lifeline.setAttribute("y1", String(node.y + node.height));
|
|
2646
|
+
lifeline.setAttribute("y2", String(bounds.height - 36));
|
|
2647
|
+
lifeline.setAttribute("stroke", lifelineStroke);
|
|
2648
|
+
lifeline.setAttribute("stroke-width", "1.5");
|
|
2649
|
+
lifeline.setAttribute("stroke-dasharray", "5 5");
|
|
2484
2650
|
lifeline.setAttribute("opacity", "0.85");
|
|
2485
2651
|
svg.appendChild(lifeline);
|
|
2652
|
+
const cap = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
2653
|
+
cap.classList.add("markdy-sequence-lifeline-cap");
|
|
2654
|
+
cap.setAttribute("cx", String(x));
|
|
2655
|
+
cap.setAttribute("cy", String(bounds.height - 36));
|
|
2656
|
+
cap.setAttribute("r", "2.5");
|
|
2657
|
+
cap.setAttribute("fill", lifelineStroke);
|
|
2658
|
+
cap.setAttribute("opacity", "0.85");
|
|
2659
|
+
svg.appendChild(cap);
|
|
2486
2660
|
}
|
|
2487
2661
|
for (const activation of activations) {
|
|
2488
2662
|
const x = centerX(activation.participant);
|
|
@@ -2589,8 +2763,12 @@ function updateSequenceLayerTheme(layer, theme) {
|
|
|
2589
2763
|
}
|
|
2590
2764
|
});
|
|
2591
2765
|
}
|
|
2766
|
+
const lifelineStroke = resolveLifelineStroke(theme);
|
|
2592
2767
|
Array.from(svg.querySelectorAll(".markdy-sequence-lifeline")).forEach((line) => {
|
|
2593
|
-
line.setAttribute("stroke",
|
|
2768
|
+
line.setAttribute("stroke", lifelineStroke);
|
|
2769
|
+
});
|
|
2770
|
+
Array.from(svg.querySelectorAll(".markdy-sequence-lifeline-cap")).forEach((cap) => {
|
|
2771
|
+
cap.setAttribute("fill", lifelineStroke);
|
|
2594
2772
|
});
|
|
2595
2773
|
Array.from(svg.querySelectorAll(".markdy-sequence-activation")).forEach((bar) => {
|
|
2596
2774
|
bar.setAttribute("fill", theme.accent);
|
|
@@ -2636,7 +2814,7 @@ function mountTreeBuses(layer, buses, theme) {
|
|
|
2636
2814
|
overflow: "visible"
|
|
2637
2815
|
});
|
|
2638
2816
|
layer.appendChild(svg);
|
|
2639
|
-
const stroke = theme.
|
|
2817
|
+
const stroke = theme.edges?.dependency ?? theme.rule ?? theme.border ?? "#64748b";
|
|
2640
2818
|
for (const bus of buses) {
|
|
2641
2819
|
const group = doc.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
2642
2820
|
group.setAttribute("data-tree-bus", bus.id);
|
|
@@ -2816,6 +2994,14 @@ function ensureSceneStyles(doc) {
|
|
|
2816
2994
|
box-sizing: border-box;
|
|
2817
2995
|
width: 100%;
|
|
2818
2996
|
}
|
|
2997
|
+
.markdy-viewport {
|
|
2998
|
+
position: relative;
|
|
2999
|
+
width: 100%;
|
|
3000
|
+
flex: 1 1 auto;
|
|
3001
|
+
min-height: 0;
|
|
3002
|
+
overflow: hidden;
|
|
3003
|
+
box-sizing: border-box;
|
|
3004
|
+
}
|
|
2819
3005
|
.markdy-footer {
|
|
2820
3006
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
2821
3007
|
position: relative;
|
|
@@ -2825,13 +3011,15 @@ function ensureSceneStyles(doc) {
|
|
|
2825
3011
|
align-items: center;
|
|
2826
3012
|
justify-content: space-between;
|
|
2827
3013
|
gap: 8px;
|
|
2828
|
-
padding: 8px 12px
|
|
3014
|
+
padding: 8px 12px 6px;
|
|
2829
3015
|
color: var(--md-text, #f8fafc);
|
|
2830
3016
|
background: transparent;
|
|
2831
3017
|
backdrop-filter: none;
|
|
2832
3018
|
-webkit-backdrop-filter: none;
|
|
2833
3019
|
border-top: none;
|
|
2834
3020
|
z-index: 100;
|
|
3021
|
+
pointer-events: auto;
|
|
3022
|
+
flex-shrink: 0;
|
|
2835
3023
|
}
|
|
2836
3024
|
.markdy-controls {
|
|
2837
3025
|
display: flex;
|
|
@@ -2843,9 +3031,15 @@ function ensureSceneStyles(doc) {
|
|
|
2843
3031
|
gap: 8px;
|
|
2844
3032
|
max-width: 100%;
|
|
2845
3033
|
overflow-x: auto;
|
|
3034
|
+
overflow-y: visible;
|
|
2846
3035
|
scrollbar-width: none;
|
|
2847
|
-
|
|
2848
|
-
|
|
3036
|
+
-ms-overflow-style: none;
|
|
3037
|
+
-webkit-overflow-scrolling: touch;
|
|
3038
|
+
padding: 4px 4px 6px;
|
|
3039
|
+
margin: 0;
|
|
3040
|
+
position: relative;
|
|
3041
|
+
z-index: 10;
|
|
3042
|
+
pointer-events: auto;
|
|
2849
3043
|
}
|
|
2850
3044
|
.markdy-controls::-webkit-scrollbar {
|
|
2851
3045
|
display: none;
|
|
@@ -2855,6 +3049,9 @@ function ensureSceneStyles(doc) {
|
|
|
2855
3049
|
align-items: center;
|
|
2856
3050
|
gap: 4px;
|
|
2857
3051
|
flex-shrink: 0;
|
|
3052
|
+
position: relative;
|
|
3053
|
+
z-index: 5;
|
|
3054
|
+
pointer-events: auto;
|
|
2858
3055
|
}
|
|
2859
3056
|
.markdy-controls-playback {
|
|
2860
3057
|
display: inline-flex;
|
|
@@ -2875,6 +3072,9 @@ function ensureSceneStyles(doc) {
|
|
|
2875
3072
|
align-items: center;
|
|
2876
3073
|
gap: 4px;
|
|
2877
3074
|
flex-shrink: 0;
|
|
3075
|
+
position: relative;
|
|
3076
|
+
z-index: 5;
|
|
3077
|
+
pointer-events: auto;
|
|
2878
3078
|
}
|
|
2879
3079
|
.markdy-control-divider {
|
|
2880
3080
|
width: 1px;
|
|
@@ -2905,19 +3105,27 @@ function ensureSceneStyles(doc) {
|
|
|
2905
3105
|
white-space: nowrap;
|
|
2906
3106
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
2907
3107
|
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3108
|
+
position: relative;
|
|
3109
|
+
pointer-events: auto;
|
|
3110
|
+
isolation: isolate;
|
|
2908
3111
|
}
|
|
2909
3112
|
.markdy-controls button:hover:not([aria-pressed="true"]) {
|
|
2910
3113
|
position: relative;
|
|
2911
|
-
z-index:
|
|
3114
|
+
z-index: 20;
|
|
2912
3115
|
background: var(--md-control-hover-bg, rgba(255, 255, 255, 0.1));
|
|
2913
3116
|
color: var(--md-control-hover-text, #f8fafc);
|
|
2914
3117
|
border-color: var(--md-control-hover-border, rgba(148, 163, 184, 0.35));
|
|
2915
3118
|
transform: translateY(-1px);
|
|
2916
|
-
box-shadow: 0
|
|
3119
|
+
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.14);
|
|
3120
|
+
}
|
|
3121
|
+
.markdy-controls-tools button:hover:not([aria-pressed="true"]) {
|
|
3122
|
+
z-index: 25;
|
|
3123
|
+
border-color: var(--md-control-hover-border, rgba(148, 163, 184, 0.45));
|
|
3124
|
+
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.16);
|
|
2917
3125
|
}
|
|
2918
3126
|
.markdy-controls button[aria-pressed="true"] {
|
|
2919
3127
|
position: relative;
|
|
2920
|
-
z-index:
|
|
3128
|
+
z-index: 20;
|
|
2921
3129
|
background: var(--accent, #10b981) !important;
|
|
2922
3130
|
color: #ffffff !important;
|
|
2923
3131
|
border-color: var(--accent, #10b981) !important;
|
|
@@ -2925,12 +3133,12 @@ function ensureSceneStyles(doc) {
|
|
|
2925
3133
|
}
|
|
2926
3134
|
.markdy-controls button:focus-visible {
|
|
2927
3135
|
position: relative;
|
|
2928
|
-
z-index:
|
|
3136
|
+
z-index: 30;
|
|
2929
3137
|
outline: 2px solid var(--accent, #10b981);
|
|
2930
|
-
outline-offset:
|
|
3138
|
+
outline-offset: 2px;
|
|
2931
3139
|
}
|
|
2932
3140
|
.markdy-controls button:active {
|
|
2933
|
-
transform: scale(0.
|
|
3141
|
+
transform: translateY(0) scale(0.96);
|
|
2934
3142
|
}
|
|
2935
3143
|
.markdy-icon {
|
|
2936
3144
|
display: inline-block;
|
|
@@ -3264,6 +3472,7 @@ function ensureSceneStyles(doc) {
|
|
|
3264
3472
|
background: var(--md-control-hover-bg, rgba(51, 65, 85, 0.95));
|
|
3265
3473
|
color: var(--md-control-hover-text, #f8fafc);
|
|
3266
3474
|
border-color: var(--md-control-hover-border, #94a3b8);
|
|
3475
|
+
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.25);
|
|
3267
3476
|
}
|
|
3268
3477
|
[data-markdy-theme="midnight"] .markdy-controls button[aria-pressed="true"],
|
|
3269
3478
|
[data-markdy-theme="blueprint"] .markdy-controls button[aria-pressed="true"],
|
|
@@ -3336,6 +3545,24 @@ function ensureSceneStyles(doc) {
|
|
|
3336
3545
|
margin: 0 auto !important;
|
|
3337
3546
|
padding: 8px 12px 4px !important;
|
|
3338
3547
|
}
|
|
3548
|
+
@media (hover: none) and (pointer: coarse) {
|
|
3549
|
+
.markdy-controls {
|
|
3550
|
+
gap: 6px;
|
|
3551
|
+
padding: 6px 4px 8px;
|
|
3552
|
+
}
|
|
3553
|
+
.markdy-controls button {
|
|
3554
|
+
height: 32px;
|
|
3555
|
+
min-width: 32px;
|
|
3556
|
+
padding: 0 8px;
|
|
3557
|
+
font-size: 12px;
|
|
3558
|
+
}
|
|
3559
|
+
.markdy-speed-group .markdy-control-rate {
|
|
3560
|
+
height: 26px;
|
|
3561
|
+
min-width: 28px;
|
|
3562
|
+
font-size: 11px;
|
|
3563
|
+
padding: 0 6px;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3339
3566
|
@media (prefers-reduced-motion: reduce) {
|
|
3340
3567
|
.markdy-node,
|
|
3341
3568
|
.markdy-beat-caption,
|
|
@@ -3411,6 +3638,100 @@ function applyThemeToScene(scene, theme) {
|
|
|
3411
3638
|
scene.style.color = theme.text;
|
|
3412
3639
|
}
|
|
3413
3640
|
|
|
3641
|
+
// src/export/svg-exporter.ts
|
|
3642
|
+
function copyRenderedStyles(source, clone) {
|
|
3643
|
+
if (typeof window === "undefined" || typeof window.getComputedStyle !== "function") return;
|
|
3644
|
+
const sourceElements = [source, ...Array.from(source.querySelectorAll("*"))];
|
|
3645
|
+
const cloneElements = [clone, ...Array.from(clone.querySelectorAll("*"))];
|
|
3646
|
+
for (let index = 0; index < Math.min(sourceElements.length, cloneElements.length); index++) {
|
|
3647
|
+
const computed = window.getComputedStyle(sourceElements[index]);
|
|
3648
|
+
const target = cloneElements[index].style;
|
|
3649
|
+
for (let propertyIndex = 0; propertyIndex < computed.length; propertyIndex++) {
|
|
3650
|
+
const property = computed.item(propertyIndex);
|
|
3651
|
+
target.setProperty(property, computed.getPropertyValue(property), computed.getPropertyPriority(property));
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
function normalizeExportViewport(scene) {
|
|
3656
|
+
scene.querySelectorAll(".markdy-viewport-transform").forEach((viewportTransform) => {
|
|
3657
|
+
viewportTransform.style.transform = "translate(0px, 0px) scale(1)";
|
|
3658
|
+
viewportTransform.style.transformOrigin = "0 0";
|
|
3659
|
+
viewportTransform.style.willChange = "auto";
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
function getDiagramSceneElement(containerEl) {
|
|
3663
|
+
const sceneEl = containerEl.classList?.contains("markdy-scene-root") ? containerEl : containerEl.querySelector(".markdy-scene-root") || containerEl.querySelector("svg") || (containerEl.tagName?.toLowerCase() === "svg" ? containerEl : null);
|
|
3664
|
+
if (!sceneEl) throw new Error("No Markdy scene element found in container");
|
|
3665
|
+
return sceneEl;
|
|
3666
|
+
}
|
|
3667
|
+
function prepareHtmlSceneForExport(sceneEl, options = {}) {
|
|
3668
|
+
const clonedScene = sceneEl.cloneNode(true);
|
|
3669
|
+
copyRenderedStyles(sceneEl, clonedScene);
|
|
3670
|
+
normalizeExportViewport(clonedScene);
|
|
3671
|
+
const widthStr = clonedScene.style.width || String(sceneEl.clientWidth || 800);
|
|
3672
|
+
const heightStr = clonedScene.style.height || String(sceneEl.clientHeight || 400);
|
|
3673
|
+
let width = parseFloat(widthStr);
|
|
3674
|
+
let height = parseFloat(heightStr);
|
|
3675
|
+
if (isNaN(width)) width = 800;
|
|
3676
|
+
if (isNaN(height)) height = 400;
|
|
3677
|
+
const scale = options.scale || 1;
|
|
3678
|
+
const scaledWidth = width * scale;
|
|
3679
|
+
const scaledHeight = height * scale;
|
|
3680
|
+
clonedScene.style.transform = `scale(${scale})`;
|
|
3681
|
+
clonedScene.style.transformOrigin = "0 0";
|
|
3682
|
+
clonedScene.style.position = "relative";
|
|
3683
|
+
clonedScene.style.left = "0px";
|
|
3684
|
+
clonedScene.style.top = "0px";
|
|
3685
|
+
clonedScene.style.margin = "0";
|
|
3686
|
+
clonedScene.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
3687
|
+
if (options.transparentBackground) {
|
|
3688
|
+
clonedScene.style.background = "transparent";
|
|
3689
|
+
}
|
|
3690
|
+
return { sceneEl, clonedScene, width, height, scaledWidth, scaledHeight };
|
|
3691
|
+
}
|
|
3692
|
+
function exportDiagramAsVectorSvg(containerEl, options = {}) {
|
|
3693
|
+
const sceneEl = getDiagramSceneElement(containerEl);
|
|
3694
|
+
if (sceneEl.tagName?.toLowerCase() === "svg") {
|
|
3695
|
+
const clonedSvg = sceneEl.cloneNode(true);
|
|
3696
|
+
if (!clonedSvg.getAttribute("xmlns")) {
|
|
3697
|
+
clonedSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
3698
|
+
}
|
|
3699
|
+
const serializer2 = new XMLSerializer();
|
|
3700
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
3701
|
+
${serializer2.serializeToString(clonedSvg)}`;
|
|
3702
|
+
}
|
|
3703
|
+
const { clonedScene, scaledWidth, scaledHeight } = prepareHtmlSceneForExport(sceneEl, options);
|
|
3704
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
3705
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
3706
|
+
svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
3707
|
+
svg.setAttribute("width", String(scaledWidth));
|
|
3708
|
+
svg.setAttribute("height", String(scaledHeight));
|
|
3709
|
+
svg.setAttribute("viewBox", `0 0 ${scaledWidth} ${scaledHeight}`);
|
|
3710
|
+
if (options.includeThemeStyles !== false) {
|
|
3711
|
+
const styleEl = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
3712
|
+
let combinedStyles = `
|
|
3713
|
+
foreignObject { width: 100%; height: 100%; }
|
|
3714
|
+
.markdy-node { transition: opacity 0.3s ease; }
|
|
3715
|
+
`;
|
|
3716
|
+
if (typeof document !== "undefined") {
|
|
3717
|
+
const styles = document.querySelectorAll("style[id^='markdy-']");
|
|
3718
|
+
for (let i = 0; i < styles.length; i++) {
|
|
3719
|
+
combinedStyles += styles[i].textContent + "\n";
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
styleEl.textContent = combinedStyles;
|
|
3723
|
+
svg.appendChild(styleEl);
|
|
3724
|
+
}
|
|
3725
|
+
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
|
3726
|
+
foreignObject.setAttribute("width", "100%");
|
|
3727
|
+
foreignObject.setAttribute("height", "100%");
|
|
3728
|
+
foreignObject.appendChild(clonedScene);
|
|
3729
|
+
svg.appendChild(foreignObject);
|
|
3730
|
+
const serializer = new XMLSerializer();
|
|
3731
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
3732
|
+
` + serializer.serializeToString(svg);
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3414
3735
|
// src/diagram.ts
|
|
3415
3736
|
var NORMAL_PLAYBACK_RATE = 4 / 5;
|
|
3416
3737
|
var MIN_VIEWPORT_ZOOM = 0.5;
|
|
@@ -3531,6 +3852,7 @@ function createDiagram(opts) {
|
|
|
3531
3852
|
const totalDurationMs = plan.duration * 1e3;
|
|
3532
3853
|
const durationSeconds = plan.duration;
|
|
3533
3854
|
container.classList.add("markdy-diagram-root");
|
|
3855
|
+
applyThemeVariables(container, plan.theme);
|
|
3534
3856
|
Object.assign(container.style, {
|
|
3535
3857
|
position: "relative",
|
|
3536
3858
|
display: "flex",
|
|
@@ -3545,12 +3867,13 @@ function createDiagram(opts) {
|
|
|
3545
3867
|
Object.assign(viewport.style, {
|
|
3546
3868
|
position: "relative",
|
|
3547
3869
|
width: "100%",
|
|
3548
|
-
height: "100%",
|
|
3549
3870
|
maxWidth: "100%",
|
|
3550
3871
|
maxHeight: "100%",
|
|
3551
3872
|
aspectRatio: `${plan.meta.width} / ${plan.meta.height}`,
|
|
3552
3873
|
overflow: "hidden",
|
|
3553
|
-
boxSizing: "border-box"
|
|
3874
|
+
boxSizing: "border-box",
|
|
3875
|
+
flex: "1 1 auto",
|
|
3876
|
+
minHeight: "0"
|
|
3554
3877
|
});
|
|
3555
3878
|
container.appendChild(viewport);
|
|
3556
3879
|
let progressEl = null;
|
|
@@ -3601,9 +3924,26 @@ function createDiagram(opts) {
|
|
|
3601
3924
|
gap: "6px",
|
|
3602
3925
|
width: "100%",
|
|
3603
3926
|
boxSizing: "border-box",
|
|
3604
|
-
padding: "6px 8px
|
|
3605
|
-
background: "transparent"
|
|
3927
|
+
padding: "6px 8px 4px",
|
|
3928
|
+
background: "transparent",
|
|
3929
|
+
pointerEvents: "auto",
|
|
3930
|
+
flexShrink: "0"
|
|
3606
3931
|
});
|
|
3932
|
+
for (const eventName of [
|
|
3933
|
+
"click",
|
|
3934
|
+
"dblclick",
|
|
3935
|
+
"pointerdown",
|
|
3936
|
+
"pointermove",
|
|
3937
|
+
"pointerup",
|
|
3938
|
+
"pointercancel",
|
|
3939
|
+
"wheel",
|
|
3940
|
+
"mousedown",
|
|
3941
|
+
"mouseup",
|
|
3942
|
+
"touchstart",
|
|
3943
|
+
"touchend"
|
|
3944
|
+
]) {
|
|
3945
|
+
footer.addEventListener(eventName, (event) => event.stopPropagation());
|
|
3946
|
+
}
|
|
3607
3947
|
container.appendChild(footer);
|
|
3608
3948
|
return footer;
|
|
3609
3949
|
}
|
|
@@ -3693,6 +4033,27 @@ function createDiagram(opts) {
|
|
|
3693
4033
|
plan.theme,
|
|
3694
4034
|
{ width: plan.meta.width, height: plan.meta.height }
|
|
3695
4035
|
);
|
|
4036
|
+
} else if (plan.diagramType === "quadrant") {
|
|
4037
|
+
mountQuadrantLayer(
|
|
4038
|
+
constellationLayer,
|
|
4039
|
+
plan.nodes,
|
|
4040
|
+
plan.theme,
|
|
4041
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4042
|
+
);
|
|
4043
|
+
} else if (plan.diagramType === "swimlane") {
|
|
4044
|
+
mountSwimlaneLayer(
|
|
4045
|
+
constellationLayer,
|
|
4046
|
+
plan.nodes,
|
|
4047
|
+
plan.theme,
|
|
4048
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4049
|
+
);
|
|
4050
|
+
} else if (plan.diagramType === "gantt") {
|
|
4051
|
+
mountGanttLayer(
|
|
4052
|
+
constellationLayer,
|
|
4053
|
+
plan.nodes,
|
|
4054
|
+
plan.theme,
|
|
4055
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4056
|
+
);
|
|
3696
4057
|
}
|
|
3697
4058
|
const groupLayer = document.createElement("div");
|
|
3698
4059
|
groupLayer.className = "markdy-group-layer";
|
|
@@ -4024,6 +4385,7 @@ function createDiagram(opts) {
|
|
|
4024
4385
|
sceneMs = totalDurationMs > 0 ? Math.min(Math.max(seconds * 1e3, 0), totalDurationMs) : Math.max(seconds * 1e3, 0);
|
|
4025
4386
|
applyCurrentTime();
|
|
4026
4387
|
if (totalDurationMs > 0) updateProgressBar(sceneMs / totalDurationMs);
|
|
4388
|
+
syncControls();
|
|
4027
4389
|
},
|
|
4028
4390
|
setPlaybackRate(rate) {
|
|
4029
4391
|
if (!Number.isFinite(rate) || rate <= 0) return;
|
|
@@ -4064,6 +4426,7 @@ function createDiagram(opts) {
|
|
|
4064
4426
|
setTheme(theme) {
|
|
4065
4427
|
const newTheme = typeof theme === "string" ? resolveTheme(theme) : theme;
|
|
4066
4428
|
plan.theme = newTheme;
|
|
4429
|
+
applyThemeVariables(container, newTheme);
|
|
4067
4430
|
applyThemeToScene(scene, newTheme);
|
|
4068
4431
|
if (footer) {
|
|
4069
4432
|
applyThemeVariables(footer, newTheme);
|
|
@@ -4098,7 +4461,7 @@ function createDiagram(opts) {
|
|
|
4098
4461
|
treeLayer.replaceChildren();
|
|
4099
4462
|
mountTreeBuses(treeLayer, plan.treeBuses, newTheme);
|
|
4100
4463
|
}
|
|
4101
|
-
if (plan.diagramType === "constellation" || plan.diagramType === "radar" || plan.diagramType === "timeline") {
|
|
4464
|
+
if (plan.diagramType === "constellation" || plan.diagramType === "radar" || plan.diagramType === "timeline" || plan.diagramType === "quadrant" || plan.diagramType === "swimlane" || plan.diagramType === "gantt") {
|
|
4102
4465
|
constellationLayer.replaceChildren();
|
|
4103
4466
|
if (plan.diagramType === "constellation") {
|
|
4104
4467
|
mountConstellationLayer(
|
|
@@ -4121,6 +4484,27 @@ function createDiagram(opts) {
|
|
|
4121
4484
|
newTheme,
|
|
4122
4485
|
{ width: plan.meta.width, height: plan.meta.height }
|
|
4123
4486
|
);
|
|
4487
|
+
} else if (plan.diagramType === "quadrant") {
|
|
4488
|
+
mountQuadrantLayer(
|
|
4489
|
+
constellationLayer,
|
|
4490
|
+
plan.nodes,
|
|
4491
|
+
newTheme,
|
|
4492
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4493
|
+
);
|
|
4494
|
+
} else if (plan.diagramType === "swimlane") {
|
|
4495
|
+
mountSwimlaneLayer(
|
|
4496
|
+
constellationLayer,
|
|
4497
|
+
plan.nodes,
|
|
4498
|
+
newTheme,
|
|
4499
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4500
|
+
);
|
|
4501
|
+
} else if (plan.diagramType === "gantt") {
|
|
4502
|
+
mountGanttLayer(
|
|
4503
|
+
constellationLayer,
|
|
4504
|
+
plan.nodes,
|
|
4505
|
+
newTheme,
|
|
4506
|
+
{ width: plan.meta.width, height: plan.meta.height }
|
|
4507
|
+
);
|
|
4124
4508
|
}
|
|
4125
4509
|
}
|
|
4126
4510
|
if (plan.diagramType === "sequence") {
|
|
@@ -4233,12 +4617,14 @@ function createDiagram(opts) {
|
|
|
4233
4617
|
});
|
|
4234
4618
|
}
|
|
4235
4619
|
function flashControlLabel(button, message) {
|
|
4236
|
-
const originalHtml = button.innerHTML;
|
|
4620
|
+
const originalHtml = button.dataset.originalHtml ?? button.innerHTML;
|
|
4621
|
+
button.dataset.originalHtml = originalHtml;
|
|
4237
4622
|
const isSuccess = !message.toLowerCase().includes("fail");
|
|
4238
4623
|
button.innerHTML = isSuccess ? `${ICONS2.check}${message}` : message;
|
|
4239
4624
|
button.classList.add("markdy-btn-flashed");
|
|
4240
4625
|
setTimeout(() => {
|
|
4241
|
-
button.innerHTML = originalHtml;
|
|
4626
|
+
button.innerHTML = button.dataset.originalHtml ?? originalHtml;
|
|
4627
|
+
delete button.dataset.originalHtml;
|
|
4242
4628
|
button.classList.remove("markdy-btn-flashed");
|
|
4243
4629
|
}, 1500);
|
|
4244
4630
|
}
|
|
@@ -4258,14 +4644,13 @@ function createDiagram(opts) {
|
|
|
4258
4644
|
if (!svgButton) return;
|
|
4259
4645
|
const button = makeControlButton("SVG", "Export diagram as SVG", ICONS2.svg);
|
|
4260
4646
|
button.className = "markdy-control-svg";
|
|
4261
|
-
button.addEventListener("click",
|
|
4647
|
+
button.addEventListener("click", () => {
|
|
4262
4648
|
const resumeAt = sceneMs;
|
|
4263
4649
|
const wasPlaying = isPlaying;
|
|
4264
4650
|
try {
|
|
4265
4651
|
diagram.pause();
|
|
4266
4652
|
diagram.seek(durationSeconds);
|
|
4267
|
-
const
|
|
4268
|
-
const svg = exportDiagramAsVectorSvg2(container);
|
|
4653
|
+
const svg = exportDiagramAsVectorSvg(container);
|
|
4269
4654
|
const name = (plan.title || "markdy-diagram").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4270
4655
|
downloadFile(`${name || "markdy-diagram"}.svg`, svg, "image/svg+xml");
|
|
4271
4656
|
flashControlLabel(button, "Saved");
|
|
@@ -4355,8 +4740,11 @@ function createDiagram(opts) {
|
|
|
4355
4740
|
const button = makeControlButton("Code", "View MarkdyScript source");
|
|
4356
4741
|
button.className = "markdy-control-code";
|
|
4357
4742
|
button.setAttribute("aria-haspopup", "dialog");
|
|
4743
|
+
button.setAttribute("aria-expanded", "false");
|
|
4358
4744
|
button.addEventListener("click", () => {
|
|
4359
4745
|
closeCodePanel?.();
|
|
4746
|
+
const previousActiveElement = document.activeElement;
|
|
4747
|
+
button.setAttribute("aria-expanded", "true");
|
|
4360
4748
|
const overlay = document.createElement("div");
|
|
4361
4749
|
overlay.className = "markdy-code-panel-overlay";
|
|
4362
4750
|
overlay.setAttribute("role", "dialog");
|
|
@@ -4414,19 +4802,36 @@ function createDiagram(opts) {
|
|
|
4414
4802
|
overlay.appendChild(panel);
|
|
4415
4803
|
const mountHost = document.fullscreenElement ?? document.body;
|
|
4416
4804
|
mountHost.appendChild(overlay);
|
|
4805
|
+
const prevOverflow = typeof document !== "undefined" ? document.body.style.overflow : "";
|
|
4806
|
+
if (typeof document !== "undefined" && !document.fullscreenElement) {
|
|
4807
|
+
document.body.style.overflow = "hidden";
|
|
4808
|
+
}
|
|
4417
4809
|
closeBtn.focus();
|
|
4418
4810
|
let closing = false;
|
|
4419
4811
|
function removePanel() {
|
|
4420
4812
|
document.removeEventListener("keydown", handleEscape);
|
|
4813
|
+
if (typeof document !== "undefined" && !document.fullscreenElement) {
|
|
4814
|
+
document.body.style.overflow = prevOverflow;
|
|
4815
|
+
}
|
|
4816
|
+
button.setAttribute("aria-expanded", "false");
|
|
4421
4817
|
overlay.remove();
|
|
4818
|
+
previousActiveElement?.focus?.();
|
|
4422
4819
|
if (closeCodePanel === removePanel) closeCodePanel = null;
|
|
4423
4820
|
}
|
|
4424
4821
|
function closePanel() {
|
|
4425
4822
|
if (closing) return;
|
|
4426
4823
|
closing = true;
|
|
4824
|
+
button.setAttribute("aria-expanded", "false");
|
|
4427
4825
|
overlay.dataset.closing = "1";
|
|
4428
4826
|
document.removeEventListener("keydown", handleEscape);
|
|
4429
|
-
|
|
4827
|
+
let cleaned = false;
|
|
4828
|
+
const doClean = () => {
|
|
4829
|
+
if (cleaned) return;
|
|
4830
|
+
cleaned = true;
|
|
4831
|
+
removePanel();
|
|
4832
|
+
};
|
|
4833
|
+
overlay.addEventListener("animationend", doClean, { once: true });
|
|
4834
|
+
setTimeout(doClean, 200);
|
|
4430
4835
|
}
|
|
4431
4836
|
closeBtn.addEventListener("click", closePanel);
|
|
4432
4837
|
overlay.addEventListener("click", (e) => {
|
|
@@ -4584,21 +4989,33 @@ function createDiagram(opts) {
|
|
|
4584
4989
|
toolbar.setAttribute("role", "toolbar");
|
|
4585
4990
|
toolbar.setAttribute("aria-label", "Diagram controls");
|
|
4586
4991
|
Object.assign(toolbar.style, {
|
|
4587
|
-
position: "
|
|
4992
|
+
position: "relative",
|
|
4588
4993
|
display: "flex",
|
|
4589
4994
|
alignItems: "center",
|
|
4590
4995
|
justifyContent: "flex-start",
|
|
4591
4996
|
maxWidth: "100%",
|
|
4592
|
-
padding: "0",
|
|
4593
4997
|
border: "0",
|
|
4594
4998
|
borderRadius: "0",
|
|
4595
4999
|
background: "transparent",
|
|
4596
5000
|
boxShadow: "none",
|
|
4597
5001
|
backdropFilter: "none",
|
|
4598
5002
|
transform: "none",
|
|
4599
|
-
pointerEvents: "auto"
|
|
5003
|
+
pointerEvents: "auto",
|
|
5004
|
+
zIndex: "10"
|
|
4600
5005
|
});
|
|
4601
|
-
for (const eventName of [
|
|
5006
|
+
for (const eventName of [
|
|
5007
|
+
"click",
|
|
5008
|
+
"dblclick",
|
|
5009
|
+
"pointerdown",
|
|
5010
|
+
"pointermove",
|
|
5011
|
+
"pointerup",
|
|
5012
|
+
"pointercancel",
|
|
5013
|
+
"wheel",
|
|
5014
|
+
"mousedown",
|
|
5015
|
+
"mouseup",
|
|
5016
|
+
"touchstart",
|
|
5017
|
+
"touchend"
|
|
5018
|
+
]) {
|
|
4602
5019
|
toolbar.addEventListener(eventName, (event) => event.stopPropagation());
|
|
4603
5020
|
}
|
|
4604
5021
|
const playbackGroup = document.createElement("div");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
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",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"html2canvas": "^1.4.1",
|
|
49
|
-
"@markdy/core": "1.0.
|
|
49
|
+
"@markdy/core": "1.0.30"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"jsdom": "^29.1.1",
|
|
53
53
|
"tsup": "^8.5.1",
|
|
54
54
|
"typescript": "^5.9.3",
|
|
55
55
|
"vitest": "^4.1.7",
|
|
56
|
-
"@markdy/stdlib-systems": "1.0.
|
|
56
|
+
"@markdy/stdlib-systems": "1.0.30"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup",
|
package/dist/chunk-VIZHA7GI.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// src/export/svg-exporter.ts
|
|
2
|
-
function copyRenderedStyles(source, clone) {
|
|
3
|
-
if (typeof window === "undefined" || typeof window.getComputedStyle !== "function") return;
|
|
4
|
-
const sourceElements = [source, ...Array.from(source.querySelectorAll("*"))];
|
|
5
|
-
const cloneElements = [clone, ...Array.from(clone.querySelectorAll("*"))];
|
|
6
|
-
for (let index = 0; index < Math.min(sourceElements.length, cloneElements.length); index++) {
|
|
7
|
-
const computed = window.getComputedStyle(sourceElements[index]);
|
|
8
|
-
const target = cloneElements[index].style;
|
|
9
|
-
for (let propertyIndex = 0; propertyIndex < computed.length; propertyIndex++) {
|
|
10
|
-
const property = computed.item(propertyIndex);
|
|
11
|
-
target.setProperty(property, computed.getPropertyValue(property), computed.getPropertyPriority(property));
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function normalizeExportViewport(scene) {
|
|
16
|
-
scene.querySelectorAll(".markdy-viewport-transform").forEach((viewportTransform) => {
|
|
17
|
-
viewportTransform.style.transform = "translate(0px, 0px) scale(1)";
|
|
18
|
-
viewportTransform.style.transformOrigin = "0 0";
|
|
19
|
-
viewportTransform.style.willChange = "auto";
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function getDiagramSceneElement(containerEl) {
|
|
23
|
-
const sceneEl = containerEl.classList?.contains("markdy-scene-root") ? containerEl : containerEl.querySelector(".markdy-scene-root") || containerEl.querySelector("svg") || (containerEl.tagName?.toLowerCase() === "svg" ? containerEl : null);
|
|
24
|
-
if (!sceneEl) throw new Error("No Markdy scene element found in container");
|
|
25
|
-
return sceneEl;
|
|
26
|
-
}
|
|
27
|
-
function prepareHtmlSceneForExport(sceneEl, options = {}) {
|
|
28
|
-
const clonedScene = sceneEl.cloneNode(true);
|
|
29
|
-
copyRenderedStyles(sceneEl, clonedScene);
|
|
30
|
-
normalizeExportViewport(clonedScene);
|
|
31
|
-
const widthStr = clonedScene.style.width || String(sceneEl.clientWidth || 800);
|
|
32
|
-
const heightStr = clonedScene.style.height || String(sceneEl.clientHeight || 400);
|
|
33
|
-
let width = parseFloat(widthStr);
|
|
34
|
-
let height = parseFloat(heightStr);
|
|
35
|
-
if (isNaN(width)) width = 800;
|
|
36
|
-
if (isNaN(height)) height = 400;
|
|
37
|
-
const scale = options.scale || 1;
|
|
38
|
-
const scaledWidth = width * scale;
|
|
39
|
-
const scaledHeight = height * scale;
|
|
40
|
-
clonedScene.style.transform = `scale(${scale})`;
|
|
41
|
-
clonedScene.style.transformOrigin = "0 0";
|
|
42
|
-
clonedScene.style.position = "relative";
|
|
43
|
-
clonedScene.style.left = "0px";
|
|
44
|
-
clonedScene.style.top = "0px";
|
|
45
|
-
clonedScene.style.margin = "0";
|
|
46
|
-
clonedScene.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
47
|
-
if (options.transparentBackground) {
|
|
48
|
-
clonedScene.style.background = "transparent";
|
|
49
|
-
}
|
|
50
|
-
return { sceneEl, clonedScene, width, height, scaledWidth, scaledHeight };
|
|
51
|
-
}
|
|
52
|
-
function exportDiagramAsVectorSvg(containerEl, options = {}) {
|
|
53
|
-
const sceneEl = getDiagramSceneElement(containerEl);
|
|
54
|
-
if (sceneEl.tagName?.toLowerCase() === "svg") {
|
|
55
|
-
const clonedSvg = sceneEl.cloneNode(true);
|
|
56
|
-
if (!clonedSvg.getAttribute("xmlns")) {
|
|
57
|
-
clonedSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
58
|
-
}
|
|
59
|
-
const serializer2 = new XMLSerializer();
|
|
60
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
61
|
-
${serializer2.serializeToString(clonedSvg)}`;
|
|
62
|
-
}
|
|
63
|
-
const { clonedScene, scaledWidth, scaledHeight } = prepareHtmlSceneForExport(sceneEl, options);
|
|
64
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
65
|
-
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
66
|
-
svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
67
|
-
svg.setAttribute("width", String(scaledWidth));
|
|
68
|
-
svg.setAttribute("height", String(scaledHeight));
|
|
69
|
-
svg.setAttribute("viewBox", `0 0 ${scaledWidth} ${scaledHeight}`);
|
|
70
|
-
if (options.includeThemeStyles !== false) {
|
|
71
|
-
const styleEl = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
72
|
-
let combinedStyles = `
|
|
73
|
-
foreignObject { width: 100%; height: 100%; }
|
|
74
|
-
.markdy-node { transition: opacity 0.3s ease; }
|
|
75
|
-
`;
|
|
76
|
-
if (typeof document !== "undefined") {
|
|
77
|
-
const styles = document.querySelectorAll("style[id^='markdy-']");
|
|
78
|
-
for (let i = 0; i < styles.length; i++) {
|
|
79
|
-
combinedStyles += styles[i].textContent + "\n";
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
styleEl.textContent = combinedStyles;
|
|
83
|
-
svg.appendChild(styleEl);
|
|
84
|
-
}
|
|
85
|
-
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
|
86
|
-
foreignObject.setAttribute("width", "100%");
|
|
87
|
-
foreignObject.setAttribute("height", "100%");
|
|
88
|
-
foreignObject.appendChild(clonedScene);
|
|
89
|
-
svg.appendChild(foreignObject);
|
|
90
|
-
const serializer = new XMLSerializer();
|
|
91
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
92
|
-
` + serializer.serializeToString(svg);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export {
|
|
96
|
-
getDiagramSceneElement,
|
|
97
|
-
prepareHtmlSceneForExport,
|
|
98
|
-
exportDiagramAsVectorSvg
|
|
99
|
-
};
|