thurview 0.18.0 → 0.20.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 +24 -19
- package/dist/cli.js +59 -42
- package/dist/cli.js.map +1 -1
- package/dist/diff.js +3 -3
- package/dist/diff.js.map +1 -1
- package/dist/document/compile.js +1 -1
- package/dist/document/compile.js.map +1 -1
- package/dist/export.js +143 -0
- package/dist/export.js.map +1 -0
- package/dist/git.js +0 -9
- package/dist/git.js.map +1 -1
- package/dist/highlight-theme.js +37 -14
- package/dist/highlight-theme.js.map +1 -1
- package/dist/highlight.js +3 -15
- package/dist/highlight.js.map +1 -1
- package/dist/server/server.js +58 -62
- package/dist/server/server.js.map +1 -1
- package/dist/ui/app.css +68 -30
- package/dist/ui/app.js +76 -39
- package/dist/ui/app.js.map +2 -2
- package/dist/ui/index.html +21 -0
- package/package.json +1 -1
- package/skills/thurview/SKILL.md +29 -20
- package/skills/thurview/references/document-authoring.md +1 -2
- package/skills/thurview/references/lifecycle.md +0 -1
- package/skills/thurview-design/SKILL.md +8 -7
- package/skills/thurview-explain/SKILL.md +8 -6
- package/dist/theme.js +0 -290
- package/dist/theme.js.map +0 -1
- package/skills/thurview/references/theme.md +0 -89
package/dist/ui/app.js
CHANGED
|
@@ -125,7 +125,30 @@
|
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
// src/ui/api.ts
|
|
128
|
+
var published = (() => {
|
|
129
|
+
const el = document.getElementById("thurview-snapshot");
|
|
130
|
+
return el?.textContent ? JSON.parse(el.textContent) : null;
|
|
131
|
+
})();
|
|
132
|
+
function offline(url) {
|
|
133
|
+
const snap = published;
|
|
134
|
+
const u = new URL(url, "http://published.invalid");
|
|
135
|
+
const sub = u.pathname.split("/").filter(Boolean)[3];
|
|
136
|
+
const path = u.searchParams.get("path") ?? "";
|
|
137
|
+
const answer = sub === void 0 ? snap.payload : sub === "commits" ? snap.commits : sub === "presence" ? snap.payload.agent : sub === "diff" ? snap.diffs[path] : sub === "file" ? sliceLines(snap.files[`${u.searchParams.get("graph")}:${path}`], u.searchParams) : void 0;
|
|
138
|
+
if (answer === void 0) throw new Error("not in this published copy");
|
|
139
|
+
return answer;
|
|
140
|
+
}
|
|
141
|
+
function sliceLines(f, q) {
|
|
142
|
+
if (!f) return void 0;
|
|
143
|
+
const from = Math.max(1, Number(q.get("from") ?? 1));
|
|
144
|
+
const to = Math.min(f.total, Number(q.get("to") ?? f.total));
|
|
145
|
+
return { ...f, from, to, lines: f.lines.slice(from - 1, to) };
|
|
146
|
+
}
|
|
128
147
|
async function j(url, init) {
|
|
148
|
+
if (published) {
|
|
149
|
+
if (init?.method && init.method !== "GET") throw new Error("a published copy is read only");
|
|
150
|
+
return offline(url);
|
|
151
|
+
}
|
|
129
152
|
const r = await fetch(url, init);
|
|
130
153
|
const body = await r.json();
|
|
131
154
|
if (!r.ok) throw new Error(body.error ?? r.statusText);
|
|
@@ -227,7 +250,10 @@
|
|
|
227
250
|
return s === "accepted" || s === "closed";
|
|
228
251
|
}
|
|
229
252
|
function readOnly() {
|
|
230
|
-
return isTerminal() ||
|
|
253
|
+
return isTerminal() || !canWrite();
|
|
254
|
+
}
|
|
255
|
+
function canWrite() {
|
|
256
|
+
return state.viewingRevision === null && !published;
|
|
231
257
|
}
|
|
232
258
|
|
|
233
259
|
// src/ui/code.ts
|
|
@@ -615,13 +641,13 @@
|
|
|
615
641
|
return table;
|
|
616
642
|
}
|
|
617
643
|
function startLineComment(e, target, quote) {
|
|
618
|
-
if (
|
|
644
|
+
if (!canWrite()) return;
|
|
619
645
|
const el = commentPopover(target, quote?.trim() ? quote.trim() : void 0);
|
|
620
646
|
popover(el, { x: e.pageX + 8, y: e.pageY + 8 });
|
|
621
647
|
}
|
|
622
648
|
var rangeStart = null;
|
|
623
649
|
function lineClick(e, path, side2, line, table) {
|
|
624
|
-
if (
|
|
650
|
+
if (!canWrite()) return;
|
|
625
651
|
if (e.shiftKey && rangeStart && rangeStart.path === path && rangeStart.side === side2) {
|
|
626
652
|
const from = Math.min(rangeStart.line, line);
|
|
627
653
|
const to = Math.max(rangeStart.line, line);
|
|
@@ -664,8 +690,7 @@
|
|
|
664
690
|
);
|
|
665
691
|
const a = rowOf(range.startContainer);
|
|
666
692
|
const b = rowOf(range.endContainer);
|
|
667
|
-
if (!a || !b || !root.contains(a) || !root.contains(b) ||
|
|
668
|
-
return remove();
|
|
693
|
+
if (!a || !b || !root.contains(a) || !root.contains(b) || !canWrite()) return remove();
|
|
669
694
|
const table = a.closest("table");
|
|
670
695
|
if (!table || table !== b.closest("table")) return remove();
|
|
671
696
|
const path = root.closest("[data-path]")?.dataset["path"] ?? "";
|
|
@@ -708,7 +733,7 @@
|
|
|
708
733
|
const sel = window.getSelection();
|
|
709
734
|
if (sel && !sel.isCollapsed) return;
|
|
710
735
|
const word = wordAt(e);
|
|
711
|
-
if (!word) return;
|
|
736
|
+
if (!word || published) return;
|
|
712
737
|
const defs = await api.symbols(state.id, word, graph);
|
|
713
738
|
if (e.ctrlKey || e.metaKey) {
|
|
714
739
|
const d = defs[0];
|
|
@@ -1421,7 +1446,7 @@
|
|
|
1421
1446
|
class: threads.length ? "count" : "",
|
|
1422
1447
|
title: design ? "Comment on what this design proposes" : "Comment on the interface delta",
|
|
1423
1448
|
onclick: (e) => {
|
|
1424
|
-
if (
|
|
1449
|
+
if (!canWrite()) return;
|
|
1425
1450
|
popover(commentPopover({ type: "document", blockId: DELTA_BLOCK }), {
|
|
1426
1451
|
x: e.pageX + 10,
|
|
1427
1452
|
y: e.pageY
|
|
@@ -1491,7 +1516,7 @@
|
|
|
1491
1516
|
class: threads.length ? "count" : "",
|
|
1492
1517
|
title: "Comment on coverage",
|
|
1493
1518
|
onclick: (e) => {
|
|
1494
|
-
if (
|
|
1519
|
+
if (!canWrite()) return;
|
|
1495
1520
|
popover(commentPopover({ type: "document", blockId: COVERAGE_BLOCK }), {
|
|
1496
1521
|
x: e.pageX + 10,
|
|
1497
1522
|
y: e.pageY
|
|
@@ -1556,7 +1581,7 @@
|
|
|
1556
1581
|
class: threads.length ? "count" : "",
|
|
1557
1582
|
title: "Comment on the trust boundaries",
|
|
1558
1583
|
onclick: (e) => {
|
|
1559
|
-
if (
|
|
1584
|
+
if (!canWrite()) return;
|
|
1560
1585
|
popover(commentPopover({ type: "document", blockId: SECURITY_BLOCK }), {
|
|
1561
1586
|
x: e.pageX + 10,
|
|
1562
1587
|
y: e.pageY
|
|
@@ -1664,7 +1689,7 @@
|
|
|
1664
1689
|
class: threads.length ? "count" : "",
|
|
1665
1690
|
title: "Comment on this block",
|
|
1666
1691
|
onclick: (e) => {
|
|
1667
|
-
if (
|
|
1692
|
+
if (!canWrite()) return;
|
|
1668
1693
|
popover(commentPopover({ type: "document", blockId: b.id }), {
|
|
1669
1694
|
x: e.pageX + 10,
|
|
1670
1695
|
y: e.pageY
|
|
@@ -1759,7 +1784,7 @@
|
|
|
1759
1784
|
if (!sel || sel.isCollapsed || !sel.rangeCount) return remove();
|
|
1760
1785
|
const range = sel.getRangeAt(0);
|
|
1761
1786
|
const block = (range.commonAncestorContainer instanceof Element ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement)?.closest(".block");
|
|
1762
|
-
if (!block || !docEl.contains(block) ||
|
|
1787
|
+
if (!block || !docEl.contains(block) || !canWrite()) return remove();
|
|
1763
1788
|
const quote = sel.toString().trim();
|
|
1764
1789
|
if (!quote) return remove();
|
|
1765
1790
|
const rect = range.getBoundingClientRect();
|
|
@@ -1897,12 +1922,12 @@
|
|
|
1897
1922
|
{ class: "muted" },
|
|
1898
1923
|
`${d.hunks.reduce((n, hk) => n + hk.rows.filter((r) => r.type === "add").length, 0)} added \xB7 ${d.hunks.reduce((n, hk) => n + hk.rows.filter((r) => r.type === "del").length, 0)} deleted`
|
|
1899
1924
|
),
|
|
1900
|
-
h(
|
|
1925
|
+
canWrite() ? h(
|
|
1901
1926
|
"span",
|
|
1902
1927
|
{ class: "muted hint" },
|
|
1903
1928
|
"click a line number to comment \xB7 shift-click or select text for a range"
|
|
1904
|
-
),
|
|
1905
|
-
|
|
1929
|
+
) : null,
|
|
1930
|
+
canWrite() ? h(
|
|
1906
1931
|
"button",
|
|
1907
1932
|
{
|
|
1908
1933
|
class: "small",
|
|
@@ -2601,18 +2626,21 @@
|
|
|
2601
2626
|
);
|
|
2602
2627
|
})
|
|
2603
2628
|
) : null,
|
|
2604
|
-
h(
|
|
2629
|
+
canWrite() ? h(
|
|
2605
2630
|
"div",
|
|
2606
2631
|
{ style: { marginTop: "12px" } },
|
|
2607
2632
|
h(
|
|
2608
2633
|
"button",
|
|
2609
2634
|
{
|
|
2610
2635
|
class: "small",
|
|
2611
|
-
onclick: (e) => popover(commentPopover({ type: "map", node: n.id }), {
|
|
2636
|
+
onclick: (e) => popover(commentPopover({ type: "map", node: n.id }), {
|
|
2637
|
+
x: e.pageX,
|
|
2638
|
+
y: e.pageY + 8
|
|
2639
|
+
})
|
|
2612
2640
|
},
|
|
2613
2641
|
"Comment on this node"
|
|
2614
2642
|
)
|
|
2615
|
-
),
|
|
2643
|
+
) : null,
|
|
2616
2644
|
threads.length ? h(
|
|
2617
2645
|
"div",
|
|
2618
2646
|
{ class: "thread-pins", style: { marginTop: "10px" } },
|
|
@@ -2956,15 +2984,6 @@
|
|
|
2956
2984
|
});
|
|
2957
2985
|
app.append(topbar, banner, h("div", { class: "main" }, center, resizer, side));
|
|
2958
2986
|
}
|
|
2959
|
-
function applyTheme() {
|
|
2960
|
-
let style = document.getElementById("review-theme");
|
|
2961
|
-
if (!style) {
|
|
2962
|
-
style = document.createElement("style");
|
|
2963
|
-
style.id = "review-theme";
|
|
2964
|
-
document.head.appendChild(style);
|
|
2965
|
-
}
|
|
2966
|
-
style.textContent = state.data?.theme?.css ?? "";
|
|
2967
|
-
}
|
|
2968
2987
|
function renderTopbar() {
|
|
2969
2988
|
clear(topbar);
|
|
2970
2989
|
const d = state.data;
|
|
@@ -3000,10 +3019,10 @@
|
|
|
3000
3019
|
)
|
|
3001
3020
|
);
|
|
3002
3021
|
const identity = h("div", { class: "bar-row bar-identity" }, [
|
|
3003
|
-
h("a", { href: "/", class: "brand", title: "All reviews" }, "thurview"),
|
|
3022
|
+
published ? h("span", { class: "brand" }, "thurview") : h("a", { href: "/", class: "brand", title: "All reviews" }, "thurview"),
|
|
3004
3023
|
h("span", { class: "title", title: r.title }, r.title),
|
|
3005
3024
|
h("span", { class: `badge ${statusClass2(r.status)}` }, r.status),
|
|
3006
|
-
r.revision > 1 ? revSel : h("span", { class: "badge bar-rev" }, `rev ${r.revision}`),
|
|
3025
|
+
r.revision > 1 && !published ? revSel : h("span", { class: "badge bar-rev" }, `rev ${r.revision}`),
|
|
3007
3026
|
h(
|
|
3008
3027
|
"span",
|
|
3009
3028
|
{
|
|
@@ -3034,12 +3053,12 @@
|
|
|
3034
3053
|
},
|
|
3035
3054
|
`Threads${open ? ` \xB7 ${open}` : ""}`
|
|
3036
3055
|
),
|
|
3037
|
-
|
|
3056
|
+
readOnly() ? null : h(
|
|
3038
3057
|
"button",
|
|
3039
3058
|
{ class: pending ? "primary" : "ok", onclick: () => submitDialog() },
|
|
3040
3059
|
pending ? `Submit${pending ? ` (${pending})` : ""}` : "Decide"
|
|
3041
3060
|
),
|
|
3042
|
-
h(
|
|
3061
|
+
published ? null : h(
|
|
3043
3062
|
"button",
|
|
3044
3063
|
{ class: "ghost bar-more", title: "More", onclick: (e) => moreMenu(e) },
|
|
3045
3064
|
"\u22EF"
|
|
@@ -3047,6 +3066,19 @@
|
|
|
3047
3066
|
]);
|
|
3048
3067
|
append(topbar, [identity, actions]);
|
|
3049
3068
|
}
|
|
3069
|
+
var THEME_PICKS = ["system", "light", "dark"];
|
|
3070
|
+
var themeLabel = () => `Theme: ${document.documentElement.dataset["themePick"] ?? "system"}`;
|
|
3071
|
+
function pickTheme(item) {
|
|
3072
|
+
const now = document.documentElement.dataset["themePick"] ?? "system";
|
|
3073
|
+
const next = THEME_PICKS[(THEME_PICKS.indexOf(now) + 1) % THEME_PICKS.length];
|
|
3074
|
+
try {
|
|
3075
|
+
localStorage.setItem("thurview.theme", next);
|
|
3076
|
+
} catch {
|
|
3077
|
+
}
|
|
3078
|
+
window.thurviewTheme?.(next);
|
|
3079
|
+
const label = [...item.querySelectorAll(".item")].find((e) => e.textContent?.startsWith("Theme"));
|
|
3080
|
+
if (label) label.textContent = themeLabel();
|
|
3081
|
+
}
|
|
3050
3082
|
function moreMenu(e) {
|
|
3051
3083
|
const r = state.data.review;
|
|
3052
3084
|
const box = h(
|
|
@@ -3086,7 +3118,7 @@
|
|
|
3086
3118
|
h(
|
|
3087
3119
|
"button",
|
|
3088
3120
|
{
|
|
3089
|
-
style: { background: "var(--del)", color: "
|
|
3121
|
+
style: { background: "var(--del)", color: "var(--on-fill)" },
|
|
3090
3122
|
onclick: async () => {
|
|
3091
3123
|
await api.remove(state.id);
|
|
3092
3124
|
location.href = "/";
|
|
@@ -3101,15 +3133,11 @@
|
|
|
3101
3133
|
},
|
|
3102
3134
|
"Delete review"
|
|
3103
3135
|
),
|
|
3136
|
+
h("div", { class: "item", onclick: () => pickTheme(box) }, themeLabel()),
|
|
3104
3137
|
h(
|
|
3105
3138
|
"div",
|
|
3106
3139
|
{ class: "item muted" },
|
|
3107
3140
|
kind() === "review" ? `base ${r.pins.base.slice(0, 12)} \xB7 head ${r.pins.head.slice(0, 12)}` : `commit ${r.pins.head.slice(0, 12)}`
|
|
3108
|
-
),
|
|
3109
|
-
h(
|
|
3110
|
-
"div",
|
|
3111
|
-
{ class: "item muted" },
|
|
3112
|
-
`theme: ${state.data.theme?.name ?? "default"}${state.data.theme?.source ? ` (${state.data.theme.source})` : ""}`
|
|
3113
3141
|
)
|
|
3114
3142
|
);
|
|
3115
3143
|
Promise.resolve().then(() => (init_dom(), dom_exports)).then(({ popover: popover2 }) => popover2(box, { x: e.pageX - 200, y: e.pageY + 10 }));
|
|
@@ -3146,7 +3174,12 @@
|
|
|
3146
3174
|
banner.hidden = true;
|
|
3147
3175
|
clear(banner);
|
|
3148
3176
|
const d = state.data;
|
|
3149
|
-
if (
|
|
3177
|
+
if (published) {
|
|
3178
|
+
banner.hidden = false;
|
|
3179
|
+
banner.append(
|
|
3180
|
+
`Published copy of revision ${d.review.revision}, ${d.review.status}. Read only: nothing here reaches the agent.`
|
|
3181
|
+
);
|
|
3182
|
+
} else if (state.viewingRevision !== null) {
|
|
3150
3183
|
banner.hidden = false;
|
|
3151
3184
|
banner.append(
|
|
3152
3185
|
`Viewing revision ${state.viewingRevision} of ${d.review.revision}. Read only.`,
|
|
@@ -3249,7 +3282,6 @@
|
|
|
3249
3282
|
document.title = `${state.data.review.title} \xB7 thurview`;
|
|
3250
3283
|
if (state.params.get("side") === "threads") state.side = { kind: "threads" };
|
|
3251
3284
|
on("data", () => {
|
|
3252
|
-
applyTheme();
|
|
3253
3285
|
renderTopbar();
|
|
3254
3286
|
renderBanner();
|
|
3255
3287
|
renderCenter();
|
|
@@ -3275,11 +3307,16 @@
|
|
|
3275
3307
|
});
|
|
3276
3308
|
window.matchMedia(NARROW).addEventListener("change", () => emit("view"));
|
|
3277
3309
|
emit("data");
|
|
3310
|
+
if (published) {
|
|
3311
|
+
document.documentElement.classList.add("published");
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3278
3314
|
connectEvents();
|
|
3279
3315
|
pollPresence();
|
|
3280
3316
|
}
|
|
3281
3317
|
var m = /^\/review\/([^/]+)/.exec(location.pathname);
|
|
3282
|
-
if (
|
|
3318
|
+
if (published) void reviewPage(published.payload.review.id);
|
|
3319
|
+
else if (m) void reviewPage(m[1]);
|
|
3283
3320
|
else void home();
|
|
3284
3321
|
})();
|
|
3285
3322
|
//# sourceMappingURL=app.js.map
|