thurview 0.15.0 → 0.17.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/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/dist/ui/app.css CHANGED
@@ -1133,6 +1133,61 @@ kbd {
1133
1133
  .seq .msg text:hover {
1134
1134
  text-decoration: underline;
1135
1135
  }
1136
+ .flow text {
1137
+ font: 12px var(--font-mono);
1138
+ fill: var(--fg);
1139
+ }
1140
+ .flow .step rect,
1141
+ .flow .step path {
1142
+ fill: var(--bg-code);
1143
+ stroke: var(--line);
1144
+ }
1145
+ .flow .step:not(.plain) {
1146
+ cursor: pointer;
1147
+ }
1148
+ .flow .step:not(.plain) text {
1149
+ fill: var(--link);
1150
+ }
1151
+ .flow .step:not(.plain):hover rect,
1152
+ .flow .step:not(.plain):hover path {
1153
+ stroke: var(--accent);
1154
+ }
1155
+ /* The step takes focus, so the focus has to be visible on the shape itself:
1156
+ a UA outline on an SVG group lands nowhere the reader can see. */
1157
+ .flow .step:focus {
1158
+ outline: none;
1159
+ }
1160
+ .flow .step:focus-visible rect,
1161
+ .flow .step:focus-visible path {
1162
+ stroke: var(--accent);
1163
+ stroke-width: 2;
1164
+ }
1165
+ /* A step nobody can open reads as the aside it is, not as a broken link.
1166
+ A decision is a path rather than a rect, so both shapes carry the rule. */
1167
+ .flow .step.plain rect,
1168
+ .flow .step.plain path {
1169
+ fill: var(--bg3);
1170
+ stroke-dasharray: 4 4;
1171
+ }
1172
+ .flow .step text.who {
1173
+ font-size: 10.5px;
1174
+ fill: var(--muted);
1175
+ }
1176
+ .flow .step.decision text {
1177
+ font-style: italic;
1178
+ }
1179
+ .flow .edge path {
1180
+ stroke: var(--fg2);
1181
+ marker-end: url(#flow-arr);
1182
+ }
1183
+ .flow .edge text {
1184
+ font-size: 10.5px;
1185
+ fill: var(--muted);
1186
+ }
1187
+ .flow .edge.back path {
1188
+ stroke: var(--muted);
1189
+ stroke-dasharray: 3 3;
1190
+ }
1136
1191
  .callstack {
1137
1192
  font-family: var(--font-mono);
1138
1193
  font-size: 12.5px;
@@ -1450,6 +1505,31 @@ kbd {
1450
1505
  font-weight: 700;
1451
1506
  flex: 1;
1452
1507
  }
1508
+ /* Why it is this row's turn sits under the title, so the reason is read with
1509
+ the name rather than hunted for across the row. */
1510
+ .home .item .why {
1511
+ display: block;
1512
+ font-family: var(--font-body);
1513
+ font-weight: 400;
1514
+ font-size: 12px;
1515
+ color: var(--muted);
1516
+ margin-top: 2px;
1517
+ }
1518
+ .home .item .cr {
1519
+ font-size: 12px;
1520
+ text-decoration: none;
1521
+ }
1522
+ .home .item .cr:hover {
1523
+ color: var(--accent);
1524
+ }
1525
+ .home h3.repo {
1526
+ display: flex;
1527
+ gap: 10px;
1528
+ align-items: center;
1529
+ text-transform: none;
1530
+ letter-spacing: 0;
1531
+ font-family: var(--font-mono);
1532
+ }
1453
1533
  .empty-state {
1454
1534
  padding: 40px;
1455
1535
  text-align: center;
package/dist/ui/app.js CHANGED
@@ -908,6 +908,73 @@
908
908
  // src/ui/diagrams.ts
909
909
  init_dom();
910
910
  init_dom();
911
+
912
+ // src/ui/flow-layout.ts
913
+ var boxW = 210;
914
+ var boxH = 46;
915
+ var gapX = 40;
916
+ var gapY = 58;
917
+ var pad = 14;
918
+ var minW = 700;
919
+ function layoutFlow(b) {
920
+ const layer = /* @__PURE__ */ new Map([[b.steps[0].id, 0]]);
921
+ const queue = [b.steps[0].id];
922
+ while (queue.length) {
923
+ const cur = queue.shift();
924
+ for (const e of b.edges)
925
+ if (e.from === cur && !layer.has(e.to)) {
926
+ layer.set(e.to, layer.get(cur) + 1);
927
+ queue.push(e.to);
928
+ }
929
+ }
930
+ const rows = [];
931
+ for (const s of b.steps) {
932
+ const li = layer.get(s.id);
933
+ (rows[li] ??= []).push(s.id);
934
+ }
935
+ const span = (e) => layer.get(e.from) - layer.get(e.to);
936
+ const loops = b.edges.filter((e) => span(e) >= 0).sort((p, q) => span(p) - span(q));
937
+ const lanes = loops.length > 0;
938
+ const widest = Math.max(...rows.map((r) => r.length));
939
+ const laneGap = 18;
940
+ const lane = lanes ? 34 + (loops.length - 1) * laneGap : 0;
941
+ const band = lanes ? gapY / 2 : 0;
942
+ const width = Math.max(minW, pad * 2 + widest * boxW + (widest - 1) * gapX + lane);
943
+ const height = pad * 2 + band * 2 + rows.length * boxH + (rows.length - 1) * gapY;
944
+ const at = (id) => {
945
+ const li = layer.get(id);
946
+ const row = rows[li];
947
+ const spanW = row.length * boxW + (row.length - 1) * gapX;
948
+ const x = pad + (width - lane - pad * 2 - spanW) / 2 + row.indexOf(id) * (boxW + gapX);
949
+ return { x, y: pad + band + li * (boxH + gapY), cx: x + boxW / 2, li };
950
+ };
951
+ const edges = b.edges.map((edge) => {
952
+ const from = at(edge.from);
953
+ const to = at(edge.to);
954
+ const back = to.li < from.li;
955
+ if (to.li > from.li) {
956
+ const mid = from.y + boxH + (to.y - from.y - boxH) / 2;
957
+ return {
958
+ edge,
959
+ back,
960
+ d: `M${from.cx},${from.y + boxH} V${mid} H${to.cx} V${to.y - 4}`,
961
+ label: { x: (from.cx + to.cx) / 2 + 6, y: mid - 5, at: "middle" }
962
+ };
963
+ }
964
+ const laneX = width - lane + 17 + loops.indexOf(edge) * laneGap;
965
+ const out = from.y + boxH + gapY / 2;
966
+ const into = to.y - gapY / 2;
967
+ return {
968
+ edge,
969
+ back,
970
+ d: `M${from.cx},${from.y + boxH} V${out} H${laneX} V${into} H${to.cx} V${to.y - 4}`,
971
+ label: { x: laneX - 6, y: (out + into) / 2, at: "end" }
972
+ };
973
+ });
974
+ return { width, height, at, edges };
975
+ }
976
+
977
+ // src/ui/diagrams.ts
911
978
  function openAnchor(id) {
912
979
  const a = state.data?.document?.anchors[id];
913
980
  if (a) openAnchorPeek(a);
@@ -1116,6 +1183,84 @@
1116
1183
  ops
1117
1184
  );
1118
1185
  }
1186
+ function flowDiagram(b, doc) {
1187
+ const fit = (text, room, px) => {
1188
+ const max = Math.floor(room / (px * 0.605));
1189
+ return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
1190
+ };
1191
+ const { width, height, at, edges } = layoutFlow(b);
1192
+ const el = svg("svg", { viewBox: `0 0 ${width} ${height}`, class: "flow" });
1193
+ el.appendChild(
1194
+ svg(
1195
+ "defs",
1196
+ {},
1197
+ svg(
1198
+ "marker",
1199
+ {
1200
+ id: "flow-arr",
1201
+ viewBox: "0 0 10 10",
1202
+ refX: "9",
1203
+ refY: "5",
1204
+ markerWidth: "7",
1205
+ markerHeight: "7",
1206
+ orient: "auto"
1207
+ },
1208
+ svg("path", { d: "M0,0 L10,5 L0,10 z", fill: "currentColor" })
1209
+ )
1210
+ )
1211
+ );
1212
+ for (const { edge, d, back, label: label2 } of edges) {
1213
+ const g = svg("g", { class: back ? "edge back" : "edge" });
1214
+ g.appendChild(svg("path", { d, fill: "none", "marker-end": "url(#flow-arr)" }));
1215
+ if (edge.case)
1216
+ g.appendChild(svg("text", { x: label2.x, y: label2.y, "text-anchor": label2.at }, edge.case));
1217
+ el.appendChild(g);
1218
+ }
1219
+ for (const s of b.steps) {
1220
+ const { x, y, cx } = at(s.id);
1221
+ const g = svg("g", {
1222
+ class: `step${s.decision ? " decision" : ""}${s.anchor ? "" : " plain"}`
1223
+ });
1224
+ g.appendChild(
1225
+ s.decision ? svg("path", {
1226
+ d: `M${x + 16},${y} H${x + boxW - 16} L${x + boxW},${y + boxH / 2} L${x + boxW - 16},${y + boxH} H${x + 16} L${x},${y + boxH / 2} Z`
1227
+ }) : svg("rect", { x, y, width: boxW, height: boxH, rx: 6 })
1228
+ );
1229
+ const room = boxW - (s.decision ? 44 : 20);
1230
+ const actor = s.actor ? doc.actors[s.actor]?.label ?? s.actor : null;
1231
+ if (actor)
1232
+ g.appendChild(
1233
+ svg(
1234
+ "text",
1235
+ { class: "who", x: cx, y: y + 17, "text-anchor": "middle" },
1236
+ fit(actor, room, 10.5)
1237
+ )
1238
+ );
1239
+ g.appendChild(
1240
+ svg(
1241
+ "text",
1242
+ { x: cx, y: actor ? y + 33 : y + boxH / 2 + 4, "text-anchor": "middle" },
1243
+ fit(s.label, room, 12)
1244
+ )
1245
+ );
1246
+ g.appendChild(svg("title", {}, s.anchor ? `${s.label} \u2014 ${s.anchor}` : s.label));
1247
+ if (s.anchor) {
1248
+ const open = () => openAnchor(s.anchor);
1249
+ g.setAttribute("role", "button");
1250
+ g.setAttribute("tabindex", "0");
1251
+ g.addEventListener("click", open);
1252
+ g.addEventListener("keydown", (e) => {
1253
+ const k = e.key;
1254
+ if (k === "Enter" || k === " ") {
1255
+ e.preventDefault();
1256
+ open();
1257
+ }
1258
+ });
1259
+ }
1260
+ el.appendChild(g);
1261
+ }
1262
+ return h("div", { class: "diagram" }, h("div", { class: "dhead" }, b.label), el);
1263
+ }
1119
1264
 
1120
1265
  // src/ui/views/review.ts
1121
1266
  function renderReview(root) {
@@ -1549,6 +1694,9 @@
1549
1694
  case "database":
1550
1695
  wrap.appendChild(databaseLens(b, doc));
1551
1696
  break;
1697
+ case "flow":
1698
+ wrap.appendChild(flowDiagram(b, doc));
1699
+ break;
1552
1700
  }
1553
1701
  if (threads.length)
1554
1702
  wrap.appendChild(h("div", { class: "thread-pins" }, threads.map(threadPinRow)));
@@ -2722,18 +2870,9 @@
2722
2870
  async function home() {
2723
2871
  clear(app);
2724
2872
  const reviews = await api.reviews();
2725
- const el = h("div", { class: "home" }, h("h2", null, "Reviews, explainers and designs"));
2873
+ const el = h("div", { class: "home" }, h("h2", null, "Queue"));
2726
2874
  const active = reviews.filter((r) => !r.dismissed);
2727
2875
  const dismissed = reviews.filter((r) => r.dismissed);
2728
- const item = (r) => h(
2729
- "div",
2730
- { class: "item", onclick: () => location.href = `/review/${r.id}` },
2731
- h("span", { class: "t" }, r.title),
2732
- h("span", { class: `badge ${statusClass2(r.status)}` }, r.status),
2733
- r.openThreads ? h("span", { class: "badge accent" }, `${r.openThreads} open`) : null,
2734
- h("span", { class: "muted mono", style: { fontSize: "12px" } }, bindingLabel(r)),
2735
- h("span", { class: "muted", style: { fontSize: "12px" } }, timeAgo(r.updatedAt))
2736
- );
2737
2876
  if (!active.length)
2738
2877
  el.appendChild(
2739
2878
  h(
@@ -2742,13 +2881,75 @@
2742
2881
  "Nothing published yet. Ask your agent for a review of a change, an explainer of the codebase, or a design of what to build next."
2743
2882
  )
2744
2883
  );
2745
- active.forEach((r) => el.appendChild(item(r)));
2884
+ const groups = /* @__PURE__ */ new Map();
2885
+ for (const r of active) groups.set(r.queue.repo, [...groups.get(r.queue.repo) ?? [], r]);
2886
+ for (const [repo, rows] of groups) {
2887
+ const yours = rows.filter((r) => r.queue.turn === "you").length;
2888
+ el.appendChild(
2889
+ h(
2890
+ "h3",
2891
+ { class: "repo" },
2892
+ repo,
2893
+ yours ? h("span", { class: "badge accent" }, `${yours} your turn`) : null
2894
+ )
2895
+ );
2896
+ rows.forEach((r) => el.appendChild(queueItem(r)));
2897
+ }
2746
2898
  if (dismissed.length) {
2747
2899
  el.appendChild(h("h3", { class: "muted" }, "Dismissed"));
2748
- dismissed.forEach((r) => el.appendChild(item(r)));
2900
+ dismissed.forEach((r) => el.appendChild(queueItem(r)));
2749
2901
  }
2750
2902
  app.appendChild(el);
2751
2903
  }
2904
+ var TURN = { you: "your turn", agent: "agent", nobody: "nobody" };
2905
+ var DECISION = { approve: "approved", "request-changes": "changes requested", close: "closed" };
2906
+ function queueItem(r) {
2907
+ const q = r.queue;
2908
+ const cr = q.change;
2909
+ const age = q.factsAt ? `forge read ${timeAgo(q.factsAt)}` : cr ? "forge not read yet" : "";
2910
+ return h(
2911
+ "div",
2912
+ { class: "item", onclick: () => location.href = `/review/${r.id}` },
2913
+ h("span", { class: `badge turn ${q.turn === "you" ? "accent" : ""}` }, TURN[q.turn]),
2914
+ h(
2915
+ "span",
2916
+ { class: "t" },
2917
+ r.title,
2918
+ h("span", { class: "why" }, [r.kind ?? "review", q.why, age].filter(Boolean).join(" \xB7 "))
2919
+ ),
2920
+ cr ? h(
2921
+ "a",
2922
+ {
2923
+ class: "mono muted cr",
2924
+ href: cr.url ?? void 0,
2925
+ target: "_blank",
2926
+ rel: "noopener",
2927
+ onclick: (e) => e.stopPropagation()
2928
+ },
2929
+ `#${cr.number}${cr.state && cr.state !== "open" ? ` ${cr.state}` : ""}`
2930
+ ) : h("span", { class: "mono muted" }, bindingLabel(r)),
2931
+ r.openThreads ? h("span", { class: "badge accent" }, `${r.openThreads} open`) : null,
2932
+ q.pin ? h(
2933
+ "span",
2934
+ { class: `badge ${q.pin === "behind" ? "warn" : "ok"}` },
2935
+ q.pin === "behind" ? "pin behind head" : "at head"
2936
+ ) : null,
2937
+ q.decision ? h(
2938
+ "span",
2939
+ {
2940
+ class: `badge ${q.decision.posted === false ? "warn" : ""}`,
2941
+ title: q.decision.posted === null ? null : q.decision.posted ? "posted to the change request" : "not posted to the change request yet"
2942
+ },
2943
+ DECISION[q.decision.decision] + (q.decision.posted === null ? "" : q.decision.posted ? " \xB7 posted" : " \xB7 not posted")
2944
+ ) : null,
2945
+ q.ci ? h(
2946
+ "span",
2947
+ { class: `badge ${q.ci.trustworthy ? "ok" : "warn"}`, title: q.ci.verdict },
2948
+ q.ci.trustworthy ? "CI gates" : "CI not a gate"
2949
+ ) : null,
2950
+ h("span", { class: "muted", style: { fontSize: "12px" } }, timeAgo(r.updatedAt))
2951
+ );
2952
+ }
2752
2953
  function bindingLabel(r) {
2753
2954
  if (r.binding.kind === "pr") return `PR #${r.binding.name}`;
2754
2955
  if (r.binding.kind === "codebase") return r.binding.name === "**" ? "codebase" : r.binding.name;