visualproof 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +99 -2
- package/dist/assets/fonts/JuliaMono-Regular.ttf +0 -0
- package/dist/assets/fonts/JuliaMono-RegularItalic.ttf +0 -0
- package/dist/assets/fonts/JuliaMono-SemiBold.ttf +0 -0
- package/dist/assets/fonts/LICENSE-JuliaMono.txt +93 -0
- package/dist/assets/latex/visualproof.sty +308 -0
- package/dist/extractor/Extract.lean +1180 -0
- package/dist/extractor/Usage.lean +900 -0
- package/dist/mcp/visualproof-mcp.mjs +476 -0
- package/dist/server/access.js +95 -0
- package/dist/server/agent.js +3235 -0
- package/dist/server/agentdocs.js +35 -0
- package/dist/server/agentlibrary.js +202 -0
- package/dist/server/agentnotes.js +701 -0
- package/dist/server/agentviews.js +703 -0
- package/dist/server/askfollow.js +286 -0
- package/dist/server/boardspots.js +159 -0
- package/dist/server/dirs.js +175 -0
- package/dist/server/doctor.js +186 -0
- package/dist/server/events.js +198 -0
- package/dist/server/export.js +420 -0
- package/dist/server/extract.js +204 -0
- package/dist/server/fetchurl.js +321 -0
- package/dist/server/figures.js +158 -0
- package/dist/server/history.js +741 -0
- package/dist/server/historyapi.js +304 -0
- package/dist/server/hover.js +595 -0
- package/dist/server/identity.js +114 -0
- package/dist/server/invites.js +241 -0
- package/dist/server/layoutthread.js +51 -0
- package/dist/server/library.js +600 -0
- package/dist/server/libraryroute.js +211 -0
- package/dist/server/live.js +1101 -0
- package/dist/server/lostfound.js +199 -0
- package/dist/server/main.js +1394 -0
- package/dist/server/mcptools.js +497 -0
- package/dist/server/pastdoc.js +73 -0
- package/dist/server/pipeline.js +156 -0
- package/dist/server/poppler.js +251 -0
- package/dist/server/presence.js +252 -0
- package/dist/server/project.js +222 -0
- package/dist/server/prompt.js +53 -0
- package/dist/server/readonly.js +152 -0
- package/dist/server/readonlygate.js +123 -0
- package/dist/server/renames.js +268 -0
- package/dist/server/review.js +334 -0
- package/dist/server/reviewapi.js +188 -0
- package/dist/server/snapshot.js +49 -0
- package/dist/server/sourcestore.js +473 -0
- package/dist/server/status.js +360 -0
- package/dist/server/talk.js +476 -0
- package/dist/server/tokens.js +221 -0
- package/dist/server/usage.js +501 -0
- package/dist/server/versions.js +82 -0
- package/dist/server/view.js +157 -0
- package/dist/server/views.js +1167 -0
- package/dist/shared/askfollow.js +46 -0
- package/dist/shared/board.js +332 -0
- package/dist/shared/build.js +2 -0
- package/dist/shared/collab.js +148 -0
- package/dist/shared/comment.js +285 -0
- package/dist/shared/context.js +157 -0
- package/dist/shared/doc/authors.js +98 -0
- package/dist/shared/doc/build.js +358 -0
- package/dist/shared/doc/latex.js +455 -0
- package/dist/shared/doc/markdown.js +159 -0
- package/dist/shared/doc/model.js +37 -0
- package/dist/shared/doc/order.js +107 -0
- package/dist/shared/doc/proof.js +272 -0
- package/dist/shared/doc/sections.js +93 -0
- package/dist/shared/doc/settings.js +75 -0
- package/dist/shared/edgecodec.js +102 -0
- package/dist/shared/editor.js +64 -0
- package/dist/shared/excerpt.js +87 -0
- package/dist/shared/graph.js +16 -0
- package/dist/shared/items.js +92 -0
- package/dist/shared/keys.js +203 -0
- package/dist/shared/layout.js +926 -0
- package/dist/shared/lexer.js +130 -0
- package/dist/shared/library.js +711 -0
- package/dist/shared/lostfound.js +150 -0
- package/dist/shared/moves.js +243 -0
- package/dist/shared/notecard.js +295 -0
- package/dist/shared/noteplace.js +505 -0
- package/dist/shared/palette.js +46 -0
- package/dist/shared/pastgraph.js +95 -0
- package/dist/shared/places.js +80 -0
- package/dist/shared/projectnotes.js +244 -0
- package/dist/shared/query.js +810 -0
- package/dist/shared/review.js +642 -0
- package/dist/shared/reviewqueue.js +333 -0
- package/dist/shared/ring.js +366 -0
- package/dist/shared/scope.js +255 -0
- package/dist/shared/scopeedit.js +81 -0
- package/dist/shared/search.js +191 -0
- package/dist/shared/separate.js +178 -0
- package/dist/shared/sharesnap.js +280 -0
- package/dist/shared/sticky.js +501 -0
- package/dist/shared/timeline.js +73 -0
- package/dist/shared/titles.js +298 -0
- package/dist/shared/viewgraph.js +139 -0
- package/dist/shared/views.js +859 -0
- package/dist/web/bundle/app.js +48733 -0
- package/dist/web/bundle/edges.js +409 -0
- package/dist/web/bundle/layout.js +1010 -0
- package/dist/web/bundle/pdfworker.js +48322 -0
- package/dist/web/index.html +239 -0
- package/dist/web/src/motion.js +430 -0
- package/dist/web/src/past.js +224 -0
- package/dist/web/src/scene.js +787 -0
- package/dist/web/style.css +2312 -0
- package/docs/agent-api/README.md +91 -0
- package/docs/agent-api/advice.md +108 -0
- package/docs/agent-api/export.md +120 -0
- package/docs/agent-api/following.md +140 -0
- package/docs/agent-api/history.md +142 -0
- package/docs/agent-api/library.md +65 -0
- package/docs/agent-api/mcp.md +104 -0
- package/docs/agent-api/notes.md +93 -0
- package/docs/agent-api/reading.md +166 -0
- package/docs/agent-api/requests.md +94 -0
- package/docs/agent-api/talking.md +100 -0
- package/docs/agent-api/tcb.md +135 -0
- package/docs/agent-api/views.md +124 -0
- package/docs/agent-api/words.md +56 -0
- package/package.json +39 -4
- package/test/toolchains.json +5 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
// src/shared/graph.ts
|
|
2
|
+
var SITE_STMT = 1;
|
|
3
|
+
var SITE_BODY = 2;
|
|
4
|
+
var SITE_PROOF = 4;
|
|
5
|
+
var SITE_GRAMMAR = 8;
|
|
6
|
+
var SITE_TCB = SITE_STMT | SITE_BODY;
|
|
7
|
+
|
|
8
|
+
// src/web/src/bundles.ts
|
|
9
|
+
var MAX_POINTS = 8;
|
|
10
|
+
function units(S, k, minPx) {
|
|
11
|
+
const cu = new Int32Array(S.nc).fill(-1);
|
|
12
|
+
for (let c = 0; c < S.nc; c++) {
|
|
13
|
+
const p = S.parent[c];
|
|
14
|
+
if (p >= 0 && cu[p] >= 0) cu[c] = cu[p];
|
|
15
|
+
else if (S.rep[c] === c || S.curR[c] * k < minPx) cu[c] = c;
|
|
16
|
+
}
|
|
17
|
+
const out = new Int32Array(S.n);
|
|
18
|
+
for (let i = 0; i < S.n; i++) {
|
|
19
|
+
const c = S.nodeCluster[i];
|
|
20
|
+
out[i] = c >= 0 && cu[c] >= 0 ? cu[c] : S.nc + i;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
function aggregate(g, unit, sites, keep) {
|
|
25
|
+
const counts = /* @__PURE__ */ new Map();
|
|
26
|
+
const W = 1 << 22;
|
|
27
|
+
for (let u = 0; u < g.n; u++) {
|
|
28
|
+
if (!keep(u)) continue;
|
|
29
|
+
const a = unit[u];
|
|
30
|
+
for (let e = g.outStart[u]; e < g.outStart[u + 1]; e++) {
|
|
31
|
+
if (!(g.outSite[e] & sites)) continue;
|
|
32
|
+
const v = g.outDst[e];
|
|
33
|
+
if (!keep(v)) continue;
|
|
34
|
+
const b = unit[v];
|
|
35
|
+
if (a === b) continue;
|
|
36
|
+
const key = a * W + b;
|
|
37
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const n = counts.size;
|
|
41
|
+
const out = { n, a: new Int32Array(n), b: new Int32Array(n), w: new Int32Array(n) };
|
|
42
|
+
let k = 0;
|
|
43
|
+
for (const [key, w] of counts) {
|
|
44
|
+
const a = Math.floor(key / W);
|
|
45
|
+
out.a[k] = a;
|
|
46
|
+
out.b[k] = key - a * W;
|
|
47
|
+
out.w[k] = w;
|
|
48
|
+
k++;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function topK(idx, score, cap) {
|
|
53
|
+
const n = idx.length;
|
|
54
|
+
if (n <= cap) return idx;
|
|
55
|
+
const s = new Float64Array(n);
|
|
56
|
+
for (let k = 0; k < n; k++) s[k] = score[idx[k]];
|
|
57
|
+
const want = n - cap;
|
|
58
|
+
let lo = 0, hi = n - 1;
|
|
59
|
+
while (lo < hi) {
|
|
60
|
+
const pivot = s[lo + hi >> 1];
|
|
61
|
+
let i = lo, j = hi;
|
|
62
|
+
while (i <= j) {
|
|
63
|
+
while (s[i] < pivot) i++;
|
|
64
|
+
while (s[j] > pivot) j--;
|
|
65
|
+
if (i <= j) {
|
|
66
|
+
const t = s[i];
|
|
67
|
+
s[i] = s[j];
|
|
68
|
+
s[j] = t;
|
|
69
|
+
i++;
|
|
70
|
+
j--;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (want <= j) hi = j;
|
|
74
|
+
else if (want >= i) lo = i;
|
|
75
|
+
else break;
|
|
76
|
+
}
|
|
77
|
+
const threshold = s[want];
|
|
78
|
+
const out = new Int32Array(cap);
|
|
79
|
+
let m = 0;
|
|
80
|
+
for (let k = 0; k < n && m < cap; k++) if (score[idx[k]] > threshold) out[m++] = idx[k];
|
|
81
|
+
for (let k = 0; k < n && m < cap; k++) if (score[idx[k]] === threshold) out[m++] = idx[k];
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function pathOf(S, a, b) {
|
|
85
|
+
const chain = (id) => {
|
|
86
|
+
const out = [id];
|
|
87
|
+
let c = id < S.nc ? S.parent[id] : S.nodeCluster[id - S.nc];
|
|
88
|
+
while (c >= 0) {
|
|
89
|
+
out.push(c);
|
|
90
|
+
c = S.parent[c];
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
};
|
|
94
|
+
return joinChains(chain(a), chain(b));
|
|
95
|
+
}
|
|
96
|
+
function joinChains(ca, cb) {
|
|
97
|
+
const inB = /* @__PURE__ */ new Map();
|
|
98
|
+
cb.forEach((x, k) => inB.set(x, k));
|
|
99
|
+
let path;
|
|
100
|
+
const ka = ca.findIndex((x) => inB.has(x));
|
|
101
|
+
if (ka >= 0) {
|
|
102
|
+
const kb = inB.get(ca[ka]);
|
|
103
|
+
path = [...ca.slice(0, ka + 1), ...cb.slice(0, kb).reverse()];
|
|
104
|
+
if (path.length < 2) path = [ca[0], cb[0]];
|
|
105
|
+
} else {
|
|
106
|
+
path = [...ca, ...[...cb].reverse()];
|
|
107
|
+
}
|
|
108
|
+
if (path.length > MAX_POINTS) {
|
|
109
|
+
const keep = [path[0]];
|
|
110
|
+
for (let k = 1; k < MAX_POINTS - 1; k++) keep.push(path[Math.round(k * (path.length - 1) / (MAX_POINTS - 1))]);
|
|
111
|
+
keep.push(path[path.length - 1]);
|
|
112
|
+
path = keep;
|
|
113
|
+
}
|
|
114
|
+
return path;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/web/src/overview.ts
|
|
118
|
+
var ZOOM_POINT = 0.25;
|
|
119
|
+
function blendLooks(a, b, t, put) {
|
|
120
|
+
const A = t >= 1 ? null : a;
|
|
121
|
+
const B = t <= 0 ? null : b;
|
|
122
|
+
const give = (e, beta, width, alpha, key) => {
|
|
123
|
+
if (alpha > 2e-3) put(e, beta, width, alpha, key);
|
|
124
|
+
};
|
|
125
|
+
if (A)
|
|
126
|
+
for (const [key, x] of A) {
|
|
127
|
+
const y = B?.get(key);
|
|
128
|
+
if (y) give(y, x.beta + (y.beta - x.beta) * t, x.width + (y.width - x.width) * t, x.alpha + (y.alpha - x.alpha) * t, key);
|
|
129
|
+
else give(x, x.beta, x.width, x.alpha * (1 - t), key);
|
|
130
|
+
}
|
|
131
|
+
if (B) {
|
|
132
|
+
for (const [key, y] of B) if (!A?.has(key)) give(y, y.beta, y.width, y.alpha * t, key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function smoothstep(a, b, x) {
|
|
136
|
+
const t = Math.min(1, Math.max(0, (x - a) / (b - a)));
|
|
137
|
+
return t * t * (3 - 2 * t);
|
|
138
|
+
}
|
|
139
|
+
var looksBuffers = (L) => [L.a, L.b, L.path, L.len, L.beta, L.width, L.alpha].map((x) => x.buffer);
|
|
140
|
+
function pack(looks) {
|
|
141
|
+
const n = looks.size;
|
|
142
|
+
const L = {
|
|
143
|
+
n,
|
|
144
|
+
a: new Int32Array(n),
|
|
145
|
+
b: new Int32Array(n),
|
|
146
|
+
path: new Int32Array(8 * n),
|
|
147
|
+
len: new Uint8Array(n),
|
|
148
|
+
beta: new Float64Array(n),
|
|
149
|
+
width: new Float64Array(n),
|
|
150
|
+
alpha: new Float64Array(n)
|
|
151
|
+
};
|
|
152
|
+
let j = 0;
|
|
153
|
+
for (const e of looks.values()) {
|
|
154
|
+
L.a[j] = e.path[0];
|
|
155
|
+
L.b[j] = e.path[e.path.length - 1];
|
|
156
|
+
L.path.set(e.path, 8 * j);
|
|
157
|
+
L.len[j] = e.path.length;
|
|
158
|
+
L.beta[j] = e.beta;
|
|
159
|
+
L.width[j] = e.width;
|
|
160
|
+
L.alpha[j] = e.alpha;
|
|
161
|
+
j++;
|
|
162
|
+
}
|
|
163
|
+
return L;
|
|
164
|
+
}
|
|
165
|
+
var LEVELS_KEPT = 6;
|
|
166
|
+
var OverviewModel = class {
|
|
167
|
+
g = null;
|
|
168
|
+
tree = null;
|
|
169
|
+
toggles = { externals: true, proofs: false };
|
|
170
|
+
levels = /* @__PURE__ */ new Map();
|
|
171
|
+
setGraph(g) {
|
|
172
|
+
this.g = g;
|
|
173
|
+
this.levels.clear();
|
|
174
|
+
}
|
|
175
|
+
setTree(tree, toggles) {
|
|
176
|
+
this.tree = tree;
|
|
177
|
+
this.toggles = { ...toggles };
|
|
178
|
+
this.levels.clear();
|
|
179
|
+
}
|
|
180
|
+
/** How many zoom steps' aggregations are held (for the tests). */
|
|
181
|
+
get levelCount() {
|
|
182
|
+
return this.levels.size;
|
|
183
|
+
}
|
|
184
|
+
/** What the overview draws at zoom point `ask.point` (zoom 2^(point · ZOOM_POINT / 2)), for the
|
|
185
|
+
* camera centred at (x, y), with everything where `places` has it: chosen from the aggregations of
|
|
186
|
+
* the two steps the point lies between, blended as it lies between them — none of the finer over
|
|
187
|
+
* the first fifth of the way, all of it over the last fifth, smoothly between. */
|
|
188
|
+
choose(ask, places) {
|
|
189
|
+
if (!this.g || !this.tree) throw new Error("the overview has no graph or no tree yet");
|
|
190
|
+
const zs = ask.point * ZOOM_POINT;
|
|
191
|
+
const cam = { x: ask.x, y: ask.y, k: Math.pow(2, zs / 2) };
|
|
192
|
+
const lo = Math.floor(zs);
|
|
193
|
+
const up = smoothstep(0.2, 0.8, zs - lo);
|
|
194
|
+
const at = (level) => {
|
|
195
|
+
let v = this.levels.get(level);
|
|
196
|
+
if (v) this.levels.delete(level);
|
|
197
|
+
else v = this.aggregateLevel(level, places);
|
|
198
|
+
this.levels.set(level, v);
|
|
199
|
+
while (this.levels.size > LEVELS_KEPT) this.levels.delete(this.levels.keys().next().value);
|
|
200
|
+
return this.looksOf(v, this.chooseLevel(v, cam, ask, places), ask, places);
|
|
201
|
+
};
|
|
202
|
+
const coarse = up < 1 ? at(lo) : null;
|
|
203
|
+
const fine = up > 0 ? at(lo + 1) : null;
|
|
204
|
+
const looks = /* @__PURE__ */ new Map();
|
|
205
|
+
blendLooks(coarse, fine, up, (e, beta, width, alpha, key) => looks.set(key, { path: e.path, beta, width, alpha }));
|
|
206
|
+
return pack(looks);
|
|
207
|
+
}
|
|
208
|
+
/** The aggregation of zoom step `level`: each node's unit as the camera at 2^(level/2) sees it,
|
|
209
|
+
* and the edges between units, counted. */
|
|
210
|
+
aggregateLevel(level, places) {
|
|
211
|
+
const g = this.g;
|
|
212
|
+
const unit = units({ ...this.tree, curR: places.curR }, Math.pow(2, level / 2), 45);
|
|
213
|
+
const sites = SITE_STMT | SITE_BODY | (this.toggles.proofs ? SITE_PROOF | SITE_GRAMMAR : 0);
|
|
214
|
+
const ext = this.toggles.externals;
|
|
215
|
+
const pairs = aggregate(g, unit, sites, (i) => i < g.projectCount || ext);
|
|
216
|
+
return { pairs, units: Int32Array.from(new Set(unit)) };
|
|
217
|
+
}
|
|
218
|
+
/** Choose what step `v` draws for a camera `cam`. About 0.6 per unit on screen (between 250 and
|
|
219
|
+
* 1,500), so that zoomed in each stays an edge one can follow: those with both ends in view
|
|
220
|
+
* first (heaviest first; among equals the short ones, then those nearest the middle of the
|
|
221
|
+
* screen), then, in up to a fifth of the budget, those leaving the view (heaviest first): what
|
|
222
|
+
* the part in view uses, or is used by, elsewhere. The limit is soft: the next fifth past it is
|
|
223
|
+
* drawn fainter the further past it. */
|
|
224
|
+
chooseLevel(v, cam, ask, S) {
|
|
225
|
+
const P = v.pairs;
|
|
226
|
+
const hw = ask.w / 2 / cam.k, hh = ask.h / 2 / cam.k;
|
|
227
|
+
const x0 = cam.x - hw * 1.25, x1 = cam.x + hw * 1.25, y0 = cam.y - hh * 1.25, y1 = cam.y + hh * 1.25;
|
|
228
|
+
const diag = Math.hypot(hw, hh);
|
|
229
|
+
const nc = this.tree.nc;
|
|
230
|
+
const X = (id) => id < nc ? S.cwx[id] : S.nwx[id - nc];
|
|
231
|
+
const Y = (id) => id < nc ? S.cwy[id] : S.nwy[id - nc];
|
|
232
|
+
const U = v.units;
|
|
233
|
+
let onScreen = 0;
|
|
234
|
+
for (let k = 0; k < U.length; k++) {
|
|
235
|
+
const x = X(U[k]), y = Y(U[k]);
|
|
236
|
+
if (Math.abs(x - cam.x) <= hw && Math.abs(y - cam.y) <= hh) onScreen++;
|
|
237
|
+
}
|
|
238
|
+
const budget = Math.max(250, Math.min(1500, Math.round(0.6 * onScreen)));
|
|
239
|
+
const score = new Float64Array(P.n);
|
|
240
|
+
const both = [];
|
|
241
|
+
const one = [];
|
|
242
|
+
for (let k = 0; k < P.n; k++) {
|
|
243
|
+
const ax = X(P.a[k]), ay = Y(P.a[k]), bx = X(P.b[k]), by = Y(P.b[k]);
|
|
244
|
+
const ia = ax >= x0 && ax <= x1 && ay >= y0 && ay <= y1;
|
|
245
|
+
const ib = bx >= x0 && bx <= x1 && by >= y0 && by <= y1;
|
|
246
|
+
if (!ia && !ib) continue;
|
|
247
|
+
if (ia && ib) {
|
|
248
|
+
const len = Math.hypot(bx - ax, by - ay) / diag;
|
|
249
|
+
const d = Math.hypot((ax + bx) / 2 - cam.x, (ay + by) / 2 - cam.y) / diag;
|
|
250
|
+
score[k] = P.w[k] + 0.55 * (1 - Math.min(1, len)) + 0.4 * (1 - Math.min(1, d));
|
|
251
|
+
both.push(k);
|
|
252
|
+
} else {
|
|
253
|
+
score[k] = P.w[k];
|
|
254
|
+
one.push(k);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const soft = (n2) => Math.ceil(n2 * 1.2);
|
|
258
|
+
const ranked = (idx, cap) => {
|
|
259
|
+
const top = Array.from(topK(Int32Array.from(idx), score, soft(cap))).sort((a, b) => score[b] - score[a]);
|
|
260
|
+
const strength2 = new Float32Array(top.length);
|
|
261
|
+
const span = Math.max(1, soft(cap) - cap);
|
|
262
|
+
for (let r = 0; r < top.length; r++) strength2[r] = r < cap ? 1 : Math.max(0, 1 - (r - cap + 1) / (span + 1));
|
|
263
|
+
return { idx: Int32Array.from(top), strength: strength2 };
|
|
264
|
+
};
|
|
265
|
+
const innerCap = budget - Math.min(Math.round(budget / 5), one.length);
|
|
266
|
+
const inner = ranked(both, innerCap);
|
|
267
|
+
const outer = ranked(one, budget - Math.min(inner.idx.length, innerCap));
|
|
268
|
+
const n = inner.idx.length + outer.idx.length;
|
|
269
|
+
const sel = new Int32Array(n), inside = new Uint8Array(n), strength = new Float32Array(n);
|
|
270
|
+
sel.set(inner.idx);
|
|
271
|
+
sel.set(outer.idx, inner.idx.length);
|
|
272
|
+
inside.fill(1, 0, inner.idx.length);
|
|
273
|
+
strength.set(inner.strength);
|
|
274
|
+
strength.set(outer.strength, inner.idx.length);
|
|
275
|
+
return { sel, inside, strength };
|
|
276
|
+
}
|
|
277
|
+
/** How step `v` draws what was chosen of it, each edge by its units' ids (`a:b`): width and alpha
|
|
278
|
+
* grow with the weight relative to the heaviest chosen of their kind (in view, or leaving it), so
|
|
279
|
+
* zoomed in a single dependency between two declarations reads as clearly as a heavy bundle does
|
|
280
|
+
* zoomed out; an edge past the soft limit fainter by its rank. */
|
|
281
|
+
looksOf(v, chosen, ask, S) {
|
|
282
|
+
const P = v.pairs;
|
|
283
|
+
const T = this.tree;
|
|
284
|
+
const { sel, inside, strength } = chosen;
|
|
285
|
+
let maxIn = 1, maxOut = 1, nIn = 0, n = 0;
|
|
286
|
+
for (let j = 0; j < sel.length; j++) {
|
|
287
|
+
const w = P.w[sel[j]];
|
|
288
|
+
n += strength[j];
|
|
289
|
+
if (inside[j]) {
|
|
290
|
+
maxIn = Math.max(maxIn, w);
|
|
291
|
+
nIn += strength[j];
|
|
292
|
+
} else maxOut = Math.max(maxOut, w);
|
|
293
|
+
}
|
|
294
|
+
const nc = T.nc;
|
|
295
|
+
const X = (id) => id < nc ? S.cwx[id] : S.nwx[id - nc];
|
|
296
|
+
const Y = (id) => id < nc ? S.cwy[id] : S.nwy[id - nc];
|
|
297
|
+
const crowd = Math.min(1, Math.sqrt(400 / Math.max(1, n)));
|
|
298
|
+
const fade = (ask.dim ? DIM : 1) * crowd;
|
|
299
|
+
const floor = 0.05 + 0.3 * Math.min(1, Math.max(0, (1200 - nIn) / 900));
|
|
300
|
+
const looks = /* @__PURE__ */ new Map();
|
|
301
|
+
for (let j = 0; j < sel.length; j++) {
|
|
302
|
+
const k = sel[j];
|
|
303
|
+
const a = P.a[k], b = P.b[k], w = P.w[k];
|
|
304
|
+
const max = inside[j] ? maxIn : maxOut;
|
|
305
|
+
const t = Math.log1p(w) / Math.log1p(max);
|
|
306
|
+
const width = 0.7 + 2 * t * Math.min(1, Math.log1p(max) / Math.log1p(50));
|
|
307
|
+
const alpha = (floor + (0.45 - floor) * t * t) * fade * strength[j];
|
|
308
|
+
const path = pathOf(T, a, b);
|
|
309
|
+
let R = 0;
|
|
310
|
+
for (let q = 1; q < path.length - 1; q++) if (path[q] < nc) R = Math.max(R, S.drawnR[path[q]]);
|
|
311
|
+
const d = Math.hypot(X(b) - X(a), Y(b) - Y(a));
|
|
312
|
+
const beta = R > 0 ? 0.85 * Math.min(1, 2 * d / R) : 0.85;
|
|
313
|
+
looks.set(`${a}:${b}`, { path, beta, width, alpha });
|
|
314
|
+
}
|
|
315
|
+
return looks;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
var DIM = 0.12;
|
|
319
|
+
|
|
320
|
+
// src/web/src/edgeworker.ts
|
|
321
|
+
var EdgeWorkerCore = class {
|
|
322
|
+
model = new OverviewModel();
|
|
323
|
+
base = -1;
|
|
324
|
+
lo = -Infinity;
|
|
325
|
+
hi = Infinity;
|
|
326
|
+
queue = [];
|
|
327
|
+
/** Take one message in; `choose` waits in the queue for `next`. */
|
|
328
|
+
receive(m, post) {
|
|
329
|
+
switch (m.type) {
|
|
330
|
+
case "graph":
|
|
331
|
+
this.model.setGraph({ n: m.n, projectCount: m.projectCount, outStart: m.outStart, outDst: m.outDst, outSite: m.outSite });
|
|
332
|
+
break;
|
|
333
|
+
case "tree":
|
|
334
|
+
this.base = m.base;
|
|
335
|
+
this.model.setTree(
|
|
336
|
+
{ nc: m.nc, n: m.n, parent: m.parent, nodeCluster: m.nodeCluster, rep: m.rep },
|
|
337
|
+
{ externals: m.externals, proofs: m.proofs }
|
|
338
|
+
);
|
|
339
|
+
this.prune(post);
|
|
340
|
+
break;
|
|
341
|
+
case "near":
|
|
342
|
+
if (m.base === this.base) {
|
|
343
|
+
this.lo = m.lo;
|
|
344
|
+
this.hi = m.hi;
|
|
345
|
+
}
|
|
346
|
+
this.prune(post);
|
|
347
|
+
break;
|
|
348
|
+
case "choose":
|
|
349
|
+
this.queue.push(m);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/** Whether a question waits. */
|
|
354
|
+
get waiting() {
|
|
355
|
+
return this.queue.length;
|
|
356
|
+
}
|
|
357
|
+
/** Drop what is no longer wanted: another tree's questions, points outside the window, and a point
|
|
358
|
+
* asked again since (the later question wins). */
|
|
359
|
+
prune(post) {
|
|
360
|
+
const keep = [];
|
|
361
|
+
for (let k = 0; k < this.queue.length; k++) {
|
|
362
|
+
const q = this.queue[k];
|
|
363
|
+
const later = this.queue.some((r, j) => j > k && r.point === q.point && r.base === q.base);
|
|
364
|
+
if (q.base !== this.base || q.point < this.lo || q.point > this.hi || later) post({ type: "dropped", id: q.id });
|
|
365
|
+
else keep.push(q);
|
|
366
|
+
}
|
|
367
|
+
this.queue = keep;
|
|
368
|
+
}
|
|
369
|
+
/** Answer the next question: what the camera needs before what is asked ahead, the first asked
|
|
370
|
+
* among equals. */
|
|
371
|
+
next(post) {
|
|
372
|
+
this.prune(post);
|
|
373
|
+
if (!this.queue.length) return;
|
|
374
|
+
let k = 0;
|
|
375
|
+
for (let j = 1; j < this.queue.length; j++) if (this.queue[j].pri < this.queue[k].pri) k = j;
|
|
376
|
+
const [q] = this.queue.splice(k, 1);
|
|
377
|
+
const t0 = performance.now();
|
|
378
|
+
try {
|
|
379
|
+
const looks = this.model.choose(q, q.places);
|
|
380
|
+
post({ type: "looks", id: q.id, point: q.point, looks, ms: performance.now() - t0 }, looksBuffers(looks));
|
|
381
|
+
} catch (err) {
|
|
382
|
+
post({ type: "error", id: q.id, error: err instanceof Error ? err.message : String(err) });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var scope = globalThis;
|
|
387
|
+
if (typeof scope.document === "undefined" && typeof globalThis.WorkerGlobalScope !== "undefined") {
|
|
388
|
+
const core = new EdgeWorkerCore();
|
|
389
|
+
const post = (a, transfer) => scope.postMessage(a, transfer ?? []);
|
|
390
|
+
const turn = new MessageChannel();
|
|
391
|
+
let scheduled = false;
|
|
392
|
+
turn.port1.onmessage = () => {
|
|
393
|
+
scheduled = false;
|
|
394
|
+
core.next(post);
|
|
395
|
+
if (core.waiting) schedule();
|
|
396
|
+
};
|
|
397
|
+
const schedule = () => {
|
|
398
|
+
if (scheduled) return;
|
|
399
|
+
scheduled = true;
|
|
400
|
+
turn.port2.postMessage(0);
|
|
401
|
+
};
|
|
402
|
+
scope.onmessage = (e) => {
|
|
403
|
+
core.receive(e.data, post);
|
|
404
|
+
if (core.waiting) schedule();
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
export {
|
|
408
|
+
EdgeWorkerCore
|
|
409
|
+
};
|