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.
Files changed (50) hide show
  1. package/dist/assets/webfonts/LICENSE-Jost.txt +93 -0
  2. package/dist/assets/webfonts/LICENSE-SourceSans3.txt +93 -0
  3. package/dist/assets/webfonts/LICENSE-SourceSerif4.txt +93 -0
  4. package/dist/assets/webfonts/jost-latin-ext-wght-normal.woff2 +0 -0
  5. package/dist/assets/webfonts/jost-latin-wght-normal.woff2 +0 -0
  6. package/dist/assets/webfonts/source-sans-3-greek-wght-italic.woff2 +0 -0
  7. package/dist/assets/webfonts/source-sans-3-greek-wght-normal.woff2 +0 -0
  8. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-italic.woff2 +0 -0
  9. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-normal.woff2 +0 -0
  10. package/dist/assets/webfonts/source-sans-3-latin-wght-italic.woff2 +0 -0
  11. package/dist/assets/webfonts/source-sans-3-latin-wght-normal.woff2 +0 -0
  12. package/dist/assets/webfonts/source-serif-4-greek-wght-italic.woff2 +0 -0
  13. package/dist/assets/webfonts/source-serif-4-greek-wght-normal.woff2 +0 -0
  14. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  15. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  16. package/dist/assets/webfonts/source-serif-4-latin-wght-italic.woff2 +0 -0
  17. package/dist/assets/webfonts/source-serif-4-latin-wght-normal.woff2 +0 -0
  18. package/dist/server/agent.js +41 -6
  19. package/dist/server/history.js +39 -0
  20. package/dist/server/historyapi.js +2 -2
  21. package/dist/server/hover.js +3 -3
  22. package/dist/server/layoutthread.js +1 -1
  23. package/dist/server/main.js +15 -3
  24. package/dist/server/mcptools.js +2 -3
  25. package/dist/server/pastdoc.js +1 -1
  26. package/dist/server/pipeline.js +5 -2
  27. package/dist/server/readonlygate.js +1 -1
  28. package/dist/server/snapshot.js +3 -2
  29. package/dist/server/usage.js +2 -2
  30. package/dist/server/view.js +7 -0
  31. package/dist/server/views.js +1 -1
  32. package/dist/shared/collab.js +94 -1
  33. package/dist/shared/doc/order.js +17 -0
  34. package/dist/shared/doc/outline.js +45 -0
  35. package/dist/shared/keys.js +1 -1
  36. package/dist/shared/moves.js +2 -1
  37. package/dist/shared/pastgraph.js +9 -1
  38. package/dist/shared/query.js +21 -13
  39. package/dist/shared/sharesnap.js +2 -1
  40. package/dist/shared/titles.js +2 -0
  41. package/dist/shared/viewgraph.js +18 -0
  42. package/dist/web/bundle/app.js +7118 -3180
  43. package/dist/web/bundle/edges.js +3 -5
  44. package/dist/web/index.html +24 -6
  45. package/dist/web/src/scene.js +17 -0
  46. package/dist/web/style.css +248 -12
  47. package/docs/agent-api/following.md +2 -2
  48. package/docs/agent-api/views.md +3 -2
  49. package/package.json +1 -1
  50. package/test/toolchains.json +2 -2
@@ -1,8 +1,6 @@
1
1
  // src/shared/graph.ts
2
2
  var SITE_STMT = 1;
3
3
  var SITE_BODY = 2;
4
- var SITE_PROOF = 4;
5
- var SITE_GRAMMAR = 8;
6
4
  var SITE_TCB = SITE_STMT | SITE_BODY;
7
5
 
8
6
  // src/web/src/bundles.ts
