thurview 0.16.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/README.md +1 -1
- package/dist/document/compile.js +81 -2
- package/dist/document/compile.js.map +1 -1
- package/dist/document/parse.js +37 -13
- package/dist/document/parse.js.map +1 -1
- package/dist/document/schema.js +36 -0
- package/dist/document/schema.js.map +1 -1
- package/dist/ui/app.css +55 -0
- package/dist/ui/app.js +148 -0
- package/dist/ui/app.js.map +3 -3
- package/package.json +1 -1
- package/skills/thurview/references/components.md +47 -0
- package/skills/thurview/references/document-authoring.md +5 -2
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)));
|