visualproof 0.2.0 → 0.4.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 +88 -33
- package/dist/assets/tips/phone-fingers.light.mp4 +0 -0
- package/dist/assets/tips/phone-fingers.light.webp +0 -0
- package/dist/assets/tips/tour-agent.webp +0 -0
- package/dist/assets/tips/tour-arrange.webp +0 -0
- package/dist/assets/tips/tour-artwork.webp +0 -0
- package/dist/assets/tips/tour-checks.webp +0 -0
- package/dist/assets/tips/tour-export.webp +0 -0
- package/dist/assets/tips/tour-keys.webp +0 -0
- package/dist/assets/tips/tour-library.webp +0 -0
- package/dist/assets/tips/tour-project.webp +0 -0
- package/dist/assets/tips/tour-review.webp +0 -0
- package/dist/assets/tips/tour-share.webp +0 -0
- package/dist/assets/tips/tour-source.webp +0 -0
- package/dist/assets/tips/tour-target.webp +0 -0
- package/dist/assets/tips/tour-view.webp +0 -0
- package/dist/assets/tips/tour.json +289 -0
- package/dist/assets/tips/tour.webp +0 -0
- package/dist/assets/webfonts/LICENSE-Noto.txt +95 -0
- package/dist/assets/webfonts/metrics.json +1 -0
- package/dist/assets/webfonts/ttf/jost-400-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/jost-500-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/noto-sans-math.ttf +0 -0
- package/dist/assets/webfonts/ttf/noto-sans.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-sans-3-400-italic.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-sans-3-400-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-sans-3-500-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-sans-3-600-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-sans-3-700-normal.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-serif-4-400-italic.ttf +0 -0
- package/dist/assets/webfonts/ttf/source-serif-4-600-italic.ttf +0 -0
- package/dist/mcp/visualproof-mcp.mjs +3 -1
- package/dist/server/agent.js +94 -91
- package/dist/server/agentdocs.js +1 -1
- package/dist/server/artcmd.js +335 -0
- package/dist/server/children.js +85 -0
- package/dist/server/doctor.js +105 -42
- package/dist/server/events.js +10 -0
- package/dist/server/export.js +11 -4
- package/dist/server/extract.js +3 -2
- package/dist/server/figures.js +3 -2
- package/dist/server/film.js +380 -0
- package/dist/server/history.js +11 -1
- package/dist/server/hover.js +8 -5
- package/dist/server/live.js +72 -9
- package/dist/server/main.js +352 -23
- package/dist/server/mcptools.js +4 -1
- package/dist/server/media.js +114 -0
- package/dist/server/pipeline.js +21 -10
- package/dist/server/poppler.js +3 -2
- package/dist/server/readonlygate.js +4 -4
- package/dist/server/review.js +4 -1
- package/dist/server/reviewapi.js +35 -1
- package/dist/server/snapshot.js +10 -1
- package/dist/server/sourcestore.js +3 -2
- package/dist/server/status.js +3 -2
- package/dist/server/supervise.js +219 -0
- package/dist/server/updates.js +951 -0
- package/dist/server/usage.js +3 -1
- package/dist/server/version.js +183 -0
- package/dist/server/view.js +0 -7
- package/dist/server/views.js +24 -0
- package/dist/shared/artfonts.js +81 -0
- package/dist/shared/artrecipe.js +316 -0
- package/dist/shared/collab.js +7 -2
- package/dist/shared/declfind.js +95 -0
- package/dist/shared/lostfound.js +1 -0
- package/dist/shared/pastgraph.js +6 -6
- package/dist/shared/review.js +62 -5
- package/dist/shared/timeline.js +5 -0
- package/dist/web/bundle/app.js +3006 -413
- package/dist/web/bundle/edges.js +1 -1
- package/dist/web/index.html +13 -1
- package/dist/web/src/agentmark.js +14 -0
- package/dist/web/src/artwork.js +1000 -0
- package/dist/web/src/bundles.js +151 -0
- package/dist/web/src/canvas.js +3716 -0
- package/dist/web/src/drag.js +61 -0
- package/dist/web/src/edgequeue.js +204 -0
- package/dist/web/src/gl/gl.js +123 -0
- package/dist/web/src/gl/renderer.js +451 -0
- package/dist/web/src/halo.js +95 -0
- package/dist/web/src/highlight.js +108 -0
- package/dist/web/src/overview.js +393 -0
- package/dist/web/src/reviewqueue.js +250 -0
- package/dist/web/src/selection.js +272 -0
- package/dist/web/src/status.js +80 -0
- package/dist/web/src/visitors.js +318 -0
- package/dist/web/src/visits.js +174 -0
- package/dist/web/style.css +196 -1
- package/docs/agent-api/README.md +1 -1
- package/docs/agent-api/export.md +41 -2
- package/docs/agent-api/history.md +5 -3
- package/docs/agent-api/mcp.md +1 -1
- package/docs/agent-api/reading.md +2 -1
- package/package.json +5 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// The reader's visitors laid on the board's scene (ViewSpec decision 27, the owner's decision of
|
|
2
|
+
// 2026-10-11): each in its own place in the cluster hierarchy, the clusters it needs added as
|
|
3
|
+
// temporary ones (web/src/visitors.ts, planVisitors), the outermost of them in the deepest cluster the
|
|
4
|
+
// board draws, at a spot chosen once.
|
|
5
|
+
//
|
|
6
|
+
// Making room: what a visitor brings goes where its cluster has free room, nearest the declarations it
|
|
7
|
+
// has references with; where the cluster has none, against its rim where it covers least, and what it
|
|
8
|
+
// covers makes way, as from a drop, the circle growing only where that cannot fit (web/src/scene.ts,
|
|
9
|
+
// setHeld). Nothing of it is a pin or the board's: when the visitors go, every place comes back.
|
|
10
|
+
//
|
|
11
|
+
// A spot is in its cluster's coordinates and carried to followers with the trail (PresenceView), so a
|
|
12
|
+
// follower draws the temporary clusters where the leader does; the rest — which clusters, nested how,
|
|
13
|
+
// packed how — is a function of the visitors and the board, the same on every page.
|
|
14
|
+
import { LEAF_R } from "./scene.js";
|
|
15
|
+
import { freeSpot, packPlan, planVisitors, rimSpot } from "./visitors.js";
|
|
16
|
+
/** How far outside what it holds a cluster's circle is drawn (web/src/scene.ts, computeExtents). */
|
|
17
|
+
const DRAWN_MARGIN = 6;
|
|
18
|
+
/** Whether a declaration's name is a namespace too, on a board whose declarations are `names` (the
|
|
19
|
+
* outside world's left out), the visitors' among them: a structure then sits in its own namespace's
|
|
20
|
+
* cluster, as the board would draw it (shared/layout.ts, pathOf). */
|
|
21
|
+
export function prefixTest(g, visitors) {
|
|
22
|
+
const memo = new Map();
|
|
23
|
+
return (name) => {
|
|
24
|
+
let hit = memo.get(name);
|
|
25
|
+
if (hit !== undefined)
|
|
26
|
+
return hit;
|
|
27
|
+
const head = `${name}.`;
|
|
28
|
+
hit = visitors.some((v) => v.startsWith(head));
|
|
29
|
+
for (let i = 0; !hit && i < g.n; i++)
|
|
30
|
+
if (!g.isExt(i) && g.nodes[i].name.startsWith(head))
|
|
31
|
+
hit = true;
|
|
32
|
+
memo.set(name, hit);
|
|
33
|
+
return hit;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Where the visitors `vs` belong on a board (web/src/visitors.ts, planVisitors): its layout `L` by
|
|
37
|
+
* `mode`, the levels `hidden`, its graph `g`. */
|
|
38
|
+
export function planOn(L, mode, hidden, g, vs) {
|
|
39
|
+
const drawn = new Map(L.clusters.map((c, k) => [c.key, k]));
|
|
40
|
+
return planVisitors(mode, drawn, new Set(hidden), vs, prefixTest(g, vs.map((v) => v.name)));
|
|
41
|
+
}
|
|
42
|
+
/** The board's clusters round the places of the visitors `chosen` (indices of the trail): the folded
|
|
43
|
+
* ones among them open for the reader while those are selected (ViewSpec decision 27). */
|
|
44
|
+
export function openFor(plan, parent, chosen) {
|
|
45
|
+
const out = new Set();
|
|
46
|
+
for (const k of chosen)
|
|
47
|
+
for (let c = plan.at[k]?.in ?? -1; c >= 0; c = parent[c])
|
|
48
|
+
out.add(c);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/** Place the visitors `vs` (the trail, oldest first) on a board as `plan` says (planOn): its layout
|
|
52
|
+
* `L`, its scene `S` as the board arranges it. An item keeps the place its oldest visitor has in
|
|
53
|
+
* that cluster; one with none is given a spot — in the order its oldest visitor came, so that a newer
|
|
54
|
+
* one never moves an older one — where `ok` (a spot of the world and a radius) finds no cluster's
|
|
55
|
+
* name written. */
|
|
56
|
+
export function placeVisits(S, L, plan, vs, ok = () => true) {
|
|
57
|
+
const names = vs.map((v) => v.name);
|
|
58
|
+
const pack = packPlan(plan, names);
|
|
59
|
+
const keyOf = (c) => (c < 0 ? "" : L.clusters[c].key);
|
|
60
|
+
// the outermost items, and which visitors each holds
|
|
61
|
+
const items = [];
|
|
62
|
+
const ofTemp = new Map();
|
|
63
|
+
plan.temps.forEach((t, k) => {
|
|
64
|
+
if (t.up >= 0)
|
|
65
|
+
return;
|
|
66
|
+
ofTemp.set(k, items.length);
|
|
67
|
+
items.push({ in: t.in, temp: k, visitor: -1, at: [0, 0], r: pack.r[k], slot: 0 });
|
|
68
|
+
});
|
|
69
|
+
const item = plan.at.map((a, k) => {
|
|
70
|
+
if (a.up < 0) {
|
|
71
|
+
items.push({ in: a.in, temp: -1, visitor: k, at: [0, 0], r: LEAF_R, slot: 0 });
|
|
72
|
+
return items.length - 1;
|
|
73
|
+
}
|
|
74
|
+
let t = a.up;
|
|
75
|
+
while (plan.temps[t].up >= 0)
|
|
76
|
+
t = plan.temps[t].up;
|
|
77
|
+
return ofTemp.get(t);
|
|
78
|
+
});
|
|
79
|
+
const members = items.map(() => []);
|
|
80
|
+
item.forEach((j, k) => members[j].push(k));
|
|
81
|
+
// in the order their oldest visitors came
|
|
82
|
+
const order = items.map((_, j) => j).sort((a, b) => members[a][0] - members[b][0]);
|
|
83
|
+
const placed = new Map();
|
|
84
|
+
for (const j of order) {
|
|
85
|
+
const it = items[j];
|
|
86
|
+
const kept = members[j].map((k) => vs[k].place).find((p) => p && p.in === keyOf(it.in));
|
|
87
|
+
if (kept)
|
|
88
|
+
it.at = [kept.at[0], kept.at[1]];
|
|
89
|
+
else {
|
|
90
|
+
const room = S.roomOf(it.in);
|
|
91
|
+
// a sub-cluster's circle is drawn a little outside what it holds (Scene, computeExtents)
|
|
92
|
+
const bodies = [...S.discsIn(it.in).map((d) => (d.r > LEAF_R ? { ...d, r: d.r + DRAWN_MARGIN } : d)), ...(placed.get(it.in) ?? []).map((o) => ({ x: o.at[0], y: o.at[1], r: o.r }))];
|
|
93
|
+
// towards the declarations it has references with, as the board arranges them
|
|
94
|
+
const [ox, oy] = S.anchorWorld(it.in);
|
|
95
|
+
let sx = 0, sy = 0, m = 0;
|
|
96
|
+
for (const k of members[j])
|
|
97
|
+
for (const i of vs[k].near) {
|
|
98
|
+
const [cx, cy] = S.anchorWorld(S.nodeCluster[i]);
|
|
99
|
+
sx += cx + S.ancNX[i];
|
|
100
|
+
sy += cy + S.ancNY[i];
|
|
101
|
+
m++;
|
|
102
|
+
}
|
|
103
|
+
const prefer = m ? [sx / m - ox, sy / m - oy] : [0, 0];
|
|
104
|
+
const okHere = (x, y) => ok(ox + x, oy + y, it.r);
|
|
105
|
+
it.at = freeSpot(bodies, room, it.r, prefer, okHere) ?? rimSpot(bodies, room, it.r, prefer);
|
|
106
|
+
}
|
|
107
|
+
const list = placed.get(it.in) ?? [];
|
|
108
|
+
it.slot = list.length;
|
|
109
|
+
list.push(it);
|
|
110
|
+
placed.set(it.in, list);
|
|
111
|
+
}
|
|
112
|
+
const held = new Map();
|
|
113
|
+
for (const [c, list] of placed)
|
|
114
|
+
held.set(c, list.map((o) => ({ x: o.at[0], y: o.at[1], r: o.r })));
|
|
115
|
+
const places = item.map((j) => ({ in: keyOf(items[j].in), at: [items[j].at[0], items[j].at[1]] }));
|
|
116
|
+
return { plan, pack, items, item, places, held };
|
|
117
|
+
}
|
|
118
|
+
export function visitWorld(V, S) {
|
|
119
|
+
const T = V.plan.temps.length, K = V.plan.at.length;
|
|
120
|
+
const W = { tx: new Float64Array(T), ty: new Float64Array(T), tr: new Float64Array(T), trep: new Int32Array(T), vx: new Float64Array(K), vy: new Float64Array(K), vrep: new Int32Array(K) };
|
|
121
|
+
const centre = (it) => {
|
|
122
|
+
const at = S.heldAt.get(it.in);
|
|
123
|
+
const hx = at ? at[2 * it.slot] : it.at[0], hy = at ? at[2 * it.slot + 1] : it.at[1];
|
|
124
|
+
if (it.in < 0)
|
|
125
|
+
return [hx, hy, 1];
|
|
126
|
+
const s = S.scale[it.in];
|
|
127
|
+
return [S.cwx[it.in] + hx * s, S.cwy[it.in] + hy * s, s];
|
|
128
|
+
};
|
|
129
|
+
const scaleOf = (c) => (c < 0 ? 1 : S.scale[c]);
|
|
130
|
+
const repOf = (c) => (c < 0 ? -1 : S.rep[c]);
|
|
131
|
+
for (let t = 0; t < T; t++) {
|
|
132
|
+
const tc = V.plan.temps[t];
|
|
133
|
+
const s = scaleOf(tc.in);
|
|
134
|
+
if (tc.up < 0) {
|
|
135
|
+
const it = V.items[V.items.findIndex((x) => x.temp === t)];
|
|
136
|
+
[W.tx[t], W.ty[t]] = centre(it);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
W.tx[t] = W.tx[tc.up] + V.pack.x[t] * s;
|
|
140
|
+
W.ty[t] = W.ty[tc.up] + V.pack.y[t] * s;
|
|
141
|
+
}
|
|
142
|
+
W.tr[t] = V.pack.r[t] * s;
|
|
143
|
+
W.trep[t] = repOf(tc.in);
|
|
144
|
+
}
|
|
145
|
+
for (let k = 0; k < K; k++) {
|
|
146
|
+
const a = V.plan.at[k];
|
|
147
|
+
if (a.up >= 0) {
|
|
148
|
+
const s = scaleOf(a.in);
|
|
149
|
+
W.vx[k] = W.tx[a.up] + V.pack.vx[k] * s;
|
|
150
|
+
W.vy[k] = W.ty[a.up] + V.pack.vy[k] * s;
|
|
151
|
+
}
|
|
152
|
+
else
|
|
153
|
+
[W.vx[k], W.vy[k]] = centre(V.items[V.item[k]]);
|
|
154
|
+
W.vrep[k] = repOf(a.in);
|
|
155
|
+
}
|
|
156
|
+
return W;
|
|
157
|
+
}
|
|
158
|
+
/** A visitor's chain of position ids up to the top level, as its edges are bundled through the
|
|
159
|
+
* hierarchy (web/src/bundles.ts, joinChains): itself, its temporary clusters, the board's clusters
|
|
160
|
+
* round it — or, hidden in a folded cluster's card, that card and the clusters round it. Ids past the
|
|
161
|
+
* scene's (`base` = clusters and declarations) are the visitors' own: a temporary cluster t at
|
|
162
|
+
* base + t, a visitor k at base + temps + k. */
|
|
163
|
+
export function visitChain(V, W, S, base, k) {
|
|
164
|
+
const out = [];
|
|
165
|
+
const a = V.plan.at[k];
|
|
166
|
+
if (W.vrep[k] < 0) {
|
|
167
|
+
out.push(base + V.plan.temps.length + k);
|
|
168
|
+
for (let t = a.up; t >= 0; t = V.plan.temps[t].up)
|
|
169
|
+
out.push(base + t);
|
|
170
|
+
}
|
|
171
|
+
for (let c = W.vrep[k] >= 0 ? W.vrep[k] : a.in; c >= 0; c = S.parent[c])
|
|
172
|
+
out.push(c);
|
|
173
|
+
return out;
|
|
174
|
+
}
|
package/dist/web/style.css
CHANGED
|
@@ -438,6 +438,12 @@ body.art-moving :is(#bar .brand, #bar .library-wrap, #export-btn, #activity-btn,
|
|
|
438
438
|
}
|
|
439
439
|
.sw-review .rv-text { min-height: calc(5 * 1.45em + 12px); }
|
|
440
440
|
.rv-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
441
|
+
/* whom the opinion is for (ReviewSpec decision 8): a name beside the buttons; while it is another
|
|
442
|
+
person's, a coloured line over the box says so */
|
|
443
|
+
.rv-for { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; font-size: 11.5px; color: var(--text-2); white-space: nowrap; }
|
|
444
|
+
.rv-for-in { width: 9em; font: 12px var(--font); color: var(--text); background: var(--surface-solid); border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px; }
|
|
445
|
+
.rv-for-on { margin: 0 0 6px; padding: 5px 9px; border-radius: 7px; font-size: 12px; color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
|
|
446
|
+
.rv-for-on[hidden] { display: none; }
|
|
441
447
|
.rv-btn {
|
|
442
448
|
border: 1px solid var(--line); background: var(--surface-solid); color: var(--text); font: 12px var(--font);
|
|
443
449
|
padding: 4px 10px; border-radius: 7px; cursor: pointer;
|
|
@@ -601,7 +607,7 @@ button.o-trust:hover { color: var(--accent); background: var(--code-bg); }
|
|
|
601
607
|
#windows > * { pointer-events: auto; }
|
|
602
608
|
/* a popover lies above the windows: it was opened last, and goes on a press anywhere else. Those
|
|
603
609
|
inside a bar raise the bar with them while they are open */
|
|
604
|
-
#bar:has(#share-pop:not([hidden]), #results:not([hidden]), #who-menu:not([hidden])), #viewbar:has(#views-menu:not([hidden]), #focus-menu:not([hidden])), #zoom:has(#eyes-pop:not([hidden])) { z-index: 36; }
|
|
610
|
+
#bar:has(#share-pop:not([hidden]), #results:not([hidden]), #who-menu:not([hidden]), #help-menu:not([hidden])), #viewbar:has(#views-menu:not([hidden]), #focus-menu:not([hidden])), #zoom:has(#eyes-pop:not([hidden])) { z-index: 36; }
|
|
605
611
|
/* a source window (web/src/sourcewin.ts): floating, not modal; as large as its code needs until the
|
|
606
612
|
reader moves or resizes it (floatwin.ts sets its place, and its bounds while it is fitted to its
|
|
607
613
|
code), scrolling down beyond that */
|
|
@@ -1955,6 +1961,9 @@ a.link:hover { text-decoration: underline; }
|
|
|
1955
1961
|
.tour-text { font-size: 13px; line-height: 1.5; color: var(--text); }
|
|
1956
1962
|
.tour-actions { display: flex; align-items: center; gap: 6px; margin-top: 14px; }
|
|
1957
1963
|
.tour-gap { flex: 1; }
|
|
1964
|
+
/* the TCB step's three views of one theorem (GuideSpec decision 2) */
|
|
1965
|
+
.tour-seg { display: inline-flex; margin: 0 0 8px; }
|
|
1966
|
+
@media (prefers-reduced-motion: reduce) { .tour-shade, .tour-spot, .tour-card { transition: none; } }
|
|
1958
1967
|
#help { position: fixed; inset: 0; z-index: 45; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.22); }
|
|
1959
1968
|
.help-box { width: min(660px, calc(100vw - 32px)); max-height: calc(100vh - 48px); overflow: auto; outline: none; }
|
|
1960
1969
|
.help-head { display: flex; justify-content: space-between; align-items: center; }
|
|
@@ -1967,6 +1976,7 @@ a.link:hover { text-decoration: underline; }
|
|
|
1967
1976
|
/* the reader's "Open in editor" template, under the keys */
|
|
1968
1977
|
.help-editor, .help-about { display: flex; flex-direction: column; gap: 6px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
|
|
1969
1978
|
.help-note { color: var(--text-2); font-size: 12px; line-height: 1.45; }
|
|
1979
|
+
.help-note a { color: var(--accent); }
|
|
1970
1980
|
.help-note code { font: 11px var(--mono); background: var(--code-bg); padding: 1px 4px; border-radius: 4px; overflow-wrap: anywhere; }
|
|
1971
1981
|
.help-input {
|
|
1972
1982
|
font: 12.5px var(--mono); color: var(--text); background: var(--surface-solid); border: 1px solid var(--line);
|
|
@@ -1982,6 +1992,183 @@ kbd {
|
|
|
1982
1992
|
color: var(--text-2); font: 600 14px var(--font); cursor: pointer;
|
|
1983
1993
|
}
|
|
1984
1994
|
.help-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
1995
|
+
/* the help menu (GuideSpec decision 8): the tips, the keys, the tour; then what the project and
|
|
1996
|
+
VisualProof are, said in it */
|
|
1997
|
+
.help-wrap { position: relative; display: flex; }
|
|
1998
|
+
#help-menu {
|
|
1999
|
+
position: absolute; top: 38px; right: 0; width: min(340px, calc(100vw - 24px)); padding: 6px; border-radius: 14px; z-index: 40;
|
|
2000
|
+
display: flex; flex-direction: column; background: color-mix(in srgb, var(--surface-solid) 96%, transparent);
|
|
2001
|
+
box-shadow: 0 0 0 1px var(--line), var(--shadow-lg);
|
|
2002
|
+
}
|
|
2003
|
+
#help-menu[hidden] { display: none; }
|
|
2004
|
+
.hm-item {
|
|
2005
|
+
display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 7px 10px; border: 0; border-radius: 9px;
|
|
2006
|
+
background: transparent; color: var(--text); font: 13px var(--font); text-align: left; text-decoration: none; cursor: pointer;
|
|
2007
|
+
}
|
|
2008
|
+
.hm-item:hover, .hm-item:focus-visible { background: var(--code-bg); outline: none; }
|
|
2009
|
+
.hm-item[aria-disabled="true"] { cursor: default; }
|
|
2010
|
+
.hm-item[aria-disabled="true"]:hover { background: none; }
|
|
2011
|
+
.hm-t { font-weight: 500; }
|
|
2012
|
+
.hm-h { font-size: 11.5px; font-weight: 600; color: var(--text-2); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
2013
|
+
.hm-d { font-size: 12px; line-height: 1.4; color: var(--text-2); }
|
|
2014
|
+
.hm-d code { font: 11px var(--mono); background: var(--code-bg); padding: 1px 4px; border-radius: 4px; overflow-wrap: anywhere; }
|
|
2015
|
+
.hm-sep { height: 1px; margin: 5px 4px; background: var(--line); }
|
|
2016
|
+
.hm-link { color: var(--accent); }
|
|
2017
|
+
/* the tour has pointed at the tips: a dot on the help until the menu is first opened */
|
|
2018
|
+
.help-btn.has-dot, #more-btn.has-dot { position: relative; }
|
|
2019
|
+
.help-btn.has-dot::after, #more-btn.has-dot::after {
|
|
2020
|
+
content: ""; position: absolute; top: -1px; right: -1px; width: 8px; height: 8px; border-radius: 50%;
|
|
2021
|
+
background: var(--accent); box-shadow: 0 0 0 2px var(--surface-solid);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/* the sheet's tabs: the tips first, then the keys (GuideSpec decision 8) */
|
|
2025
|
+
.help-tabs { display: inline-flex; }
|
|
2026
|
+
.help-tabs button { padding: 4px 14px; font-size: 13px; }
|
|
2027
|
+
.help-pane { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }
|
|
2028
|
+
.help-pane[hidden] { display: none; }
|
|
2029
|
+
/* the Tips tab (GuideSpec decisions 5–8): the tour film with its chapters, then the tips it does not
|
|
2030
|
+
show, one row each. On a larger screen the sheet is as wide as the film needs, within the window */
|
|
2031
|
+
@media (min-width: 641px) {
|
|
2032
|
+
.help-box:has([data-help-pane="tips"]:not([hidden])) { width: min(1340px, calc(100vw - 48px)); }
|
|
2033
|
+
}
|
|
2034
|
+
.help-pane [hidden] { display: none !important; }
|
|
2035
|
+
.tips-filter { font-family: var(--font); }
|
|
2036
|
+
.tips-list { display: flex; flex-direction: column; gap: 14px; }
|
|
2037
|
+
.tips-group { display: flex; flex-direction: column; gap: 6px; }
|
|
2038
|
+
/* a part of the panel with the ⓘ of its tip beside its heading (GuideSpec decision 15) */
|
|
2039
|
+
.sec-row { display: flex; align-items: center; gap: 4px; }
|
|
2040
|
+
.sec-row > .sec-h { flex: 1; min-width: 0; }
|
|
2041
|
+
.p-info { flex: none; width: 22px; height: 22px; border: none; border-radius: 50%; background: none; color: var(--text-3); font-size: 14px; cursor: pointer; }
|
|
2042
|
+
.p-info:hover { color: var(--accent); background: var(--code-bg); }
|
|
2043
|
+
@keyframes tip-flash { from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 60%, transparent); } to { box-shadow: 0 0 0 3px transparent; } }
|
|
2044
|
+
/* the film: the player, its chapters beside it (under it, where the sheet is narrow) */
|
|
2045
|
+
.film-h { font-weight: 650; font-size: 15px; letter-spacing: -0.01em; }
|
|
2046
|
+
.film { display: grid; grid-template-columns: minmax(0, 960px) minmax(240px, 320px); gap: 16px; align-items: start; }
|
|
2047
|
+
@media (max-width: 1100px) { .film { grid-template-columns: minmax(0, 1fr); } }
|
|
2048
|
+
/* the film is light: in the dark theme, a thin frame of the theme's own, so that it does not glare */
|
|
2049
|
+
.film-stage { outline: none; border-radius: 12px; }
|
|
2050
|
+
.film-stage:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 60%, transparent); }
|
|
2051
|
+
.film-frame { position: relative; aspect-ratio: 16 / 10; border-radius: 12px; overflow: hidden; background: #f5f5f7; border: 1px solid var(--line); box-shadow: var(--shadow); }
|
|
2052
|
+
:root[data-theme="dark"] .film-frame { border: 6px solid #2c2c2e; box-shadow: 0 0 0 1px var(--line), var(--shadow); }
|
|
2053
|
+
:root[data-theme="dark"] .film-video, :root[data-theme="dark"] .film-still { filter: brightness(0.92); }
|
|
2054
|
+
.film-video, .film-still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
|
|
2055
|
+
/* the controls over the film: play, captions, larger — light, as the film is */
|
|
2056
|
+
.film-controls { position: absolute; left: 12px; right: 12px; bottom: 12px; display: flex; align-items: center; gap: 8px; pointer-events: none; }
|
|
2057
|
+
.film-controls button {
|
|
2058
|
+
pointer-events: auto; min-width: 36px; height: 36px; padding: 0 10px; border-radius: 18px; border: 1px solid rgba(0, 0, 0, 0.08);
|
|
2059
|
+
background: rgba(255, 255, 255, 0.86); color: #1d1d1f; font: 600 13px var(--font); box-shadow: var(--shadow); cursor: pointer;
|
|
2060
|
+
backdrop-filter: blur(10px);
|
|
2061
|
+
}
|
|
2062
|
+
.film-controls button:hover { color: var(--accent); }
|
|
2063
|
+
.film-controls [data-film-cc][aria-pressed="false"] { color: #a1a1a6; text-decoration: line-through; }
|
|
2064
|
+
.film-gap { flex: 1; }
|
|
2065
|
+
/* a caption: over the film at the bottom, in the page's own type, a soft pill readable on the light film */
|
|
2066
|
+
.film-video::cue { color: transparent; background: transparent; }
|
|
2067
|
+
.film-cc {
|
|
2068
|
+
position: absolute; left: 50%; bottom: 60px; transform: translateX(-50%); max-width: min(78%, 760px); padding: 6px 14px;
|
|
2069
|
+
border-radius: 14px; background: rgba(255, 255, 255, 0.84); color: #1d1d1f; font: 500 14px/1.4 var(--font); text-align: center;
|
|
2070
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); backdrop-filter: blur(10px); pointer-events: none;
|
|
2071
|
+
}
|
|
2072
|
+
/* the film while the server fetches it: said quietly, over the poster */
|
|
2073
|
+
.film-wait {
|
|
2074
|
+
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 6px 14px; border-radius: 14px;
|
|
2075
|
+
background: rgba(255, 255, 255, 0.84); color: #6e6e73; font: 500 13px/1.4 var(--font); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
2076
|
+
backdrop-filter: blur(10px); pointer-events: none;
|
|
2077
|
+
}
|
|
2078
|
+
.film-controls button[disabled] { opacity: 0.45; cursor: default; }
|
|
2079
|
+
/* a chapter's captions where the film does not play them: listed under its words */
|
|
2080
|
+
.film-cues { margin: 2px 0 0; padding-left: 16px; color: var(--text-2); font-size: 12px; line-height: 1.45; display: flex; flex-direction: column; gap: 2px; }
|
|
2081
|
+
.film-side { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
2082
|
+
.film-list-btn { display: none; align-self: flex-start; }
|
|
2083
|
+
/* large: the film fills the sheet, its chapters beside it (folded away at a press); then the screen */
|
|
2084
|
+
.help-box.film-large { width: calc(100vw - 48px); height: calc(100vh - 48px); max-height: none; }
|
|
2085
|
+
.help-box.film-large [data-help-pane="tips"] > :not([data-film]) { display: none; }
|
|
2086
|
+
.help-box.film-large .film { grid-template-columns: minmax(0, 1fr) minmax(240px, 320px); align-items: start; }
|
|
2087
|
+
.help-box.film-large .film.list-hidden { grid-template-columns: minmax(0, 1fr); }
|
|
2088
|
+
.help-box.film-large .film.list-hidden .film-list { display: none; }
|
|
2089
|
+
.help-box.film-large .film-list-btn { display: inline-flex; padding: 4px 10px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface-solid); color: var(--text-2); font: 12.5px var(--font); cursor: pointer; }
|
|
2090
|
+
.help-box.film-large .film-frame, .help-box.film-large .film-bar { width: min(100%, calc((100vh - 230px) * 1.6)); margin-left: auto; margin-right: auto; }
|
|
2091
|
+
.help-box.film-large .film-cc { font-size: 19.5px; bottom: 70px; padding: 8px 18px; border-radius: 18px; }
|
|
2092
|
+
@media (max-width: 1100px) { .help-box.film-large .film { grid-template-columns: minmax(0, 1fr); } }
|
|
2093
|
+
.film-stage:fullscreen { background: #000; display: flex; flex-direction: column; justify-content: center; padding: 24px; }
|
|
2094
|
+
.film-stage:fullscreen .film-frame { width: min(100%, calc((100vh - 80px) * 1.6)); margin: 0 auto; border: none; }
|
|
2095
|
+
.film-stage:fullscreen .film-cc { font-size: 24px; bottom: 80px; }
|
|
2096
|
+
.film-stage:fullscreen .film-bar { width: min(100%, calc((100vh - 80px) * 1.6)); margin: 8px auto 0; }
|
|
2097
|
+
.film-stage:fullscreen .film-track { background: rgba(255, 255, 255, 0.22); }
|
|
2098
|
+
/* under the player, a segment per chapter: the one playing fills */
|
|
2099
|
+
.film-bar { display: flex; gap: 3px; margin-top: 6px; }
|
|
2100
|
+
.film-seg { flex-basis: 0; flex-shrink: 1; padding: 5px 0; border: none; background: none; cursor: pointer; min-width: 4px; }
|
|
2101
|
+
.film-track { display: block; height: 5px; border-radius: 3px; background: var(--line); overflow: hidden; }
|
|
2102
|
+
.film-seg:hover .film-track { background: color-mix(in srgb, var(--accent) 22%, var(--line)); }
|
|
2103
|
+
.film-fill { display: block; height: 100%; background: var(--accent); transform-origin: left; transform: scaleX(0); }
|
|
2104
|
+
/* the chapters: one row each, the one playing open */
|
|
2105
|
+
.film-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
2106
|
+
.film-item { border: 1px solid transparent; border-radius: 12px; }
|
|
2107
|
+
.film-item.on { border-color: var(--line); background: var(--surface-solid); box-shadow: var(--shadow); }
|
|
2108
|
+
.film-card { display: flex; gap: 10px; align-items: baseline; padding: 7px 10px; border-radius: 11px; cursor: pointer; outline: none; }
|
|
2109
|
+
.film-card:hover { background: var(--code-bg); }
|
|
2110
|
+
.film-item.on .film-card:hover { background: none; }
|
|
2111
|
+
.film-card:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 60%, transparent); }
|
|
2112
|
+
.film-n { flex: none; width: 1.4em; text-align: right; color: var(--text-3); font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
2113
|
+
.film-item.on .film-n { color: var(--accent); font-weight: 600; }
|
|
2114
|
+
.film-title { font-weight: 600; font-size: 13px; }
|
|
2115
|
+
.film-body { display: flex; flex-direction: column; gap: 6px; padding: 0 12px 11px calc(1.4em + 20px); }
|
|
2116
|
+
/* the tips the film does not show: a row each, its title and its first line; one open */
|
|
2117
|
+
.refs-head { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
|
|
2118
|
+
.refs-head .help-h { margin: 0; }
|
|
2119
|
+
.refs-head .tips-filter { flex: 1; max-width: 320px; margin-left: auto; }
|
|
2120
|
+
.refs { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--surface-solid); }
|
|
2121
|
+
.ref + .ref { border-top: 1px solid var(--line); }
|
|
2122
|
+
.ref.flash { animation: tip-flash 1.6s ease-out; }
|
|
2123
|
+
@media (prefers-reduced-motion: reduce) { .ref.flash { animation: none; box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 60%, transparent); } }
|
|
2124
|
+
.ref-head { display: flex; gap: 12px; align-items: baseline; width: 100%; padding: 9px 12px; border: none; background: none; text-align: left; font: inherit; color: var(--text); cursor: pointer; }
|
|
2125
|
+
.ref-head:hover { background: var(--code-bg); }
|
|
2126
|
+
.ref-title { flex: none; font-weight: 600; font-size: 13px; }
|
|
2127
|
+
.ref-line { flex: 1; min-width: 0; color: var(--text-2); font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2128
|
+
.ref.open .ref-line { display: none; }
|
|
2129
|
+
.ref-body { display: flex; flex-direction: column; gap: 6px; padding: 0 12px 12px; }
|
|
2130
|
+
.ref-clip video { display: block; width: 100%; max-height: 70vh; border-radius: 10px; background: #f5f5f7; object-fit: contain; }
|
|
2131
|
+
.tip-title { font-weight: 650; font-size: 13.5px; letter-spacing: -0.01em; }
|
|
2132
|
+
.tip-text { font-size: 12.5px; line-height: 1.45; color: var(--text-2); }
|
|
2133
|
+
.tip-text b { color: var(--text); font-weight: 600; }
|
|
2134
|
+
.tip-acts { display: flex; gap: 12px; margin-top: 2px; font-size: 12.5px; }
|
|
2135
|
+
.tip-acts a { color: var(--accent); text-decoration: none; }
|
|
2136
|
+
.tip-new { font-size: 10.5px; font-weight: 600; color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 999px; padding: 0 6px; vertical-align: 1px; }
|
|
2137
|
+
.tips-off { display: flex; align-items: center; gap: 6px; }
|
|
2138
|
+
.help-update { margin-top: 6px; color: var(--text); }
|
|
2139
|
+
.help-about.flash { animation: tip-flash 1.6s ease-out; border-radius: 8px; }
|
|
2140
|
+
/* a tip in place, beside its control, its arrow pointing at it (GuideSpec decision 9) */
|
|
2141
|
+
#intip {
|
|
2142
|
+
position: fixed; left: 0; top: 0; z-index: 44; width: min(280px, calc(100vw - 16px)); padding: 11px 13px 10px;
|
|
2143
|
+
border-radius: 12px; background: var(--surface-solid); box-shadow: var(--shadow-lg);
|
|
2144
|
+
}
|
|
2145
|
+
#intip::before {
|
|
2146
|
+
content: ""; position: absolute; left: calc(var(--arrow-x, 50%) - 6px); top: -6px; width: 12px; height: 12px;
|
|
2147
|
+
background: var(--surface-solid); transform: rotate(45deg); border-left: 1px solid var(--line); border-top: 1px solid var(--line);
|
|
2148
|
+
}
|
|
2149
|
+
#intip.above::before { top: auto; bottom: -6px; border: none; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
|
|
2150
|
+
.intip-title { font-weight: 650; font-size: 13.5px; margin-bottom: 4px; }
|
|
2151
|
+
.intip-text { font-size: 12.5px; line-height: 1.45; color: var(--text-2); }
|
|
2152
|
+
.intip-text b { color: var(--text); font-weight: 600; }
|
|
2153
|
+
.intip-acts { display: flex; justify-content: flex-end; gap: 6px; margin-top: 10px; }
|
|
2154
|
+
/* the welcome of a first visit (GuideSpec decision 1): beside the side panel, over the legend; it
|
|
2155
|
+
folds into the dot on ? */
|
|
2156
|
+
#welcome {
|
|
2157
|
+
position: fixed; left: 300px; bottom: 64px; z-index: 44; width: min(360px, calc(100vw - 24px)); padding: 14px 16px 12px;
|
|
2158
|
+
border-radius: 14px; background: var(--surface-solid); box-shadow: var(--shadow-lg);
|
|
2159
|
+
transform-origin: top right; transition: transform 0.36s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.36s;
|
|
2160
|
+
}
|
|
2161
|
+
#welcome.folding { opacity: 0; }
|
|
2162
|
+
@media (prefers-reduced-motion: reduce) { #welcome { transition: none; } }
|
|
2163
|
+
.wel-title { font-weight: 700; font-size: 15px; letter-spacing: -0.01em; margin-bottom: 4px; }
|
|
2164
|
+
.wel-text { font-size: 13px; line-height: 1.45; color: var(--text-2); }
|
|
2165
|
+
.wel-acts { display: flex; align-items: center; gap: 6px; margin-top: 12px; }
|
|
2166
|
+
/* What's new, once: at the bottom, in the middle, quiet (GuideSpec decision 11) */
|
|
2167
|
+
#whatsnew {
|
|
2168
|
+
position: fixed; left: 50%; bottom: 56px; transform: translateX(-50%); z-index: 44; display: flex; align-items: center; gap: 4px;
|
|
2169
|
+
padding: 4px 6px 4px 14px; border-radius: 999px; background: var(--surface-solid); box-shadow: var(--shadow);
|
|
2170
|
+
}
|
|
2171
|
+
.news-open { border: none; background: none; color: var(--accent); font: 600 13px var(--font); cursor: pointer; padding: 4px 0; }
|
|
1985
2172
|
|
|
1986
2173
|
/* the zoom buttons: the bottom right of the canvas, beside the panel when it is open */
|
|
1987
2174
|
#zoom {
|
|
@@ -2434,6 +2621,8 @@ span.tm-row { color: var(--text-2); cursor: default; }
|
|
|
2434
2621
|
#more-badge { position: absolute; top: -5px; right: -5px; }
|
|
2435
2622
|
#more-pop { position: fixed; top: 58px; right: 8px; width: min(260px, calc(100vw - 16px)); z-index: 46; border-radius: 14px; padding: 6px; display: flex; flex-direction: column; background: var(--surface-solid); box-shadow: 0 0 0 0.5px var(--line), var(--shadow-lg); }
|
|
2436
2623
|
#more-pop[hidden] { display: none; }
|
|
2624
|
+
/* the help menu, from ⋯ → Help: at the top right, as ⋯'s own */
|
|
2625
|
+
#help-menu { position: fixed; top: 58px; right: 8px; z-index: 46; background: var(--surface-solid); }
|
|
2437
2626
|
.more-item { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 12px; border: 0; border-radius: 10px; background: none; color: var(--text); font: 15px var(--font); text-align: left; }
|
|
2438
2627
|
.more-item:active { background: var(--code-bg); }
|
|
2439
2628
|
.more-item svg { flex: none; width: 18px; height: 18px; color: var(--text-2); fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
|
|
@@ -2479,6 +2668,12 @@ span.tm-row { color: var(--text-2); cursor: default; }
|
|
|
2479
2668
|
#eyes-pop kbd { display: none; }
|
|
2480
2669
|
.help-touch .help-row { grid-template-columns: 116px minmax(0, 1fr); font-size: 14px; padding: 5px 0; }
|
|
2481
2670
|
.help-touch .help-g { color: var(--text); font-weight: 600; }
|
|
2671
|
+
.film { grid-template-columns: minmax(0, 1fr); }
|
|
2672
|
+
.refs-head .tips-filter { max-width: none; }
|
|
2673
|
+
#whatsnew { bottom: calc(64px + env(safe-area-inset-bottom)); }
|
|
2674
|
+
/* over the bottom of the screen, where the sheet comes up */
|
|
2675
|
+
#welcome { left: 8px; right: 8px; width: auto; bottom: calc(64px + env(safe-area-inset-bottom)); }
|
|
2676
|
+
.wel-acts { flex-wrap: wrap; }
|
|
2482
2677
|
#zoom #zoom-in, #zoom #zoom-out { display: none; }
|
|
2483
2678
|
#foot { left: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); }
|
|
2484
2679
|
/* under the view bar, the review strip, then the selection bar (--strip-h: main.ts) */
|
package/docs/agent-api/README.md
CHANGED
|
@@ -85,7 +85,7 @@ parameters and answer.
|
|
|
85
85
|
| [following](following.md) | where people are, your own view, links to it, asking to follow, what people see |
|
|
86
86
|
| [views](views.md) | the query language, views, places on the board, pins and undo |
|
|
87
87
|
| [library](library.md) | papers and images, their pages, links to them |
|
|
88
|
-
| [export](export.md) | a view's document: its model, typesetting, its files |
|
|
88
|
+
| [export](export.md) | a view's document: its model, typesetting, its files; a picture (artwork) |
|
|
89
89
|
| [history](history.md) | the timeline, the builds kept, diffs, renames, the lost-and-found |
|
|
90
90
|
| [talking](talking.md) | your inbox, replies, your status line, your state, the conversation |
|
|
91
91
|
| [mcp](mcp.md) | the MCP server: installing it, its tools and their actions, `wait_for_message` |
|
package/docs/agent-api/export.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Exported documents
|
|
2
2
|
|
|
3
|
-
A view's document, read as JSON or typeset as a PDF
|
|
4
|
-
(`model`, `typeset`, `job`, `file`).
|
|
3
|
+
A view's document, read as JSON or typeset as a PDF; and a picture of the board ([Artwork](#artwork)).
|
|
4
|
+
Any level: it only reads. MCP: `export` (`model`, `typeset`, `job`, `file`, `artwork`).
|
|
5
5
|
|
|
6
6
|
## What it holds
|
|
7
7
|
|
|
@@ -118,3 +118,42 @@ kept). 503: no XeLaTeX.
|
|
|
118
118
|
typeset it again (`.tex`, style file, fonts). **409** with `Retry-After: 2` while running, 409 if
|
|
119
119
|
failed, 404 if unknown or gone from the cache. E.g.
|
|
120
120
|
`curl -fsSK ~/.visualproof/curl.cfg "$VP/api/agent/export/file?id=<id>" -o doc.pdf`.
|
|
121
|
+
|
|
122
|
+
## Artwork
|
|
123
|
+
|
|
124
|
+
`POST /api/agent/artwork {recipe, save?}`: a link that opens the board as a picture in the artwork mode, as a **recipe** says, in words that
|
|
125
|
+
hold on any project: one recipe makes the same kind of picture of every codebase. The page that
|
|
126
|
+
opens it draws the picture and, with `save` (`png`, `svg`, `png,svg`), saves the files there once
|
|
127
|
+
the board is still; nothing comes back to the server. The link holds no key (a script enters with
|
|
128
|
+
it: `scripts/artwork-batch.mjs` in VisualProof's repository).
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{ "recipe": { "size": "a2", "orientation": "portrait", "theme": "light", "border": true,
|
|
132
|
+
"names": { "clusters": 1 }, "targets": ["Lib.mainTheorem"],
|
|
133
|
+
"selection": { "name": "Lib.mainTheorem", "mode": "tcb" }, "keys": false, "notes": false,
|
|
134
|
+
"source": true, "title": "Lib", "subtitle": "One line about the work" }, "save": "png,svg" }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Every field is optional; left out, as a new artwork has it.
|
|
138
|
+
|
|
139
|
+
- `size`: `hd`, `4k` (default), `square`, `a3`, `a2` (300 dpi), or `{w, h}`; `orientation`;
|
|
140
|
+
`style` (`board`, `cells`, `stars`); `theme` (`light`, `dark`), `transparent`; `colouring`
|
|
141
|
+
(`kind`, `status`); `border`; `title`, `subtitle` (null: the project's own; `""`: none),
|
|
142
|
+
`titleSize`, `footSize` (hundredths of the height); `source` (git source and commit).
|
|
143
|
+
- `names`: `board`, `clusters`, `none`, or a rule `{clusters, minShare, targets, decls, omit,
|
|
144
|
+
from}`: `clusters` a depth (0: the top level) or keys; a depth names the outside world and the
|
|
145
|
+
clusters holding at least `minShare` of the project (default 0.01), each written only where it
|
|
146
|
+
covers no name already written (`ruled`), never over another; keys, `decls` (full names) and
|
|
147
|
+
`targets` (default true: the marked targets' names) written wherever they fall; `omit` keys or
|
|
148
|
+
names; `from` `none` (default) or `board`.
|
|
149
|
+
- `targets`: `all` (default), `none`, `faded`, or a list of targets marked alone. `keys`, `notes`:
|
|
150
|
+
default true. `edges`: `all` (under a selection, the rest faint), `selection`, `none`.
|
|
151
|
+
- `selection`: `{name, mode}` (`mode` as `POST view`'s, or `refs`, what it uses directly; default
|
|
152
|
+
`tcb`), lit while `lit` (default true). `view`: a view's id; left out, the Project.
|
|
153
|
+
|
|
154
|
+
Answer: `{ok, link, length, size, names: {clusters, decls, ruled}, targets, selection, left, note}`;
|
|
155
|
+
`left`: names or targets beyond what an artwork carries, left out. Saving both files, the page says
|
|
156
|
+
the browser may ask to allow several downloads. 400: a field unknown or wrong; 404: a name, target,
|
|
157
|
+
cluster or view the recipe names that the board has not (`problems`; a target of the review the
|
|
158
|
+
graph no longer has is left out unsaid).
|
|
159
|
+
|
|
@@ -20,7 +20,8 @@ name bare).
|
|
|
20
20
|
Every event with a `by` has `agent`. `by` is null on a `view`, `note` or `undo` event done without a
|
|
21
21
|
name (a private reader's; their opinions, targets, key marks and trust keep their name).
|
|
22
22
|
|
|
23
|
-
- Review's logs (no `id`): `opinion
|
|
23
|
+
- Review's logs (no `id`): `opinion` (`recordedBy`: who typed it for `by`, at one screen), `target`,
|
|
24
|
+
`key` (as marked now: `target`, `name`), `file`
|
|
24
25
|
(`what` added, edited, archived or restored; `sha`, `title` then, `wasTitle` before an edit),
|
|
25
26
|
`link` (`what` made, edited or removed; `id`, `subject`, `label`, `file`; an edit's `changed`
|
|
26
27
|
(of `subject`, `label`, `place`), `wasSubject`, `wasLabel`, and `auto` if VisualProof carried it
|
|
@@ -32,7 +33,8 @@ name (a private reader's; their opinions, targets, key marks and trust keep thei
|
|
|
32
33
|
`note` its id, `text` then, `shown` whether the view shows it by it), `note` (`what` created,
|
|
33
34
|
edited or deleted; `view`, `title` for a view's own note, "" for a project note; `note` its id;
|
|
34
35
|
`text` after, or before a deletion; `carried` `{mode, from, to}` where a build carried a project
|
|
35
|
-
note to the cluster its declarations moved to, an edit by VisualProof)
|
|
36
|
+
note to the cluster its declarations moved to, an edit by VisualProof), `tool` (the VisualProof
|
|
37
|
+
version serving the site changed: `from`, `to`; `setAside`, a version that failed).
|
|
36
38
|
|
|
37
39
|
## History
|
|
38
40
|
|
|
@@ -50,7 +52,7 @@ on a saved graph (`--graph`): 404. MCP: `history` (`snapshots`, `diff`, `moves`,
|
|
|
50
52
|
"current": 2, "diskBytes": 415893 }
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
Oldest first. `at`: when the build finished (newest `.olean`); `approx`: only an extraction made
|
|
55
|
+
Oldest first. `tool`: the VisualProof version that read it. `at`: when the build finished (newest `.olean`); `approx`: only an extraction made
|
|
54
56
|
before the history dates it. `commit`: checked out when read (`dirty`: `.lean` files differed;
|
|
55
57
|
`guessed`: imported, the last commit before it). `missing`: modules not read (unknown at that
|
|
56
58
|
build, not gone). `bytes`: `new` written to keep it, `all` its files. `renames`: found against the
|
package/docs/agent-api/mcp.md
CHANGED
|
@@ -40,7 +40,7 @@ Each wraps the endpoints of a topic, which says what their answers hold. `*`: th
|
|
|
40
40
|
| `library` | `list`, `file`, `text`, `search`, `page`, `clip`, `save`; `add_url`, `add_file`, `edit`, `archive` (advise) | `library/…`, [library](library.md) |
|
|
41
41
|
| `links` | `list`; `make`, `edit`, `remove` (advise) | `GET links`, `POST link`, `link/edit`, `unlink`, [library](library.md) |
|
|
42
42
|
| `history` | `snapshots`, `diff`, `moves`, `last`, `lost`, `archived`; `attach`, `unmove`, `archive` (full) | `history/…`, [history](history.md) |
|
|
43
|
-
| `export` | `model`, `typeset`, `job`, `file` | `export`, `export/job`, `export/file`, [export](export.md) |
|
|
43
|
+
| `export` | `model`, `typeset`, `job`, `file`, `artwork` | `export`, `export/job`, `export/file`, `artwork`, [export](export.md) |
|
|
44
44
|
|
|
45
45
|
Paths are under `/api/agent/`. `GET /api/agent/history/graph` has no tool (it is large: ask it over
|
|
46
46
|
HTTP when you need a past build's whole graph).
|
|
@@ -36,7 +36,8 @@ Everything about one declaration; here `include=print` while Lean loads:
|
|
|
36
36
|
- `axioms`: all its proof and definition reach; `nonStandardAxioms`: beyond the standard three
|
|
37
37
|
(`sorryAx`). `review`: `opinions` (people's latest) and `advice` (agents', `yours: true`), one
|
|
38
38
|
each; `onPresentVersion: false`: on an earlier version; `verified: true`: from someone invited by
|
|
39
|
-
a personal link, `by` their name now; `
|
|
39
|
+
a personal link, `by` their name now; `recordedBy`: typed for `by` by that person, at one screen
|
|
40
|
+
(theirs all the same); `trustedBy`: covering prefixes, or `["standard axiom"]`.
|
|
40
41
|
- `editedSinceCompiled`: file differs from what was compiled (null outside the project); `keyFor`,
|
|
41
42
|
`inTcbOf`: targets it is key for, whose TCB holds it; `usesDirect`, `usedBy.direct`: what it
|
|
42
43
|
refers to by name, what refers to it (`uses`, `users` list them); `usedBy.all`: all reaching it,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "visualproof",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "See a Lean codebase's declaration graph and the TCB of its theorems.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -20,15 +20,19 @@
|
|
|
20
20
|
"build": "node scripts/build.mjs",
|
|
21
21
|
"prepack": "npm run build",
|
|
22
22
|
"serve": "node src/server/main.ts serve",
|
|
23
|
+
"showcase": "node scripts/showcase.mjs",
|
|
24
|
+
"tips:record": "node scripts/tips-record.mjs",
|
|
23
25
|
"typecheck": "tsc --noEmit -p .",
|
|
24
26
|
"test": "node --test --import ./test/setup.ts \"test/*.test.ts\"",
|
|
25
27
|
"test:toolchains": "node scripts/test-toolchains.mjs",
|
|
28
|
+
"test:updates": "node scripts/test-updates.mjs",
|
|
26
29
|
"golden:update": "VP_GOLDEN_UPDATE=1 node --test --import ./test/setup.ts test/compat.test.ts"
|
|
27
30
|
},
|
|
28
31
|
"engines": {
|
|
29
32
|
"node": "^22.18.0 || >=24.2.0"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
35
|
+
"@resvg/resvg-wasm": "2.6.2",
|
|
32
36
|
"d3-hierarchy": "^3.1.2",
|
|
33
37
|
"pdfjs-dist": "6.3.289"
|
|
34
38
|
},
|