@markdy/renderer-dom 0.8.25 → 0.8.27
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 +17 -2
- package/dist/index.js +224 -57
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,17 @@ interface SvgExportOptions {
|
|
|
81
81
|
}
|
|
82
82
|
declare function exportDiagramAsVectorSvg(containerEl: HTMLElement, options?: SvgExportOptions): string;
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* packages/renderer-dom/src/export/png-exporter.ts
|
|
86
|
+
* High-DPI raster PNG export with 2x retina scaling.
|
|
87
|
+
* Zero external dependencies.
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
interface PngExportOptions extends SvgExportOptions {
|
|
91
|
+
pixelRatio?: number;
|
|
92
|
+
}
|
|
93
|
+
declare function exportDiagramAsPng(containerEl: HTMLElement, options?: PngExportOptions): Promise<Blob>;
|
|
94
|
+
|
|
84
95
|
/**
|
|
85
96
|
* packages/renderer-dom/src/presentation-controller.ts
|
|
86
97
|
* Interactive Beat Navigation & Keyboard-driven presentation controller.
|
|
@@ -95,13 +106,17 @@ declare class DiagramPresentationController {
|
|
|
95
106
|
private diagram;
|
|
96
107
|
private plan;
|
|
97
108
|
private currentBeatIndex;
|
|
109
|
+
private options;
|
|
110
|
+
private isDestroyed;
|
|
98
111
|
constructor(diagram: Diagram, plan: RenderPlan, options?: ControllerOptions);
|
|
99
112
|
nextBeat(): void;
|
|
100
113
|
prevBeat(): void;
|
|
114
|
+
private applyCurrentBeat;
|
|
101
115
|
getCurrentBeatIndex(): number;
|
|
102
116
|
togglePlay(): void;
|
|
103
117
|
setSpeed(rate: number): void;
|
|
104
|
-
private
|
|
118
|
+
private onKeyDown;
|
|
119
|
+
destroy(): void;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
|
-
export { type AnimationRecordFrame, type ControllerOptions, type Diagram, type DiagramOptions, DiagramPresentationController, type GifExportOptions, ICON_REGISTRY, type IconSpec, type SvgExportOptions, createDiagram, encodeGifSequence, exportDiagramAsVectorSvg };
|
|
122
|
+
export { type AnimationRecordFrame, type ControllerOptions, type Diagram, type DiagramOptions, DiagramPresentationController, type GifExportOptions, ICON_REGISTRY, type IconSpec, type PngExportOptions, type SvgExportOptions, createDiagram, encodeGifSequence, exportDiagramAsPng, exportDiagramAsVectorSvg };
|
package/dist/index.js
CHANGED
|
@@ -808,13 +808,14 @@ function ensureAnnotationStyles(doc) {
|
|
|
808
808
|
doc.head.appendChild(style);
|
|
809
809
|
}
|
|
810
810
|
function positionForAnnotation(position, bounds, index) {
|
|
811
|
-
const pad =
|
|
811
|
+
const pad = 28;
|
|
812
|
+
const topPad = 68;
|
|
812
813
|
const p = (position ?? "").toLowerCase();
|
|
813
|
-
if (p.includes("top") && p.includes("right")) return { x: bounds.width - pad - 200, y:
|
|
814
|
-
if (p.includes("top") && p.includes("left")) return { x: pad, y:
|
|
814
|
+
if (p.includes("top") && p.includes("right")) return { x: bounds.width - pad - 200, y: topPad + index * 48 };
|
|
815
|
+
if (p.includes("top") && p.includes("left")) return { x: pad, y: topPad + index * 48 };
|
|
815
816
|
if (p.includes("bottom") && p.includes("right")) return { x: bounds.width - pad - 200, y: bounds.height - pad - 40 };
|
|
816
817
|
if (p.includes("bottom") && p.includes("left")) return { x: pad, y: bounds.height - pad - 40 };
|
|
817
|
-
return { x: bounds.width - pad - 200, y:
|
|
818
|
+
return { x: bounds.width - pad - 200, y: topPad + index * 48 };
|
|
818
819
|
}
|
|
819
820
|
function mountAnnotations(layer, annotations, nodes, theme, bounds) {
|
|
820
821
|
if (annotations.length === 0) return;
|
|
@@ -2110,11 +2111,16 @@ function createDiagram(opts) {
|
|
|
2110
2111
|
const totalDurationMs = plan.duration * 1e3;
|
|
2111
2112
|
const durationSeconds = plan.duration;
|
|
2112
2113
|
const viewport = document.createElement("div");
|
|
2114
|
+
viewport.className = "markdy-viewport";
|
|
2113
2115
|
Object.assign(viewport.style, {
|
|
2114
2116
|
position: "relative",
|
|
2115
2117
|
width: "100%",
|
|
2118
|
+
height: "100%",
|
|
2119
|
+
maxWidth: "100%",
|
|
2120
|
+
maxHeight: "100%",
|
|
2116
2121
|
aspectRatio: `${plan.meta.width} / ${plan.meta.height}`,
|
|
2117
|
-
overflow: "hidden"
|
|
2122
|
+
overflow: "hidden",
|
|
2123
|
+
boxSizing: "border-box"
|
|
2118
2124
|
});
|
|
2119
2125
|
container.appendChild(viewport);
|
|
2120
2126
|
let progressEl = null;
|
|
@@ -2269,14 +2275,85 @@ function createDiagram(opts) {
|
|
|
2269
2275
|
nodeEls.set(node.id, el);
|
|
2270
2276
|
}
|
|
2271
2277
|
let fitScale = 1;
|
|
2278
|
+
let sceneOffsetX = 0;
|
|
2279
|
+
let sceneOffsetY = 0;
|
|
2280
|
+
function computeContentBounds() {
|
|
2281
|
+
if (!plan.nodes || plan.nodes.length === 0) {
|
|
2282
|
+
return { minX: 0, minY: 0, maxX: plan.meta.width, maxY: plan.meta.height, width: plan.meta.width, height: plan.meta.height };
|
|
2283
|
+
}
|
|
2284
|
+
let minX = Infinity;
|
|
2285
|
+
let minY = Infinity;
|
|
2286
|
+
let maxX = -Infinity;
|
|
2287
|
+
let maxY = -Infinity;
|
|
2288
|
+
for (const node of plan.nodes) {
|
|
2289
|
+
minX = Math.min(minX, node.x);
|
|
2290
|
+
minY = Math.min(minY, node.y);
|
|
2291
|
+
maxX = Math.max(maxX, node.x + node.width);
|
|
2292
|
+
maxY = Math.max(maxY, node.y + node.height);
|
|
2293
|
+
}
|
|
2294
|
+
for (const gb of plan.groupBoundaries ?? []) {
|
|
2295
|
+
minX = Math.min(minX, gb.x);
|
|
2296
|
+
minY = Math.min(minY, gb.y);
|
|
2297
|
+
maxX = Math.max(maxX, gb.x + gb.width);
|
|
2298
|
+
maxY = Math.max(maxY, gb.y + gb.height);
|
|
2299
|
+
}
|
|
2300
|
+
for (const bus of plan.treeBuses ?? []) {
|
|
2301
|
+
minX = Math.min(minX, bus.parentX, ...bus.childXs.length ? bus.childXs : [bus.parentX]);
|
|
2302
|
+
minY = Math.min(minY, bus.parentY, bus.branchY, bus.childY);
|
|
2303
|
+
maxX = Math.max(maxX, bus.parentX, ...bus.childXs.length ? bus.childXs : [bus.parentX]);
|
|
2304
|
+
maxY = Math.max(maxY, bus.parentY, bus.branchY, bus.childY);
|
|
2305
|
+
}
|
|
2306
|
+
if (plan.title) {
|
|
2307
|
+
minY = Math.min(minY, 20);
|
|
2308
|
+
}
|
|
2309
|
+
const pad = 36;
|
|
2310
|
+
minX = Math.max(0, minX - pad);
|
|
2311
|
+
minY = Math.max(0, minY - pad);
|
|
2312
|
+
maxX = Math.min(plan.meta.width, maxX + pad);
|
|
2313
|
+
maxY = Math.min(plan.meta.height, maxY + pad);
|
|
2314
|
+
const width = Math.max(maxX - minX, 200);
|
|
2315
|
+
const height = Math.max(maxY - minY, 140);
|
|
2316
|
+
return { minX, minY, maxX, maxY, width, height };
|
|
2317
|
+
}
|
|
2272
2318
|
function scaleScene() {
|
|
2273
|
-
const
|
|
2274
|
-
|
|
2319
|
+
const isBrowserLayout = (viewport.clientWidth || container.clientWidth || 0) > 0;
|
|
2320
|
+
if (!isBrowserLayout) {
|
|
2321
|
+
fitScale = 1;
|
|
2322
|
+
sceneOffsetX = 0;
|
|
2323
|
+
sceneOffsetY = 0;
|
|
2324
|
+
scene.style.left = "0px";
|
|
2325
|
+
scene.style.top = "0px";
|
|
2326
|
+
scene.style.transformOrigin = "0 0";
|
|
2327
|
+
scene.style.transform = "scale(1)";
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
const vWidth = viewport.clientWidth || container.clientWidth || plan.meta.width;
|
|
2331
|
+
const vHeight = viewport.clientHeight || container.clientHeight || vWidth * plan.meta.height / plan.meta.width;
|
|
2332
|
+
const canvasScaleX = vWidth / plan.meta.width;
|
|
2333
|
+
const canvasScaleY = vHeight / plan.meta.height;
|
|
2334
|
+
const baseCanvasScale = Math.min(canvasScaleX, canvasScaleY);
|
|
2335
|
+
const bounds = computeContentBounds();
|
|
2336
|
+
const contentScaleX = vWidth * 0.94 / bounds.width;
|
|
2337
|
+
const contentScaleY = vHeight * 0.94 / bounds.height;
|
|
2338
|
+
const optimalContentScale = Math.min(contentScaleX, contentScaleY);
|
|
2339
|
+
const chosenScale = Math.max(
|
|
2340
|
+
baseCanvasScale,
|
|
2341
|
+
Math.min(optimalContentScale, baseCanvasScale * 1.45)
|
|
2342
|
+
);
|
|
2343
|
+
fitScale = Number.isFinite(chosenScale) && chosenScale > 0 ? chosenScale : 1;
|
|
2344
|
+
const scaledWidth = plan.meta.width * fitScale;
|
|
2345
|
+
const scaledHeight = plan.meta.height * fitScale;
|
|
2346
|
+
sceneOffsetX = (vWidth - scaledWidth) / 2;
|
|
2347
|
+
sceneOffsetY = (vHeight - scaledHeight) / 2;
|
|
2348
|
+
scene.style.left = `${sceneOffsetX}px`;
|
|
2349
|
+
scene.style.top = `${sceneOffsetY}px`;
|
|
2350
|
+
scene.style.transformOrigin = "0 0";
|
|
2275
2351
|
scene.style.transform = `scale(${fitScale})`;
|
|
2276
2352
|
}
|
|
2277
2353
|
scaleScene();
|
|
2278
2354
|
const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(scaleScene) : null;
|
|
2279
2355
|
resizeObserver?.observe(viewport);
|
|
2356
|
+
if (container !== viewport) resizeObserver?.observe(container);
|
|
2280
2357
|
const edgeRuntimes = /* @__PURE__ */ new Map();
|
|
2281
2358
|
const edgeSceneId = createEdgeSceneId();
|
|
2282
2359
|
const sequenceAnims = plan.diagramType === "sequence" ? mountSequenceLayer(
|
|
@@ -2341,8 +2418,8 @@ function createDiagram(opts) {
|
|
|
2341
2418
|
function handleViewportWheel(event) {
|
|
2342
2419
|
event.preventDefault();
|
|
2343
2420
|
const rect = viewport.getBoundingClientRect();
|
|
2344
|
-
const pointerX = (event.clientX - rect.left) / fitScale;
|
|
2345
|
-
const pointerY = (event.clientY - rect.top) / fitScale;
|
|
2421
|
+
const pointerX = (event.clientX - rect.left - sceneOffsetX) / fitScale;
|
|
2422
|
+
const pointerY = (event.clientY - rect.top - sceneOffsetY) / fitScale;
|
|
2346
2423
|
const nextScale = Math.min(MAX_VIEWPORT_ZOOM, Math.max(MIN_VIEWPORT_ZOOM, viewportScale * Math.exp(-event.deltaY * VIEWPORT_ZOOM_STEP)));
|
|
2347
2424
|
if (nextScale === viewportScale) return;
|
|
2348
2425
|
const sceneX = (pointerX - viewportPanX) / viewportScale;
|
|
@@ -2694,7 +2771,7 @@ function lzwCompress(pixels, minCodeSize = 8) {
|
|
|
2694
2771
|
if (nextCode === 1 << codeSize && codeSize < 12) {
|
|
2695
2772
|
codeSize++;
|
|
2696
2773
|
}
|
|
2697
|
-
if (nextCode >=
|
|
2774
|
+
if (nextCode >= 4096) {
|
|
2698
2775
|
codes.push(clearCode);
|
|
2699
2776
|
dict = /* @__PURE__ */ new Map();
|
|
2700
2777
|
for (let j = 0; j < clearCode; j++) dict.set(String(j), j);
|
|
@@ -2785,31 +2862,101 @@ function encodeGifSequence(frames, options = {}) {
|
|
|
2785
2862
|
|
|
2786
2863
|
// src/export/svg-exporter.ts
|
|
2787
2864
|
function exportDiagramAsVectorSvg(containerEl, options = {}) {
|
|
2788
|
-
const
|
|
2789
|
-
if (!
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2865
|
+
const sceneEl = containerEl.classList?.contains("markdy-scene-root") ? containerEl : containerEl.querySelector(".markdy-scene-root") || containerEl.querySelector("svg") || (containerEl.tagName?.toLowerCase() === "svg" ? containerEl : null);
|
|
2866
|
+
if (!sceneEl) throw new Error("No Markdy scene element found in container");
|
|
2867
|
+
if (sceneEl.tagName?.toLowerCase() === "svg") {
|
|
2868
|
+
const clonedSvg = sceneEl.cloneNode(true);
|
|
2869
|
+
if (!clonedSvg.getAttribute("xmlns")) {
|
|
2870
|
+
clonedSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2871
|
+
}
|
|
2872
|
+
const serializer2 = new XMLSerializer();
|
|
2873
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2874
|
+
${serializer2.serializeToString(clonedSvg)}`;
|
|
2875
|
+
}
|
|
2876
|
+
const clonedScene = sceneEl.cloneNode(true);
|
|
2877
|
+
const widthStr = clonedScene.style.width || String(sceneEl.clientWidth || 800);
|
|
2878
|
+
const heightStr = clonedScene.style.height || String(sceneEl.clientHeight || 400);
|
|
2879
|
+
let width = parseFloat(widthStr);
|
|
2880
|
+
let height = parseFloat(heightStr);
|
|
2881
|
+
if (isNaN(width)) width = 800;
|
|
2882
|
+
if (isNaN(height)) height = 400;
|
|
2883
|
+
const scale = options.scale || 1;
|
|
2884
|
+
const scaledWidth = width * scale;
|
|
2885
|
+
const scaledHeight = height * scale;
|
|
2886
|
+
clonedScene.style.transform = `scale(${scale})`;
|
|
2887
|
+
clonedScene.style.transformOrigin = "0 0";
|
|
2888
|
+
clonedScene.style.position = "relative";
|
|
2889
|
+
clonedScene.style.left = "0px";
|
|
2890
|
+
clonedScene.style.top = "0px";
|
|
2891
|
+
clonedScene.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2798
2892
|
if (options.transparentBackground) {
|
|
2799
|
-
|
|
2800
|
-
if (bgRect) bgRect.remove();
|
|
2893
|
+
clonedScene.style.background = "transparent";
|
|
2801
2894
|
}
|
|
2895
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
2896
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2897
|
+
svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
2898
|
+
svg.setAttribute("width", String(scaledWidth));
|
|
2899
|
+
svg.setAttribute("height", String(scaledHeight));
|
|
2900
|
+
svg.setAttribute("viewBox", `0 0 ${scaledWidth} ${scaledHeight}`);
|
|
2802
2901
|
if (options.includeThemeStyles !== false) {
|
|
2803
2902
|
const styleEl = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
2804
|
-
|
|
2805
|
-
|
|
2903
|
+
let combinedStyles = `
|
|
2904
|
+
foreignObject { width: 100%; height: 100%; }
|
|
2806
2905
|
.markdy-node { transition: opacity 0.3s ease; }
|
|
2807
2906
|
`;
|
|
2808
|
-
|
|
2809
|
-
|
|
2907
|
+
if (typeof document !== "undefined") {
|
|
2908
|
+
const styles = document.querySelectorAll("style[id^='markdy-']");
|
|
2909
|
+
for (let i = 0; i < styles.length; i++) {
|
|
2910
|
+
combinedStyles += styles[i].textContent + "\n";
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
styleEl.textContent = combinedStyles;
|
|
2914
|
+
svg.appendChild(styleEl);
|
|
2915
|
+
}
|
|
2916
|
+
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
|
2917
|
+
foreignObject.setAttribute("width", "100%");
|
|
2918
|
+
foreignObject.setAttribute("height", "100%");
|
|
2919
|
+
foreignObject.appendChild(clonedScene);
|
|
2920
|
+
svg.appendChild(foreignObject);
|
|
2810
2921
|
const serializer = new XMLSerializer();
|
|
2811
2922
|
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2812
|
-
` + serializer.serializeToString(
|
|
2923
|
+
` + serializer.serializeToString(svg);
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
// src/export/png-exporter.ts
|
|
2927
|
+
async function exportDiagramAsPng(containerEl, options = {}) {
|
|
2928
|
+
const svgXml = exportDiagramAsVectorSvg(containerEl, options);
|
|
2929
|
+
const pixelRatio = options.pixelRatio || 2;
|
|
2930
|
+
const blob = new Blob([svgXml], { type: "image/svg+xml;charset=utf-8" });
|
|
2931
|
+
const url = URL.createObjectURL(blob);
|
|
2932
|
+
const img = new Image();
|
|
2933
|
+
await new Promise((resolve, reject) => {
|
|
2934
|
+
img.onload = () => resolve();
|
|
2935
|
+
img.onerror = () => {
|
|
2936
|
+
URL.revokeObjectURL(url);
|
|
2937
|
+
reject(new Error("Failed to rasterize SVG into Image for PNG export"));
|
|
2938
|
+
};
|
|
2939
|
+
img.src = url;
|
|
2940
|
+
});
|
|
2941
|
+
const width = img.naturalWidth || 800;
|
|
2942
|
+
const height = img.naturalHeight || 400;
|
|
2943
|
+
const canvas = document.createElement("canvas");
|
|
2944
|
+
canvas.width = width * pixelRatio;
|
|
2945
|
+
canvas.height = height * pixelRatio;
|
|
2946
|
+
const ctx = canvas.getContext("2d");
|
|
2947
|
+
if (!ctx) {
|
|
2948
|
+
URL.revokeObjectURL(url);
|
|
2949
|
+
throw new Error("Could not get 2D canvas context for PNG export");
|
|
2950
|
+
}
|
|
2951
|
+
ctx.scale(pixelRatio, pixelRatio);
|
|
2952
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
2953
|
+
URL.revokeObjectURL(url);
|
|
2954
|
+
return new Promise((resolve, reject) => {
|
|
2955
|
+
canvas.toBlob((b) => {
|
|
2956
|
+
if (b) resolve(b);
|
|
2957
|
+
else reject(new Error("Canvas toBlob failed for PNG export"));
|
|
2958
|
+
}, "image/png");
|
|
2959
|
+
});
|
|
2813
2960
|
}
|
|
2814
2961
|
|
|
2815
2962
|
// src/presentation-controller.ts
|
|
@@ -2817,60 +2964,79 @@ var DiagramPresentationController = class {
|
|
|
2817
2964
|
diagram;
|
|
2818
2965
|
plan;
|
|
2819
2966
|
currentBeatIndex = 0;
|
|
2967
|
+
options;
|
|
2968
|
+
isDestroyed = false;
|
|
2820
2969
|
constructor(diagram, plan, options = {}) {
|
|
2821
2970
|
this.diagram = diagram;
|
|
2822
2971
|
this.plan = plan;
|
|
2972
|
+
this.options = options;
|
|
2823
2973
|
if (options.enableKeyboard !== false && typeof window !== "undefined") {
|
|
2824
|
-
this.
|
|
2974
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
2825
2975
|
}
|
|
2826
2976
|
}
|
|
2827
2977
|
nextBeat() {
|
|
2828
2978
|
if (this.currentBeatIndex < this.plan.beats.length - 1) {
|
|
2829
2979
|
this.currentBeatIndex++;
|
|
2830
|
-
|
|
2831
|
-
this.diagram.seek(beat.start);
|
|
2980
|
+
this.applyCurrentBeat();
|
|
2832
2981
|
}
|
|
2833
2982
|
}
|
|
2834
2983
|
prevBeat() {
|
|
2835
2984
|
if (this.currentBeatIndex > 0) {
|
|
2836
2985
|
this.currentBeatIndex--;
|
|
2837
|
-
|
|
2838
|
-
|
|
2986
|
+
this.applyCurrentBeat();
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
applyCurrentBeat() {
|
|
2990
|
+
const beat = this.plan.beats[this.currentBeatIndex];
|
|
2991
|
+
this.diagram.seek(beat.start);
|
|
2992
|
+
if (this.options.onBeatChange) {
|
|
2993
|
+
this.options.onBeatChange(beat.name, this.currentBeatIndex);
|
|
2839
2994
|
}
|
|
2840
2995
|
}
|
|
2841
2996
|
getCurrentBeatIndex() {
|
|
2842
2997
|
return this.currentBeatIndex;
|
|
2843
2998
|
}
|
|
2844
2999
|
togglePlay() {
|
|
2845
|
-
this.diagram.
|
|
3000
|
+
if (this.diagram.isPlaying()) {
|
|
3001
|
+
this.diagram.pause();
|
|
3002
|
+
} else {
|
|
3003
|
+
this.diagram.play();
|
|
3004
|
+
}
|
|
2846
3005
|
}
|
|
2847
3006
|
setSpeed(rate) {
|
|
2848
3007
|
this.diagram.setPlaybackRate(rate);
|
|
2849
3008
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
}
|
|
3009
|
+
onKeyDown = (e) => {
|
|
3010
|
+
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLSelectElement || e.target?.isContentEditable) {
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
switch (e.key) {
|
|
3014
|
+
case "ArrowRight":
|
|
3015
|
+
case "PageDown":
|
|
3016
|
+
this.nextBeat();
|
|
3017
|
+
break;
|
|
3018
|
+
case "ArrowLeft":
|
|
3019
|
+
case "PageUp":
|
|
3020
|
+
this.prevBeat();
|
|
3021
|
+
break;
|
|
3022
|
+
case " ":
|
|
3023
|
+
e.preventDefault();
|
|
3024
|
+
this.togglePlay();
|
|
3025
|
+
break;
|
|
3026
|
+
case "1":
|
|
3027
|
+
this.setSpeed(1);
|
|
3028
|
+
break;
|
|
3029
|
+
case "2":
|
|
3030
|
+
this.setSpeed(2);
|
|
3031
|
+
break;
|
|
3032
|
+
}
|
|
3033
|
+
};
|
|
3034
|
+
destroy() {
|
|
3035
|
+
if (this.isDestroyed) return;
|
|
3036
|
+
this.isDestroyed = true;
|
|
3037
|
+
if (typeof window !== "undefined") {
|
|
3038
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
3039
|
+
}
|
|
2874
3040
|
}
|
|
2875
3041
|
};
|
|
2876
3042
|
export {
|
|
@@ -2878,5 +3044,6 @@ export {
|
|
|
2878
3044
|
ICON_REGISTRY,
|
|
2879
3045
|
createDiagram,
|
|
2880
3046
|
encodeGifSequence,
|
|
3047
|
+
exportDiagramAsPng,
|
|
2881
3048
|
exportDiagramAsVectorSvg
|
|
2882
3049
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.27",
|
|
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",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@markdy/core": "0.8.
|
|
47
|
+
"@markdy/core": "0.8.27"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"jsdom": "^29.1.1",
|
|
51
51
|
"tsup": "^8.5.1",
|
|
52
52
|
"typescript": "^5.9.3",
|
|
53
53
|
"vitest": "^4.1.7",
|
|
54
|
-
"@markdy/stdlib-systems": "0.8.
|
|
54
|
+
"@markdy/stdlib-systems": "0.8.27"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup",
|