@@ -166,7 +164,7 @@ var LEVELS_KEPT = 6;
166
164
  var OverviewModel = class {
167
165
  g = null;
168
166
  tree = null;
169
- toggles = { externals: true, proofs: false };
167
+ toggles = { externals: true };
170
168
  levels = /* @__PURE__ */ new Map();
171
169
  setGraph(g) {
172
170
  this.g = g;
@@ -210,7 +208,7 @@ var OverviewModel = class {
210
208
  aggregateLevel(level, places) {
211
209
  const g = this.g;
212
210
  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);
211
+ const sites = SITE_STMT | SITE_BODY;
214
212
  const ext = this.toggles.externals;
215
213
  const pairs = aggregate(g, unit, sites, (i) => i < g.projectCount || ext);
216
214
  return { pairs, units: Int32Array.from(new Set(unit)) };
@@ -334,7 +332,7 @@ var EdgeWorkerCore = class {
334
332
  this.base = m.base;
335
333
  this.model.setTree(
336
334
  { nc: m.nc, n: m.n, parent: m.parent, nodeCluster: m.nodeCluster, rep: m.rep },
337
- { externals: m.externals, proofs: m.proofs }
335
+ { externals: m.externals }
338
336
  );
339
337
  this.prune(post);
340
338
  break;
@@ -2,26 +2,36 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
6
  <title>VisualProof</title>
7
7
  <link rel="icon" href="data:,">
8
8
  <script>
9
- // the theme before the first paint (web/src/theme.ts keeps it after): light, dark, or the system's
9
+ // the theme before the first paint (web/src/theme.ts keeps it after): light unless the reader
10
+ // chose dark, or the system's
10
11
  try {
11
12
  var t = localStorage.getItem("visualproof:theme");
12
- document.documentElement.dataset.theme = t === "light" || t === "dark" ? t : matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
13
+ document.documentElement.dataset.theme = t === "dark" ? "dark" : t === "system" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
13
14
  } catch (e) {
14
- document.documentElement.dataset.theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
15
+ document.documentElement.dataset.theme = "light";
15
16
  }
16
17
  </script>
17
18
  <link rel="stylesheet" href="/style.css">
18
19
  </head>
19
20
  <body data-mode="tcb">
21
+ <!-- an artwork style's ground, under the board within the frame (web/src/artframe.ts) -->
22
+ <canvas id="art-sky" hidden></canvas>
20
23
  <canvas id="canvas"></canvas>
21
24
  <canvas id="labels"></canvas>
22
25
  <div id="cards"></div>
23
26
  <!-- the notes on the board, over the canvas at a fixed size (web/src/sticky.ts) -->
24
27
  <div id="stickies"></div>
28
+ <!-- the linear form: the board drawn as its document's outline (web/src/linear.ts, ExportSpec
29
+ decision 18) -->
30
+ <canvas id="linear" hidden></canvas>
31
+ <!-- the artwork mode: the frame over the board and the shade around it, and its panel
32
+ (web/src/artframe.ts, ExportSpec decision 19) -->
33
+ <div id="art" hidden></div>
34
+ <div id="art-panel" class="modal-box glass" hidden></div>
25
35
 
26
36
  <header id="bar" class="glass">
27
37
  <div class="brand"><span class="nav"><button id="nav-back" title="Back (Alt ←)" aria-label="Back" disabled><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M10 3.5 5.5 8l4.5 4.5"/></svg></button><button id="nav-fwd" title="Forward (Alt →)" aria-label="Forward" disabled><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M6 3.5 10.5 8 6 12.5"/></svg></button></span><span class="logo">VisualProof</span><span id="project" class="project"></span><span id="snapshot" class="snapshot" hidden></span><button id="build" class="build-pill" hidden title="Build status (b)"><span class="b-dot"></span><span class="b-text"></span></button></div>
@@ -46,7 +56,7 @@
46
56
  <div id="library-pop" class="glass" role="dialog" aria-label="The library" hidden></div>
47
57
  </div>
48
58
  <!-- a document of targets, typeset with LaTeX (web/src/exportmodal.ts) -->
49
- <button id="export-btn" class="act-btn" title="Export: this view as a document, typeset with LaTeX">
59
+ <button id="export-btn" class="act-btn" title="Export: this view as a document, typeset with LaTeX; or the board as a picture (Artwork)">
50
60
  <svg viewBox="0 0 16 16" width="15" height="15" aria-hidden="true"><path d="M8 2.5v7.5M5 7l3 3 3-3M3 11.5v1.8h10v-1.8"/></svg>
51
61
  <span>Export</span>
52
62
  </button>
@@ -68,11 +78,18 @@
68
78
  <div id="who-menu" class="glass" role="menu" aria-label="Your name and visibility" hidden></div>
69
79
  </div>
70
80
  <button id="help-btn" class="help-btn" title="Keys and gestures, and the tour (?)" aria-label="Help">?</button>
81
+ <!-- on a phone the bar's buttons are in one menu (web/src/phone.ts) -->
82
+ <div class="more-wrap">
83
+ <button id="more-btn" class="act-btn act-icon" aria-label="More" aria-haspopup="menu" aria-expanded="false" title="Share, the library, export, activity, your name, help"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="3.5" cy="8" r="1.3"/><circle cx="8" cy="8" r="1.3"/><circle cx="12.5" cy="8" r="1.3"/></svg><span id="more-badge" class="act-badge" hidden></span></button>
84
+ <div id="more-pop" class="glass" role="menu" aria-label="More" hidden></div>
85
+ </div>
71
86
  </div>
72
87
  <div id="progress"><div></div></div>
73
88
  </header>
74
89
  <!-- the top: your tabs (the views you have open) and your mode, how a click selects — yours alone -->
75
90
  <div id="viewbar">
91
+ <!-- on a phone, the side panel opens as a drawer from here (web/src/phone.ts) -->
92
+ <button id="side-btn" class="views-btn side-btn" aria-label="The board: targets and clusters" aria-expanded="false" title="The board: targets and clusters"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M2.5 4h11M2.5 8h11M2.5 12h11"/></svg></button>
76
93
  <div id="tabs" class="tabs" role="tablist" aria-label="Your views"></div>
77
94
  <div class="views-wrap">
78
95
  <button id="views-btn" class="views-btn" aria-haspopup="menu" title="Views: a new one, a copy of this one, its name; every view kept on this server">Views<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
@@ -98,6 +115,7 @@
98
115
  <button data-mode="deps" aria-describedby="mode-tip">Uses</button>
99
116
  <button data-mode="tcb" aria-describedby="mode-tip">TCB</button>
100
117
  <button data-mode="keys" aria-describedby="mode-tip">Keys</button>
118
+ <button data-mode="refs" aria-describedby="mode-tip">Direct</button>
101
119
  </span>
102
120
  </span>
103
121
  <button data-mode="apps" aria-describedby="mode-tip">Used by</button>
@@ -179,7 +197,7 @@
179
197
  <div id="eyes-pop" class="glass" role="dialog" aria-label="How you see the board" hidden>
180
198
  <button id="lock-btn" class="eye" title="The lock, on by default: a drag pans the view, never a declaration, a circle or a note; clicks still select. Let it go and a drag moves them, for everyone on this board (l). Hold Ctrl (⌘ on a Mac) for the other way while held." aria-pressed="true"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path class="lk-body" d="M4 7.5h8v5.5H4z"/><path class="lk-shackle" d="M5.6 7.5V5.4a2.4 2.4 0 0 1 4.8 0v2.1"/></svg><span class="eye-l">Layout locked</span><kbd>l</kbd></button>
181
199
  <button id="edges-btn" class="eye" title="Edges: the bundled overview of the dependencies, faint under a selection, whose own edges always show (g)" aria-pressed="true"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3.5 12.5C7 12.5 9 3.5 12.5 3.5"/><circle cx="3.5" cy="12.5" r="1.4"/><circle cx="12.5" cy="3.5" r="1.4"/></svg><span class="eye-l">Edges</span><kbd>g</kbd></button>
182
- <button id="proofs-btn" class="eye" title="Proof edges: also the dependencies that occur only inside proofs" aria-pressed="false"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3.5 12.5C7 12.5 9 3.5 12.5 3.5" stroke-dasharray="1.6 2.2"/><rect x="10.5" y="10.5" width="3.5" height="3.5" rx="0.5"/></svg><span class="eye-l">Proof edges</span></button>
200
+ <button id="linear-btn" class="eye" title="Linear form: the board drawn as its document's outline, on one line, the first at the top; drag along it to reorder the document (o)" aria-pressed="false"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M8 2v12"/><circle cx="8" cy="4" r="1.4"/><circle cx="8" cy="8" r="1.4"/><circle cx="8" cy="12" r="1.4"/><path d="M10.5 4h3M10.5 8h3M10.5 12h3"/></svg><span class="eye-l">Linear form</span><kbd>o</kbd></button>
183
201
  <div class="eye-theme"><span class="eye-l">Theme</span><div id="theme-seg" class="seg mini" role="radiogroup" aria-label="Theme"></div></div>
184
202
  </div>
185
203
  </div>
@@ -224,6 +224,23 @@ export class Scene {
224
224
  this.moving = true;
225
225
  return true;
226
226
  }
227
+ /** Pin one body at a place in its parent's coordinates, as a drop there would (the linear form's
228
+ * drop, web/src/linear.ts): brought inside the parent, the latest drop, the rest making way. */
229
+ pinAt(kind, k, lx, ly) {
230
+ if (kind === "cluster") {
231
+ this.pinC.set(k, this.inside(this.parent[k], this.homeSize(k), lx, ly));
232
+ this.dropSeq.set(k, this.dropNext++);
233
+ this.touch(this.parent[k]);
234
+ }
235
+ else {
236
+ this.pinN.set(k, this.inside(this.nodeCluster[k], LEAF_R, lx, ly));
237
+ this.dropSeq.set(this.nc + k, this.dropNext++);
238
+ this.touch(this.nodeCluster[k]);
239
+ }
240
+ this.computeAnchors();
241
+ this.motion?.wakeDisplaced();
242
+ this.moving = true;
243
+ }
227
244
  pins(names) {
228
245
  const out = { nodes: {}, clusters: {} };
229
246
  const r = (v) => Math.round(v * 10) / 10;
@@ -1,10 +1,33 @@
1
1
  /* VisualProof — quiet surfaces, one gradient for what matters. */
2
2
 
3
+ /* the fonts an artwork is drawn in (ExportSpec decision 19; assets/webfonts/, SIL Open Font License):
4
+ Source Sans 3 for its names, Source Serif 4 for the starfield's; their Latin and Greek, the rest of
5
+ a name from the fonts the system has */
6
+ @font-face { font-family: "Source Sans 3"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-greek-wght-normal.woff2") format("woff2"); unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF; }
7
+ @font-face { font-family: "Source Sans 3"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-latin-ext-wght-normal.woff2") format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF; }
8
+ @font-face { font-family: "Source Sans 3"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-latin-wght-normal.woff2") format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
9
+ @font-face { font-family: "Source Sans 3"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-greek-wght-italic.woff2") format("woff2"); unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF; }
10
+ @font-face { font-family: "Source Sans 3"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-latin-ext-wght-italic.woff2") format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF; }
11
+ @font-face { font-family: "Source Sans 3"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-sans-3-latin-wght-italic.woff2") format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
12
+ @font-face { font-family: "Source Serif 4"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-greek-wght-normal.woff2") format("woff2"); unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF; }
13
+ @font-face { font-family: "Source Serif 4"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-latin-ext-wght-normal.woff2") format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF; }
14
+ @font-face { font-family: "Source Serif 4"; font-style: normal; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-latin-wght-normal.woff2") format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
15
+ @font-face { font-family: "Source Serif 4"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-greek-wght-italic.woff2") format("woff2"); unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF; }
16
+ @font-face { font-family: "Source Serif 4"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-latin-ext-wght-italic.woff2") format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF; }
17
+ @font-face { font-family: "Source Serif 4"; font-style: italic; font-display: swap; font-weight: 200 900; src: url("/fonts/source-serif-4-latin-wght-italic.woff2") format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
18
+
19
+ /* Jost, for an artwork's title and its top-level clusters' names, in spaced capitals (the owner's
20
+ decision of 2026-10-15): its Latin */
21
+ @font-face { font-family: "Jost"; font-style: normal; font-display: swap; font-weight: 100 900; src: url("/fonts/jost-latin-ext-wght-normal.woff2") format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
22
+ @font-face { font-family: "Jost"; font-style: normal; font-display: swap; font-weight: 100 900; src: url("/fonts/jost-latin-wght-normal.woff2") format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
23
+
3
24
  /* `hidden` hides, whatever display a class gives the element (the tooltip's `display: flex` kept
4
25
  it on screen after it was hidden) */
5
26
  [hidden] { display: none !important; }
6
27
 
7
28
  :root {
29
+ /* the camera's easing (web/src/canvas.ts, animate), for what moves with it */
30
+ --art-ease: cubic-bezier(0.65, 0, 0.35, 1);
8
31
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif;
9
32
  --mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
10
33
 
@@ -189,6 +212,88 @@ button { font: inherit; color: inherit; cursor: pointer; }
189
212
  #canvas { position: fixed; inset: 0; width: 100%; height: 100%; display: block; cursor: grab; touch-action: none; }
190
213
  #labels { position: fixed; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
191
214
  #cards { position: fixed; inset: 0; pointer-events: none; overflow: hidden; }
215
+ /* a finger held on the board opens its menu there, under the finger (web/src/canvas.ts): iOS would
216
+ select the text it then finds under it, or offer the canvas as an image */
217
+ #canvas, #cards, #ctxmenu { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
218
+ #ctxmenu input, #ctxmenu textarea { -webkit-user-select: text; user-select: text; }
219
+ /* the linear form, over the board and its notes, under the panels (web/src/linear.ts) */
220
+ #linear { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; z-index: 5; }
221
+ #linear[hidden] { display: none; }
222
+ /* the artwork mode (web/src/artframe.ts): the board is the picture, the frame over it a viewfinder,
223
+ everything around it shaded; the board under it takes the pointer */
224
+ #art { position: fixed; inset: 0; z-index: 30; pointer-events: none; }
225
+ #art[hidden] { display: none; }
226
+ .art-frame { position: absolute; box-shadow: 0 0 0 200vmax rgba(0, 0, 0, 0.45); outline: 2px solid var(--accent); border-radius: 2px; }
227
+ /* over the board within the frame, its title and foot as the files have them (web/src/artframe.ts,
228
+ renderInk); under it, a style's ground (renderSky), before the board's canvas in the page */
229
+ .art-ink { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
230
+ #art-sky { position: fixed; display: block; pointer-events: none; }
231
+ #art-sky[hidden] { display: none; }
232
+ /* the mode coming and going (web/src/artframe.ts, start and end; ExportSpec decision 19): as the linear
233
+ form, over 480 ms — the frame closing in from the whole screen as its shade comes, or opening out as
234
+ it goes; the panel sliding in or away; the board dipping to its ground while its look changes; the
235
+ title, the foot and the strips beside the frame once it is still */
236
+ body.art-moving .art-frame { transition: left 480ms var(--art-ease), top 480ms var(--art-ease), width 480ms var(--art-ease), height 480ms var(--art-ease), box-shadow 480ms ease, outline-color 480ms ease; }
237
+ #art.art-away .art-frame { box-shadow: 0 0 0 200vmax rgba(0, 0, 0, 0); outline-color: transparent; }
238
+ .art-ink, .art-head, .art-tail { transition: opacity 200ms ease; }
239
+ body.art-moving .art-ink, body.art-moving .art-head, body.art-moving .art-tail, #art.art-away .art-head, #art.art-away .art-tail { opacity: 0; }
240
+ #art-panel { transition: opacity 480ms var(--art-ease), transform 480ms var(--art-ease); }
241
+ #art-panel.art-away { opacity: 0; transform: translateX(24px); }
242
+ body.art-moving { transition: background-color 180ms ease; }
243
+ body.art-moving #canvas, body.art-moving #labels, body.art-moving #cards, body.art-moving #stickies { transition: opacity 180ms ease; }
244
+ body.art-moving #art-sky { transition: opacity 180ms ease, left 480ms var(--art-ease), top 480ms var(--art-ease), width 480ms var(--art-ease), height 480ms var(--art-ease); }
245
+ body.art-dip #canvas, body.art-dip #labels, body.art-dip #cards, body.art-dip #stickies, body.art-dip #art-sky { opacity: 0; }
246
+ body[data-art-style]:not([data-art-bg]) { background: #06080e; }
247
+ .art-size output { margin-left: 6px; color: var(--text-2); font-variant-numeric: tabular-nums; }
248
+ .art-size input[type="range"] { width: 100%; }
249
+ /* the artwork's background on the board: none is shown as a checkerboard; its notes hidden */
250
+ body[data-art-bg="transparent"] { background: repeating-conic-gradient(rgba(128, 128, 128, 0.2) 0 25%, transparent 0 50%) 0 0 / 16px 16px, var(--canvas); }
251
+ body.art-nonotes #stickies { display: none; }
252
+ #art-panel { position: fixed; top: 76px; right: 16px; z-index: 41; width: 300px; max-height: calc(100vh - 100px); overflow: auto; padding-bottom: 0; }
253
+ /* the panel's sections, each under a small heading; its buttons kept in view at its foot */
254
+ .art-secs { display: grid; gap: 14px; margin-top: 10px; }
255
+ .art-sec { border-top: 1px solid var(--line); padding-top: 10px; }
256
+ .art-sec-h { margin: 0 0 8px; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-2); }
257
+ .art-save { position: sticky; bottom: 0; margin: 4px -18px 0; padding: 12px 18px 14px; background: var(--surface-solid); border-top: 1px solid var(--line); }
258
+ /* in the shade beside the frame: the title's strip above its top left, the source's switch below its
259
+ bottom right (web/src/artframe.ts) */
260
+ .art-head, .art-tail { position: absolute; pointer-events: auto; border-radius: 10px; font-size: 12px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); }
261
+ .art-head { transform: translateY(-100%); display: flex; flex-wrap: wrap; align-items: flex-end; gap: 6px 10px; padding: 7px 10px; }
262
+ .art-head-field input[type="text"] { width: var(--w, 10em); max-width: 100%; }
263
+ .art-head .art-size input[type="range"], .art-tail .art-size input[type="range"] { width: 9em; padding: 0; border: 0; background: none; }
264
+ .art-head [data-art-own][hidden] { display: none; }
265
+ .art-tail { display: flex; align-items: center; gap: 14px; padding: 4px 10px; }
266
+ .art-tail .art-size { flex-direction: row; align-items: center; gap: 8px; }
267
+ .art-tail .ex-check { margin: 0; }
268
+ #art-panel[hidden] { display: none; }
269
+ .art-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
270
+ .art-grid .ex-wide, .art-grid .art-note, .art-grid .ex-checks { grid-column: 1 / -1; }
271
+ .art-note { font-size: 11.5px; line-height: 1.4; }
272
+ /* names and targets by selection (ExportSpec decision 19): a filter, All and None, the lines ticked
273
+ or not, scrolled within the panel */
274
+ .art-pick { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 5px; }
275
+ .art-pick-head { display: flex; align-items: center; gap: 5px; font-size: 11.5px; }
276
+ .art-pick-head > span { flex: 1; }
277
+ .art-pick-head .rv-btn { padding: 3px 8px; font-size: 11.5px; white-space: nowrap; }
278
+ .art-filter {
279
+ flex: 1; min-width: 0; font: 12.5px var(--font); color: var(--text); background: var(--surface-solid);
280
+ border: 1px solid var(--line); border-radius: 7px; padding: 4px 7px;
281
+ }
282
+ .art-rows { max-height: 176px; overflow: auto; padding: 3px 0; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-solid); }
283
+ .art-row { display: flex; align-items: center; gap: 6px; padding: 1px 8px; font-size: 12px; line-height: 1.5; cursor: pointer; white-space: nowrap; }
284
+ .art-row:hover { background: var(--code-bg); }
285
+ .art-row input { margin: 0; flex: none; }
286
+ .art-row span { overflow: hidden; text-overflow: ellipsis; }
287
+ .art-rows-h { padding: 4px 8px 1px; font-size: 10.5px; color: var(--text-3); }
288
+ .ex-tabs { margin: 2px 0 4px; }
289
+ /* in the artwork mode, the panels are put away: the frame has the room */
290
+ body.framing #side, body.framing #panel, body.framing #selbar, body.framing #lensbar, body.framing #tm-kept,
291
+ body.framing #viewbar, body.framing #foot, body.framing #zoom, body.framing #tabs, body.framing #legend { visibility: hidden; opacity: 0; }
292
+ /* of the bar, Share and Search alone: the rest out of reach (web/src/main.ts, drawLook) */
293
+ body.framing #bar [inert] { opacity: 0.32; }
294
+ /* both fading as the mode comes and goes */
295
+ body.art-moving :is(#side, #panel, #selbar, #lensbar, #tm-kept, #viewbar, #foot, #zoom, #tabs, #legend) { transition: opacity 240ms ease, visibility 240ms; }
296
+ body.art-moving :is(#bar .brand, #bar .library-wrap, #export-btn, #activity-btn, #bar .peers-wrap, #bar .who-wrap, #help-btn, #bar .more-wrap) { transition: opacity 240ms ease; }
192
297
 
193
298
  .glass {
194
299
  background: var(--surface);
@@ -526,11 +631,11 @@ button.o-trust:hover { color: var(--accent); background: var(--code-bg); }
526
631
  .sw-win.moving .sw-head { cursor: grabbing; }
527
632
  .sw-head .nav { margin-right: 2px; }
528
633
  /* under the title bar, why the pairing opened no reference beside it (AttachmentSpec decision 8) */
529
- .sw-pair { padding: 5px 12px; border-bottom: 1px solid var(--line); font-size: 12px; color: var(--text-2); }
530
- /* a narrow window shortens the namespace and the place, never its buttons */
531
- .sw-name { font: 600 15px var(--font); flex: none; }
532
- .sw-ns { color: var(--text-2); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40vw; min-width: 0; }
533
- .sw-loc { color: var(--text-3); font: 11.5px var(--mono); margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
634
+ .sw-pair { padding: 5px 12px; border-bottom: 1px solid var(--line); font-size: 12px; color: var(--text-2); overflow-wrap: anywhere; }
635
+ /* a narrow window shortens the namespace and the place, then the name, never its buttons */
636
+ .sw-name { font: 600 15px var(--font); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
637
+ .sw-ns { color: var(--text-2); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40vw; min-width: 0; flex-shrink: 1000; }
638
+ .sw-loc { color: var(--text-3); font: 11.5px var(--mono); margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex-shrink: 1000; }
534
639
  .sw-acts { display: inline-flex; align-items: center; gap: 2px; align-self: center; flex: none; }
535
640
  .sw-acts .icon, .sw-pill .icon {
536
641
  display: inline-grid; place-items: center; padding: 0; border: 0; background: transparent; color: var(--text-2);
@@ -818,12 +923,10 @@ button.o-trust:hover { color: var(--accent); background: var(--code-bg); }
818
923
  .modal-title { font-weight: 600; font-size: 15px; }
819
924
  .modal-text { color: var(--text-2); font-size: 12.5px; line-height: 1.45; }
820
925
  /* Export (web/src/exportmodal.ts): which view, what its document holds, its title and author line,
821
- the common settings, then the job */
822
- #export-modal {
823
- position: fixed; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center;
824
- background: rgba(0, 0, 0, 0.22);
825
- }
826
- .ex-box { width: min(580px, calc(100vw - 32px)); max-height: 90vh; overflow: auto; outline: none; }
926
+ the common settings, then the job. At the right, over nothing but the panel: the board is drawn
927
+ in the linear form beside it while it is open (ExportSpec decision 18) */
928
+ #export-modal { position: fixed; top: 106px; right: 12px; z-index: 40; pointer-events: none; }
929
+ .ex-box { width: min(480px, calc(100vw - 32px)); max-height: calc(100vh - 130px); overflow: auto; outline: none; pointer-events: auto; }
827
930
  .ex-head { display: flex; align-items: center; justify-content: space-between; }
828
931
  .ex-sec { display: flex; flex-direction: column; gap: 6px; }
829
932
  .ex-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px 10px; }
@@ -1687,7 +1790,7 @@ details.lf-archived > summary { cursor: pointer; }
1687
1790
  word-break: break-word;
1688
1791
  }
1689
1792
  .p-ns { color: var(--text-2); font-size: 12.5px; word-break: break-all; }
1690
- .p-mod { color: var(--text-3); font-size: 12px; margin: 6px 0 12px; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
1793
+ .p-mod { color: var(--text-3); font-size: 12px; margin: 6px 0 12px; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; overflow-wrap: anywhere; }
1691
1794
  .p-gen { color: var(--text-2); font-size: 12px; margin: -6px 0 12px; line-height: 1.45; }
1692
1795
  .sw-print { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
1693
1796
  .sw-print-h { color: var(--text-3); font-size: 12px; margin-bottom: 6px; font-family: var(--mono, ui-monospace, monospace); }
@@ -2310,3 +2413,136 @@ span.tm-row { color: var(--text-2); cursor: default; }
2310
2413
  @media (max-width: 1350px) { .tm-growth, .tm-w { display: none; } }
2311
2414
  @media (max-width: 1150px) { .tm-view { display: none; } .tm-n { padding: 0 6px; } }
2312
2415
  @media (max-width: 900px) { .tm-btn[data-tm-save] { display: none; } }
2416
+
2417
+ /* ---------------------------------------------------------------- a phone (PhoneSpec)
2418
+
2419
+ At most 640 px wide: a page for reading and reviewing (web/src/phone.ts). The bar keeps Back, the
2420
+ project's name, search and ⋯, which holds the modes and the rest; the side panel is a drawer from
2421
+ ☰; the panel is a sheet from the bottom, the board above it. */
2422
+ .more-wrap, .side-btn, .side-back { display: none; }
2423
+ @media (max-width: 640px) {
2424
+ /* over the sheet, which stops under it at its tallest, so that what opens from the bar (⋯, the
2425
+ search's results, Share) opens over the sheet too */
2426
+ #bar { top: 8px; left: 8px; right: 8px; height: 44px; gap: 6px; padding: 0 6px 0 4px; z-index: 31; }
2427
+ #bar .right { gap: 4px; }
2428
+ #nav-fwd, .brand .logo { display: none; }
2429
+ .brand { align-items: center; gap: 6px; }
2430
+ .brand .project { display: inline; font-weight: 600; font-size: 15px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2431
+ #share-btn, #library-btn, #export-btn, #activity-btn, #who, #help-btn { display: none !important; }
2432
+ .more-wrap { display: block; position: relative; }
2433
+ #more-btn { width: 38px; height: 34px; }
2434
+ #more-badge { position: absolute; top: -5px; right: -5px; }
2435
+ #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
+ #more-pop[hidden] { display: none; }
2437
+ .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
+ .more-item:active { background: var(--code-bg); }
2439
+ .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; }
2440
+ .more-item .act-badge { position: static; margin-left: auto; }
2441
+ /* the modes, at the top of ⋯: each with what a tap selects under its name, the one in use ticked */
2442
+ #more-pop { max-height: calc(100dvh - 70px); overflow-y: auto; }
2443
+ .more-h { padding: 6px 12px 2px; font-size: 12px; font-weight: 600; color: var(--text-2); }
2444
+ .more-sub { padding: 6px 12px 0 36px; font-size: 12px; color: var(--text-2); }
2445
+ .more-mode { gap: 6px; padding: 4px 12px; }
2446
+ .more-mode.step { padding-left: 28px; }
2447
+ .more-mode.on { color: var(--accent); }
2448
+ .more-tick { flex: none; width: 16px; text-align: center; font-weight: 700; }
2449
+ .more-l { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
2450
+ .more-l small { font-size: 12px; color: var(--text-2); }
2451
+ .more-sep { height: 1px; margin: 6px 4px; background: var(--line); }
2452
+ /* search: a narrow field that takes the whole bar while it is used */
2453
+ #search { width: 92px; height: 34px; padding: 0 10px; }
2454
+ .search-wrap:focus-within { position: fixed; top: 8px; left: 8px; right: 8px; z-index: 45; }
2455
+ .search-wrap:focus-within #search { width: 100%; height: 44px; font-size: 16px; box-shadow: var(--shadow-lg); }
2456
+ #results { position: fixed; top: 58px; left: 8px; right: 8px; width: auto; max-height: calc(100dvh - 70px); }
2457
+ /* every popover of the bar and the view bar: a sheet under the bar, the screen's width */
2458
+ #share-pop, #library-pop, #who-menu, #peer-menu, #views-menu, #focus-menu {
2459
+ position: fixed !important; top: 58px !important; left: 8px !important; right: 8px !important; bottom: auto !important;
2460
+ width: auto !important; max-width: none !important; max-height: calc(100dvh - 70px); overflow: auto;
2461
+ }
2462
+ #viewbar { top: 58px; left: 8px; right: 8px; gap: 6px; }
2463
+ .side-btn { display: inline-flex; flex: none; width: 36px; justify-content: center; padding: 0; }
2464
+ #tabs { flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
2465
+ #tabs::-webkit-scrollbar { display: none; }
2466
+ .vb-gap, .light-l { display: none; }
2467
+ .focus-btn { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2468
+ /* the modes are in ⋯ */
2469
+ #lightup { display: none; }
2470
+ #zoom { right: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); }
2471
+ /* ⋯ opens upwards from its box, at the right (the box's blur makes it the popover's frame) */
2472
+ #eyes-pop { right: 0; bottom: calc(100% + 8px); width: min(280px, calc(100vw - 16px)); min-width: 0; background: var(--surface-solid); }
2473
+ #zoom #eyes-pop button.eye { min-height: 40px; }
2474
+ /* no finger moves anything: the lock means nothing here */
2475
+ #lock-btn { display: none !important; }
2476
+ /* the help: gestures, as large as the screen allows */
2477
+ .help-box { width: calc(100vw - 16px); max-height: calc(100dvh - 32px); background: var(--surface-solid); }
2478
+ /* no keyboard: no keys beside the menus' items */
2479
+ #eyes-pop kbd { display: none; }
2480
+ .help-touch .help-row { grid-template-columns: 116px minmax(0, 1fr); font-size: 14px; padding: 5px 0; }
2481
+ .help-touch .help-g { color: var(--text); font-weight: 600; }
2482
+ #zoom #zoom-in, #zoom #zoom-out { display: none; }
2483
+ #foot { left: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); }
2484
+ /* under the view bar, the review strip, then the selection bar (--strip-h: main.ts) */
2485
+ #lensbar, #tm-kept { top: 100px; left: 8px; right: 8px; transform: none; max-width: none; }
2486
+ #selbar { top: calc(100px + var(--strip-h, 0px)); left: 8px; right: 8px; width: auto; }
2487
+ /* the panel: a sheet from the bottom, its height the reader's to drag */
2488
+ #panel {
2489
+ top: auto; left: 0; right: 0; bottom: 0; width: auto; height: var(--sheet-h, 55dvh); max-height: calc(100dvh - 60px);
2490
+ border-radius: 18px 18px 0 0; padding: 26px 16px calc(24px + env(safe-area-inset-bottom)); z-index: 30;
2491
+ background: var(--surface-solid); box-shadow: 0 -1px 0 var(--line), 0 -8px 30px rgba(0, 0, 0, 0.12);
2492
+ transform: translateY(105%); transition: transform 0.32s cubic-bezier(0.2, 0.9, 0.25, 1), height 0.2s;
2493
+ }
2494
+ #panel.open { transform: none; }
2495
+ #panel::before { content: ""; position: sticky; top: -18px; display: block; width: 40px; height: 5px; margin: -18px auto 13px; border-radius: 3px; background: var(--text-3); opacity: 0.6; }
2496
+ /* the side panel: a drawer from the left, over a shade */
2497
+ body.side-open #side { display: flex; position: fixed; top: 0; left: 0; bottom: 0; width: min(320px, 86vw); border-radius: 0 16px 16px 0; z-index: 47; padding-top: env(safe-area-inset-top); background: var(--surface-solid); }
2498
+ body.side-open::after { content: ""; position: fixed; inset: 0; z-index: 46; background: rgba(0, 0, 0, 0.3); }
2499
+ /* ⚙ Board and Trusted: in the drawer's place, over it, with a way back */
2500
+ body.side-open .side-pop {
2501
+ left: 0 !important; top: 0 !important; bottom: 0 !important; right: auto !important; width: min(320px, 86vw) !important;
2502
+ height: auto !important; max-height: none !important; border-radius: 0 16px 16px 0; z-index: 48;
2503
+ background: var(--surface-solid); padding-top: calc(8px + env(safe-area-inset-top));
2504
+ }
2505
+ body.side-open .side-back {
2506
+ display: flex; align-items: center; gap: 4px; min-height: 40px; margin: 0 0 6px -6px; padding: 0 6px; border: 0;
2507
+ background: none; color: var(--accent); font: 600 15px var(--font);
2508
+ }
2509
+ .side-back svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
2510
+ /* a field under 16 px makes iOS zoom the whole page into it */
2511
+ #panel textarea, #panel input, #share-pop input, #share-pop select, #who-menu input, #modal input, .sh-field input, .sh-field select { font-size: 16px; }
2512
+ /* nothing slides sideways: the page is the screen */
2513
+ html, body { overflow: hidden; overscroll-behavior: none; }
2514
+ #panel { overflow-x: hidden; overscroll-behavior: contain; }
2515
+ /* a source window: the whole screen, its title bar its name and a close */
2516
+ .sw-win:not(.collapsed) {
2517
+ left: 0 !important; top: 0 !important; right: 0 !important; bottom: 0 !important; width: auto !important; height: auto !important;
2518
+ max-width: none !important; max-height: none !important; min-width: 0 !important; min-height: 0 !important;
2519
+ border-radius: 0 !important; padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); z-index: 48;
2520
+ }
2521
+ .sw-win .fl-rz, .sw-ns, .sw-loc, .sw-acts [data-sw-collapse], .sw-pin { display: none !important; }
2522
+ /* the menu: no keys beside its items */
2523
+ #ctxmenu .cm-key { display: none; }
2524
+ /* the tree in the drawer: a row a finger can hit, an arrow wide enough to open or fold a level;
2525
+ hiding a level arranges the board, which a phone does not */
2526
+ #outline .o-row { height: 40px; }
2527
+ #outline .o-row .tw { width: 32px; height: 40px; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; margin-left: -8px; }
2528
+ #outline .o-hide { display: none; }
2529
+ /* the Time Machine: its line, the build and a way out; keeping a build or saving it as a view
2530
+ arranges the board, which a phone does not */
2531
+ body.tm .focus-wrap { display: none; }
2532
+ #tmbar { flex: 1 1 auto; gap: 6px; padding: 0 2px 0 8px; }
2533
+ #tmbar .tm-acts, #tmbar .tm-sum, #tmbar .tm-growth, #tmbar .tm-latest { display: none !important; }
2534
+ #tmbar .tm-x { flex: none; width: 36px; height: 34px; font-size: 16px; }
2535
+ .sw-head .sw-close { width: 40px; height: 40px; font-size: 20px; }
2536
+ /* a long name goes on over a second line, its end (what tells it from its neighbours) in sight;
2537
+ the buttons keep their place at the right */
2538
+ .sw-head { align-items: center; }
2539
+ .sw-name { flex: 1 1 auto; white-space: normal; overflow-wrap: anywhere; }
2540
+ .pw-win:not(.collapsed) { left: 4px !important; right: 4px !important; width: auto !important; }
2541
+ /* Activity and the lost-and-found: the whole screen, from the right */
2542
+ #activity, #lostfound {
2543
+ top: 0; left: 0; right: 0; bottom: 0; width: auto; border-radius: 0; z-index: 48;
2544
+ padding: calc(10px + env(safe-area-inset-top)) 16px calc(24px + env(safe-area-inset-bottom));
2545
+ background: var(--surface-solid); transform: translateX(100%);
2546
+ }
2547
+ .act-head .icon { width: 40px; height: 40px; font-size: 20px; }
2548
+ }
@@ -30,7 +30,7 @@ MCP: `companion`.
30
30
  | `source`, `sourceCollapsed` | the source window following their selection: open; collapsed to its pill |
31
31
  | `pinned` | pinned source windows, first pinned first, `{declaration, collapsed}`; followers mirror them (an agent pins none) |
32
32
  | `history` | `{snapshot, built}`: the build they browse in the Time Machine |
33
- | `activity`, `lostAndFound`, `colouring`, `edges`, `proofEdges` | what else followers take, where their page says it (a page from before, and an agent, say none): `activity` `{open, kind, who, mine?}` (kind: all, review, build, change; who: all, agents, people; `mine`: the person whose targets it keeps to); `lostAndFound` `{open}`; `colouring` `kind` or `status`; edges drawn or not |
33
+ | `activity`, `lostAndFound`, `colouring`, `edges` | what else followers take, where their page says it (a page from before, and an agent, say none): `activity` `{open, kind, who, mine?}` (kind: all, review, build, change; who: all, agents, people; `mine`: the person whose targets it keeps to); `lostAndFound` `{open}`; `colouring` `kind` or `status`; edges drawn or not |
34
34
  | `inWords` | the place in words; of the row above, only what differs from a fresh page |
35
35
 
36
36
  `wait=<s>` (≤ 60) waits until someone comes, goes, goes idle or moves (view, selection,
@@ -80,7 +80,7 @@ Fields go over your own place (none: your place): every `POST view` field but `l
80
80
  | `history` | a snapshot id: the Time Machine at that build; `null`: none |
81
81
  | `region` | `[x, y, w, h]`: centre, width, height in the board's world (a top-level cluster's place is its `at` in `views/view`, a nested one's its parents' added up), fitted to the opener's screen. Left out, framed as for any link |
82
82
  | `activity` | `{"open": true, "kind": "review", "who": "people"}`, values as in `companion` |
83
- | `lostFound`, `scheme`, `edges`, `proofEdges` | the lost-and-found open; the colouring, `kind` or `status`; edges, proof edges drawn |
83
+ | `lostFound`, `scheme`, `edges` | the lost-and-found open; the colouring, `kind` or `status`; edges drawn |
84
84
  | `exact` | `false`: the view's address alone (Share's switch off) |
85
85
 
86
86
  ```json
@@ -34,14 +34,15 @@ board holds, else `null`. Your own focus (`POST view {focus}`) changes nothing o
34
34
 
35
35
  ## The query language
36
36
 
37
- `all`, `tcb(A, B)`, `uses(A)`, `usedBy(A)`, `decl(A, B)`, `key(T)` (the target and the declarations
37
+ `all`, `tcb(A, B)`, `uses(A)` (everything used), `usedBy(A)`, `refs(A)` (A and what it uses
38
+ directly: the page's Direct), `decl(A, B)`, `key(T)` (the target and the declarations
38
39
  marked key for it that its review still holds; `key(targets)`: every target's), `ns(P)`, `file(P)`,
39
40
  `trusted`, `external`, `kind(theorem, def)`, `status(unreviewed|approved|commented|stale)`,
40
41
  `advice(approve|comment)`, `sorry`, `nonstandard`; joined left to right by `and`, `or`, `except`;
41
42
  parentheses group; a closure may take a query (`tcb(ns(P) except external)`); a name that needs it
42
43
  is in JSON quotes. A target's review set: `tcb(T) except trusted or (uses(T) and kind(axiom) and
43
44
  nonstandard)`. At most 20,000 characters, 32 levels of nesting, 1,000 parts, 2,000 names, 64
44
- closures (`tcb`, `uses`, `usedBy`, each target of a `key(…)`); beyond, 400.
45
+ closures (`tcb`, `uses`, `usedBy`, `refs`, each target of a `key(…)`); beyond, 400.
45
46
 
46
47
  ## The endpoints
47
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "visualproof",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",
@@ -1,5 +1,5 @@
1
1
  {
2
- "policy": "The last six or so stable Lean releases, and whatever the showcase project needs (v4.28.0, for the sphere packing formalization); the newest patch of each minor release.",
2
+ "policy": "The last six or so stable Lean releases, and whatever the showcase project needs (v4.34.0-rc2, for NavierStokesAndEuler); v4.28.0, for the sphere packing formalization (the showcase before it), stays the floor; the newest patch of each minor release.",
3
3
  "floor": "v4.28.0",
4
- "toolchains": ["v4.28.0", "v4.29.1", "v4.30.0", "v4.31.0", "v4.32.2", "v4.33.1", "v4.34.1", "v4.35.0"]
4
+ "toolchains": ["v4.28.0", "v4.29.1", "v4.30.0", "v4.31.0", "v4.32.2", "v4.33.1", "v4.34.0-rc2", "v4.34.1", "v4.35.0"]
5
5
  }