@markdy/renderer-dom 0.8.16 → 0.8.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +124 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Web Animations API renderer for [MarkdyScript](../../docs/SYNTAX.md) scenes. Tra
|
|
|
13
13
|
- **Seek-safe** — manual `currentTime` control enables reliable `seek()` in any direction
|
|
14
14
|
- **Playback-rate controls** — set normalized timeline speed to slow down or speed up diagrams without rebuilding animations
|
|
15
15
|
- **Interactive viewport** — opt into wheel zoom and drag pan while click-to-pause stays available
|
|
16
|
+
- **Embed controls** — opt into a compact playback toolbar with restart, speed, and view reset controls
|
|
16
17
|
- **Semantic themes** — `paper`, `editorial`, `nebula`, `midnight`, `blueprint`, and `graphite`, with per-role node and edge colors
|
|
17
18
|
- **Single dependency** — only `@markdy/core`
|
|
18
19
|
|
|
@@ -76,6 +77,7 @@ diagram.destroy(); // clean up DOM + cancel animations
|
|
|
76
77
|
| `sceneBoundaryProgress` | `boolean` | `progressBar ?? true` | Preferred flag for the rainbow scene-boundary progress bar |
|
|
77
78
|
| `playbackRate` | `number` | `1` | Normalized timeline speed multiplier; `1` is Markdy's normal pace |
|
|
78
79
|
| `interactiveViewport` | `boolean` | `false` | Enable wheel zoom and drag pan on the rendered viewport |
|
|
80
|
+
| `controls` | `boolean` | `false` | Show a compact toolbar with play/pause, restart, speed, and view reset controls; also enables viewport interaction |
|
|
79
81
|
| `onWarning` | `(warning: Diagnostic) => void` | `console.warn` | Called for each soft parse warning |
|
|
80
82
|
| `onTimeUpdate` | `(seconds: number, durationSeconds: number) => void` | — | Called whenever playback or seek changes the current time |
|
|
81
83
|
| `onPlayStateChange` | `(playing: boolean) => void` | — | Called when playback starts or pauses |
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ interface DiagramOptions {
|
|
|
24
24
|
playbackRate?: number;
|
|
25
25
|
/** Enable wheel zoom and drag pan on the rendered viewport. Defaults to false. */
|
|
26
26
|
interactiveViewport?: boolean;
|
|
27
|
+
/** Show built-in playback and viewport controls. Also enables viewport interaction. Defaults to false. */
|
|
28
|
+
controls?: boolean;
|
|
27
29
|
onWarning?: (warning: Diagnostic) => void;
|
|
28
30
|
onTimeUpdate?: (seconds: number, durationSeconds: number) => void;
|
|
29
31
|
onPlayStateChange?: (playing: boolean) => void;
|
package/dist/index.js
CHANGED
|
@@ -1863,7 +1863,8 @@ function createDiagram(opts) {
|
|
|
1863
1863
|
progressBar,
|
|
1864
1864
|
sceneBoundaryProgress,
|
|
1865
1865
|
playbackRate: initialPlaybackRate = DEFAULT_PLAYBACK_RATE,
|
|
1866
|
-
|
|
1866
|
+
controls = false,
|
|
1867
|
+
interactiveViewport = controls,
|
|
1867
1868
|
onWarning = (w) => console.warn(`[markdy] line ${w.line}: ${w.message}`),
|
|
1868
1869
|
onTimeUpdate,
|
|
1869
1870
|
onPlayStateChange
|
|
@@ -2074,9 +2075,17 @@ function createDiagram(opts) {
|
|
|
2074
2075
|
let dragLastY = 0;
|
|
2075
2076
|
let dragMoved = false;
|
|
2076
2077
|
let suppressNextClick = false;
|
|
2078
|
+
let controlsPlayButton = null;
|
|
2079
|
+
let controlsRateButtons = [];
|
|
2077
2080
|
function applyViewportTransform() {
|
|
2078
2081
|
viewportTransform.style.transform = `translate(${viewportPanX}px, ${viewportPanY}px) scale(${viewportScale})`;
|
|
2079
2082
|
}
|
|
2083
|
+
function resetViewportTransform() {
|
|
2084
|
+
viewportScale = 1;
|
|
2085
|
+
viewportPanX = 0;
|
|
2086
|
+
viewportPanY = 0;
|
|
2087
|
+
applyViewportTransform();
|
|
2088
|
+
}
|
|
2080
2089
|
function handleViewportWheel(event) {
|
|
2081
2090
|
event.preventDefault();
|
|
2082
2091
|
const rect = viewport.getBoundingClientRect();
|
|
@@ -2123,6 +2132,28 @@ function createDiagram(opts) {
|
|
|
2123
2132
|
dragMoved = false;
|
|
2124
2133
|
viewport.style.cursor = interactiveViewport ? "grab" : "pointer";
|
|
2125
2134
|
}
|
|
2135
|
+
function handleViewportDoubleClick(event) {
|
|
2136
|
+
event.preventDefault();
|
|
2137
|
+
suppressNextClick = false;
|
|
2138
|
+
resetViewportTransform();
|
|
2139
|
+
}
|
|
2140
|
+
function syncControls() {
|
|
2141
|
+
if (controlsPlayButton) {
|
|
2142
|
+
controlsPlayButton.textContent = isPlaying ? "Pause" : "Play";
|
|
2143
|
+
controlsPlayButton.setAttribute("aria-label", isPlaying ? "Pause diagram" : "Play diagram");
|
|
2144
|
+
}
|
|
2145
|
+
for (const button of controlsRateButtons) {
|
|
2146
|
+
const rate = Number(button.dataset.rate ?? "1");
|
|
2147
|
+
const active = Math.abs(rate - playbackRate) < 1e-3;
|
|
2148
|
+
button.setAttribute("aria-pressed", active ? "true" : "false");
|
|
2149
|
+
button.style.background = active ? "rgba(15, 23, 42, 0.92)" : "rgba(255, 255, 255, 0.92)";
|
|
2150
|
+
button.style.color = active ? "#ffffff" : "#111827";
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
function emitPlayStateChange(playing) {
|
|
2154
|
+
onPlayStateChange?.(playing);
|
|
2155
|
+
syncControls();
|
|
2156
|
+
}
|
|
2126
2157
|
function applyCurrentTime() {
|
|
2127
2158
|
for (const anim of allAnims) anim.currentTime = sceneMs;
|
|
2128
2159
|
onTimeUpdate?.(sceneMs / 1e3, durationSeconds);
|
|
@@ -2136,7 +2167,7 @@ function createDiagram(opts) {
|
|
|
2136
2167
|
sceneMs = totalDurationMs;
|
|
2137
2168
|
applyCurrentTime();
|
|
2138
2169
|
isPlaying = false;
|
|
2139
|
-
|
|
2170
|
+
emitPlayStateChange(false);
|
|
2140
2171
|
lastRafTs = null;
|
|
2141
2172
|
rafId = null;
|
|
2142
2173
|
return;
|
|
@@ -2150,14 +2181,14 @@ function createDiagram(opts) {
|
|
|
2150
2181
|
play() {
|
|
2151
2182
|
if (isPlaying) return;
|
|
2152
2183
|
isPlaying = true;
|
|
2153
|
-
|
|
2184
|
+
emitPlayStateChange(true);
|
|
2154
2185
|
lastRafTs = null;
|
|
2155
2186
|
rafId = requestAnimationFrame(rafTick);
|
|
2156
2187
|
},
|
|
2157
2188
|
pause() {
|
|
2158
2189
|
if (!isPlaying) return;
|
|
2159
2190
|
isPlaying = false;
|
|
2160
|
-
|
|
2191
|
+
emitPlayStateChange(false);
|
|
2161
2192
|
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
2162
2193
|
rafId = null;
|
|
2163
2194
|
lastRafTs = null;
|
|
@@ -2170,6 +2201,7 @@ function createDiagram(opts) {
|
|
|
2170
2201
|
setPlaybackRate(rate) {
|
|
2171
2202
|
if (!Number.isFinite(rate) || rate <= 0) return;
|
|
2172
2203
|
playbackRate = rate;
|
|
2204
|
+
syncControls();
|
|
2173
2205
|
},
|
|
2174
2206
|
playbackRate() {
|
|
2175
2207
|
return playbackRate;
|
|
@@ -2199,6 +2231,91 @@ function createDiagram(opts) {
|
|
|
2199
2231
|
if (viewport.parentNode === container) container.removeChild(viewport);
|
|
2200
2232
|
}
|
|
2201
2233
|
};
|
|
2234
|
+
function togglePlayback() {
|
|
2235
|
+
if (isPlaying) diagram.pause();
|
|
2236
|
+
else {
|
|
2237
|
+
if (!loop && sceneMs >= totalDurationMs) sceneMs = 0;
|
|
2238
|
+
diagram.play();
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
function makeControlButton(label, ariaLabel) {
|
|
2242
|
+
const button = document.createElement("button");
|
|
2243
|
+
button.type = "button";
|
|
2244
|
+
button.textContent = label;
|
|
2245
|
+
button.setAttribute("aria-label", ariaLabel);
|
|
2246
|
+
button.title = ariaLabel;
|
|
2247
|
+
Object.assign(button.style, {
|
|
2248
|
+
appearance: "none",
|
|
2249
|
+
border: "1px solid rgba(15, 23, 42, 0.16)",
|
|
2250
|
+
borderRadius: "6px",
|
|
2251
|
+
background: "rgba(255, 255, 255, 0.92)",
|
|
2252
|
+
color: "#111827",
|
|
2253
|
+
cursor: "pointer",
|
|
2254
|
+
font: "600 11px/1.2 system-ui, sans-serif",
|
|
2255
|
+
padding: "6px 8px",
|
|
2256
|
+
minWidth: "34px",
|
|
2257
|
+
whiteSpace: "nowrap",
|
|
2258
|
+
boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08)"
|
|
2259
|
+
});
|
|
2260
|
+
return button;
|
|
2261
|
+
}
|
|
2262
|
+
function mountControls() {
|
|
2263
|
+
const toolbar = document.createElement("div");
|
|
2264
|
+
toolbar.className = "markdy-controls";
|
|
2265
|
+
toolbar.setAttribute("role", "toolbar");
|
|
2266
|
+
toolbar.setAttribute("aria-label", "Diagram controls");
|
|
2267
|
+
Object.assign(toolbar.style, {
|
|
2268
|
+
position: "absolute",
|
|
2269
|
+
left: "50%",
|
|
2270
|
+
bottom: "10px",
|
|
2271
|
+
zIndex: "10000",
|
|
2272
|
+
display: "flex",
|
|
2273
|
+
alignItems: "center",
|
|
2274
|
+
justifyContent: "center",
|
|
2275
|
+
flexWrap: "wrap",
|
|
2276
|
+
gap: "6px",
|
|
2277
|
+
maxWidth: "calc(100% - 20px)",
|
|
2278
|
+
padding: "6px",
|
|
2279
|
+
border: "1px solid rgba(15, 23, 42, 0.12)",
|
|
2280
|
+
borderRadius: "8px",
|
|
2281
|
+
background: "rgba(255, 255, 255, 0.78)",
|
|
2282
|
+
boxShadow: "0 10px 28px rgba(15, 23, 42, 0.16)",
|
|
2283
|
+
backdropFilter: "blur(10px)",
|
|
2284
|
+
transform: "translateX(-50%)",
|
|
2285
|
+
pointerEvents: "auto"
|
|
2286
|
+
});
|
|
2287
|
+
for (const eventName of ["click", "dblclick", "pointerdown", "pointermove", "pointerup", "wheel"]) {
|
|
2288
|
+
toolbar.addEventListener(eventName, (event) => event.stopPropagation());
|
|
2289
|
+
}
|
|
2290
|
+
controlsPlayButton = makeControlButton("Play", "Play diagram");
|
|
2291
|
+
controlsPlayButton.className = "markdy-control-play";
|
|
2292
|
+
controlsPlayButton.addEventListener("click", togglePlayback);
|
|
2293
|
+
toolbar.appendChild(controlsPlayButton);
|
|
2294
|
+
const restartButton = makeControlButton("Restart", "Restart diagram");
|
|
2295
|
+
restartButton.className = "markdy-control-restart";
|
|
2296
|
+
restartButton.addEventListener("click", () => {
|
|
2297
|
+
diagram.seek(0);
|
|
2298
|
+
diagram.play();
|
|
2299
|
+
});
|
|
2300
|
+
toolbar.appendChild(restartButton);
|
|
2301
|
+
controlsRateButtons = [0.5, 1, 2].map((rate) => {
|
|
2302
|
+
const button = makeControlButton(`${rate}x`, `Set playback speed to ${rate}x`);
|
|
2303
|
+
button.className = "markdy-control-rate";
|
|
2304
|
+
button.dataset.rate = String(rate);
|
|
2305
|
+
button.setAttribute("aria-pressed", "false");
|
|
2306
|
+
button.addEventListener("click", () => diagram.setPlaybackRate(rate));
|
|
2307
|
+
toolbar.appendChild(button);
|
|
2308
|
+
return button;
|
|
2309
|
+
});
|
|
2310
|
+
if (interactiveViewport) {
|
|
2311
|
+
const resetButton = makeControlButton("Reset", "Reset diagram view");
|
|
2312
|
+
resetButton.className = "markdy-control-reset-view";
|
|
2313
|
+
resetButton.addEventListener("click", resetViewportTransform);
|
|
2314
|
+
toolbar.appendChild(resetButton);
|
|
2315
|
+
}
|
|
2316
|
+
viewport.appendChild(toolbar);
|
|
2317
|
+
syncControls();
|
|
2318
|
+
}
|
|
2202
2319
|
viewport.style.cursor = interactiveViewport ? "grab" : "pointer";
|
|
2203
2320
|
if (interactiveViewport) {
|
|
2204
2321
|
viewport.style.touchAction = "none";
|
|
@@ -2207,17 +2324,15 @@ function createDiagram(opts) {
|
|
|
2207
2324
|
viewport.addEventListener("pointermove", handleViewportPointerMove);
|
|
2208
2325
|
viewport.addEventListener("pointerup", handleViewportPointerEnd);
|
|
2209
2326
|
viewport.addEventListener("pointercancel", handleViewportPointerEnd);
|
|
2327
|
+
viewport.addEventListener("dblclick", handleViewportDoubleClick);
|
|
2210
2328
|
}
|
|
2329
|
+
if (controls) mountControls();
|
|
2211
2330
|
viewport.addEventListener("click", () => {
|
|
2212
2331
|
if (suppressNextClick) {
|
|
2213
2332
|
suppressNextClick = false;
|
|
2214
2333
|
return;
|
|
2215
2334
|
}
|
|
2216
|
-
|
|
2217
|
-
else {
|
|
2218
|
-
if (!loop && sceneMs >= totalDurationMs) sceneMs = 0;
|
|
2219
|
-
diagram.play();
|
|
2220
|
-
}
|
|
2335
|
+
togglePlayback();
|
|
2221
2336
|
});
|
|
2222
2337
|
if (autoplay) diagram.play();
|
|
2223
2338
|
return diagram;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.18",
|
|
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.18"
|
|
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.18"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup",
|