@cstart/coldstart 2.2.18 → 2.3.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 (48) hide show
  1. package/README.md +19 -0
  2. package/dist/cli.d.ts +9 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +44 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/graph/dump.d.ts +63 -0
  7. package/dist/graph/dump.d.ts.map +1 -0
  8. package/dist/graph/dump.js +183 -0
  9. package/dist/graph/dump.js.map +1 -0
  10. package/dist/graph/view-template.d.ts +10 -0
  11. package/dist/graph/view-template.d.ts.map +1 -0
  12. package/dist/graph/view-template.js +10 -0
  13. package/dist/graph/view-template.js.map +1 -0
  14. package/dist/graph/view.d.ts +20 -0
  15. package/dist/graph/view.d.ts.map +1 -0
  16. package/dist/graph/view.js +99 -0
  17. package/dist/graph/view.js.map +1 -0
  18. package/dist/index-manager.d.ts +16 -0
  19. package/dist/index-manager.d.ts.map +1 -1
  20. package/dist/index-manager.js +50 -0
  21. package/dist/index-manager.js.map +1 -1
  22. package/dist/index.js +8 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/indexer/cochange.d.ts +65 -0
  25. package/dist/indexer/cochange.d.ts.map +1 -0
  26. package/dist/indexer/cochange.js +237 -0
  27. package/dist/indexer/cochange.js.map +1 -0
  28. package/dist/kb/view-template.d.ts +1 -1
  29. package/dist/kb/view-template.d.ts.map +1 -1
  30. package/dist/kb/view-template.js +1 -1
  31. package/dist/kb/view-template.js.map +1 -1
  32. package/dist/kb/view.d.ts +1 -0
  33. package/dist/kb/view.d.ts.map +1 -1
  34. package/dist/kb/view.js +51 -1
  35. package/dist/kb/view.js.map +1 -1
  36. package/dist/server/mcp.d.ts.map +1 -1
  37. package/dist/server/mcp.js +10 -4
  38. package/dist/server/mcp.js.map +1 -1
  39. package/dist/server/tools.d.ts +6 -1
  40. package/dist/server/tools.d.ts.map +1 -1
  41. package/dist/server/tools.js +63 -3
  42. package/dist/server/tools.js.map +1 -1
  43. package/dist/status.d.ts.map +1 -1
  44. package/dist/status.js +8 -0
  45. package/dist/status.js.map +1 -1
  46. package/package.json +1 -1
  47. package/templates/coldstart.cli.md +2 -0
  48. package/templates/coldstart.mcp.md +2 -0
package/README.md CHANGED
@@ -160,6 +160,25 @@ Returns the file's symbols (with line ranges), its 1-hop internal imports, who i
160
160
 
161
161
  **Flags:** `--symbol a,b` (deliver named method bodies inline) · `--match TERM` (filter a god-file to one area; `a|b` = OR, `/regex/` = regex) · `--view symbols|imports|importers|callers` · `--json`
162
162
 
