thurview 0.17.1 → 0.18.0
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 +43 -43
- package/dist/cli.js +23 -222
- package/dist/cli.js.map +1 -1
- package/dist/coverage.js +53 -148
- package/dist/coverage.js.map +1 -1
- package/dist/document/compile.js +51 -43
- package/dist/document/compile.js.map +1 -1
- package/dist/document/schema.js +25 -13
- package/dist/document/schema.js.map +1 -1
- package/dist/git.js +29 -0
- package/dist/git.js.map +1 -1
- package/dist/interfaces.js +7 -244
- package/dist/interfaces.js.map +1 -1
- package/dist/ui/app.css +29 -10
- package/dist/ui/app.js +169 -192
- package/dist/ui/app.js.map +3 -3
- package/package.json +1 -12
- package/skills/thurview/SKILL.md +32 -34
- package/skills/thurview/references/components.md +45 -22
- package/skills/thurview/references/document-authoring.md +22 -26
- package/skills/thurview/references/searching.md +74 -0
- package/skills/thurview-design/SKILL.md +17 -14
- package/skills/thurview-design/references/anchors-and-proposals.md +11 -9
- package/skills/thurview-explain/SKILL.md +44 -31
- package/skills/thurview-fix/SKILL.md +47 -42
- package/dist/graph.js +0 -568
- package/dist/graph.js.map +0 -1
package/dist/ui/app.js
CHANGED
|
@@ -980,13 +980,48 @@
|
|
|
980
980
|
if (a) openAnchorPeek(a);
|
|
981
981
|
}
|
|
982
982
|
function sequenceDiagram(b) {
|
|
983
|
-
const
|
|
984
|
-
const
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
983
|
+
const charW = 12 * 0.605;
|
|
984
|
+
const lineH = 15;
|
|
985
|
+
const wrap = (text, room) => {
|
|
986
|
+
const max = Math.max(8, Math.floor(room / charW));
|
|
987
|
+
const lines = [];
|
|
988
|
+
for (const word of text.split(" ")) {
|
|
989
|
+
const last = lines.length - 1;
|
|
990
|
+
if (last >= 0 && lines[last].length + 1 + word.length <= max) lines[last] += ` ${word}`;
|
|
991
|
+
else lines.push(word);
|
|
992
|
+
}
|
|
993
|
+
return lines;
|
|
994
|
+
};
|
|
995
|
+
const widest = (lines) => Math.max(...lines.map((l) => l.length)) * charW;
|
|
996
|
+
const names = b.actors.map((a) => wrap(a.label, 20 * charW));
|
|
997
|
+
const nameLines = Math.max(...names.map((l) => l.length));
|
|
998
|
+
const boxH2 = 13 + nameLines * lineH;
|
|
999
|
+
const top = 6 + boxH2 + 10;
|
|
1000
|
+
const boxes = names.map((l) => Math.max(120, widest(l) + 20));
|
|
1001
|
+
const cols = boxes.map((w) => Math.max(150, w + 16));
|
|
1002
|
+
const centres = cols.map((w, i) => cols.slice(0, i).reduce((s, c) => s + c, 0) + w / 2);
|
|
1003
|
+
const x = (id) => centres[b.actors.findIndex((a) => a.id === id)];
|
|
1004
|
+
let y = top;
|
|
1005
|
+
const rows = b.messages.map((m2, i) => {
|
|
1006
|
+
const x1 = x(m2.from);
|
|
1007
|
+
const x2 = x(m2.to);
|
|
1008
|
+
const self = x1 === x2;
|
|
1009
|
+
const start = self ? x1 + 36 : Math.min(x1, x2) + 8;
|
|
1010
|
+
const lines = wrap(`${i + 1}. ${m2.label}`, Math.max(180, Math.abs(x2 - x1) - 16));
|
|
1011
|
+
y += 20 + lines.length * lineH;
|
|
1012
|
+
return { x1, x2, self, start, lines, y, end: start + widest(lines) };
|
|
1013
|
+
});
|
|
1014
|
+
const width = Math.ceil(
|
|
1015
|
+
Math.max(
|
|
1016
|
+
320,
|
|
1017
|
+
cols.reduce((s, c) => s + c, 0),
|
|
1018
|
+
...rows.map((r) => r.end + 12)
|
|
1019
|
+
)
|
|
1020
|
+
);
|
|
1021
|
+
const height = y + 24;
|
|
989
1022
|
const el = svg("svg", { viewBox: `0 0 ${width} ${height}`, class: "seq" });
|
|
1023
|
+
el.style.maxWidth = `${width}px`;
|
|
1024
|
+
el.style.minWidth = `${Math.ceil(width * 0.9)}px`;
|
|
990
1025
|
el.appendChild(
|
|
991
1026
|
svg(
|
|
992
1027
|
"defs",
|
|
@@ -1006,39 +1041,35 @@
|
|
|
1006
1041
|
)
|
|
1007
1042
|
)
|
|
1008
1043
|
);
|
|
1009
|
-
const
|
|
1010
|
-
b.actors.forEach((
|
|
1011
|
-
const cx = i
|
|
1044
|
+
const tspans = (at, baseline, text) => text.map((l, i) => svg("tspan", { x: at, y: baseline - (text.length - 1 - i) * lineH }, l));
|
|
1045
|
+
b.actors.forEach((_, i) => {
|
|
1046
|
+
const cx = centres[i];
|
|
1047
|
+
const w = boxes[i];
|
|
1048
|
+
const name = names[i];
|
|
1012
1049
|
const g = svg("g", { class: "actor" });
|
|
1013
|
-
g.appendChild(svg("rect", { x: cx -
|
|
1014
|
-
|
|
1015
|
-
g.appendChild(svg("
|
|
1050
|
+
g.appendChild(svg("rect", { x: cx - w / 2, y: 6, width: w, height: boxH2, rx: 6 }));
|
|
1051
|
+
const baseline = 6 + (boxH2 + name.length * lineH) / 2 - 4;
|
|
1052
|
+
g.appendChild(svg("text", { "text-anchor": "middle" }, ...tspans(cx, baseline, name)));
|
|
1053
|
+
g.appendChild(svg("line", { class: "life", x1: cx, y1: 6 + boxH2, x2: cx, y2: height - 6 }));
|
|
1016
1054
|
el.appendChild(g);
|
|
1017
1055
|
});
|
|
1018
1056
|
b.messages.forEach((m2, i) => {
|
|
1019
|
-
const
|
|
1020
|
-
const x1 = x(m2.from);
|
|
1021
|
-
const x2 = x(m2.to);
|
|
1057
|
+
const { x1, x2, self, start, y: y2 } = rows[i];
|
|
1022
1058
|
const g = svg("g", { class: "msg" });
|
|
1023
|
-
const self = x1 === x2;
|
|
1024
1059
|
if (self) {
|
|
1025
1060
|
g.appendChild(
|
|
1026
1061
|
svg("path", {
|
|
1027
|
-
d: `M${x1},${
|
|
1062
|
+
d: `M${x1},${y2 - 10} h30 v20 h-30`,
|
|
1028
1063
|
fill: "none",
|
|
1029
1064
|
stroke: "currentColor",
|
|
1030
1065
|
"marker-end": "url(#arr)"
|
|
1031
1066
|
})
|
|
1032
1067
|
);
|
|
1033
1068
|
} else {
|
|
1034
|
-
g.appendChild(svg("line", { x1, y1:
|
|
1069
|
+
g.appendChild(svg("line", { x1, y1: y2, x2: x2 + (x2 > x1 ? -4 : 4), y2 }));
|
|
1035
1070
|
}
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
{ x: self ? x1 + 36 : (x1 + x2) / 2, y: y - 6, "text-anchor": self ? "start" : "middle" },
|
|
1039
|
-
`${i + 1}. ${m2.label}`
|
|
1040
|
-
);
|
|
1041
|
-
label2.addEventListener("click", (e) => {
|
|
1071
|
+
const label = svg("text", {}, ...tspans(start, y2 - 6, rows[i].lines));
|
|
1072
|
+
const open = (at) => {
|
|
1042
1073
|
if (m2.anchor) openAnchor(m2.anchor);
|
|
1043
1074
|
else if (m2.code)
|
|
1044
1075
|
popover(
|
|
@@ -1047,13 +1078,28 @@
|
|
|
1047
1078
|
{ class: "def-popover" },
|
|
1048
1079
|
h("pre", { style: { margin: "0", border: "none" } }, m2.code.text)
|
|
1049
1080
|
),
|
|
1050
|
-
|
|
1081
|
+
at
|
|
1051
1082
|
);
|
|
1083
|
+
};
|
|
1084
|
+
label.addEventListener("click", (e) => open({ x: e.pageX, y: e.pageY + 8 }));
|
|
1085
|
+
label.setAttribute("role", "button");
|
|
1086
|
+
label.setAttribute("tabindex", "0");
|
|
1087
|
+
label.addEventListener("keydown", (e) => {
|
|
1088
|
+
const k = e.key;
|
|
1089
|
+
if (k !== "Enter" && k !== " ") return;
|
|
1090
|
+
e.preventDefault();
|
|
1091
|
+
const r = label.getBoundingClientRect();
|
|
1092
|
+
open({ x: r.left + window.scrollX, y: r.bottom + window.scrollY + 8 });
|
|
1052
1093
|
});
|
|
1053
|
-
g.appendChild(
|
|
1094
|
+
g.appendChild(label);
|
|
1054
1095
|
el.appendChild(g);
|
|
1055
1096
|
});
|
|
1056
|
-
return h(
|
|
1097
|
+
return h(
|
|
1098
|
+
"div",
|
|
1099
|
+
{ class: "diagram" },
|
|
1100
|
+
h("div", { class: "dhead" }, b.label),
|
|
1101
|
+
h("div", { class: "seq-scroll" }, el)
|
|
1102
|
+
);
|
|
1057
1103
|
}
|
|
1058
1104
|
function callstackDiff(b, doc) {
|
|
1059
1105
|
const rows = b.rows.map((r, i) => {
|
|
@@ -1209,11 +1255,11 @@
|
|
|
1209
1255
|
)
|
|
1210
1256
|
)
|
|
1211
1257
|
);
|
|
1212
|
-
for (const { edge, d, back, label
|
|
1258
|
+
for (const { edge, d, back, label } of edges) {
|
|
1213
1259
|
const g = svg("g", { class: back ? "edge back" : "edge" });
|
|
1214
1260
|
g.appendChild(svg("path", { d, fill: "none", "marker-end": "url(#flow-arr)" }));
|
|
1215
1261
|
if (edge.case)
|
|
1216
|
-
g.appendChild(svg("text", { x:
|
|
1262
|
+
g.appendChild(svg("text", { x: label.x, y: label.y, "text-anchor": label.at }, edge.case));
|
|
1217
1263
|
el.appendChild(g);
|
|
1218
1264
|
}
|
|
1219
1265
|
for (const s of b.steps) {
|
|
@@ -1408,7 +1454,7 @@
|
|
|
1408
1454
|
h(
|
|
1409
1455
|
"p",
|
|
1410
1456
|
{ class: "ifd-verdict" },
|
|
1411
|
-
delta ? delta.verdict : design ? "Unavailable: this revision declared no proposal." : "Unavailable:
|
|
1457
|
+
delta ? delta.verdict : design ? "Unavailable: this revision declared no proposal." : "Unavailable: this revision recorded no interface delta."
|
|
1412
1458
|
)
|
|
1413
1459
|
);
|
|
1414
1460
|
const entries = delta?.entries ?? [];
|
|
@@ -1913,7 +1959,7 @@
|
|
|
1913
1959
|
}
|
|
1914
1960
|
function gapRow(d, from, to, colspan, onExpand) {
|
|
1915
1961
|
if (to < from) return null;
|
|
1916
|
-
const
|
|
1962
|
+
const count2 = to - from + 1;
|
|
1917
1963
|
return h(
|
|
1918
1964
|
"tr",
|
|
1919
1965
|
{ class: "gap" },
|
|
@@ -1933,7 +1979,7 @@
|
|
|
1933
1979
|
);
|
|
1934
1980
|
}
|
|
1935
1981
|
},
|
|
1936
|
-
`\u22EF ${
|
|
1982
|
+
`\u22EF ${count2} unchanged line${count2 > 1 ? "s" : ""}`
|
|
1937
1983
|
)
|
|
1938
1984
|
);
|
|
1939
1985
|
}
|
|
@@ -2055,9 +2101,9 @@
|
|
|
2055
2101
|
let prevEnd = 0;
|
|
2056
2102
|
const gapBoth = (from, to) => {
|
|
2057
2103
|
if (to < from) return;
|
|
2058
|
-
const
|
|
2059
|
-
const lg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${
|
|
2060
|
-
const rg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${
|
|
2104
|
+
const count2 = to - from + 1;
|
|
2105
|
+
const lg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${count2} unchanged`));
|
|
2106
|
+
const rg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${count2} unchanged`));
|
|
2061
2107
|
const expand = async () => {
|
|
2062
2108
|
const f = await api.file(state.id, d.path, "head", from, to);
|
|
2063
2109
|
const off = gapOffset(d, from) ?? 0;
|
|
@@ -2430,7 +2476,7 @@
|
|
|
2430
2476
|
group(design ? "Context \u2014 untouched by the design" : "Context \u2014 unchanged here", cold)
|
|
2431
2477
|
);
|
|
2432
2478
|
};
|
|
2433
|
-
const
|
|
2479
|
+
const links = () => {
|
|
2434
2480
|
const here = new Set(childrenOf(all, level, map.filesByNode).map((k) => k.id));
|
|
2435
2481
|
const top = (id) => {
|
|
2436
2482
|
let x = id;
|
|
@@ -2478,7 +2524,7 @@
|
|
|
2478
2524
|
};
|
|
2479
2525
|
const draw = () => {
|
|
2480
2526
|
canvas.innerHTML = "";
|
|
2481
|
-
append(canvas, [intro2(), crumbs(), board(),
|
|
2527
|
+
append(canvas, [intro2(), crumbs(), board(), links()]);
|
|
2482
2528
|
drawSide();
|
|
2483
2529
|
};
|
|
2484
2530
|
const drawSide = () => {
|
|
@@ -2598,6 +2644,12 @@
|
|
|
2598
2644
|
// src/ui/views/coverage.ts
|
|
2599
2645
|
init_dom();
|
|
2600
2646
|
var SHOWN = 8;
|
|
2647
|
+
var LABELS = [
|
|
2648
|
+
["explained", "anchored in the document"],
|
|
2649
|
+
["placed", "placed on the map only"],
|
|
2650
|
+
["searched", "matched by a recorded search only"],
|
|
2651
|
+
["uncovered", "not examined"]
|
|
2652
|
+
];
|
|
2601
2653
|
function renderCoverage(root) {
|
|
2602
2654
|
const cov = state.data?.coverage;
|
|
2603
2655
|
if (!cov) {
|
|
@@ -2611,29 +2663,23 @@
|
|
|
2611
2663
|
]);
|
|
2612
2664
|
return;
|
|
2613
2665
|
}
|
|
2614
|
-
append(root, [intro(cov),
|
|
2666
|
+
append(root, [intro(cov), searches(cov), clusters(cov), owners(cov)]);
|
|
2615
2667
|
}
|
|
2668
|
+
var count = (cov, s) => cov.states[s] ?? 0;
|
|
2616
2669
|
function bar(cov) {
|
|
2617
2670
|
const total = Math.max(cov.files.total, 1);
|
|
2618
|
-
const seg = (n, cls,
|
|
2671
|
+
const seg = (n, cls, label) => n ? h("span", {
|
|
2619
2672
|
class: `cov-seg ${cls}`,
|
|
2620
2673
|
style: { width: `${n / total * 100}%` },
|
|
2621
|
-
title: `${n} ${
|
|
2674
|
+
title: `${n} ${label}`
|
|
2622
2675
|
}) : null;
|
|
2623
2676
|
return h(
|
|
2624
2677
|
"div",
|
|
2625
2678
|
{ class: "cov-bar" },
|
|
2626
|
-
seg(cov
|
|
2627
|
-
seg(cov.states.placed, "placed", "placed on the map only"),
|
|
2628
|
-
seg(cov.states.uncovered, "uncovered", "not examined")
|
|
2679
|
+
LABELS.map(([s, label]) => seg(count(cov, s), s, label))
|
|
2629
2680
|
);
|
|
2630
2681
|
}
|
|
2631
2682
|
function intro(cov) {
|
|
2632
|
-
const key = [
|
|
2633
|
-
[cov.states.explained, "explained", "anchored in the document"],
|
|
2634
|
-
[cov.states.placed, "placed", "placed on the map only"],
|
|
2635
|
-
[cov.states.uncovered, "uncovered", "not examined"]
|
|
2636
|
-
];
|
|
2637
2683
|
return h(
|
|
2638
2684
|
"div",
|
|
2639
2685
|
{ class: "cov-intro" },
|
|
@@ -2643,14 +2689,19 @@
|
|
|
2643
2689
|
null,
|
|
2644
2690
|
"A codebase does not fit in a short document, so this one selects. Here is the",
|
|
2645
2691
|
" selection, counted rather than claimed: every file in scope at the pinned commit,",
|
|
2646
|
-
" and which of the
|
|
2692
|
+
" and which of the four states it is in."
|
|
2647
2693
|
),
|
|
2648
2694
|
bar(cov),
|
|
2649
2695
|
h(
|
|
2650
2696
|
"div",
|
|
2651
2697
|
{ class: "cov-key" },
|
|
2652
|
-
|
|
2653
|
-
([
|
|
2698
|
+
LABELS.map(
|
|
2699
|
+
([s, label]) => h(
|
|
2700
|
+
"span",
|
|
2701
|
+
{ class: "cov-key-item" },
|
|
2702
|
+
h("i", { class: `cov-dot ${s}` }),
|
|
2703
|
+
`${count(cov, s)} ${label}`
|
|
2704
|
+
)
|
|
2654
2705
|
)
|
|
2655
2706
|
),
|
|
2656
2707
|
h(
|
|
@@ -2658,15 +2709,12 @@
|
|
|
2658
2709
|
{ class: "cov-facts" },
|
|
2659
2710
|
fact("scope", cov.scope === "**" ? "the whole repository" : cov.scope),
|
|
2660
2711
|
fact("commit", cov.commit.slice(0, 12)),
|
|
2661
|
-
fact("files in scope", String(cov.files.total))
|
|
2662
|
-
fact("read by the code graph", `${cov.files.inGraph} of ${cov.files.total}`),
|
|
2663
|
-
cov.unresolved ? fact("references the graph could not place", String(cov.unresolved)) : null,
|
|
2664
|
-
cov.truncated ? fact("file list capped", "the graph is partial; treat every count as a floor") : null
|
|
2712
|
+
fact("files in scope", String(cov.files.total))
|
|
2665
2713
|
)
|
|
2666
2714
|
);
|
|
2667
2715
|
}
|
|
2668
|
-
function fact(
|
|
2669
|
-
return h("div", { class: "cov-fact" }, h("b", null, value), h("span", { class: "muted" },
|
|
2716
|
+
function fact(label, value) {
|
|
2717
|
+
return h("div", { class: "cov-fact" }, h("b", null, value), h("span", { class: "muted" }, label));
|
|
2670
2718
|
}
|
|
2671
2719
|
function fileList(files, cls) {
|
|
2672
2720
|
if (!files.length) return null;
|
|
@@ -2687,9 +2735,49 @@
|
|
|
2687
2735
|
}
|
|
2688
2736
|
return wrap;
|
|
2689
2737
|
}
|
|
2738
|
+
function command(cov, s) {
|
|
2739
|
+
const quote = (v) => `'${v.replace(/'/g, `'\\''`)}'`;
|
|
2740
|
+
const paths = s.paths.map((p) => ` ${quote(p)}`).join("");
|
|
2741
|
+
return `git grep -I -n -E -e ${quote(s.pattern)} ${cov.commit.slice(0, 12)} --${paths}`;
|
|
2742
|
+
}
|
|
2743
|
+
function searches(cov) {
|
|
2744
|
+
const list = cov.searches ?? [];
|
|
2745
|
+
if (!list.length) return null;
|
|
2746
|
+
return h(
|
|
2747
|
+
"div",
|
|
2748
|
+
{ class: "cov-section" },
|
|
2749
|
+
h("h3", null, "What the agent searched"),
|
|
2750
|
+
h(
|
|
2751
|
+
"p",
|
|
2752
|
+
{ class: "muted" },
|
|
2753
|
+
"Each search the agent recorded, re-run at the pinned commit. A search that matched",
|
|
2754
|
+
" nothing is listed too: that zero is what a claim like \u201Cnothing else calls it\u201D rests on."
|
|
2755
|
+
),
|
|
2756
|
+
h(
|
|
2757
|
+
"div",
|
|
2758
|
+
{ class: "cov-links" },
|
|
2759
|
+
list.map(
|
|
2760
|
+
(s) => h(
|
|
2761
|
+
"div",
|
|
2762
|
+
{ class: "cov-link" },
|
|
2763
|
+
h("code", null, s.key),
|
|
2764
|
+
s.why ? h("span", null, s.why) : null,
|
|
2765
|
+
h(
|
|
2766
|
+
"span",
|
|
2767
|
+
{ class: "muted mono" },
|
|
2768
|
+
`${s.hits} line${s.hits === 1 ? "" : "s"} in ${s.files.length} file${s.files.length === 1 ? "" : "s"}`
|
|
2769
|
+
),
|
|
2770
|
+
h("code", { class: "muted" }, command(cov, s)),
|
|
2771
|
+
fileList(s.files, "searched")
|
|
2772
|
+
)
|
|
2773
|
+
)
|
|
2774
|
+
)
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2690
2777
|
function clusterRow(c) {
|
|
2691
2778
|
const anchors = state.data?.document?.anchors ?? {};
|
|
2692
2779
|
const firstAnchor = c.explained.length ? Object.values(anchors).find((a) => a.peek && c.explained.includes(a.peek.file)) : void 0;
|
|
2780
|
+
const badge = (files, cls, label) => files?.length ? h("span", { class: `badge cov-b-${cls}` }, `${files.length} ${label}`) : null;
|
|
2693
2781
|
return h(
|
|
2694
2782
|
"div",
|
|
2695
2783
|
{ class: "cov-cluster" },
|
|
@@ -2697,19 +2785,14 @@
|
|
|
2697
2785
|
"div",
|
|
2698
2786
|
{ class: "cov-cluster-head" },
|
|
2699
2787
|
h("code", { class: "cov-label" }, c.label),
|
|
2700
|
-
h("span", { class: "muted" }, `${c.files} files
|
|
2788
|
+
h("span", { class: "muted" }, `${c.files} files`),
|
|
2701
2789
|
h("span", { class: "spacer" }),
|
|
2702
|
-
c.explained
|
|
2703
|
-
c.placed
|
|
2704
|
-
c.
|
|
2790
|
+
badge(c.explained, "explained", "anchored"),
|
|
2791
|
+
badge(c.placed, "placed", "placed"),
|
|
2792
|
+
badge(c.searched, "searched", "searched"),
|
|
2793
|
+
badge(c.uncovered, "uncovered", "not examined"),
|
|
2705
2794
|
firstAnchor ? h("button", { class: "small", onclick: () => openAnchorPeek(firstAnchor) }, "Peek code \u25B8") : null
|
|
2706
2795
|
),
|
|
2707
|
-
c.hubs.length ? h(
|
|
2708
|
-
"div",
|
|
2709
|
-
{ class: "muted cov-hubs" },
|
|
2710
|
-
"most referenced here: ",
|
|
2711
|
-
c.hubs.map((n) => h("code", null, n))
|
|
2712
|
-
) : null,
|
|
2713
2796
|
fileList(c.uncovered, "uncovered")
|
|
2714
2797
|
);
|
|
2715
2798
|
}
|
|
@@ -2717,153 +2800,47 @@
|
|
|
2717
2800
|
return h(
|
|
2718
2801
|
"div",
|
|
2719
2802
|
{ class: "cov-section" },
|
|
2720
|
-
h("h3", null, "By
|
|
2803
|
+
h("h3", null, "By directory"),
|
|
2721
2804
|
h(
|
|
2722
2805
|
"p",
|
|
2723
2806
|
{ class: "muted" },
|
|
2724
|
-
"
|
|
2725
|
-
" The files listed under a
|
|
2807
|
+
"The files in scope by the directory they sit in, largest first.",
|
|
2808
|
+
" The files listed under a directory are the ones nothing in this explainer reached."
|
|
2726
2809
|
),
|
|
2727
2810
|
cov.clusters.map(clusterRow)
|
|
2728
2811
|
);
|
|
2729
2812
|
}
|
|
2730
|
-
function
|
|
2731
|
-
if (!cov.
|
|
2813
|
+
function owners(cov) {
|
|
2814
|
+
if (!cov.owners.length) return null;
|
|
2732
2815
|
return h(
|
|
2733
2816
|
"div",
|
|
2734
2817
|
{ class: "cov-section" },
|
|
2735
|
-
h("h3", null, "What
|
|
2818
|
+
h("h3", null, "What each map node owns"),
|
|
2736
2819
|
h(
|
|
2737
2820
|
"p",
|
|
2738
2821
|
{ class: "muted" },
|
|
2739
|
-
"
|
|
2740
|
-
"
|
|
2822
|
+
"A file counts as placed because a map node's globs match it. The globs are here",
|
|
2823
|
+
" so a broad one is visible rather than silently inflating the count."
|
|
2741
2824
|
),
|
|
2742
2825
|
h(
|
|
2743
2826
|
"div",
|
|
2744
2827
|
{ class: "cov-links" },
|
|
2745
|
-
cov.
|
|
2746
|
-
(
|
|
2828
|
+
cov.owners.map(
|
|
2829
|
+
(o) => h(
|
|
2747
2830
|
"div",
|
|
2748
|
-
{
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
)
|
|
2757
|
-
);
|
|
2758
|
-
}
|
|
2759
|
-
function label(cov, id) {
|
|
2760
|
-
return cov.clusters.find((c) => c.id === id)?.label ?? id;
|
|
2761
|
-
}
|
|
2762
|
-
function sharedNames(cov) {
|
|
2763
|
-
if (!cov.sharedNames.length) return null;
|
|
2764
|
-
return h(
|
|
2765
|
-
"div",
|
|
2766
|
-
{ class: "cov-section" },
|
|
2767
|
-
h("h3", null, "Names defined in more than one part"),
|
|
2768
|
-
h(
|
|
2769
|
-
"p",
|
|
2770
|
-
{ class: "muted" },
|
|
2771
|
-
`${cov.sharedNamesTotal} name${cov.sharedNamesTotal === 1 ? " at this commit is" : "s at this commit are"}`,
|
|
2772
|
-
" defined in two or more parts. What that means here - the same idea in two places,",
|
|
2773
|
-
" two different ideas sharing a word, or a name too common to mean anything - is",
|
|
2774
|
-
" what the code says and this page does not."
|
|
2775
|
-
),
|
|
2776
|
-
h(
|
|
2777
|
-
"div",
|
|
2778
|
-
{ class: "cov-shared" },
|
|
2779
|
-
cov.sharedNames.map(
|
|
2780
|
-
(n) => h(
|
|
2781
|
-
"div",
|
|
2782
|
-
{ class: "cov-link" },
|
|
2783
|
-
h("code", null, n.name),
|
|
2784
|
-
h("span", { class: "muted mono" }, `${n.clusters.length} parts`),
|
|
2785
|
-
h("span", { class: "muted" }, n.files.join(" \xB7 "))
|
|
2831
|
+
{
|
|
2832
|
+
class: "cov-link",
|
|
2833
|
+
onclick: () => navigate("map", { node: o.node }),
|
|
2834
|
+
style: { cursor: "pointer" }
|
|
2835
|
+
},
|
|
2836
|
+
h("code", null, o.node),
|
|
2837
|
+
h("span", { class: "muted" }, o.globs.join(" ")),
|
|
2838
|
+
h("span", { class: "muted mono" }, `${o.files} files`)
|
|
2786
2839
|
)
|
|
2787
2840
|
)
|
|
2788
2841
|
)
|
|
2789
2842
|
);
|
|
2790
2843
|
}
|
|
2791
|
-
function unread(cov) {
|
|
2792
|
-
if (!cov.outsideGraph.length && !cov.files.capped && !cov.owners.length) return null;
|
|
2793
|
-
return h(
|
|
2794
|
-
"div",
|
|
2795
|
-
{ class: "cov-section" },
|
|
2796
|
-
h("h3", null, "Outside the code graph"),
|
|
2797
|
-
cov.outsideGraph.length ? h(
|
|
2798
|
-
"div",
|
|
2799
|
-
null,
|
|
2800
|
-
h(
|
|
2801
|
-
"p",
|
|
2802
|
-
{ class: "muted" },
|
|
2803
|
-
`${cov.files.outsideGraph} files in scope are not in a language the graph reads,`,
|
|
2804
|
-
" so they are in no part above. They are absent from the structure, not empty."
|
|
2805
|
-
),
|
|
2806
|
-
h(
|
|
2807
|
-
"div",
|
|
2808
|
-
{ class: "cov-links" },
|
|
2809
|
-
cov.outsideGraph.slice(0, 16).map(
|
|
2810
|
-
(e) => h(
|
|
2811
|
-
"div",
|
|
2812
|
-
{ class: "cov-link" },
|
|
2813
|
-
h("code", null, `.${e.extension}`),
|
|
2814
|
-
h("span", { class: "muted mono" }, `${e.files} files`)
|
|
2815
|
-
)
|
|
2816
|
-
)
|
|
2817
|
-
),
|
|
2818
|
-
fileList(
|
|
2819
|
-
cov.unclustered.filter((u) => u.state === "uncovered" && u.reason === "outsideGraph").map((u) => u.file),
|
|
2820
|
-
"uncovered"
|
|
2821
|
-
)
|
|
2822
|
-
) : null,
|
|
2823
|
-
cov.files.capped ? h(
|
|
2824
|
-
"div",
|
|
2825
|
-
null,
|
|
2826
|
-
h(
|
|
2827
|
-
"p",
|
|
2828
|
-
{ class: "muted" },
|
|
2829
|
-
`${cov.files.capped} files in scope are in a language the graph reads, but the`,
|
|
2830
|
-
" repo-wide file cap was hit before this scope was read, so they were never parsed."
|
|
2831
|
-
),
|
|
2832
|
-
fileList(
|
|
2833
|
-
cov.unclustered.filter((u) => u.state === "uncovered" && u.reason === "capped").map((u) => u.file),
|
|
2834
|
-
"uncovered"
|
|
2835
|
-
)
|
|
2836
|
-
) : null,
|
|
2837
|
-
cov.owners.length ? h(
|
|
2838
|
-
"div",
|
|
2839
|
-
null,
|
|
2840
|
-
h("h4", null, "What each map node owns"),
|
|
2841
|
-
h(
|
|
2842
|
-
"p",
|
|
2843
|
-
{ class: "muted" },
|
|
2844
|
-
"A file counts as placed because a map node's globs match it. The globs are here",
|
|
2845
|
-
" so a broad one is visible rather than silently inflating the count."
|
|
2846
|
-
),
|
|
2847
|
-
h(
|
|
2848
|
-
"div",
|
|
2849
|
-
{ class: "cov-links" },
|
|
2850
|
-
cov.owners.map(
|
|
2851
|
-
(o) => h(
|
|
2852
|
-
"div",
|
|
2853
|
-
{
|
|
2854
|
-
class: "cov-link",
|
|
2855
|
-
onclick: () => navigate("map", { node: o.node }),
|
|
2856
|
-
style: { cursor: "pointer" }
|
|
2857
|
-
},
|
|
2858
|
-
h("code", null, o.node),
|
|
2859
|
-
h("span", { class: "muted" }, o.globs.join(" ")),
|
|
2860
|
-
h("span", { class: "muted mono" }, `${o.files} files`)
|
|
2861
|
-
)
|
|
2862
|
-
)
|
|
2863
|
-
)
|
|
2864
|
-
) : null
|
|
2865
|
-
);
|
|
2866
|
-
}
|
|
2867
2844
|
|
|
2868
2845
|
// src/ui/app.ts
|
|
2869
2846
|
var app = document.getElementById("app");
|
|
@@ -3042,7 +3019,7 @@
|
|
|
3042
3019
|
"div",
|
|
3043
3020
|
{ class: "tabs" },
|
|
3044
3021
|
tabs.map(
|
|
3045
|
-
([v,
|
|
3022
|
+
([v, label]) => h("button", { class: v === current2 ? "active" : "", onclick: () => navigate(v) }, label)
|
|
3046
3023
|
)
|
|
3047
3024
|
),
|
|
3048
3025
|
h("span", { class: "spacer" }),
|