@markdy/renderer-dom 0.7.26 → 0.7.28
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.js +1232 -94
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -37,6 +37,9 @@ function toEasing(val) {
|
|
|
37
37
|
return "linear";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// src/actors.ts
|
|
41
|
+
import { TECHNICAL_NODE_KINDS, VISUAL_PRIMITIVE_TYPES } from "@markdy/core";
|
|
42
|
+
|
|
40
43
|
// src/figure.ts
|
|
41
44
|
function createFigureEl(def) {
|
|
42
45
|
const skinColor = def.args[0] || "#ffdbac";
|
|
@@ -309,6 +312,766 @@ function readRotation(el) {
|
|
|
309
312
|
}
|
|
310
313
|
|
|
311
314
|
// src/actors.ts
|
|
315
|
+
var ARCHITECTURE_NODE_TYPES = new Set(Object.keys(TECHNICAL_NODE_KINDS));
|
|
316
|
+
var LEGACY_VISUAL_PRIMITIVE_TYPES = ["parking_map", "ascii_map", "game_scene", "byte_viz"];
|
|
317
|
+
var VISUAL_PRIMITIVE_TYPE_SET = /* @__PURE__ */ new Set([
|
|
318
|
+
...VISUAL_PRIMITIVE_TYPES,
|
|
319
|
+
...LEGACY_VISUAL_PRIMITIVE_TYPES
|
|
320
|
+
]);
|
|
321
|
+
var ARCHITECTURE_STYLE_ID = "markdy-architecture-node-styles";
|
|
322
|
+
var VISUAL_PRIMITIVE_STYLE_ID = "markdy-visual-primitive-styles";
|
|
323
|
+
function ensureVisualPrimitiveStyles(doc) {
|
|
324
|
+
if (doc.getElementById(VISUAL_PRIMITIVE_STYLE_ID)) return;
|
|
325
|
+
const style = doc.createElement("style");
|
|
326
|
+
style.id = VISUAL_PRIMITIVE_STYLE_ID;
|
|
327
|
+
style.textContent = `
|
|
328
|
+
.markdy-visual {
|
|
329
|
+
--surface-a: rgba(15, 23, 42, 0.9);
|
|
330
|
+
--accent: #38bdf8;
|
|
331
|
+
--accent-2: #22c55e;
|
|
332
|
+
position: relative;
|
|
333
|
+
box-sizing: border-box;
|
|
334
|
+
width: 390px;
|
|
335
|
+
height: 250px;
|
|
336
|
+
overflow: hidden;
|
|
337
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
338
|
+
border-radius: 24px;
|
|
339
|
+
color: #e5f3ff;
|
|
340
|
+
background:
|
|
341
|
+
radial-gradient(circle at 12% 0%, color-mix(in srgb, var(--accent) 32%, transparent), transparent 34%),
|
|
342
|
+
radial-gradient(circle at 90% 15%, color-mix(in srgb, var(--accent-2) 26%, transparent), transparent 34%),
|
|
343
|
+
linear-gradient(145deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.025) 32%, var(--surface-a));
|
|
344
|
+
box-shadow:
|
|
345
|
+
0 26px 70px rgba(2, 6, 23, 0.42),
|
|
346
|
+
0 0 0 1px rgba(255, 255, 255, 0.06) inset,
|
|
347
|
+
0 1px 0 rgba(255, 255, 255, 0.18) inset,
|
|
348
|
+
0 0 44px color-mix(in srgb, var(--accent) 24%, transparent);
|
|
349
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
350
|
+
line-height: 1.2;
|
|
351
|
+
isolation: isolate;
|
|
352
|
+
contain: layout paint style;
|
|
353
|
+
backdrop-filter: blur(16px) saturate(1.2);
|
|
354
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.2);
|
|
355
|
+
}
|
|
356
|
+
.markdy-visual,
|
|
357
|
+
.markdy-visual * {
|
|
358
|
+
box-sizing: border-box;
|
|
359
|
+
}
|
|
360
|
+
.markdy-visual::before {
|
|
361
|
+
content: "";
|
|
362
|
+
position: absolute;
|
|
363
|
+
inset: 0;
|
|
364
|
+
z-index: -1;
|
|
365
|
+
background:
|
|
366
|
+
linear-gradient(90deg, transparent 0 48%, rgba(255, 255, 255, 0.09) 50%, transparent 52%) 0 0 / 34px 34px,
|
|
367
|
+
linear-gradient(0deg, transparent 0 48%, rgba(255, 255, 255, 0.055) 50%, transparent 52%) 0 0 / 34px 34px;
|
|
368
|
+
mask-image: linear-gradient(to bottom, rgba(0,0,0,0.85), transparent 82%);
|
|
369
|
+
opacity: 0.48;
|
|
370
|
+
animation: markdyGridDrift 7s linear infinite;
|
|
371
|
+
}
|
|
372
|
+
.markdy-visual::after {
|
|
373
|
+
content: "";
|
|
374
|
+
position: absolute;
|
|
375
|
+
inset: -40% -20%;
|
|
376
|
+
z-index: -1;
|
|
377
|
+
background: conic-gradient(from 90deg, transparent, color-mix(in srgb, var(--accent) 20%, transparent), transparent 32%);
|
|
378
|
+
opacity: 0.6;
|
|
379
|
+
animation: markdyAurora 9s linear infinite;
|
|
380
|
+
}
|
|
381
|
+
.markdy-visual[data-tone="green"] { --accent: #22c55e; --accent-2: #38bdf8; }
|
|
382
|
+
.markdy-visual[data-tone="amber"] { --accent: #f59e0b; --accent-2: #22c55e; }
|
|
383
|
+
.markdy-visual[data-tone="purple"] { --accent: #a78bfa; --accent-2: #38bdf8; }
|
|
384
|
+
.markdy-visual[data-tone="rose"] { --accent: #fb7185; --accent-2: #f59e0b; }
|
|
385
|
+
.markdy-visual__top {
|
|
386
|
+
display: flex;
|
|
387
|
+
align-items: center;
|
|
388
|
+
justify-content: space-between;
|
|
389
|
+
gap: 12px;
|
|
390
|
+
padding: 14px 16px 8px;
|
|
391
|
+
}
|
|
392
|
+
.markdy-visual__title {
|
|
393
|
+
min-width: 0;
|
|
394
|
+
overflow: hidden;
|
|
395
|
+
text-overflow: ellipsis;
|
|
396
|
+
white-space: nowrap;
|
|
397
|
+
font-size: 15px;
|
|
398
|
+
font-weight: 820;
|
|
399
|
+
letter-spacing: 0.01em;
|
|
400
|
+
color: #f8fafc;
|
|
401
|
+
}
|
|
402
|
+
.markdy-visual__pill {
|
|
403
|
+
flex: 0 0 auto;
|
|
404
|
+
border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent);
|
|
405
|
+
border-radius: 999px;
|
|
406
|
+
padding: 4px 8px;
|
|
407
|
+
color: #bae6fd;
|
|
408
|
+
background: rgba(8, 47, 73, 0.54);
|
|
409
|
+
font-size: 10px;
|
|
410
|
+
font-weight: 800;
|
|
411
|
+
letter-spacing: 0.08em;
|
|
412
|
+
text-transform: uppercase;
|
|
413
|
+
box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 22%, transparent);
|
|
414
|
+
}
|
|
415
|
+
.markdy-visual__body { position: absolute; inset: 48px 14px 14px; }
|
|
416
|
+
.markdy-visual--metric {
|
|
417
|
+
width: 112px;
|
|
418
|
+
height: 44px;
|
|
419
|
+
border-radius: 14px;
|
|
420
|
+
}
|
|
421
|
+
.markdy-visual--grid {
|
|
422
|
+
width: 190px;
|
|
423
|
+
height: 92px;
|
|
424
|
+
border-radius: 18px;
|
|
425
|
+
}
|
|
426
|
+
.markdy-visual--lane {
|
|
427
|
+
width: 300px;
|
|
428
|
+
height: 44px;
|
|
429
|
+
border-radius: 999px;
|
|
430
|
+
}
|
|
431
|
+
.markdy-visual--marker {
|
|
432
|
+
width: 50px;
|
|
433
|
+
height: 24px;
|
|
434
|
+
border-radius: 999px;
|
|
435
|
+
}
|
|
436
|
+
.markdy-visual--token_strip {
|
|
437
|
+
width: 300px;
|
|
438
|
+
height: 54px;
|
|
439
|
+
border-radius: 16px;
|
|
440
|
+
}
|
|
441
|
+
.markdy-visual--glyph_card {
|
|
442
|
+
width: 98px;
|
|
443
|
+
height: 120px;
|
|
444
|
+
border-radius: 22px;
|
|
445
|
+
}
|
|
446
|
+
.visual-panel__scan {
|
|
447
|
+
position: absolute;
|
|
448
|
+
left: 18px;
|
|
449
|
+
top: 18px;
|
|
450
|
+
width: 92px;
|
|
451
|
+
height: 122px;
|
|
452
|
+
border-radius: 24px;
|
|
453
|
+
border: 1px solid color-mix(in srgb, var(--accent) 54%, transparent);
|
|
454
|
+
background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 22%, transparent), transparent);
|
|
455
|
+
box-shadow: 0 0 24px color-mix(in srgb, var(--accent) 28%, transparent);
|
|
456
|
+
animation: markdyScanPulse 1.8s ease-in-out infinite;
|
|
457
|
+
}
|
|
458
|
+
.visual-panel__rails {
|
|
459
|
+
position: absolute;
|
|
460
|
+
left: 18px;
|
|
461
|
+
right: 18px;
|
|
462
|
+
bottom: 42px;
|
|
463
|
+
height: 48px;
|
|
464
|
+
border-radius: 999px;
|
|
465
|
+
background:
|
|
466
|
+
repeating-linear-gradient(90deg, rgba(226, 232, 240, 0.74) 0 18px, transparent 18px 34px) center / 100% 3px no-repeat,
|
|
467
|
+
linear-gradient(90deg, rgba(15, 23, 42, 0.96), rgba(30, 41, 59, 0.88));
|
|
468
|
+
box-shadow: 0 10px 28px rgba(2, 6, 23, 0.28) inset;
|
|
469
|
+
}
|
|
470
|
+
.visual-panel__pulse {
|
|
471
|
+
position: absolute;
|
|
472
|
+
left: 110px;
|
|
473
|
+
right: 78px;
|
|
474
|
+
bottom: 64px;
|
|
475
|
+
height: 3px;
|
|
476
|
+
border-radius: 999px;
|
|
477
|
+
background: linear-gradient(90deg, transparent, var(--accent-2), var(--accent), transparent);
|
|
478
|
+
filter: drop-shadow(0 0 8px var(--accent-2));
|
|
479
|
+
animation: markdyRouteFlow 1.5s linear infinite;
|
|
480
|
+
}
|
|
481
|
+
.visual-panel__cells {
|
|
482
|
+
position: absolute;
|
|
483
|
+
right: 18px;
|
|
484
|
+
top: 22px;
|
|
485
|
+
display: grid;
|
|
486
|
+
grid-template-columns: repeat(4, 42px);
|
|
487
|
+
gap: 7px;
|
|
488
|
+
}
|
|
489
|
+
.visual-panel__cell {
|
|
490
|
+
height: 30px;
|
|
491
|
+
border-radius: 10px;
|
|
492
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
493
|
+
background: rgba(15, 23, 42, 0.7);
|
|
494
|
+
box-shadow: 0 0 0 1px rgba(255,255,255,0.035) inset;
|
|
495
|
+
}
|
|
496
|
+
.visual-panel__cell:nth-child(3),
|
|
497
|
+
.visual-panel__cell:nth-child(6),
|
|
498
|
+
.visual-panel__cell:nth-child(8) {
|
|
499
|
+
border-color: color-mix(in srgb, var(--accent-2) 78%, transparent);
|
|
500
|
+
background: color-mix(in srgb, var(--accent-2) 35%, rgba(15, 23, 42, 0.7));
|
|
501
|
+
animation: markdySlotGlow 1.7s ease-in-out infinite;
|
|
502
|
+
}
|
|
503
|
+
.visual-terminal__chrome {
|
|
504
|
+
height: 28px;
|
|
505
|
+
padding-left: 12px;
|
|
506
|
+
display: flex;
|
|
507
|
+
align-items: center;
|
|
508
|
+
gap: 6px;
|
|
509
|
+
border-bottom: 1px solid rgba(148, 163, 184, 0.16);
|
|
510
|
+
}
|
|
511
|
+
.visual-terminal__chrome span {
|
|
512
|
+
width: 8px;
|
|
513
|
+
height: 8px;
|
|
514
|
+
border-radius: 999px;
|
|
515
|
+
background: #ef4444;
|
|
516
|
+
box-shadow: 14px 0 #f59e0b, 28px 0 #22c55e;
|
|
517
|
+
}
|
|
518
|
+
.visual-terminal__lines {
|
|
519
|
+
position: absolute;
|
|
520
|
+
left: 16px;
|
|
521
|
+
right: 16px;
|
|
522
|
+
top: 46px;
|
|
523
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
524
|
+
font-size: 18px;
|
|
525
|
+
line-height: 1.55;
|
|
526
|
+
color: color-mix(in srgb, var(--accent-2) 62%, #f8fafc);
|
|
527
|
+
text-shadow: 0 0 12px color-mix(in srgb, var(--accent-2) 38%, transparent);
|
|
528
|
+
}
|
|
529
|
+
.visual-terminal__line {
|
|
530
|
+
overflow: hidden;
|
|
531
|
+
text-overflow: ellipsis;
|
|
532
|
+
white-space: nowrap;
|
|
533
|
+
}
|
|
534
|
+
.visual-metric {
|
|
535
|
+
height: 100%;
|
|
536
|
+
padding: 7px 9px;
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
justify-content: center;
|
|
540
|
+
gap: 6px;
|
|
541
|
+
min-width: 0;
|
|
542
|
+
overflow: hidden;
|
|
543
|
+
white-space: nowrap;
|
|
544
|
+
}
|
|
545
|
+
.visual-metric strong {
|
|
546
|
+
flex: 0 0 auto;
|
|
547
|
+
color: #f8fafc;
|
|
548
|
+
font-size: 15px;
|
|
549
|
+
line-height: 1;
|
|
550
|
+
}
|
|
551
|
+
.visual-metric span {
|
|
552
|
+
min-width: 0;
|
|
553
|
+
overflow: hidden;
|
|
554
|
+
color: #94a3b8;
|
|
555
|
+
font-size: 8px;
|
|
556
|
+
font-weight: 840;
|
|
557
|
+
letter-spacing: 0.08em;
|
|
558
|
+
line-height: 1;
|
|
559
|
+
text-overflow: ellipsis;
|
|
560
|
+
text-transform: uppercase;
|
|
561
|
+
}
|
|
562
|
+
.visual-grid {
|
|
563
|
+
position: absolute;
|
|
564
|
+
inset: 10px;
|
|
565
|
+
display: grid;
|
|
566
|
+
gap: 7px;
|
|
567
|
+
}
|
|
568
|
+
.visual-grid__cell {
|
|
569
|
+
border-radius: 8px;
|
|
570
|
+
border: 1px solid rgba(148, 163, 184, 0.3);
|
|
571
|
+
background: rgba(15, 23, 42, 0.74);
|
|
572
|
+
}
|
|
573
|
+
.visual-grid__cell.is-active {
|
|
574
|
+
border-color: color-mix(in srgb, var(--accent-2) 78%, transparent);
|
|
575
|
+
background: color-mix(in srgb, var(--accent-2) 35%, rgba(15, 23, 42, 0.74));
|
|
576
|
+
box-shadow: 0 0 18px color-mix(in srgb, var(--accent-2) 26%, transparent);
|
|
577
|
+
}
|
|
578
|
+
.visual-lane {
|
|
579
|
+
position: absolute;
|
|
580
|
+
inset: 0;
|
|
581
|
+
border-radius: inherit;
|
|
582
|
+
background:
|
|
583
|
+
repeating-linear-gradient(90deg, rgba(226, 232, 240, 0.76) 0 18px, transparent 18px 34px) center / 100% 3px no-repeat,
|
|
584
|
+
linear-gradient(90deg, rgba(15, 23, 42, 0.96), rgba(30, 41, 59, 0.88));
|
|
585
|
+
box-shadow: 0 10px 28px rgba(2, 6, 23, 0.28) inset, 0 0 18px color-mix(in srgb, var(--accent) 22%, transparent);
|
|
586
|
+
}
|
|
587
|
+
.visual-lane::after {
|
|
588
|
+
content: "";
|
|
589
|
+
position: absolute;
|
|
590
|
+
left: 18%;
|
|
591
|
+
right: 18%;
|
|
592
|
+
top: 50%;
|
|
593
|
+
height: 3px;
|
|
594
|
+
border-radius: 999px;
|
|
595
|
+
transform: translateY(-50%);
|
|
596
|
+
background: linear-gradient(90deg, transparent, var(--accent-2), var(--accent), transparent);
|
|
597
|
+
filter: drop-shadow(0 0 8px var(--accent-2));
|
|
598
|
+
animation: markdyRouteFlow 1.5s linear infinite;
|
|
599
|
+
}
|
|
600
|
+
.visual-marker {
|
|
601
|
+
position: absolute;
|
|
602
|
+
inset: 0;
|
|
603
|
+
border-radius: inherit;
|
|
604
|
+
background: linear-gradient(90deg, var(--accent), #818cf8);
|
|
605
|
+
box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 55%, transparent), 0 8px 18px rgba(2, 6, 23, 0.35);
|
|
606
|
+
}
|
|
607
|
+
.visual-marker::before,
|
|
608
|
+
.visual-marker::after {
|
|
609
|
+
content: "";
|
|
610
|
+
position: absolute;
|
|
611
|
+
bottom: -3px;
|
|
612
|
+
width: 8px;
|
|
613
|
+
height: 8px;
|
|
614
|
+
border-radius: 999px;
|
|
615
|
+
background: #020617;
|
|
616
|
+
border: 2px solid #cbd5e1;
|
|
617
|
+
}
|
|
618
|
+
.visual-marker::before { left: 9px; }
|
|
619
|
+
.visual-marker::after { right: 9px; }
|
|
620
|
+
.visual-tokens {
|
|
621
|
+
height: 100%;
|
|
622
|
+
padding: 8px;
|
|
623
|
+
display: flex;
|
|
624
|
+
align-items: center;
|
|
625
|
+
gap: 7px;
|
|
626
|
+
}
|
|
627
|
+
.visual-token {
|
|
628
|
+
flex: 1 1 0;
|
|
629
|
+
min-width: 0;
|
|
630
|
+
border: 1px solid rgba(148, 163, 184, 0.25);
|
|
631
|
+
border-radius: 12px;
|
|
632
|
+
padding: 8px 10px;
|
|
633
|
+
background: rgba(2, 6, 23, 0.42);
|
|
634
|
+
color: color-mix(in srgb, var(--accent) 45%, #f8fafc);
|
|
635
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
636
|
+
font-size: 12px;
|
|
637
|
+
font-weight: 800;
|
|
638
|
+
overflow: hidden;
|
|
639
|
+
text-overflow: ellipsis;
|
|
640
|
+
white-space: nowrap;
|
|
641
|
+
}
|
|
642
|
+
.visual-glyph {
|
|
643
|
+
height: 100%;
|
|
644
|
+
display: grid;
|
|
645
|
+
place-items: center;
|
|
646
|
+
padding: 10px;
|
|
647
|
+
text-align: center;
|
|
648
|
+
}
|
|
649
|
+
.visual-glyph strong {
|
|
650
|
+
color: #f8fafc;
|
|
651
|
+
font-size: 56px;
|
|
652
|
+
line-height: 1;
|
|
653
|
+
}
|
|
654
|
+
.visual-glyph span {
|
|
655
|
+
margin-top: 6px;
|
|
656
|
+
color: #cbd5e1;
|
|
657
|
+
font-size: 10px;
|
|
658
|
+
font-weight: 800;
|
|
659
|
+
letter-spacing: 0.08em;
|
|
660
|
+
text-transform: uppercase;
|
|
661
|
+
}
|
|
662
|
+
@keyframes markdyGridDrift { to { background-position: 34px 34px, 34px 34px; } }
|
|
663
|
+
@keyframes markdyAurora { to { transform: rotate(1turn); } }
|
|
664
|
+
@keyframes markdyScanPulse { 50% { transform: translateX(18px); opacity: 0.72; } }
|
|
665
|
+
@keyframes markdySlotGlow { 50% { box-shadow: 0 0 20px color-mix(in srgb, var(--accent-2) 32%, transparent); } }
|
|
666
|
+
@keyframes markdyRouteFlow { to { filter: hue-rotate(70deg) drop-shadow(0 0 9px var(--accent)); } }
|
|
667
|
+
`;
|
|
668
|
+
doc.head.appendChild(style);
|
|
669
|
+
}
|
|
670
|
+
function escapeHtml(value) {
|
|
671
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
672
|
+
}
|
|
673
|
+
function toneArg(def, index, fallback = "cyan") {
|
|
674
|
+
const tone = def.args[index]?.trim().toLowerCase() || fallback;
|
|
675
|
+
return /^(cyan|green|amber|purple|rose)$/.test(tone) ? tone : fallback;
|
|
676
|
+
}
|
|
677
|
+
function parseGridSpec(spec) {
|
|
678
|
+
const match = /^(\d+)x(\d+)$/i.exec(spec?.trim() ?? "");
|
|
679
|
+
const cols = match ? Number(match[1]) : 4;
|
|
680
|
+
const rows = match ? Number(match[2]) : 2;
|
|
681
|
+
return {
|
|
682
|
+
cols: Math.min(Math.max(cols, 1), 8),
|
|
683
|
+
rows: Math.min(Math.max(rows, 1), 4)
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function activeCellSet(value) {
|
|
687
|
+
return new Set(
|
|
688
|
+
(value ?? "").split(/[|\s]+/).map((part) => Number(part.trim())).filter((part) => Number.isInteger(part) && part > 0)
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
function canonicalVisualType(type) {
|
|
692
|
+
switch (type) {
|
|
693
|
+
case "surface":
|
|
694
|
+
return "panel";
|
|
695
|
+
case "stat":
|
|
696
|
+
return "metric";
|
|
697
|
+
case "matrix":
|
|
698
|
+
return "grid";
|
|
699
|
+
case "track":
|
|
700
|
+
return "lane";
|
|
701
|
+
case "dot":
|
|
702
|
+
return "marker";
|
|
703
|
+
case "chips":
|
|
704
|
+
return "token_strip";
|
|
705
|
+
case "glyph":
|
|
706
|
+
return "glyph_card";
|
|
707
|
+
case "parking_map":
|
|
708
|
+
case "game_scene":
|
|
709
|
+
case "byte_viz":
|
|
710
|
+
return "panel";
|
|
711
|
+
case "ascii_map":
|
|
712
|
+
return "terminal";
|
|
713
|
+
default:
|
|
714
|
+
return type;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function legacyVisualArgs(type, args) {
|
|
718
|
+
const label = args[0] || type.replace("_", " ");
|
|
719
|
+
switch (type) {
|
|
720
|
+
case "parking_map":
|
|
721
|
+
return [label, "live ops", "cyan"];
|
|
722
|
+
case "ascii_map":
|
|
723
|
+
return [label, "[P1][P2][ ][EV]", "[IN]===LANE===[OUT]", "0101 0000 0100 0001", "green"];
|
|
724
|
+
case "game_scene":
|
|
725
|
+
return [label, "60 fps", "amber"];
|
|
726
|
+
case "byte_viz":
|
|
727
|
+
return [label, "UTF-8", "purple"];
|
|
728
|
+
default:
|
|
729
|
+
return args;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function createVisualPrimitiveEl(type, def) {
|
|
733
|
+
ensureVisualPrimitiveStyles(document);
|
|
734
|
+
const visualType = canonicalVisualType(type);
|
|
735
|
+
const args = legacyVisualArgs(type, def.args);
|
|
736
|
+
const label = args[0] || type.replace("_", " ");
|
|
737
|
+
const root = document.createElement("div");
|
|
738
|
+
root.className = `markdy-visual markdy-visual--${visualType}`;
|
|
739
|
+
root.dataset.visualType = visualType;
|
|
740
|
+
root.setAttribute("role", "img");
|
|
741
|
+
root.setAttribute("aria-label", label);
|
|
742
|
+
if (visualType === "panel") {
|
|
743
|
+
const tag = escapeHtml(args[1] || "live");
|
|
744
|
+
root.dataset.tone = toneArg({ ...def, args }, 2);
|
|
745
|
+
root.innerHTML = `
|
|
746
|
+
<div class="markdy-visual__top"><div class="markdy-visual__title">${escapeHtml(label)}</div><div class="markdy-visual__pill">${tag}</div></div>
|
|
747
|
+
<div class="markdy-visual__body">
|
|
748
|
+
<div class="visual-panel__scan"></div>
|
|
749
|
+
<div class="visual-panel__cells">${Array.from({ length: 8 }, () => '<span class="visual-panel__cell"></span>').join("")}</div>
|
|
750
|
+
<div class="visual-panel__rails"></div>
|
|
751
|
+
<div class="visual-panel__pulse"></div>
|
|
752
|
+
</div>`;
|
|
753
|
+
} else if (visualType === "terminal") {
|
|
754
|
+
root.dataset.tone = toneArg({ ...def, args }, 4, "green");
|
|
755
|
+
const lines = [args[1], args[2], args[3]].filter((line) => typeof line === "string" && line.length > 0).map((line) => `<div class="visual-terminal__line">${escapeHtml(line)}</div>`).join("");
|
|
756
|
+
root.innerHTML = `
|
|
757
|
+
<div class="markdy-visual__top"><div class="markdy-visual__title">${escapeHtml(label)}</div><div class="markdy-visual__pill">terminal</div></div>
|
|
758
|
+
<div class="markdy-visual__body"><div class="visual-terminal__chrome"><span></span></div><div class="visual-terminal__lines">${lines}</div></div>`;
|
|
759
|
+
} else if (visualType === "metric") {
|
|
760
|
+
root.dataset.tone = toneArg({ ...def, args }, 2);
|
|
761
|
+
root.innerHTML = `<div class="visual-metric"><strong>${escapeHtml(args[1] || "\u2014")}</strong><span>${escapeHtml(label)}</span></div>`;
|
|
762
|
+
} else if (visualType === "grid") {
|
|
763
|
+
root.dataset.tone = toneArg({ ...def, args }, 3, "green");
|
|
764
|
+
const { cols, rows } = parseGridSpec(args[1]);
|
|
765
|
+
const active = activeCellSet(args[2]);
|
|
766
|
+
root.innerHTML = `<div class="visual-grid" style="grid-template-columns: repeat(${cols}, 1fr);">${Array.from({ length: cols * rows }, (_, i) => `<span class="visual-grid__cell${active.has(i + 1) ? " is-active" : ""}"></span>`).join("")}</div>`;
|
|
767
|
+
} else if (visualType === "lane") {
|
|
768
|
+
root.dataset.tone = toneArg({ ...def, args }, 1);
|
|
769
|
+
root.innerHTML = `<div class="visual-lane"></div>`;
|
|
770
|
+
} else if (visualType === "marker") {
|
|
771
|
+
root.dataset.tone = toneArg({ ...def, args }, 1);
|
|
772
|
+
root.innerHTML = `<div class="visual-marker"></div>`;
|
|
773
|
+
} else if (visualType === "token_strip") {
|
|
774
|
+
root.dataset.tone = toneArg({ ...def, args }, 1, "purple");
|
|
775
|
+
const tokens = (args[0] || "token").split("|").map((token) => token.trim()).filter(Boolean);
|
|
776
|
+
root.innerHTML = `<div class="visual-tokens">${tokens.map((token) => `<span class="visual-token">${escapeHtml(token)}</span>`).join("")}</div>`;
|
|
777
|
+
} else {
|
|
778
|
+
root.dataset.tone = toneArg({ ...def, args }, 2, "purple");
|
|
779
|
+
root.innerHTML = `<div class="visual-glyph"><div><strong>${escapeHtml(args[0] || "A")}</strong><span>${escapeHtml(args[1] || "glyph")}</span></div></div>`;
|
|
780
|
+
}
|
|
781
|
+
return root;
|
|
782
|
+
}
|
|
783
|
+
function ensureArchitectureStyles(doc) {
|
|
784
|
+
if (doc.getElementById(ARCHITECTURE_STYLE_ID)) return;
|
|
785
|
+
const style = doc.createElement("style");
|
|
786
|
+
style.id = ARCHITECTURE_STYLE_ID;
|
|
787
|
+
style.textContent = `
|
|
788
|
+
.markdy-arch-node {
|
|
789
|
+
--markdy-node-accent: #38bdf8;
|
|
790
|
+
--markdy-node-accent-2: #22c55e;
|
|
791
|
+
--markdy-node-surface: rgba(15, 23, 42, 0.82);
|
|
792
|
+
--markdy-node-border: rgba(148, 163, 184, 0.34);
|
|
793
|
+
--markdy-node-glow: rgba(56, 189, 248, 0.24);
|
|
794
|
+
position: relative;
|
|
795
|
+
isolation: isolate;
|
|
796
|
+
width: 184px;
|
|
797
|
+
min-height: 88px;
|
|
798
|
+
box-sizing: border-box;
|
|
799
|
+
display: grid;
|
|
800
|
+
grid-template-columns: 34px minmax(0, 1fr);
|
|
801
|
+
align-items: center;
|
|
802
|
+
gap: 10px;
|
|
803
|
+
padding: 13px 14px;
|
|
804
|
+
border: 1px solid var(--markdy-node-border);
|
|
805
|
+
border-radius: 14px;
|
|
806
|
+
color: #e5eefb;
|
|
807
|
+
background:
|
|
808
|
+
linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.02) 34%, rgba(15, 23, 42, 0.88) 100%),
|
|
809
|
+
radial-gradient(circle at 18% 0%, color-mix(in srgb, var(--markdy-node-accent) 34%, transparent), transparent 42%),
|
|
810
|
+
var(--markdy-node-surface);
|
|
811
|
+
box-shadow:
|
|
812
|
+
0 16px 34px rgba(2, 6, 23, 0.28),
|
|
813
|
+
0 0 0 1px rgba(255, 255, 255, 0.04) inset,
|
|
814
|
+
0 1px 0 rgba(255, 255, 255, 0.12) inset,
|
|
815
|
+
0 0 28px var(--markdy-node-glow);
|
|
816
|
+
overflow: hidden;
|
|
817
|
+
contain: layout paint style;
|
|
818
|
+
backdrop-filter: blur(14px) saturate(1.18);
|
|
819
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.18);
|
|
820
|
+
}
|
|
821
|
+
.markdy-arch-node::before {
|
|
822
|
+
content: "";
|
|
823
|
+
position: absolute;
|
|
824
|
+
inset: 0;
|
|
825
|
+
z-index: -1;
|
|
826
|
+
background:
|
|
827
|
+
linear-gradient(90deg, transparent 0 24%, rgba(255, 255, 255, 0.06) 50%, transparent 76%) -180px 0 / 180px 100% no-repeat,
|
|
828
|
+
repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.055) 0 1px, transparent 1px 10px);
|
|
829
|
+
opacity: 0.6;
|
|
830
|
+
}
|
|
831
|
+
.markdy-arch-node::after {
|
|
832
|
+
content: "";
|
|
833
|
+
position: absolute;
|
|
834
|
+
left: 14px;
|
|
835
|
+
right: 14px;
|
|
836
|
+
bottom: 9px;
|
|
837
|
+
height: 2px;
|
|
838
|
+
border-radius: 999px;
|
|
839
|
+
background: linear-gradient(90deg, transparent, var(--markdy-node-accent), var(--markdy-node-accent-2), transparent);
|
|
840
|
+
opacity: 0.82;
|
|
841
|
+
filter: drop-shadow(0 0 6px var(--markdy-node-accent));
|
|
842
|
+
}
|
|
843
|
+
.markdy-arch-node__icon {
|
|
844
|
+
position: relative;
|
|
845
|
+
width: 32px;
|
|
846
|
+
height: 32px;
|
|
847
|
+
border-radius: 11px;
|
|
848
|
+
background:
|
|
849
|
+
radial-gradient(circle at 35% 25%, rgba(255, 255, 255, 0.28), transparent 42%),
|
|
850
|
+
linear-gradient(145deg, var(--markdy-node-accent), color-mix(in srgb, var(--markdy-node-accent) 38%, #020617));
|
|
851
|
+
box-shadow:
|
|
852
|
+
0 0 0 1px rgba(255, 255, 255, 0.16) inset,
|
|
853
|
+
0 10px 20px color-mix(in srgb, var(--markdy-node-accent) 25%, transparent);
|
|
854
|
+
}
|
|
855
|
+
.markdy-arch-node__icon::before,
|
|
856
|
+
.markdy-arch-node__icon::after {
|
|
857
|
+
content: "";
|
|
858
|
+
position: absolute;
|
|
859
|
+
box-sizing: border-box;
|
|
860
|
+
border-color: rgba(255, 255, 255, 0.9);
|
|
861
|
+
}
|
|
862
|
+
.markdy-arch-node__label {
|
|
863
|
+
min-width: 0;
|
|
864
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
865
|
+
font-size: 15px;
|
|
866
|
+
font-weight: 720;
|
|
867
|
+
line-height: 1.08;
|
|
868
|
+
color: #f8fafc;
|
|
869
|
+
text-shadow: 0 1px 12px rgba(15, 23, 42, 0.8);
|
|
870
|
+
white-space: nowrap;
|
|
871
|
+
overflow: hidden;
|
|
872
|
+
text-overflow: ellipsis;
|
|
873
|
+
}
|
|
874
|
+
.markdy-arch-node__type {
|
|
875
|
+
display: block;
|
|
876
|
+
margin-top: 5px;
|
|
877
|
+
font-size: 9px;
|
|
878
|
+
font-weight: 740;
|
|
879
|
+
letter-spacing: 0.08em;
|
|
880
|
+
text-transform: uppercase;
|
|
881
|
+
color: color-mix(in srgb, var(--markdy-node-accent) 70%, #e2e8f0);
|
|
882
|
+
opacity: 0.86;
|
|
883
|
+
}
|
|
884
|
+
.markdy-arch-node[data-markdy-system-kind="compute"],
|
|
885
|
+
.markdy-arch-node[data-markdy-system-kind="code"] {
|
|
886
|
+
--markdy-node-accent: #38bdf8;
|
|
887
|
+
--markdy-node-accent-2: #818cf8;
|
|
888
|
+
}
|
|
889
|
+
.markdy-arch-node[data-markdy-system-kind="client"] {
|
|
890
|
+
--markdy-node-accent: #f59e0b;
|
|
891
|
+
--markdy-node-accent-2: #fb7185;
|
|
892
|
+
}
|
|
893
|
+
.markdy-arch-node[data-markdy-system-kind="data"] {
|
|
894
|
+
--markdy-node-accent: #22c55e;
|
|
895
|
+
--markdy-node-accent-2: #14b8a6;
|
|
896
|
+
}
|
|
897
|
+
.markdy-arch-node[data-markdy-system-kind="messaging"] {
|
|
898
|
+
--markdy-node-accent: #a78bfa;
|
|
899
|
+
--markdy-node-accent-2: #38bdf8;
|
|
900
|
+
}
|
|
901
|
+
.markdy-arch-node[data-markdy-system-kind="network"] {
|
|
902
|
+
--markdy-node-accent: #60a5fa;
|
|
903
|
+
--markdy-node-accent-2: #67e8f9;
|
|
904
|
+
}
|
|
905
|
+
.markdy-arch-node[data-markdy-system-kind="platform"] {
|
|
906
|
+
--markdy-node-accent: #c084fc;
|
|
907
|
+
--markdy-node-accent-2: #f472b6;
|
|
908
|
+
}
|
|
909
|
+
.markdy-arch-node[data-markdy-system-kind="security"] {
|
|
910
|
+
--markdy-node-accent: #fb7185;
|
|
911
|
+
--markdy-node-accent-2: #f59e0b;
|
|
912
|
+
}
|
|
913
|
+
.markdy-arch-node[data-markdy-system-kind="delivery"] {
|
|
914
|
+
--markdy-node-accent: #facc15;
|
|
915
|
+
--markdy-node-accent-2: #22c55e;
|
|
916
|
+
}
|
|
917
|
+
.markdy-arch-node[data-markdy-system-kind="observability"] {
|
|
918
|
+
--markdy-node-accent: #2dd4bf;
|
|
919
|
+
--markdy-node-accent-2: #38bdf8;
|
|
920
|
+
}
|
|
921
|
+
.markdy-arch-node[data-markdy-system-kind="flow"] {
|
|
922
|
+
--markdy-node-accent: #e879f9;
|
|
923
|
+
--markdy-node-accent-2: #a78bfa;
|
|
924
|
+
}
|
|
925
|
+
.markdy-arch-node[data-markdy-system-kind="distributed"] {
|
|
926
|
+
--markdy-node-accent: #94a3b8;
|
|
927
|
+
--markdy-node-accent-2: #38bdf8;
|
|
928
|
+
}
|
|
929
|
+
.markdy-arch-node[data-markdy-system-kind="data"] {
|
|
930
|
+
border-radius: 18px 18px 24px 24px;
|
|
931
|
+
}
|
|
932
|
+
.markdy-arch-node[data-markdy-system-kind="network"] {
|
|
933
|
+
border-radius: 999px;
|
|
934
|
+
}
|
|
935
|
+
.markdy-arch-node[data-markdy-system-kind="platform"] {
|
|
936
|
+
border-radius: 10px;
|
|
937
|
+
}
|
|
938
|
+
.markdy-arch-node[data-markdy-system-kind="security"] {
|
|
939
|
+
border-style: solid;
|
|
940
|
+
}
|
|
941
|
+
.markdy-arch-node[data-markdy-system-kind="compute"] .markdy-arch-node__icon::before,
|
|
942
|
+
.markdy-arch-node[data-markdy-system-kind="code"] .markdy-arch-node__icon::before {
|
|
943
|
+
inset: 9px 7px;
|
|
944
|
+
border-top: 3px solid rgba(255, 255, 255, 0.92);
|
|
945
|
+
border-bottom: 3px solid rgba(255, 255, 255, 0.92);
|
|
946
|
+
}
|
|
947
|
+
.markdy-arch-node[data-markdy-system-kind="compute"] .markdy-arch-node__icon::after,
|
|
948
|
+
.markdy-arch-node[data-markdy-system-kind="code"] .markdy-arch-node__icon::after {
|
|
949
|
+
inset: 14px 7px auto;
|
|
950
|
+
border-top: 3px solid rgba(255, 255, 255, 0.92);
|
|
951
|
+
}
|
|
952
|
+
.markdy-arch-node[data-markdy-system-kind="client"] .markdy-arch-node__icon::before {
|
|
953
|
+
left: 8px;
|
|
954
|
+
right: 8px;
|
|
955
|
+
top: 8px;
|
|
956
|
+
height: 13px;
|
|
957
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
958
|
+
border-radius: 3px;
|
|
959
|
+
}
|
|
960
|
+
.markdy-arch-node[data-markdy-system-kind="client"] .markdy-arch-node__icon::after {
|
|
961
|
+
left: 12px;
|
|
962
|
+
right: 12px;
|
|
963
|
+
bottom: 7px;
|
|
964
|
+
border-top: 2px solid rgba(255, 255, 255, 0.92);
|
|
965
|
+
}
|
|
966
|
+
.markdy-arch-node[data-markdy-system-kind="data"] .markdy-arch-node__icon::before {
|
|
967
|
+
inset: 7px 7px 9px;
|
|
968
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
969
|
+
border-radius: 50% / 16%;
|
|
970
|
+
}
|
|
971
|
+
.markdy-arch-node[data-markdy-system-kind="data"] .markdy-arch-node__icon::after {
|
|
972
|
+
left: 8px;
|
|
973
|
+
right: 8px;
|
|
974
|
+
top: 12px;
|
|
975
|
+
border-top: 2px solid rgba(255, 255, 255, 0.72);
|
|
976
|
+
}
|
|
977
|
+
.markdy-arch-node[data-markdy-system-kind="messaging"] .markdy-arch-node__icon::before {
|
|
978
|
+
inset: 9px 8px;
|
|
979
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
980
|
+
border-radius: 999px;
|
|
981
|
+
}
|
|
982
|
+
.markdy-arch-node[data-markdy-system-kind="messaging"] .markdy-arch-node__icon::after {
|
|
983
|
+
left: 13px;
|
|
984
|
+
top: 9px;
|
|
985
|
+
width: 8px;
|
|
986
|
+
height: 14px;
|
|
987
|
+
border-left: 2px solid rgba(255, 255, 255, 0.82);
|
|
988
|
+
border-right: 2px solid rgba(255, 255, 255, 0.82);
|
|
989
|
+
}
|
|
990
|
+
.markdy-arch-node[data-markdy-system-kind="network"] .markdy-arch-node__icon::before {
|
|
991
|
+
left: 7px;
|
|
992
|
+
right: 7px;
|
|
993
|
+
bottom: 9px;
|
|
994
|
+
height: 12px;
|
|
995
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
996
|
+
border-radius: 999px;
|
|
997
|
+
}
|
|
998
|
+
.markdy-arch-node[data-markdy-system-kind="network"] .markdy-arch-node__icon::after {
|
|
999
|
+
left: 10px;
|
|
1000
|
+
top: 7px;
|
|
1001
|
+
width: 13px;
|
|
1002
|
+
height: 13px;
|
|
1003
|
+
border-top: 2px solid rgba(255, 255, 255, 0.92);
|
|
1004
|
+
border-left: 2px solid rgba(255, 255, 255, 0.92);
|
|
1005
|
+
border-radius: 999px 0 0 0;
|
|
1006
|
+
}
|
|
1007
|
+
.markdy-arch-node[data-markdy-system-kind="platform"] .markdy-arch-node__icon::before {
|
|
1008
|
+
inset: 8px;
|
|
1009
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
1010
|
+
border-radius: 4px;
|
|
1011
|
+
}
|
|
1012
|
+
.markdy-arch-node[data-markdy-system-kind="platform"] .markdy-arch-node__icon::after {
|
|
1013
|
+
left: 10px;
|
|
1014
|
+
right: 10px;
|
|
1015
|
+
top: 14px;
|
|
1016
|
+
border-top: 2px solid rgba(255, 255, 255, 0.72);
|
|
1017
|
+
}
|
|
1018
|
+
.markdy-arch-node[data-markdy-system-kind="security"] .markdy-arch-node__icon::before {
|
|
1019
|
+
left: 9px;
|
|
1020
|
+
right: 9px;
|
|
1021
|
+
top: 7px;
|
|
1022
|
+
bottom: 7px;
|
|
1023
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
1024
|
+
border-radius: 10px 10px 14px 14px;
|
|
1025
|
+
}
|
|
1026
|
+
.markdy-arch-node[data-markdy-system-kind="security"] .markdy-arch-node__icon::after {
|
|
1027
|
+
left: 14px;
|
|
1028
|
+
right: 14px;
|
|
1029
|
+
top: 14px;
|
|
1030
|
+
border-top: 2px solid rgba(255, 255, 255, 0.82);
|
|
1031
|
+
}
|
|
1032
|
+
.markdy-arch-node[data-markdy-system-kind="delivery"] .markdy-arch-node__icon::before,
|
|
1033
|
+
.markdy-arch-node[data-markdy-system-kind="observability"] .markdy-arch-node__icon::before,
|
|
1034
|
+
.markdy-arch-node[data-markdy-system-kind="distributed"] .markdy-arch-node__icon::before {
|
|
1035
|
+
inset: 8px;
|
|
1036
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
1037
|
+
border-radius: 999px;
|
|
1038
|
+
}
|
|
1039
|
+
.markdy-arch-node[data-markdy-system-kind="delivery"] .markdy-arch-node__icon::after,
|
|
1040
|
+
.markdy-arch-node[data-markdy-system-kind="observability"] .markdy-arch-node__icon::after,
|
|
1041
|
+
.markdy-arch-node[data-markdy-system-kind="distributed"] .markdy-arch-node__icon::after {
|
|
1042
|
+
left: 15px;
|
|
1043
|
+
top: 7px;
|
|
1044
|
+
bottom: 7px;
|
|
1045
|
+
border-left: 2px solid rgba(255, 255, 255, 0.82);
|
|
1046
|
+
}
|
|
1047
|
+
.markdy-arch-node[data-markdy-system-kind="flow"] .markdy-arch-node__icon::before {
|
|
1048
|
+
inset: 8px;
|
|
1049
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
1050
|
+
transform: rotate(45deg);
|
|
1051
|
+
}
|
|
1052
|
+
.markdy-arch-node[data-markdy-system-kind="flow"] .markdy-arch-node__icon::after {
|
|
1053
|
+
left: 14px;
|
|
1054
|
+
right: 14px;
|
|
1055
|
+
top: 15px;
|
|
1056
|
+
border-top: 2px solid rgba(255, 255, 255, 0.82);
|
|
1057
|
+
}
|
|
1058
|
+
`;
|
|
1059
|
+
doc.head.appendChild(style);
|
|
1060
|
+
}
|
|
1061
|
+
function isArchitectureNodeType(type) {
|
|
1062
|
+
return ARCHITECTURE_NODE_TYPES.has(type);
|
|
1063
|
+
}
|
|
1064
|
+
function isVisualPrimitiveType(type) {
|
|
1065
|
+
return VISUAL_PRIMITIVE_TYPE_SET.has(type);
|
|
1066
|
+
}
|
|
1067
|
+
function architectureTypeLabel(type) {
|
|
1068
|
+
if (type === "db") return "database";
|
|
1069
|
+
if (type === "api") return "service";
|
|
1070
|
+
return type;
|
|
1071
|
+
}
|
|
1072
|
+
function architectureNodeKind(type) {
|
|
1073
|
+
return TECHNICAL_NODE_KINDS[type] ?? "compute";
|
|
1074
|
+
}
|
|
312
1075
|
function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
313
1076
|
let el;
|
|
314
1077
|
switch (def.type) {
|
|
@@ -377,75 +1140,43 @@ function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
|
377
1140
|
el = createFigureEl(def);
|
|
378
1141
|
break;
|
|
379
1142
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
1143
|
+
default: {
|
|
1144
|
+
if (VISUAL_PRIMITIVE_TYPE_SET.has(def.type)) {
|
|
1145
|
+
el = createVisualPrimitiveEl(def.type, def);
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
if (!isArchitectureNodeType(def.type)) {
|
|
1149
|
+
const div = document.createElement("div");
|
|
1150
|
+
div.style.width = "100px";
|
|
1151
|
+
div.style.height = "100px";
|
|
1152
|
+
div.style.background = "#999";
|
|
1153
|
+
div.style.boxSizing = "border-box";
|
|
1154
|
+
el = div;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
ensureArchitectureStyles(document);
|
|
384
1158
|
const card = document.createElement("div");
|
|
1159
|
+
card.className = "markdy-arch-node";
|
|
1160
|
+
card.dataset.markdySystemType = def.type;
|
|
1161
|
+
card.dataset.markdySystemKind = architectureNodeKind(def.type);
|
|
1162
|
+
card.setAttribute("role", "img");
|
|
1163
|
+
card.setAttribute("aria-label", `${architectureTypeLabel(def.type)} ${def.args[0] ?? name}`);
|
|
1164
|
+
const icon = document.createElement("span");
|
|
1165
|
+
icon.className = "markdy-arch-node__icon";
|
|
1166
|
+
icon.setAttribute("aria-hidden", "true");
|
|
385
1167
|
const label = document.createElement("div");
|
|
1168
|
+
label.className = "markdy-arch-node__label";
|
|
386
1169
|
label.textContent = def.args[0] ?? "";
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
lineHeight: "1.2",
|
|
394
|
-
pointerEvents: "none",
|
|
395
|
-
whiteSpace: "nowrap",
|
|
396
|
-
overflow: "hidden",
|
|
397
|
-
textOverflow: "ellipsis",
|
|
398
|
-
maxWidth: "100%"
|
|
399
|
-
});
|
|
400
|
-
Object.assign(card.style, {
|
|
401
|
-
width: "180px",
|
|
402
|
-
height: "84px",
|
|
403
|
-
boxSizing: "border-box",
|
|
404
|
-
border: "1px solid #475569",
|
|
405
|
-
background: "#0f172a",
|
|
406
|
-
display: "grid",
|
|
407
|
-
placeItems: "center",
|
|
408
|
-
padding: "10px 12px"
|
|
409
|
-
});
|
|
410
|
-
if (def.type === "service") {
|
|
411
|
-
card.style.borderRadius = "12px";
|
|
412
|
-
card.style.background = "linear-gradient(180deg, #1e293b 0%, #0f172a 100%)";
|
|
413
|
-
} else if (def.type === "client") {
|
|
414
|
-
const bar = document.createElement("div");
|
|
415
|
-
Object.assign(bar.style, {
|
|
416
|
-
position: "absolute",
|
|
417
|
-
top: "8px",
|
|
418
|
-
left: "8px",
|
|
419
|
-
right: "8px",
|
|
420
|
-
height: "8px",
|
|
421
|
-
borderRadius: "6px",
|
|
422
|
-
background: "#334155"
|
|
423
|
-
});
|
|
424
|
-
card.style.position = "relative";
|
|
425
|
-
card.style.borderRadius = "10px";
|
|
426
|
-
card.style.paddingTop = "20px";
|
|
427
|
-
card.appendChild(bar);
|
|
428
|
-
} else if (def.type === "db") {
|
|
429
|
-
card.style.borderRadius = "50% / 14%";
|
|
430
|
-
card.style.background = "linear-gradient(180deg, #334155 0%, #0f172a 70%)";
|
|
431
|
-
card.style.boxShadow = "inset 0 10px 0 rgba(226, 232, 240, 0.12)";
|
|
432
|
-
} else if (def.type === "queue") {
|
|
433
|
-
card.style.borderRadius = "8px";
|
|
434
|
-
card.style.boxShadow = "-6px -6px 0 rgba(30, 41, 59, 0.9), -12px -12px 0 rgba(15, 23, 42, 0.9)";
|
|
435
|
-
}
|
|
1170
|
+
label.style.fontSize = `${def.size ?? 15}px`;
|
|
1171
|
+
const typeLabel = document.createElement("span");
|
|
1172
|
+
typeLabel.className = "markdy-arch-node__type";
|
|
1173
|
+
typeLabel.textContent = architectureTypeLabel(def.type);
|
|
1174
|
+
label.appendChild(typeLabel);
|
|
1175
|
+
card.appendChild(icon);
|
|
436
1176
|
card.appendChild(label);
|
|
437
1177
|
el = card;
|
|
438
1178
|
break;
|
|
439
1179
|
}
|
|
440
|
-
default: {
|
|
441
|
-
const div = document.createElement("div");
|
|
442
|
-
div.style.width = "100px";
|
|
443
|
-
div.style.height = "100px";
|
|
444
|
-
div.style.background = "#999";
|
|
445
|
-
div.style.boxSizing = "border-box";
|
|
446
|
-
el = div;
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
1180
|
}
|
|
450
1181
|
el.dataset.markdyActor = name;
|
|
451
1182
|
el.style.position = "absolute";
|
|
@@ -456,6 +1187,8 @@ function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
|
456
1187
|
el.style.opacity = String(def.opacity ?? 1);
|
|
457
1188
|
if (def.z !== void 0) el.style.zIndex = String(def.z);
|
|
458
1189
|
else if (def.type === "caption") el.style.zIndex = "100";
|
|
1190
|
+
else if (isVisualPrimitiveType(def.type)) el.style.zIndex = "25";
|
|
1191
|
+
else if (isArchitectureNodeType(def.type)) el.style.zIndex = "10";
|
|
459
1192
|
return el;
|
|
460
1193
|
}
|
|
461
1194
|
|
|
@@ -776,27 +1509,190 @@ var bounce = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
|
776
1509
|
anims.push(el.animate(keyframes, { ...baseOpts, easing: "ease-out" }));
|
|
777
1510
|
};
|
|
778
1511
|
|
|
1512
|
+
// src/actions/effects.ts
|
|
1513
|
+
function numeric(value, fallback) {
|
|
1514
|
+
return typeof value === "number" ? value : fallback;
|
|
1515
|
+
}
|
|
1516
|
+
function stringParam(value, fallback) {
|
|
1517
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
1518
|
+
}
|
|
1519
|
+
var spring = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1520
|
+
const to = ev.params.to;
|
|
1521
|
+
const toX = to?.[0] ?? state.x;
|
|
1522
|
+
const toY = to?.[1] ?? state.y;
|
|
1523
|
+
const stiffness = numeric(ev.params.stiffness, 0.18);
|
|
1524
|
+
const overshootX = toX + (toX - state.x) * stiffness;
|
|
1525
|
+
const overshootY = toY + (toY - state.y) * stiffness;
|
|
1526
|
+
anims.push(
|
|
1527
|
+
el.animate(
|
|
1528
|
+
[
|
|
1529
|
+
{ transform: tx2(state), offset: 0 },
|
|
1530
|
+
{ transform: tx2({ ...state, x: overshootX, y: overshootY }), offset: 0.72 },
|
|
1531
|
+
{ transform: tx2({ ...state, x: toX, y: toY }), offset: 1 }
|
|
1532
|
+
],
|
|
1533
|
+
{ ...baseOpts, easing: "cubic-bezier(.2,1.35,.35,1)" }
|
|
1534
|
+
)
|
|
1535
|
+
);
|
|
1536
|
+
state.x = toX;
|
|
1537
|
+
state.y = toY;
|
|
1538
|
+
};
|
|
1539
|
+
var followPath = ({ ev, el, baseOpts, anims }) => {
|
|
1540
|
+
const path = stringParam(ev.params.path, "M 0 0 L 120 0");
|
|
1541
|
+
const rotate2 = ev.params.rotate === false ? "0deg" : "auto";
|
|
1542
|
+
anims.push(
|
|
1543
|
+
el.animate(
|
|
1544
|
+
[
|
|
1545
|
+
{ offsetPath: `path('${path}')`, offsetDistance: "0%", offsetRotate: rotate2 },
|
|
1546
|
+
{ offsetPath: `path('${path}')`, offsetDistance: "100%", offsetRotate: rotate2 }
|
|
1547
|
+
],
|
|
1548
|
+
baseOpts
|
|
1549
|
+
)
|
|
1550
|
+
);
|
|
1551
|
+
};
|
|
1552
|
+
var pulse = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1553
|
+
const amount = numeric(ev.params.amount, 1.08);
|
|
1554
|
+
anims.push(
|
|
1555
|
+
el.animate(
|
|
1556
|
+
[
|
|
1557
|
+
{ transform: tx2(state), offset: 0 },
|
|
1558
|
+
{ transform: tx2({ ...state, scale: state.scale * amount }), offset: 0.48 },
|
|
1559
|
+
{ transform: tx2(state), offset: 1 }
|
|
1560
|
+
],
|
|
1561
|
+
{ ...baseOpts, easing: "ease-in-out" }
|
|
1562
|
+
)
|
|
1563
|
+
);
|
|
1564
|
+
};
|
|
1565
|
+
var glow = ({ ev, el, baseOpts, anims }) => {
|
|
1566
|
+
const color = stringParam(ev.params.color, "#38bdf8");
|
|
1567
|
+
const strength = numeric(ev.params.strength, 24);
|
|
1568
|
+
anims.push(
|
|
1569
|
+
el.animate(
|
|
1570
|
+
[
|
|
1571
|
+
{ filter: "drop-shadow(0 0 0 transparent)", boxShadow: "0 0 0 rgba(0,0,0,0)" },
|
|
1572
|
+
{ filter: `drop-shadow(0 0 ${strength}px ${color})`, boxShadow: `0 0 ${strength}px ${color}` },
|
|
1573
|
+
{ filter: "drop-shadow(0 0 0 transparent)", boxShadow: "0 0 0 rgba(0,0,0,0)" }
|
|
1574
|
+
],
|
|
1575
|
+
{ ...baseOpts, easing: "ease-in-out" }
|
|
1576
|
+
)
|
|
1577
|
+
);
|
|
1578
|
+
};
|
|
1579
|
+
var ripple = ({ ev, el, delayMs, durMs, anims }) => {
|
|
1580
|
+
const color = stringParam(ev.params.color, "#38bdf8");
|
|
1581
|
+
const size = numeric(ev.params.size, 140);
|
|
1582
|
+
const ring = document.createElement("span");
|
|
1583
|
+
ring.setAttribute("data-markdy-ripple", "1");
|
|
1584
|
+
Object.assign(ring.style, {
|
|
1585
|
+
position: "absolute",
|
|
1586
|
+
left: "50%",
|
|
1587
|
+
top: "50%",
|
|
1588
|
+
width: `${size}px`,
|
|
1589
|
+
height: `${size}px`,
|
|
1590
|
+
marginLeft: `${-size / 2}px`,
|
|
1591
|
+
marginTop: `${-size / 2}px`,
|
|
1592
|
+
border: `2px solid ${color}`,
|
|
1593
|
+
borderRadius: "999px",
|
|
1594
|
+
pointerEvents: "none",
|
|
1595
|
+
opacity: "0"
|
|
1596
|
+
});
|
|
1597
|
+
el.appendChild(ring);
|
|
1598
|
+
anims.push(
|
|
1599
|
+
ring.animate(
|
|
1600
|
+
[
|
|
1601
|
+
{ transform: "scale(0.2)", opacity: 0.6 },
|
|
1602
|
+
{ transform: "scale(1)", opacity: 0 }
|
|
1603
|
+
],
|
|
1604
|
+
{ delay: delayMs, duration: durMs, fill: "forwards", easing: "ease-out" }
|
|
1605
|
+
)
|
|
1606
|
+
);
|
|
1607
|
+
};
|
|
1608
|
+
var blur = ({ ev, el, baseOpts, anims }) => {
|
|
1609
|
+
const from = numeric(ev.params.from, 0);
|
|
1610
|
+
const to = numeric(ev.params.to, 8);
|
|
1611
|
+
anims.push(el.animate([{ filter: `blur(${from}px)` }, { filter: `blur(${to}px)` }], baseOpts));
|
|
1612
|
+
};
|
|
1613
|
+
var lineReveal = ({ ev, el, baseOpts, anims }) => {
|
|
1614
|
+
const from = stringParam(ev.params.from, "left");
|
|
1615
|
+
const start = from === "right" ? "inset(0 0 0 100%)" : "inset(0 100% 0 0)";
|
|
1616
|
+
anims.push(el.animate([{ clipPath: start }, { clipPath: "inset(0 0 0 0)" }], baseOpts));
|
|
1617
|
+
};
|
|
1618
|
+
var mask = ({ ev, el, baseOpts, anims }) => {
|
|
1619
|
+
const from = numeric(ev.params.from, 0);
|
|
1620
|
+
const to = numeric(ev.params.to, 120);
|
|
1621
|
+
anims.push(
|
|
1622
|
+
el.animate(
|
|
1623
|
+
[
|
|
1624
|
+
{ clipPath: `circle(${from}% at 50% 50%)` },
|
|
1625
|
+
{ clipPath: `circle(${to}% at 50% 50%)` }
|
|
1626
|
+
],
|
|
1627
|
+
baseOpts
|
|
1628
|
+
)
|
|
1629
|
+
);
|
|
1630
|
+
};
|
|
1631
|
+
var parallax = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1632
|
+
const depth = numeric(ev.params.depth, 0.35);
|
|
1633
|
+
const by = ev.params.by;
|
|
1634
|
+
const dx = (by?.[0] ?? 24) * depth;
|
|
1635
|
+
const dy = (by?.[1] ?? 0) * depth;
|
|
1636
|
+
anims.push(
|
|
1637
|
+
el.animate(
|
|
1638
|
+
[
|
|
1639
|
+
{ transform: tx2(state) },
|
|
1640
|
+
{ transform: tx2({ ...state, x: state.x + dx, y: state.y + dy }) }
|
|
1641
|
+
],
|
|
1642
|
+
baseOpts
|
|
1643
|
+
)
|
|
1644
|
+
);
|
|
1645
|
+
};
|
|
1646
|
+
|
|
779
1647
|
// src/geometry/rect.ts
|
|
1648
|
+
import { TECHNICAL_NODE_TYPES } from "@markdy/core";
|
|
1649
|
+
var SYSTEM_NODE_SIZE = { width: 184, height: 88 };
|
|
780
1650
|
var ACTOR_SIZES = {
|
|
781
|
-
|
|
782
|
-
client: { width: 180, height: 84 },
|
|
783
|
-
db: { width: 180, height: 84 },
|
|
784
|
-
queue: { width: 180, height: 84 },
|
|
1651
|
+
...Object.fromEntries(TECHNICAL_NODE_TYPES.map((type) => [type, SYSTEM_NODE_SIZE])),
|
|
785
1652
|
box: { width: 100, height: 100 },
|
|
786
1653
|
caption: { width: 260, height: 56 },
|
|
787
|
-
figure: { width: 120, height: 170 }
|
|
1654
|
+
figure: { width: 120, height: 170 },
|
|
1655
|
+
panel: { width: 390, height: 250 },
|
|
1656
|
+
surface: { width: 390, height: 250 },
|
|
1657
|
+
terminal: { width: 390, height: 250 },
|
|
1658
|
+
metric: { width: 112, height: 44 },
|
|
1659
|
+
stat: { width: 112, height: 44 },
|
|
1660
|
+
grid: { width: 190, height: 92 },
|
|
1661
|
+
matrix: { width: 190, height: 92 },
|
|
1662
|
+
lane: { width: 300, height: 44 },
|
|
1663
|
+
track: { width: 300, height: 44 },
|
|
1664
|
+
marker: { width: 50, height: 24 },
|
|
1665
|
+
dot: { width: 50, height: 24 },
|
|
1666
|
+
token_strip: { width: 300, height: 54 },
|
|
1667
|
+
chips: { width: 300, height: 54 },
|
|
1668
|
+
glyph_card: { width: 98, height: 120 },
|
|
1669
|
+
glyph: { width: 98, height: 120 },
|
|
1670
|
+
parking_map: { width: 390, height: 250 },
|
|
1671
|
+
ascii_map: { width: 390, height: 250 },
|
|
1672
|
+
game_scene: { width: 390, height: 250 },
|
|
1673
|
+
byte_viz: { width: 390, height: 250 }
|
|
788
1674
|
};
|
|
789
1675
|
var DEFAULT_ACTOR_SIZE = { width: 140, height: 42 };
|
|
790
1676
|
function actorSizeByType(type) {
|
|
791
1677
|
return ACTOR_SIZES[type] ?? DEFAULT_ACTOR_SIZE;
|
|
792
1678
|
}
|
|
793
1679
|
function actorCenter(state, actorType) {
|
|
794
|
-
const
|
|
795
|
-
return { x:
|
|
1680
|
+
const rect = actorRect(state, actorType);
|
|
1681
|
+
return { x: rect.x1 + (rect.x2 - rect.x1) / 2, y: rect.y1 + (rect.y2 - rect.y1) / 2 };
|
|
796
1682
|
}
|
|
797
1683
|
function actorRect(state, actorType) {
|
|
798
1684
|
const { width, height } = actorSizeByType(actorType);
|
|
799
|
-
|
|
1685
|
+
const scale2 = Math.max(1e-3, state.scale);
|
|
1686
|
+
const scaledWidth = width * scale2;
|
|
1687
|
+
const scaledHeight = height * scale2;
|
|
1688
|
+
const dx = (scaledWidth - width) / 2;
|
|
1689
|
+
const dy = (scaledHeight - height) / 2;
|
|
1690
|
+
return {
|
|
1691
|
+
x1: state.x - dx,
|
|
1692
|
+
y1: state.y - dy,
|
|
1693
|
+
x2: state.x + width + dx,
|
|
1694
|
+
y2: state.y + height + dy
|
|
1695
|
+
};
|
|
800
1696
|
}
|
|
801
1697
|
function inflateRect(rect, pad) {
|
|
802
1698
|
return {
|
|
@@ -849,19 +1745,58 @@ function polylineLength(points) {
|
|
|
849
1745
|
}
|
|
850
1746
|
return Math.max(1, total);
|
|
851
1747
|
}
|
|
852
|
-
function
|
|
853
|
-
|
|
1748
|
+
function segmentLength(a, b) {
|
|
1749
|
+
return Math.hypot(b.x - a.x, b.y - a.y);
|
|
1750
|
+
}
|
|
1751
|
+
function labelPointForPath(points, lane) {
|
|
1752
|
+
if (points.length < 2) return points[0] ?? { x: 0, y: 0 };
|
|
1753
|
+
let bestIndex = 0;
|
|
1754
|
+
let bestLength = -1;
|
|
854
1755
|
for (let i = 0; i < points.length - 1; i++) {
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
const t = seg <= 0 ? 0 : Math.min(1, remain / seg);
|
|
860
|
-
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
1756
|
+
const len = segmentLength(points[i], points[i + 1]);
|
|
1757
|
+
if (len > bestLength) {
|
|
1758
|
+
bestIndex = i;
|
|
1759
|
+
bestLength = len;
|
|
861
1760
|
}
|
|
862
|
-
remain -= seg;
|
|
863
1761
|
}
|
|
864
|
-
|
|
1762
|
+
const a = points[bestIndex];
|
|
1763
|
+
const b = points[bestIndex + 1];
|
|
1764
|
+
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
1765
|
+
const offset = lane * 8;
|
|
1766
|
+
if (Math.abs(a.x - b.x) >= Math.abs(a.y - b.y)) {
|
|
1767
|
+
return { x: mid.x, y: mid.y - 10 - offset };
|
|
1768
|
+
}
|
|
1769
|
+
return { x: mid.x + 10 + offset, y: mid.y };
|
|
1770
|
+
}
|
|
1771
|
+
function clamp(n, min, max) {
|
|
1772
|
+
if (max < min) return n;
|
|
1773
|
+
return Math.min(max, Math.max(min, n));
|
|
1774
|
+
}
|
|
1775
|
+
function clampPointToScene(point, ast) {
|
|
1776
|
+
const pad = 14;
|
|
1777
|
+
return {
|
|
1778
|
+
x: round1(clamp(point.x, pad, ast.meta.width - pad)),
|
|
1779
|
+
y: round1(clamp(point.y, pad, ast.meta.height - pad))
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
function routeLength(points) {
|
|
1783
|
+
let total = 0;
|
|
1784
|
+
for (let i = 0; i < points.length - 1; i++) total += segmentLength(points[i], points[i + 1]);
|
|
1785
|
+
return total;
|
|
1786
|
+
}
|
|
1787
|
+
function routeBends(points) {
|
|
1788
|
+
let bends = 0;
|
|
1789
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
1790
|
+
const prev = points[i - 1];
|
|
1791
|
+
const cur = points[i];
|
|
1792
|
+
const next = points[i + 1];
|
|
1793
|
+
const dx1 = Math.sign(cur.x - prev.x);
|
|
1794
|
+
const dy1 = Math.sign(cur.y - prev.y);
|
|
1795
|
+
const dx2 = Math.sign(next.x - cur.x);
|
|
1796
|
+
const dy2 = Math.sign(next.y - cur.y);
|
|
1797
|
+
if (dx1 !== dx2 || dy1 !== dy2) bends++;
|
|
1798
|
+
}
|
|
1799
|
+
return bends;
|
|
865
1800
|
}
|
|
866
1801
|
function routeFlowPath(sourceName, targetName, sourceState, targetState, states, ast, lane) {
|
|
867
1802
|
const sourceType = ast.actors[sourceName]?.type ?? "box";
|
|
@@ -898,17 +1833,25 @@ function routeFlowPath(sourceName, targetName, sourceState, targetState, states,
|
|
|
898
1833
|
[source, { x: source.x, y: topLane }, { x: target.x, y: topLane }, target],
|
|
899
1834
|
[source, { x: source.x, y: bottomLane }, { x: target.x, y: bottomLane }, target]
|
|
900
1835
|
);
|
|
1836
|
+
const minObstacleX = obstacles.length ? Math.min(...obstacles.map((o) => o.x1)) : Math.min(source.x, target.x);
|
|
1837
|
+
const maxObstacleX = obstacles.length ? Math.max(...obstacles.map((o) => o.x2)) : Math.max(source.x, target.x);
|
|
1838
|
+
const leftLane = Math.max(16, minObstacleX - 24 - lane * 12);
|
|
1839
|
+
const rightLane = Math.min(ast.meta.width - 16, maxObstacleX + 24 + lane * 12);
|
|
1840
|
+
candidates.push(
|
|
1841
|
+
[source, { x: leftLane, y: source.y }, { x: leftLane, y: target.y }, target],
|
|
1842
|
+
[source, { x: rightLane, y: source.y }, { x: rightLane, y: target.y }, target]
|
|
1843
|
+
);
|
|
901
1844
|
let best = candidates[0];
|
|
902
|
-
let
|
|
1845
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
903
1846
|
for (const candidate of candidates) {
|
|
904
1847
|
const hits = countPathIntersections(candidate, obstacles);
|
|
905
|
-
|
|
1848
|
+
const score = hits * 1e5 + routeBends(candidate) * 800 + routeLength(candidate);
|
|
1849
|
+
if (score < bestScore) {
|
|
906
1850
|
best = candidate;
|
|
907
|
-
|
|
908
|
-
if (hits === 0) break;
|
|
1851
|
+
bestScore = score;
|
|
909
1852
|
}
|
|
910
1853
|
}
|
|
911
|
-
return best;
|
|
1854
|
+
return best.map((point) => clampPointToScene(point, ast));
|
|
912
1855
|
}
|
|
913
1856
|
|
|
914
1857
|
// src/actions/flow.ts
|
|
@@ -938,6 +1881,41 @@ function ensureEdgeLayer(scene) {
|
|
|
938
1881
|
scene.appendChild(svg);
|
|
939
1882
|
return svg;
|
|
940
1883
|
}
|
|
1884
|
+
function ensureEdgeDefs(svg) {
|
|
1885
|
+
if (svg.querySelector("defs[data-markdy-flow-defs='1']")) return;
|
|
1886
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1887
|
+
defs.setAttribute("data-markdy-flow-defs", "1");
|
|
1888
|
+
const glow2 = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
1889
|
+
glow2.setAttribute("id", "markdy-flow-glow");
|
|
1890
|
+
glow2.setAttribute("x", "-40%");
|
|
1891
|
+
glow2.setAttribute("y", "-40%");
|
|
1892
|
+
glow2.setAttribute("width", "180%");
|
|
1893
|
+
glow2.setAttribute("height", "180%");
|
|
1894
|
+
const blur2 = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
1895
|
+
blur2.setAttribute("stdDeviation", "3");
|
|
1896
|
+
blur2.setAttribute("result", "coloredBlur");
|
|
1897
|
+
const merge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
1898
|
+
const glowNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1899
|
+
glowNode.setAttribute("in", "coloredBlur");
|
|
1900
|
+
const sourceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1901
|
+
sourceNode.setAttribute("in", "SourceGraphic");
|
|
1902
|
+
merge.append(glowNode, sourceNode);
|
|
1903
|
+
glow2.append(blur2, merge);
|
|
1904
|
+
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
1905
|
+
arrow.setAttribute("id", "markdy-flow-arrow");
|
|
1906
|
+
arrow.setAttribute("viewBox", "0 0 10 10");
|
|
1907
|
+
arrow.setAttribute("refX", "8");
|
|
1908
|
+
arrow.setAttribute("refY", "5");
|
|
1909
|
+
arrow.setAttribute("markerWidth", "6");
|
|
1910
|
+
arrow.setAttribute("markerHeight", "6");
|
|
1911
|
+
arrow.setAttribute("orient", "auto-start-reverse");
|
|
1912
|
+
const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1913
|
+
arrowPath.setAttribute("d", "M 1 1 L 9 5 L 1 9 z");
|
|
1914
|
+
arrowPath.setAttribute("fill", "context-stroke");
|
|
1915
|
+
arrow.appendChild(arrowPath);
|
|
1916
|
+
defs.append(glow2, arrow);
|
|
1917
|
+
svg.prepend(defs);
|
|
1918
|
+
}
|
|
941
1919
|
function isDashed(ctx) {
|
|
942
1920
|
const style = String(ctx.ev.params.style ?? "");
|
|
943
1921
|
return style === "dashed" || style === "fire_and_forget" || ctx.ev.action === "response";
|
|
@@ -951,38 +1929,51 @@ function buildEdge(ctx, targetName) {
|
|
|
951
1929
|
const length = polylineLength(points);
|
|
952
1930
|
const pathD = toPathD(points);
|
|
953
1931
|
const stroke = STROKE_BY_ACTION[ev.action] ?? DEFAULT_STROKE;
|
|
1932
|
+
const svg = ensureEdgeLayer(scene);
|
|
1933
|
+
ensureEdgeDefs(svg);
|
|
954
1934
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
955
1935
|
group.setAttribute("data-markdy-flow-edge", "1");
|
|
956
1936
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
957
1937
|
path.setAttribute("d", pathD);
|
|
958
1938
|
path.setAttribute("fill", "none");
|
|
959
1939
|
path.setAttribute("stroke", stroke);
|
|
960
|
-
path.setAttribute("stroke-width", "
|
|
1940
|
+
path.setAttribute("stroke-width", "3");
|
|
1941
|
+
path.setAttribute("stroke-linecap", "round");
|
|
1942
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
1943
|
+
path.setAttribute("marker-end", "url(#markdy-flow-arrow)");
|
|
1944
|
+
path.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
961
1945
|
path.setAttribute("data-markdy-flow-action", ev.action);
|
|
962
1946
|
path.style.strokeDasharray = isDashed(ctx) ? "8 6" : `${length}`;
|
|
963
1947
|
path.style.strokeDashoffset = `${length}`;
|
|
964
1948
|
group.appendChild(path);
|
|
965
1949
|
const marker = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
966
|
-
marker.setAttribute("r", "
|
|
1950
|
+
marker.setAttribute("r", "4.5");
|
|
967
1951
|
marker.setAttribute("fill", stroke);
|
|
1952
|
+
marker.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
968
1953
|
marker.style.offsetPath = `path('${pathD}')`;
|
|
969
1954
|
marker.style.offsetDistance = "0%";
|
|
970
1955
|
marker.style.opacity = "0";
|
|
971
1956
|
group.appendChild(marker);
|
|
972
1957
|
const labelText = String(ev.params.label ?? "");
|
|
973
1958
|
if (labelText) {
|
|
974
|
-
const midPoint =
|
|
1959
|
+
const midPoint = labelPointForPath(points, lane);
|
|
975
1960
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
976
1961
|
label.setAttribute("x", `${round1(midPoint.x)}`);
|
|
977
1962
|
label.setAttribute("y", `${round1(midPoint.y - 8)}`);
|
|
978
1963
|
label.setAttribute("text-anchor", "middle");
|
|
979
1964
|
label.setAttribute("font-size", "12");
|
|
1965
|
+
label.setAttribute("font-weight", "700");
|
|
1966
|
+
label.setAttribute("paint-order", "stroke");
|
|
1967
|
+
label.setAttribute("stroke", "rgba(2, 6, 23, 0.88)");
|
|
1968
|
+
label.setAttribute("stroke-width", "5");
|
|
1969
|
+
label.setAttribute("stroke-linejoin", "round");
|
|
980
1970
|
label.setAttribute("fill", "#cbd5e1");
|
|
1971
|
+
label.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
981
1972
|
label.setAttribute("data-full-label", labelText);
|
|
982
1973
|
label.textContent = labelText.length > LABEL_MAX_CHARS ? `${labelText.slice(0, LABEL_MAX_CHARS - 1)}\u2026` : labelText;
|
|
983
1974
|
group.appendChild(label);
|
|
984
1975
|
}
|
|
985
|
-
|
|
1976
|
+
svg.appendChild(group);
|
|
986
1977
|
anims.push(
|
|
987
1978
|
path.animate([{ strokeDashoffset: length }, { strokeDashoffset: 0 }], baseOpts),
|
|
988
1979
|
marker.animate(
|
|
@@ -1164,9 +2155,18 @@ var ACTION_HANDLERS = Object.freeze({
|
|
|
1164
2155
|
exit,
|
|
1165
2156
|
fade_in: fadeIn,
|
|
1166
2157
|
fade_out: fadeOut,
|
|
2158
|
+
spring,
|
|
2159
|
+
follow_path: followPath,
|
|
1167
2160
|
scale,
|
|
1168
2161
|
rotate,
|
|
1169
2162
|
shake,
|
|
2163
|
+
pulse,
|
|
2164
|
+
glow,
|
|
2165
|
+
ripple,
|
|
2166
|
+
blur,
|
|
2167
|
+
line_reveal: lineReveal,
|
|
2168
|
+
mask,
|
|
2169
|
+
parallax,
|
|
1170
2170
|
jump,
|
|
1171
2171
|
bounce,
|
|
1172
2172
|
say,
|
|
@@ -1196,7 +2196,7 @@ var DEFAULT_EASE_BY_ACTION = {
|
|
|
1196
2196
|
exit: "in",
|
|
1197
2197
|
fade_out: "in"
|
|
1198
2198
|
};
|
|
1199
|
-
function buildAnimations(ast, actorEls,
|
|
2199
|
+
function buildAnimations(ast, actorEls, actorLayer, cameraLayer, assetOverrides, faceSwaps) {
|
|
1200
2200
|
const anims = [];
|
|
1201
2201
|
const states = /* @__PURE__ */ new Map();
|
|
1202
2202
|
for (const [name, def] of Object.entries(ast.actors)) {
|
|
@@ -1222,7 +2222,7 @@ function buildAnimations(ast, actorEls, scene, assetOverrides, faceSwaps) {
|
|
|
1222
2222
|
easing: toEasing(ev.params.ease ?? DEFAULT_EASE_BY_ACTION[ev.action])
|
|
1223
2223
|
};
|
|
1224
2224
|
if (ev.actor === "camera") {
|
|
1225
|
-
buildCameraAction(ev,
|
|
2225
|
+
buildCameraAction(ev, cameraLayer, ast, baseOpts, anims, cameraState);
|
|
1226
2226
|
continue;
|
|
1227
2227
|
}
|
|
1228
2228
|
const el = actorEls.get(ev.actor);
|
|
@@ -1239,7 +2239,7 @@ function buildAnimations(ast, actorEls, scene, assetOverrides, faceSwaps) {
|
|
|
1239
2239
|
ast,
|
|
1240
2240
|
states,
|
|
1241
2241
|
actorEls,
|
|
1242
|
-
scene,
|
|
2242
|
+
scene: actorLayer,
|
|
1243
2243
|
assetOverrides,
|
|
1244
2244
|
faceSwaps,
|
|
1245
2245
|
anims,
|
|
@@ -1251,6 +2251,134 @@ function buildAnimations(ast, actorEls, scene, assetOverrides, faceSwaps) {
|
|
|
1251
2251
|
}
|
|
1252
2252
|
|
|
1253
2253
|
// src/player.ts
|
|
2254
|
+
var SCENE_STYLE_ID = "markdy-scene-ambience-styles";
|
|
2255
|
+
var SAFE_FRAME_PADDING = 28;
|
|
2256
|
+
function ensureSceneStyles(doc) {
|
|
2257
|
+
if (doc.getElementById(SCENE_STYLE_ID)) return;
|
|
2258
|
+
const style = doc.createElement("style");
|
|
2259
|
+
style.id = SCENE_STYLE_ID;
|
|
2260
|
+
style.textContent = `
|
|
2261
|
+
.markdy-scene-root {
|
|
2262
|
+
--markdy-scene-grid: rgba(148, 163, 184, 0.13);
|
|
2263
|
+
--markdy-scene-grid-strong: rgba(148, 163, 184, 0.2);
|
|
2264
|
+
--markdy-scene-vignette: rgba(2, 6, 23, 0.72);
|
|
2265
|
+
--markdy-scene-accent: rgba(56, 189, 248, 0.16);
|
|
2266
|
+
isolation: isolate;
|
|
2267
|
+
}
|
|
2268
|
+
.markdy-scene-root::before,
|
|
2269
|
+
.markdy-scene-root::after {
|
|
2270
|
+
content: "";
|
|
2271
|
+
position: absolute;
|
|
2272
|
+
inset: 0;
|
|
2273
|
+
pointer-events: none;
|
|
2274
|
+
}
|
|
2275
|
+
.markdy-scene-root::before {
|
|
2276
|
+
z-index: 0;
|
|
2277
|
+
background:
|
|
2278
|
+
linear-gradient(var(--markdy-scene-grid) 1px, transparent 1px) 0 0 / 32px 32px,
|
|
2279
|
+
linear-gradient(90deg, var(--markdy-scene-grid) 1px, transparent 1px) 0 0 / 32px 32px,
|
|
2280
|
+
linear-gradient(var(--markdy-scene-grid-strong) 1px, transparent 1px) 0 0 / 160px 160px,
|
|
2281
|
+
linear-gradient(90deg, var(--markdy-scene-grid-strong) 1px, transparent 1px) 0 0 / 160px 160px;
|
|
2282
|
+
mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent);
|
|
2283
|
+
opacity: 0.82;
|
|
2284
|
+
}
|
|
2285
|
+
.markdy-scene-root::after {
|
|
2286
|
+
z-index: 1;
|
|
2287
|
+
background:
|
|
2288
|
+
radial-gradient(ellipse at 50% 0%, var(--markdy-scene-accent), transparent 42%),
|
|
2289
|
+
linear-gradient(180deg, transparent 0%, rgba(2, 6, 23, 0.08) 58%, var(--markdy-scene-vignette) 100%),
|
|
2290
|
+
linear-gradient(90deg, rgba(255, 255, 255, 0.035), transparent 18%, transparent 82%, rgba(255, 255, 255, 0.035));
|
|
2291
|
+
mix-blend-mode: screen;
|
|
2292
|
+
opacity: 0.8;
|
|
2293
|
+
}
|
|
2294
|
+
.markdy-scene-root[data-markdy-scene-tone="light"] {
|
|
2295
|
+
--markdy-scene-grid: rgba(15, 23, 42, 0.09);
|
|
2296
|
+
--markdy-scene-grid-strong: rgba(15, 23, 42, 0.14);
|
|
2297
|
+
--markdy-scene-vignette: rgba(241, 245, 249, 0.74);
|
|
2298
|
+
--markdy-scene-accent: rgba(14, 165, 233, 0.12);
|
|
2299
|
+
}
|
|
2300
|
+
.markdy-scene-content {
|
|
2301
|
+
z-index: 2;
|
|
2302
|
+
}
|
|
2303
|
+
.markdy-scene-actor-layer {
|
|
2304
|
+
position: absolute;
|
|
2305
|
+
inset: 0;
|
|
2306
|
+
overflow: visible;
|
|
2307
|
+
transform-origin: 0 0;
|
|
2308
|
+
will-change: transform;
|
|
2309
|
+
}
|
|
2310
|
+
`;
|
|
2311
|
+
doc.head.appendChild(style);
|
|
2312
|
+
}
|
|
2313
|
+
function unionRect(a, b) {
|
|
2314
|
+
if (!a) return b;
|
|
2315
|
+
return {
|
|
2316
|
+
x1: Math.min(a.x1, b.x1),
|
|
2317
|
+
y1: Math.min(a.y1, b.y1),
|
|
2318
|
+
x2: Math.max(a.x2, b.x2),
|
|
2319
|
+
y2: Math.max(a.y2, b.y2)
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
function numericPair(value) {
|
|
2323
|
+
if (!Array.isArray(value) || value.length < 2) return null;
|
|
2324
|
+
const [x, y] = value;
|
|
2325
|
+
return typeof x === "number" && typeof y === "number" ? [x, y] : null;
|
|
2326
|
+
}
|
|
2327
|
+
function computeContentBounds(ast) {
|
|
2328
|
+
let bounds = null;
|
|
2329
|
+
const maxScaleByActor = /* @__PURE__ */ new Map();
|
|
2330
|
+
const positionsByActor = /* @__PURE__ */ new Map();
|
|
2331
|
+
for (const [name, def] of Object.entries(ast.actors)) {
|
|
2332
|
+
maxScaleByActor.set(name, Math.max(1e-3, def.scale ?? 1));
|
|
2333
|
+
positionsByActor.set(name, [[def.x, def.y]]);
|
|
2334
|
+
}
|
|
2335
|
+
for (const ev of ast.events) {
|
|
2336
|
+
if (ev.actor === "camera") continue;
|
|
2337
|
+
if (ev.action === "scale" && typeof ev.params.to === "number") {
|
|
2338
|
+
maxScaleByActor.set(ev.actor, Math.max(maxScaleByActor.get(ev.actor) ?? 1, ev.params.to));
|
|
2339
|
+
}
|
|
2340
|
+
if (ev.action === "move" || ev.action === "spring") {
|
|
2341
|
+
const to = numericPair(ev.params.to);
|
|
2342
|
+
if (to) positionsByActor.get(ev.actor)?.push(to);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
for (const [name, def] of Object.entries(ast.actors)) {
|
|
2346
|
+
const positions = positionsByActor.get(name) ?? [[def.x, def.y]];
|
|
2347
|
+
const scale2 = maxScaleByActor.get(name) ?? def.scale ?? 1;
|
|
2348
|
+
for (const [x, y] of positions) {
|
|
2349
|
+
bounds = unionRect(bounds, actorRect({ ...stateFrom(def), x, y, scale: scale2 }, def.type));
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
return bounds;
|
|
2353
|
+
}
|
|
2354
|
+
function computeSafeFrame(ast) {
|
|
2355
|
+
const bounds = computeContentBounds(ast);
|
|
2356
|
+
if (!bounds) return { x: 0, y: 0, scale: 1 };
|
|
2357
|
+
const boundsWidth = Math.max(1, bounds.x2 - bounds.x1);
|
|
2358
|
+
const boundsHeight = Math.max(1, bounds.y2 - bounds.y1);
|
|
2359
|
+
const availableWidth = Math.max(1, ast.meta.width - SAFE_FRAME_PADDING * 2);
|
|
2360
|
+
const availableHeight = Math.max(1, ast.meta.height - SAFE_FRAME_PADDING * 2);
|
|
2361
|
+
const scale2 = Math.min(1, availableWidth / boundsWidth, availableHeight / boundsHeight);
|
|
2362
|
+
const scaledWidth = boundsWidth * scale2;
|
|
2363
|
+
const scaledHeight = boundsHeight * scale2;
|
|
2364
|
+
function axisOffset(min, max, sceneSize, scaledSize) {
|
|
2365
|
+
const paddedMin = SAFE_FRAME_PADDING;
|
|
2366
|
+
const paddedMax = sceneSize - SAFE_FRAME_PADDING;
|
|
2367
|
+
const scaledMin = min * scale2;
|
|
2368
|
+
const scaledMax = max * scale2;
|
|
2369
|
+
if (scaledSize > paddedMax - paddedMin) return (sceneSize - scaledSize) / 2 - scaledMin;
|
|
2370
|
+
if (scaledMin < paddedMin) return paddedMin - scaledMin;
|
|
2371
|
+
if (scaledMax > paddedMax) return paddedMax - scaledMax;
|
|
2372
|
+
return 0;
|
|
2373
|
+
}
|
|
2374
|
+
const x = axisOffset(bounds.x1, bounds.x2, ast.meta.width, scaledWidth);
|
|
2375
|
+
const y = axisOffset(bounds.y1, bounds.y2, ast.meta.height, scaledHeight);
|
|
2376
|
+
return {
|
|
2377
|
+
x: Math.round(x * 1e3) / 1e3,
|
|
2378
|
+
y: Math.round(y * 1e3) / 1e3,
|
|
2379
|
+
scale: Math.round(scale2 * 1e3) / 1e3
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
1254
2382
|
function bgToTextColor(bg) {
|
|
1255
2383
|
let hex = bg.trim().replace(/^#/, "");
|
|
1256
2384
|
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
@@ -1350,6 +2478,9 @@ function createPlayer(opts) {
|
|
|
1350
2478
|
}
|
|
1351
2479
|
}
|
|
1352
2480
|
const scene = document.createElement("div");
|
|
2481
|
+
ensureSceneStyles(document);
|
|
2482
|
+
scene.className = "markdy-scene-root";
|
|
2483
|
+
scene.dataset.markdySceneTone = bgToTextColor(ast.meta.bg) === "#1a1a1a" ? "light" : "dark";
|
|
1353
2484
|
Object.assign(scene.style, {
|
|
1354
2485
|
position: "absolute",
|
|
1355
2486
|
top: "0",
|
|
@@ -1364,6 +2495,7 @@ function createPlayer(opts) {
|
|
|
1364
2495
|
});
|
|
1365
2496
|
viewport.appendChild(scene);
|
|
1366
2497
|
const sceneContent = document.createElement("div");
|
|
2498
|
+
sceneContent.className = "markdy-scene-content";
|
|
1367
2499
|
Object.assign(sceneContent.style, {
|
|
1368
2500
|
position: "absolute",
|
|
1369
2501
|
top: "0",
|
|
@@ -1374,6 +2506,12 @@ function createPlayer(opts) {
|
|
|
1374
2506
|
willChange: "transform"
|
|
1375
2507
|
});
|
|
1376
2508
|
scene.appendChild(sceneContent);
|
|
2509
|
+
const actorLayer = document.createElement("div");
|
|
2510
|
+
actorLayer.className = "markdy-scene-actor-layer";
|
|
2511
|
+
const safeFrame = computeSafeFrame(ast);
|
|
2512
|
+
actorLayer.dataset.markdySafeFrame = `${safeFrame.x},${safeFrame.y},${safeFrame.scale}`;
|
|
2513
|
+
actorLayer.style.transform = `translate(${safeFrame.x}px, ${safeFrame.y}px) scale(${safeFrame.scale})`;
|
|
2514
|
+
sceneContent.appendChild(actorLayer);
|
|
1377
2515
|
function scaleScene() {
|
|
1378
2516
|
const s = viewport.clientWidth / ast.meta.width;
|
|
1379
2517
|
scene.style.transform = `scale(${s})`;
|
|
@@ -1384,11 +2522,11 @@ function createPlayer(opts) {
|
|
|
1384
2522
|
const actorEls = /* @__PURE__ */ new Map();
|
|
1385
2523
|
for (const [name, def] of Object.entries(ast.actors)) {
|
|
1386
2524
|
const el = createActorEl(name, def, ast.assets, assetOverrides);
|
|
1387
|
-
|
|
2525
|
+
actorLayer.appendChild(el);
|
|
1388
2526
|
actorEls.set(name, el);
|
|
1389
2527
|
}
|
|
1390
2528
|
const faceSwaps = [];
|
|
1391
|
-
const allAnims = buildAnimations(ast, actorEls, sceneContent, assetOverrides, faceSwaps);
|
|
2529
|
+
const allAnims = buildAnimations(ast, actorEls, actorLayer, sceneContent, assetOverrides, faceSwaps);
|
|
1392
2530
|
faceSwaps.sort((a, b) => a.timeMs - b.timeMs);
|
|
1393
2531
|
for (const anim of allAnims) {
|
|
1394
2532
|
anim.pause();
|