163
+ ### `graph` — see the whole repo at once
164
+
165
+ ```bash
166
+ coldstart graph
167
+ ```
168
+
169
+ For humans, not agents. Writes one self-contained HTML file and opens it: every indexed file is a
170
+ point on a sphere, positioned by directory, and clicking one opens a 2D view of everything it is
171
+ connected to — with each relation *named* (`imports`, `calls save(), load()`,
172
+ `edited together · 13 of 42 commits`, `same note · <title>`). Click a neighbour to open its
173
+ connections; click one of those and the view slides along, so you always see two levels rather than
174
+ an ever-growing hairball.
175
+
176
+ **No dependencies.** The page is HTML, CSS, and a canvas script with your repo's data baked in —
177
+ no server, no build step, and nothing leaves your machine. Mail it, drop it in a gist, open it on a
178
+ plane. [Try it on coldstart's own codebase.](https://coldstartmcp.dev/graph/)
179
+
180
+ **Flags:** `--out PATH` (default `.coldstart/graph.html`, gitignored for you) · `--no-open` · `--json`
181
+
163
182
  ### Batch independent lookups in one shell call
164
183
 
165
184
  ```bash
package/dist/cli.d.ts CHANGED
@@ -12,6 +12,15 @@ type BuildFn = (rootDir: string, excludes: string[], includes: string[], quiet:
12
12
  export declare function getIndex(root: string, cacheDir: string | undefined, buildIndex: BuildFn, profile?: LoadProfile): Promise<CodebaseIndex | null>;
13
13
  export declare function runGs(argv: string[], buildIndex: BuildFn): Promise<number>;
14
14
  export declare function runFind(argv: string[], buildIndex: BuildFn): Promise<number>;
15
+ /**
16
+ * `coldstart graph` — write a self-contained HTML page of the repo's file graph
17
+ * and open it. Human-facing only: this is a picture, not an agent surface, so
18
+ * it is deliberately NOT an MCP tool (same call as `kb view`).
19
+ *
20
+ * Uses the `gs` load profile because it needs exactly what `gs` needs — edges,
21
+ * symbol edges, and the co-change sidecar.
22
+ */
23
+ export declare function runGraph(argv: string[], buildIndex: BuildFn): Promise<number>;
15
24
  /**
16
25
  * Consumer counts for the capture hook's "no consumers in import graph"
17
26
  * worklist annotation: per path, how many files the graph knows import it.
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAeA,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,KAAK,OAAO,GAAG,CACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,aAAa,CAAC,CAAC;AAoD5B;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,OAAO,EACnB,OAAO,GAAE,WAAoB,GAC5B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmD/B;AAgBD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBhF;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBlF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBxF;AAED,wEAAwE;AACxE,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAcvF"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAeA,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,KAAK,OAAO,GAAG,CACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,aAAa,CAAC,CAAC;AAwD5B;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,OAAO,EACnB,OAAO,GAAE,WAAoB,GAC5B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmD/B;AAgBD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBhF;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBlF;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BnF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBxF;AAED,wEAAwE;AACxE,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAcvF"}
package/dist/cli.js CHANGED
@@ -62,6 +62,12 @@ function parseQueryArgs(argv) {
62
62
  case '--via':
63
63
  flags.via = true;
64
64
  break;
65
+ case '--out':
66
+ flags.out = argv[++i];
67
+ break;
68
+ case '--no-open':
69
+ flags.noOpen = true;
70
+ break;
65
71
  default:
66
72
  if (a.startsWith('--'))
67
73
  err(`[coldstart] unknown flag: ${a}`);
@@ -159,12 +165,13 @@ export async function runGs(argv, buildIndex) {
159
165
  err('[coldstart] no index available');
160
166
  return 1;
161
167
  }
168
+ const { loadCoChange } = await import('./indexer/cochange.js');
162
169
  const result = handleGetStructure(index, {
163
170
  file_path: file,
164
171
  match: flags.match,
165
172
  symbol: flags.symbol,
166
173
  view: flags.view,
167
- });
174
+ }, loadCoChange(root, flags.cacheDir));
168
175
  emit(result, flags.json === true);
169
176
  // file-not-found (no __rawText, only error) → exit 2
170
177
  return result.error && result.__rawText === undefined ? 2 : 0;
@@ -189,6 +196,42 @@ export async function runFind(argv, buildIndex) {
189
196
  process.stdout.write((await buildRichPage(index, root, query, flags.json, flags.via === true)) + '\n');
190
197
  return 0;
191
198
  }
199
+ /**
200
+ * `coldstart graph` — write a self-contained HTML page of the repo's file graph
201
+ * and open it. Human-facing only: this is a picture, not an agent surface, so
202
+ * it is deliberately NOT an MCP tool (same call as `kb view`).
203
+ *
204
+ * Uses the `gs` load profile because it needs exactly what `gs` needs — edges,
205
+ * symbol edges, and the co-change sidecar.
206
+ */
207
+ export async function runGraph(argv, buildIndex) {
208
+ const { flags } = parseQueryArgs(argv);
209
+ const root = resolve(flags.root ?? '.');
210
+ await ensureKeeper(root);
211
+ const index = await getIndex(root, flags.cacheDir, buildIndex, 'gs');
212
+ if (!index) {
213
+ err('[coldstart] no index available');
214
+ return 1;
215
+ }
216
+ const { graphView } = await import('./graph/view.js');
217
+ const { path, stats } = graphView(root, index, {
218
+ out: flags.out ? resolve(flags.out) : undefined,
219
+ open: flags.noOpen !== true,
220
+ cacheDir: flags.cacheDir,
221
+ });
222
+ if (flags.json) {
223
+ process.stdout.write(JSON.stringify({ path, ...stats }, null, 2) + '\n');
224
+ return 0;
225
+ }
226
+ const [imp, calls, coedit, note] = stats.byKind;
227
+ process.stdout.write(`${path}\n` +
228
+ ` ${stats.files} files in ${stats.clusters} directory bands, ` +
229
+ `${stats.edges} relations (imports ${imp}, calls ${calls}, edited-together ${coedit}, ` +
230
+ `same-note ${note} from ${stats.notes} notes)\n` +
231
+ ` ${stats.isolated} files have no relation at all.\n` +
232
+ ` Self-contained page — no server and no dependencies. Open it anywhere, or send the file.\n`);
233
+ return 0;
234
+ }
192
235
  /**
193
236
  * Consumer counts for the capture hook's "no consumers in import graph"
194
237
  * worklist annotation: per path, how many files the graph knows import it.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The data half of `coldstart graph` — turn an already-built index into the
3
+ * compact payload the viewer draws.
4
+ *
5
+ * Nothing here builds or parses: it reads the index the keeper already has,
6
+ * plus the co-change sidecar and the notebook, and reduces them to two arrays.
7
+ * Globe positions are NOT dumped — the viewer derives every seat from the band
8
+ * assignment below, so a file lands in the same place on every reload.
9
+ */
10
+ import type { CodebaseIndex } from '../types.js';
11
+ import type { CoChangeIndex } from '../indexer/cochange.js';
12
+ /** [filename, full path, cluster index, degree] */
13
+ export type GraphNode = [string, string, number, number];
14
+ /** [a, b, kind, directed, detail] — kinds below. */
15
+ export type GraphEdge = [number, number, number, number, string];
16
+ export declare const REL_IMPORT = 0;
17
+ export declare const REL_CALLS = 1;
18
+ export declare const REL_COEDIT = 2;
19
+ export declare const REL_NOTE = 3;
20
+ export interface GraphPayload {
21
+ repo: string;
22
+ clusters: string[];
23
+ n: GraphNode[];
24
+ e: GraphEdge[];
25
+ }
26
+ export interface GraphStats {
27
+ files: number;
28
+ clusters: number;
29
+ edges: number;
30
+ byKind: [number, number, number, number];
31
+ isolated: number;
32
+ notes: number;
33
+ }
34
+ /** A note reduced to the file paths it anchors — the caller supplies these so
35
+ * this module never has to load the notebook itself. */
36
+ export interface GraphNoteAnchors {
37
+ title: string;
38
+ paths: string[];
39
+ }
40
+ /**
41
+ * Directory bands, chosen ADAPTIVELY rather than at a fixed depth.
42
+ *
43
+ * A fixed two-segment cut is what a repo's own shape defeats: on arches it put
44
+ * 1,093 of 1,615 files into `arches/app`, so one band swallowed two thirds of
45
+ * the sphere while 14 other bands held fewer than 5 files each. Instead: split
46
+ * any band that is too big one segment deeper until none dominates, then fold
47
+ * the slivers together so the legend stays readable.
48
+ */
49
+ export declare function assignClusters(ids: string[]): Map<string, string>;
50
+ /**
51
+ * Relations, each carrying a NAME so the explorer can say *why* two files are
52
+ * linked instead of drawing an anonymous line.
53
+ *
54
+ * kind 0 import directed "imports" / "imported by"
55
+ * kind 1 calls directed "calls foo(), bar()"
56
+ * kind 2 coedit mutual "edited together · N of M commits"
57
+ * kind 3 note mutual "same note · <title>"
58
+ */
59
+ export declare function buildGraphPayload(repo: string, index: CodebaseIndex, cochange: CoChangeIndex | null, notes: GraphNoteAnchors[]): {
60
+ payload: GraphPayload;
61
+ stats: GraphStats;
62
+ };
63
+ //# sourceMappingURL=dump.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dump.d.ts","sourceRoot":"","sources":["../../src/graph/dump.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,mDAAmD;AACnD,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,oDAAoD;AACpD,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjE,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,CAAC,EAAE,SAAS,EAAE,CAAC;IACf,CAAC,EAAE,SAAS,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;yDACyD;AACzD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAmDjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,aAAa,GAAG,IAAI,EAC9B,KAAK,EAAE,gBAAgB,EAAE,GACxB;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAgG9C"}
@@ -0,0 +1,183 @@
1
+ export const REL_IMPORT = 0;
2
+ export const REL_CALLS = 1;
3
+ export const REL_COEDIT = 2;
4
+ export const REL_NOTE = 3;
5
+ /**
6
+ * Directory bands, chosen ADAPTIVELY rather than at a fixed depth.
7
+ *
8
+ * A fixed two-segment cut is what a repo's own shape defeats: on arches it put
9
+ * 1,093 of 1,615 files into `arches/app`, so one band swallowed two thirds of
10
+ * the sphere while 14 other bands held fewer than 5 files each. Instead: split
11
+ * any band that is too big one segment deeper until none dominates, then fold
12
+ * the slivers together so the legend stays readable.
13
+ */
14
+ export function assignClusters(ids) {
15
+ const N = ids.length;
16
+ const MAX_SHARE = 0.13;
17
+ const MIN_COUNT = Math.max(5, Math.round(N * 0.012));
18
+ const MAX_DEPTH = 5;
19
+ const dirOf = (p) => { const s = p.split('/'); s.pop(); return s; };
20
+ const at = (p, d) => {
21
+ const s = dirOf(p);
22
+ return s.length === 0 ? '/' : s.slice(0, Math.min(d, s.length)).join('/');
23
+ };
24
+ const depth = new Map();
25
+ const assign = new Map(ids.map((id) => [id, at(id, 1)]));
26
+ for (const id of ids)
27
+ depth.set(assign.get(id), 1);
28
+ for (let pass = 0; pass < MAX_DEPTH; pass++) {
29
+ const counts = new Map();
30
+ for (const id of ids)
31
+ counts.set(assign.get(id), (counts.get(assign.get(id)) ?? 0) + 1);
32
+ const oversized = [...counts].filter(([k, c]) => c > N * MAX_SHARE && (depth.get(k) ?? MAX_DEPTH) < MAX_DEPTH);
33
+ if (!oversized.length)
34
+ break;
35
+ for (const [key] of oversized) {
36
+ const d = (depth.get(key) ?? 1) + 1;
37
+ for (const id of ids) {
38
+ if (assign.get(id) !== key)
39
+ continue;
40
+ const sub = at(id, d);
41
+ assign.set(id, sub);
42
+ depth.set(sub, sub === key ? MAX_DEPTH : d);
43
+ }
44
+ }
45
+ }
46
+ for (let pass = 0; pass < 3; pass++) {
47
+ const counts = new Map();
48
+ for (const id of ids)
49
+ counts.set(assign.get(id), (counts.get(assign.get(id)) ?? 0) + 1);
50
+ let changed = false;
51
+ for (const id of ids) {
52
+ const k = assign.get(id);
53
+ if ((counts.get(k) ?? 0) >= MIN_COUNT)
54
+ continue;
55
+ const parent = k.includes('/') ? k.slice(0, k.lastIndexOf('/')) : null;
56
+ const next = parent && counts.has(parent) ? parent : (parent ?? 'other');
57
+ if (next !== k) {
58
+ assign.set(id, next);
59
+ changed = true;
60
+ }
61
+ }
62
+ if (!changed)
63
+ break;
64
+ }
65
+ const counts = new Map();
66
+ for (const id of ids)
67
+ counts.set(assign.get(id), (counts.get(assign.get(id)) ?? 0) + 1);
68
+ for (const id of ids)
69
+ if ((counts.get(assign.get(id)) ?? 0) < MIN_COUNT)
70
+ assign.set(id, 'other');
71
+ return assign;
72
+ }
73
+ /**
74
+ * Relations, each carrying a NAME so the explorer can say *why* two files are
75
+ * linked instead of drawing an anonymous line.
76
+ *
77
+ * kind 0 import directed "imports" / "imported by"
78
+ * kind 1 calls directed "calls foo(), bar()"
79
+ * kind 2 coedit mutual "edited together · N of M commits"
80
+ * kind 3 note mutual "same note · <title>"
81
+ */
82
+ export function buildGraphPayload(repo, index, cochange, notes) {
83
+ const ids = [...index.files.keys()].sort();
84
+ const assigned = assignClusters(ids);
85
+ const clusterOf = (p) => assigned.get(p) ?? 'other';
86
+ const idx = new Map(ids.map((id, i) => [id, i]));
87
+ const rel = new Map();
88
+ const put = (s, t, kind, directed, detail) => {
89
+ const a = idx.get(s), b = idx.get(t);
90
+ if (a === undefined || b === undefined || a === b)
91
+ return;
92
+ const key = directed
93
+ ? a + '|' + b + '|' + kind
94
+ : Math.min(a, b) + '|' + Math.max(a, b) + '|' + kind;
95
+ const prev = rel.get(key);
96
+ if (prev) {
97
+ if (detail && !prev[4])
98
+ prev[4] = detail;
99
+ return;
100
+ }
101
+ rel.set(key, [
102
+ directed ? a : Math.min(a, b),
103
+ directed ? b : Math.max(a, b),
104
+ kind, directed ? 1 : 0, detail,
105
+ ]);
106
+ };
107
+ for (const e of index.edges)
108
+ put(e.from, e.to, REL_IMPORT, true, '');
109
+ // Cross-file calls aggregated to the file pair: a reader wants "calls save(),
110
+ // load()", not 40 anonymous parallel edges. Every file-level edge is a plain
111
+ // import, so symbol edges are the only source of real verbs.
112
+ const symFile = new Map();
113
+ for (const [fid, f] of index.files)
114
+ for (const sym of f.symbols ?? [])
115
+ symFile.set(sym.id, fid);
116
+ const callNames = new Map();
117
+ for (const e of index.symbolEdges ?? []) {
118
+ if (e.type !== 'calls')
119
+ continue;
120
+ const fa = symFile.get(e.from), fb = symFile.get(e.to);
121
+ if (!fa || !fb || fa === fb)
122
+ continue;
123
+ const key = fa + ' ' + fb;
124
+ let set = callNames.get(key);
125
+ if (!set)
126
+ callNames.set(key, set = new Set());
127
+ set.add(e.to.includes('#') ? e.to.slice(e.to.indexOf('#') + 1) : e.to);
128
+ }
129
+ for (const [key, names] of callNames) {
130
+ const [fa, fb] = key.split(' ');
131
+ const list = [...names];
132
+ const shown = list.slice(0, 3).map((n) => n + '()').join(', ');
133
+ put(fa, fb, REL_CALLS, true, list.length > 3 ? shown + ' +' + (list.length - 3) : shown);
134
+ }
135
+ // Edited together — carry the support count, which IS the evidence. Top 3
136
+ // only, matching what `gs` prints; drawing all 8 stored partners triples the
137
+ // ink for pairs no agent is ever told about.
138
+ if (cochange) {
139
+ for (const [f, ps] of Object.entries(cochange.partners)) {
140
+ const outOf = cochange.touched?.[f] ?? 0;
141
+ for (const [p, shared] of ps.slice(0, 3)) {
142
+ put(f, p, REL_COEDIT, false, outOf ? shared + ' of ' + outOf + ' commits' : shared + ' commits');
143
+ }
144
+ }
145
+ }
146
+ // Notebook — a note joins the files it anchors, and names itself. Star from
147
+ // the first anchor rather than all-pairs, so a 12-anchor flow note
148
+ // contributes 11 edges instead of 66.
149
+ let noteCount = 0;
150
+ for (const n of notes) {
151
+ if (n.paths.length < 2)
152
+ continue;
153
+ noteCount++;
154
+ for (let i = 1; i < n.paths.length; i++)
155
+ put(n.paths[0], n.paths[i], REL_NOTE, false, n.title);
156
+ }
157
+ const edges = [...rel.values()];
158
+ const deg = new Int32Array(ids.length);
159
+ for (const [a, b] of edges) {
160
+ deg[a]++;
161
+ deg[b]++;
162
+ }
163
+ const clusters = [...new Set(ids.map(clusterOf))].sort();
164
+ const cIdx = new Map(clusters.map((c, i) => [c, i]));
165
+ const byKind = [0, 1, 2, 3].map((k) => edges.filter((e) => e[2] === k).length);
166
+ return {
167
+ payload: {
168
+ repo,
169
+ clusters,
170
+ n: ids.map((id, i) => [id.split('/').pop(), id, cIdx.get(clusterOf(id)), deg[i]]),
171
+ e: edges,
172
+ },
173
+ stats: {
174
+ files: ids.length,
175
+ clusters: clusters.length,
176
+ edges: edges.length,
177
+ byKind,
178
+ isolated: ids.filter((_, i) => deg[i] === 0).length,
179
+ notes: noteCount,
180
+ },
181
+ };
182
+ }
183
+ //# sourceMappingURL=dump.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dump.js","sourceRoot":"","sources":["../../src/graph/dump.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Embedded single-file codebase graph viewer (client). GENERATED from
3
+ * scripts/graph-viewer/sphere.template.html by
4
+ * scripts/graph-viewer/build-template.mjs — do not hand-edit; edit the HTML and
5
+ * re-run that script. Two placeholders are substituted by `renderGraphHtml`:
6
+ * `__TITLE__` (the tab title) and `__DATA_JSON__` (the baked graph payload).
7
+ * Shipped as a string because tsc does not copy non-.ts assets into dist.
8
+ */
9
+ export declare const GRAPH_TEMPLATE = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>__TITLE__</title>\n<style>\n :root{\n --ink:#05070c;\n --panel:rgba(10,14,23,.84);\n --line:rgba(140,160,200,.16);\n --fg:#e8ecf5;\n --fg-dim:#8b97b0;\n --fg-faint:#5a6480;\n --accent:#ffd479;\n --mono:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,monospace;\n --sans:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;\n }\n *{box-sizing:border-box}\n html,body{height:100%}\n body{\n margin:0;background:var(--ink);color:var(--fg);\n font-family:var(--sans);overflow:hidden;-webkit-font-smoothing:antialiased;\n }\n canvas{display:block;position:fixed;inset:0;cursor:grab}\n canvas.dragging{cursor:grabbing}\n canvas.pointing{cursor:pointer}\n\n .panel{\n position:fixed;background:var(--panel);backdrop-filter:blur(14px);\n border:1px solid var(--line);border-radius:10px;\n }\n\n #head{top:18px;left:18px;padding:14px 16px;min-width:236px}\n #home{\n display:inline-block;margin:0 0 9px;font-size:11px;letter-spacing:.03em;\n color:var(--fg-faint);text-decoration:none;\n }\n #home:hover{color:var(--accent)}\n #head h1{\n margin:0 0 2px;font-size:11px;font-weight:600;letter-spacing:.14em;\n text-transform:uppercase;color:var(--fg-dim);\n }\n #repo{\n font-family:var(--mono);font-size:19px;color:var(--fg);background:none;\n border:none;padding:0;margin:0 0 10px;cursor:pointer;display:block;\n }\n #repo:hover{color:var(--accent)}\n #repo::after{content:\" \u21C4\";font-size:12px;color:var(--fg-faint)}\n .stat{display:flex;justify-content:space-between;gap:18px;font-size:12px;line-height:1.75}\n .stat b{font-family:var(--mono);font-weight:500;font-variant-numeric:tabular-nums}\n .stat span{color:var(--fg-dim)}\n .stat.total b{font-size:15px}\n\n #legend{\n top:18px;right:18px;padding:12px 14px;width:212px;\n max-height:calc(100vh - 36px);overflow-y:auto;\n }\n #legend h2{\n margin:0 0 8px;font-size:10px;font-weight:600;letter-spacing:.14em;\n text-transform:uppercase;color:var(--fg-dim);\n }\n .cl{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:11.5px;cursor:pointer;color:var(--fg-dim)}\n .cl:hover,.cl.on{color:var(--fg)}\n .cl .dot{width:8px;height:8px;border-radius:50%;flex:none}\n .cl .nm{font-family:var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}\n .cl .ct{margin-left:auto;font-family:var(--mono);font-size:10.5px;color:var(--fg-faint)}\n\n #search{top:18px;left:50%;transform:translateX(-50%);width:min(420px,50vw);padding:0}\n #q{width:100%;background:none;border:none;outline:none;color:var(--fg);font-family:var(--mono);font-size:13px;padding:11px 14px}\n #q::placeholder{color:var(--fg-faint)}\n #hits{max-height:46vh;overflow-y:auto;border-top:1px solid var(--line)}\n #hits:empty{display:none}\n .hit{padding:7px 14px;cursor:pointer;font-size:12px;display:flex;align-items:baseline;gap:8px}\n .hit:hover{background:rgba(255,212,121,.1)}\n .hit b{font-family:var(--mono);font-weight:500}\n .hit i{font-style:normal;color:var(--fg-faint);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n /* ---- map-mode chrome ---- */\n #mapbar{top:18px;left:18px;padding:13px 15px;width:340px;display:none}\n body.map #mapbar{display:block}\n body.map #head,body.map #legend{display:none}\n #crumb{font-size:11px;color:var(--fg-faint);margin-bottom:9px;line-height:1.7}\n #crumb b{color:var(--fg-dim);font-family:var(--mono);font-weight:500;cursor:pointer}\n #crumb b:hover{color:var(--accent)}\n .who{font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--fg-faint);margin-bottom:3px}\n #mapfile{font-family:var(--mono);font-size:15px;word-break:break-all;color:var(--accent)}\n #mappath{font-family:var(--mono);font-size:11px;color:var(--fg-faint);word-break:break-all}\n #expblock{margin-top:11px;padding-top:10px;border-top:1px solid var(--line);display:none}\n #expblock.on{display:block}\n #expfile{font-family:var(--mono);font-size:13.5px;word-break:break-all;color:#8fd3ff}\n #exppath{font-family:var(--mono);font-size:11px;color:var(--fg-faint);word-break:break-all}\n #rels{border-top:1px solid var(--line);padding-top:9px;margin-top:11px}\n .rel{display:flex;gap:8px;align-items:center;font-size:11.5px;padding:2.5px 0}\n .rel .sw{width:15px;height:2px;border-radius:2px;flex:none}\n .rel span{color:var(--fg-dim)}\n .rel b{margin-left:auto;font-family:var(--mono);font-weight:500;font-variant-numeric:tabular-nums}\n #back{\n margin-top:12px;width:100%;padding:7px;border-radius:7px;cursor:pointer;\n background:rgba(255,212,121,.1);border:1px solid rgba(255,212,121,.3);\n color:var(--accent);font-family:var(--mono);font-size:11.5px;\n }\n #back:hover{background:rgba(255,212,121,.18)}\n\n #hint{\n position:fixed;bottom:18px;left:50%;transform:translateX(-50%);\n font-size:11px;color:var(--fg-faint);pointer-events:none;text-align:center;\n }\n #tip{\n position:fixed;pointer-events:none;background:rgba(5,7,12,.94);\n border:1px solid var(--line);border-radius:5px;padding:4px 8px;\n font-family:var(--mono);font-size:11px;display:none;z-index:9;white-space:nowrap;\n }\n @media (max-width:900px){#legend{display:none}#search{width:56vw}}\n</style>\n</head>\n<body>\n<canvas id=\"c\"></canvas>\n\n<div class=\"panel\" id=\"head\">\n <!-- Replaced with a link home when this page is generated for the website;\n the CLI leaves it empty, since there is nowhere to go back to. -->\n <!--__HOME_LINK__-->\n <h1>Codebase sphere</h1>\n <button id=\"repo\"></button>\n <div class=\"stat total\"><span>files indexed</span><b id=\"s-files\">\u2014</b></div>\n <div class=\"stat\"><span>relations</span><b id=\"s-rel\">\u2014</b></div>\n <div class=\"stat\"><span>directories</span><b id=\"s-dirs\">\u2014</b></div>\n</div>\n\n<div class=\"panel\" id=\"mapbar\">\n <div id=\"crumb\"></div>\n <div class=\"who\">centre</div>\n <div id=\"mapfile\"></div>\n <div id=\"mappath\"></div>\n <div id=\"expblock\">\n <div class=\"who\">opened</div>\n <div id=\"expfile\"></div>\n <div id=\"exppath\"></div>\n </div>\n <div id=\"rels\"></div>\n <button id=\"back\">\u2190 back to the sphere</button>\n</div>\n\n<div class=\"panel\" id=\"search\">\n <input id=\"q\" placeholder=\"search a file\u2026\" autocomplete=\"off\" spellcheck=\"false\">\n <div id=\"hits\"></div>\n</div>\n\n<div class=\"panel\" id=\"legend\"><h2>Directories</h2><div id=\"cls\"></div></div>\n\n<div id=\"hint\"></div>\n<div id=\"tip\"></div>\n\n<script>\nconst DATA = __DATA_JSON__;\nconst TAU = Math.PI * 2;\nconst GOLDEN = Math.PI * (3 - Math.sqrt(5));\n\n// Relation vocabulary. Colour is semantic ONLY in map mode, where the name is\n// written next to the line anyway \u2014 the globe is deliberately polychrome.\nconst REL = [\n { name:'imports', rev:'imported by', hex:'#7ab2ff', col:[122,178,255] },\n { name:'calls', rev:'called by', hex:'#c39cff', col:[195,156,255] },\n { name:'edited together', rev:'edited together', hex:'#ff9d6e', col:[255,157,110] },\n { name:'same note', rev:'same note', hex:'#8fe9a8', col:[143,233,168] },\n];\n\n/**\n * Palette sets. White is deliberately absent from both: it is the brightest\n * value on a dark ground, so it out-shouts every hue regardless of which\n * directory happens to receive it \u2014 and the map view already spends white on\n * hover and labels. A colour used for interaction should never also encode data.\n *\n * Lightness is hand-compensated per hue, not held constant: at equal HSL\n * lightness yellow reads far brighter than blue, which makes a sphere look\n * unevenly lit. These are tuned to sit at roughly the same perceived weight.\n */\nconst PALETTES = {\n // adjacent hues in path order \u2014 the sphere reads as one continuous object\n aurora: [\n { h: 265, s: 0.70, l: 0.70 }, // violet\n { h: 218, s: 0.88, l: 0.64 }, // blue\n { h: 176, s: 0.68, l: 0.53 }, // teal\n { h: 132, s: 0.60, l: 0.57 }, // green\n { h: 44, s: 0.92, l: 0.56 }, // amber\n { h: 350, s: 0.86, l: 0.66 }, // rose\n ],\n // far-apart hues in path order \u2014 each zone maximally distinct\n jewel: [\n { h: 214, s: 0.90, l: 0.64 }, // blue\n { h: 344, s: 0.86, l: 0.66 }, // rose\n { h: 44, s: 0.94, l: 0.56 }, // amber\n { h: 158, s: 0.64, l: 0.54 }, // emerald\n { h: 282, s: 0.74, l: 0.70 }, // violet\n { h: 14, s: 0.86, l: 0.63 }, // coral\n ],\n};\nlet PALETTE_NAME = 'aurora';\n\nlet M = null;\nlet repoKeys = Object.keys(DATA), repoAt = 0;\nlet mode = 'globe';\nlet fade = 0;\n\nfunction hslToRgb(h, s, l) {\n h = ((h % 360) + 360) % 360 / 360;\n const f = (p, q, t) => {\n if (t < 0) t += 1; if (t > 1) t -= 1;\n if (t < 1/6) return p + (q - p) * 6 * t;\n if (t < 1/2) return q;\n if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;\n return p;\n };\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q;\n return [Math.round(f(p,q,h+1/3)*255), Math.round(f(p,q,h)*255), Math.round(f(p,q,h-1/3)*255)];\n}\n\nfunction buildModel(key) {\n const d = DATA[key];\n const n = d.n.length;\n const px = new Float32Array(n), py = new Float32Array(n), pz = new Float32Array(n);\n const cluster = new Int16Array(n), deg = new Int32Array(n);\n for (let i = 0; i < n; i++) { cluster[i] = d.n[i][2]; deg[i] = d.n[i][3]; }\n\n // Equal-area latitude bands: y is uniform on a unit sphere, so a band's\n // share of y IS its share of surface \u2014 a directory with twice the files\n // gets twice the territory, with no polar distortion.\n const counts = new Array(d.clusters.length).fill(0);\n for (let i = 0; i < n; i++) counts[cluster[i]]++;\n const bandY = [];\n let acc = 0;\n for (let c = 0; c < d.clusters.length; c++) {\n const y0 = 1 - 2 * (acc / n);\n acc += counts[c];\n bandY.push([y0, 1 - 2 * (acc / n)]);\n }\n\n let seed = 0x9e3779b9;\n const rnd = () => { seed ^= seed<<13; seed^=seed>>>17; seed^=seed<<5; return (seed>>>0)/4294967296; };\n const placed = new Array(d.clusters.length).fill(0);\n const tint = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n const c = cluster[i], j = placed[c]++;\n const t = (j + 0.5) / counts[c];\n const [y0, y1] = bandY[c], gap = y0 - y1;\n const y = y0 - gap * (0.06 + 0.88 * t) - gap * 0.06 * (rnd() - 0.5);\n const r = Math.sqrt(Math.max(0, 1 - y * y));\n const lon = j * GOLDEN + c * 1.7;\n px[i] = r * Math.cos(lon); py[i] = y; pz[i] = r * Math.sin(lon);\n tint[i] = rnd();\n }\n\n // Six named colours on a cycle rather than an even walk of the hue wheel.\n // The walk put most bands in the green-yellow arc and made neighbours differ\n // by only a few degrees; cycling a fixed palette guarantees that adjacent\n // latitude bands are always strongly different.\n const PALETTE = PALETTES[PALETTE_NAME];\n // Contiguous ZONES, not a repeating cycle. Cycling every band scattered the\n // six colours into confetti and threw away the one thing the layout encodes\n // \u2014 latitude is the directory. Six blocks of consecutive bands keep the\n // sphere ordered top-to-bottom while still using all six colours.\n const perZone = Math.max(1, Math.ceil(d.clusters.length / PALETTE.length));\n const zoneOf = (c) => Math.min(PALETTE.length - 1, Math.floor(c / perZone));\n // position within the zone, -0.5..0.5, so the four bands sharing a colour\n // still separate from each other\n const stepOf = (c) => (perZone === 1 ? 0 : ((c % perZone) / (perZone - 1)) - 0.5);\n const bandRgb = d.clusters.map((_, i) => {\n const z = PALETTE[zoneOf(i)];\n return hslToRgb(z.h + stepOf(i) * 22, z.s, z.l + stepOf(i) * 0.16);\n });\n const rgb = new Uint8Array(n * 3);\n for (let i = 0; i < n; i++) {\n const base = PALETTE[zoneOf(cluster[i])];\n const step = stepOf(cluster[i]);\n // vary WITHIN the colour so a band has depth but never drifts into the\n // neighbouring band's identity\n const h = base.h + step * 22 + (tint[i] - 0.5) * 10;\n const s = Math.min(1, base.s * (0.88 + tint[i] * 0.24));\n const l = Math.min(0.94, base.l + step * 0.16 + (tint[i] - 0.5) * 0.10 + Math.min(0.07, deg[i] * 0.005));\n const c = hslToRgb(h, s, l);\n rgb[i*3] = c[0]; rgb[i*3+1] = c[1]; rgb[i*3+2] = c[2];\n }\n\n const adj = Array.from({ length: n }, () => []);\n d.e.forEach((e, i) => { adj[e[0]].push(i); adj[e[1]].push(i); });\n\n return {\n key, repo: d.repo, nodes: d.n, n, px, py, pz, cluster, deg, rgb,\n edges: d.e, adj, clusters: d.clusters, counts, bandRgb,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Canvas\n// ---------------------------------------------------------------------------\nconst cvs = document.getElementById('c');\nconst ctx = cvs.getContext('2d', { alpha: false });\nlet W = 0, H = 0, DPR = 1;\nfunction resize() {\n DPR = Math.min(devicePixelRatio || 1, 2);\n W = innerWidth; H = innerHeight;\n cvs.width = W * DPR; cvs.height = H * DPR;\n cvs.style.width = W + 'px'; cvs.style.height = H + 'px';\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0);\n}\naddEventListener('resize', resize);\n\n// ---------------------------------------------------------------------------\n// GLOBE \u2014 points only. No chords, no standing labels: the sphere's job is to\n// show the whole repo at once and be beautiful; naming happens on hover.\n// ---------------------------------------------------------------------------\nconst cam = { yaw: 0.6, pitch: -0.25, dist: 3.0 };\nlet hovered = -1, hotCluster = -1, lastInteract = 0;\nlet sxb, syb, szb;\n\nfunction projectGlobe() {\n const cy = Math.cos(cam.yaw), sy = Math.sin(cam.yaw);\n const cp = Math.cos(cam.pitch), sp = Math.sin(cam.pitch);\n const cx = W/2, cyy = H/2;\n // Perspective divides by (dist - z), so the focal length must carry the\n // camera distance or the globe renders as a marble in a large empty frame.\n const R = Math.min(W, H) * 1.06;\n const n = M.n;\n if (!sxb || sxb.length < n) { sxb = new Float32Array(n); syb = new Float32Array(n); szb = new Float32Array(n); }\n for (let i = 0; i < n; i++) {\n const x1 = M.px[i]*cy - M.pz[i]*sy, z1 = M.px[i]*sy + M.pz[i]*cy;\n const y2 = M.py[i]*cp - z1*sp, z2 = M.py[i]*sp + z1*cp;\n const f = R / (cam.dist - z2);\n sxb[i] = cx + x1*f; syb[i] = cyy - y2*f; szb[i] = z2;\n }\n return { cx, cyy, R };\n}\n\nfunction drawGlobe(dt, alpha) {\n if (!dragging && performance.now() - lastInteract > 2200) cam.yaw += 0.035 * dt;\n const P = projectGlobe();\n ctx.globalAlpha = alpha;\n\n const shell = P.R / cam.dist;\n const g = ctx.createRadialGradient(P.cx, P.cyy, shell*0.1, P.cx, P.cyy, shell*1.08);\n g.addColorStop(0, 'rgba(26,36,60,.55)');\n g.addColorStop(0.72, 'rgba(13,19,33,.42)');\n g.addColorStop(1, 'rgba(5,7,12,0)');\n ctx.fillStyle = g;\n ctx.beginPath(); ctx.arc(P.cx, P.cyy, shell*1.08, 0, TAU); ctx.fill();\n\n const order = [];\n for (let i = 0; i < M.n; i++) order.push(i);\n order.sort((a, b) => szb[a] - szb[b]); // painter's algorithm\n for (const i of order) {\n const z = szb[i], front = z > 0;\n const dim = hotCluster >= 0 && M.cluster[i] !== hotCluster;\n // Far-side dots sit BELOW the lit shell, and a saturated colour at middling\n // alpha over that blue-grey composites to olive. Keep them faint enough to\n // read as depth rather than as dirty dots.\n let a = front ? 0.96 : 0.14;\n if (dim) a *= 0.12;\n const rad = (1.6 + Math.min(3.6, Math.sqrt(M.deg[i]) * 0.62)) * (0.72 + 0.42 * ((z+1)/2));\n if (i === hovered) {\n ctx.fillStyle = 'rgba(255,212,121,.25)';\n ctx.beginPath(); ctx.arc(sxb[i], syb[i], rad + 7, 0, TAU); ctx.fill();\n }\n ctx.fillStyle = 'rgba('+M.rgb[i*3]+','+M.rgb[i*3+1]+','+M.rgb[i*3+2]+','+a.toFixed(3)+')';\n ctx.beginPath(); ctx.arc(sxb[i], syb[i], rad, 0, TAU); ctx.fill();\n }\n\n // The ONLY text on the sphere, and only while pointing at something.\n if (hovered >= 0) {\n ctx.font = '500 12px ' + MONO;\n ctx.textBaseline = 'middle'; ctx.textAlign = 'left';\n const label = M.nodes[hovered][0];\n const w = ctx.measureText(label).width;\n const x = sxb[hovered] + 11, y = syb[hovered];\n ctx.fillStyle = 'rgba(5,7,12,.85)';\n ctx.fillRect(x - 4, y - 9, w + 8, 18);\n ctx.fillStyle = '#ffd479';\n ctx.fillText(label, x, y);\n }\n ctx.globalAlpha = 1;\n}\n\n// ---------------------------------------------------------------------------\n// MAP \u2014 a sliding two-level window, never a growing tree.\n//\n// centre = the file you came FROM\n// opened = the neighbour you clicked, drawn with ITS own neighbours\n//\n// Click one of the opened node's neighbours and the window slides: opened\n// becomes the centre, and the node just clicked becomes the new opened. So the\n// picture never accumulates hops \u2014 it always shows exactly two levels.\n// ---------------------------------------------------------------------------\nconst map = {\n center: -1, opened: -1, nodes: [], links: [], trail: [],\n ring: new Set(), kids: new Set(), radius: 240,\n pan: { x: 0, y: 0 }, zoom: 1, drag: null, hover: -1,\n};\nconst KID_CAP = 20;\n\n/** keep the head and tail \u2014 the distinguishing parts of a long filename */\nfunction shortName(s) {\n return s.length <= 34 ? s : s.slice(0, 20) + '\u2026' + s.slice(-12);\n}\n\nfunction relLabel(kind, detail, reversed) {\n if (kind === 1) return reversed ? 'called by' : 'calls ' + (detail || 'a symbol');\n if (kind === 2) return 'edited together' + (detail ? ' \u00B7 ' + detail : '');\n if (kind === 3) return 'same note' + (detail ? ' \u00B7 ' + detail : '');\n return reversed ? REL[0].rev : REL[0].name;\n}\n\n/** every distinct neighbour of `id`, with the relations that connect them */\nfunction neighboursOf(id) {\n const byOther = new Map();\n for (const ei of M.adj[id]) {\n const e = M.edges[ei];\n const other = e[0] === id ? e[1] : e[0];\n if (other === id) continue;\n if (!byOther.has(other)) byOther.set(other, []);\n byOther.get(other).push({ kind: e[2], detail: e[4], reversed: e[0] !== id && e[3] === 1 });\n }\n return [...byOther.entries()]\n .map(([other, rels]) => ({ other, rels }))\n .sort((a, b) => Math.min(...a.rels.map((r) => r.kind)) - Math.min(...b.rels.map((r) => r.kind))\n || M.deg[b.other] - M.deg[a.other]);\n}\n\nfunction layoutMap(keep) {\n const prev = new Map(map.nodes.map((n) => [n.id, n]));\n const nodes = [], links = [], ring = new Set(), kids = new Set();\n\n const n1 = neighboursOf(map.center);\n const opened = (map.opened >= 0 && n1.some((nb) => nb.other === map.opened)) ? map.opened : -1;\n map.opened = opened;\n\n if (opened < 0) {\n // ---- RADIAL: one file and its whole neighbourhood ----------------------\n map.spine = false;\n nodes.push({ id: map.center, level: 0, x: 0, y: 0, a: 0 });\n // Ring radius grows with occupancy, or the dots touch and labels overprint.\n const r1 = Math.max(235, n1.length * 34 / TAU);\n map.radius = r1;\n n1.forEach((nb, i) => {\n const a = (i / Math.max(1, n1.length)) * TAU - Math.PI / 2;\n nodes.push({ id: nb.other, level: 1, x: Math.cos(a) * r1, y: Math.sin(a) * r1, a });\n ring.add(nb.other);\n for (const r of nb.rels) links.push({ from: map.center, to: nb.other, ...r, level: 1 });\n });\n map.kidOverflow = 0;\n } else {\n // ---- SPINE: where you came FROM, and what you just opened --------------\n //\n // Fanning the opened node inside the centre's own ring was the ugly part:\n // both levels landed in the same annulus, so every spoke of one crossed\n // every spoke of the other and you could not tell whose neighbour was\n // whose. Giving each node its own half of the screen makes overlap\n // impossible by construction, and the A\u2192B relation gets the middle.\n map.spine = true;\n const others = n1.filter((nb) => nb.other !== opened);\n const otherIds = new Set(others.map((o) => o.other));\n const kidList = neighboursOf(opened)\n .filter((nb) => nb.other !== map.center && !otherIds.has(nb.other));\n const shown = kidList.slice(0, KID_CAP);\n map.kidOverflow = kidList.length - shown.length;\n\n // each side spans a semicircle, so radius comes from PI, not TAU\n const rA = Math.max(200, others.length * 34 / Math.PI);\n const rB = Math.max(200, shown.length * 34 / Math.PI);\n const ax = -Math.max(300, (rA + rB) * 0.30), bx = -ax;\n\n nodes.push({ id: map.center, level: 0, x: ax, y: 0, a: Math.PI });\n nodes.push({ id: opened, level: 0, x: bx, y: 0, a: 0 });\n ring.add(opened);\n for (const r of (n1.find((nb) => nb.other === opened)?.rels ?? [])) {\n links.push({ from: map.center, to: opened, ...r, level: 1, spine: true });\n }\n\n // centre's remaining neighbours fan LEFT, away from the spine\n const AL = Math.PI/2 + 0.22, AH = Math.PI*1.5 - 0.22;\n others.forEach((nb, i) => {\n const t = others.length === 1 ? 0.5 : i / (others.length - 1);\n const a = AL + t * (AH - AL);\n nodes.push({ id: nb.other, level: 1, x: ax + Math.cos(a) * rA, y: Math.sin(a) * rA, a });\n ring.add(nb.other);\n for (const r of nb.rels) links.push({ from: map.center, to: nb.other, ...r, level: 1 });\n });\n\n // opened node's neighbours fan RIGHT\n const BL = -Math.PI/2 + 0.22, BH = Math.PI/2 - 0.22;\n shown.forEach((nb, i) => {\n const t = shown.length === 1 ? 0.5 : i / (shown.length - 1);\n const a = BL + t * (BH - BL);\n nodes.push({ id: nb.other, level: 2, x: bx + Math.cos(a) * rB, y: Math.sin(a) * rB, a });\n kids.add(nb.other);\n for (const r of nb.rels) links.push({ from: opened, to: nb.other, ...r, level: 2 });\n });\n }\n\n // relations between two on-screen nodes that bypass the focus \u2014 real, but\n // background, and the main source of the web when everything is drawn\n const on = new Set(nodes.map((nd) => nd.id));\n for (const e of M.edges) {\n if (!on.has(e[0]) || !on.has(e[1])) continue;\n if (links.some((l) => l.kind === e[2] &&\n ((l.from === e[0] && l.to === e[1]) || (l.from === e[1] && l.to === e[0])))) continue;\n links.push({ from: e[0], to: e[1], kind: e[2], detail: e[4], reversed: false, level: 9, extra: true });\n }\n\n for (const nd of nodes) {\n const old = keep && prev.get(nd.id);\n nd.cx = old ? old.cx : nd.x * 0.4;\n nd.cy = old ? old.cy : nd.y * 0.4;\n }\n map.nodes = nodes; map.links = links; map.ring = ring; map.kids = kids;\n\n let minX = 1e9, maxX = -1e9, minY = 1e9, maxY = -1e9;\n for (const nd of nodes) {\n minX = Math.min(minX, nd.x); maxX = Math.max(maxX, nd.x);\n minY = Math.min(minY, nd.y); maxY = Math.max(maxY, nd.y);\n }\n // Fit against the space actually available, not the raw window: the info\n // panel owns the left ~380px, and every node hangs a filename outside the\n // node bounding box, so both fans clipped without this allowance.\n const PAD_L = 380, PAD_R = 70, LABEL = 215;\n const availW = W - PAD_L - PAD_R, availH = H - 150;\n map.zoom = Math.min(1.15, availW / (maxX - minX + LABEL * 2), availH / (maxY - minY + 130));\n map.pan = {\n x: -(minX + maxX) / 2 + ((PAD_L - PAD_R) / 2) / map.zoom,\n y: -(minY + maxY) / 2,\n };\n}\n\nfunction openFile(id) {\n if (mode === 'globe') { map.trail = []; map.opened = -1; fade = 1; }\n mode = 'map';\n document.body.classList.add('map');\n map.center = id; map.opened = -1;\n layoutMap(true); renderMapPanel();\n hint('click a neighbour to open its connections \u00B7 click one of those to slide the view along');\n}\n\nfunction exitMap() {\n mode = 'globe'; fade = 1;\n document.body.classList.remove('map');\n map.center = -1; map.opened = -1; map.nodes = []; map.links = [];\n hint('drag to rotate \u00B7 scroll to zoom \u00B7 hover for a name \u00B7 click any file to explore it');\n}\n\n/** the whole navigation rule, in one place */\nfunction mapClick(id) {\n if (id < 0 || id === map.center) { map.opened = -1; layoutMap(true); renderMapPanel(); return; }\n if (map.ring.has(id)) {\n // a neighbour of the centre: open it in place (or drill in if already open)\n if (map.opened === id) { map.trail.push(map.center); map.center = id; map.opened = -1; }\n else map.opened = id;\n } else if (map.kids.has(id)) {\n // a neighbour of the opened node: slide the window along by one\n map.trail.push(map.center);\n map.center = map.opened;\n map.opened = id;\n } else return;\n layoutMap(true); renderMapPanel();\n}\n\nfunction mapScreen(nd) {\n return [W/2 + (nd.cx + map.pan.x) * map.zoom, H/2 + (nd.cy + map.pan.y) * map.zoom];\n}\n\nfunction drawMap(dt, alpha) {\n ctx.globalAlpha = alpha;\n const k = Math.min(1, dt * 7);\n for (const nd of map.nodes) { nd.cx += (nd.x - nd.cx) * k; nd.cy += (nd.y - nd.cy) * k; }\n\n const cx0 = W/2 + map.pan.x * map.zoom, cy0 = H/2 + map.pan.y * map.zoom;\n if (!map.spine) {\n ctx.strokeStyle = 'rgba(140,160,200,.09)';\n ctx.lineWidth = 1;\n ctx.beginPath(); ctx.arc(cx0, cy0, map.radius * map.zoom, 0, TAU); ctx.stroke();\n }\n\n const pos = new Map(map.nodes.map((nd) => [nd.id, mapScreen(nd)]));\n const hov = map.hover;\n\n for (const l of map.links) {\n const A = pos.get(l.from), B = pos.get(l.to);\n if (!A || !B) continue;\n const touches = hov >= 0 && (l.from === hov || l.to === hov);\n // With two focus nodes on screen the bypass links are what turn the\n // picture into a web \u2014 keep them for hover only.\n if (l.extra && map.spine && !touches) continue;\n const R = REL[l.kind];\n const base = l.extra ? 0.10 : (l.spine ? 0.9 : (l.level === 2 ? 0.7 : 0.5));\n ctx.strokeStyle = 'rgba('+R.col[0]+','+R.col[1]+','+R.col[2]+','+(touches ? 0.95 : base)+')';\n ctx.lineWidth = touches ? 2 : (l.extra ? 1 : (l.spine ? 2 : 1.4));\n const dx = B[0]-A[0], dy = B[1]-A[1];\n const mx = (A[0]+B[0])/2, my = (A[1]+B[1])/2;\n const bow = (l.kind - 1.5) * 7; // keeps parallel relations apart\n ctx.beginPath();\n ctx.moveTo(A[0], A[1]);\n ctx.quadraticCurveTo(mx - dy*0.008*bow, my + dx*0.008*bow, B[0], B[1]);\n ctx.stroke();\n\n if (l.kind <= 1 && !l.extra) {\n const t = 0.62, ax = A[0] + dx*t, ay = A[1] + dy*t;\n const ang = Math.atan2(dy, dx) + (l.reversed ? Math.PI : 0);\n ctx.fillStyle = ctx.strokeStyle;\n ctx.beginPath();\n ctx.moveTo(ax + Math.cos(ang)*5, ay + Math.sin(ang)*5);\n ctx.lineTo(ax + Math.cos(ang+2.5)*5, ay + Math.sin(ang+2.5)*5);\n ctx.lineTo(ax + Math.cos(ang-2.5)*5, ay + Math.sin(ang-2.5)*5);\n ctx.fill();\n }\n }\n\n // Filenames claim their boxes FIRST; relation labels route around them.\n ctx.font = '500 11.5px ' + MONO;\n const boxes = [], dots = [], nodeLabels = [];\n for (const nd of map.nodes) {\n const [x, y] = pos.get(nd.id);\n const isC = nd.id === map.center, isO = nd.id === map.opened;\n const r = isC ? 13 : (isO ? 10 : (nd.level === 1 ? 7.5 : 5.5));\n // Dots are drawn AFTER the relation labels, so they must be reserved too \u2014\n // but in their OWN list. Putting them in `boxes` made every node's label\n // collide with its own dot and vanish.\n dots.push([x - r - 2, y - r - 2, r*2 + 4, r*2 + 4]);\n const label = shortName(M.nodes[nd.id][0]);\n const w = ctx.measureText(label).width;\n // On the spine both focus nodes sit in open space, so their labels go\n // straight above the dot \u2014 hanging them sideways puts them into the fan.\n const above = map.spine && (isC || isO);\n const right = isC ? true : (x >= cx0);\n const lx = above ? x : (right ? x + r + 6 : x - r - 6 - w);\n const box = above ? [x - w/2 - 3, y - r - 23, w + 6, 16] : [lx - 3, y - 8, w + 6, 16];\n const clash = boxes.some((b) => box[0] < b[0]+b[2]+3 && b[0] < box[0]+box[2]+3 &&\n box[1] < b[1]+b[3]+2 && b[1] < box[1]+box[3]+2);\n if (clash && !isC && !isO && nd.id !== hov) { nodeLabels.push({ nd, x, y, r, skip: true }); continue; }\n boxes.push(box);\n nodeLabels.push({ nd, x, y, r, label, lx, box, above });\n }\n\n ctx.font = '500 10px ' + SANS;\n ctx.textAlign = 'center'; ctx.textBaseline = 'middle';\n for (const l of map.links) {\n const show = !l.extra && (l.level === 1 || l.level === 2 ||\n (hov >= 0 && (l.from === hov || l.to === hov)));\n if (!show) continue;\n const A = pos.get(l.from), B = pos.get(l.to);\n if (!A || !B) continue;\n const txt = relLabel(l.kind, l.detail, l.reversed);\n const w = ctx.measureText(txt).width;\n let placed = null;\n for (const t of [0.55, 0.66, 0.44, 0.75]) {\n const x = A[0] + (B[0]-A[0])*t, y = A[1] + (B[1]-A[1])*t;\n const box = [x - w/2 - 4, y - 8, w + 8, 16];\n const hit = (b) => box[0] < b[0]+b[2]+3 && b[0] < box[0]+box[2]+3 &&\n box[1] < b[1]+b[3]+2 && b[1] < box[1]+box[3]+2;\n if (!boxes.some(hit) && !dots.some(hit)) { placed = { x, y, box }; break; }\n }\n if (!placed) continue;\n boxes.push(placed.box);\n const R = REL[l.kind];\n ctx.fillStyle = 'rgba(5,7,12,.9)';\n ctx.fillRect(placed.box[0], placed.box[1], placed.box[2], placed.box[3]);\n ctx.fillStyle = 'rgba('+R.col[0]+','+R.col[1]+','+R.col[2]+',.95)';\n ctx.fillText(txt, placed.x, placed.y);\n }\n\n ctx.font = '500 11.5px ' + MONO;\n for (const nl of nodeLabels) {\n const { nd, x, y, r } = nl;\n const isC = nd.id === map.center, isO = nd.id === map.opened;\n if (isC || isO || nd.id === hov) {\n ctx.fillStyle = isO ? 'rgba(143,211,255,.2)' : 'rgba(255,212,121,.2)';\n ctx.beginPath(); ctx.arc(x, y, r + 9, 0, TAU); ctx.fill();\n }\n ctx.fillStyle = 'rgb('+M.rgb[nd.id*3]+','+M.rgb[nd.id*3+1]+','+M.rgb[nd.id*3+2]+')';\n ctx.beginPath(); ctx.arc(x, y, r, 0, TAU); ctx.fill();\n if (isC || isO) {\n ctx.strokeStyle = isO ? '#8fd3ff' : '#ffd479';\n ctx.lineWidth = 2; ctx.stroke();\n }\n if (nl.skip) continue;\n ctx.textAlign = nl.above ? 'center' : 'left';\n ctx.fillStyle = 'rgba(5,7,12,.85)';\n ctx.fillRect(nl.box[0], nl.box[1], nl.box[2], nl.box[3]);\n ctx.fillStyle = isC ? '#ffd479' : (isO ? '#8fd3ff' : (nd.id === hov ? '#fff' : 'rgba(232,236,245,.9)'));\n ctx.fillText(nl.label, nl.lx, nl.above ? y - r - 15 : y);\n }\n\n if (map.kidOverflow > 0 && map.opened >= 0) {\n const p = pos.get(map.opened);\n ctx.font = '500 10px ' + SANS;\n ctx.textAlign = 'center';\n ctx.fillStyle = 'rgba(139,151,176,.8)';\n ctx.fillText('+' + map.kidOverflow + ' more', p[0], p[1] + 26);\n }\n ctx.globalAlpha = 1;\n}\n\nfunction mapHit(mx, my) {\n let best = -1, bestD = 18*18;\n for (const nd of map.nodes) {\n const [x, y] = mapScreen(nd);\n const d = (x-mx)*(x-mx) + (y-my)*(y-my);\n if (d < bestD) { bestD = d; best = nd.id; }\n }\n return best;\n}\n\n// ---------------------------------------------------------------------------\n// Input\n// ---------------------------------------------------------------------------\nlet dragging = false, lastX = 0, lastY = 0, moved = 0;\nconst tip = document.getElementById('tip');\n\ncvs.addEventListener('pointerdown', (e) => {\n dragging = true; moved = 0; lastX = e.clientX; lastY = e.clientY;\n cvs.setPointerCapture(e.pointerId); cvs.classList.add('dragging');\n if (mode === 'map') {\n const id = mapHit(e.clientX, e.clientY);\n map.drag = id >= 0 ? map.nodes.find((n) => n.id === id) : null;\n }\n});\ncvs.addEventListener('pointermove', (e) => {\n const dx = e.clientX - lastX, dy = e.clientY - lastY;\n if (dragging) {\n moved += Math.abs(dx) + Math.abs(dy);\n if (mode === 'globe') {\n cam.yaw -= dx * 0.006;\n cam.pitch = Math.max(-1.45, Math.min(1.45, cam.pitch + dy * 0.006));\n } else if (map.drag) {\n map.drag.x += dx / map.zoom; map.drag.y += dy / map.zoom;\n map.drag.cx += dx / map.zoom; map.drag.cy += dy / map.zoom;\n } else {\n map.pan.x += dx / map.zoom; map.pan.y += dy / map.zoom;\n }\n lastX = e.clientX; lastY = e.clientY; lastInteract = performance.now();\n return;\n }\n if (mode === 'globe') {\n let best = -1, bestD = 13*13;\n for (let i = 0; i < M.n; i++) {\n if (szb[i] < -0.05) continue; // never grab a back-face node\n const d = (sxb[i]-e.clientX)**2 + (syb[i]-e.clientY)**2;\n if (d < bestD) { bestD = d; best = i; }\n }\n hovered = best;\n cvs.classList.toggle('pointing', best >= 0);\n tip.style.display = 'none'; // the name is drawn on canvas\n } else {\n map.hover = mapHit(e.clientX, e.clientY);\n cvs.classList.toggle('pointing', map.hover >= 0);\n if (map.hover >= 0) {\n tip.style.display = 'block';\n tip.textContent = M.nodes[map.hover][1];\n tip.style.left = (e.clientX + 14) + 'px'; tip.style.top = (e.clientY + 14) + 'px';\n } else tip.style.display = 'none';\n }\n});\ncvs.addEventListener('pointerup', (e) => {\n const wasDrag = moved >= 5;\n dragging = false; cvs.classList.remove('dragging'); map.drag = null;\n if (!wasDrag) {\n if (mode === 'globe') { if (hovered >= 0) openFile(hovered); }\n else mapClick(mapHit(e.clientX, e.clientY));\n }\n lastInteract = performance.now();\n});\ncvs.addEventListener('wheel', (e) => {\n e.preventDefault();\n if (mode === 'globe') cam.dist = Math.max(1.35, Math.min(6, cam.dist + e.deltaY * 0.0022));\n else map.zoom = Math.max(0.25, Math.min(2.6, map.zoom * (1 - e.deltaY * 0.0012)));\n lastInteract = performance.now();\n}, { passive: false });\n\n// ---------------------------------------------------------------------------\n// Panels\n// ---------------------------------------------------------------------------\nlet MONO = 'monospace', SANS = 'sans-serif';\nfunction hint(t) { document.getElementById('hint').textContent = t; }\n\nfunction renderHead() {\n document.getElementById('repo').textContent = M.repo;\n document.getElementById('s-files').textContent = M.n.toLocaleString();\n document.getElementById('s-rel').textContent = M.edges.length.toLocaleString();\n document.getElementById('s-dirs').textContent = M.clusters.length;\n const box = document.getElementById('cls');\n box.innerHTML = '';\n M.clusters.forEach((name, c) => {\n const [r, g, b] = M.bandRgb[c];\n const el = document.createElement('div');\n el.className = 'cl' + (hotCluster === c ? ' on' : '');\n el.innerHTML = '<i class=\"dot\" style=\"background:rgb('+r+','+g+','+b+')\"></i>' +\n '<span class=\"nm\">' + name + '</span><i class=\"ct\">' + M.counts[c] + '</i>';\n el.onclick = () => { hotCluster = hotCluster === c ? -1 : c; renderHead(); };\n box.appendChild(el);\n });\n}\n\nfunction renderMapPanel() {\n document.getElementById('mapfile').textContent = M.nodes[map.center][0];\n document.getElementById('mappath').textContent = M.nodes[map.center][1];\n\n const exp = document.getElementById('expblock');\n if (map.opened >= 0) {\n exp.classList.add('on');\n document.getElementById('expfile').textContent = M.nodes[map.opened][0];\n document.getElementById('exppath').textContent = M.nodes[map.opened][1];\n } else exp.classList.remove('on');\n\n const crumb = document.getElementById('crumb');\n crumb.innerHTML = '';\n map.trail.slice(-5).forEach((id) => {\n const b = document.createElement('b');\n b.textContent = M.nodes[id][0];\n b.onclick = () => {\n map.trail = map.trail.slice(0, map.trail.indexOf(id));\n map.center = id; map.opened = -1;\n layoutMap(true); renderMapPanel();\n };\n crumb.appendChild(b);\n crumb.appendChild(document.createTextNode(' \u203A '));\n });\n\n const who = map.opened >= 0 ? map.opened : map.center;\n const counts = [0, 0, 0, 0];\n for (const ei of M.adj[who]) counts[M.edges[ei][2]]++;\n const rels = document.getElementById('rels');\n rels.innerHTML = '';\n REL.forEach((R, i) => {\n const el = document.createElement('div');\n el.className = 'rel';\n el.innerHTML = '<i class=\"sw\" style=\"background:' + (counts[i] ? R.hex : '#5a6480') + '\"></i>' +\n '<span>' + R.name + '</span><b>' + counts[i] + '</b>';\n rels.appendChild(el);\n });\n if (!counts.reduce((a, b) => a + b, 0)) {\n const el = document.createElement('div');\n el.className = 'rel';\n el.innerHTML = '<span style=\"color:#5a6480\">no resolved connections \u2014 isolated in the index</span>';\n rels.appendChild(el);\n }\n}\n\ndocument.getElementById('back').onclick = exitMap;\ndocument.getElementById('repo').onclick = () => { repoAt = (repoAt + 1) % repoKeys.length; load(repoKeys[repoAt]); };\n\nconst q = document.getElementById('q'), hits = document.getElementById('hits');\nq.addEventListener('input', () => {\n const v = q.value.trim().toLowerCase();\n hits.innerHTML = '';\n if (!v) return;\n const out = [];\n for (let i = 0; i < M.n; i++) {\n const f = M.nodes[i][0].toLowerCase(), p = M.nodes[i][1].toLowerCase();\n let s = -1;\n if (f.startsWith(v)) s = 0; else if (f.includes(v)) s = 1; else if (p.includes(v)) s = 2;\n if (s >= 0) out.push([s, -M.deg[i], i]);\n }\n out.sort((a, b) => a[0] - b[0] || a[1] - b[1]);\n for (const [, , i] of out.slice(0, 9)) {\n const el = document.createElement('div');\n el.className = 'hit';\n el.innerHTML = '<b>' + M.nodes[i][0] + '</b><i>' + M.nodes[i][1] + '</i>';\n el.onclick = () => { openFile(i); q.value = ''; hits.innerHTML = ''; q.blur(); };\n hits.appendChild(el);\n }\n});\nq.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') { const f = hits.querySelector('.hit'); if (f) f.click(); }\n if (e.key === 'Escape') { q.value = ''; hits.innerHTML = ''; q.blur(); }\n});\naddEventListener('keydown', (e) => { if (e.key === 'Escape' && mode === 'map') exitMap(); });\n\nfunction load(key) {\n M = buildModel(key);\n hovered = -1; hotCluster = -1;\n cam.yaw = 0.6; cam.pitch = -0.25; cam.dist = 3.0;\n exitMap();\n q.value = ''; hits.innerHTML = '';\n renderHead();\n // Seed the projection buffers for the NEW model: the hit-test reads them,\n // and until the first frame runs they still describe the old repo.\n projectGlobe();\n}\n\nfunction setPalette(name) { PALETTE_NAME = name; load(M.key); }\n\nconst cs = getComputedStyle(document.body);\nMONO = cs.getPropertyValue('--mono'); SANS = cs.getPropertyValue('--sans');\nresize();\nload(repoKeys[repoAt]);\n\nlet prev = performance.now();\n(function loop(now) {\n const dt = Math.min(0.05, (now - prev) / 1000);\n prev = now;\n ctx.fillStyle = '#05070c';\n ctx.fillRect(0, 0, W, H);\n fade = Math.max(0, fade - dt * 3.2);\n const a = 1 - fade;\n if (mode === 'globe') drawGlobe(dt, a); else drawMap(dt, a);\n requestAnimationFrame(loop);\n})(performance.now());\n</script>\n</body>\n</html>\n";
10
+ //# sourceMappingURL=view-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-template.d.ts","sourceRoot":"","sources":["../../src/graph/view-template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,izsCAA2psC,CAAC"}