thurview 0.17.2 → 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 +11 -9
- package/dist/ui/app.js +103 -172
- 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
|
@@ -1068,7 +1068,7 @@
|
|
|
1068
1068
|
} else {
|
|
1069
1069
|
g.appendChild(svg("line", { x1, y1: y2, x2: x2 + (x2 > x1 ? -4 : 4), y2 }));
|
|
1070
1070
|
}
|
|
1071
|
-
const
|
|
1071
|
+
const label = svg("text", {}, ...tspans(start, y2 - 6, rows[i].lines));
|
|
1072
1072
|
const open = (at) => {
|
|
1073
1073
|
if (m2.anchor) openAnchor(m2.anchor);
|
|
1074
1074
|
else if (m2.code)
|
|
@@ -1081,17 +1081,17 @@
|
|
|
1081
1081
|
at
|
|
1082
1082
|
);
|
|
1083
1083
|
};
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
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
1088
|
const k = e.key;
|
|
1089
1089
|
if (k !== "Enter" && k !== " ") return;
|
|
1090
1090
|
e.preventDefault();
|
|
1091
|
-
const r =
|
|
1091
|
+
const r = label.getBoundingClientRect();
|
|
1092
1092
|
open({ x: r.left + window.scrollX, y: r.bottom + window.scrollY + 8 });
|
|
1093
1093
|
});
|
|
1094
|
-
g.appendChild(
|
|
1094
|
+
g.appendChild(label);
|
|
1095
1095
|
el.appendChild(g);
|
|
1096
1096
|
});
|
|
1097
1097
|
return h(
|
|
@@ -1255,11 +1255,11 @@
|
|
|
1255
1255
|
)
|
|
1256
1256
|
)
|
|
1257
1257
|
);
|
|
1258
|
-
for (const { edge, d, back, label
|
|
1258
|
+
for (const { edge, d, back, label } of edges) {
|
|
1259
1259
|
const g = svg("g", { class: back ? "edge back" : "edge" });
|
|
1260
1260
|
g.appendChild(svg("path", { d, fill: "none", "marker-end": "url(#flow-arr)" }));
|
|
1261
1261
|
if (edge.case)
|
|
1262
|
-
g.appendChild(svg("text", { x:
|
|
1262
|
+
g.appendChild(svg("text", { x: label.x, y: label.y, "text-anchor": label.at }, edge.case));
|
|
1263
1263
|
el.appendChild(g);
|
|
1264
1264
|
}
|
|
1265
1265
|
for (const s of b.steps) {
|
|
@@ -1454,7 +1454,7 @@
|
|
|
1454
1454
|
h(
|
|
1455
1455
|
"p",
|
|
1456
1456
|
{ class: "ifd-verdict" },
|
|
1457
|
-
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."
|
|
1458
1458
|
)
|
|
1459
1459
|
);
|
|
1460
1460
|
const entries = delta?.entries ?? [];
|
|
@@ -1959,7 +1959,7 @@
|
|
|
1959
1959
|
}
|
|
1960
1960
|
function gapRow(d, from, to, colspan, onExpand) {
|
|
1961
1961
|
if (to < from) return null;
|
|
1962
|
-
const
|
|
1962
|
+
const count2 = to - from + 1;
|
|
1963
1963
|
return h(
|
|
1964
1964
|
"tr",
|
|
1965
1965
|
{ class: "gap" },
|
|
@@ -1979,7 +1979,7 @@
|
|
|
1979
1979
|
);
|
|
1980
1980
|
}
|
|
1981
1981
|
},
|
|
1982
|
-
`\u22EF ${
|
|
1982
|
+
`\u22EF ${count2} unchanged line${count2 > 1 ? "s" : ""}`
|
|
1983
1983
|
)
|
|
1984
1984
|
);
|
|
1985
1985
|
}
|
|
@@ -2101,9 +2101,9 @@
|
|
|
2101
2101
|
let prevEnd = 0;
|
|
2102
2102
|
const gapBoth = (from, to) => {
|
|
2103
2103
|
if (to < from) return;
|
|
2104
|
-
const
|
|
2105
|
-
const lg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${
|
|
2106
|
-
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`));
|
|
2107
2107
|
const expand = async () => {
|
|
2108
2108
|
const f = await api.file(state.id, d.path, "head", from, to);
|
|
2109
2109
|
const off = gapOffset(d, from) ?? 0;
|
|
@@ -2476,7 +2476,7 @@
|
|
|
2476
2476
|
group(design ? "Context \u2014 untouched by the design" : "Context \u2014 unchanged here", cold)
|
|
2477
2477
|
);
|
|
2478
2478
|
};
|
|
2479
|
-
const
|
|
2479
|
+
const links = () => {
|
|
2480
2480
|
const here = new Set(childrenOf(all, level, map.filesByNode).map((k) => k.id));
|
|
2481
2481
|
const top = (id) => {
|
|
2482
2482
|
let x = id;
|
|
@@ -2524,7 +2524,7 @@
|
|
|
2524
2524
|
};
|
|
2525
2525
|
const draw = () => {
|
|
2526
2526
|
canvas.innerHTML = "";
|
|
2527
|
-
append(canvas, [intro2(), crumbs(), board(),
|
|
2527
|
+
append(canvas, [intro2(), crumbs(), board(), links()]);
|
|
2528
2528
|
drawSide();
|
|
2529
2529
|
};
|
|
2530
2530
|
const drawSide = () => {
|
|
@@ -2644,6 +2644,12 @@
|
|
|
2644
2644
|
// src/ui/views/coverage.ts
|
|
2645
2645
|
init_dom();
|
|
2646
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
|
+
];
|
|
2647
2653
|
function renderCoverage(root) {
|
|
2648
2654
|
const cov = state.data?.coverage;
|
|
2649
2655
|
if (!cov) {
|
|
@@ -2657,29 +2663,23 @@
|
|
|
2657
2663
|
]);
|
|
2658
2664
|
return;
|
|
2659
2665
|
}
|
|
2660
|
-
append(root, [intro(cov),
|
|
2666
|
+
append(root, [intro(cov), searches(cov), clusters(cov), owners(cov)]);
|
|
2661
2667
|
}
|
|
2668
|
+
var count = (cov, s) => cov.states[s] ?? 0;
|
|
2662
2669
|
function bar(cov) {
|
|
2663
2670
|
const total = Math.max(cov.files.total, 1);
|
|
2664
|
-
const seg = (n, cls,
|
|
2671
|
+
const seg = (n, cls, label) => n ? h("span", {
|
|
2665
2672
|
class: `cov-seg ${cls}`,
|
|
2666
2673
|
style: { width: `${n / total * 100}%` },
|
|
2667
|
-
title: `${n} ${
|
|
2674
|
+
title: `${n} ${label}`
|
|
2668
2675
|
}) : null;
|
|
2669
2676
|
return h(
|
|
2670
2677
|
"div",
|
|
2671
2678
|
{ class: "cov-bar" },
|
|
2672
|
-
seg(cov
|
|
2673
|
-
seg(cov.states.placed, "placed", "placed on the map only"),
|
|
2674
|
-
seg(cov.states.uncovered, "uncovered", "not examined")
|
|
2679
|
+
LABELS.map(([s, label]) => seg(count(cov, s), s, label))
|
|
2675
2680
|
);
|
|
2676
2681
|
}
|
|
2677
2682
|
function intro(cov) {
|
|
2678
|
-
const key = [
|
|
2679
|
-
[cov.states.explained, "explained", "anchored in the document"],
|
|
2680
|
-
[cov.states.placed, "placed", "placed on the map only"],
|
|
2681
|
-
[cov.states.uncovered, "uncovered", "not examined"]
|
|
2682
|
-
];
|
|
2683
2683
|
return h(
|
|
2684
2684
|
"div",
|
|
2685
2685
|
{ class: "cov-intro" },
|
|
@@ -2689,14 +2689,19 @@
|
|
|
2689
2689
|
null,
|
|
2690
2690
|
"A codebase does not fit in a short document, so this one selects. Here is the",
|
|
2691
2691
|
" selection, counted rather than claimed: every file in scope at the pinned commit,",
|
|
2692
|
-
" and which of the
|
|
2692
|
+
" and which of the four states it is in."
|
|
2693
2693
|
),
|
|
2694
2694
|
bar(cov),
|
|
2695
2695
|
h(
|
|
2696
2696
|
"div",
|
|
2697
2697
|
{ class: "cov-key" },
|
|
2698
|
-
|
|
2699
|
-
([
|
|
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
|
+
)
|
|
2700
2705
|
)
|
|
2701
2706
|
),
|
|
2702
2707
|
h(
|
|
@@ -2704,15 +2709,12 @@
|
|
|
2704
2709
|
{ class: "cov-facts" },
|
|
2705
2710
|
fact("scope", cov.scope === "**" ? "the whole repository" : cov.scope),
|
|
2706
2711
|
fact("commit", cov.commit.slice(0, 12)),
|
|
2707
|
-
fact("files in scope", String(cov.files.total))
|
|
2708
|
-
fact("read by the code graph", `${cov.files.inGraph} of ${cov.files.total}`),
|
|
2709
|
-
cov.unresolved ? fact("references the graph could not place", String(cov.unresolved)) : null,
|
|
2710
|
-
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))
|
|
2711
2713
|
)
|
|
2712
2714
|
);
|
|
2713
2715
|
}
|
|
2714
|
-
function fact(
|
|
2715
|
-
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));
|
|
2716
2718
|
}
|
|
2717
2719
|
function fileList(files, cls) {
|
|
2718
2720
|
if (!files.length) return null;
|
|
@@ -2733,9 +2735,49 @@
|
|
|
2733
2735
|
}
|
|
2734
2736
|
return wrap;
|
|
2735
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
|
+
}
|
|
2736
2777
|
function clusterRow(c) {
|
|
2737
2778
|
const anchors = state.data?.document?.anchors ?? {};
|
|
2738
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;
|
|
2739
2781
|
return h(
|
|
2740
2782
|
"div",
|
|
2741
2783
|
{ class: "cov-cluster" },
|
|
@@ -2743,19 +2785,14 @@
|
|
|
2743
2785
|
"div",
|
|
2744
2786
|
{ class: "cov-cluster-head" },
|
|
2745
2787
|
h("code", { class: "cov-label" }, c.label),
|
|
2746
|
-
h("span", { class: "muted" }, `${c.files} files
|
|
2788
|
+
h("span", { class: "muted" }, `${c.files} files`),
|
|
2747
2789
|
h("span", { class: "spacer" }),
|
|
2748
|
-
c.explained
|
|
2749
|
-
c.placed
|
|
2750
|
-
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"),
|
|
2751
2794
|
firstAnchor ? h("button", { class: "small", onclick: () => openAnchorPeek(firstAnchor) }, "Peek code \u25B8") : null
|
|
2752
2795
|
),
|
|
2753
|
-
c.hubs.length ? h(
|
|
2754
|
-
"div",
|
|
2755
|
-
{ class: "muted cov-hubs" },
|
|
2756
|
-
"most referenced here: ",
|
|
2757
|
-
c.hubs.map((n) => h("code", null, n))
|
|
2758
|
-
) : null,
|
|
2759
2796
|
fileList(c.uncovered, "uncovered")
|
|
2760
2797
|
);
|
|
2761
2798
|
}
|
|
@@ -2763,153 +2800,47 @@
|
|
|
2763
2800
|
return h(
|
|
2764
2801
|
"div",
|
|
2765
2802
|
{ class: "cov-section" },
|
|
2766
|
-
h("h3", null, "By
|
|
2803
|
+
h("h3", null, "By directory"),
|
|
2767
2804
|
h(
|
|
2768
2805
|
"p",
|
|
2769
2806
|
{ class: "muted" },
|
|
2770
|
-
"
|
|
2771
|
-
" 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."
|
|
2772
2809
|
),
|
|
2773
2810
|
cov.clusters.map(clusterRow)
|
|
2774
2811
|
);
|
|
2775
2812
|
}
|
|
2776
|
-
function
|
|
2777
|
-
if (!cov.
|
|
2813
|
+
function owners(cov) {
|
|
2814
|
+
if (!cov.owners.length) return null;
|
|
2778
2815
|
return h(
|
|
2779
2816
|
"div",
|
|
2780
2817
|
{ class: "cov-section" },
|
|
2781
|
-
h("h3", null, "What
|
|
2818
|
+
h("h3", null, "What each map node owns"),
|
|
2782
2819
|
h(
|
|
2783
2820
|
"p",
|
|
2784
2821
|
{ class: "muted" },
|
|
2785
|
-
"
|
|
2786
|
-
"
|
|
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."
|
|
2787
2824
|
),
|
|
2788
2825
|
h(
|
|
2789
2826
|
"div",
|
|
2790
2827
|
{ class: "cov-links" },
|
|
2791
|
-
cov.
|
|
2792
|
-
(
|
|
2793
|
-
"div",
|
|
2794
|
-
{ class: "cov-link" },
|
|
2795
|
-
h("code", null, label(cov, l.from)),
|
|
2796
|
-
h("span", { class: "muted" }, "\u2192"),
|
|
2797
|
-
h("code", null, label(cov, l.to)),
|
|
2798
|
-
h("span", { class: "muted mono" }, `${l.references} refs`),
|
|
2799
|
-
l.bothWays ? h("span", { class: "badge" }, "both ways") : null
|
|
2800
|
-
)
|
|
2801
|
-
)
|
|
2802
|
-
)
|
|
2803
|
-
);
|
|
2804
|
-
}
|
|
2805
|
-
function label(cov, id) {
|
|
2806
|
-
return cov.clusters.find((c) => c.id === id)?.label ?? id;
|
|
2807
|
-
}
|
|
2808
|
-
function sharedNames(cov) {
|
|
2809
|
-
if (!cov.sharedNames.length) return null;
|
|
2810
|
-
return h(
|
|
2811
|
-
"div",
|
|
2812
|
-
{ class: "cov-section" },
|
|
2813
|
-
h("h3", null, "Names defined in more than one part"),
|
|
2814
|
-
h(
|
|
2815
|
-
"p",
|
|
2816
|
-
{ class: "muted" },
|
|
2817
|
-
`${cov.sharedNamesTotal} name${cov.sharedNamesTotal === 1 ? " at this commit is" : "s at this commit are"}`,
|
|
2818
|
-
" defined in two or more parts. What that means here - the same idea in two places,",
|
|
2819
|
-
" two different ideas sharing a word, or a name too common to mean anything - is",
|
|
2820
|
-
" what the code says and this page does not."
|
|
2821
|
-
),
|
|
2822
|
-
h(
|
|
2823
|
-
"div",
|
|
2824
|
-
{ class: "cov-shared" },
|
|
2825
|
-
cov.sharedNames.map(
|
|
2826
|
-
(n) => h(
|
|
2828
|
+
cov.owners.map(
|
|
2829
|
+
(o) => h(
|
|
2827
2830
|
"div",
|
|
2828
|
-
{
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
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`)
|
|
2832
2839
|
)
|
|
2833
2840
|
)
|
|
2834
2841
|
)
|
|
2835
2842
|
);
|
|
2836
2843
|
}
|
|
2837
|
-
function unread(cov) {
|
|
2838
|
-
if (!cov.outsideGraph.length && !cov.files.capped && !cov.owners.length) return null;
|
|
2839
|
-
return h(
|
|
2840
|
-
"div",
|
|
2841
|
-
{ class: "cov-section" },
|
|
2842
|
-
h("h3", null, "Outside the code graph"),
|
|
2843
|
-
cov.outsideGraph.length ? h(
|
|
2844
|
-
"div",
|
|
2845
|
-
null,
|
|
2846
|
-
h(
|
|
2847
|
-
"p",
|
|
2848
|
-
{ class: "muted" },
|
|
2849
|
-
`${cov.files.outsideGraph} files in scope are not in a language the graph reads,`,
|
|
2850
|
-
" so they are in no part above. They are absent from the structure, not empty."
|
|
2851
|
-
),
|
|
2852
|
-
h(
|
|
2853
|
-
"div",
|
|
2854
|
-
{ class: "cov-links" },
|
|
2855
|
-
cov.outsideGraph.slice(0, 16).map(
|
|
2856
|
-
(e) => h(
|
|
2857
|
-
"div",
|
|
2858
|
-
{ class: "cov-link" },
|
|
2859
|
-
h("code", null, `.${e.extension}`),
|
|
2860
|
-
h("span", { class: "muted mono" }, `${e.files} files`)
|
|
2861
|
-
)
|
|
2862
|
-
)
|
|
2863
|
-
),
|
|
2864
|
-
fileList(
|
|
2865
|
-
cov.unclustered.filter((u) => u.state === "uncovered" && u.reason === "outsideGraph").map((u) => u.file),
|
|
2866
|
-
"uncovered"
|
|
2867
|
-
)
|
|
2868
|
-
) : null,
|
|
2869
|
-
cov.files.capped ? h(
|
|
2870
|
-
"div",
|
|
2871
|
-
null,
|
|
2872
|
-
h(
|
|
2873
|
-
"p",
|
|
2874
|
-
{ class: "muted" },
|
|
2875
|
-
`${cov.files.capped} files in scope are in a language the graph reads, but the`,
|
|
2876
|
-
" repo-wide file cap was hit before this scope was read, so they were never parsed."
|
|
2877
|
-
),
|
|
2878
|
-
fileList(
|
|
2879
|
-
cov.unclustered.filter((u) => u.state === "uncovered" && u.reason === "capped").map((u) => u.file),
|
|
2880
|
-
"uncovered"
|
|
2881
|
-
)
|
|
2882
|
-
) : null,
|
|
2883
|
-
cov.owners.length ? h(
|
|
2884
|
-
"div",
|
|
2885
|
-
null,
|
|
2886
|
-
h("h4", null, "What each map node owns"),
|
|
2887
|
-
h(
|
|
2888
|
-
"p",
|
|
2889
|
-
{ class: "muted" },
|
|
2890
|
-
"A file counts as placed because a map node's globs match it. The globs are here",
|
|
2891
|
-
" so a broad one is visible rather than silently inflating the count."
|
|
2892
|
-
),
|
|
2893
|
-
h(
|
|
2894
|
-
"div",
|
|
2895
|
-
{ class: "cov-links" },
|
|
2896
|
-
cov.owners.map(
|
|
2897
|
-
(o) => h(
|
|
2898
|
-
"div",
|
|
2899
|
-
{
|
|
2900
|
-
class: "cov-link",
|
|
2901
|
-
onclick: () => navigate("map", { node: o.node }),
|
|
2902
|
-
style: { cursor: "pointer" }
|
|
2903
|
-
},
|
|
2904
|
-
h("code", null, o.node),
|
|
2905
|
-
h("span", { class: "muted" }, o.globs.join(" ")),
|
|
2906
|
-
h("span", { class: "muted mono" }, `${o.files} files`)
|
|
2907
|
-
)
|
|
2908
|
-
)
|
|
2909
|
-
)
|
|
2910
|
-
) : null
|
|
2911
|
-
);
|
|
2912
|
-
}
|
|
2913
2844
|
|
|
2914
2845
|
// src/ui/app.ts
|
|
2915
2846
|
var app = document.getElementById("app");
|
|
@@ -3088,7 +3019,7 @@
|
|
|
3088
3019
|
"div",
|
|
3089
3020
|
{ class: "tabs" },
|
|
3090
3021
|
tabs.map(
|
|
3091
|
-
([v,
|
|
3022
|
+
([v, label]) => h("button", { class: v === current2 ? "active" : "", onclick: () => navigate(v) }, label)
|
|
3092
3023
|
)
|
|
3093
3024
|
),
|
|
3094
3025
|
h("span", { class: "spacer" }),
|