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/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 label2 = svg("text", {}, ...tspans(start, y2 - 6, rows[i].lines));
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
- label2.addEventListener("click", (e) => open({ x: e.pageX, y: e.pageY + 8 }));
1085
- label2.setAttribute("role", "button");
1086
- label2.setAttribute("tabindex", "0");
1087
- label2.addEventListener("keydown", (e) => {
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 = label2.getBoundingClientRect();
1091
+ const r = label.getBoundingClientRect();
1092
1092
  open({ x: r.left + window.scrollX, y: r.bottom + window.scrollY + 8 });
1093
1093
  });
1094
- g.appendChild(label2);
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: label2 } of edges) {
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: label2.x, y: label2.y, "text-anchor": label2.at }, edge.case));
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: the code graph could not be built for these commits."
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 count = to - from + 1;
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 ${count} unchanged line${count > 1 ? "s" : ""}`
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 count = to - from + 1;
2105
- const lg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${count} unchanged`));
2106
- const rg = h("tr", { class: "gap" }, h("td", { colspan: 2 }, `\u22EF ${count} unchanged`));
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 links2 = () => {
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(), links2()]);
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), clusters(cov), links(cov), sharedNames(cov), unread(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, label2) => n ? h("span", {
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} ${label2}`
2674
+ title: `${n} ${label}`
2668
2675
  }) : null;
2669
2676
  return h(
2670
2677
  "div",
2671
2678
  { class: "cov-bar" },
2672
- seg(cov.states.explained, "explained", "anchored in the document"),
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 three states it is in."
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
- key.map(
2699
- ([n, cls, label2]) => h("span", { class: "cov-key-item" }, h("i", { class: `cov-dot ${cls}` }), `${n} ${label2}`)
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(label2, value) {
2715
- return h("div", { class: "cov-fact" }, h("b", null, value), h("span", { class: "muted" }, label2));
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 \xB7 ${c.symbols} symbols`),
2788
+ h("span", { class: "muted" }, `${c.files} files`),
2747
2789
  h("span", { class: "spacer" }),
2748
- c.explained.length ? h("span", { class: "badge cov-b-explained" }, `${c.explained.length} anchored`) : null,
2749
- c.placed.length ? h("span", { class: "badge cov-b-placed" }, `${c.placed.length} placed`) : null,
2750
- c.uncovered.length ? h("span", { class: "badge cov-b-uncovered" }, `${c.uncovered.length} not examined`) : null,
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 part of the system"),
2803
+ h("h3", null, "By directory"),
2767
2804
  h(
2768
2805
  "p",
2769
2806
  { class: "muted" },
2770
- "Files clustered by how they reference each other at the pinned commit, largest first.",
2771
- " The files listed under a part are the ones this explainer never examined."
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 links(cov) {
2777
- if (!cov.links.length) return null;
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 reaches what"),
2818
+ h("h3", null, "What each map node owns"),
2782
2819
  h(
2783
2820
  "p",
2784
2821
  { class: "muted" },
2785
- "References that cross from one part to another, with how many there are.",
2786
- " A pair marked both ways references each other in both directions."
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.links.slice(0, 24).map(
2792
- (l) => h(
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
- { class: "cov-link" },
2829
- h("code", null, n.name),
2830
- h("span", { class: "muted mono" }, `${n.clusters.length} parts`),
2831
- 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`)
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, label2]) => h("button", { class: v === current2 ? "active" : "", onclick: () => navigate(v) }, label2)
3022
+ ([v, label]) => h("button", { class: v === current2 ? "active" : "", onclick: () => navigate(v) }, label)
3092
3023
  )
3093
3024
  ),
3094
3025
  h("span", { class: "spacer" }),