@markdy/renderer-dom 0.7.25 → 0.7.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -5
- package/dist/index.js +1762 -721
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -309,6 +309,730 @@ function readRotation(el) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
// src/actors.ts
|
|
312
|
+
var ARCHITECTURE_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
313
|
+
"service",
|
|
314
|
+
"api",
|
|
315
|
+
"microservice",
|
|
316
|
+
"client",
|
|
317
|
+
"user",
|
|
318
|
+
"db",
|
|
319
|
+
"database",
|
|
320
|
+
"queue",
|
|
321
|
+
"cache",
|
|
322
|
+
"cloud",
|
|
323
|
+
"region",
|
|
324
|
+
"container",
|
|
325
|
+
"cluster"
|
|
326
|
+
]);
|
|
327
|
+
var SHOWCASE_SURFACE_TYPES = /* @__PURE__ */ new Set(["parking_map", "ascii_map", "game_scene", "byte_viz"]);
|
|
328
|
+
var ARCHITECTURE_STYLE_ID = "markdy-architecture-node-styles";
|
|
329
|
+
var SHOWCASE_STYLE_ID = "markdy-showcase-surface-styles";
|
|
330
|
+
function ensureShowcaseStyles(doc) {
|
|
331
|
+
if (doc.getElementById(SHOWCASE_STYLE_ID)) return;
|
|
332
|
+
const style = doc.createElement("style");
|
|
333
|
+
style.id = SHOWCASE_STYLE_ID;
|
|
334
|
+
style.textContent = `
|
|
335
|
+
.markdy-showcase {
|
|
336
|
+
--surface-a: rgba(15, 23, 42, 0.9);
|
|
337
|
+
--accent: #38bdf8;
|
|
338
|
+
--accent-2: #22c55e;
|
|
339
|
+
position: relative;
|
|
340
|
+
box-sizing: border-box;
|
|
341
|
+
width: 390px;
|
|
342
|
+
height: 250px;
|
|
343
|
+
overflow: hidden;
|
|
344
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
345
|
+
border-radius: 24px;
|
|
346
|
+
color: #e5f3ff;
|
|
347
|
+
background:
|
|
348
|
+
radial-gradient(circle at 12% 0%, color-mix(in srgb, var(--accent) 32%, transparent), transparent 34%),
|
|
349
|
+
radial-gradient(circle at 90% 15%, color-mix(in srgb, var(--accent-2) 26%, transparent), transparent 34%),
|
|
350
|
+
linear-gradient(145deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.025) 32%, var(--surface-a));
|
|
351
|
+
box-shadow:
|
|
352
|
+
0 26px 70px rgba(2, 6, 23, 0.42),
|
|
353
|
+
0 0 0 1px rgba(255, 255, 255, 0.06) inset,
|
|
354
|
+
0 1px 0 rgba(255, 255, 255, 0.18) inset,
|
|
355
|
+
0 0 44px color-mix(in srgb, var(--accent) 24%, transparent);
|
|
356
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
357
|
+
isolation: isolate;
|
|
358
|
+
contain: layout paint style;
|
|
359
|
+
backdrop-filter: blur(16px) saturate(1.2);
|
|
360
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.2);
|
|
361
|
+
}
|
|
362
|
+
.markdy-showcase::before {
|
|
363
|
+
content: "";
|
|
364
|
+
position: absolute;
|
|
365
|
+
inset: 0;
|
|
366
|
+
z-index: -1;
|
|
367
|
+
background:
|
|
368
|
+
linear-gradient(90deg, transparent 0 48%, rgba(255, 255, 255, 0.09) 50%, transparent 52%) 0 0 / 34px 34px,
|
|
369
|
+
linear-gradient(0deg, transparent 0 48%, rgba(255, 255, 255, 0.055) 50%, transparent 52%) 0 0 / 34px 34px;
|
|
370
|
+
mask-image: linear-gradient(to bottom, rgba(0,0,0,0.85), transparent 82%);
|
|
371
|
+
opacity: 0.48;
|
|
372
|
+
animation: markdyGridDrift 7s linear infinite;
|
|
373
|
+
}
|
|
374
|
+
.markdy-showcase::after {
|
|
375
|
+
content: "";
|
|
376
|
+
position: absolute;
|
|
377
|
+
inset: -40% -20%;
|
|
378
|
+
z-index: -1;
|
|
379
|
+
background: conic-gradient(from 90deg, transparent, color-mix(in srgb, var(--accent) 20%, transparent), transparent 32%);
|
|
380
|
+
opacity: 0.6;
|
|
381
|
+
animation: markdyAurora 9s linear infinite;
|
|
382
|
+
}
|
|
383
|
+
.markdy-showcase--byte_viz { width: 420px; }
|
|
384
|
+
.markdy-showcase__top {
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
justify-content: space-between;
|
|
388
|
+
gap: 12px;
|
|
389
|
+
padding: 14px 16px 8px;
|
|
390
|
+
}
|
|
391
|
+
.markdy-showcase__title {
|
|
392
|
+
min-width: 0;
|
|
393
|
+
overflow: hidden;
|
|
394
|
+
text-overflow: ellipsis;
|
|
395
|
+
white-space: nowrap;
|
|
396
|
+
font-size: 15px;
|
|
397
|
+
font-weight: 820;
|
|
398
|
+
letter-spacing: 0.01em;
|
|
399
|
+
color: #f8fafc;
|
|
400
|
+
}
|
|
401
|
+
.markdy-showcase__pill {
|
|
402
|
+
flex: 0 0 auto;
|
|
403
|
+
border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent);
|
|
404
|
+
border-radius: 999px;
|
|
405
|
+
padding: 4px 8px;
|
|
406
|
+
color: #bae6fd;
|
|
407
|
+
background: rgba(8, 47, 73, 0.54);
|
|
408
|
+
font-size: 10px;
|
|
409
|
+
font-weight: 800;
|
|
410
|
+
letter-spacing: 0.08em;
|
|
411
|
+
text-transform: uppercase;
|
|
412
|
+
box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 22%, transparent);
|
|
413
|
+
}
|
|
414
|
+
.markdy-showcase__body { position: absolute; inset: 48px 14px 14px; }
|
|
415
|
+
.markdy-showcase--parking_map { --accent: #38bdf8; --accent-2: #22c55e; }
|
|
416
|
+
.parking-map__road {
|
|
417
|
+
position: absolute;
|
|
418
|
+
left: 14px;
|
|
419
|
+
right: 14px;
|
|
420
|
+
top: 78px;
|
|
421
|
+
height: 50px;
|
|
422
|
+
border-radius: 999px;
|
|
423
|
+
background:
|
|
424
|
+
repeating-linear-gradient(90deg, rgba(226, 232, 240, 0.78) 0 18px, transparent 18px 34px) center / 100% 3px no-repeat,
|
|
425
|
+
linear-gradient(90deg, rgba(15, 23, 42, 0.96), rgba(30, 41, 59, 0.88));
|
|
426
|
+
box-shadow: 0 10px 28px rgba(2, 6, 23, 0.28) inset;
|
|
427
|
+
}
|
|
428
|
+
.parking-map__scan {
|
|
429
|
+
position: absolute;
|
|
430
|
+
left: 28px;
|
|
431
|
+
top: 42px;
|
|
432
|
+
width: 66px;
|
|
433
|
+
height: 124px;
|
|
434
|
+
border-radius: 18px;
|
|
435
|
+
border: 1px solid rgba(56, 189, 248, 0.55);
|
|
436
|
+
background: linear-gradient(180deg, rgba(56, 189, 248, 0.22), transparent);
|
|
437
|
+
box-shadow: 0 0 24px rgba(56, 189, 248, 0.28);
|
|
438
|
+
animation: markdyScanPulse 1.8s ease-in-out infinite;
|
|
439
|
+
}
|
|
440
|
+
.parking-map__car {
|
|
441
|
+
position: absolute;
|
|
442
|
+
left: 48px;
|
|
443
|
+
top: 86px;
|
|
444
|
+
width: 46px;
|
|
445
|
+
height: 22px;
|
|
446
|
+
border-radius: 11px 15px 15px 11px;
|
|
447
|
+
background: linear-gradient(90deg, #22d3ee, #818cf8);
|
|
448
|
+
box-shadow: 0 0 18px rgba(56, 189, 248, 0.55), 0 8px 18px rgba(2, 6, 23, 0.35);
|
|
449
|
+
animation: markdyCarGlide 3.8s cubic-bezier(.4,0,.2,1) infinite;
|
|
450
|
+
}
|
|
451
|
+
.parking-map__car::before,
|
|
452
|
+
.parking-map__car::after {
|
|
453
|
+
content: "";
|
|
454
|
+
position: absolute;
|
|
455
|
+
bottom: -4px;
|
|
456
|
+
width: 8px;
|
|
457
|
+
height: 8px;
|
|
458
|
+
border-radius: 999px;
|
|
459
|
+
background: #020617;
|
|
460
|
+
border: 2px solid #cbd5e1;
|
|
461
|
+
}
|
|
462
|
+
.parking-map__car::before { left: 7px; }
|
|
463
|
+
.parking-map__car::after { right: 7px; }
|
|
464
|
+
.parking-map__slots {
|
|
465
|
+
position: absolute;
|
|
466
|
+
right: 12px;
|
|
467
|
+
top: 24px;
|
|
468
|
+
display: grid;
|
|
469
|
+
grid-template-columns: repeat(4, 44px);
|
|
470
|
+
gap: 8px;
|
|
471
|
+
}
|
|
472
|
+
.parking-map__slot {
|
|
473
|
+
height: 34px;
|
|
474
|
+
border-radius: 10px;
|
|
475
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
476
|
+
background: rgba(15, 23, 42, 0.7);
|
|
477
|
+
box-shadow: 0 0 0 1px rgba(255,255,255,0.035) inset;
|
|
478
|
+
}
|
|
479
|
+
.parking-map__slot:nth-child(3),
|
|
480
|
+
.parking-map__slot:nth-child(6),
|
|
481
|
+
.parking-map__slot:nth-child(8) {
|
|
482
|
+
border-color: rgba(34, 197, 94, 0.78);
|
|
483
|
+
background: rgba(20, 83, 45, 0.5);
|
|
484
|
+
animation: markdySlotGlow 1.7s ease-in-out infinite;
|
|
485
|
+
}
|
|
486
|
+
.parking-map__route {
|
|
487
|
+
position: absolute;
|
|
488
|
+
left: 91px;
|
|
489
|
+
right: 95px;
|
|
490
|
+
top: 109px;
|
|
491
|
+
height: 3px;
|
|
492
|
+
border-radius: 999px;
|
|
493
|
+
background: linear-gradient(90deg, transparent, #22c55e, #38bdf8, transparent);
|
|
494
|
+
filter: drop-shadow(0 0 8px #22c55e);
|
|
495
|
+
animation: markdyRouteFlow 1.5s linear infinite;
|
|
496
|
+
}
|
|
497
|
+
.parking-map__stats {
|
|
498
|
+
position: absolute;
|
|
499
|
+
left: 12px;
|
|
500
|
+
right: 12px;
|
|
501
|
+
bottom: 5px;
|
|
502
|
+
display: grid;
|
|
503
|
+
grid-template-columns: repeat(3, 1fr);
|
|
504
|
+
gap: 8px;
|
|
505
|
+
}
|
|
506
|
+
.parking-map__stat,
|
|
507
|
+
.ascii-map__badge,
|
|
508
|
+
.game-scene__hud,
|
|
509
|
+
.byte-viz__badge {
|
|
510
|
+
border: 1px solid rgba(148, 163, 184, 0.24);
|
|
511
|
+
border-radius: 12px;
|
|
512
|
+
padding: 8px;
|
|
513
|
+
background: rgba(2, 6, 23, 0.42);
|
|
514
|
+
color: #cbd5e1;
|
|
515
|
+
font-size: 10px;
|
|
516
|
+
font-weight: 760;
|
|
517
|
+
}
|
|
518
|
+
.parking-map__stat strong,
|
|
519
|
+
.byte-viz__badge strong {
|
|
520
|
+
display: block;
|
|
521
|
+
margin-top: 2px;
|
|
522
|
+
color: #f8fafc;
|
|
523
|
+
font-size: 16px;
|
|
524
|
+
}
|
|
525
|
+
.markdy-showcase--ascii_map { --accent: #22c55e; --accent-2: #38bdf8; }
|
|
526
|
+
.ascii-map__terminal {
|
|
527
|
+
position: absolute;
|
|
528
|
+
inset: 0;
|
|
529
|
+
border-radius: 18px;
|
|
530
|
+
border: 1px solid rgba(34, 197, 94, 0.34);
|
|
531
|
+
background: linear-gradient(180deg, rgba(2, 6, 23, 0.72), rgba(2, 6, 23, 0.36));
|
|
532
|
+
box-shadow: 0 0 28px rgba(34, 197, 94, 0.16) inset;
|
|
533
|
+
}
|
|
534
|
+
.ascii-map__chrome {
|
|
535
|
+
height: 28px;
|
|
536
|
+
padding-left: 12px;
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
gap: 6px;
|
|
540
|
+
border-bottom: 1px solid rgba(148, 163, 184, 0.16);
|
|
541
|
+
}
|
|
542
|
+
.ascii-map__chrome span {
|
|
543
|
+
width: 8px;
|
|
544
|
+
height: 8px;
|
|
545
|
+
border-radius: 999px;
|
|
546
|
+
background: #ef4444;
|
|
547
|
+
box-shadow: 14px 0 #f59e0b, 28px 0 #22c55e;
|
|
548
|
+
}
|
|
549
|
+
.ascii-map__code {
|
|
550
|
+
position: absolute;
|
|
551
|
+
left: 16px;
|
|
552
|
+
top: 46px;
|
|
553
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
554
|
+
font-size: 19px;
|
|
555
|
+
line-height: 1.55;
|
|
556
|
+
color: #bbf7d0;
|
|
557
|
+
text-shadow: 0 0 12px rgba(34, 197, 94, 0.45);
|
|
558
|
+
}
|
|
559
|
+
.ascii-map__code b {
|
|
560
|
+
color: #67e8f9;
|
|
561
|
+
animation: markdyBlink 1.2s steps(2, end) infinite;
|
|
562
|
+
}
|
|
563
|
+
.ascii-map__minimap {
|
|
564
|
+
position: absolute;
|
|
565
|
+
right: 18px;
|
|
566
|
+
top: 48px;
|
|
567
|
+
width: 102px;
|
|
568
|
+
height: 122px;
|
|
569
|
+
display: grid;
|
|
570
|
+
grid-template-columns: repeat(3, 1fr);
|
|
571
|
+
gap: 7px;
|
|
572
|
+
}
|
|
573
|
+
.ascii-map__mini-slot {
|
|
574
|
+
border-radius: 7px;
|
|
575
|
+
border: 1px solid rgba(148, 163, 184, 0.28);
|
|
576
|
+
background: rgba(15, 23, 42, 0.72);
|
|
577
|
+
}
|
|
578
|
+
.ascii-map__mini-slot:nth-child(2),
|
|
579
|
+
.ascii-map__mini-slot:nth-child(6) {
|
|
580
|
+
border-color: rgba(34, 197, 94, 0.85);
|
|
581
|
+
background: rgba(20, 83, 45, 0.56);
|
|
582
|
+
}
|
|
583
|
+
.ascii-map__transform {
|
|
584
|
+
position: absolute;
|
|
585
|
+
left: 16px;
|
|
586
|
+
right: 16px;
|
|
587
|
+
bottom: 10px;
|
|
588
|
+
display: grid;
|
|
589
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
590
|
+
gap: 8px;
|
|
591
|
+
}
|
|
592
|
+
.markdy-showcase--game_scene { --accent: #f59e0b; --accent-2: #22c55e; }
|
|
593
|
+
.game-scene__world {
|
|
594
|
+
position: absolute;
|
|
595
|
+
inset: 0;
|
|
596
|
+
border-radius: 18px;
|
|
597
|
+
overflow: hidden;
|
|
598
|
+
background:
|
|
599
|
+
radial-gradient(circle at 80% 18%, rgba(245, 158, 11, 0.22), transparent 26%),
|
|
600
|
+
linear-gradient(160deg, #172554, #020617 72%);
|
|
601
|
+
}
|
|
602
|
+
.game-scene__world::before {
|
|
603
|
+
content: "";
|
|
604
|
+
position: absolute;
|
|
605
|
+
inset: 0;
|
|
606
|
+
background: repeating-linear-gradient(115deg, transparent 0 22px, rgba(255,255,255,0.06) 22px 24px);
|
|
607
|
+
animation: markdyRoadMove 4s linear infinite;
|
|
608
|
+
}
|
|
609
|
+
.game-scene__road {
|
|
610
|
+
position: absolute;
|
|
611
|
+
left: -35px;
|
|
612
|
+
right: -35px;
|
|
613
|
+
top: 94px;
|
|
614
|
+
height: 76px;
|
|
615
|
+
transform: rotate(-8deg);
|
|
616
|
+
background:
|
|
617
|
+
repeating-linear-gradient(90deg, #e2e8f0 0 24px, transparent 24px 46px) center / 100% 4px no-repeat,
|
|
618
|
+
linear-gradient(180deg, #334155, #0f172a);
|
|
619
|
+
box-shadow: 0 18px 32px rgba(2, 6, 23, 0.42);
|
|
620
|
+
}
|
|
621
|
+
.game-scene__car {
|
|
622
|
+
position: absolute;
|
|
623
|
+
left: 88px;
|
|
624
|
+
top: 126px;
|
|
625
|
+
width: 58px;
|
|
626
|
+
height: 30px;
|
|
627
|
+
border-radius: 12px 18px 18px 12px;
|
|
628
|
+
background: linear-gradient(90deg, #fb7185, #f59e0b);
|
|
629
|
+
transform: rotate(-8deg);
|
|
630
|
+
box-shadow: 0 0 24px rgba(245, 158, 11, 0.52);
|
|
631
|
+
animation: markdyCarPark 3.4s cubic-bezier(.2,1,.3,1) infinite;
|
|
632
|
+
}
|
|
633
|
+
.game-scene__spot {
|
|
634
|
+
position: absolute;
|
|
635
|
+
right: 54px;
|
|
636
|
+
top: 85px;
|
|
637
|
+
width: 90px;
|
|
638
|
+
height: 68px;
|
|
639
|
+
border: 3px solid rgba(34, 197, 94, 0.78);
|
|
640
|
+
border-left-style: dashed;
|
|
641
|
+
border-radius: 10px;
|
|
642
|
+
filter: drop-shadow(0 0 12px rgba(34, 197, 94, 0.48));
|
|
643
|
+
}
|
|
644
|
+
.game-scene__cones {
|
|
645
|
+
position: absolute;
|
|
646
|
+
right: 80px;
|
|
647
|
+
bottom: 34px;
|
|
648
|
+
display: flex;
|
|
649
|
+
gap: 14px;
|
|
650
|
+
}
|
|
651
|
+
.game-scene__cone {
|
|
652
|
+
width: 0;
|
|
653
|
+
height: 0;
|
|
654
|
+
border-left: 10px solid transparent;
|
|
655
|
+
border-right: 10px solid transparent;
|
|
656
|
+
border-bottom: 26px solid #f97316;
|
|
657
|
+
filter: drop-shadow(0 5px 8px rgba(2, 6, 23, 0.35));
|
|
658
|
+
}
|
|
659
|
+
.game-scene__hud {
|
|
660
|
+
position: absolute;
|
|
661
|
+
left: 14px;
|
|
662
|
+
top: 14px;
|
|
663
|
+
min-width: 120px;
|
|
664
|
+
border-color: rgba(245, 158, 11, 0.38);
|
|
665
|
+
}
|
|
666
|
+
.game-scene__hud strong { color: #fed7aa; font-size: 18px; }
|
|
667
|
+
.game-scene__perfect {
|
|
668
|
+
position: absolute;
|
|
669
|
+
left: 132px;
|
|
670
|
+
top: 54px;
|
|
671
|
+
color: #fef3c7;
|
|
672
|
+
font-size: 21px;
|
|
673
|
+
font-weight: 900;
|
|
674
|
+
letter-spacing: 0.05em;
|
|
675
|
+
text-shadow: 0 0 18px rgba(245, 158, 11, 0.7);
|
|
676
|
+
animation: markdyPerfectPop 1.6s ease-in-out infinite;
|
|
677
|
+
}
|
|
678
|
+
.markdy-showcase--byte_viz { --accent: #a78bfa; --accent-2: #38bdf8; }
|
|
679
|
+
.byte-viz__pipeline {
|
|
680
|
+
position: absolute;
|
|
681
|
+
inset: 4px;
|
|
682
|
+
display: grid;
|
|
683
|
+
grid-template-columns: 98px 1fr 122px;
|
|
684
|
+
gap: 12px;
|
|
685
|
+
align-items: stretch;
|
|
686
|
+
}
|
|
687
|
+
.byte-viz__glyph {
|
|
688
|
+
display: grid;
|
|
689
|
+
place-items: center;
|
|
690
|
+
border-radius: 22px;
|
|
691
|
+
border: 1px solid rgba(167, 139, 250, 0.45);
|
|
692
|
+
background: radial-gradient(circle at 30% 20%, rgba(167, 139, 250, 0.42), rgba(15, 23, 42, 0.78));
|
|
693
|
+
color: #fff;
|
|
694
|
+
font-size: 72px;
|
|
695
|
+
font-weight: 920;
|
|
696
|
+
box-shadow: 0 0 34px rgba(167, 139, 250, 0.32) inset;
|
|
697
|
+
}
|
|
698
|
+
.byte-viz__middle { display: grid; gap: 10px; }
|
|
699
|
+
.byte-viz__row {
|
|
700
|
+
display: flex;
|
|
701
|
+
align-items: center;
|
|
702
|
+
gap: 7px;
|
|
703
|
+
}
|
|
704
|
+
.byte-viz__chip {
|
|
705
|
+
flex: 1 1 0;
|
|
706
|
+
border: 1px solid rgba(148, 163, 184, 0.25);
|
|
707
|
+
border-radius: 12px;
|
|
708
|
+
padding: 8px 10px;
|
|
709
|
+
background: rgba(2, 6, 23, 0.42);
|
|
710
|
+
color: #ddd6fe;
|
|
711
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
712
|
+
font-size: 12px;
|
|
713
|
+
font-weight: 800;
|
|
714
|
+
}
|
|
715
|
+
.byte-viz__arrow {
|
|
716
|
+
color: #67e8f9;
|
|
717
|
+
filter: drop-shadow(0 0 8px #38bdf8);
|
|
718
|
+
animation: markdyArrowPulse 1.1s ease-in-out infinite;
|
|
719
|
+
}
|
|
720
|
+
.byte-viz__bits {
|
|
721
|
+
display: grid;
|
|
722
|
+
grid-template-columns: repeat(8, 1fr);
|
|
723
|
+
gap: 5px;
|
|
724
|
+
}
|
|
725
|
+
.byte-viz__bit {
|
|
726
|
+
display: grid;
|
|
727
|
+
place-items: center;
|
|
728
|
+
height: 31px;
|
|
729
|
+
border-radius: 8px;
|
|
730
|
+
background: rgba(15, 23, 42, 0.82);
|
|
731
|
+
border: 1px solid rgba(56, 189, 248, 0.28);
|
|
732
|
+
color: #bae6fd;
|
|
733
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
734
|
+
font-size: 12px;
|
|
735
|
+
font-weight: 900;
|
|
736
|
+
animation: markdyBitFlip 1.9s ease-in-out infinite;
|
|
737
|
+
}
|
|
738
|
+
.byte-viz__bit:nth-child(2n) { animation-delay: 0.12s; }
|
|
739
|
+
.byte-viz__bit:nth-child(3n) { animation-delay: 0.24s; }
|
|
740
|
+
.byte-viz__side { display: grid; gap: 9px; }
|
|
741
|
+
@keyframes markdyGridDrift { to { background-position: 34px 34px, 34px 34px; } }
|
|
742
|
+
@keyframes markdyAurora { to { transform: rotate(1turn); } }
|
|
743
|
+
@keyframes markdyScanPulse { 50% { transform: translateX(18px); opacity: 0.72; } }
|
|
744
|
+
@keyframes markdyCarGlide { 0%, 12% { transform: translateX(0); } 68%, 100% { transform: translateX(198px); } }
|
|
745
|
+
@keyframes markdySlotGlow { 50% { box-shadow: 0 0 20px rgba(34, 197, 94, 0.38); } }
|
|
746
|
+
@keyframes markdyRouteFlow { to { filter: hue-rotate(70deg) drop-shadow(0 0 9px #38bdf8); } }
|
|
747
|
+
@keyframes markdyBlink { 50% { opacity: 0.25; } }
|
|
748
|
+
@keyframes markdyRoadMove { to { background-position: 44px 0; } }
|
|
749
|
+
@keyframes markdyCarPark { 0%, 12% { transform: translateX(0) rotate(-8deg); } 70%, 100% { transform: translateX(164px) translateY(-31px) rotate(-1deg); } }
|
|
750
|
+
@keyframes markdyPerfectPop { 0%, 100% { transform: scale(0.92); opacity: 0.72; } 50% { transform: scale(1.05); opacity: 1; } }
|
|
751
|
+
@keyframes markdyArrowPulse { 50% { transform: translateX(3px); opacity: 0.72; } }
|
|
752
|
+
@keyframes markdyBitFlip { 50% { transform: translateY(-2px); border-color: rgba(167, 139, 250, 0.75); } }
|
|
753
|
+
`;
|
|
754
|
+
doc.head.appendChild(style);
|
|
755
|
+
}
|
|
756
|
+
function createShowcaseSurfaceEl(type, def) {
|
|
757
|
+
ensureShowcaseStyles(document);
|
|
758
|
+
const label = def.args[0] || type.replace("_", " ");
|
|
759
|
+
const root = document.createElement("div");
|
|
760
|
+
root.className = `markdy-showcase markdy-showcase--${type}`;
|
|
761
|
+
root.setAttribute("role", "img");
|
|
762
|
+
root.setAttribute("aria-label", label);
|
|
763
|
+
if (type === "parking_map") {
|
|
764
|
+
root.innerHTML = `
|
|
765
|
+
<div class="markdy-showcase__top"><div class="markdy-showcase__title"></div><div class="markdy-showcase__pill">live ops</div></div>
|
|
766
|
+
<div class="markdy-showcase__body">
|
|
767
|
+
<div class="parking-map__scan"></div><div class="parking-map__road"></div><div class="parking-map__route"></div><div class="parking-map__car"></div>
|
|
768
|
+
<div class="parking-map__slots"><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span><span class="parking-map__slot"></span></div>
|
|
769
|
+
<div class="parking-map__stats"><div class="parking-map__stat">occupancy<strong>87%</strong></div><div class="parking-map__stat">OCR<strong>18ms</strong></div><div class="parking-map__stat">route<strong>A-17</strong></div></div>
|
|
770
|
+
</div>`;
|
|
771
|
+
} else if (type === "ascii_map") {
|
|
772
|
+
root.innerHTML = `
|
|
773
|
+
<div class="markdy-showcase__top"><div class="markdy-showcase__title"></div><div class="markdy-showcase__pill">parser</div></div>
|
|
774
|
+
<div class="markdy-showcase__body">
|
|
775
|
+
<div class="ascii-map__terminal"><div class="ascii-map__chrome"><span></span></div><div class="ascii-map__code">[P1][P2][<b> </b>][EV]<br />[IN]===LANE===[OUT]<br />0101 0000 0100 0001</div><div class="ascii-map__minimap"><i class="ascii-map__mini-slot"></i><i class="ascii-map__mini-slot"></i><i class="ascii-map__mini-slot"></i><i class="ascii-map__mini-slot"></i><i class="ascii-map__mini-slot"></i><i class="ascii-map__mini-slot"></i></div><div class="ascii-map__transform"><div class="ascii-map__badge">tokens</div><div class="ascii-map__badge">graph</div><div class="ascii-map__badge">slots</div></div></div>
|
|
776
|
+
</div>`;
|
|
777
|
+
} else if (type === "game_scene") {
|
|
778
|
+
root.innerHTML = `
|
|
779
|
+
<div class="markdy-showcase__top"><div class="markdy-showcase__title"></div><div class="markdy-showcase__pill">60 fps</div></div>
|
|
780
|
+
<div class="markdy-showcase__body">
|
|
781
|
+
<div class="game-scene__world"><div class="game-scene__hud">combo<br /><strong>x4.2</strong></div><div class="game-scene__perfect">PERFECT PARK</div><div class="game-scene__road"></div><div class="game-scene__spot"></div><div class="game-scene__car"></div><div class="game-scene__cones"><i class="game-scene__cone"></i><i class="game-scene__cone"></i><i class="game-scene__cone"></i></div></div>
|
|
782
|
+
</div>`;
|
|
783
|
+
} else {
|
|
784
|
+
root.innerHTML = `
|
|
785
|
+
<div class="markdy-showcase__top"><div class="markdy-showcase__title"></div><div class="markdy-showcase__pill">UTF-8</div></div>
|
|
786
|
+
<div class="markdy-showcase__body">
|
|
787
|
+
<div class="byte-viz__pipeline"><div class="byte-viz__glyph">A</div><div class="byte-viz__middle"><div class="byte-viz__row"><div class="byte-viz__chip">char</div><span class="byte-viz__arrow">-></span><div class="byte-viz__chip">U+0041</div></div><div class="byte-viz__row"><div class="byte-viz__chip">0x41</div><span class="byte-viz__arrow">-></span><div class="byte-viz__chip">01000001</div></div><div class="byte-viz__bits"><span class="byte-viz__bit">0</span><span class="byte-viz__bit">1</span><span class="byte-viz__bit">0</span><span class="byte-viz__bit">0</span><span class="byte-viz__bit">0</span><span class="byte-viz__bit">0</span><span class="byte-viz__bit">0</span><span class="byte-viz__bit">1</span></div></div><div class="byte-viz__side"><div class="byte-viz__badge">bytes<strong>1</strong></div><div class="byte-viz__badge">planes<strong>BMP</strong></div><div class="byte-viz__badge">glyph<strong>paint</strong></div></div></div>
|
|
788
|
+
</div>`;
|
|
789
|
+
}
|
|
790
|
+
const title = root.querySelector(".markdy-showcase__title");
|
|
791
|
+
if (title) title.textContent = label;
|
|
792
|
+
return root;
|
|
793
|
+
}
|
|
794
|
+
function ensureArchitectureStyles(doc) {
|
|
795
|
+
if (doc.getElementById(ARCHITECTURE_STYLE_ID)) return;
|
|
796
|
+
const style = doc.createElement("style");
|
|
797
|
+
style.id = ARCHITECTURE_STYLE_ID;
|
|
798
|
+
style.textContent = `
|
|
799
|
+
.markdy-arch-node {
|
|
800
|
+
--markdy-node-accent: #38bdf8;
|
|
801
|
+
--markdy-node-accent-2: #22c55e;
|
|
802
|
+
--markdy-node-surface: rgba(15, 23, 42, 0.82);
|
|
803
|
+
--markdy-node-border: rgba(148, 163, 184, 0.34);
|
|
804
|
+
--markdy-node-glow: rgba(56, 189, 248, 0.24);
|
|
805
|
+
position: relative;
|
|
806
|
+
isolation: isolate;
|
|
807
|
+
width: 184px;
|
|
808
|
+
min-height: 88px;
|
|
809
|
+
box-sizing: border-box;
|
|
810
|
+
display: grid;
|
|
811
|
+
grid-template-columns: 34px minmax(0, 1fr);
|
|
812
|
+
align-items: center;
|
|
813
|
+
gap: 10px;
|
|
814
|
+
padding: 13px 14px;
|
|
815
|
+
border: 1px solid var(--markdy-node-border);
|
|
816
|
+
border-radius: 14px;
|
|
817
|
+
color: #e5eefb;
|
|
818
|
+
background:
|
|
819
|
+
linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.02) 34%, rgba(15, 23, 42, 0.88) 100%),
|
|
820
|
+
radial-gradient(circle at 18% 0%, color-mix(in srgb, var(--markdy-node-accent) 34%, transparent), transparent 42%),
|
|
821
|
+
var(--markdy-node-surface);
|
|
822
|
+
box-shadow:
|
|
823
|
+
0 16px 34px rgba(2, 6, 23, 0.28),
|
|
824
|
+
0 0 0 1px rgba(255, 255, 255, 0.04) inset,
|
|
825
|
+
0 1px 0 rgba(255, 255, 255, 0.12) inset,
|
|
826
|
+
0 0 28px var(--markdy-node-glow);
|
|
827
|
+
overflow: hidden;
|
|
828
|
+
contain: layout paint style;
|
|
829
|
+
backdrop-filter: blur(14px) saturate(1.18);
|
|
830
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.18);
|
|
831
|
+
}
|
|
832
|
+
.markdy-arch-node::before {
|
|
833
|
+
content: "";
|
|
834
|
+
position: absolute;
|
|
835
|
+
inset: 0;
|
|
836
|
+
z-index: -1;
|
|
837
|
+
background:
|
|
838
|
+
linear-gradient(90deg, transparent 0 24%, rgba(255, 255, 255, 0.06) 50%, transparent 76%) -180px 0 / 180px 100% no-repeat,
|
|
839
|
+
repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.055) 0 1px, transparent 1px 10px);
|
|
840
|
+
opacity: 0.6;
|
|
841
|
+
}
|
|
842
|
+
.markdy-arch-node::after {
|
|
843
|
+
content: "";
|
|
844
|
+
position: absolute;
|
|
845
|
+
left: 14px;
|
|
846
|
+
right: 14px;
|
|
847
|
+
bottom: 9px;
|
|
848
|
+
height: 2px;
|
|
849
|
+
border-radius: 999px;
|
|
850
|
+
background: linear-gradient(90deg, transparent, var(--markdy-node-accent), var(--markdy-node-accent-2), transparent);
|
|
851
|
+
opacity: 0.82;
|
|
852
|
+
filter: drop-shadow(0 0 6px var(--markdy-node-accent));
|
|
853
|
+
}
|
|
854
|
+
.markdy-arch-node__icon {
|
|
855
|
+
position: relative;
|
|
856
|
+
width: 32px;
|
|
857
|
+
height: 32px;
|
|
858
|
+
border-radius: 11px;
|
|
859
|
+
background:
|
|
860
|
+
radial-gradient(circle at 35% 25%, rgba(255, 255, 255, 0.28), transparent 42%),
|
|
861
|
+
linear-gradient(145deg, var(--markdy-node-accent), color-mix(in srgb, var(--markdy-node-accent) 38%, #020617));
|
|
862
|
+
box-shadow:
|
|
863
|
+
0 0 0 1px rgba(255, 255, 255, 0.16) inset,
|
|
864
|
+
0 10px 20px color-mix(in srgb, var(--markdy-node-accent) 25%, transparent);
|
|
865
|
+
}
|
|
866
|
+
.markdy-arch-node__icon::before,
|
|
867
|
+
.markdy-arch-node__icon::after {
|
|
868
|
+
content: "";
|
|
869
|
+
position: absolute;
|
|
870
|
+
box-sizing: border-box;
|
|
871
|
+
border-color: rgba(255, 255, 255, 0.9);
|
|
872
|
+
}
|
|
873
|
+
.markdy-arch-node__label {
|
|
874
|
+
min-width: 0;
|
|
875
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
876
|
+
font-size: 15px;
|
|
877
|
+
font-weight: 720;
|
|
878
|
+
line-height: 1.08;
|
|
879
|
+
color: #f8fafc;
|
|
880
|
+
text-shadow: 0 1px 12px rgba(15, 23, 42, 0.8);
|
|
881
|
+
white-space: nowrap;
|
|
882
|
+
overflow: hidden;
|
|
883
|
+
text-overflow: ellipsis;
|
|
884
|
+
}
|
|
885
|
+
.markdy-arch-node__type {
|
|
886
|
+
display: block;
|
|
887
|
+
margin-top: 5px;
|
|
888
|
+
font-size: 9px;
|
|
889
|
+
font-weight: 740;
|
|
890
|
+
letter-spacing: 0.08em;
|
|
891
|
+
text-transform: uppercase;
|
|
892
|
+
color: color-mix(in srgb, var(--markdy-node-accent) 70%, #e2e8f0);
|
|
893
|
+
opacity: 0.86;
|
|
894
|
+
}
|
|
895
|
+
.markdy-arch-node[data-markdy-system-type="service"],
|
|
896
|
+
.markdy-arch-node[data-markdy-system-type="api"],
|
|
897
|
+
.markdy-arch-node[data-markdy-system-type="microservice"] {
|
|
898
|
+
--markdy-node-accent: #38bdf8;
|
|
899
|
+
--markdy-node-accent-2: #818cf8;
|
|
900
|
+
}
|
|
901
|
+
.markdy-arch-node[data-markdy-system-type="client"],
|
|
902
|
+
.markdy-arch-node[data-markdy-system-type="user"] {
|
|
903
|
+
--markdy-node-accent: #f59e0b;
|
|
904
|
+
--markdy-node-accent-2: #fb7185;
|
|
905
|
+
}
|
|
906
|
+
.markdy-arch-node[data-markdy-system-type="database"],
|
|
907
|
+
.markdy-arch-node[data-markdy-system-type="db"] {
|
|
908
|
+
--markdy-node-accent: #22c55e;
|
|
909
|
+
--markdy-node-accent-2: #14b8a6;
|
|
910
|
+
border-radius: 18px 18px 24px 24px;
|
|
911
|
+
}
|
|
912
|
+
.markdy-arch-node[data-markdy-system-type="cache"] {
|
|
913
|
+
--markdy-node-accent: #a3e635;
|
|
914
|
+
--markdy-node-accent-2: #22c55e;
|
|
915
|
+
border-style: dashed;
|
|
916
|
+
}
|
|
917
|
+
.markdy-arch-node[data-markdy-system-type="queue"] {
|
|
918
|
+
--markdy-node-accent: #a78bfa;
|
|
919
|
+
--markdy-node-accent-2: #38bdf8;
|
|
920
|
+
}
|
|
921
|
+
.markdy-arch-node[data-markdy-system-type="cloud"],
|
|
922
|
+
.markdy-arch-node[data-markdy-system-type="region"] {
|
|
923
|
+
--markdy-node-accent: #60a5fa;
|
|
924
|
+
--markdy-node-accent-2: #67e8f9;
|
|
925
|
+
border-radius: 999px;
|
|
926
|
+
}
|
|
927
|
+
.markdy-arch-node[data-markdy-system-type="container"],
|
|
928
|
+
.markdy-arch-node[data-markdy-system-type="cluster"] {
|
|
929
|
+
--markdy-node-accent: #c084fc;
|
|
930
|
+
--markdy-node-accent-2: #f472b6;
|
|
931
|
+
border-radius: 10px;
|
|
932
|
+
}
|
|
933
|
+
.markdy-arch-node[data-markdy-system-type="service"] .markdy-arch-node__icon::before,
|
|
934
|
+
.markdy-arch-node[data-markdy-system-type="api"] .markdy-arch-node__icon::before,
|
|
935
|
+
.markdy-arch-node[data-markdy-system-type="microservice"] .markdy-arch-node__icon::before {
|
|
936
|
+
inset: 9px 7px;
|
|
937
|
+
border-top: 3px solid rgba(255, 255, 255, 0.92);
|
|
938
|
+
border-bottom: 3px solid rgba(255, 255, 255, 0.92);
|
|
939
|
+
}
|
|
940
|
+
.markdy-arch-node[data-markdy-system-type="service"] .markdy-arch-node__icon::after,
|
|
941
|
+
.markdy-arch-node[data-markdy-system-type="api"] .markdy-arch-node__icon::after,
|
|
942
|
+
.markdy-arch-node[data-markdy-system-type="microservice"] .markdy-arch-node__icon::after {
|
|
943
|
+
inset: 14px 7px auto;
|
|
944
|
+
border-top: 3px solid rgba(255, 255, 255, 0.92);
|
|
945
|
+
}
|
|
946
|
+
.markdy-arch-node[data-markdy-system-type="client"] .markdy-arch-node__icon::before,
|
|
947
|
+
.markdy-arch-node[data-markdy-system-type="user"] .markdy-arch-node__icon::before {
|
|
948
|
+
left: 8px;
|
|
949
|
+
right: 8px;
|
|
950
|
+
top: 8px;
|
|
951
|
+
height: 13px;
|
|
952
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
953
|
+
border-radius: 3px;
|
|
954
|
+
}
|
|
955
|
+
.markdy-arch-node[data-markdy-system-type="client"] .markdy-arch-node__icon::after,
|
|
956
|
+
.markdy-arch-node[data-markdy-system-type="user"] .markdy-arch-node__icon::after {
|
|
957
|
+
left: 12px;
|
|
958
|
+
right: 12px;
|
|
959
|
+
bottom: 7px;
|
|
960
|
+
border-top: 2px solid rgba(255, 255, 255, 0.92);
|
|
961
|
+
}
|
|
962
|
+
.markdy-arch-node[data-markdy-system-type="database"] .markdy-arch-node__icon::before,
|
|
963
|
+
.markdy-arch-node[data-markdy-system-type="db"] .markdy-arch-node__icon::before,
|
|
964
|
+
.markdy-arch-node[data-markdy-system-type="cache"] .markdy-arch-node__icon::before {
|
|
965
|
+
inset: 7px 7px 9px;
|
|
966
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
967
|
+
border-radius: 50% / 16%;
|
|
968
|
+
}
|
|
969
|
+
.markdy-arch-node[data-markdy-system-type="database"] .markdy-arch-node__icon::after,
|
|
970
|
+
.markdy-arch-node[data-markdy-system-type="db"] .markdy-arch-node__icon::after,
|
|
971
|
+
.markdy-arch-node[data-markdy-system-type="cache"] .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-type="queue"] .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-type="queue"] .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-type="cloud"] .markdy-arch-node__icon::before,
|
|
991
|
+
.markdy-arch-node[data-markdy-system-type="region"] .markdy-arch-node__icon::before {
|
|
992
|
+
left: 7px;
|
|
993
|
+
right: 7px;
|
|
994
|
+
bottom: 9px;
|
|
995
|
+
height: 12px;
|
|
996
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
997
|
+
border-radius: 999px;
|
|
998
|
+
}
|
|
999
|
+
.markdy-arch-node[data-markdy-system-type="cloud"] .markdy-arch-node__icon::after,
|
|
1000
|
+
.markdy-arch-node[data-markdy-system-type="region"] .markdy-arch-node__icon::after {
|
|
1001
|
+
left: 10px;
|
|
1002
|
+
top: 7px;
|
|
1003
|
+
width: 13px;
|
|
1004
|
+
height: 13px;
|
|
1005
|
+
border-top: 2px solid rgba(255, 255, 255, 0.92);
|
|
1006
|
+
border-left: 2px solid rgba(255, 255, 255, 0.92);
|
|
1007
|
+
border-radius: 999px 0 0 0;
|
|
1008
|
+
}
|
|
1009
|
+
.markdy-arch-node[data-markdy-system-type="container"] .markdy-arch-node__icon::before,
|
|
1010
|
+
.markdy-arch-node[data-markdy-system-type="cluster"] .markdy-arch-node__icon::before {
|
|
1011
|
+
inset: 8px;
|
|
1012
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
1013
|
+
border-radius: 4px;
|
|
1014
|
+
}
|
|
1015
|
+
.markdy-arch-node[data-markdy-system-type="container"] .markdy-arch-node__icon::after,
|
|
1016
|
+
.markdy-arch-node[data-markdy-system-type="cluster"] .markdy-arch-node__icon::after {
|
|
1017
|
+
left: 10px;
|
|
1018
|
+
right: 10px;
|
|
1019
|
+
top: 14px;
|
|
1020
|
+
border-top: 2px solid rgba(255, 255, 255, 0.72);
|
|
1021
|
+
}
|
|
1022
|
+
`;
|
|
1023
|
+
doc.head.appendChild(style);
|
|
1024
|
+
}
|
|
1025
|
+
function isArchitectureNodeType(type) {
|
|
1026
|
+
return ARCHITECTURE_NODE_TYPES.has(type);
|
|
1027
|
+
}
|
|
1028
|
+
function isShowcaseSurfaceType(type) {
|
|
1029
|
+
return SHOWCASE_SURFACE_TYPES.has(type);
|
|
1030
|
+
}
|
|
1031
|
+
function architectureTypeLabel(type) {
|
|
1032
|
+
if (type === "db") return "database";
|
|
1033
|
+
if (type === "api") return "service";
|
|
1034
|
+
return type;
|
|
1035
|
+
}
|
|
312
1036
|
function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
313
1037
|
let el;
|
|
314
1038
|
switch (def.type) {
|
|
@@ -377,62 +1101,44 @@ function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
|
377
1101
|
el = createFigureEl(def);
|
|
378
1102
|
break;
|
|
379
1103
|
}
|
|
1104
|
+
case "parking_map":
|
|
1105
|
+
case "ascii_map":
|
|
1106
|
+
case "game_scene":
|
|
1107
|
+
case "byte_viz": {
|
|
1108
|
+
el = createShowcaseSurfaceEl(def.type, def);
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
380
1111
|
case "service":
|
|
1112
|
+
case "api":
|
|
1113
|
+
case "microservice":
|
|
381
1114
|
case "client":
|
|
1115
|
+
case "user":
|
|
382
1116
|
case "db":
|
|
383
|
-
case "
|
|
1117
|
+
case "database":
|
|
1118
|
+
case "queue":
|
|
1119
|
+
case "cache":
|
|
1120
|
+
case "cloud":
|
|
1121
|
+
case "region":
|
|
1122
|
+
case "container":
|
|
1123
|
+
case "cluster": {
|
|
1124
|
+
ensureArchitectureStyles(document);
|
|
384
1125
|
const card = document.createElement("div");
|
|
1126
|
+
card.className = "markdy-arch-node";
|
|
1127
|
+
card.dataset.markdySystemType = def.type;
|
|
1128
|
+
card.setAttribute("role", "img");
|
|
1129
|
+
card.setAttribute("aria-label", `${architectureTypeLabel(def.type)} ${def.args[0] ?? name}`);
|
|
1130
|
+
const icon = document.createElement("span");
|
|
1131
|
+
icon.className = "markdy-arch-node__icon";
|
|
1132
|
+
icon.setAttribute("aria-hidden", "true");
|
|
385
1133
|
const label = document.createElement("div");
|
|
1134
|
+
label.className = "markdy-arch-node__label";
|
|
386
1135
|
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
|
-
}
|
|
1136
|
+
label.style.fontSize = `${def.size ?? 15}px`;
|
|
1137
|
+
const typeLabel = document.createElement("span");
|
|
1138
|
+
typeLabel.className = "markdy-arch-node__type";
|
|
1139
|
+
typeLabel.textContent = architectureTypeLabel(def.type);
|
|
1140
|
+
label.appendChild(typeLabel);
|
|
1141
|
+
card.appendChild(icon);
|
|
436
1142
|
card.appendChild(label);
|
|
437
1143
|
el = card;
|
|
438
1144
|
break;
|
|
@@ -456,82 +1162,74 @@ function createActorEl(name, def, assetDefs, assetOverrides) {
|
|
|
456
1162
|
el.style.opacity = String(def.opacity ?? 1);
|
|
457
1163
|
if (def.z !== void 0) el.style.zIndex = String(def.z);
|
|
458
1164
|
else if (def.type === "caption") el.style.zIndex = "100";
|
|
1165
|
+
else if (isShowcaseSurfaceType(def.type)) el.style.zIndex = "25";
|
|
1166
|
+
else if (isArchitectureNodeType(def.type)) el.style.zIndex = "10";
|
|
459
1167
|
return el;
|
|
460
1168
|
}
|
|
461
1169
|
|
|
462
|
-
// src/
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
fade_in: "out",
|
|
466
|
-
exit: "in",
|
|
467
|
-
fade_out: "in"
|
|
468
|
-
};
|
|
469
|
-
function isSceneDark(scene) {
|
|
470
|
-
const bg = scene.style.background || "white";
|
|
471
|
-
let hex = bg.trim().replace(/^#/, "");
|
|
472
|
-
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
473
|
-
if (hex.length === 6) {
|
|
474
|
-
const r = parseInt(hex.slice(0, 2), 16);
|
|
475
|
-
const g = parseInt(hex.slice(2, 4), 16);
|
|
476
|
-
const b = parseInt(hex.slice(4, 6), 16);
|
|
477
|
-
return 0.299 * r + 0.587 * g + 0.114 * b <= 140;
|
|
478
|
-
}
|
|
479
|
-
const dark = { black: true, "#000": true, "#000000": true };
|
|
480
|
-
return dark[bg.toLowerCase()] ?? false;
|
|
1170
|
+
// src/camera.ts
|
|
1171
|
+
function freshCameraState() {
|
|
1172
|
+
return { x: 0, y: 0, zoom: 1 };
|
|
481
1173
|
}
|
|
482
|
-
function
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const easing = toEasing(ev.params.ease ?? DEFAULT_EASE_BY_ACTION[ev.action]);
|
|
503
|
-
const baseOpts = {
|
|
504
|
-
delay: delayMs,
|
|
505
|
-
duration: durMs,
|
|
506
|
-
fill: "forwards",
|
|
507
|
-
easing
|
|
508
|
-
};
|
|
509
|
-
if (ev.actor === "camera") {
|
|
510
|
-
buildCameraAction(ev, scene, ast, baseOpts, anims, cameraState);
|
|
511
|
-
continue;
|
|
1174
|
+
function cameraTx(s) {
|
|
1175
|
+
return `translate(${-s.x}px, ${-s.y}px) scale(${s.zoom})`;
|
|
1176
|
+
}
|
|
1177
|
+
var DEFAULT_SHAKE_INTENSITY = 8;
|
|
1178
|
+
function buildCameraAction(ev, scene, ast, baseOpts, anims, state) {
|
|
1179
|
+
switch (ev.action) {
|
|
1180
|
+
case "pan": {
|
|
1181
|
+
const to = ev.params.to;
|
|
1182
|
+
if (!to) break;
|
|
1183
|
+
const next = {
|
|
1184
|
+
...state,
|
|
1185
|
+
x: to[0] - ast.meta.width / 2,
|
|
1186
|
+
y: to[1] - ast.meta.height / 2
|
|
1187
|
+
};
|
|
1188
|
+
anims.push(
|
|
1189
|
+
scene.animate([{ transform: cameraTx(state) }, { transform: cameraTx(next) }], baseOpts)
|
|
1190
|
+
);
|
|
1191
|
+
state.x = next.x;
|
|
1192
|
+
state.y = next.y;
|
|
1193
|
+
break;
|
|
512
1194
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
1195
|
+
case "zoom": {
|
|
1196
|
+
const next = {
|
|
1197
|
+
...state,
|
|
1198
|
+
zoom: typeof ev.params.to === "number" ? ev.params.to : state.zoom
|
|
1199
|
+
};
|
|
1200
|
+
anims.push(
|
|
1201
|
+
scene.animate([{ transform: cameraTx(state) }, { transform: cameraTx(next) }], baseOpts)
|
|
1202
|
+
);
|
|
1203
|
+
state.zoom = next.zoom;
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
case "shake": {
|
|
1207
|
+
const mag = typeof ev.params.intensity === "number" ? ev.params.intensity : DEFAULT_SHAKE_INTENSITY;
|
|
1208
|
+
const at = (dx, dy, offset) => ({
|
|
1209
|
+
transform: cameraTx({ ...state, x: state.x + dx, y: state.y + dy }),
|
|
1210
|
+
offset
|
|
1211
|
+
});
|
|
1212
|
+
anims.push(
|
|
1213
|
+
scene.animate(
|
|
1214
|
+
[
|
|
1215
|
+
at(0, 0, 0),
|
|
1216
|
+
at(-mag, -mag * 0.4, 0.15),
|
|
1217
|
+
at(mag, mag * 0.4, 0.35),
|
|
1218
|
+
at(-mag * 0.6, mag * 0.3, 0.55),
|
|
1219
|
+
at(mag * 0.5, -mag * 0.3, 0.75),
|
|
1220
|
+
at(0, 0, 1)
|
|
1221
|
+
],
|
|
1222
|
+
{ ...baseOpts, easing: "linear" }
|
|
1223
|
+
)
|
|
1224
|
+
);
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
default:
|
|
1228
|
+
break;
|
|
532
1229
|
}
|
|
533
|
-
return anims;
|
|
534
1230
|
}
|
|
1231
|
+
|
|
1232
|
+
// src/stage.ts
|
|
535
1233
|
function offscreenState(s, direction, sceneWidth, sceneHeight) {
|
|
536
1234
|
const out = { ...s };
|
|
537
1235
|
switch (direction) {
|
|
@@ -561,113 +1259,409 @@ function preInitInlineStyles(ast, actorEls, states, events, txFor) {
|
|
|
561
1259
|
const s = states.get(name);
|
|
562
1260
|
if (!el || !s) continue;
|
|
563
1261
|
const firstEv = firstEventByActor.get(name);
|
|
564
|
-
|
|
565
|
-
if (firstEv
|
|
1262
|
+
if (!firstEv) continue;
|
|
1263
|
+
if (firstEv.action === "enter") {
|
|
566
1264
|
const from = String(firstEv.params.from ?? "left");
|
|
567
|
-
|
|
568
|
-
el.style.transform = txFn(offscreen);
|
|
1265
|
+
el.style.transform = txFor(name)(offscreenState(s, from, ast.meta.width, ast.meta.height));
|
|
569
1266
|
}
|
|
570
|
-
if (firstEv
|
|
1267
|
+
if (firstEv.action === "fade_in" && (def.opacity === void 0 || def.opacity > 0)) {
|
|
571
1268
|
el.style.opacity = "0";
|
|
572
1269
|
}
|
|
573
1270
|
}
|
|
574
1271
|
}
|
|
575
|
-
|
|
576
|
-
|
|
1272
|
+
|
|
1273
|
+
// src/actions/figure.ts
|
|
1274
|
+
function sideOf(ctx) {
|
|
1275
|
+
return String(ctx.ev.params.side ?? "right") === "left" ? "left" : "right";
|
|
577
1276
|
}
|
|
578
|
-
function
|
|
579
|
-
|
|
1277
|
+
function partEl(ctx, part) {
|
|
1278
|
+
const selector = PART_SEL[part];
|
|
1279
|
+
return selector ? ctx.el.querySelector(selector) : null;
|
|
580
1280
|
}
|
|
581
|
-
function
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1281
|
+
function rotateKeyframe(deg, offset) {
|
|
1282
|
+
return offset === void 0 ? { transform: `rotate(${deg}deg)` } : { transform: `rotate(${deg}deg)`, offset };
|
|
1283
|
+
}
|
|
1284
|
+
function swingLimb(ctx, part, extendDeg, peakOffset) {
|
|
1285
|
+
const el = partEl(ctx, part);
|
|
1286
|
+
if (!el) return;
|
|
1287
|
+
const rest = readRotation(el);
|
|
1288
|
+
ctx.anims.push(
|
|
1289
|
+
el.animate([rotateKeyframe(rest), rotateKeyframe(extendDeg, peakOffset), rotateKeyframe(rest)], {
|
|
1290
|
+
...ctx.baseOpts,
|
|
1291
|
+
easing: "ease-in-out",
|
|
1292
|
+
fill: "forwards"
|
|
1293
|
+
})
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
var punch = (ctx) => {
|
|
1297
|
+
const side = sideOf(ctx);
|
|
1298
|
+
swingLimb(ctx, side === "left" ? "arm_left" : "arm_right", side === "left" ? -75 : 75, 0.35);
|
|
1299
|
+
};
|
|
1300
|
+
var kick = (ctx) => {
|
|
1301
|
+
const side = sideOf(ctx);
|
|
1302
|
+
swingLimb(ctx, side === "left" ? "leg_left" : "leg_right", side === "left" ? -100 : 100, 0.38);
|
|
1303
|
+
};
|
|
1304
|
+
var wave = (ctx) => {
|
|
1305
|
+
const side = sideOf(ctx);
|
|
1306
|
+
const el = partEl(ctx, side === "left" ? "arm_left" : "arm_right");
|
|
1307
|
+
if (!el) return;
|
|
1308
|
+
const rest = readRotation(el);
|
|
1309
|
+
const up = side === "left" ? 70 : -70;
|
|
1310
|
+
const inward = side === "left" ? 50 : -50;
|
|
1311
|
+
ctx.anims.push(
|
|
1312
|
+
el.animate(
|
|
1313
|
+
[
|
|
1314
|
+
rotateKeyframe(rest, 0),
|
|
1315
|
+
rotateKeyframe(up, 0.2),
|
|
1316
|
+
rotateKeyframe(inward, 0.4),
|
|
1317
|
+
rotateKeyframe(up, 0.55),
|
|
1318
|
+
rotateKeyframe(inward, 0.7),
|
|
1319
|
+
rotateKeyframe(up, 0.85),
|
|
1320
|
+
rotateKeyframe(rest, 1)
|
|
1321
|
+
],
|
|
1322
|
+
{ ...ctx.baseOpts, easing: "ease-in-out", fill: "forwards" }
|
|
1323
|
+
)
|
|
1324
|
+
);
|
|
1325
|
+
};
|
|
1326
|
+
var nod = (ctx) => {
|
|
1327
|
+
const el = partEl(ctx, "head");
|
|
1328
|
+
if (!el) return;
|
|
1329
|
+
const rest = readRotation(el);
|
|
1330
|
+
const down = 15;
|
|
1331
|
+
ctx.anims.push(
|
|
1332
|
+
el.animate(
|
|
1333
|
+
[
|
|
1334
|
+
rotateKeyframe(rest, 0),
|
|
1335
|
+
rotateKeyframe(down, 0.35),
|
|
1336
|
+
rotateKeyframe(rest, 0.65),
|
|
1337
|
+
rotateKeyframe(down, 0.8),
|
|
1338
|
+
rotateKeyframe(rest, 1)
|
|
1339
|
+
],
|
|
1340
|
+
{ ...ctx.baseOpts, easing: "ease-in-out", fill: "forwards" }
|
|
1341
|
+
)
|
|
1342
|
+
);
|
|
1343
|
+
};
|
|
1344
|
+
function commitRotation(el, deg) {
|
|
1345
|
+
el.style.transform = el.style.transform.replace(/rotate\([^)]*\)/, `rotate(${deg}deg)`);
|
|
1346
|
+
}
|
|
1347
|
+
var rotatePart = (ctx) => {
|
|
1348
|
+
const el = partEl(ctx, String(ctx.ev.params.part ?? ""));
|
|
1349
|
+
if (!el) return;
|
|
1350
|
+
const from = readRotation(el);
|
|
1351
|
+
const to = typeof ctx.ev.params.to === "number" ? ctx.ev.params.to : from;
|
|
1352
|
+
ctx.anims.push(
|
|
1353
|
+
el.animate([rotateKeyframe(from), rotateKeyframe(to)], { ...ctx.baseOpts, fill: "forwards" })
|
|
1354
|
+
);
|
|
1355
|
+
commitRotation(el, to);
|
|
1356
|
+
};
|
|
1357
|
+
var POSEABLE_PARTS = ["arm_left", "arm_right", "leg_left", "leg_right", "head", "body"];
|
|
1358
|
+
var pose = (ctx) => {
|
|
1359
|
+
for (const part of POSEABLE_PARTS) {
|
|
1360
|
+
const to = ctx.ev.params[part];
|
|
1361
|
+
if (typeof to !== "number") continue;
|
|
1362
|
+
const el = partEl(ctx, part);
|
|
1363
|
+
if (!el) continue;
|
|
1364
|
+
ctx.anims.push(
|
|
1365
|
+
el.animate([rotateKeyframe(readRotation(el)), rotateKeyframe(to)], {
|
|
1366
|
+
...ctx.baseOpts,
|
|
1367
|
+
fill: "forwards"
|
|
1368
|
+
})
|
|
1369
|
+
);
|
|
1370
|
+
commitRotation(el, to);
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
var face = (ctx) => {
|
|
1374
|
+
const el = ctx.el.querySelector("[data-fig-face]");
|
|
1375
|
+
if (!el) return;
|
|
1376
|
+
const emoji = String(ctx.ev.params.text ?? ctx.ev.params._0 ?? "");
|
|
1377
|
+
if (emoji) ctx.faceSwaps.push({ timeMs: ctx.ev.time * 1e3, el, emoji });
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/actions/transform.ts
|
|
1381
|
+
var move = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1382
|
+
const to = ev.params.to;
|
|
1383
|
+
const toX = to?.[0] ?? state.x;
|
|
1384
|
+
const toY = to?.[1] ?? state.y;
|
|
1385
|
+
anims.push(el.animate([{ transform: tx2(state) }, { transform: tx2({ ...state, x: toX, y: toY }) }], baseOpts));
|
|
1386
|
+
state.x = toX;
|
|
1387
|
+
state.y = toY;
|
|
1388
|
+
};
|
|
1389
|
+
var enter = ({ ev, el, state, baseOpts, ast, anims, tx: tx2 }) => {
|
|
1390
|
+
const from = String(ev.params.from ?? "left");
|
|
1391
|
+
const fromState = offscreenState(state, from, ast.meta.width, ast.meta.height);
|
|
1392
|
+
anims.push(
|
|
1393
|
+
el.animate(
|
|
1394
|
+
[
|
|
1395
|
+
{ transform: tx2(fromState), opacity: state.opacity },
|
|
1396
|
+
{ transform: tx2(state), opacity: 1 }
|
|
1397
|
+
],
|
|
1398
|
+
baseOpts
|
|
1399
|
+
)
|
|
1400
|
+
);
|
|
1401
|
+
state.opacity = 1;
|
|
1402
|
+
};
|
|
1403
|
+
var exit = ({ ev, el, state, baseOpts, ast, anims, tx: tx2 }) => {
|
|
1404
|
+
const to = String(ev.params.to ?? "right");
|
|
1405
|
+
const toState = offscreenState(state, to, ast.meta.width, ast.meta.height);
|
|
1406
|
+
anims.push(
|
|
1407
|
+
el.animate(
|
|
1408
|
+
[
|
|
1409
|
+
{ transform: tx2(state), opacity: state.opacity },
|
|
1410
|
+
{ transform: tx2(toState), opacity: 0 }
|
|
1411
|
+
],
|
|
1412
|
+
baseOpts
|
|
1413
|
+
)
|
|
1414
|
+
);
|
|
1415
|
+
state.x = toState.x;
|
|
1416
|
+
state.y = toState.y;
|
|
1417
|
+
state.opacity = 0;
|
|
1418
|
+
};
|
|
1419
|
+
var fadeIn = ({ el, state, baseOpts, anims }) => {
|
|
1420
|
+
anims.push(el.animate([{ opacity: 0 }, { opacity: 1 }], baseOpts));
|
|
1421
|
+
state.opacity = 1;
|
|
1422
|
+
};
|
|
1423
|
+
var fadeOut = ({ el, state, baseOpts, anims }) => {
|
|
1424
|
+
anims.push(el.animate([{ opacity: state.opacity }, { opacity: 0 }], baseOpts));
|
|
1425
|
+
state.opacity = 0;
|
|
1426
|
+
};
|
|
1427
|
+
var scale = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1428
|
+
const to = typeof ev.params.to === "number" ? ev.params.to : state.scale;
|
|
1429
|
+
anims.push(el.animate([{ transform: tx2(state) }, { transform: tx2({ ...state, scale: to }) }], baseOpts));
|
|
1430
|
+
state.scale = to;
|
|
1431
|
+
};
|
|
1432
|
+
var rotate = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1433
|
+
const to = typeof ev.params.to === "number" ? ev.params.to : state.rotate;
|
|
1434
|
+
anims.push(el.animate([{ transform: tx2(state) }, { transform: tx2({ ...state, rotate: to }) }], baseOpts));
|
|
1435
|
+
state.rotate = to;
|
|
1436
|
+
};
|
|
1437
|
+
var shake = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1438
|
+
const mag = typeof ev.params.intensity === "number" ? ev.params.intensity : 5;
|
|
1439
|
+
anims.push(
|
|
1440
|
+
el.animate(
|
|
1441
|
+
[
|
|
1442
|
+
{ transform: tx2(state), offset: 0 },
|
|
1443
|
+
{ transform: tx2({ ...state, x: state.x + mag }), offset: 0.2 },
|
|
1444
|
+
{ transform: tx2({ ...state, x: state.x - mag }), offset: 0.4 },
|
|
1445
|
+
{ transform: tx2({ ...state, x: state.x + mag }), offset: 0.6 },
|
|
1446
|
+
{ transform: tx2({ ...state, x: state.x - mag }), offset: 0.8 },
|
|
1447
|
+
{ transform: tx2(state), offset: 1 }
|
|
1448
|
+
],
|
|
1449
|
+
{ ...baseOpts, easing: "linear" }
|
|
1450
|
+
)
|
|
1451
|
+
);
|
|
1452
|
+
};
|
|
1453
|
+
var jump = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1454
|
+
const height = typeof ev.params.height === "number" ? ev.params.height : 30;
|
|
1455
|
+
anims.push(
|
|
1456
|
+
el.animate(
|
|
1457
|
+
[
|
|
1458
|
+
{ transform: tx2(state), offset: 0 },
|
|
1459
|
+
{ transform: tx2({ ...state, scale: state.scale * 0.9 }), offset: 0.1 },
|
|
1460
|
+
{ transform: tx2({ ...state, y: state.y - height, scale: state.scale * 1.1 }), offset: 0.45 },
|
|
1461
|
+
{ transform: tx2({ ...state, y: state.y - height * 0.3, scale: state.scale * 1.05 }), offset: 0.7 },
|
|
1462
|
+
{ transform: tx2({ ...state, scale: state.scale * 0.92 }), offset: 0.88 },
|
|
1463
|
+
{ transform: tx2(state), offset: 1 }
|
|
1464
|
+
],
|
|
1465
|
+
{ ...baseOpts, easing: "ease-in-out" }
|
|
1466
|
+
)
|
|
1467
|
+
);
|
|
1468
|
+
};
|
|
1469
|
+
var bounce = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1470
|
+
const intensity = typeof ev.params.intensity === "number" ? ev.params.intensity : 15;
|
|
1471
|
+
const count = typeof ev.params.count === "number" ? ev.params.count : 3;
|
|
1472
|
+
const keyframes = [{ transform: tx2(state), offset: 0 }];
|
|
1473
|
+
for (let i = 0; i < count; i++) {
|
|
1474
|
+
const amp = intensity * Math.pow(0.55, i);
|
|
1475
|
+
const baseOffset = (i + 0.5) / (count + 0.5);
|
|
1476
|
+
const peakOffset = Math.min(baseOffset, 0.98);
|
|
1477
|
+
const valleyOffset = Math.min(baseOffset + 0.25 / (count + 0.5), 0.99);
|
|
1478
|
+
keyframes.push({ transform: tx2({ ...state, y: state.y - amp }), offset: peakOffset });
|
|
1479
|
+
if (i < count - 1) {
|
|
1480
|
+
keyframes.push({ transform: tx2(state), offset: valleyOffset });
|
|
630
1481
|
}
|
|
631
|
-
default:
|
|
632
|
-
break;
|
|
633
1482
|
}
|
|
1483
|
+
keyframes.push({ transform: tx2(state), offset: 1 });
|
|
1484
|
+
anims.push(el.animate(keyframes, { ...baseOpts, easing: "ease-out" }));
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1487
|
+
// src/actions/effects.ts
|
|
1488
|
+
function numeric(value, fallback) {
|
|
1489
|
+
return typeof value === "number" ? value : fallback;
|
|
634
1490
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
1491
|
+
function stringParam(value, fallback) {
|
|
1492
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
1493
|
+
}
|
|
1494
|
+
var spring = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1495
|
+
const to = ev.params.to;
|
|
1496
|
+
const toX = to?.[0] ?? state.x;
|
|
1497
|
+
const toY = to?.[1] ?? state.y;
|
|
1498
|
+
const stiffness = numeric(ev.params.stiffness, 0.18);
|
|
1499
|
+
const overshootX = toX + (toX - state.x) * stiffness;
|
|
1500
|
+
const overshootY = toY + (toY - state.y) * stiffness;
|
|
1501
|
+
anims.push(
|
|
1502
|
+
el.animate(
|
|
1503
|
+
[
|
|
1504
|
+
{ transform: tx2(state), offset: 0 },
|
|
1505
|
+
{ transform: tx2({ ...state, x: overshootX, y: overshootY }), offset: 0.72 },
|
|
1506
|
+
{ transform: tx2({ ...state, x: toX, y: toY }), offset: 1 }
|
|
1507
|
+
],
|
|
1508
|
+
{ ...baseOpts, easing: "cubic-bezier(.2,1.35,.35,1)" }
|
|
1509
|
+
)
|
|
1510
|
+
);
|
|
1511
|
+
state.x = toX;
|
|
1512
|
+
state.y = toY;
|
|
639
1513
|
};
|
|
1514
|
+
var followPath = ({ ev, el, baseOpts, anims }) => {
|
|
1515
|
+
const path = stringParam(ev.params.path, "M 0 0 L 120 0");
|
|
1516
|
+
const rotate2 = ev.params.rotate === false ? "0deg" : "auto";
|
|
1517
|
+
anims.push(
|
|
1518
|
+
el.animate(
|
|
1519
|
+
[
|
|
1520
|
+
{ offsetPath: `path('${path}')`, offsetDistance: "0%", offsetRotate: rotate2 },
|
|
1521
|
+
{ offsetPath: `path('${path}')`, offsetDistance: "100%", offsetRotate: rotate2 }
|
|
1522
|
+
],
|
|
1523
|
+
baseOpts
|
|
1524
|
+
)
|
|
1525
|
+
);
|
|
1526
|
+
};
|
|
1527
|
+
var pulse = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1528
|
+
const amount = numeric(ev.params.amount, 1.08);
|
|
1529
|
+
anims.push(
|
|
1530
|
+
el.animate(
|
|
1531
|
+
[
|
|
1532
|
+
{ transform: tx2(state), offset: 0 },
|
|
1533
|
+
{ transform: tx2({ ...state, scale: state.scale * amount }), offset: 0.48 },
|
|
1534
|
+
{ transform: tx2(state), offset: 1 }
|
|
1535
|
+
],
|
|
1536
|
+
{ ...baseOpts, easing: "ease-in-out" }
|
|
1537
|
+
)
|
|
1538
|
+
);
|
|
1539
|
+
};
|
|
1540
|
+
var glow = ({ ev, el, baseOpts, anims }) => {
|
|
1541
|
+
const color = stringParam(ev.params.color, "#38bdf8");
|
|
1542
|
+
const strength = numeric(ev.params.strength, 24);
|
|
1543
|
+
anims.push(
|
|
1544
|
+
el.animate(
|
|
1545
|
+
[
|
|
1546
|
+
{ filter: "drop-shadow(0 0 0 transparent)", boxShadow: "0 0 0 rgba(0,0,0,0)" },
|
|
1547
|
+
{ filter: `drop-shadow(0 0 ${strength}px ${color})`, boxShadow: `0 0 ${strength}px ${color}` },
|
|
1548
|
+
{ filter: "drop-shadow(0 0 0 transparent)", boxShadow: "0 0 0 rgba(0,0,0,0)" }
|
|
1549
|
+
],
|
|
1550
|
+
{ ...baseOpts, easing: "ease-in-out" }
|
|
1551
|
+
)
|
|
1552
|
+
);
|
|
1553
|
+
};
|
|
1554
|
+
var ripple = ({ ev, el, delayMs, durMs, anims }) => {
|
|
1555
|
+
const color = stringParam(ev.params.color, "#38bdf8");
|
|
1556
|
+
const size = numeric(ev.params.size, 140);
|
|
1557
|
+
const ring = document.createElement("span");
|
|
1558
|
+
ring.setAttribute("data-markdy-ripple", "1");
|
|
1559
|
+
Object.assign(ring.style, {
|
|
1560
|
+
position: "absolute",
|
|
1561
|
+
left: "50%",
|
|
1562
|
+
top: "50%",
|
|
1563
|
+
width: `${size}px`,
|
|
1564
|
+
height: `${size}px`,
|
|
1565
|
+
marginLeft: `${-size / 2}px`,
|
|
1566
|
+
marginTop: `${-size / 2}px`,
|
|
1567
|
+
border: `2px solid ${color}`,
|
|
1568
|
+
borderRadius: "999px",
|
|
1569
|
+
pointerEvents: "none",
|
|
1570
|
+
opacity: "0"
|
|
1571
|
+
});
|
|
1572
|
+
el.appendChild(ring);
|
|
1573
|
+
anims.push(
|
|
1574
|
+
ring.animate(
|
|
1575
|
+
[
|
|
1576
|
+
{ transform: "scale(0.2)", opacity: 0.6 },
|
|
1577
|
+
{ transform: "scale(1)", opacity: 0 }
|
|
1578
|
+
],
|
|
1579
|
+
{ delay: delayMs, duration: durMs, fill: "forwards", easing: "ease-out" }
|
|
1580
|
+
)
|
|
1581
|
+
);
|
|
1582
|
+
};
|
|
1583
|
+
var blur = ({ ev, el, baseOpts, anims }) => {
|
|
1584
|
+
const from = numeric(ev.params.from, 0);
|
|
1585
|
+
const to = numeric(ev.params.to, 8);
|
|
1586
|
+
anims.push(el.animate([{ filter: `blur(${from}px)` }, { filter: `blur(${to}px)` }], baseOpts));
|
|
1587
|
+
};
|
|
1588
|
+
var lineReveal = ({ ev, el, baseOpts, anims }) => {
|
|
1589
|
+
const from = stringParam(ev.params.from, "left");
|
|
1590
|
+
const start = from === "right" ? "inset(0 0 0 100%)" : "inset(0 100% 0 0)";
|
|
1591
|
+
anims.push(el.animate([{ clipPath: start }, { clipPath: "inset(0 0 0 0)" }], baseOpts));
|
|
1592
|
+
};
|
|
1593
|
+
var mask = ({ ev, el, baseOpts, anims }) => {
|
|
1594
|
+
const from = numeric(ev.params.from, 0);
|
|
1595
|
+
const to = numeric(ev.params.to, 120);
|
|
1596
|
+
anims.push(
|
|
1597
|
+
el.animate(
|
|
1598
|
+
[
|
|
1599
|
+
{ clipPath: `circle(${from}% at 50% 50%)` },
|
|
1600
|
+
{ clipPath: `circle(${to}% at 50% 50%)` }
|
|
1601
|
+
],
|
|
1602
|
+
baseOpts
|
|
1603
|
+
)
|
|
1604
|
+
);
|
|
1605
|
+
};
|
|
1606
|
+
var parallax = ({ ev, el, state, baseOpts, anims, tx: tx2 }) => {
|
|
1607
|
+
const depth = numeric(ev.params.depth, 0.35);
|
|
1608
|
+
const by = ev.params.by;
|
|
1609
|
+
const dx = (by?.[0] ?? 24) * depth;
|
|
1610
|
+
const dy = (by?.[1] ?? 0) * depth;
|
|
1611
|
+
anims.push(
|
|
1612
|
+
el.animate(
|
|
1613
|
+
[
|
|
1614
|
+
{ transform: tx2(state) },
|
|
1615
|
+
{ transform: tx2({ ...state, x: state.x + dx, y: state.y + dy }) }
|
|
1616
|
+
],
|
|
1617
|
+
baseOpts
|
|
1618
|
+
)
|
|
1619
|
+
);
|
|
1620
|
+
};
|
|
1621
|
+
|
|
1622
|
+
// src/geometry/rect.ts
|
|
1623
|
+
var ACTOR_SIZES = {
|
|
1624
|
+
service: { width: 184, height: 88 },
|
|
1625
|
+
api: { width: 184, height: 88 },
|
|
1626
|
+
microservice: { width: 184, height: 88 },
|
|
1627
|
+
client: { width: 184, height: 88 },
|
|
1628
|
+
user: { width: 184, height: 88 },
|
|
1629
|
+
db: { width: 184, height: 88 },
|
|
1630
|
+
database: { width: 184, height: 88 },
|
|
1631
|
+
queue: { width: 184, height: 88 },
|
|
1632
|
+
cache: { width: 184, height: 88 },
|
|
1633
|
+
cloud: { width: 184, height: 88 },
|
|
1634
|
+
region: { width: 184, height: 88 },
|
|
1635
|
+
container: { width: 184, height: 88 },
|
|
1636
|
+
cluster: { width: 184, height: 88 },
|
|
1637
|
+
box: { width: 100, height: 100 },
|
|
1638
|
+
caption: { width: 260, height: 56 },
|
|
1639
|
+
figure: { width: 120, height: 170 },
|
|
1640
|
+
parking_map: { width: 390, height: 250 },
|
|
1641
|
+
ascii_map: { width: 390, height: 250 },
|
|
1642
|
+
game_scene: { width: 390, height: 250 },
|
|
1643
|
+
byte_viz: { width: 420, height: 250 }
|
|
1644
|
+
};
|
|
1645
|
+
var DEFAULT_ACTOR_SIZE = { width: 140, height: 42 };
|
|
640
1646
|
function actorSizeByType(type) {
|
|
641
|
-
|
|
642
|
-
case "service":
|
|
643
|
-
case "client":
|
|
644
|
-
case "db":
|
|
645
|
-
case "queue":
|
|
646
|
-
return { width: 180, height: 84 };
|
|
647
|
-
case "box":
|
|
648
|
-
return { width: 100, height: 100 };
|
|
649
|
-
case "caption":
|
|
650
|
-
return { width: 260, height: 56 };
|
|
651
|
-
case "figure":
|
|
652
|
-
return { width: 120, height: 170 };
|
|
653
|
-
default:
|
|
654
|
-
return { width: 140, height: 42 };
|
|
655
|
-
}
|
|
1647
|
+
return ACTOR_SIZES[type] ?? DEFAULT_ACTOR_SIZE;
|
|
656
1648
|
}
|
|
657
1649
|
function actorCenter(state, actorType) {
|
|
658
|
-
const
|
|
659
|
-
return {
|
|
660
|
-
x: state.x + width / 2,
|
|
661
|
-
y: state.y + height / 2
|
|
662
|
-
};
|
|
1650
|
+
const rect = actorRect(state, actorType);
|
|
1651
|
+
return { x: rect.x1 + (rect.x2 - rect.x1) / 2, y: rect.y1 + (rect.y2 - rect.y1) / 2 };
|
|
663
1652
|
}
|
|
664
1653
|
function actorRect(state, actorType) {
|
|
665
1654
|
const { width, height } = actorSizeByType(actorType);
|
|
1655
|
+
const scale2 = Math.max(1e-3, state.scale);
|
|
1656
|
+
const scaledWidth = width * scale2;
|
|
1657
|
+
const scaledHeight = height * scale2;
|
|
1658
|
+
const dx = (scaledWidth - width) / 2;
|
|
1659
|
+
const dy = (scaledHeight - height) / 2;
|
|
666
1660
|
return {
|
|
667
|
-
x1: state.x,
|
|
668
|
-
y1: state.y,
|
|
669
|
-
x2: state.x + width,
|
|
670
|
-
y2: state.y + height
|
|
1661
|
+
x1: state.x - dx,
|
|
1662
|
+
y1: state.y - dy,
|
|
1663
|
+
x2: state.x + width + dx,
|
|
1664
|
+
y2: state.y + height + dy
|
|
671
1665
|
};
|
|
672
1666
|
}
|
|
673
1667
|
function inflateRect(rect, pad) {
|
|
@@ -706,12 +1700,14 @@ function countPathIntersections(points, obstacles) {
|
|
|
706
1700
|
}
|
|
707
1701
|
return hits;
|
|
708
1702
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
1703
|
+
|
|
1704
|
+
// src/geometry/path.ts
|
|
712
1705
|
function round1(n) {
|
|
713
1706
|
return Math.round(n * 10) / 10;
|
|
714
1707
|
}
|
|
1708
|
+
function toPathD(points) {
|
|
1709
|
+
return points.map((p, i) => `${i === 0 ? "M" : "L"} ${round1(p.x)} ${round1(p.y)}`).join(" ");
|
|
1710
|
+
}
|
|
715
1711
|
function polylineLength(points) {
|
|
716
1712
|
let total = 0;
|
|
717
1713
|
for (let i = 0; i < points.length - 1; i++) {
|
|
@@ -719,26 +1715,67 @@ function polylineLength(points) {
|
|
|
719
1715
|
}
|
|
720
1716
|
return Math.max(1, total);
|
|
721
1717
|
}
|
|
722
|
-
function
|
|
723
|
-
|
|
1718
|
+
function segmentLength(a, b) {
|
|
1719
|
+
return Math.hypot(b.x - a.x, b.y - a.y);
|
|
1720
|
+
}
|
|
1721
|
+
function labelPointForPath(points, lane) {
|
|
1722
|
+
if (points.length < 2) return points[0] ?? { x: 0, y: 0 };
|
|
1723
|
+
let bestIndex = 0;
|
|
1724
|
+
let bestLength = -1;
|
|
724
1725
|
for (let i = 0; i < points.length - 1; i++) {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
const t = seg <= 0 ? 0 : remain / seg;
|
|
730
|
-
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
1726
|
+
const len = segmentLength(points[i], points[i + 1]);
|
|
1727
|
+
if (len > bestLength) {
|
|
1728
|
+
bestIndex = i;
|
|
1729
|
+
bestLength = len;
|
|
731
1730
|
}
|
|
732
|
-
remain -= seg;
|
|
733
1731
|
}
|
|
734
|
-
|
|
1732
|
+
const a = points[bestIndex];
|
|
1733
|
+
const b = points[bestIndex + 1];
|
|
1734
|
+
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
1735
|
+
const offset = lane * 8;
|
|
1736
|
+
if (Math.abs(a.x - b.x) >= Math.abs(a.y - b.y)) {
|
|
1737
|
+
return { x: mid.x, y: mid.y - 10 - offset };
|
|
1738
|
+
}
|
|
1739
|
+
return { x: mid.x + 10 + offset, y: mid.y };
|
|
1740
|
+
}
|
|
1741
|
+
function clamp(n, min, max) {
|
|
1742
|
+
if (max < min) return n;
|
|
1743
|
+
return Math.min(max, Math.max(min, n));
|
|
1744
|
+
}
|
|
1745
|
+
function clampPointToScene(point, ast) {
|
|
1746
|
+
const pad = 14;
|
|
1747
|
+
return {
|
|
1748
|
+
x: round1(clamp(point.x, pad, ast.meta.width - pad)),
|
|
1749
|
+
y: round1(clamp(point.y, pad, ast.meta.height - pad))
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
function routeLength(points) {
|
|
1753
|
+
let total = 0;
|
|
1754
|
+
for (let i = 0; i < points.length - 1; i++) total += segmentLength(points[i], points[i + 1]);
|
|
1755
|
+
return total;
|
|
1756
|
+
}
|
|
1757
|
+
function routeBends(points) {
|
|
1758
|
+
let bends = 0;
|
|
1759
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
1760
|
+
const prev = points[i - 1];
|
|
1761
|
+
const cur = points[i];
|
|
1762
|
+
const next = points[i + 1];
|
|
1763
|
+
const dx1 = Math.sign(cur.x - prev.x);
|
|
1764
|
+
const dy1 = Math.sign(cur.y - prev.y);
|
|
1765
|
+
const dx2 = Math.sign(next.x - cur.x);
|
|
1766
|
+
const dy2 = Math.sign(next.y - cur.y);
|
|
1767
|
+
if (dx1 !== dx2 || dy1 !== dy2) bends++;
|
|
1768
|
+
}
|
|
1769
|
+
return bends;
|
|
735
1770
|
}
|
|
736
1771
|
function routeFlowPath(sourceName, targetName, sourceState, targetState, states, ast, lane) {
|
|
737
|
-
const
|
|
738
|
-
const
|
|
1772
|
+
const sourceType = ast.actors[sourceName]?.type ?? "box";
|
|
1773
|
+
const targetType = ast.actors[targetName]?.type ?? "box";
|
|
1774
|
+
const sourceRect = actorRect(sourceState, sourceType);
|
|
1775
|
+
const targetRect = actorRect(targetState, targetType);
|
|
739
1776
|
const laneShift = lane * 18;
|
|
740
|
-
const sourceCenter = actorCenter(sourceState,
|
|
741
|
-
const targetCenter = actorCenter(targetState,
|
|
1777
|
+
const sourceCenter = actorCenter(sourceState, sourceType);
|
|
1778
|
+
const targetCenter = actorCenter(targetState, targetType);
|
|
742
1779
|
const horizontalPrimary = Math.abs(targetCenter.x - sourceCenter.x) >= Math.abs(targetCenter.y - sourceCenter.y);
|
|
743
1780
|
const source = horizontalPrimary ? { x: targetCenter.x >= sourceCenter.x ? sourceRect.x2 : sourceRect.x1, y: sourceCenter.y } : { x: sourceCenter.x, y: targetCenter.y >= sourceCenter.y ? sourceRect.y2 : sourceRect.y1 };
|
|
744
1781
|
const target = horizontalPrimary ? { x: targetCenter.x >= sourceCenter.x ? targetRect.x1 : targetRect.x2, y: targetCenter.y } : { x: targetCenter.x, y: targetCenter.y >= sourceCenter.y ? targetRect.y1 : targetRect.y2 };
|
|
@@ -766,23 +1803,42 @@ function routeFlowPath(sourceName, targetName, sourceState, targetState, states,
|
|
|
766
1803
|
[source, { x: source.x, y: topLane }, { x: target.x, y: topLane }, target],
|
|
767
1804
|
[source, { x: source.x, y: bottomLane }, { x: target.x, y: bottomLane }, target]
|
|
768
1805
|
);
|
|
1806
|
+
const minObstacleX = obstacles.length ? Math.min(...obstacles.map((o) => o.x1)) : Math.min(source.x, target.x);
|
|
1807
|
+
const maxObstacleX = obstacles.length ? Math.max(...obstacles.map((o) => o.x2)) : Math.max(source.x, target.x);
|
|
1808
|
+
const leftLane = Math.max(16, minObstacleX - 24 - lane * 12);
|
|
1809
|
+
const rightLane = Math.min(ast.meta.width - 16, maxObstacleX + 24 + lane * 12);
|
|
1810
|
+
candidates.push(
|
|
1811
|
+
[source, { x: leftLane, y: source.y }, { x: leftLane, y: target.y }, target],
|
|
1812
|
+
[source, { x: rightLane, y: source.y }, { x: rightLane, y: target.y }, target]
|
|
1813
|
+
);
|
|
769
1814
|
let best = candidates[0];
|
|
770
|
-
let
|
|
1815
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
771
1816
|
for (const candidate of candidates) {
|
|
772
1817
|
const hits = countPathIntersections(candidate, obstacles);
|
|
773
|
-
|
|
1818
|
+
const score = hits * 1e5 + routeBends(candidate) * 800 + routeLength(candidate);
|
|
1819
|
+
if (score < bestScore) {
|
|
774
1820
|
best = candidate;
|
|
775
|
-
|
|
776
|
-
if (hits === 0) break;
|
|
1821
|
+
bestScore = score;
|
|
777
1822
|
}
|
|
778
1823
|
}
|
|
779
|
-
return best;
|
|
1824
|
+
return best.map((point) => clampPointToScene(point, ast));
|
|
780
1825
|
}
|
|
1826
|
+
|
|
1827
|
+
// src/actions/flow.ts
|
|
1828
|
+
var EDGE_LAYER_ATTR = "data-markdy-edge-layer";
|
|
1829
|
+
var STROKE_BY_ACTION = {
|
|
1830
|
+
request: "#38bdf8",
|
|
1831
|
+
response: "#a78bfa",
|
|
1832
|
+
emit: "#f59e0b"
|
|
1833
|
+
};
|
|
1834
|
+
var DEFAULT_STROKE = "#38bdf8";
|
|
1835
|
+
var LABEL_MAX_CHARS = 28;
|
|
1836
|
+
var EDGE_FADE_MS = 140;
|
|
781
1837
|
function ensureEdgeLayer(scene) {
|
|
782
|
-
const existing = scene.querySelector(
|
|
1838
|
+
const existing = scene.querySelector(`svg[${EDGE_LAYER_ATTR}='1']`);
|
|
783
1839
|
if (existing) return existing;
|
|
784
1840
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
785
|
-
svg.setAttribute(
|
|
1841
|
+
svg.setAttribute(EDGE_LAYER_ATTR, "1");
|
|
786
1842
|
Object.assign(svg.style, {
|
|
787
1843
|
position: "absolute",
|
|
788
1844
|
inset: "0",
|
|
@@ -795,529 +1851,504 @@ function ensureEdgeLayer(scene) {
|
|
|
795
1851
|
scene.appendChild(svg);
|
|
796
1852
|
return svg;
|
|
797
1853
|
}
|
|
798
|
-
function
|
|
799
|
-
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
);
|
|
1854
|
+
function ensureEdgeDefs(svg) {
|
|
1855
|
+
if (svg.querySelector("defs[data-markdy-flow-defs='1']")) return;
|
|
1856
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1857
|
+
defs.setAttribute("data-markdy-flow-defs", "1");
|
|
1858
|
+
const glow2 = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
1859
|
+
glow2.setAttribute("id", "markdy-flow-glow");
|
|
1860
|
+
glow2.setAttribute("x", "-40%");
|
|
1861
|
+
glow2.setAttribute("y", "-40%");
|
|
1862
|
+
glow2.setAttribute("width", "180%");
|
|
1863
|
+
glow2.setAttribute("height", "180%");
|
|
1864
|
+
const blur2 = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
1865
|
+
blur2.setAttribute("stdDeviation", "3");
|
|
1866
|
+
blur2.setAttribute("result", "coloredBlur");
|
|
1867
|
+
const merge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
1868
|
+
const glowNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1869
|
+
glowNode.setAttribute("in", "coloredBlur");
|
|
1870
|
+
const sourceNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1871
|
+
sourceNode.setAttribute("in", "SourceGraphic");
|
|
1872
|
+
merge.append(glowNode, sourceNode);
|
|
1873
|
+
glow2.append(blur2, merge);
|
|
1874
|
+
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
1875
|
+
arrow.setAttribute("id", "markdy-flow-arrow");
|
|
1876
|
+
arrow.setAttribute("viewBox", "0 0 10 10");
|
|
1877
|
+
arrow.setAttribute("refX", "8");
|
|
1878
|
+
arrow.setAttribute("refY", "5");
|
|
1879
|
+
arrow.setAttribute("markerWidth", "6");
|
|
1880
|
+
arrow.setAttribute("markerHeight", "6");
|
|
1881
|
+
arrow.setAttribute("orient", "auto-start-reverse");
|
|
1882
|
+
const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1883
|
+
arrowPath.setAttribute("d", "M 1 1 L 9 5 L 1 9 z");
|
|
1884
|
+
arrowPath.setAttribute("fill", "context-stroke");
|
|
1885
|
+
arrow.appendChild(arrowPath);
|
|
1886
|
+
defs.append(glow2, arrow);
|
|
1887
|
+
svg.prepend(defs);
|
|
1888
|
+
}
|
|
1889
|
+
function isDashed(ctx) {
|
|
1890
|
+
const style = String(ctx.ev.params.style ?? "");
|
|
1891
|
+
return style === "dashed" || style === "fire_and_forget" || ctx.ev.action === "response";
|
|
1892
|
+
}
|
|
1893
|
+
function buildEdge(ctx, targetName) {
|
|
1894
|
+
const { ev, state, states, ast, scene, baseOpts, anims } = ctx;
|
|
1895
|
+
const targetState = states.get(targetName);
|
|
1896
|
+
if (!targetState) return;
|
|
1897
|
+
const lane = ev.line % 5 - 2;
|
|
1898
|
+
const points = routeFlowPath(ev.actor, targetName, state, targetState, states, ast, lane);
|
|
811
1899
|
const length = polylineLength(points);
|
|
812
|
-
const midPoint = pointAtDistance(points, length * 0.5);
|
|
813
1900
|
const pathD = toPathD(points);
|
|
1901
|
+
const stroke = STROKE_BY_ACTION[ev.action] ?? DEFAULT_STROKE;
|
|
814
1902
|
const svg = ensureEdgeLayer(scene);
|
|
1903
|
+
ensureEdgeDefs(svg);
|
|
815
1904
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
816
1905
|
group.setAttribute("data-markdy-flow-edge", "1");
|
|
817
1906
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
818
1907
|
path.setAttribute("d", pathD);
|
|
819
1908
|
path.setAttribute("fill", "none");
|
|
820
1909
|
path.setAttribute("stroke", stroke);
|
|
821
|
-
path.setAttribute("stroke-width", "
|
|
1910
|
+
path.setAttribute("stroke-width", "3");
|
|
1911
|
+
path.setAttribute("stroke-linecap", "round");
|
|
1912
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
1913
|
+
path.setAttribute("marker-end", "url(#markdy-flow-arrow)");
|
|
1914
|
+
path.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
822
1915
|
path.setAttribute("data-markdy-flow-action", ev.action);
|
|
823
|
-
path.style.strokeDasharray = isDashed ? "8 6" : `${length}`;
|
|
1916
|
+
path.style.strokeDasharray = isDashed(ctx) ? "8 6" : `${length}`;
|
|
824
1917
|
path.style.strokeDashoffset = `${length}`;
|
|
825
1918
|
group.appendChild(path);
|
|
826
1919
|
const marker = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
827
|
-
marker.setAttribute("r", "
|
|
1920
|
+
marker.setAttribute("r", "4.5");
|
|
828
1921
|
marker.setAttribute("fill", stroke);
|
|
1922
|
+
marker.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
829
1923
|
marker.style.offsetPath = `path('${pathD}')`;
|
|
830
1924
|
marker.style.offsetDistance = "0%";
|
|
831
1925
|
marker.style.opacity = "0";
|
|
832
1926
|
group.appendChild(marker);
|
|
833
|
-
const
|
|
834
|
-
if (
|
|
835
|
-
const
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1927
|
+
const labelText = String(ev.params.label ?? "");
|
|
1928
|
+
if (labelText) {
|
|
1929
|
+
const midPoint = labelPointForPath(points, lane);
|
|
1930
|
+
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
1931
|
+
label.setAttribute("x", `${round1(midPoint.x)}`);
|
|
1932
|
+
label.setAttribute("y", `${round1(midPoint.y - 8)}`);
|
|
1933
|
+
label.setAttribute("text-anchor", "middle");
|
|
1934
|
+
label.setAttribute("font-size", "12");
|
|
1935
|
+
label.setAttribute("font-weight", "700");
|
|
1936
|
+
label.setAttribute("paint-order", "stroke");
|
|
1937
|
+
label.setAttribute("stroke", "rgba(2, 6, 23, 0.88)");
|
|
1938
|
+
label.setAttribute("stroke-width", "5");
|
|
1939
|
+
label.setAttribute("stroke-linejoin", "round");
|
|
1940
|
+
label.setAttribute("fill", "#cbd5e1");
|
|
1941
|
+
label.setAttribute("filter", "url(#markdy-flow-glow)");
|
|
1942
|
+
label.setAttribute("data-full-label", labelText);
|
|
1943
|
+
label.textContent = labelText.length > LABEL_MAX_CHARS ? `${labelText.slice(0, LABEL_MAX_CHARS - 1)}\u2026` : labelText;
|
|
1944
|
+
group.appendChild(label);
|
|
845
1945
|
}
|
|
846
1946
|
svg.appendChild(group);
|
|
847
|
-
anims.push(path.animate([{ strokeDashoffset: length }, { strokeDashoffset: 0 }], baseOpts));
|
|
848
1947
|
anims.push(
|
|
1948
|
+
path.animate([{ strokeDashoffset: length }, { strokeDashoffset: 0 }], baseOpts),
|
|
849
1949
|
marker.animate(
|
|
850
|
-
[
|
|
1950
|
+
[
|
|
1951
|
+
{ offsetDistance: "0%", opacity: 1 },
|
|
1952
|
+
{ offsetDistance: "100%", opacity: 1 }
|
|
1953
|
+
],
|
|
851
1954
|
baseOpts
|
|
852
|
-
)
|
|
1955
|
+
),
|
|
1956
|
+
group.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
1957
|
+
delay: Number(baseOpts.delay ?? 0) + Number(baseOpts.duration ?? 0),
|
|
1958
|
+
duration: EDGE_FADE_MS,
|
|
1959
|
+
fill: "forwards"
|
|
1960
|
+
})
|
|
853
1961
|
);
|
|
854
|
-
|
|
1962
|
+
}
|
|
1963
|
+
var flowEdge = (ctx) => {
|
|
1964
|
+
const targetName = String(ctx.ev.params.to ?? "");
|
|
1965
|
+
if (targetName) buildEdge(ctx, targetName);
|
|
1966
|
+
};
|
|
1967
|
+
|
|
1968
|
+
// src/theme.ts
|
|
1969
|
+
var DARK_LUMINANCE_THRESHOLD = 140;
|
|
1970
|
+
var NAMED_DARK = {
|
|
1971
|
+
black: true,
|
|
1972
|
+
"#000": true,
|
|
1973
|
+
"#000000": true
|
|
1974
|
+
};
|
|
1975
|
+
function isSceneDark(scene) {
|
|
1976
|
+
const bg = scene.style.background || "white";
|
|
1977
|
+
let hex = bg.trim().replace(/^#/, "");
|
|
1978
|
+
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
1979
|
+
if (hex.length === 6) {
|
|
1980
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1981
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1982
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1983
|
+
return 0.299 * r + 0.587 * g + 0.114 * b <= DARK_LUMINANCE_THRESHOLD;
|
|
1984
|
+
}
|
|
1985
|
+
return NAMED_DARK[bg.toLowerCase()] ?? false;
|
|
1986
|
+
}
|
|
1987
|
+
function speechBubbleTheme(dark) {
|
|
1988
|
+
return dark ? {
|
|
1989
|
+
background: "#1e2530",
|
|
1990
|
+
border: "#475569",
|
|
1991
|
+
text: "#e2e8f0",
|
|
1992
|
+
shadow: "0 2px 8px rgba(0,0,0,0.35)"
|
|
1993
|
+
} : {
|
|
1994
|
+
background: "white",
|
|
1995
|
+
border: "#222",
|
|
1996
|
+
text: "#222",
|
|
1997
|
+
shadow: "0 2px 8px rgba(0,0,0,0.12)"
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// src/actions/speech.ts
|
|
2002
|
+
var MAX_FADE_MS = 200;
|
|
2003
|
+
var FADE_FRACTION = 0.15;
|
|
2004
|
+
var say = ({ ev, el, state, delayMs, durMs, scene, anims }) => {
|
|
2005
|
+
const text = String(ev.params.text ?? "");
|
|
2006
|
+
const theme = speechBubbleTheme(isSceneDark(scene));
|
|
2007
|
+
const inverseScale = 1 / (state.scale || 1);
|
|
2008
|
+
const bubble = document.createElement("div");
|
|
2009
|
+
bubble.textContent = text;
|
|
2010
|
+
Object.assign(bubble.style, {
|
|
2011
|
+
position: "absolute",
|
|
2012
|
+
bottom: "calc(100% + 10px)",
|
|
2013
|
+
left: "50%",
|
|
2014
|
+
transform: `translateX(-50%) scale(${inverseScale})`,
|
|
2015
|
+
transformOrigin: "center bottom",
|
|
2016
|
+
background: theme.background,
|
|
2017
|
+
border: `2px solid ${theme.border}`,
|
|
2018
|
+
color: theme.text,
|
|
2019
|
+
borderRadius: "12px",
|
|
2020
|
+
padding: "6px 14px",
|
|
2021
|
+
fontFamily: "system-ui, sans-serif",
|
|
2022
|
+
fontSize: "15px",
|
|
2023
|
+
lineHeight: "1.3",
|
|
2024
|
+
whiteSpace: "nowrap",
|
|
2025
|
+
maxWidth: "220px",
|
|
2026
|
+
overflow: "hidden",
|
|
2027
|
+
textOverflow: "ellipsis",
|
|
2028
|
+
pointerEvents: "none",
|
|
2029
|
+
zIndex: "10",
|
|
2030
|
+
boxShadow: theme.shadow,
|
|
2031
|
+
opacity: "0"
|
|
2032
|
+
});
|
|
2033
|
+
const tail = document.createElement("span");
|
|
2034
|
+
Object.assign(tail.style, {
|
|
2035
|
+
position: "absolute",
|
|
2036
|
+
bottom: "-10px",
|
|
2037
|
+
left: "50%",
|
|
2038
|
+
transform: "translateX(-50%)",
|
|
2039
|
+
width: "0",
|
|
2040
|
+
height: "0",
|
|
2041
|
+
borderLeft: "7px solid transparent",
|
|
2042
|
+
borderRight: "7px solid transparent",
|
|
2043
|
+
borderTop: `10px solid ${theme.border}`
|
|
2044
|
+
});
|
|
2045
|
+
bubble.appendChild(tail);
|
|
2046
|
+
el.style.overflow = "visible";
|
|
2047
|
+
el.appendChild(bubble);
|
|
2048
|
+
const fadeMs = Math.min(MAX_FADE_MS, durMs * FADE_FRACTION);
|
|
855
2049
|
anims.push(
|
|
856
|
-
|
|
857
|
-
delay:
|
|
858
|
-
duration:
|
|
2050
|
+
bubble.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
2051
|
+
delay: delayMs,
|
|
2052
|
+
duration: fadeMs,
|
|
2053
|
+
fill: "forwards"
|
|
2054
|
+
}),
|
|
2055
|
+
bubble.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
2056
|
+
delay: delayMs + durMs - fadeMs,
|
|
2057
|
+
duration: fadeMs,
|
|
859
2058
|
fill: "forwards"
|
|
860
2059
|
})
|
|
861
2060
|
);
|
|
2061
|
+
};
|
|
2062
|
+
|
|
2063
|
+
// src/actions/projectile.ts
|
|
2064
|
+
var PROJECTILE_SIZE_PX = 32;
|
|
2065
|
+
function createProjectile(assetName, assetDef, assetOverrides) {
|
|
2066
|
+
if (assetDef.type === "image") {
|
|
2067
|
+
const img = document.createElement("img");
|
|
2068
|
+
img.src = assetOverrides[assetName] ?? assetDef.value;
|
|
2069
|
+
img.alt = assetName;
|
|
2070
|
+
img.setAttribute("draggable", "false");
|
|
2071
|
+
img.style.width = `${PROJECTILE_SIZE_PX}px`;
|
|
2072
|
+
img.style.height = `${PROJECTILE_SIZE_PX}px`;
|
|
2073
|
+
return img;
|
|
2074
|
+
}
|
|
2075
|
+
const span = document.createElement("span");
|
|
2076
|
+
span.className = "iconify";
|
|
2077
|
+
span.dataset.icon = assetDef.value;
|
|
2078
|
+
span.style.fontSize = `${PROJECTILE_SIZE_PX}px`;
|
|
2079
|
+
span.style.lineHeight = "1";
|
|
2080
|
+
span.style.display = "inline-block";
|
|
2081
|
+
return span;
|
|
862
2082
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
baseOpts
|
|
978
|
-
)
|
|
979
|
-
);
|
|
980
|
-
s.rotate = toDeg;
|
|
981
|
-
break;
|
|
982
|
-
}
|
|
983
|
-
// ── shake ───────────────────────────────────────────────────────────────
|
|
984
|
-
case "shake": {
|
|
985
|
-
const mag = typeof ev.params.intensity === "number" ? ev.params.intensity : 5;
|
|
986
|
-
anims.push(
|
|
987
|
-
el.animate(
|
|
988
|
-
[
|
|
989
|
-
{ transform: txFn(s), offset: 0 },
|
|
990
|
-
{ transform: txFn({ ...s, x: s.x + mag }), offset: 0.2 },
|
|
991
|
-
{ transform: txFn({ ...s, x: s.x - mag }), offset: 0.4 },
|
|
992
|
-
{ transform: txFn({ ...s, x: s.x + mag }), offset: 0.6 },
|
|
993
|
-
{ transform: txFn({ ...s, x: s.x - mag }), offset: 0.8 },
|
|
994
|
-
{ transform: txFn(s), offset: 1 }
|
|
995
|
-
],
|
|
996
|
-
{ ...baseOpts, easing: "linear" }
|
|
997
|
-
)
|
|
998
|
-
);
|
|
999
|
-
break;
|
|
1000
|
-
}
|
|
1001
|
-
// ── punch ───────────────────────────────────────────────────────────────
|
|
1002
|
-
case "punch": {
|
|
1003
|
-
const pSide = String(ev.params.side ?? "right");
|
|
1004
|
-
const pArmEl = el.querySelector(
|
|
1005
|
-
pSide === "left" ? "[data-fig-arm-l]" : "[data-fig-arm-r]"
|
|
1006
|
-
);
|
|
1007
|
-
if (!pArmEl) break;
|
|
1008
|
-
const pRest = readRotation(pArmEl);
|
|
1009
|
-
const pExtend = pSide === "left" ? -75 : 75;
|
|
1010
|
-
anims.push(
|
|
1011
|
-
pArmEl.animate(
|
|
1012
|
-
[
|
|
1013
|
-
{ transform: `rotate(${pRest}deg)` },
|
|
1014
|
-
{ transform: `rotate(${pExtend}deg)`, offset: 0.35 },
|
|
1015
|
-
{ transform: `rotate(${pRest}deg)` }
|
|
1016
|
-
],
|
|
1017
|
-
{ ...baseOpts, easing: "ease-in-out", fill: "forwards" }
|
|
1018
|
-
)
|
|
1019
|
-
);
|
|
1020
|
-
break;
|
|
1021
|
-
}
|
|
1022
|
-
// ── kick ────────────────────────────────────────────────────────────────
|
|
1023
|
-
case "kick": {
|
|
1024
|
-
const kSide = String(ev.params.side ?? "right");
|
|
1025
|
-
const kLegEl = el.querySelector(
|
|
1026
|
-
kSide === "left" ? "[data-fig-leg-l]" : "[data-fig-leg-r]"
|
|
1027
|
-
);
|
|
1028
|
-
if (!kLegEl) break;
|
|
1029
|
-
const kRest = readRotation(kLegEl);
|
|
1030
|
-
const kExtend = kSide === "left" ? -100 : 100;
|
|
1031
|
-
anims.push(
|
|
1032
|
-
kLegEl.animate(
|
|
1033
|
-
[
|
|
1034
|
-
{ transform: `rotate(${kRest}deg)` },
|
|
1035
|
-
{ transform: `rotate(${kExtend}deg)`, offset: 0.38 },
|
|
1036
|
-
{ transform: `rotate(${kRest}deg)` }
|
|
1037
|
-
],
|
|
1038
|
-
{ ...baseOpts, easing: "ease-in-out", fill: "forwards" }
|
|
1039
|
-
)
|
|
1040
|
-
);
|
|
1041
|
-
break;
|
|
1042
|
-
}
|
|
1043
|
-
// ── rotate_part ─────────────────────────────────────────────────────────
|
|
1044
|
-
case "rotate_part": {
|
|
1045
|
-
const rpName = String(ev.params.part ?? "");
|
|
1046
|
-
const rpSel = PART_SEL[rpName];
|
|
1047
|
-
if (!rpSel) break;
|
|
1048
|
-
const rpEl = el.querySelector(rpSel);
|
|
1049
|
-
if (!rpEl) break;
|
|
1050
|
-
const rpFrom = readRotation(rpEl);
|
|
1051
|
-
const rpTo = typeof ev.params.to === "number" ? ev.params.to : rpFrom;
|
|
1052
|
-
anims.push(
|
|
1053
|
-
rpEl.animate(
|
|
1054
|
-
[
|
|
1055
|
-
{ transform: `rotate(${rpFrom}deg)` },
|
|
1056
|
-
{ transform: `rotate(${rpTo}deg)` }
|
|
1057
|
-
],
|
|
1058
|
-
{ ...baseOpts, fill: "forwards" }
|
|
1059
|
-
)
|
|
1060
|
-
);
|
|
1061
|
-
rpEl.style.transform = rpEl.style.transform.replace(
|
|
1062
|
-
/rotate\([^)]*\)/,
|
|
1063
|
-
`rotate(${rpTo}deg)`
|
|
1064
|
-
);
|
|
1065
|
-
break;
|
|
1066
|
-
}
|
|
1067
|
-
// ── face ────────────────────────────────────────────────────────────────
|
|
1068
|
-
case "face": {
|
|
1069
|
-
const fEl = el.querySelector("[data-fig-face]");
|
|
1070
|
-
if (!fEl) break;
|
|
1071
|
-
const emoji = String(ev.params.text ?? ev.params._0 ?? "");
|
|
1072
|
-
if (emoji) faceSwaps.push({ timeMs: ev.time * 1e3, el: fEl, emoji });
|
|
1073
|
-
break;
|
|
1074
|
-
}
|
|
1075
|
-
// ── say ─────────────────────────────────────────────────────────────────
|
|
1076
|
-
case "say": {
|
|
1077
|
-
const text = String(ev.params.text ?? "");
|
|
1078
|
-
const inverseScale = 1 / (s.scale || 1);
|
|
1079
|
-
const sceneDark = isSceneDark(scene);
|
|
1080
|
-
const bubbleBg = sceneDark ? "#1e2530" : "white";
|
|
1081
|
-
const bubbleBorder = sceneDark ? "#475569" : "#222";
|
|
1082
|
-
const bubbleText = sceneDark ? "#e2e8f0" : "#222";
|
|
1083
|
-
const bubbleShadow = sceneDark ? "0 2px 8px rgba(0,0,0,0.35)" : "0 2px 8px rgba(0,0,0,0.12)";
|
|
1084
|
-
const bubble = document.createElement("div");
|
|
1085
|
-
bubble.textContent = text;
|
|
1086
|
-
bubble.style.opacity = "0";
|
|
1087
|
-
Object.assign(bubble.style, {
|
|
1088
|
-
position: "absolute",
|
|
1089
|
-
bottom: "calc(100% + 10px)",
|
|
1090
|
-
left: "50%",
|
|
1091
|
-
transform: `translateX(-50%) scale(${inverseScale})`,
|
|
1092
|
-
transformOrigin: "center bottom",
|
|
1093
|
-
background: bubbleBg,
|
|
1094
|
-
border: `2px solid ${bubbleBorder}`,
|
|
1095
|
-
color: bubbleText,
|
|
1096
|
-
borderRadius: "12px",
|
|
1097
|
-
padding: "6px 14px",
|
|
1098
|
-
fontFamily: "system-ui, sans-serif",
|
|
1099
|
-
fontSize: "15px",
|
|
1100
|
-
lineHeight: "1.3",
|
|
1101
|
-
whiteSpace: "nowrap",
|
|
1102
|
-
maxWidth: "220px",
|
|
1103
|
-
overflow: "hidden",
|
|
1104
|
-
textOverflow: "ellipsis",
|
|
1105
|
-
pointerEvents: "none",
|
|
1106
|
-
zIndex: "10",
|
|
1107
|
-
boxShadow: bubbleShadow
|
|
1108
|
-
});
|
|
1109
|
-
const tail = document.createElement("span");
|
|
1110
|
-
Object.assign(tail.style, {
|
|
1111
|
-
position: "absolute",
|
|
1112
|
-
bottom: "-10px",
|
|
1113
|
-
left: "50%",
|
|
1114
|
-
transform: "translateX(-50%)",
|
|
1115
|
-
width: "0",
|
|
1116
|
-
height: "0",
|
|
1117
|
-
borderLeft: "7px solid transparent",
|
|
1118
|
-
borderRight: "7px solid transparent",
|
|
1119
|
-
borderTop: `10px solid ${bubbleBorder}`
|
|
1120
|
-
});
|
|
1121
|
-
bubble.appendChild(tail);
|
|
1122
|
-
el.style.overflow = "visible";
|
|
1123
|
-
el.appendChild(bubble);
|
|
1124
|
-
const fadeDur = Math.min(200, durMs * 0.15);
|
|
1125
|
-
anims.push(
|
|
1126
|
-
bubble.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1127
|
-
delay: delayMs,
|
|
1128
|
-
duration: fadeDur,
|
|
1129
|
-
fill: "forwards"
|
|
1130
|
-
}),
|
|
1131
|
-
bubble.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
1132
|
-
delay: delayMs + durMs - fadeDur,
|
|
1133
|
-
duration: fadeDur,
|
|
1134
|
-
fill: "forwards"
|
|
1135
|
-
})
|
|
1136
|
-
);
|
|
1137
|
-
break;
|
|
1138
|
-
}
|
|
1139
|
-
// ── pose ────────────────────────────────────────────────────────────────
|
|
1140
|
-
// Sets multiple body parts to target angles simultaneously.
|
|
1141
|
-
// Usage: @1.0: hero.pose(arm_left=45, arm_right=-45, leg_left=10, dur=0.4)
|
|
1142
|
-
case "pose": {
|
|
1143
|
-
const poseParts = ["arm_left", "arm_right", "leg_left", "leg_right", "head", "body"];
|
|
1144
|
-
for (const partName of poseParts) {
|
|
1145
|
-
if (typeof ev.params[partName] !== "number") continue;
|
|
1146
|
-
const pSel = PART_SEL[partName];
|
|
1147
|
-
if (!pSel) continue;
|
|
1148
|
-
const pEl = el.querySelector(pSel);
|
|
1149
|
-
if (!pEl) continue;
|
|
1150
|
-
const fromDeg = readRotation(pEl);
|
|
1151
|
-
const toDeg = ev.params[partName];
|
|
1152
|
-
anims.push(
|
|
1153
|
-
pEl.animate(
|
|
1154
|
-
[
|
|
1155
|
-
{ transform: `rotate(${fromDeg}deg)` },
|
|
1156
|
-
{ transform: `rotate(${toDeg}deg)` }
|
|
1157
|
-
],
|
|
1158
|
-
{ ...baseOpts, fill: "forwards" }
|
|
1159
|
-
)
|
|
1160
|
-
);
|
|
1161
|
-
pEl.style.transform = pEl.style.transform.replace(
|
|
1162
|
-
/rotate\([^)]*\)/,
|
|
1163
|
-
`rotate(${toDeg}deg)`
|
|
1164
|
-
);
|
|
1165
|
-
}
|
|
1166
|
-
break;
|
|
1167
|
-
}
|
|
1168
|
-
// ── wave ────────────────────────────────────────────────────────────────
|
|
1169
|
-
// Built-in wave gesture: raises arm, oscillates, returns.
|
|
1170
|
-
// Usage: @2.0: hero.wave(side=right, dur=0.8)
|
|
1171
|
-
case "wave": {
|
|
1172
|
-
const wSide = String(ev.params.side ?? "right");
|
|
1173
|
-
const wArmEl = el.querySelector(
|
|
1174
|
-
wSide === "left" ? "[data-fig-arm-l]" : "[data-fig-arm-r]"
|
|
1175
|
-
);
|
|
1176
|
-
if (!wArmEl) break;
|
|
1177
|
-
const wRest = readRotation(wArmEl);
|
|
1178
|
-
const wUp = wSide === "left" ? 70 : -70;
|
|
1179
|
-
const wMid1 = wSide === "left" ? 50 : -50;
|
|
1180
|
-
const wMid2 = wSide === "left" ? 70 : -70;
|
|
1181
|
-
anims.push(
|
|
1182
|
-
wArmEl.animate(
|
|
1183
|
-
[
|
|
1184
|
-
{ transform: `rotate(${wRest}deg)`, offset: 0 },
|
|
1185
|
-
{ transform: `rotate(${wUp}deg)`, offset: 0.2 },
|
|
1186
|
-
{ transform: `rotate(${wMid1}deg)`, offset: 0.4 },
|
|
1187
|
-
{ transform: `rotate(${wMid2}deg)`, offset: 0.55 },
|
|
1188
|
-
{ transform: `rotate(${wMid1}deg)`, offset: 0.7 },
|
|
1189
|
-
{ transform: `rotate(${wMid2}deg)`, offset: 0.85 },
|
|
1190
|
-
{ transform: `rotate(${wRest}deg)`, offset: 1 }
|
|
1191
|
-
],
|
|
1192
|
-
{ ...baseOpts, easing: "ease-in-out", fill: "forwards" }
|
|
1193
|
-
)
|
|
1194
|
-
);
|
|
1195
|
-
break;
|
|
1196
|
-
}
|
|
1197
|
-
// ── jump ────────────────────────────────────────────────────────────────
|
|
1198
|
-
// Jumps the actor up with squash/stretch effect.
|
|
1199
|
-
// Usage: @3.0: hero.jump(height=30, dur=0.5)
|
|
1200
|
-
case "jump": {
|
|
1201
|
-
const jHeight = typeof ev.params.height === "number" ? ev.params.height : 30;
|
|
1202
|
-
anims.push(
|
|
1203
|
-
el.animate(
|
|
1204
|
-
[
|
|
1205
|
-
{ transform: txFn(s), offset: 0 },
|
|
1206
|
-
{ transform: txFn({ ...s, scale: s.scale * 0.9 }), offset: 0.1 },
|
|
1207
|
-
{ transform: txFn({ ...s, y: s.y - jHeight, scale: s.scale * 1.1 }), offset: 0.45 },
|
|
1208
|
-
{ transform: txFn({ ...s, y: s.y - jHeight * 0.3, scale: s.scale * 1.05 }), offset: 0.7 },
|
|
1209
|
-
{ transform: txFn({ ...s, scale: s.scale * 0.92 }), offset: 0.88 },
|
|
1210
|
-
{ transform: txFn(s), offset: 1 }
|
|
1211
|
-
],
|
|
1212
|
-
{ ...baseOpts, easing: "ease-in-out" }
|
|
1213
|
-
)
|
|
1214
|
-
);
|
|
1215
|
-
break;
|
|
2083
|
+
var throwAsset = ({
|
|
2084
|
+
ev,
|
|
2085
|
+
state,
|
|
2086
|
+
baseOpts,
|
|
2087
|
+
ast,
|
|
2088
|
+
states,
|
|
2089
|
+
scene,
|
|
2090
|
+
assetOverrides,
|
|
2091
|
+
anims
|
|
2092
|
+
}) => {
|
|
2093
|
+
const assetName = String(ev.params.asset ?? "");
|
|
2094
|
+
const targetState = states.get(String(ev.params.to ?? ""));
|
|
2095
|
+
const assetDef = ast.assets[assetName];
|
|
2096
|
+
if (!assetDef || !targetState) return;
|
|
2097
|
+
const projectile = createProjectile(assetName, assetDef, assetOverrides);
|
|
2098
|
+
Object.assign(projectile.style, {
|
|
2099
|
+
position: "absolute",
|
|
2100
|
+
left: "0",
|
|
2101
|
+
top: "0",
|
|
2102
|
+
pointerEvents: "none",
|
|
2103
|
+
zIndex: "9",
|
|
2104
|
+
opacity: "0"
|
|
2105
|
+
});
|
|
2106
|
+
scene.appendChild(projectile);
|
|
2107
|
+
const anim = projectile.animate(
|
|
2108
|
+
[
|
|
2109
|
+
{ transform: tx(state), opacity: 1 },
|
|
2110
|
+
{ transform: tx(targetState), opacity: 0 }
|
|
2111
|
+
],
|
|
2112
|
+
{ ...baseOpts, easing: "ease-in" }
|
|
2113
|
+
);
|
|
2114
|
+
anim.addEventListener("finish", () => {
|
|
2115
|
+
if (projectile.parentNode === scene) scene.removeChild(projectile);
|
|
2116
|
+
});
|
|
2117
|
+
anims.push(anim);
|
|
2118
|
+
};
|
|
2119
|
+
|
|
2120
|
+
// src/actions/registry.ts
|
|
2121
|
+
var ACTION_HANDLERS = Object.freeze({
|
|
2122
|
+
// Universal — every actor type
|
|
2123
|
+
move,
|
|
2124
|
+
enter,
|
|
2125
|
+
exit,
|
|
2126
|
+
fade_in: fadeIn,
|
|
2127
|
+
fade_out: fadeOut,
|
|
2128
|
+
spring,
|
|
2129
|
+
follow_path: followPath,
|
|
2130
|
+
scale,
|
|
2131
|
+
rotate,
|
|
2132
|
+
shake,
|
|
2133
|
+
pulse,
|
|
2134
|
+
glow,
|
|
2135
|
+
ripple,
|
|
2136
|
+
blur,
|
|
2137
|
+
line_reveal: lineReveal,
|
|
2138
|
+
mask,
|
|
2139
|
+
parallax,
|
|
2140
|
+
jump,
|
|
2141
|
+
bounce,
|
|
2142
|
+
say,
|
|
2143
|
+
throw: throwAsset,
|
|
2144
|
+
// Figure-only — the parser rejects these on other actor types
|
|
2145
|
+
punch,
|
|
2146
|
+
kick,
|
|
2147
|
+
wave,
|
|
2148
|
+
nod,
|
|
2149
|
+
rotate_part: rotatePart,
|
|
2150
|
+
pose,
|
|
2151
|
+
face,
|
|
2152
|
+
// System-diagram flow edges (@markdy/stdlib-systems)
|
|
2153
|
+
request: flowEdge,
|
|
2154
|
+
response: flowEdge,
|
|
2155
|
+
emit: flowEdge
|
|
2156
|
+
});
|
|
2157
|
+
function handlerFor(action) {
|
|
2158
|
+
return ACTION_HANDLERS[action];
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// src/animations.ts
|
|
2162
|
+
var DEFAULT_DURATION_S = 0.5;
|
|
2163
|
+
var DEFAULT_EASE_BY_ACTION = {
|
|
2164
|
+
enter: "out",
|
|
2165
|
+
fade_in: "out",
|
|
2166
|
+
exit: "in",
|
|
2167
|
+
fade_out: "in"
|
|
2168
|
+
};
|
|
2169
|
+
function buildAnimations(ast, actorEls, actorLayer, cameraLayer, assetOverrides, faceSwaps) {
|
|
2170
|
+
const anims = [];
|
|
2171
|
+
const states = /* @__PURE__ */ new Map();
|
|
2172
|
+
for (const [name, def] of Object.entries(ast.actors)) {
|
|
2173
|
+
states.set(name, stateFrom(def));
|
|
2174
|
+
}
|
|
2175
|
+
const captionActors = new Set(
|
|
2176
|
+
Object.entries(ast.actors).filter(([, def]) => def.type === "caption").map(([name]) => name)
|
|
2177
|
+
);
|
|
2178
|
+
const txFor = (actorName) => captionActors.has(actorName) ? txCaption : tx;
|
|
2179
|
+
const events = [...ast.events].sort((a, b) => a.time - b.time);
|
|
2180
|
+
preInitInlineStyles(ast, actorEls, states, events, txFor);
|
|
2181
|
+
const cameraState = freshCameraState();
|
|
2182
|
+
for (const ev of events) {
|
|
2183
|
+
const delayMs = ev.time * 1e3;
|
|
2184
|
+
const durMs = Math.max(
|
|
2185
|
+
1,
|
|
2186
|
+
(typeof ev.params.dur === "number" ? ev.params.dur : DEFAULT_DURATION_S) * 1e3
|
|
2187
|
+
);
|
|
2188
|
+
const baseOpts = {
|
|
2189
|
+
delay: delayMs,
|
|
2190
|
+
duration: durMs,
|
|
2191
|
+
fill: "forwards",
|
|
2192
|
+
easing: toEasing(ev.params.ease ?? DEFAULT_EASE_BY_ACTION[ev.action])
|
|
2193
|
+
};
|
|
2194
|
+
if (ev.actor === "camera") {
|
|
2195
|
+
buildCameraAction(ev, cameraLayer, ast, baseOpts, anims, cameraState);
|
|
2196
|
+
continue;
|
|
1216
2197
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
2198
|
+
const el = actorEls.get(ev.actor);
|
|
2199
|
+
const state = states.get(ev.actor);
|
|
2200
|
+
const handler = handlerFor(ev.action);
|
|
2201
|
+
if (!el || !state || !handler) continue;
|
|
2202
|
+
const ctx = {
|
|
2203
|
+
ev,
|
|
2204
|
+
el,
|
|
2205
|
+
state,
|
|
2206
|
+
baseOpts,
|
|
2207
|
+
delayMs,
|
|
2208
|
+
durMs,
|
|
2209
|
+
ast,
|
|
2210
|
+
states,
|
|
2211
|
+
actorEls,
|
|
2212
|
+
scene: actorLayer,
|
|
2213
|
+
assetOverrides,
|
|
2214
|
+
faceSwaps,
|
|
2215
|
+
anims,
|
|
2216
|
+
tx: txFor(ev.actor)
|
|
2217
|
+
};
|
|
2218
|
+
handler(ctx);
|
|
2219
|
+
}
|
|
2220
|
+
return anims;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// src/player.ts
|
|
2224
|
+
var SCENE_STYLE_ID = "markdy-scene-ambience-styles";
|
|
2225
|
+
var SAFE_FRAME_PADDING = 28;
|
|
2226
|
+
function ensureSceneStyles(doc) {
|
|
2227
|
+
if (doc.getElementById(SCENE_STYLE_ID)) return;
|
|
2228
|
+
const style = doc.createElement("style");
|
|
2229
|
+
style.id = SCENE_STYLE_ID;
|
|
2230
|
+
style.textContent = `
|
|
2231
|
+
.markdy-scene-root {
|
|
2232
|
+
--markdy-scene-grid: rgba(148, 163, 184, 0.13);
|
|
2233
|
+
--markdy-scene-grid-strong: rgba(148, 163, 184, 0.2);
|
|
2234
|
+
--markdy-scene-vignette: rgba(2, 6, 23, 0.72);
|
|
2235
|
+
--markdy-scene-accent: rgba(56, 189, 248, 0.16);
|
|
2236
|
+
isolation: isolate;
|
|
2237
|
+
}
|
|
2238
|
+
.markdy-scene-root::before,
|
|
2239
|
+
.markdy-scene-root::after {
|
|
2240
|
+
content: "";
|
|
2241
|
+
position: absolute;
|
|
2242
|
+
inset: 0;
|
|
2243
|
+
pointer-events: none;
|
|
2244
|
+
}
|
|
2245
|
+
.markdy-scene-root::before {
|
|
2246
|
+
z-index: 0;
|
|
2247
|
+
background:
|
|
2248
|
+
linear-gradient(var(--markdy-scene-grid) 1px, transparent 1px) 0 0 / 32px 32px,
|
|
2249
|
+
linear-gradient(90deg, var(--markdy-scene-grid) 1px, transparent 1px) 0 0 / 32px 32px,
|
|
2250
|
+
linear-gradient(var(--markdy-scene-grid-strong) 1px, transparent 1px) 0 0 / 160px 160px,
|
|
2251
|
+
linear-gradient(90deg, var(--markdy-scene-grid-strong) 1px, transparent 1px) 0 0 / 160px 160px;
|
|
2252
|
+
mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent);
|
|
2253
|
+
opacity: 0.82;
|
|
2254
|
+
}
|
|
2255
|
+
.markdy-scene-root::after {
|
|
2256
|
+
z-index: 1;
|
|
2257
|
+
background:
|
|
2258
|
+
radial-gradient(ellipse at 50% 0%, var(--markdy-scene-accent), transparent 42%),
|
|
2259
|
+
linear-gradient(180deg, transparent 0%, rgba(2, 6, 23, 0.08) 58%, var(--markdy-scene-vignette) 100%),
|
|
2260
|
+
linear-gradient(90deg, rgba(255, 255, 255, 0.035), transparent 18%, transparent 82%, rgba(255, 255, 255, 0.035));
|
|
2261
|
+
mix-blend-mode: screen;
|
|
2262
|
+
opacity: 0.8;
|
|
2263
|
+
}
|
|
2264
|
+
.markdy-scene-root[data-markdy-scene-tone="light"] {
|
|
2265
|
+
--markdy-scene-grid: rgba(15, 23, 42, 0.09);
|
|
2266
|
+
--markdy-scene-grid-strong: rgba(15, 23, 42, 0.14);
|
|
2267
|
+
--markdy-scene-vignette: rgba(241, 245, 249, 0.74);
|
|
2268
|
+
--markdy-scene-accent: rgba(14, 165, 233, 0.12);
|
|
2269
|
+
}
|
|
2270
|
+
.markdy-scene-content {
|
|
2271
|
+
z-index: 2;
|
|
2272
|
+
}
|
|
2273
|
+
.markdy-scene-actor-layer {
|
|
2274
|
+
position: absolute;
|
|
2275
|
+
inset: 0;
|
|
2276
|
+
overflow: visible;
|
|
2277
|
+
transform-origin: 0 0;
|
|
2278
|
+
will-change: transform;
|
|
2279
|
+
}
|
|
2280
|
+
`;
|
|
2281
|
+
doc.head.appendChild(style);
|
|
2282
|
+
}
|
|
2283
|
+
function unionRect(a, b) {
|
|
2284
|
+
if (!a) return b;
|
|
2285
|
+
return {
|
|
2286
|
+
x1: Math.min(a.x1, b.x1),
|
|
2287
|
+
y1: Math.min(a.y1, b.y1),
|
|
2288
|
+
x2: Math.max(a.x2, b.x2),
|
|
2289
|
+
y2: Math.max(a.y2, b.y2)
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
function numericPair(value) {
|
|
2293
|
+
if (!Array.isArray(value) || value.length < 2) return null;
|
|
2294
|
+
const [x, y] = value;
|
|
2295
|
+
return typeof x === "number" && typeof y === "number" ? [x, y] : null;
|
|
2296
|
+
}
|
|
2297
|
+
function computeContentBounds(ast) {
|
|
2298
|
+
let bounds = null;
|
|
2299
|
+
const maxScaleByActor = /* @__PURE__ */ new Map();
|
|
2300
|
+
const positionsByActor = /* @__PURE__ */ new Map();
|
|
2301
|
+
for (const [name, def] of Object.entries(ast.actors)) {
|
|
2302
|
+
maxScaleByActor.set(name, Math.max(1e-3, def.scale ?? 1));
|
|
2303
|
+
positionsByActor.set(name, [[def.x, def.y]]);
|
|
2304
|
+
}
|
|
2305
|
+
for (const ev of ast.events) {
|
|
2306
|
+
if (ev.actor === "camera") continue;
|
|
2307
|
+
if (ev.action === "scale" && typeof ev.params.to === "number") {
|
|
2308
|
+
maxScaleByActor.set(ev.actor, Math.max(maxScaleByActor.get(ev.actor) ?? 1, ev.params.to));
|
|
1238
2309
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
case "bounce": {
|
|
1243
|
-
const bIntensity = typeof ev.params.intensity === "number" ? ev.params.intensity : 15;
|
|
1244
|
-
const bCount = typeof ev.params.count === "number" ? ev.params.count : 3;
|
|
1245
|
-
const keyframes = [{ transform: txFn(s), offset: 0 }];
|
|
1246
|
-
for (let bi = 0; bi < bCount; bi++) {
|
|
1247
|
-
const amp = bIntensity * Math.pow(0.55, bi);
|
|
1248
|
-
const baseOffset = (bi + 0.5) / (bCount + 0.5);
|
|
1249
|
-
const peakOffset = Math.min(baseOffset, 0.98);
|
|
1250
|
-
const valleyOffset = Math.min(baseOffset + 0.25 / (bCount + 0.5), 0.99);
|
|
1251
|
-
keyframes.push({
|
|
1252
|
-
transform: txFn({ ...s, y: s.y - amp }),
|
|
1253
|
-
offset: peakOffset
|
|
1254
|
-
});
|
|
1255
|
-
if (bi < bCount - 1) {
|
|
1256
|
-
keyframes.push({
|
|
1257
|
-
transform: txFn(s),
|
|
1258
|
-
offset: valleyOffset
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
keyframes.push({ transform: txFn(s), offset: 1 });
|
|
1263
|
-
anims.push(
|
|
1264
|
-
el.animate(keyframes, { ...baseOpts, easing: "ease-out" })
|
|
1265
|
-
);
|
|
1266
|
-
break;
|
|
2310
|
+
if (ev.action === "move" || ev.action === "spring") {
|
|
2311
|
+
const to = numericPair(ev.params.to);
|
|
2312
|
+
if (to) positionsByActor.get(ev.actor)?.push(to);
|
|
1267
2313
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
if (!assetDef || !targetState) break;
|
|
1275
|
-
let projectile;
|
|
1276
|
-
if (assetDef.type === "image") {
|
|
1277
|
-
const img = document.createElement("img");
|
|
1278
|
-
img.src = assetOverrides[assetName] ?? assetDef.value;
|
|
1279
|
-
img.alt = assetName;
|
|
1280
|
-
img.setAttribute("draggable", "false");
|
|
1281
|
-
img.style.width = "32px";
|
|
1282
|
-
img.style.height = "32px";
|
|
1283
|
-
projectile = img;
|
|
1284
|
-
} else {
|
|
1285
|
-
const span = document.createElement("span");
|
|
1286
|
-
span.className = "iconify";
|
|
1287
|
-
span.dataset.icon = assetDef.value;
|
|
1288
|
-
span.style.fontSize = "32px";
|
|
1289
|
-
span.style.lineHeight = "1";
|
|
1290
|
-
span.style.display = "inline-block";
|
|
1291
|
-
projectile = span;
|
|
1292
|
-
}
|
|
1293
|
-
Object.assign(projectile.style, {
|
|
1294
|
-
position: "absolute",
|
|
1295
|
-
left: "0",
|
|
1296
|
-
top: "0",
|
|
1297
|
-
pointerEvents: "none",
|
|
1298
|
-
zIndex: "9",
|
|
1299
|
-
opacity: "0"
|
|
1300
|
-
});
|
|
1301
|
-
scene.appendChild(projectile);
|
|
1302
|
-
const throwAnim = projectile.animate(
|
|
1303
|
-
[
|
|
1304
|
-
{ transform: tx(s), opacity: 1 },
|
|
1305
|
-
{ transform: tx(targetState), opacity: 0 }
|
|
1306
|
-
],
|
|
1307
|
-
{ ...baseOpts, easing: "ease-in" }
|
|
1308
|
-
);
|
|
1309
|
-
throwAnim.addEventListener("finish", () => {
|
|
1310
|
-
if (projectile.parentNode === scene) scene.removeChild(projectile);
|
|
1311
|
-
});
|
|
1312
|
-
anims.push(throwAnim);
|
|
1313
|
-
break;
|
|
2314
|
+
}
|
|
2315
|
+
for (const [name, def] of Object.entries(ast.actors)) {
|
|
2316
|
+
const positions = positionsByActor.get(name) ?? [[def.x, def.y]];
|
|
2317
|
+
const scale2 = maxScaleByActor.get(name) ?? def.scale ?? 1;
|
|
2318
|
+
for (const [x, y] of positions) {
|
|
2319
|
+
bounds = unionRect(bounds, actorRect({ ...stateFrom(def), x, y, scale: scale2 }, def.type));
|
|
1314
2320
|
}
|
|
1315
|
-
default:
|
|
1316
|
-
break;
|
|
1317
2321
|
}
|
|
2322
|
+
return bounds;
|
|
2323
|
+
}
|
|
2324
|
+
function computeSafeFrame(ast) {
|
|
2325
|
+
const bounds = computeContentBounds(ast);
|
|
2326
|
+
if (!bounds) return { x: 0, y: 0, scale: 1 };
|
|
2327
|
+
const boundsWidth = Math.max(1, bounds.x2 - bounds.x1);
|
|
2328
|
+
const boundsHeight = Math.max(1, bounds.y2 - bounds.y1);
|
|
2329
|
+
const availableWidth = Math.max(1, ast.meta.width - SAFE_FRAME_PADDING * 2);
|
|
2330
|
+
const availableHeight = Math.max(1, ast.meta.height - SAFE_FRAME_PADDING * 2);
|
|
2331
|
+
const scale2 = Math.min(1, availableWidth / boundsWidth, availableHeight / boundsHeight);
|
|
2332
|
+
const scaledWidth = boundsWidth * scale2;
|
|
2333
|
+
const scaledHeight = boundsHeight * scale2;
|
|
2334
|
+
function axisOffset(min, max, sceneSize, scaledSize) {
|
|
2335
|
+
const paddedMin = SAFE_FRAME_PADDING;
|
|
2336
|
+
const paddedMax = sceneSize - SAFE_FRAME_PADDING;
|
|
2337
|
+
const scaledMin = min * scale2;
|
|
2338
|
+
const scaledMax = max * scale2;
|
|
2339
|
+
if (scaledSize > paddedMax - paddedMin) return (sceneSize - scaledSize) / 2 - scaledMin;
|
|
2340
|
+
if (scaledMin < paddedMin) return paddedMin - scaledMin;
|
|
2341
|
+
if (scaledMax > paddedMax) return paddedMax - scaledMax;
|
|
2342
|
+
return 0;
|
|
2343
|
+
}
|
|
2344
|
+
const x = axisOffset(bounds.x1, bounds.x2, ast.meta.width, scaledWidth);
|
|
2345
|
+
const y = axisOffset(bounds.y1, bounds.y2, ast.meta.height, scaledHeight);
|
|
2346
|
+
return {
|
|
2347
|
+
x: Math.round(x * 1e3) / 1e3,
|
|
2348
|
+
y: Math.round(y * 1e3) / 1e3,
|
|
2349
|
+
scale: Math.round(scale2 * 1e3) / 1e3
|
|
2350
|
+
};
|
|
1318
2351
|
}
|
|
1319
|
-
|
|
1320
|
-
// src/player.ts
|
|
1321
2352
|
function bgToTextColor(bg) {
|
|
1322
2353
|
let hex = bg.trim().replace(/^#/, "");
|
|
1323
2354
|
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
@@ -1417,6 +2448,9 @@ function createPlayer(opts) {
|
|
|
1417
2448
|
}
|
|
1418
2449
|
}
|
|
1419
2450
|
const scene = document.createElement("div");
|
|
2451
|
+
ensureSceneStyles(document);
|
|
2452
|
+
scene.className = "markdy-scene-root";
|
|
2453
|
+
scene.dataset.markdySceneTone = bgToTextColor(ast.meta.bg) === "#1a1a1a" ? "light" : "dark";
|
|
1420
2454
|
Object.assign(scene.style, {
|
|
1421
2455
|
position: "absolute",
|
|
1422
2456
|
top: "0",
|
|
@@ -1431,6 +2465,7 @@ function createPlayer(opts) {
|
|
|
1431
2465
|
});
|
|
1432
2466
|
viewport.appendChild(scene);
|
|
1433
2467
|
const sceneContent = document.createElement("div");
|
|
2468
|
+
sceneContent.className = "markdy-scene-content";
|
|
1434
2469
|
Object.assign(sceneContent.style, {
|
|
1435
2470
|
position: "absolute",
|
|
1436
2471
|
top: "0",
|
|
@@ -1441,6 +2476,12 @@ function createPlayer(opts) {
|
|
|
1441
2476
|
willChange: "transform"
|
|
1442
2477
|
});
|
|
1443
2478
|
scene.appendChild(sceneContent);
|
|
2479
|
+
const actorLayer = document.createElement("div");
|
|
2480
|
+
actorLayer.className = "markdy-scene-actor-layer";
|
|
2481
|
+
const safeFrame = computeSafeFrame(ast);
|
|
2482
|
+
actorLayer.dataset.markdySafeFrame = `${safeFrame.x},${safeFrame.y},${safeFrame.scale}`;
|
|
2483
|
+
actorLayer.style.transform = `translate(${safeFrame.x}px, ${safeFrame.y}px) scale(${safeFrame.scale})`;
|
|
2484
|
+
sceneContent.appendChild(actorLayer);
|
|
1444
2485
|
function scaleScene() {
|
|
1445
2486
|
const s = viewport.clientWidth / ast.meta.width;
|
|
1446
2487
|
scene.style.transform = `scale(${s})`;
|
|
@@ -1451,11 +2492,11 @@ function createPlayer(opts) {
|
|
|
1451
2492
|
const actorEls = /* @__PURE__ */ new Map();
|
|
1452
2493
|
for (const [name, def] of Object.entries(ast.actors)) {
|
|
1453
2494
|
const el = createActorEl(name, def, ast.assets, assetOverrides);
|
|
1454
|
-
|
|
2495
|
+
actorLayer.appendChild(el);
|
|
1455
2496
|
actorEls.set(name, el);
|
|
1456
2497
|
}
|
|
1457
2498
|
const faceSwaps = [];
|
|
1458
|
-
const allAnims = buildAnimations(ast, actorEls, sceneContent, assetOverrides, faceSwaps);
|
|
2499
|
+
const allAnims = buildAnimations(ast, actorEls, actorLayer, sceneContent, assetOverrides, faceSwaps);
|
|
1459
2500
|
faceSwaps.sort((a, b) => a.timeMs - b.timeMs);
|
|
1460
2501
|
for (const anim of allAnims) {
|
|
1461
2502
|
anim.pause();
|