thurview 0.17.1 → 0.17.2

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.css CHANGED
@@ -1110,6 +1110,12 @@ kbd {
1110
1110
  width: 100%;
1111
1111
  height: auto;
1112
1112
  }
1113
+ .seq-scroll {
1114
+ overflow-x: auto;
1115
+ }
1116
+ .diagram svg.seq {
1117
+ margin: 0 auto;
1118
+ }
1113
1119
  .seq text {
1114
1120
  font: 12px var(--font-mono);
1115
1121
  fill: var(--fg);
@@ -1128,11 +1134,22 @@ kbd {
1128
1134
  }
1129
1135
  .seq .msg text {
1130
1136
  fill: var(--link);
1137
+ /* A label crosses the lifelines between its two ends; a halo in the frame's
1138
+ colour keeps the dashes from striking through the words. */
1139
+ paint-order: stroke;
1140
+ stroke: var(--bg2);
1141
+ stroke-width: 4px;
1142
+ stroke-linejoin: round;
1131
1143
  cursor: pointer;
1132
1144
  }
1133
- .seq .msg text:hover {
1145
+ .seq .msg text:hover,
1146
+ .seq .msg text:focus-visible {
1134
1147
  text-decoration: underline;
1135
1148
  }
1149
+ .seq .msg text:focus-visible {
1150
+ outline: 2px solid var(--accent);
1151
+ outline-offset: 2px;
1152
+ }
1136
1153
  .flow text {
1137
1154
  font: 12px var(--font-mono);
1138
1155
  fill: var(--fg);
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 colW = 170;
984
- const top = 44;
985
- const rowH = 34;
986
- const n = b.actors.length;
987
- const width = Math.max(colW * n, 320);
988
- const height = top + rowH * (b.messages.length + 1) + 10;
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 x = (id) => b.actors.findIndex((a) => a.id === id) * colW + colW / 2;
1010
- b.actors.forEach((a, i) => {
1011
- const cx = i * colW + colW / 2;
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 - 70, y: 6, width: 140, height: 28, rx: 6 }));
1014
- g.appendChild(svg("text", { x: cx, y: 25, "text-anchor": "middle" }, a.label));
1015
- g.appendChild(svg("line", { class: "life", x1: cx, y1: 34, x2: cx, y2: height - 6 }));
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 y = top + rowH * (i + 1);
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},${y - 10} h30 v20 h-30`,
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: y, x2: x2 + (x2 > x1 ? -4 : 4), y2: y }));
1069
+ g.appendChild(svg("line", { x1, y1: y2, x2: x2 + (x2 > x1 ? -4 : 4), y2 }));
1035
1070
  }
1036
- const label2 = svg(
1037
- "text",
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 label2 = 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
- { x: e.pageX, y: e.pageY + 8 }
1081
+ at
1051
1082
  );
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) => {
1088
+ const k = e.key;
1089
+ if (k !== "Enter" && k !== " ") return;
1090
+ e.preventDefault();
1091
+ const r = label2.getBoundingClientRect();
1092
+ open({ x: r.left + window.scrollX, y: r.bottom + window.scrollY + 8 });
1052
1093
  });
1053
1094
  g.appendChild(label2);
1054
1095
  el.appendChild(g);
1055
1096
  });
1056
- return h("div", { class: "diagram" }, h("div", { class: "dhead" }, b.label), el);
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) => {