@markdy/renderer-dom 1.0.18 → 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/README.md +10 -10
- package/dist/index.d.ts +4 -3
- package/dist/index.js +491 -200
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,7 +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
|
|
16
|
+
- **Embed controls** — opt into only the playback, timeline, viewport, export, and sharing controls a scene needs
|
|
17
17
|
- **Semantic themes** — `paper`, `editorial`, `nebula`, `midnight`, `blueprint`, and `graphite`, with per-role node and edge colors
|
|
18
18
|
- **Single dependency** — only `@markdy/core`
|
|
19
19
|
|
|
@@ -70,21 +70,21 @@ diagram.destroy(); // clean up DOM + cancel animations
|
|
|
70
70
|
|---|---|---|---|
|
|
71
71
|
| `container` | `HTMLElement` | *(required)* | DOM element to mount the scene into |
|
|
72
72
|
| `code` | `string` | *(required)* | MarkdyScript source code |
|
|
73
|
-
| `autoplay` | `boolean` | `
|
|
74
|
-
| `loop` | `boolean` | `
|
|
75
|
-
| `copyright` | `boolean` | `
|
|
73
|
+
| `autoplay` | `boolean` | `player.playback.autoplay ?? true` | Override whether playback starts immediately |
|
|
74
|
+
| `loop` | `boolean` | `player.playback.loop ?? true` | Override whether playback loops at the end |
|
|
75
|
+
| `copyright` | `boolean` | `player.chrome.badge ?? true` | Show the linked badge at the footer's right edge |
|
|
76
76
|
| `progressBar` | `boolean \| string` | `true` | Deprecated compatibility flag for the scene-boundary progress bar |
|
|
77
|
-
| `sceneBoundaryProgress` | `boolean \| string` | `
|
|
78
|
-
| `progressColor` | `string` | `
|
|
79
|
-
| `playbackRate` | `number` | `
|
|
80
|
-
| `interactiveViewport` | `boolean` | `
|
|
81
|
-
| `controls` | `boolean` | `player.controls` |
|
|
77
|
+
| `sceneBoundaryProgress` | `boolean \| string` | `player.chrome.progress` | Override boundary progress, or pass a custom color/gradient |
|
|
78
|
+
| `progressColor` | `string` | `player.chrome.color` or rainbow | Override the progress color or gradient |
|
|
79
|
+
| `playbackRate` | `number` | `player.playback.rate ?? 1` | Override the initial timeline multiplier |
|
|
80
|
+
| `interactiveViewport` | `boolean` | `player.interaction` | `true` supplies default gestures; `false` suppresses script gestures |
|
|
81
|
+
| `controls` | `boolean` | `player.controls` | `true` supplies legacy toolbar defaults; `false` suppresses script controls |
|
|
82
82
|
| `shareUrl` | `string` | Markdy playground | Base URL used by the Share control when building `#code=` links |
|
|
83
83
|
| `onWarning` | `(warning: Diagnostic) => void` | `console.warn` | Called for each soft parse warning |
|
|
84
84
|
| `onTimeUpdate` | `(seconds: number, durationSeconds: number) => void` | — | Called whenever playback or seek changes the current time |
|
|
85
85
|
| `onPlayStateChange` | `(playing: boolean) => void` | — | Called when playback starts or pauses |
|
|
86
86
|
|
|
87
|
-
> **Note:**
|
|
87
|
+
> **Note:** Prefer grouped `player:` configuration in MarkdyScript. Omitted host options preserve it; host `false` suppresses controls or interaction, while host `true` supplies legacy defaults for leaves the script does not set. A speed selector renders only with at least two distinct positive `speeds` values.
|
|
88
88
|
|
|
89
89
|
### `Diagram`
|
|
90
90
|
|
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.
|
|
@@ -26,11 +26,11 @@ interface DiagramOptions {
|
|
|
26
26
|
progressBarColor?: string;
|
|
27
27
|
/** Playback speed multiplier. Defaults to 1, where 1 is Markdy's normal pace. */
|
|
28
28
|
playbackRate?: number;
|
|
29
|
-
/**
|
|
29
|
+
/** Host interaction override. `true` enables default gestures; `false` suppresses script interaction. */
|
|
30
30
|
interactiveViewport?: boolean;
|
|
31
31
|
/** Base URL for the Share control. Defaults to the Markdy playground. */
|
|
32
32
|
shareUrl?: string;
|
|
33
|
-
/**
|
|
33
|
+
/** Host controls override. `true` enables the legacy default set; `false` suppresses script controls. */
|
|
34
34
|
controls?: boolean;
|
|
35
35
|
onWarning?: (warning: Diagnostic) => void;
|
|
36
36
|
onTimeUpdate?: (seconds: number, durationSeconds: number) => void;
|
|
@@ -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) {
|
|
@@ -2511,17 +2725,20 @@ function ensureSceneStyles(doc) {
|
|
|
2511
2725
|
justify-content: space-between;
|
|
2512
2726
|
gap: 8px;
|
|
2513
2727
|
padding: 6px 12px;
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2728
|
+
color: var(--md-text, #f8fafc);
|
|
2729
|
+
background: transparent;
|
|
2730
|
+
backdrop-filter: none;
|
|
2731
|
+
-webkit-backdrop-filter: none;
|
|
2732
|
+
border-top: none;
|
|
2518
2733
|
z-index: 20;
|
|
2519
2734
|
}
|
|
2520
2735
|
.markdy-controls {
|
|
2521
2736
|
display: flex;
|
|
2522
2737
|
align-items: center;
|
|
2523
2738
|
justify-content: flex-start;
|
|
2524
|
-
|
|
2739
|
+
flex: 1 1 auto;
|
|
2740
|
+
width: auto;
|
|
2741
|
+
min-width: 0;
|
|
2525
2742
|
gap: 8px;
|
|
2526
2743
|
max-width: 100%;
|
|
2527
2744
|
overflow-x: auto;
|
|
@@ -2555,7 +2772,6 @@ function ensureSceneStyles(doc) {
|
|
|
2555
2772
|
align-items: center;
|
|
2556
2773
|
gap: 4px;
|
|
2557
2774
|
flex-shrink: 0;
|
|
2558
|
-
margin-left: auto;
|
|
2559
2775
|
}
|
|
2560
2776
|
.markdy-control-divider {
|
|
2561
2777
|
width: 1px;
|
|
@@ -2626,37 +2842,24 @@ function ensureSceneStyles(doc) {
|
|
|
2626
2842
|
transform: translateY(-1px) scale(1.02);
|
|
2627
2843
|
}
|
|
2628
2844
|
.markdy-badge {
|
|
2629
|
-
position: absolute;
|
|
2630
|
-
top: 10px;
|
|
2631
|
-
right: 12px;
|
|
2632
|
-
z-index: 25;
|
|
2633
2845
|
display: inline-flex;
|
|
2634
2846
|
align-items: center;
|
|
2635
|
-
|
|
2636
|
-
font-family:
|
|
2847
|
+
text-align: right;
|
|
2848
|
+
font-family: system-ui, sans-serif;
|
|
2637
2849
|
font-size: 10px;
|
|
2638
|
-
font-weight:
|
|
2639
|
-
|
|
2640
|
-
color: var(--md-text-muted, #94a3b8);
|
|
2850
|
+
font-weight: 400;
|
|
2851
|
+
color: var(--md-control-text, #94a3b8);
|
|
2641
2852
|
text-decoration: none;
|
|
2642
|
-
padding:
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
-
|
|
2648
|
-
opacity: 0.65;
|
|
2649
|
-
transition: all 0.2s ease;
|
|
2650
|
-
pointer-events: auto;
|
|
2651
|
-
user-select: none;
|
|
2853
|
+
padding: 0;
|
|
2854
|
+
opacity: 0.7;
|
|
2855
|
+
margin-left: auto;
|
|
2856
|
+
flex-shrink: 0;
|
|
2857
|
+
transition: opacity 0.15s ease, color 0.15s ease;
|
|
2858
|
+
white-space: nowrap;
|
|
2652
2859
|
}
|
|
2653
2860
|
.markdy-badge:hover {
|
|
2654
2861
|
opacity: 1;
|
|
2655
|
-
color: var(--
|
|
2656
|
-
border-color: var(--accent, #10b981);
|
|
2657
|
-
background: var(--md-surface-raised, rgba(15, 23, 42, 0.75));
|
|
2658
|
-
transform: translateY(-1px);
|
|
2659
|
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
|
|
2862
|
+
color: var(--md-text, #f8fafc);
|
|
2660
2863
|
}
|
|
2661
2864
|
.markdy-speed-group {
|
|
2662
2865
|
display: inline-flex;
|
|
@@ -2689,57 +2892,6 @@ function ensureSceneStyles(doc) {
|
|
|
2689
2892
|
color: #ffffff !important;
|
|
2690
2893
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25) !important;
|
|
2691
2894
|
}
|
|
2692
|
-
.markdy-control-seek {
|
|
2693
|
-
appearance: none;
|
|
2694
|
-
-webkit-appearance: none;
|
|
2695
|
-
width: 100%;
|
|
2696
|
-
height: 5px;
|
|
2697
|
-
border-radius: 999px;
|
|
2698
|
-
background: linear-gradient(
|
|
2699
|
-
to right,
|
|
2700
|
-
var(--accent, #10b981) 0%,
|
|
2701
|
-
var(--accent, #10b981) var(--seek-pct, 0%),
|
|
2702
|
-
rgba(148, 163, 184, 0.22) var(--seek-pct, 0%),
|
|
2703
|
-
rgba(148, 163, 184, 0.22) 100%
|
|
2704
|
-
);
|
|
2705
|
-
outline: none;
|
|
2706
|
-
cursor: pointer;
|
|
2707
|
-
margin: 0;
|
|
2708
|
-
transition: height 0.15s ease;
|
|
2709
|
-
}
|
|
2710
|
-
.markdy-control-seek:hover {
|
|
2711
|
-
height: 7px;
|
|
2712
|
-
}
|
|
2713
|
-
.markdy-control-seek::-webkit-slider-thumb {
|
|
2714
|
-
appearance: none;
|
|
2715
|
-
-webkit-appearance: none;
|
|
2716
|
-
width: 12px;
|
|
2717
|
-
height: 12px;
|
|
2718
|
-
border-radius: 50%;
|
|
2719
|
-
background: #ffffff;
|
|
2720
|
-
border: 2px solid var(--accent, #10b981);
|
|
2721
|
-
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
|
|
2722
|
-
cursor: pointer;
|
|
2723
|
-
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
2724
|
-
}
|
|
2725
|
-
.markdy-control-seek:hover::-webkit-slider-thumb {
|
|
2726
|
-
transform: scale(1.25);
|
|
2727
|
-
box-shadow: 0 0 8px var(--accent-glow, rgba(16, 185, 129, 0.6));
|
|
2728
|
-
}
|
|
2729
|
-
.markdy-control-seek::-moz-range-thumb {
|
|
2730
|
-
width: 12px;
|
|
2731
|
-
height: 12px;
|
|
2732
|
-
border-radius: 50%;
|
|
2733
|
-
background: #ffffff;
|
|
2734
|
-
border: 2px solid var(--accent, #10b981);
|
|
2735
|
-
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
|
|
2736
|
-
cursor: pointer;
|
|
2737
|
-
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
2738
|
-
}
|
|
2739
|
-
.markdy-control-seek:hover::-moz-range-thumb {
|
|
2740
|
-
transform: scale(1.25);
|
|
2741
|
-
box-shadow: 0 0 8px var(--accent-glow, rgba(16, 185, 129, 0.6));
|
|
2742
|
-
}
|
|
2743
2895
|
.markdy-control-time {
|
|
2744
2896
|
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
2745
2897
|
font-size: 10px;
|
|
@@ -2925,44 +3077,61 @@ function ensureSceneStyles(doc) {
|
|
|
2925
3077
|
.markdy-control-seek::-webkit-slider-runnable-track {
|
|
2926
3078
|
width: 100%;
|
|
2927
3079
|
height: 4.5px;
|
|
2928
|
-
background:
|
|
3080
|
+
background: linear-gradient(
|
|
3081
|
+
to right,
|
|
3082
|
+
var(--accent, #10b981) 0%,
|
|
3083
|
+
var(--accent, #10b981) var(--seek-pct, 0%),
|
|
3084
|
+
rgba(148, 163, 184, 0.35) var(--seek-pct, 0%),
|
|
3085
|
+
rgba(148, 163, 184, 0.35) 100%
|
|
3086
|
+
);
|
|
2929
3087
|
border-radius: 9999px;
|
|
2930
|
-
transition: background 0.15s ease;
|
|
2931
|
-
}
|
|
2932
|
-
.markdy-control-seek:hover::-webkit-slider-runnable-track {
|
|
2933
|
-
background: rgba(148, 163, 184, 0.55);
|
|
2934
3088
|
}
|
|
2935
3089
|
.markdy-control-seek::-webkit-slider-thumb {
|
|
3090
|
+
appearance: none;
|
|
2936
3091
|
-webkit-appearance: none;
|
|
2937
3092
|
height: 13px;
|
|
2938
3093
|
width: 13px;
|
|
2939
3094
|
border-radius: 50%;
|
|
2940
|
-
background: #
|
|
3095
|
+
background: var(--accent, #10b981);
|
|
2941
3096
|
border: 2px solid #ffffff;
|
|
2942
3097
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
2943
3098
|
margin-top: -4.25px;
|
|
2944
3099
|
cursor: grab;
|
|
2945
|
-
transition: transform 0.1s ease,
|
|
3100
|
+
transition: transform 0.1s ease, box-shadow 0.15s ease;
|
|
3101
|
+
}
|
|
3102
|
+
.markdy-control-seek:hover::-webkit-slider-thumb {
|
|
3103
|
+
transform: scale(1.2);
|
|
3104
|
+
box-shadow: 0 0 8px var(--accent-glow, rgba(16, 185, 129, 0.6));
|
|
2946
3105
|
}
|
|
2947
3106
|
.markdy-control-seek:active::-webkit-slider-thumb {
|
|
2948
3107
|
cursor: grabbing;
|
|
2949
3108
|
transform: scale(1.2);
|
|
2950
|
-
background: #1d4ed8;
|
|
2951
3109
|
}
|
|
2952
3110
|
.markdy-control-seek::-moz-range-track {
|
|
2953
3111
|
width: 100%;
|
|
2954
3112
|
height: 4.5px;
|
|
2955
|
-
background:
|
|
3113
|
+
background: linear-gradient(
|
|
3114
|
+
to right,
|
|
3115
|
+
var(--accent, #10b981) 0%,
|
|
3116
|
+
var(--accent, #10b981) var(--seek-pct, 0%),
|
|
3117
|
+
rgba(148, 163, 184, 0.35) var(--seek-pct, 0%),
|
|
3118
|
+
rgba(148, 163, 184, 0.35) 100%
|
|
3119
|
+
);
|
|
2956
3120
|
border-radius: 9999px;
|
|
2957
3121
|
}
|
|
2958
3122
|
.markdy-control-seek::-moz-range-thumb {
|
|
2959
3123
|
height: 13px;
|
|
2960
3124
|
width: 13px;
|
|
2961
3125
|
border-radius: 50%;
|
|
2962
|
-
background: #
|
|
3126
|
+
background: var(--accent, #10b981);
|
|
2963
3127
|
border: 2px solid #ffffff;
|
|
2964
3128
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
2965
3129
|
cursor: grab;
|
|
3130
|
+
transition: transform 0.1s ease, box-shadow 0.15s ease;
|
|
3131
|
+
}
|
|
3132
|
+
.markdy-control-seek:hover::-moz-range-thumb {
|
|
3133
|
+
transform: scale(1.2);
|
|
3134
|
+
box-shadow: 0 0 8px var(--accent-glow, rgba(16, 185, 129, 0.6));
|
|
2966
3135
|
}
|
|
2967
3136
|
|
|
2968
3137
|
[data-markdy-theme="midnight"] .markdy-controls button,
|
|
@@ -2983,9 +3152,9 @@ function ensureSceneStyles(doc) {
|
|
|
2983
3152
|
[data-markdy-theme="nebula"] .markdy-controls button:hover:not([aria-pressed="true"]),
|
|
2984
3153
|
:root[data-theme="dark"] .markdy-controls button:hover:not([aria-pressed="true"]),
|
|
2985
3154
|
.theme-dark .markdy-controls button:hover:not([aria-pressed="true"]) {
|
|
2986
|
-
background: rgba(51, 65, 85, 0.95);
|
|
2987
|
-
color: #f8fafc;
|
|
2988
|
-
border-color: #94a3b8;
|
|
3155
|
+
background: var(--md-control-hover-bg, rgba(51, 65, 85, 0.95));
|
|
3156
|
+
color: var(--md-control-hover-text, #f8fafc);
|
|
3157
|
+
border-color: var(--md-control-hover-border, #94a3b8);
|
|
2989
3158
|
}
|
|
2990
3159
|
[data-markdy-theme="midnight"] .markdy-controls button[aria-pressed="true"],
|
|
2991
3160
|
[data-markdy-theme="blueprint"] .markdy-controls button[aria-pressed="true"],
|
|
@@ -2994,10 +3163,10 @@ function ensureSceneStyles(doc) {
|
|
|
2994
3163
|
[data-markdy-theme="nebula"] .markdy-controls button[aria-pressed="true"],
|
|
2995
3164
|
:root[data-theme="dark"] .markdy-controls button[aria-pressed="true"],
|
|
2996
3165
|
.theme-dark .markdy-controls button[aria-pressed="true"] {
|
|
2997
|
-
background: #3b82f6 !important;
|
|
3166
|
+
background: var(--accent, #3b82f6) !important;
|
|
2998
3167
|
color: #ffffff !important;
|
|
2999
|
-
border-color: #3b82f6 !important;
|
|
3000
|
-
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5) !important;
|
|
3168
|
+
border-color: var(--accent, #3b82f6) !important;
|
|
3169
|
+
box-shadow: 0 0 10px var(--accent-glow, rgba(59, 130, 246, 0.5)) !important;
|
|
3001
3170
|
}
|
|
3002
3171
|
.markdy-footer a {
|
|
3003
3172
|
transition: opacity 0.15s ease, color 0.15s ease;
|
|
@@ -3091,41 +3260,57 @@ function ensureSceneStyles(doc) {
|
|
|
3091
3260
|
`;
|
|
3092
3261
|
doc.head.appendChild(style);
|
|
3093
3262
|
}
|
|
3094
|
-
function
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
if (theme.
|
|
3113
|
-
if (theme.
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3263
|
+
function applyThemeVariables(element, theme) {
|
|
3264
|
+
element.style.setProperty("--md-canvas", theme.canvas);
|
|
3265
|
+
element.style.setProperty("--md-surface", theme.surface);
|
|
3266
|
+
element.style.setProperty("--md-surface-raised", theme.surfaceRaised);
|
|
3267
|
+
element.style.setProperty("--md-border", theme.border);
|
|
3268
|
+
element.style.setProperty("--md-text", theme.text);
|
|
3269
|
+
element.style.setProperty("--md-text-muted", theme.textMuted);
|
|
3270
|
+
element.style.setProperty("--md-paper", theme.paper ?? theme.canvas);
|
|
3271
|
+
element.style.setProperty("--md-ink", theme.ink ?? theme.text);
|
|
3272
|
+
element.style.setProperty("--md-muted", theme.muted ?? theme.textMuted);
|
|
3273
|
+
element.style.setProperty("--md-rule", theme.rule ?? theme.border);
|
|
3274
|
+
element.style.setProperty("--md-grid-minor", theme.gridMinor);
|
|
3275
|
+
element.style.setProperty("--md-grid-major", theme.gridMajor);
|
|
3276
|
+
element.style.setProperty("--md-vignette", theme.vignette);
|
|
3277
|
+
element.style.setProperty("--md-accent", theme.accent);
|
|
3278
|
+
element.style.setProperty("--accent", theme.accent);
|
|
3279
|
+
element.style.setProperty("--accent-glow", `color-mix(in srgb, ${theme.accent} 40%, transparent)`);
|
|
3280
|
+
element.style.setProperty("--accent-light", theme.accentTint ?? `color-mix(in srgb, ${theme.accent} 15%, transparent)`);
|
|
3281
|
+
if (theme.link) element.style.setProperty("--md-link", theme.link);
|
|
3282
|
+
if (theme.soft) element.style.setProperty("--md-soft", theme.soft);
|
|
3283
|
+
if (theme.accentTint) element.style.setProperty("--md-accent-tint", theme.accentTint);
|
|
3284
|
+
element.style.setProperty("--md-node-surface", theme.nodeSurface ?? theme.surface);
|
|
3285
|
+
element.style.setProperty("--md-node-surface-raised", theme.nodeSurfaceRaised ?? theme.surfaceRaised);
|
|
3286
|
+
element.style.setProperty("--md-hairline", theme.hairline ?? `color-mix(in srgb, ${theme.border} 50%, transparent)`);
|
|
3287
|
+
element.style.setProperty("--md-shadow", theme.shadow ?? "rgba(2, 6, 23, 0.55)");
|
|
3288
|
+
element.style.setProperty("--md-footer-bg", "transparent");
|
|
3289
|
+
element.style.setProperty("--md-footer-border", "transparent");
|
|
3290
|
+
element.style.setProperty("--md-control-bg", theme.surfaceRaised);
|
|
3291
|
+
element.style.setProperty("--md-control-border", theme.border);
|
|
3292
|
+
element.style.setProperty("--md-control-text", theme.textMuted);
|
|
3293
|
+
element.style.setProperty("--md-control-hover-bg", theme.canvas);
|
|
3294
|
+
element.style.setProperty("--md-control-hover-text", theme.text);
|
|
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)`);
|
|
3297
|
+
element.style.setProperty("--md-divider", theme.border);
|
|
3298
|
+
if (theme.fonts?.title) element.style.setProperty("--md-font-title", theme.fonts.title);
|
|
3299
|
+
if (theme.fonts?.nodeName) element.style.setProperty("--md-font-node", theme.fonts.nodeName);
|
|
3300
|
+
if (theme.fonts?.mono) element.style.setProperty("--md-font-mono", theme.fonts.mono);
|
|
3301
|
+
if (theme.radiusMd) element.style.setProperty("--md-radius-md", `${theme.radiusMd}px`);
|
|
3122
3302
|
if (theme.spacing) {
|
|
3123
3303
|
for (const [key, value] of Object.entries(theme.spacing)) {
|
|
3124
|
-
|
|
3304
|
+
element.style.setProperty(`--md-space-${key}`, `${value}px`);
|
|
3125
3305
|
}
|
|
3126
3306
|
}
|
|
3127
|
-
|
|
3128
|
-
if (theme.flatCards)
|
|
3307
|
+
element.dataset.markdyTheme = theme.name;
|
|
3308
|
+
if (theme.flatCards) element.dataset.flat = "1";
|
|
3309
|
+
}
|
|
3310
|
+
function applyThemeToScene(scene, theme) {
|
|
3311
|
+
applyThemeVariables(scene, theme);
|
|
3312
|
+
scene.style.background = theme.canvas;
|
|
3313
|
+
scene.style.color = theme.text;
|
|
3129
3314
|
}
|
|
3130
3315
|
|
|
3131
3316
|
// src/diagram.ts
|
|
@@ -3277,7 +3462,7 @@ function createDiagram(opts) {
|
|
|
3277
3462
|
function updateProgressBar(pct) {
|
|
3278
3463
|
if (!progressEl) return;
|
|
3279
3464
|
if (progressMode === "bar") {
|
|
3280
|
-
progressEl.style.background = customColor ?? "#2563eb";
|
|
3465
|
+
progressEl.style.background = customColor ?? plan.theme.accent ?? "#2563eb";
|
|
3281
3466
|
progressEl.style.transformOrigin = "left center";
|
|
3282
3467
|
progressEl.style.transform = `scaleX(${pct})`;
|
|
3283
3468
|
return;
|
|
@@ -3289,27 +3474,44 @@ function createDiagram(opts) {
|
|
|
3289
3474
|
progressEl.style.webkitMask = progressEl.style.mask;
|
|
3290
3475
|
progressEl.style.maskComposite = "exclude";
|
|
3291
3476
|
progressEl.style.webkitMaskComposite = "xor";
|
|
3292
|
-
progressEl.style.padding = "
|
|
3477
|
+
progressEl.style.padding = "1px";
|
|
3293
3478
|
}
|
|
3294
3479
|
let footer = null;
|
|
3295
3480
|
function ensureFooter() {
|
|
3296
3481
|
if (footer) return footer;
|
|
3297
3482
|
footer = document.createElement("div");
|
|
3298
3483
|
footer.className = "markdy-footer";
|
|
3484
|
+
applyThemeVariables(footer, plan.theme);
|
|
3299
3485
|
Object.assign(footer.style, {
|
|
3300
3486
|
display: "flex",
|
|
3301
3487
|
alignItems: "center",
|
|
3302
3488
|
justifyContent: "space-between",
|
|
3303
|
-
flexWrap: "
|
|
3489
|
+
flexWrap: "nowrap",
|
|
3304
3490
|
gap: "6px",
|
|
3305
3491
|
width: "100%",
|
|
3306
3492
|
boxSizing: "border-box",
|
|
3307
|
-
padding: "4px 6px 0"
|
|
3493
|
+
padding: "4px 6px 0",
|
|
3494
|
+
background: "transparent"
|
|
3308
3495
|
});
|
|
3309
3496
|
if (container.parentNode) container.parentNode.insertBefore(footer, container.nextSibling);
|
|
3310
3497
|
else container.appendChild(footer);
|
|
3311
3498
|
return footer;
|
|
3312
3499
|
}
|
|
3500
|
+
let badge = null;
|
|
3501
|
+
if (copyright) {
|
|
3502
|
+
badge = document.createElement("a");
|
|
3503
|
+
badge.className = "markdy-badge";
|
|
3504
|
+
badge.href = `${MARKDY_PLAYGROUND_URL}#code=${encodeCodeForPlaygroundHash(code)}`;
|
|
3505
|
+
badge.target = "_blank";
|
|
3506
|
+
badge.rel = "noopener noreferrer";
|
|
3507
|
+
badge.title = "Open and edit in Markdy Playground";
|
|
3508
|
+
badge.textContent = "Powered by Markdy";
|
|
3509
|
+
ensureFooter().appendChild(badge);
|
|
3510
|
+
const badgeLink = badge;
|
|
3511
|
+
void compressMarkdyToUrlHash(code).then((hash) => {
|
|
3512
|
+
badgeLink.href = `${MARKDY_PLAYGROUND_URL}#code=${hash}`;
|
|
3513
|
+
}).catch(() => void 0);
|
|
3514
|
+
}
|
|
3313
3515
|
ensureSceneStyles(document);
|
|
3314
3516
|
ensureNodeStyles(document);
|
|
3315
3517
|
const scene = document.createElement("div");
|
|
@@ -3326,17 +3528,6 @@ function createDiagram(opts) {
|
|
|
3326
3528
|
});
|
|
3327
3529
|
applyThemeToScene(scene, plan.theme);
|
|
3328
3530
|
viewport.appendChild(scene);
|
|
3329
|
-
let badge = null;
|
|
3330
|
-
if (copyright) {
|
|
3331
|
-
badge = document.createElement("a");
|
|
3332
|
-
badge.className = "markdy-badge";
|
|
3333
|
-
badge.href = "https://markdy.com";
|
|
3334
|
-
badge.target = "_blank";
|
|
3335
|
-
badge.rel = "noopener noreferrer";
|
|
3336
|
-
badge.title = "Powered by Markdy - Diagram as Code";
|
|
3337
|
-
badge.textContent = "Powered by Markdy";
|
|
3338
|
-
viewport.appendChild(badge);
|
|
3339
|
-
}
|
|
3340
3531
|
const viewportTransform = document.createElement("div");
|
|
3341
3532
|
viewportTransform.className = "markdy-viewport-transform";
|
|
3342
3533
|
Object.assign(viewportTransform.style, {
|
|
@@ -3548,6 +3739,7 @@ function createDiagram(opts) {
|
|
|
3548
3739
|
let controlsSeekBar = null;
|
|
3549
3740
|
let controlsTimeEl = null;
|
|
3550
3741
|
let controlsFitButton = null;
|
|
3742
|
+
let closeCodePanel = null;
|
|
3551
3743
|
let fitViewActive = false;
|
|
3552
3744
|
const ICONS2 = {
|
|
3553
3745
|
play: '<svg class="markdy-icon" width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>',
|
|
@@ -3759,6 +3951,84 @@ function createDiagram(opts) {
|
|
|
3759
3951
|
}
|
|
3760
3952
|
diagram.seek(target);
|
|
3761
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
|
+
},
|
|
3762
4032
|
destroy() {
|
|
3763
4033
|
diagram.pause();
|
|
3764
4034
|
if (keyboardShortcuts && typeof window !== "undefined") {
|
|
@@ -3767,8 +4037,9 @@ function createDiagram(opts) {
|
|
|
3767
4037
|
for (const anim of allAnims) anim.cancel();
|
|
3768
4038
|
resizeObserver?.disconnect();
|
|
3769
4039
|
removeFullscreenListeners?.();
|
|
4040
|
+
closeCodePanel?.();
|
|
4041
|
+
closeCodePanel = null;
|
|
3770
4042
|
if (progressEl?.parentNode === viewport) viewport.removeChild(progressEl);
|
|
3771
|
-
if (badge?.parentNode) badge.parentNode.removeChild(badge);
|
|
3772
4043
|
if (footer?.parentNode) footer.parentNode.removeChild(footer);
|
|
3773
4044
|
if (viewport.parentNode === container) container.removeChild(viewport);
|
|
3774
4045
|
}
|
|
@@ -3924,6 +4195,7 @@ function createDiagram(opts) {
|
|
|
3924
4195
|
const targetThemes = isDark ? lightThemes : darkThemes;
|
|
3925
4196
|
const nextTheme = targetThemes[Math.floor(Math.random() * targetThemes.length)];
|
|
3926
4197
|
flashControlLabel(button, nextTheme);
|
|
4198
|
+
diagram.setTheme(nextTheme);
|
|
3927
4199
|
container.dispatchEvent(
|
|
3928
4200
|
new CustomEvent("markdy-theme-switch", {
|
|
3929
4201
|
bubbles: true,
|
|
@@ -3935,19 +4207,33 @@ function createDiagram(opts) {
|
|
|
3935
4207
|
}
|
|
3936
4208
|
function tintCode(raw) {
|
|
3937
4209
|
return raw.split("\n").map((line) => {
|
|
3938
|
-
if (/^\s*\/\//.test(line)) {
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
line = line.replace(/(->|<-|~>|--|<~)/g, '<span class="t-edge">$1</span>');
|
|
3942
|
-
line = line.replace(/"([^"]*)"/g, '"<span class="t-string">$1</span>"');
|
|
3943
|
-
line = line.replace(
|
|
3944
|
-
/^(\s*)(scene|beat|group|player|show|frame|glow|focus|layout|edge|annotation|start|end|decision|service|browser|gateway|database|cache|queue|worker|function|pod|user|client|hub|station|metric|mobile)\b/,
|
|
3945
|
-
'$1<span class="t-keyword">$2</span>'
|
|
4210
|
+
if (/^\s*\/\//.test(line)) return `<span class="t-comment">${escHtml(line)}</span>`;
|
|
4211
|
+
const keyword = line.match(
|
|
4212
|
+
/^(\s*)(scene|beat|group|player|show|frame|glow|focus|layout|edge|annotation|start|end|decision|service|browser|gateway|database|cache|queue|worker|function|pod|user|client|hub|station|metric|mobile)\b/
|
|
3946
4213
|
);
|
|
3947
|
-
|
|
3948
|
-
return line;
|
|
4214
|
+
const prefix = keyword ? `${escHtml(keyword[1])}<span class="t-keyword">${keyword[2]}</span>` : "";
|
|
4215
|
+
return prefix + tintInlineCode(line.slice(keyword?.[0].length ?? 0));
|
|
3949
4216
|
}).join("\n");
|
|
3950
4217
|
}
|
|
4218
|
+
function tintInlineCode(raw) {
|
|
4219
|
+
const tokenPattern = /"[^"]*"|->|<-|~>|--|<~|\b\d[\d.]*(?:ms|px|s)?\b/g;
|
|
4220
|
+
let output = "";
|
|
4221
|
+
let cursor = 0;
|
|
4222
|
+
for (const match of raw.matchAll(tokenPattern)) {
|
|
4223
|
+
const token = match[0];
|
|
4224
|
+
const index = match.index ?? 0;
|
|
4225
|
+
output += escHtml(raw.slice(cursor, index));
|
|
4226
|
+
if (token.startsWith('"')) {
|
|
4227
|
+
output += `"<span class="t-string">${escHtml(token.slice(1, -1))}</span>"`;
|
|
4228
|
+
} else if (/^\d/.test(token)) {
|
|
4229
|
+
output += `<span class="t-number">${token}</span>`;
|
|
4230
|
+
} else {
|
|
4231
|
+
output += `<span class="t-edge">${escHtml(token)}</span>`;
|
|
4232
|
+
}
|
|
4233
|
+
cursor = index + token.length;
|
|
4234
|
+
}
|
|
4235
|
+
return output + escHtml(raw.slice(cursor));
|
|
4236
|
+
}
|
|
3951
4237
|
function escHtml(str) {
|
|
3952
4238
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3953
4239
|
}
|
|
@@ -3957,6 +4243,7 @@ function createDiagram(opts) {
|
|
|
3957
4243
|
button.className = "markdy-control-code";
|
|
3958
4244
|
button.setAttribute("aria-haspopup", "dialog");
|
|
3959
4245
|
button.addEventListener("click", () => {
|
|
4246
|
+
closeCodePanel?.();
|
|
3960
4247
|
const overlay = document.createElement("div");
|
|
3961
4248
|
overlay.className = "markdy-code-panel-overlay";
|
|
3962
4249
|
overlay.setAttribute("role", "dialog");
|
|
@@ -4014,24 +4301,28 @@ function createDiagram(opts) {
|
|
|
4014
4301
|
overlay.appendChild(panel);
|
|
4015
4302
|
document.body.appendChild(overlay);
|
|
4016
4303
|
closeBtn.focus();
|
|
4304
|
+
let closing = false;
|
|
4305
|
+
function removePanel() {
|
|
4306
|
+
document.removeEventListener("keydown", handleEscape);
|
|
4307
|
+
overlay.remove();
|
|
4308
|
+
if (closeCodePanel === removePanel) closeCodePanel = null;
|
|
4309
|
+
}
|
|
4017
4310
|
function closePanel() {
|
|
4311
|
+
if (closing) return;
|
|
4312
|
+
closing = true;
|
|
4018
4313
|
overlay.dataset.closing = "1";
|
|
4019
|
-
|
|
4314
|
+
document.removeEventListener("keydown", handleEscape);
|
|
4315
|
+
overlay.addEventListener("animationend", removePanel, { once: true });
|
|
4020
4316
|
}
|
|
4021
4317
|
closeBtn.addEventListener("click", closePanel);
|
|
4022
4318
|
overlay.addEventListener("click", (e) => {
|
|
4023
4319
|
if (e.target === overlay) closePanel();
|
|
4024
4320
|
});
|
|
4025
4321
|
function handleEscape(e) {
|
|
4026
|
-
if (e.key === "Escape")
|
|
4027
|
-
closePanel();
|
|
4028
|
-
document.removeEventListener("keydown", handleEscape);
|
|
4029
|
-
}
|
|
4322
|
+
if (e.key === "Escape") closePanel();
|
|
4030
4323
|
}
|
|
4031
4324
|
document.addEventListener("keydown", handleEscape);
|
|
4032
|
-
|
|
4033
|
-
if (overlay.dataset.closing === "1") document.removeEventListener("keydown", handleEscape);
|
|
4034
|
-
}, { once: true });
|
|
4325
|
+
closeCodePanel = removePanel;
|
|
4035
4326
|
});
|
|
4036
4327
|
toolbar.appendChild(button);
|
|
4037
4328
|
}
|
|
@@ -4238,7 +4529,7 @@ function createDiagram(opts) {
|
|
|
4238
4529
|
mountCodeControl(toolsGroup);
|
|
4239
4530
|
}
|
|
4240
4531
|
if (toolsGroup.children.length > 0) toolbar.appendChild(toolsGroup);
|
|
4241
|
-
ensureFooter().
|
|
4532
|
+
ensureFooter().insertBefore(toolbar, badge);
|
|
4242
4533
|
syncControls();
|
|
4243
4534
|
}
|
|
4244
4535
|
viewport.style.cursor = interactiveViewport ? "grab" : "pointer";
|
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",
|