@markdy/renderer-dom 0.7.11 → 0.7.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +140 -16
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -645,6 +645,123 @@ function actorCenter(state, actorType) {
|
|
|
645
645
|
y: state.y + height / 2
|
|
646
646
|
};
|
|
647
647
|
}
|
|
648
|
+
function actorRect(state, actorType) {
|
|
649
|
+
const { width, height } = actorSizeByType(actorType);
|
|
650
|
+
return {
|
|
651
|
+
x1: state.x,
|
|
652
|
+
y1: state.y,
|
|
653
|
+
x2: state.x + width,
|
|
654
|
+
y2: state.y + height
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function inflateRect(rect, pad) {
|
|
658
|
+
return {
|
|
659
|
+
x1: rect.x1 - pad,
|
|
660
|
+
y1: rect.y1 - pad,
|
|
661
|
+
x2: rect.x2 + pad,
|
|
662
|
+
y2: rect.y2 + pad
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function segmentIntersectsRect(a, b, rect) {
|
|
666
|
+
const minX = Math.min(a.x, b.x);
|
|
667
|
+
const maxX = Math.max(a.x, b.x);
|
|
668
|
+
const minY = Math.min(a.y, b.y);
|
|
669
|
+
const maxY = Math.max(a.y, b.y);
|
|
670
|
+
const horizontal = Math.abs(a.y - b.y) < 1e-3;
|
|
671
|
+
const vertical = Math.abs(a.x - b.x) < 1e-3;
|
|
672
|
+
if (horizontal) {
|
|
673
|
+
const yHit = a.y > rect.y1 && a.y < rect.y2;
|
|
674
|
+
const xOverlap = maxX > rect.x1 && minX < rect.x2;
|
|
675
|
+
return yHit && xOverlap;
|
|
676
|
+
}
|
|
677
|
+
if (vertical) {
|
|
678
|
+
const xHit = a.x > rect.x1 && a.x < rect.x2;
|
|
679
|
+
const yOverlap = maxY > rect.y1 && minY < rect.y2;
|
|
680
|
+
return xHit && yOverlap;
|
|
681
|
+
}
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
function countPathIntersections(points, obstacles) {
|
|
685
|
+
let hits = 0;
|
|
686
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
687
|
+
for (const obstacle of obstacles) {
|
|
688
|
+
if (segmentIntersectsRect(points[i], points[i + 1], obstacle)) hits++;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return hits;
|
|
692
|
+
}
|
|
693
|
+
function toPathD(points) {
|
|
694
|
+
return points.map((p, i) => `${i === 0 ? "M" : "L"} ${round1(p.x)} ${round1(p.y)}`).join(" ");
|
|
695
|
+
}
|
|
696
|
+
function round1(n) {
|
|
697
|
+
return Math.round(n * 10) / 10;
|
|
698
|
+
}
|
|
699
|
+
function polylineLength(points) {
|
|
700
|
+
let total = 0;
|
|
701
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
702
|
+
total += Math.hypot(points[i + 1].x - points[i].x, points[i + 1].y - points[i].y);
|
|
703
|
+
}
|
|
704
|
+
return Math.max(1, total);
|
|
705
|
+
}
|
|
706
|
+
function pointAtDistance(points, dist) {
|
|
707
|
+
let remain = dist;
|
|
708
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
709
|
+
const a = points[i];
|
|
710
|
+
const b = points[i + 1];
|
|
711
|
+
const seg = Math.hypot(b.x - a.x, b.y - a.y);
|
|
712
|
+
if (remain <= seg || i === points.length - 2) {
|
|
713
|
+
const t = seg <= 0 ? 0 : remain / seg;
|
|
714
|
+
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
715
|
+
}
|
|
716
|
+
remain -= seg;
|
|
717
|
+
}
|
|
718
|
+
return points[0];
|
|
719
|
+
}
|
|
720
|
+
function routeFlowPath(sourceName, targetName, sourceState, targetState, states, ast, lane) {
|
|
721
|
+
const sourceRect = actorRect(sourceState, ast.actors[sourceName]?.type ?? "box");
|
|
722
|
+
const targetRect = actorRect(targetState, ast.actors[targetName]?.type ?? "box");
|
|
723
|
+
const laneShift = lane * 18;
|
|
724
|
+
const sourceCenter = actorCenter(sourceState, ast.actors[sourceName]?.type ?? "box");
|
|
725
|
+
const targetCenter = actorCenter(targetState, ast.actors[targetName]?.type ?? "box");
|
|
726
|
+
const horizontalPrimary = Math.abs(targetCenter.x - sourceCenter.x) >= Math.abs(targetCenter.y - sourceCenter.y);
|
|
727
|
+
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 };
|
|
728
|
+
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 };
|
|
729
|
+
const obstacles = [];
|
|
730
|
+
for (const [name, state] of states.entries()) {
|
|
731
|
+
if (name === sourceName || name === targetName) continue;
|
|
732
|
+
const type = ast.actors[name]?.type ?? "box";
|
|
733
|
+
obstacles.push(inflateRect(actorRect(state, type), 8));
|
|
734
|
+
}
|
|
735
|
+
const candidates = [];
|
|
736
|
+
if (Math.abs(source.y - target.y) < 1e-3 || Math.abs(source.x - target.x) < 1e-3) {
|
|
737
|
+
candidates.push([source, target]);
|
|
738
|
+
}
|
|
739
|
+
const midX = round1((source.x + target.x) / 2 + laneShift);
|
|
740
|
+
const midY = round1((source.y + target.y) / 2 + laneShift);
|
|
741
|
+
candidates.push(
|
|
742
|
+
[source, { x: midX, y: source.y }, { x: midX, y: target.y }, target],
|
|
743
|
+
[source, { x: source.x, y: midY }, { x: target.x, y: midY }, target]
|
|
744
|
+
);
|
|
745
|
+
const minObstacleY = obstacles.length ? Math.min(...obstacles.map((o) => o.y1)) : Math.min(source.y, target.y);
|
|
746
|
+
const maxObstacleY = obstacles.length ? Math.max(...obstacles.map((o) => o.y2)) : Math.max(source.y, target.y);
|
|
747
|
+
const topLane = Math.max(16, minObstacleY - 24 - lane * 12);
|
|
748
|
+
const bottomLane = Math.min(ast.meta.height - 16, maxObstacleY + 24 + lane * 12);
|
|
749
|
+
candidates.push(
|
|
750
|
+
[source, { x: source.x, y: topLane }, { x: target.x, y: topLane }, target],
|
|
751
|
+
[source, { x: source.x, y: bottomLane }, { x: target.x, y: bottomLane }, target]
|
|
752
|
+
);
|
|
753
|
+
let best = candidates[0];
|
|
754
|
+
let bestHits = Number.POSITIVE_INFINITY;
|
|
755
|
+
for (const candidate of candidates) {
|
|
756
|
+
const hits = countPathIntersections(candidate, obstacles);
|
|
757
|
+
if (hits < bestHits) {
|
|
758
|
+
best = candidate;
|
|
759
|
+
bestHits = hits;
|
|
760
|
+
if (hits === 0) break;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return best;
|
|
764
|
+
}
|
|
648
765
|
function ensureEdgeLayer(scene) {
|
|
649
766
|
const existing = scene.querySelector("svg[data-markdy-edge-layer='1']");
|
|
650
767
|
if (existing) return existing;
|
|
@@ -662,18 +779,22 @@ function ensureEdgeLayer(scene) {
|
|
|
662
779
|
scene.appendChild(svg);
|
|
663
780
|
return svg;
|
|
664
781
|
}
|
|
665
|
-
function renderFlowEdge(ev, sourceState, targetState,
|
|
782
|
+
function renderFlowEdge(ev, sourceName, targetName, sourceState, targetState, states, ast, lane, scene, baseOpts, anims) {
|
|
666
783
|
const styleToken = String(ev.params.style ?? "");
|
|
667
784
|
const isDashed = styleToken === "dashed" || styleToken === "fire_and_forget" || ev.action === "response";
|
|
668
785
|
const stroke = FLOW_STROKE_BY_ACTION[ev.action] ?? "#38bdf8";
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
786
|
+
const points = routeFlowPath(
|
|
787
|
+
sourceName,
|
|
788
|
+
targetName,
|
|
789
|
+
sourceState,
|
|
790
|
+
targetState,
|
|
791
|
+
states,
|
|
792
|
+
ast,
|
|
793
|
+
lane
|
|
794
|
+
);
|
|
795
|
+
const length = polylineLength(points);
|
|
796
|
+
const midPoint = pointAtDistance(points, length * 0.5);
|
|
797
|
+
const pathD = toPathD(points);
|
|
677
798
|
const svg = ensureEdgeLayer(scene);
|
|
678
799
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
679
800
|
group.setAttribute("data-markdy-flow-edge", "1");
|
|
@@ -682,6 +803,7 @@ function renderFlowEdge(ev, sourceState, targetState, sourceType, targetType, sc
|
|
|
682
803
|
path.setAttribute("fill", "none");
|
|
683
804
|
path.setAttribute("stroke", stroke);
|
|
684
805
|
path.setAttribute("stroke-width", "2.5");
|
|
806
|
+
path.setAttribute("data-markdy-flow-action", ev.action);
|
|
685
807
|
path.style.strokeDasharray = isDashed ? "8 6" : `${length}`;
|
|
686
808
|
path.style.strokeDashoffset = `${length}`;
|
|
687
809
|
group.appendChild(path);
|
|
@@ -696,8 +818,8 @@ function renderFlowEdge(ev, sourceState, targetState, sourceType, targetType, sc
|
|
|
696
818
|
if (labelRaw) {
|
|
697
819
|
const label = labelRaw.length > 28 ? `${labelRaw.slice(0, 27)}\u2026` : labelRaw;
|
|
698
820
|
const labelEl = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
699
|
-
labelEl.setAttribute("x", `${
|
|
700
|
-
labelEl.setAttribute("y", `${
|
|
821
|
+
labelEl.setAttribute("x", `${round1(midPoint.x)}`);
|
|
822
|
+
labelEl.setAttribute("y", `${round1(midPoint.y - 8)}`);
|
|
701
823
|
labelEl.setAttribute("text-anchor", "middle");
|
|
702
824
|
labelEl.setAttribute("font-size", "12");
|
|
703
825
|
labelEl.setAttribute("fill", "#cbd5e1");
|
|
@@ -722,7 +844,7 @@ function renderFlowEdge(ev, sourceState, targetState, sourceType, targetType, sc
|
|
|
722
844
|
})
|
|
723
845
|
);
|
|
724
846
|
}
|
|
725
|
-
function buildAction(ev, el, s, baseOpts, delayMs, durMs, ast, states,
|
|
847
|
+
function buildAction(ev, el, s, baseOpts, delayMs, durMs, ast, states, _actorEls, scene, assetOverrides, faceSwaps, anims, txFn) {
|
|
726
848
|
switch (ev.action) {
|
|
727
849
|
case "request":
|
|
728
850
|
case "response":
|
|
@@ -731,14 +853,16 @@ function buildAction(ev, el, s, baseOpts, delayMs, durMs, ast, states, actorEls,
|
|
|
731
853
|
if (!targetActorName) break;
|
|
732
854
|
const targetState = states.get(targetActorName);
|
|
733
855
|
if (!targetState) break;
|
|
734
|
-
const
|
|
735
|
-
const targetType = ast.actors[targetActorName]?.type ?? "box";
|
|
856
|
+
const lane = ev.line % 5 - 2;
|
|
736
857
|
renderFlowEdge(
|
|
737
858
|
ev,
|
|
859
|
+
ev.actor,
|
|
860
|
+
targetActorName,
|
|
738
861
|
s,
|
|
739
862
|
targetState,
|
|
740
|
-
|
|
741
|
-
|
|
863
|
+
states,
|
|
864
|
+
ast,
|
|
865
|
+
lane,
|
|
742
866
|
scene,
|
|
743
867
|
baseOpts,
|
|
744
868
|
anims
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.13",
|
|
4
4
|
"description": "Web Animations API renderer for MarkdyScript scenes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@markdy/core": "0.7.
|
|
46
|
+
"@markdy/core": "0.7.13"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"jsdom": "^29.1.1",
|