visualproof 0.1.0 → 0.2.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/dist/assets/webfonts/LICENSE-Jost.txt +93 -0
- package/dist/assets/webfonts/LICENSE-SourceSans3.txt +93 -0
- package/dist/assets/webfonts/LICENSE-SourceSerif4.txt +93 -0
- package/dist/assets/webfonts/jost-latin-ext-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/jost-latin-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-greek-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-greek-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-latin-ext-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-latin-ext-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-latin-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-sans-3-latin-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-greek-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-greek-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-latin-wght-italic.woff2 +0 -0
- package/dist/assets/webfonts/source-serif-4-latin-wght-normal.woff2 +0 -0
- package/dist/server/agent.js +41 -6
- package/dist/server/history.js +39 -0
- package/dist/server/historyapi.js +2 -2
- package/dist/server/hover.js +3 -3
- package/dist/server/layoutthread.js +1 -1
- package/dist/server/main.js +15 -3
- package/dist/server/mcptools.js +2 -3
- package/dist/server/pastdoc.js +1 -1
- package/dist/server/pipeline.js +5 -2
- package/dist/server/readonlygate.js +1 -1
- package/dist/server/snapshot.js +3 -2
- package/dist/server/usage.js +2 -2
- package/dist/server/view.js +7 -0
- package/dist/server/views.js +1 -1
- package/dist/shared/collab.js +94 -1
- package/dist/shared/doc/order.js +17 -0
- package/dist/shared/doc/outline.js +45 -0
- package/dist/shared/keys.js +1 -1
- package/dist/shared/moves.js +2 -1
- package/dist/shared/pastgraph.js +9 -1
- package/dist/shared/query.js +21 -13
- package/dist/shared/sharesnap.js +2 -1
- package/dist/shared/titles.js +2 -0
- package/dist/shared/viewgraph.js +18 -0
- package/dist/web/bundle/app.js +7118 -3180
- package/dist/web/bundle/edges.js +3 -5
- package/dist/web/index.html +24 -6
- package/dist/web/src/scene.js +17 -0
- package/dist/web/style.css +248 -12
- package/docs/agent-api/following.md +2 -2
- package/docs/agent-api/views.md +3 -2
- package/package.json +1 -1
- package/test/toolchains.json +2 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The Jost Project Authors (https://github.com/indestructible-type/Jost) Jost-Italic[wght].ttf: Copyright 2020 The Jost Project Authors (https://github.com/indestructible-type/Jost)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Google Inc.
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Google Inc.
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/server/agent.js
CHANGED
|
@@ -47,6 +47,8 @@ import { authorLine, authorsOf, reviewTraces, viewTraces } from "../shared/doc/a
|
|
|
47
47
|
import { itemsShownOn } from "../shared/noteplace.js";
|
|
48
48
|
import { buildDoc, DocInputError, docNotes } from "../shared/doc/build.js";
|
|
49
49
|
import { boardSpots } from "./boardspots.js";
|
|
50
|
+
import { outlineOf } from "../shared/doc/outline.js";
|
|
51
|
+
import { planDoc } from "../shared/doc/sections.js";
|
|
50
52
|
import { docFiles, figureFiles, figureNotes } from "./figures.js";
|
|
51
53
|
import { renderLatex } from "../shared/doc/latex.js";
|
|
52
54
|
import { readSettings, SETTINGS_HELP } from "../shared/doc/settings.js";
|
|
@@ -717,6 +719,39 @@ export class AgentApi {
|
|
|
717
719
|
const tv = this.titleVersion(rec, at, version);
|
|
718
720
|
return ok({ view: rec.id, title: docTitle({ ...base, ...(tv !== undefined ? { version: tv } : {}) }, titleContext(this.world)), author: authorLine(authors), authors });
|
|
719
721
|
}
|
|
722
|
+
/** The outline of the document of view `view` at the build `version` names (ExportSpec decision
|
|
723
|
+
* 18; left out, the view's own): its sections, declarations and notes in the document's order,
|
|
724
|
+
* without their text, from the board as its page draws it (as `document` reads it). What the
|
|
725
|
+
* page's linear form draws (GET /api/export/outline); a view too large to print has one too. */
|
|
726
|
+
exportOutline(view, version) {
|
|
727
|
+
const rec = view ? this.world.views?.book.get(view) : undefined;
|
|
728
|
+
if (!rec)
|
|
729
|
+
return fail(404, `There is no view ${view.slice(0, 200)}.`, "GET /api/agent/views lists them.");
|
|
730
|
+
const at = this.exportAt(rec, version);
|
|
731
|
+
if (at !== null && typeof at === "object")
|
|
732
|
+
return at;
|
|
733
|
+
const v = this.graph();
|
|
734
|
+
if (!(v instanceof View))
|
|
735
|
+
return v;
|
|
736
|
+
const notes = this.world.views?.book.notes ?? [];
|
|
737
|
+
const memory = this.world.views?.book.memoryOf(rec.id, rec.clustering) ?? null;
|
|
738
|
+
const whole = v.meta.layouts?.[rec.clustering];
|
|
739
|
+
let g = v.g;
|
|
740
|
+
let review = this.world.review.data;
|
|
741
|
+
let past;
|
|
742
|
+
if (at !== null) {
|
|
743
|
+
const b = this.pasts.get(at, v.g, v.version, (m) => v.modules.get(m)?.file ?? null);
|
|
744
|
+
if (!b)
|
|
745
|
+
return fail(404, `There is no snapshot ${at}.`, "GET /api/agent/history lists them.");
|
|
746
|
+
past = { snapshot: at, nowName: b.past.nowName, now: v.g, nowReview: review };
|
|
747
|
+
g = b.past.graph;
|
|
748
|
+
review = reviewThen(review, b.past.nowName);
|
|
749
|
+
}
|
|
750
|
+
const set = evaluate(rec.scope, { g, review });
|
|
751
|
+
const plan = planDoc(g, set, rec.clustering, rec.hidden[rec.clustering] ?? []);
|
|
752
|
+
const board = boardSpots({ g, review, rec, memory, whole, notes, ...(past ? { past } : {}) });
|
|
753
|
+
return ok({ view: rec.id, ...(at !== null ? { version: at } : {}), outline: outlineOf(plan, g, board) });
|
|
754
|
+
}
|
|
720
755
|
/** The exported document's model (ExportSpec decision 6) of view `view` with `settings` at the
|
|
721
756
|
* build `version` names (decision 16; left out, the view's own), as a request sends them: what
|
|
722
757
|
* the page's Export and an agent typeset (POST /api/export, POST /api/agent/export), and what
|
|
@@ -956,7 +991,7 @@ export class AgentApi {
|
|
|
956
991
|
people: out,
|
|
957
992
|
privateCount: P.privateCount(),
|
|
958
993
|
agents,
|
|
959
|
-
note: "privateCount: how many people are here privately — not listed, not followable, nothing known of where they are. Each person's place is that of their liveliest page: the view, the tool (how a click selects), what is selected (a query), the declaration their panel tells of, whether its source window is open (and collapsed), the source windows they pinned (pinned: declaration, collapsed), and, where their page says them, Activity (open, kind, who, mine: whose targets), the lost-and-found (lostAndFound.open), the colouring (kind or status), edges
|
|
994
|
+
note: "privateCount: how many people are here privately — not listed, not followable, nothing known of where they are. Each person's place is that of their liveliest page: the view, the tool (how a click selects), what is selected (a query), the declaration their panel tells of, whether its source window is open (and collapsed), the source windows they pinned (pinned: declaration, collapsed), and, where their page says them, Activity (open, kind, who, mine: whose targets), the lost-and-found (lostAndFound.open), the colouring (kind or status), edges — all of which those following them see too. idle: no input for five minutes, or the page in the background. verified: invited by a personal link, so the name is theirs. To look where someone looks, POST /api/agent/view with their view and declaration; ?wait=<s>&since=<rev> waits for the next change (at most 60 s).",
|
|
960
995
|
});
|
|
961
996
|
}
|
|
962
997
|
/** A page's or an agent's place, as the companion tells it (and a conversation's message, where
|
|
@@ -986,7 +1021,7 @@ export class AgentApi {
|
|
|
986
1021
|
: v.lostFound?.open
|
|
987
1022
|
? ", with the lost-and-found open"
|
|
988
1023
|
: "";
|
|
989
|
-
const drawn = (v.scheme ? `, coloured by ${v.scheme}` : "") + (v.edges === false ? ", edges hidden" : "")
|
|
1024
|
+
const drawn = (v.scheme ? `, coloured by ${v.scheme}` : "") + (v.edges === false ? ", edges hidden" : "");
|
|
990
1025
|
return {
|
|
991
1026
|
view: v.view ?? PROJECT,
|
|
992
1027
|
viewTitle: title,
|
|
@@ -1004,7 +1039,6 @@ export class AgentApi {
|
|
|
1004
1039
|
...(v.lostFound ? { lostAndFound: { open: v.lostFound.open } } : {}),
|
|
1005
1040
|
...(v.scheme ? { colouring: v.scheme } : {}),
|
|
1006
1041
|
...(v.edges !== undefined ? { edges: v.edges } : {}),
|
|
1007
|
-
...(v.proofEdges !== undefined ? { proofEdges: v.proofEdges } : {}),
|
|
1008
1042
|
inWords: words + then + shownBy + drawn + ".",
|
|
1009
1043
|
};
|
|
1010
1044
|
}
|
|
@@ -2519,7 +2553,8 @@ export class AgentApi {
|
|
|
2519
2553
|
place[k] = b[k];
|
|
2520
2554
|
return null;
|
|
2521
2555
|
};
|
|
2522
|
-
|
|
2556
|
+
// (proofEdges, said until 2026-10-15, is no more: left as it is, as any word not known)
|
|
2557
|
+
const bad = bool("sourceCollapsed") ?? bool("edges");
|
|
2523
2558
|
if (bad)
|
|
2524
2559
|
return bad;
|
|
2525
2560
|
if (b.lostFound !== undefined) {
|
|
@@ -2744,7 +2779,7 @@ export class AgentApi {
|
|
|
2744
2779
|
{
|
|
2745
2780
|
method: "GET", path: "/api/agent/companion", brief: "who is on the page, and where each one looks", about: "Who is on the page now and where each one is — every person (the place of their liveliest page: view, tool, selection, the declaration in their panel, the source window and the pinned ones, Activity, the lost-and-found, the colouring and the edges, idle or not) and the agents present — to follow the people you help; privateCount says how many people are here privately, who are not listed and cannot be followed. With wait, the answer waits until someone comes, goes or moves.",
|
|
2746
2781
|
params: { wait: "optional: seconds to wait for a change (at most 60)", since: "optional: the rev of an earlier answer; with wait, the answer comes as soon as the revision is another" },
|
|
2747
|
-
returns: "{rev, changed?, now, sharedBy?: {name, here}, sharedByNote?, people: [{name, idle, pages, place: {view, viewTitle, targets?, scope?, mode, tool, selection, declaration, source, sourceCollapsed, pinned: [{declaration, collapsed}], history?, activity?: {open, kind, who, mine?}, lostAndFound?: {open}, colouring?, edges?,
|
|
2782
|
+
returns: "{rev, changed?, now, sharedBy?: {name, here}, sharedByNote?, people: [{name, idle, pages, place: {view, viewTitle, targets?, scope?, mode, tool, selection, declaration, source, sourceCollapsed, pinned: [{declaration, collapsed}], history?, activity?: {open, kind, who, mine?}, lostAndFound?: {open}, colouring?, edges?, inWords}}], privateCount, agents: [{name, idle, state?, status?, lastSeen?, you?, place}], note}",
|
|
2748
2783
|
run: (a) => this.companion(a),
|
|
2749
2784
|
},
|
|
2750
2785
|
{
|
|
@@ -2881,7 +2916,7 @@ export class AgentApi {
|
|
|
2881
2916
|
},
|
|
2882
2917
|
{
|
|
2883
2918
|
method: "POST", path: "/api/agent/view/link", about: "A link to what you look at, or to a place you give, that opens exactly that for whoever opens it, as a person's Share makes with Exactly what I see (the owner's decision of 2026-10-12): the address, and after it a snapshot of everything followed — the view, the build in the Time Machine, the region of the board, the tool, the selection, the focus, the panel, the source windows, the reference window, Activity, the lost-and-found, the colouring, the edges. Fields you send are taken over your own place (as POST view takes them); nothing moves, you neither. The page that opens it applies it once, and says in a line what is no longer there. It holds no key: it opens for whoever is let in already, the people on the page.",
|
|
2884
|
-
params: { "view, review, sel, selection, mode, source, focus, paper": "as POST view: over your own place", sourceCollapsed: "true: the source window following sel is collapsed to its pill", pinned: `source windows pinned, [{name, collapsed?}], at most ${MAX_PINNED}, the first pinned first`, history: "a snapshot's id (GET /api/agent/history): the Time Machine at that build; null for none", region: "[x, y, w, h]: the region of the board framed — its centre, width and height in the board's world — fitted to the opener's screen; left out, framed as for any link", activity: "{open, kind?, who?, mine?}: Activity open or closed, its filters", lostFound: "true: the lost-and-found open", scheme: "kind or status: the colouring", edges: "edges drawn",
|
|
2919
|
+
params: { "view, review, sel, selection, mode, source, focus, paper": "as POST view: over your own place", sourceCollapsed: "true: the source window following sel is collapsed to its pill", pinned: `source windows pinned, [{name, collapsed?}], at most ${MAX_PINNED}, the first pinned first`, history: "a snapshot's id (GET /api/agent/history): the Time Machine at that build; null for none", region: "[x, y, w, h]: the region of the board framed — its centre, width and height in the board's world — fitted to the opener's screen; left out, framed as for any link", activity: "{open, kind?, who?, mine?}: Activity open or closed, its filters", lostFound: "true: the lost-and-found open", scheme: "kind or status: the colouring", edges: "edges drawn", exact: "false: only the view, as Share's switch turned off makes it (the address alone)" },
|
|
2885
2920
|
returns: "{ok, link, exact, carries: what the link opens on, length, note, temporary?}",
|
|
2886
2921
|
run: (a) => this.viewLink(a),
|
|
2887
2922
|
},
|
package/dist/server/history.js
CHANGED
|
@@ -70,6 +70,45 @@ export function commitNow(root) {
|
|
|
70
70
|
const status = git(root, "status", "--porcelain", "--untracked-files=no", "--", "*.lean");
|
|
71
71
|
return { hash, branch: branch && branch !== "HEAD" ? branch : null, ...(status ? { dirty: true } : {}) };
|
|
72
72
|
}
|
|
73
|
+
/** The project's git source and the commit checked out, as a page may show them (an artwork's foot,
|
|
74
|
+
* ExportSpec decision 19): the remote's address (`origin`, else the first) with no user name or
|
|
75
|
+
* password (sourceAddress); null where the project is not a git repository. Read-only, as `git`. */
|
|
76
|
+
export function gitSource(root) {
|
|
77
|
+
const c = commitNow(root);
|
|
78
|
+
if (!c)
|
|
79
|
+
return null;
|
|
80
|
+
const remotes = (git(root, "remote") ?? "").split("\n").filter(Boolean);
|
|
81
|
+
const name = remotes.includes("origin") ? "origin" : remotes[0];
|
|
82
|
+
const url = name ? git(root, "remote", "get-url", name) : null;
|
|
83
|
+
return { source: url ? sourceAddress(url) : null, commit: c.hash, branch: c.branch, ...(c.dirty ? { dirty: true } : {}) };
|
|
84
|
+
}
|
|
85
|
+
/** A remote's address as a page shows it: its host and path, without the scheme, a user name, a
|
|
86
|
+
* password, a port or `.git` — `git@github.com:A/B.git` and `https://user:token@github.com/A/B`
|
|
87
|
+
* both read `github.com/A/B`. Null for a path on this machine, or what is no address. */
|
|
88
|
+
export function sourceAddress(url) {
|
|
89
|
+
const u = url.trim();
|
|
90
|
+
let host, path;
|
|
91
|
+
// scp-like, [user@]host:path, but not a Windows drive
|
|
92
|
+
const scp = /^(?:[^@/\s]+@)?([A-Za-z0-9.-]+):(?!\/\/)(\S+)$/.exec(u);
|
|
93
|
+
if (scp && !/^[A-Za-z]:[\\/]/.test(u))
|
|
94
|
+
[host, path] = [scp[1], scp[2]];
|
|
95
|
+
else {
|
|
96
|
+
let p;
|
|
97
|
+
try {
|
|
98
|
+
p = new URL(u);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (!["https:", "http:", "ssh:", "git:"].includes(p.protocol) || !p.hostname)
|
|
104
|
+
return null;
|
|
105
|
+
[host, path] = [p.hostname, decodeURIComponent(p.pathname)];
|
|
106
|
+
}
|
|
107
|
+
path = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
|
|
108
|
+
if (!/^[A-Za-z0-9.-]+$/.test(host) || !path || !/^[\w.~/-]+$/.test(path))
|
|
109
|
+
return null;
|
|
110
|
+
return `${host}/${path}`;
|
|
111
|
+
}
|
|
73
112
|
/** The last commit made before `at`: what an imported extraction was probably built from. */
|
|
74
113
|
function commitBefore(root, at) {
|
|
75
114
|
const hash = git(root, "log", "-1", "--format=%H", `--before=${at}`);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// or an agent at level full.
|
|
9
9
|
//
|
|
10
10
|
// The Time Machine steps from build to build, and a snapshot's graph is large (on a large project,
|
|
11
|
-
// megabytes of JSON and a noticeable time to make and compress:
|
|
11
|
+
// megabytes of JSON and a noticeable time to make and compress: Sphere-Packing-Lean's, 7,851 declarations, is 13.7 MB, made
|
|
12
12
|
// in 0.6 s): the page takes it in two parts, its declarations as JSON and its
|
|
13
13
|
// edges packed as the graph on show's are (shared/edgecodec.ts), each compressed once and kept for
|
|
14
14
|
// the last few snapshots asked, under the renames in force, and tagged so that a page that has one
|
|
@@ -21,7 +21,7 @@ const no = (status, error) => ({ status, body: { error } });
|
|
|
21
21
|
const ok = (body) => ({ status: 200, body });
|
|
22
22
|
/** the parts of the last snapshots asked, per history, by snapshot, build read and renames */
|
|
23
23
|
const kept = new WeakMap();
|
|
24
|
-
/** how many snapshots' parts are kept: about a megabyte each, compressed, on a large project (
|
|
24
|
+
/** how many snapshots' parts are kept: about a megabyte each, compressed, on a large project (Sphere-Packing-Lean's:
|
|
25
25
|
* 0.7 MB of declarations, 0.5 MB of edges) */
|
|
26
26
|
const KEEP_PARTS = 8;
|
|
27
27
|
/** what changed between two snapshots, as answered last (a step on the Time Machine's timeline asks
|
package/dist/server/hover.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// (a diagnostic), not as a hover.
|
|
16
16
|
//
|
|
17
17
|
// Lean runs each open file in a worker process of its own, which can die (a worker on a large
|
|
18
|
-
// project holds gigabytes: 4.1 GB for
|
|
18
|
+
// project holds gigabytes: 4.1 GB for Sphere-Packing-Lean's main theorem's module; and the kernel's OOM killer picks the
|
|
19
19
|
// largest). Lean then says the file's progress
|
|
20
20
|
// is a fatal error, and starts the worker again only when the document changes: until then every
|
|
21
21
|
// question about it would wait forever. So a crashed document is closed and opened again here, as a
|
|
@@ -38,8 +38,8 @@ const IDLE_MS = 10 * 60 * 1000;
|
|
|
38
38
|
const REQUEST_MS = 5 * 60 * 1000;
|
|
39
39
|
/** a document still being elaborated that Lean has said nothing about for this long has stopped,
|
|
40
40
|
* whatever Lean says: it is opened again, as a crashed one (loading a large project's imports can
|
|
41
|
-
* take minutes on a machine short of memory, in which Lean says nothing; on an idle one
|
|
42
|
-
*
|
|
41
|
+
* take minutes on a machine short of memory, in which Lean says nothing; on an idle one
|
|
42
|
+
* Sphere-Packing-Lean's main theorem's module answers in 2 s) */
|
|
43
43
|
const STALL_MS = 10 * 60 * 1000;
|
|
44
44
|
/** crashes in a row (the document never elaborated to its end between them) after which a document
|
|
45
45
|
* is not opened again until GIVE_UP_MS have passed */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The graph's layouts, off the server's thread: computeLayout then stabilize, for each clustering in
|
|
2
|
-
// a worker of its own, both at once. Laid out on the server's thread,
|
|
2
|
+
// a worker of its own, both at once. Laid out on the server's thread, Sphere-Packing-Lean's graph held
|
|
3
3
|
// every request for seconds (2.5 s a clustering, more against a memory far off); a worker holds
|
|
4
4
|
// none. A worker is given what a layout reads of each declaration, the edges and the memory, as
|
|
5
5
|
// the page's own (web/src/layoutworker.ts), and gives the layout back. This module is its own
|
package/dist/server/main.js
CHANGED
|
@@ -7,7 +7,7 @@ import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
|
7
7
|
import { createServer } from "node:http";
|
|
8
8
|
import { extname, join, resolve } from "node:path";
|
|
9
9
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
10
|
-
import { WEB, bundleDir, bundleDirIn, ensureBundle, prepare, useBundleDir } from "./pipeline.js";
|
|
10
|
+
import { WEB, WEBFONTS, bundleDir, bundleDirIn, ensureBundle, prepare, useBundleDir } from "./pipeline.js";
|
|
11
11
|
import { SiteBusy, cacheHome, dataHome, lockSite, openSite, projectDirs } from "./dirs.js";
|
|
12
12
|
import { ProjectError, openProject, targetsInWords } from "./project.js";
|
|
13
13
|
import { Live } from "./live.js";
|
|
@@ -16,7 +16,7 @@ import { isDeclName, LeanHover } from "./hover.js";
|
|
|
16
16
|
import { Identities, layoutAuthor } from "./identity.js";
|
|
17
17
|
import { LAYOUT_VERSION } from "../shared/graph.js";
|
|
18
18
|
import { MAX_MEMORY_BYTES, Views } from "./views.js";
|
|
19
|
-
import { cleanView, defaultAgentName, validSession } from "../shared/collab.js";
|
|
19
|
+
import { cleanView, defaultAgentName, validSession, withoutArtwork } from "../shared/collab.js";
|
|
20
20
|
import { writeSnapshot } from "./snapshot.js";
|
|
21
21
|
import { lakeTextHash } from "./status.js";
|
|
22
22
|
import { invalidTemplate } from "../shared/editor.js";
|
|
@@ -265,6 +265,11 @@ export class App {
|
|
|
265
265
|
async export(req, res, url, agent, readOnly = false) {
|
|
266
266
|
const path = url.pathname;
|
|
267
267
|
const api = this.agents;
|
|
268
|
+
// the document's outline, which the linear form draws: nothing is typeset for it
|
|
269
|
+
if (path === "/api/export/outline") {
|
|
270
|
+
const o = api.exportOutline(url.searchParams.get("view") ?? "", url.searchParams.get("version") ?? undefined);
|
|
271
|
+
return sendJson(res, o.body, o.status, o.headers);
|
|
272
|
+
}
|
|
268
273
|
if (!this.exporter)
|
|
269
274
|
return sendJson(res, { error: "This server does not export documents." }, 404);
|
|
270
275
|
let a;
|
|
@@ -817,6 +822,9 @@ export class App {
|
|
|
817
822
|
return sendFile(req, res, join(WEB, "index.html"));
|
|
818
823
|
if (path === "/style.css")
|
|
819
824
|
return sendFile(req, res, join(WEB, "style.css"));
|
|
825
|
+
// the fonts an artwork is drawn in (ExportSpec decision 19)
|
|
826
|
+
if (FONT_PATH.test(path))
|
|
827
|
+
return sendFile(req, res, join(WEBFONTS, path.slice("/fonts/".length)));
|
|
820
828
|
// the page, and its workers: the one that lays out the Time Machine's past graphs
|
|
821
829
|
// (web/src/layoutworker.ts), the one that chooses the overview's edges (web/src/edgeworker.ts),
|
|
822
830
|
// and pdf.js's, which reads the library's PDFs for the reference window (AttachmentSpec decision 11)
|
|
@@ -937,8 +945,9 @@ export class App {
|
|
|
937
945
|
// its own event stream only, by the secret the stream was given (`hello`)
|
|
938
946
|
const name = this.ids.who(req);
|
|
939
947
|
const priv = !agent && b.private === true;
|
|
948
|
+
// agents have no artwork (ExportSpec decision 19): the page draws it
|
|
940
949
|
if (agent && name)
|
|
941
|
-
live.presence.agentBeat(name, cleanView(b.view));
|
|
950
|
+
live.presence.agentBeat(name, withoutArtwork(cleanView(b.view)));
|
|
942
951
|
else if (!live.beat(b.token, priv ? null : name, priv ? null : cleanView(b.view), !priv && b.idle === true, priv, !!this.ids.person(req)))
|
|
943
952
|
return sendJson(res, { error: "This heartbeat is not from an open event stream of this page's: it is sent with the token the stream says hello with.", stream: false }, 403);
|
|
944
953
|
return sendJson(res, { ok: true });
|
|
@@ -1080,7 +1089,10 @@ export class App {
|
|
|
1080
1089
|
}
|
|
1081
1090
|
}
|
|
1082
1091
|
}
|
|
1092
|
+
/** an artwork's font file (assets/webfonts/) */
|
|
1093
|
+
const FONT_PATH = /^\/fonts\/(source-sans-3|source-serif-4|jost)-[a-z-]+\.woff2$/;
|
|
1083
1094
|
const TYPES = {
|
|
1095
|
+
".woff2": "font/woff2",
|
|
1084
1096
|
".html": "text/html; charset=utf-8",
|
|
1085
1097
|
".css": "text/css; charset=utf-8",
|
|
1086
1098
|
".js": "text/javascript; charset=utf-8",
|
package/dist/server/mcptools.js
CHANGED
|
@@ -149,7 +149,7 @@ export const TOOLS = [
|
|
|
149
149
|
default: "move",
|
|
150
150
|
actions: {
|
|
151
151
|
move: { method: "POST", path: "/api/agent/view", says: "go to a view, a target's view, a declaration, its source; fields left out keep their value", args: ["view", "review", "sel", "selection", "mode", "source", "paper", "focus", "leave"] },
|
|
152
|
-
link: { method: "POST", path: "/api/agent/view/link", says: "a link that opens exactly what you look at, or a place you describe, to hand people in a reply", args: ["view", "review", "sel", "selection", "mode", "source", "paper", "focus", "sourceCollapsed", "pinned", "history", "region", "activity", "lostFound", "scheme", "edges", "
|
|
152
|
+
link: { method: "POST", path: "/api/agent/view/link", says: "a link that opens exactly what you look at, or a place you describe, to hand people in a reply", args: ["view", "review", "sel", "selection", "mode", "source", "paper", "focus", "sourceCollapsed", "pinned", "history", "region", "activity", "lostFound", "scheme", "edges", "exact"] },
|
|
153
153
|
ask_follow: { method: "POST", path: "/api/agent/ask-follow", says: "ask the people on the page to follow you first", args: ["who", "wait"] },
|
|
154
154
|
},
|
|
155
155
|
args: {
|
|
@@ -157,7 +157,7 @@ export const TOOLS = [
|
|
|
157
157
|
review: str("a target: be on its view"),
|
|
158
158
|
sel: str("the declaration you look at"),
|
|
159
159
|
selection: str("what you select, as a query: decl(A.b) or tcb(A.c)"),
|
|
160
|
-
mode: str("how you select: plain; Rests on's deps, tcb, keys; apps", ["plain", "deps", "tcb", "keys", "apps"]),
|
|
160
|
+
mode: str("how you select: plain; Rests on's deps, tcb, keys, refs (what it uses directly); apps", ["plain", "deps", "tcb", "keys", "refs", "apps"]),
|
|
161
161
|
source: bool("the source window open on sel"),
|
|
162
162
|
paper: PAPER,
|
|
163
163
|
focus: str("your focus on the view: a target, \"none\", or \"view\" for the view's own"),
|
|
@@ -170,7 +170,6 @@ export const TOOLS = [
|
|
|
170
170
|
lostFound: bool("link: the lost-and-found open"),
|
|
171
171
|
scheme: str("link: the colouring", ["kind", "status"]),
|
|
172
172
|
edges: bool("link: edges drawn"),
|
|
173
|
-
proofEdges: bool("link: proof edges drawn"),
|
|
174
173
|
exact: bool("link: false for the view's address alone"),
|
|
175
174
|
who: str("ask_follow: a person's name; left out, everyone"),
|
|
176
175
|
wait: bool("ask_follow: false answers at once (default: wait for them, at most 30 s)"),
|
package/dist/server/pastdoc.js
CHANGED
|
@@ -14,7 +14,7 @@ import { pastGraph } from "../shared/pastgraph.js";
|
|
|
14
14
|
import { pageGraph } from "./history.js";
|
|
15
15
|
import { MOST_DIFF, snapshotDiff } from "./historyapi.js";
|
|
16
16
|
/** how many past builds are kept: a past graph and the texts read for a large view are large on a
|
|
17
|
-
* large project (
|
|
17
|
+
* large project (Sphere-Packing-Lean's past graph: 13.7 MB of JSON) */
|
|
18
18
|
const KEEP = 2;
|
|
19
19
|
export class PastBuilds {
|
|
20
20
|
w;
|