omkit 0.8.17 → 0.8.19
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 +313 -222
- package/dist/cli/ui/views/OmList.d.ts +8 -4
- package/dist/cli/ui/views/OmList.js +21 -7
- package/dist/cli/ui/views/SearchableList.d.ts +7 -1
- package/dist/cli/ui/views/SearchableList.js +14 -4
- package/dist/client/check.js +5 -8
- package/dist/client/discovery.js +16 -11
- package/dist/client/outline.d.ts +7 -0
- package/dist/client/outline.js +20 -0
- package/dist/client/registry.d.ts +6 -0
- package/dist/client/tsCache.d.ts +27 -0
- package/dist/client/tsCache.js +103 -0
- package/dist/output/writers/report/report.css +16 -20
- package/dist/output/writers/report/report.js +143 -157
- package/package.json +1 -1
|
@@ -195,6 +195,12 @@
|
|
|
195
195
|
if (!from) continue;
|
|
196
196
|
links.push({ item: it, op: l.op, from, to: it.node, t: it.t, t0: it.t - (l.ms || 0), key: l.key || "", ms: l.ms, outcome: l.outcome || "", site: l.site || "" });
|
|
197
197
|
}
|
|
198
|
+
// …drawn as a point on the row of the action that made it: what it did, when it did it
|
|
199
|
+
for (const l of links) {
|
|
200
|
+
l.label = linkLabel(l);
|
|
201
|
+
l.from.marks.push({ idx: l.item.idx, e: l.item.e, kind: "link", t: l.t, label: l.label, node: l.from, link: l });
|
|
202
|
+
}
|
|
203
|
+
for (const l of links) l.from.marks.sort((a, b) => a.t - b.t);
|
|
198
204
|
|
|
199
205
|
const asserts = items.filter((i) => i.kind === "pass" || i.kind === "fail");
|
|
200
206
|
const errors = collectErrors(items, nodes, run, tEnd - t0);
|
|
@@ -277,6 +283,12 @@
|
|
|
277
283
|
out.forEach((e, i) => (e.idx = i));
|
|
278
284
|
return out;
|
|
279
285
|
}
|
|
286
|
+
/** A link's short label, as its point or chip shows it. */
|
|
287
|
+
function linkLabel(l) {
|
|
288
|
+
if (l.op === "once") return `waited ${l.key}`;
|
|
289
|
+
if (l.op === "cancel") return `cancel ${displayName(l.to)}`;
|
|
290
|
+
return `${displayName(l.to)}.${l.key}()`;
|
|
291
|
+
}
|
|
280
292
|
function firstLine(s) {
|
|
281
293
|
return String(s).split("\n")[0];
|
|
282
294
|
}
|
|
@@ -515,21 +527,8 @@
|
|
|
515
527
|
.on("mouseleave", () => moveCursor(null));
|
|
516
528
|
chartSvg.append("g").attr("class", "grid");
|
|
517
529
|
chartSvg.append("g").attr("class", "links");
|
|
518
|
-
chartSvg
|
|
519
|
-
.append("defs")
|
|
520
|
-
.append("marker")
|
|
521
|
-
.attr("id", "xl-arrow")
|
|
522
|
-
.attr("viewBox", "0 0 10 10")
|
|
523
|
-
.attr("refX", 9)
|
|
524
|
-
.attr("refY", 5)
|
|
525
|
-
.attr("markerWidth", 6)
|
|
526
|
-
.attr("markerHeight", 6)
|
|
527
|
-
.attr("orient", "auto")
|
|
528
|
-
.append("path")
|
|
529
|
-
.attr("d", "M0,1 L10,5 L0,9 z");
|
|
530
|
+
chartSvg.append("g").attr("class", "xwires");
|
|
530
531
|
chartSvg.append("g").attr("class", "rows");
|
|
531
|
-
// over the rows: their hover targets would otherwise swallow the link strips
|
|
532
|
-
chartSvg.append("g").attr("class", "xlinks");
|
|
533
532
|
chartSvg.append("line").attr("class", "pin").attr("stroke", css("--accent")).attr("stroke-width", 1.5).attr("y1", 0).attr("y2", H);
|
|
534
533
|
chartSvg.append("line").attr("class", "cursor").attr("y1", 0).attr("y2", H).attr("visibility", "hidden");
|
|
535
534
|
chartSvg.call(chartZoom).call(chartZoom.transform, state.zoom);
|
|
@@ -645,74 +644,15 @@
|
|
|
645
644
|
g.select("g.marks").each(function (n) {
|
|
646
645
|
drawMarks(d3.select(this), n, x);
|
|
647
646
|
});
|
|
648
|
-
|
|
647
|
+
rowIndex = index;
|
|
648
|
+
drawWires(x);
|
|
649
649
|
|
|
650
650
|
chartSvg.select("line.pin").attr("x1", state.pin != null ? x(state.pin) : -10).attr("x2", state.pin != null ? x(state.pin) : -10);
|
|
651
651
|
if (app.classList.contains("hl-on") || state.errHl) applyHl();
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
for (let p = n; p; p = p.parent) if (index.has(p.id)) return index.get(p.id);
|
|
657
|
-
return -1;
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Cross-node links on the timeline. A strip under the caller's bar holds its waits
|
|
661
|
-
* (`once`, dashed) and its calls on another action's ref (solid), in the target's colour;
|
|
662
|
-
* a dashed connector joins the two rows where a wait ended (target → waiter) and where a
|
|
663
|
-
* cancel landed (canceller → target).
|
|
664
|
-
*/
|
|
665
|
-
function drawLinks(x, index) {
|
|
666
|
-
const STRIP_Y = BAR_Y + BAR_H + 3;
|
|
667
|
-
const spans = [];
|
|
668
|
-
const wires = [];
|
|
669
|
-
for (const l of model.links) {
|
|
670
|
-
const fi = rowIndexOf(l.from, index);
|
|
671
|
-
const ti = rowIndexOf(l.to, index);
|
|
672
|
-
if (fi < 0) continue;
|
|
673
|
-
if (l.op !== "cancel") spans.push({ l, fi });
|
|
674
|
-
if (l.op !== "ref" && ti >= 0 && ti !== fi) wires.push({ l, fi, ti });
|
|
675
|
-
}
|
|
676
|
-
const layer = chartSvg.select(".xlinks");
|
|
677
|
-
layer
|
|
678
|
-
.selectAll("line.xl-span")
|
|
679
|
-
.data(spans)
|
|
680
|
-
.join("line")
|
|
681
|
-
.attr("class", (d) => `xl-span ${d.l.op}`)
|
|
682
|
-
.attr("x1", (d) => x(d.l.t0))
|
|
683
|
-
.attr("x2", (d) => Math.max(x(d.l.t), x(d.l.t0) + 2))
|
|
684
|
-
.attr("y1", (d) => d.fi * ROW_H + STRIP_Y)
|
|
685
|
-
.attr("y2", (d) => d.fi * ROW_H + STRIP_Y)
|
|
686
|
-
.attr("stroke", (d) => colorOf(d.l.to))
|
|
687
|
-
.call(bindLink);
|
|
688
|
-
const wirePath = (d) => {
|
|
689
|
-
const px = x(d.l.t);
|
|
690
|
-
const fy = d.fi * ROW_H + STRIP_Y;
|
|
691
|
-
const ty = d.ti * ROW_H + BAR_Y + BAR_H / 2;
|
|
692
|
-
const [y1, y2] = d.l.op === "once" ? [ty, fy] : [fy, ty];
|
|
693
|
-
return `M${px},${y1} C${px + 10},${(2 * y1 + y2) / 3} ${px + 10},${(y1 + 2 * y2) / 3} ${px},${y2}`;
|
|
694
|
-
};
|
|
695
|
-
layer
|
|
696
|
-
.selectAll("path.xl-wire")
|
|
697
|
-
.data(wires)
|
|
698
|
-
.join("path")
|
|
699
|
-
.attr("class", (d) => `xl-wire ${d.l.op}`)
|
|
700
|
-
.attr("d", wirePath)
|
|
701
|
-
.attr("stroke", (d) => (d.l.op === "cancel" ? css("--cancel") : colorOf(d.l.to)))
|
|
702
|
-
.attr("marker-end", "url(#xl-arrow)");
|
|
703
|
-
// a wide transparent twin over each wire, so a 1px dashed line is easy to hover
|
|
704
|
-
layer.selectAll("path.xl-hit").data(wires).join("path").attr("class", "xl-hit").attr("d", wirePath).call(bindLink);
|
|
705
|
-
}
|
|
706
|
-
function bindLink(sel) {
|
|
707
|
-
sel
|
|
708
|
-
.on("click", (ev, d) => {
|
|
709
|
-
ev.stopPropagation();
|
|
710
|
-
setPin(d.l.t);
|
|
711
|
-
select(d.l.from.id, "links");
|
|
712
|
-
})
|
|
713
|
-
.on("mouseenter", (ev, d) => showTip(ev, linkTip(d.l), true))
|
|
714
|
-
.on("mousemove", (ev) => moveTip(ev))
|
|
715
|
-
.on("mouseleave", hideTip);
|
|
654
|
+
function linkColor(l) {
|
|
655
|
+
return l.op === "cancel" ? css("--cancel") : colorOf(l.to);
|
|
716
656
|
}
|
|
717
657
|
/** A link in words: who did what to whom, how it ended, and the line that did it. */
|
|
718
658
|
function linkTip(l, count) {
|
|
@@ -737,6 +677,40 @@
|
|
|
737
677
|
${l.site ? `<div class="mono small" style="margin-top:6px">${esc(relOf(l.site))}${line ? ":" + line : ""}</div>` : ""}`;
|
|
738
678
|
}
|
|
739
679
|
|
|
680
|
+
let rowIndex = new Map();
|
|
681
|
+
/** The visible row standing in for `n`: its own, or its nearest expanded ancestor's. */
|
|
682
|
+
function rowOf(n) {
|
|
683
|
+
for (let p = n; p; p = p.parent) if (rowIndex.has(p.id)) return rowIndex.get(p.id);
|
|
684
|
+
return -1;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* A link's wire: straight down (or up) the timeline at the moment it happened, from the
|
|
688
|
+
* caller's point to the target's row — both ends share the instant, so the line reads as
|
|
689
|
+
* "this, then, to that". Waits and cancels always have one; a ref call's shows on hover only,
|
|
690
|
+
* since a chatty handle would otherwise hatch the chart.
|
|
691
|
+
*/
|
|
692
|
+
function wireOf(l, x) {
|
|
693
|
+
const a = rowOf(l.from);
|
|
694
|
+
const b = rowOf(l.to);
|
|
695
|
+
if (a < 0 || b < 0 || a === b) return null;
|
|
696
|
+
const cy = BAR_Y + BAR_H / 2;
|
|
697
|
+
return { l, x: x(l.t), y1: a * ROW_H + cy, y2: b * ROW_H + cy };
|
|
698
|
+
}
|
|
699
|
+
function drawWires(x, extra) {
|
|
700
|
+
const shown = model.links.filter((l) => l.op !== "ref" || l === extra);
|
|
701
|
+
chartSvg
|
|
702
|
+
.select(".xwires")
|
|
703
|
+
.selectAll("line")
|
|
704
|
+
.data(shown.map((l) => wireOf(l, x)).filter(Boolean))
|
|
705
|
+
.join("line")
|
|
706
|
+
.attr("class", (w) => `xwire ${w.l.op}${w.l === extra ? " hot" : ""}`)
|
|
707
|
+
.attr("x1", (w) => w.x)
|
|
708
|
+
.attr("x2", (w) => w.x)
|
|
709
|
+
.attr("y1", (w) => w.y1)
|
|
710
|
+
.attr("y2", (w) => w.y2)
|
|
711
|
+
.attr("stroke", (w) => linkColor(w.l));
|
|
712
|
+
}
|
|
713
|
+
|
|
740
714
|
function drawMarks(g, n, x) {
|
|
741
715
|
const cy = BAR_Y + BAR_H / 2;
|
|
742
716
|
// cluster marks closer than 4px so a chatty node draws a readable line of dots
|
|
@@ -754,18 +728,27 @@
|
|
|
754
728
|
g.selectAll("circle.mark")
|
|
755
729
|
.data(marks)
|
|
756
730
|
.join("circle")
|
|
757
|
-
.attr("class",
|
|
731
|
+
.attr("class", (d) => `mark ${worst(d)}`)
|
|
758
732
|
.attr("cx", (d) => d.px)
|
|
759
733
|
.attr("cy", cy)
|
|
760
734
|
.attr("r", (d) => (d.more.length ? 6 : 5))
|
|
761
735
|
.attr("fill", (d) => css(MARK[worst(d)].color))
|
|
736
|
+
// a link is a ring in its target's colour — set as styles, over the `.mark` stroke rule
|
|
737
|
+
.style("fill", (d) => (worst(d) === "link" ? "var(--panel)" : null))
|
|
738
|
+
.style("stroke", (d) => (worst(d) === "link" ? linkColor(d.m.link) : null))
|
|
762
739
|
.on("click", (ev, d) => {
|
|
763
740
|
ev.stopPropagation();
|
|
764
741
|
openItem(d.m);
|
|
765
742
|
})
|
|
766
|
-
.on("mouseenter", (ev, d) =>
|
|
743
|
+
.on("mouseenter", (ev, d) => {
|
|
744
|
+
showTip(ev, markTip(d), d.m.kind === "link");
|
|
745
|
+
if (d.m.kind === "link") drawWires(xz(), d.m.link);
|
|
746
|
+
})
|
|
767
747
|
.on("mousemove", (ev) => moveTip(ev))
|
|
768
|
-
.on("mouseleave",
|
|
748
|
+
.on("mouseleave", (ev, d) => {
|
|
749
|
+
hideTip();
|
|
750
|
+
if (d.m.kind === "link") drawWires(xz());
|
|
751
|
+
});
|
|
769
752
|
// labels under the bar, skipped where they would collide
|
|
770
753
|
const labels = [];
|
|
771
754
|
let edge = -Infinity;
|
|
@@ -852,6 +835,18 @@
|
|
|
852
835
|
.attr("stroke", color ? alpha(color, 0.45) : css("--line"));
|
|
853
836
|
return { g, w };
|
|
854
837
|
}
|
|
838
|
+
/** A link chip's text: what kind of interaction, at a glance. */
|
|
839
|
+
function linkGlyph(x2) {
|
|
840
|
+
if (x2.l.op === "once") return "wait";
|
|
841
|
+
if (x2.l.op === "cancel") return "stop";
|
|
842
|
+
return x2.n > 1 ? `()×${x2.n}` : "()";
|
|
843
|
+
}
|
|
844
|
+
/** "3 snapshots", "2 links", or "4 items" for a mix. */
|
|
845
|
+
function chipCount(shots) {
|
|
846
|
+
const links = shots.filter((x2) => x2.type === "link").length;
|
|
847
|
+
if (!links) return `${shots.length} snapshots`;
|
|
848
|
+
return links === shots.length ? `${links} links` : `${shots.length} items`;
|
|
849
|
+
}
|
|
855
850
|
function renderFlow() {
|
|
856
851
|
d3.select(el.flow).selectAll("svg").remove();
|
|
857
852
|
const svg = d3.select(el.flow).append("svg");
|
|
@@ -879,6 +874,16 @@
|
|
|
879
874
|
if (!shotsBy.has(g.node)) shotsBy.set(g.node, []);
|
|
880
875
|
shotsBy.get(g.node).push(g);
|
|
881
876
|
}
|
|
877
|
+
// a node's links sit with its snapshots, in time order; a run of identical ref calls is one chip
|
|
878
|
+
for (const l of model.links) {
|
|
879
|
+
const list = shotsBy.get(l.from) || shotsBy.set(l.from, []).get(l.from);
|
|
880
|
+
const prev = list[list.length - 1];
|
|
881
|
+
if (l.op === "ref" && prev && prev.type === "link" && prev.l.op === "ref" && prev.l.to === l.to && prev.l.key === l.key) {
|
|
882
|
+
prev.n++;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
list.push({ type: "link", l, n: 1, t: l.t, name: l.label });
|
|
886
|
+
}
|
|
882
887
|
const byTime = (a, b) => (a.t ?? Infinity) - (b.t ?? Infinity);
|
|
883
888
|
const pos = new Map();
|
|
884
889
|
const lanes = [];
|
|
@@ -965,70 +970,6 @@
|
|
|
965
970
|
}
|
|
966
971
|
|
|
967
972
|
const nodes = [...pos.keys()].map((id) => model.byId.get(id));
|
|
968
|
-
// cross-node links: one curve per (caller, target, kind), arcing out past the cards' right
|
|
969
|
-
// edges; drawn first so the cards sit on top of their ends
|
|
970
|
-
const agg = new Map();
|
|
971
|
-
for (const l of model.links) {
|
|
972
|
-
if (!pos.has(l.from.id) || !pos.has(l.to.id) || l.from === l.to) continue;
|
|
973
|
-
const k = `${l.from.id}\0${l.to.id}\0${l.op}`;
|
|
974
|
-
if (!agg.has(k)) agg.set(k, { op: l.op, from: l.from, to: l.to, list: [] });
|
|
975
|
-
agg.get(k).list.push(l);
|
|
976
|
-
}
|
|
977
|
-
const xl = flowG.append("g").attr("class", "flow-xlinks");
|
|
978
|
-
const fan = new Map(); // edges between the same two cards fan out instead of overlapping
|
|
979
|
-
for (const a of agg.values()) {
|
|
980
|
-
const f = pos.get(a.from.id);
|
|
981
|
-
const t = pos.get(a.to.id);
|
|
982
|
-
const [s, e] = a.op === "once" ? [t, f] : [f, t];
|
|
983
|
-
const pair = [a.from.id, a.to.id].sort().join("");
|
|
984
|
-
const k = fan.get(pair) || 0;
|
|
985
|
-
fan.set(pair, k + 1);
|
|
986
|
-
const off = (k % 2 ? 1 : -1) * Math.ceil(k / 2) * 10;
|
|
987
|
-
let x1, y1, x2, y2, path, lx, ly;
|
|
988
|
-
if (Math.abs(s.x - e.x) < CW) {
|
|
989
|
-
// same column: arc out past the right edges
|
|
990
|
-
x1 = s.x + CW;
|
|
991
|
-
x2 = e.x + CW;
|
|
992
|
-
y1 = s.y + CH / 2 + off;
|
|
993
|
-
y2 = e.y + CH / 2 + off;
|
|
994
|
-
const bulge = 36 + Math.min(140, Math.abs(y2 - y1) * 0.2) + Math.abs(off);
|
|
995
|
-
path = `M${x1},${y1} C${x1 + bulge},${y1} ${x2 + bulge},${y2} ${x2 + 2},${y2}`;
|
|
996
|
-
lx = (x1 + x2) / 2 + bulge * 0.75 + 4;
|
|
997
|
-
ly = (y1 + y2) / 2 + 4;
|
|
998
|
-
} else {
|
|
999
|
-
// different columns: facing sides, an S-curve between them, below the tree's own links
|
|
1000
|
-
const rightward = e.x > s.x;
|
|
1001
|
-
x1 = rightward ? s.x + CW : s.x;
|
|
1002
|
-
x2 = rightward ? e.x : e.x + CW;
|
|
1003
|
-
y1 = s.y + CH * 0.72 + off;
|
|
1004
|
-
y2 = e.y + CH * 0.72 + off;
|
|
1005
|
-
const mx = (x1 + x2) / 2;
|
|
1006
|
-
path = `M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2 + (rightward ? -2 : 2)},${y2}`;
|
|
1007
|
-
lx = mx + 4;
|
|
1008
|
-
ly = (y1 + y2) / 2 - 4;
|
|
1009
|
-
}
|
|
1010
|
-
const keys = [...new Set(a.list.map((l) => l.key))];
|
|
1011
|
-
const label =
|
|
1012
|
-
a.op === "cancel" ? "cancel" : a.op === "once" ? `once ${keys.join(", ")}` : keys.length === 1 ? `${keys[0]}() ×${a.list.length}` : `ref ×${a.list.length}`;
|
|
1013
|
-
const g = xl
|
|
1014
|
-
.append("g")
|
|
1015
|
-
.attr("class", `flow-xlink ${a.op}`)
|
|
1016
|
-
.on("click", (ev) => {
|
|
1017
|
-
ev.stopPropagation();
|
|
1018
|
-
select(a.from.id, "links");
|
|
1019
|
-
})
|
|
1020
|
-
.on("mouseenter", (ev) => showTip(ev, linkTip(a.list[a.list.length - 1], a.list.length), true))
|
|
1021
|
-
.on("mousemove", (ev) => moveTip(ev))
|
|
1022
|
-
.on("mouseleave", hideTip);
|
|
1023
|
-
g.append("path")
|
|
1024
|
-
.attr("d", path)
|
|
1025
|
-
.attr("stroke", a.op === "cancel" ? css("--cancel") : colorOf(a.to))
|
|
1026
|
-
.attr("marker-end", "url(#flow-arrow)");
|
|
1027
|
-
g.append("path").attr("class", "hit").attr("d", path);
|
|
1028
|
-
// between neighbouring columns there is no room for words — the tooltip carries them
|
|
1029
|
-
if (Math.abs(x2 - x1) >= 90) g.append("text").attr("x", lx).attr("y", ly).text(truncate(label, 28));
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
973
|
const card = flowG
|
|
1033
974
|
.append("g")
|
|
1034
975
|
.selectAll("g.flow-card")
|
|
@@ -1118,6 +1059,31 @@
|
|
|
1118
1059
|
|
|
1119
1060
|
// snapshot chips — on lane arrows, and in strips under leaf cards
|
|
1120
1061
|
const chipLayer = flowG.append("g").attr("class", "flow-shots");
|
|
1062
|
+
// A link chip's wire, drawn under everything else: out of the chip to a gutter right of the
|
|
1063
|
+
// column, along it, and into the target card's right edge — so it never runs behind the cards
|
|
1064
|
+
// between. Waits and cancels always have one; a ref call's shows while hovered.
|
|
1065
|
+
const wireLayer = flowG.insert("g", ":first-child").attr("class", "flow-wires");
|
|
1066
|
+
const linkChips = [];
|
|
1067
|
+
const drawFlowWires = (hot) => {
|
|
1068
|
+
const wires = [];
|
|
1069
|
+
for (const c of linkChips) {
|
|
1070
|
+
const p = pos.get(c.x2.l.to.id);
|
|
1071
|
+
if (!p || (c.x2.l.op === "ref" && c.x2 !== hot)) continue;
|
|
1072
|
+
wires.push({ c, p });
|
|
1073
|
+
}
|
|
1074
|
+
wireLayer
|
|
1075
|
+
.selectAll("path")
|
|
1076
|
+
.data(wires)
|
|
1077
|
+
.join("path")
|
|
1078
|
+
.attr("class", (w) => `fwire ${w.c.x2.l.op}${w.c.x2 === hot ? " hot" : ""}`)
|
|
1079
|
+
.attr("d", (w, i) => {
|
|
1080
|
+
const tx = w.p.x + CW;
|
|
1081
|
+
const ty = w.p.y + CH / 2;
|
|
1082
|
+
const gx = Math.max(tx, w.c.cx + TW / 2) + 12 + (i % 6) * 6; // staggered within the column gap
|
|
1083
|
+
return `M${w.c.cx + TW / 2},${w.c.cy} H${gx} V${ty} H${tx}`;
|
|
1084
|
+
})
|
|
1085
|
+
.attr("stroke", (w) => linkColor(w.c.x2.l));
|
|
1086
|
+
};
|
|
1121
1087
|
const drawChips = (x, y, shots, max, owner) => {
|
|
1122
1088
|
const shown = shots.length > max ? shots.slice(0, max - 1) : shots;
|
|
1123
1089
|
const g = chipLayer.append("g").attr("transform", `translate(${x},${y})`);
|
|
@@ -1129,12 +1095,28 @@
|
|
|
1129
1095
|
.attr("transform", (_x, i) => `translate(${i * (TW + 4)},0)`)
|
|
1130
1096
|
.on("click", (ev, x2) => {
|
|
1131
1097
|
ev.stopPropagation();
|
|
1132
|
-
openGallery(x2);
|
|
1098
|
+
if (x2.type !== "link") return openGallery(x2);
|
|
1099
|
+
setPin(x2.t);
|
|
1100
|
+
select(x2.l.from.id, "links");
|
|
1101
|
+
})
|
|
1102
|
+
.on("mouseenter", (ev, x2) => {
|
|
1103
|
+
showTip(ev, x2.type === "link" ? linkTip(x2.l, x2.n) : galleryTip(x2), x2.type === "link");
|
|
1104
|
+
if (x2.type === "link") drawFlowWires(x2);
|
|
1133
1105
|
})
|
|
1134
|
-
.on("mouseenter", (ev, x2) => showTip(ev, galleryTip(x2)))
|
|
1135
1106
|
.on("mousemove", (ev) => moveTip(ev))
|
|
1136
|
-
.on("mouseleave",
|
|
1137
|
-
|
|
1107
|
+
.on("mouseleave", (ev, x2) => {
|
|
1108
|
+
hideTip();
|
|
1109
|
+
if (x2.type === "link") drawFlowWires(null);
|
|
1110
|
+
});
|
|
1111
|
+
shown.forEach((x2, i) => {
|
|
1112
|
+
if (x2.type === "link") linkChips.push({ x2, cx: x + i * (TW + 4) + TW / 2, cy: y + TH / 2 });
|
|
1113
|
+
});
|
|
1114
|
+
cell
|
|
1115
|
+
.append("rect")
|
|
1116
|
+
.attr("width", TW)
|
|
1117
|
+
.attr("height", TH)
|
|
1118
|
+
.attr("rx", 5)
|
|
1119
|
+
.attr("stroke", (x2) => (x2.type === "link" ? linkColor(x2.l) : null));
|
|
1138
1120
|
cell
|
|
1139
1121
|
.filter((x2) => x2.image)
|
|
1140
1122
|
.append("image")
|
|
@@ -1150,7 +1132,7 @@
|
|
|
1150
1132
|
.attr("x", TW / 2)
|
|
1151
1133
|
.attr("y", TH / 2 + 3.5)
|
|
1152
1134
|
.attr("text-anchor", "middle")
|
|
1153
|
-
.text((x2) => (x2.type === "snapshot" ? "{ }" : "file"));
|
|
1135
|
+
.text((x2) => (x2.type === "link" ? linkGlyph(x2) : x2.type === "snapshot" ? "{ }" : "file"));
|
|
1154
1136
|
let end = shown.length * (TW + 4);
|
|
1155
1137
|
if (shots.length > shown.length) {
|
|
1156
1138
|
const more = g
|
|
@@ -1176,7 +1158,7 @@
|
|
|
1176
1158
|
.attr("class", "flow-shot-t")
|
|
1177
1159
|
.attr("x", end + 2)
|
|
1178
1160
|
.attr("y", TH / 2 + 4)
|
|
1179
|
-
.text(`${fmtRel(t)} · ${shots.length > 1 ?
|
|
1161
|
+
.text(`${fmtRel(t)} · ${shots.length > 1 ? chipCount(shots) : truncate(shots[0].name, 22)}`);
|
|
1180
1162
|
}
|
|
1181
1163
|
};
|
|
1182
1164
|
for (const r of chipRows) {
|
|
@@ -1184,6 +1166,7 @@
|
|
|
1184
1166
|
drawChips(r.x, r.y, r.shots, 6, owner);
|
|
1185
1167
|
}
|
|
1186
1168
|
for (const s of strips) drawChips(s.x, s.y, s.shots, Math.floor((CW - 12) / (TW + 4)), s.n);
|
|
1169
|
+
drawFlowWires(null);
|
|
1187
1170
|
|
|
1188
1171
|
flowPos = new Map([...pos].map(([id, p]) => [id, { x: p.x + CW / 2, y: p.y + CH / 2 }]));
|
|
1189
1172
|
svg.call(flowZoom).on("dblclick.zoom", null);
|
|
@@ -1446,7 +1429,7 @@
|
|
|
1446
1429
|
title: "Snapshots & files",
|
|
1447
1430
|
count: shots.length,
|
|
1448
1431
|
empty: !shots.length,
|
|
1449
|
-
body: () => `<div class="
|
|
1432
|
+
body: () => `<div class="dshots">${shots.map((g) => shotHtml(g, n)).join("")}</div>`,
|
|
1450
1433
|
},
|
|
1451
1434
|
{
|
|
1452
1435
|
key: "events",
|
|
@@ -1527,7 +1510,7 @@
|
|
|
1527
1510
|
/** A snapshot or file in the details panel: a small thumb with its name and when it landed. */
|
|
1528
1511
|
function shotHtml(g, n) {
|
|
1529
1512
|
const i = model.gallery.indexOf(g);
|
|
1530
|
-
return `<div class="
|
|
1513
|
+
return `<div class="dshot" data-gallery="${i}" title="${esc(g.name)}">
|
|
1531
1514
|
${thumbHtml(g)}
|
|
1532
1515
|
<span class="nm">${esc(g.name)}</span>
|
|
1533
1516
|
${g.t != null ? `<span class="t">${fmtRel(g.t - n.start)}</span>` : ""}
|
|
@@ -1569,7 +1552,8 @@
|
|
|
1569
1552
|
function openItem(item) {
|
|
1570
1553
|
if (!item) return;
|
|
1571
1554
|
setPin(item.t);
|
|
1572
|
-
if (item.
|
|
1555
|
+
if (item.kind === "link") select(item.node.id, "links");
|
|
1556
|
+
else if (item.node && item.node.id !== state.selected) select(item.node.id, "events");
|
|
1573
1557
|
if (item.gallery) openGallery(item.gallery);
|
|
1574
1558
|
}
|
|
1575
1559
|
function setPin(t) {
|
|
@@ -1929,6 +1913,9 @@
|
|
|
1929
1913
|
}
|
|
1930
1914
|
function markTip(d) {
|
|
1931
1915
|
const m = d.m;
|
|
1916
|
+
if (m.kind === "link") {
|
|
1917
|
+
return linkTip(m.link) + (d.more.length ? `<div class="ts" style="margin-top:4px">+${d.more.length} more within a few ms</div>` : "");
|
|
1918
|
+
}
|
|
1932
1919
|
const mk = MARK[m.kind] || MARK.event;
|
|
1933
1920
|
const img = m.gallery && m.gallery.image ? `<img src="${model.asset(m.gallery.f.asset)}" alt="">` : "";
|
|
1934
1921
|
const more = d.more.length ? `<div class="ts">+${d.more.length} more within a few ms</div>` : "";
|
|
@@ -2321,8 +2308,7 @@
|
|
|
2321
2308
|
${legendItem("--fail", "Failure / error", "dot")}
|
|
2322
2309
|
${legendItem("--cancel", "Cancelled", "dash")}
|
|
2323
2310
|
${legendItem("--accent", "Pinned time", "line")}
|
|
2324
|
-
${legendItem("--text-2", "
|
|
2325
|
-
${legendItem("--text-2", "Call on a ref", "tick")}
|
|
2311
|
+
${legendItem("--text-2", "Wait · cancel · ref call", "ring")}
|
|
2326
2312
|
</div>
|
|
2327
2313
|
<div class="muted" style="margin-top:12px;font-size:11px">Ctrl + wheel to zoom · drag to pan · double-click a bar to focus · ↑/↓ to step</div>
|
|
2328
2314
|
</div>
|
|
@@ -2339,8 +2325,8 @@
|
|
|
2339
2325
|
const sw =
|
|
2340
2326
|
shape === "bar"
|
|
2341
2327
|
? `<span style="width:16px;height:9px;border-radius:3px;background:${c}"></span>`
|
|
2342
|
-
: shape === "
|
|
2343
|
-
? `<span style="width:
|
|
2328
|
+
: shape === "ring"
|
|
2329
|
+
? `<span class="dot" style="width:11px;height:11px;background:var(--panel);box-shadow:inset 0 0 0 2px ${c}"></span>`
|
|
2344
2330
|
: shape === "dash"
|
|
2345
2331
|
? `<span style="width:16px;height:0;border-top:2px dashed ${c}"></span>`
|
|
2346
2332
|
: shape === "line"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omkit",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.19",
|
|
4
4
|
"description": "A tiny runtime for developer workflows: typed actions that pass runtime capabilities and emit typed events, under one run and one shared log. The operational/runtime layer to tskb's knowledge layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|