ts-knowledge-graph 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -8
- package/contribs/{web_visualisation → webview}/README.md +7 -7
- package/contribs/webview/web/css/style.css +310 -0
- package/contribs/{web_visualisation → webview}/web/index.html +40 -5
- package/contribs/{web_visualisation → webview}/web/js/app.js +378 -39
- package/contribs/{web_visualisation/web/data → webview/web/js_autogenerated}/kind_descriptions.js +2 -1
- package/contribs/{web_visualisation → webview}/web/types/app_globals.d.ts +11 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/cluster/cluster_weights.d.ts +20 -0
- package/dist/cluster/cluster_weights.d.ts.map +1 -0
- package/dist/cluster/cluster_weights.js +32 -0
- package/dist/cluster/cluster_weights.js.map +1 -0
- package/dist/cluster/community_detector.d.ts +61 -0
- package/dist/cluster/community_detector.d.ts.map +1 -0
- package/dist/cluster/community_detector.js +120 -0
- package/dist/cluster/community_detector.js.map +1 -0
- package/dist/cluster/community_labeler.d.ts +84 -0
- package/dist/cluster/community_labeler.d.ts.map +1 -0
- package/dist/cluster/community_labeler.js +194 -0
- package/dist/cluster/community_labeler.js.map +1 -0
- package/dist/cluster/graph_clusterer.d.ts +47 -0
- package/dist/cluster/graph_clusterer.d.ts.map +1 -0
- package/dist/cluster/graph_clusterer.js +126 -0
- package/dist/cluster/graph_clusterer.js.map +1 -0
- package/dist/commands/benchmark_command.d.ts.map +1 -1
- package/dist/commands/benchmark_command.js +13 -10
- package/dist/commands/benchmark_command.js.map +1 -1
- package/dist/commands/blast_radius_command.d.ts.map +1 -1
- package/dist/commands/blast_radius_command.js +6 -5
- package/dist/commands/blast_radius_command.js.map +1 -1
- package/dist/commands/cluster_command.d.ts +7 -0
- package/dist/commands/cluster_command.d.ts.map +1 -0
- package/dist/commands/cluster_command.js +55 -0
- package/dist/commands/cluster_command.js.map +1 -0
- package/dist/commands/command_helpers.d.ts +9 -4
- package/dist/commands/command_helpers.d.ts.map +1 -1
- package/dist/commands/command_helpers.js +13 -8
- package/dist/commands/command_helpers.js.map +1 -1
- package/dist/commands/cost_command.d.ts.map +1 -1
- package/dist/commands/cost_command.js +25 -8
- package/dist/commands/cost_command.js.map +1 -1
- package/dist/commands/enrich_command.d.ts.map +1 -1
- package/dist/commands/enrich_command.js +7 -5
- package/dist/commands/enrich_command.js.map +1 -1
- package/dist/commands/extract_command.d.ts.map +1 -1
- package/dist/commands/extract_command.js +12 -6
- package/dist/commands/extract_command.js.map +1 -1
- package/dist/commands/hotspots_command.d.ts.map +1 -1
- package/dist/commands/hotspots_command.js +6 -5
- package/dist/commands/hotspots_command.js.map +1 -1
- package/dist/commands/install_command.d.ts +15 -5
- package/dist/commands/install_command.d.ts.map +1 -1
- package/dist/commands/install_command.js +61 -23
- package/dist/commands/install_command.js.map +1 -1
- package/dist/commands/load_command.d.ts.map +1 -1
- package/dist/commands/load_command.js +18 -13
- package/dist/commands/load_command.js.map +1 -1
- package/dist/commands/neighbors_command.d.ts.map +1 -1
- package/dist/commands/neighbors_command.js +6 -5
- package/dist/commands/neighbors_command.js.map +1 -1
- package/dist/commands/references_command.d.ts.map +1 -1
- package/dist/commands/references_command.js +6 -5
- package/dist/commands/references_command.js.map +1 -1
- package/dist/commands/report_command.d.ts +16 -0
- package/dist/commands/report_command.d.ts.map +1 -0
- package/dist/commands/report_command.js +115 -0
- package/dist/commands/report_command.js.map +1 -0
- package/dist/commands/webview_command.d.ts +36 -0
- package/dist/commands/webview_command.d.ts.map +1 -0
- package/dist/commands/webview_command.js +186 -0
- package/dist/commands/webview_command.js.map +1 -0
- package/dist/enrich/cpu_profile.d.ts +33 -0
- package/dist/enrich/cpu_profile.d.ts.map +1 -1
- package/dist/enrich/cpu_profile.js +88 -0
- package/dist/enrich/cpu_profile.js.map +1 -1
- package/dist/enrich/runtime_enricher.d.ts +8 -0
- package/dist/enrich/runtime_enricher.d.ts.map +1 -1
- package/dist/enrich/runtime_enricher.js +18 -0
- package/dist/enrich/runtime_enricher.js.map +1 -1
- package/dist/enrich/runtime_join.d.ts +25 -1
- package/dist/enrich/runtime_join.d.ts.map +1 -1
- package/dist/enrich/runtime_join.js +43 -0
- package/dist/enrich/runtime_join.js.map +1 -1
- package/dist/extract/git_source.d.ts +23 -0
- package/dist/extract/git_source.d.ts.map +1 -0
- package/dist/extract/git_source.js +75 -0
- package/dist/extract/git_source.js.map +1 -0
- package/dist/query/graph_query.d.ts +36 -1
- package/dist/query/graph_query.d.ts.map +1 -1
- package/dist/query/graph_query.js +69 -6
- package/dist/query/graph_query.js.map +1 -1
- package/dist/report/graph_report.d.ts +51 -0
- package/dist/report/graph_report.d.ts.map +1 -0
- package/dist/report/graph_report.js +312 -0
- package/dist/report/graph_report.js.map +1 -0
- package/dist/report/pdf_renderer.d.ts +22 -0
- package/dist/report/pdf_renderer.d.ts.map +1 -0
- package/dist/report/pdf_renderer.js +54 -0
- package/dist/report/pdf_renderer.js.map +1 -0
- package/dist/report/report_data.d.ts +128 -0
- package/dist/report/report_data.d.ts.map +1 -0
- package/dist/report/report_data.js +191 -0
- package/dist/report/report_data.js.map +1 -0
- package/dist/schema/edge.d.ts +5 -5
- package/dist/schema/edge.d.ts.map +1 -1
- package/dist/schema/edge.js +3 -0
- package/dist/schema/edge.js.map +1 -1
- package/dist/schema/source_manifest.d.ts +30 -0
- package/dist/schema/source_manifest.d.ts.map +1 -0
- package/dist/schema/source_manifest.js +21 -0
- package/dist/schema/source_manifest.js.map +1 -0
- package/dist/store/jsonl_reader.d.ts +4 -0
- package/dist/store/jsonl_reader.d.ts.map +1 -1
- package/dist/store/jsonl_reader.js +13 -1
- package/dist/store/jsonl_reader.js.map +1 -1
- package/dist/store/jsonl_store.d.ts +2 -1
- package/dist/store/jsonl_store.d.ts.map +1 -1
- package/dist/store/jsonl_store.js +4 -1
- package/dist/store/jsonl_store.js.map +1 -1
- package/dist/store/kuzu_store.d.ts +13 -0
- package/dist/store/kuzu_store.d.ts.map +1 -1
- package/dist/store/kuzu_store.js +29 -0
- package/dist/store/kuzu_store.js.map +1 -1
- package/dist/store/output_folder.d.ts +43 -0
- package/dist/store/output_folder.d.ts.map +1 -0
- package/dist/store/output_folder.js +61 -0
- package/dist/store/output_folder.js.map +1 -0
- package/dotclaude_folder/commands/code-graph-interview.md +123 -0
- package/dotclaude_folder/commands/code-graph-optimize.md +65 -0
- package/dotclaude_folder/skills/code-graph-query/SKILL.md +4 -4
- package/package.json +72 -62
- package/contribs/web_visualisation/web/css/style.css +0 -219
- package/contribs/web_visualisation/web/tsconfig.json +0 -18
- /package/contribs/{web_visualisation/web/data → webview/web/js_autogenerated}/.gitignore +0 -0
package/README.md
CHANGED
|
@@ -37,11 +37,15 @@ hosts), and `Endpoint` (HTTP routes).
|
|
|
37
37
|
| Type | `EXTENDS`, `IMPLEMENTS`, `USES_TYPE`, `RETURNS`, `PARAM_TYPE` |
|
|
38
38
|
| Behavioral | `CALLS`, `INSTANTIATES`, `OVERRIDES`, `READS`, `WRITES` |
|
|
39
39
|
| System-level | `READS_CONFIG`, `CALLS_EXTERNAL`, `HANDLES` |
|
|
40
|
+
| Runtime | `CALLS_RUNTIME` |
|
|
40
41
|
|
|
41
42
|
The structural layer — plus the always-on config and outbound-HTTP surfaces
|
|
42
43
|
(`ConfigFlag` / `READS_CONFIG`, `ExternalAPI` / `CALLS_EXTERNAL`) — is cheap and
|
|
43
44
|
needs no symbol resolution. The type, behavioral, and endpoint (`Endpoint` /
|
|
44
|
-
`HANDLES`) layers require symbol resolution and are emitted with `--semantic`.
|
|
45
|
+
`HANDLES`) layers require symbol resolution and are emitted with `--semantic`. The
|
|
46
|
+
runtime layer (`CALLS_RUNTIME`) is reconstructed from a CPU profile's call tree by
|
|
47
|
+
[`enrich`](docs/commands/enrich.md), not parsed from source — the calls that
|
|
48
|
+
actually fired, dynamic dispatch included.
|
|
45
49
|
|
|
46
50
|
`ConfigFlag` nodes come from `process.env.X` reads; `ExternalAPI` nodes from
|
|
47
51
|
`fetch(...)` call sites (one per host); `Endpoint` nodes from route registrations
|
|
@@ -61,9 +65,9 @@ npm run extract -- <path-to-project>
|
|
|
61
65
|
npm run extract -- <path-to-project> --semantic
|
|
62
66
|
```
|
|
63
67
|
|
|
64
|
-
Output is two JSONL files —
|
|
65
|
-
|
|
66
|
-
to inspect, diff, and load into any store.
|
|
68
|
+
Output is two JSONL files — `.ts_knowledge_graph/graph/nodes.jsonl` and
|
|
69
|
+
`.ts_knowledge_graph/graph/edges.jsonl` (override the base folder with `-o, --output-folder`)
|
|
70
|
+
— one record per line, easy to inspect, diff, and load into any store.
|
|
67
71
|
|
|
68
72
|
### Querying the graph
|
|
69
73
|
|
|
@@ -71,7 +75,7 @@ Load the JSONL into an embedded [Kùzu](https://kuzudb.com) database, then run t
|
|
|
71
75
|
query tools:
|
|
72
76
|
|
|
73
77
|
```bash
|
|
74
|
-
npm run dev -- load # reads
|
|
78
|
+
npm run dev -- load # reads ./.ts_knowledge_graph/graph, writes ./.ts_knowledge_graph/graph.kuzu
|
|
75
79
|
|
|
76
80
|
npm run dev -- find <name> # resolve a name to node ids
|
|
77
81
|
npm run dev -- who-calls <id> # direct callers of a symbol
|
|
@@ -83,6 +87,7 @@ npm run dev -- neighbors <id> # one-hop neighbourhood (in + out)
|
|
|
83
87
|
npm run dev -- hotspots --by self-time # rank nodes by optimization leverage
|
|
84
88
|
npm run dev -- cost # inclusive cost + share-of-total (causal)
|
|
85
89
|
npm run dev -- cost <id> # where one node's cost goes / who causes it
|
|
90
|
+
npm run dev -- cluster # detect code communities (Leiden) -> metadata.community
|
|
86
91
|
```
|
|
87
92
|
|
|
88
93
|
Every query command accepts `--json` to emit machine-readable output — this is
|
|
@@ -107,11 +112,11 @@ answer impact, dead-code, and dependency questions — see the
|
|
|
107
112
|
|
|
108
113
|
Serve the database as an interactive graph — pan/zoom, kind filters, symbol
|
|
109
114
|
search, per-node edge listing (see
|
|
110
|
-
[contribs/
|
|
115
|
+
[contribs/webview](contribs/webview)):
|
|
111
116
|
|
|
112
117
|
```bash
|
|
113
|
-
npm run
|
|
114
|
-
npm run
|
|
118
|
+
npm run webview # reads ./.ts_knowledge_graph/graph.kuzu, serves http://localhost:4173
|
|
119
|
+
npm run webview -- -o ./.ts_knowledge_graph --port 8080
|
|
115
120
|
```
|
|
116
121
|
|
|
117
122
|
### The optimization agent
|
|
@@ -148,6 +153,15 @@ grounds each candidate in the graph, producing tasks you can then hand to
|
|
|
148
153
|
`/code-graph-optimize`. Both commands, plus the `code-graph-query` skill, live
|
|
149
154
|
under [`dotclaude_folder/`](dotclaude_folder) and are mirrored into `.claude/`.
|
|
150
155
|
|
|
156
|
+
To install all of them into another project, run
|
|
157
|
+
[`install`](docs/commands/install.md) from that project — it copies every
|
|
158
|
+
bundled command and skill into the project's `.claude/` directory:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npx ts-knowledge-graph install # into ./.claude
|
|
162
|
+
npx ts-knowledge-graph install --force # overwrite previously installed copies
|
|
163
|
+
```
|
|
164
|
+
|
|
151
165
|
## Architecture
|
|
152
166
|
|
|
153
167
|
```
|
|
@@ -210,5 +224,9 @@ declaration node the structural layer emitted.
|
|
|
210
224
|
so an optimization is reported by its *measured* impact (e.g. −57% self-time on
|
|
211
225
|
`titleCase`) rather than a guess. Advisory by design, distinct from the hard
|
|
212
226
|
`verify` gate.
|
|
227
|
+
- [x] **Community detection** — the [`cluster`](docs/commands/cluster.md) command
|
|
228
|
+
runs the Leiden algorithm (CPM) over the weighted coupling graph and attaches a
|
|
229
|
+
module index onto nodes as `metadata.community`, with the internal-connectedness
|
|
230
|
+
guarantee Louvain lacks.
|
|
213
231
|
- [ ] **Vector index** — embed per-node summaries for hybrid graph + semantic
|
|
214
232
|
retrieval, so the agent can find candidates by meaning, not just by name.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# WebView
|
|
2
2
|
|
|
3
3
|
Interactive viewer for the knowledge graph — pan/zoom, color-coded node and
|
|
4
4
|
edge kinds with toggleable filters, symbol search, and a per-node detail panel
|
|
@@ -11,7 +11,7 @@ file).
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm run build # embed
|
|
14
|
+
npm run build # embed ../../.ts_knowledge_graph/graph/*.jsonl into web/js_autogenerated/graph_data.js
|
|
15
15
|
npm start # serve web/ on http://localhost:4173 (optional)
|
|
16
16
|
npm run open # open web/index.html directly (file://, macOS)
|
|
17
17
|
```
|
|
@@ -22,25 +22,25 @@ npm run open # open web/index.html directly (file://, macOS)
|
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
# from the repo root, after `npm run extract -- . --semantic`
|
|
25
|
-
cd contribs/
|
|
25
|
+
cd contribs/webview
|
|
26
26
|
npm run build
|
|
27
27
|
npm run open
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
`scripts/build-data.ts` reads
|
|
30
|
+
`scripts/build-data.ts` reads `../../.ts_knowledge_graph/graph/{nodes,edges}.jsonl` by
|
|
31
31
|
default; pass a different graph directory as the first argument
|
|
32
32
|
(`npx tsx scripts/build-data.ts /path/to/graph`).
|
|
33
33
|
|
|
34
34
|
**B. Drag & drop** — open `web/index.html` any way at all and drop
|
|
35
|
-
|
|
35
|
+
`.ts_knowledge_graph/graph/nodes.jsonl` + `.ts_knowledge_graph/graph/edges.jsonl` onto the page.
|
|
36
36
|
|
|
37
37
|
**C. Serve the repo root** — the page then auto-fetches
|
|
38
|
-
|
|
38
|
+
`../../../.ts_knowledge_graph/graph/*.jsonl`:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
# from the repo root
|
|
42
42
|
npx serve # or: python3 -m http.server
|
|
43
|
-
# open http://localhost:3000/contribs/
|
|
43
|
+
# open http://localhost:3000/contribs/webview/web/
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
## Reading the graph
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/* Dark theme (default). Every colour is a variable so a single `data-theme`
|
|
2
|
+
attribute on <html> repaints the whole viewer; see [data-theme="light"] below
|
|
3
|
+
and the matching graph-canvas variables read by js/app.js (cyStyle). */
|
|
4
|
+
:root {
|
|
5
|
+
color-scheme: dark;
|
|
6
|
+
--bg: #0f172a;
|
|
7
|
+
--surface: #111c33;
|
|
8
|
+
--surface-raised: #1e293b;
|
|
9
|
+
--surface-hover: #334155;
|
|
10
|
+
--border: #1e293b;
|
|
11
|
+
--border-control: #334155;
|
|
12
|
+
--border-subtle: #475569;
|
|
13
|
+
--input-bg: #0f172a;
|
|
14
|
+
--text: #cbd5e1;
|
|
15
|
+
--heading: #f1f5f9;
|
|
16
|
+
--muted: #64748b;
|
|
17
|
+
--muted-strong: #94a3b8;
|
|
18
|
+
--text-on-control: #e2e8f0;
|
|
19
|
+
--accent: #4f8cff;
|
|
20
|
+
--link: #7db2ff;
|
|
21
|
+
--tooltip-bg: #0b1424;
|
|
22
|
+
--tooltip-text: #e2e8f0;
|
|
23
|
+
--overlay-bg: rgba(15, 23, 42, .85);
|
|
24
|
+
--shadow: 0 4px 14px rgba(0, 0, 0, .45);
|
|
25
|
+
--on-swatch: #0f172a;
|
|
26
|
+
--num: #f59e0b;
|
|
27
|
+
--unmeasured-fill: #243044;
|
|
28
|
+
--unmeasured-border: #475569;
|
|
29
|
+
/* Graph canvas colours, read from JS so the graph tracks the theme. */
|
|
30
|
+
--graph-label: #cbd5e1;
|
|
31
|
+
--graph-label-bg: #0f172a;
|
|
32
|
+
--graph-sel-border: #ffffff;
|
|
33
|
+
--graph-node-border: #334155;
|
|
34
|
+
--graph-node-border-width: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-theme="light"] {
|
|
38
|
+
color-scheme: light;
|
|
39
|
+
--bg: #f8fafc;
|
|
40
|
+
--surface: #f1f5f9;
|
|
41
|
+
--surface-raised: #e2e8f0;
|
|
42
|
+
--surface-hover: #cbd5e1;
|
|
43
|
+
--border: #e2e8f0;
|
|
44
|
+
--border-control: #cbd5e1;
|
|
45
|
+
--border-subtle: #94a3b8;
|
|
46
|
+
--input-bg: #ffffff;
|
|
47
|
+
--text: #334155;
|
|
48
|
+
--heading: #0f172a;
|
|
49
|
+
--muted: #64748b;
|
|
50
|
+
--muted-strong: #475569;
|
|
51
|
+
--text-on-control: #1e293b;
|
|
52
|
+
--accent: #2563eb;
|
|
53
|
+
--link: #2563eb;
|
|
54
|
+
--tooltip-bg: #ffffff;
|
|
55
|
+
--tooltip-text: #1e293b;
|
|
56
|
+
--overlay-bg: rgba(226, 232, 240, .85);
|
|
57
|
+
--shadow: 0 4px 14px rgba(15, 23, 42, .18);
|
|
58
|
+
--on-swatch: #0f172a;
|
|
59
|
+
--num: #b45309;
|
|
60
|
+
--unmeasured-fill: #e2e8f0;
|
|
61
|
+
--unmeasured-border: #94a3b8;
|
|
62
|
+
--graph-label: #334155;
|
|
63
|
+
--graph-label-bg: #f8fafc;
|
|
64
|
+
--graph-sel-border: #0f172a;
|
|
65
|
+
--graph-node-border: #cbd5e1;
|
|
66
|
+
--graph-node-border-width: 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
* { box-sizing: border-box; }
|
|
70
|
+
|
|
71
|
+
html, body {
|
|
72
|
+
margin: 0;
|
|
73
|
+
height: 100%;
|
|
74
|
+
font: 13px/1.45 -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
75
|
+
background: var(--bg);
|
|
76
|
+
color: var(--text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
body { display: flex; }
|
|
80
|
+
|
|
81
|
+
#sidebar {
|
|
82
|
+
width: 300px;
|
|
83
|
+
flex: none;
|
|
84
|
+
height: 100%;
|
|
85
|
+
overflow-y: auto;
|
|
86
|
+
padding: 14px;
|
|
87
|
+
background: var(--surface);
|
|
88
|
+
border-right: 1px solid var(--border);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.sidebar-head { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; }
|
|
92
|
+
#sidebar h1 { font-size: 15px; margin: 0; flex: 1; color: var(--heading); }
|
|
93
|
+
#sidebar h2 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 16px 0 6px; }
|
|
94
|
+
#sidebar section { margin-top: 8px; }
|
|
95
|
+
|
|
96
|
+
.theme-toggle {
|
|
97
|
+
flex: none;
|
|
98
|
+
width: 26px;
|
|
99
|
+
height: 26px;
|
|
100
|
+
padding: 0;
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
font-size: 14px;
|
|
105
|
+
line-height: 1;
|
|
106
|
+
border-radius: 6px;
|
|
107
|
+
background: var(--surface-raised);
|
|
108
|
+
color: var(--text-on-control);
|
|
109
|
+
border: 1px solid var(--border-control);
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
}
|
|
112
|
+
.theme-toggle:hover { background: var(--surface-hover); }
|
|
113
|
+
|
|
114
|
+
#sidebar .foldable {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: 7px;
|
|
118
|
+
line-height: 1;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
user-select: none;
|
|
121
|
+
}
|
|
122
|
+
#sidebar .foldable:hover { color: var(--muted-strong); }
|
|
123
|
+
#sidebar .foldable::before {
|
|
124
|
+
content: '';
|
|
125
|
+
flex: none;
|
|
126
|
+
width: 0;
|
|
127
|
+
height: 0;
|
|
128
|
+
border-left: 5px solid currentColor;
|
|
129
|
+
border-top: 4px solid transparent;
|
|
130
|
+
border-bottom: 4px solid transparent;
|
|
131
|
+
transition: transform .12s ease;
|
|
132
|
+
}
|
|
133
|
+
#sidebar .foldable:not(.collapsed)::before { transform: rotate(90deg); }
|
|
134
|
+
#sidebar .foldable.collapsed ~ * { display: none; }
|
|
135
|
+
|
|
136
|
+
#status { font-size: 11px; color: var(--muted); margin-bottom: 10px; }
|
|
137
|
+
|
|
138
|
+
#search {
|
|
139
|
+
width: 100%;
|
|
140
|
+
padding: 6px 8px;
|
|
141
|
+
border: 1px solid var(--border-control);
|
|
142
|
+
border-radius: 6px;
|
|
143
|
+
background: var(--input-bg);
|
|
144
|
+
color: var(--text-on-control);
|
|
145
|
+
}
|
|
146
|
+
#search:focus { outline: none; border-color: var(--accent); }
|
|
147
|
+
|
|
148
|
+
#search-results { margin-top: 4px; }
|
|
149
|
+
#search-results .hit {
|
|
150
|
+
padding: 3px 6px;
|
|
151
|
+
border-radius: 4px;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
font-size: 12px;
|
|
154
|
+
white-space: nowrap;
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
text-overflow: ellipsis;
|
|
157
|
+
}
|
|
158
|
+
#search-results .hit:hover { background: var(--surface-raised); }
|
|
159
|
+
#search-results .hit .loc { color: var(--muted); font-size: 10px; }
|
|
160
|
+
|
|
161
|
+
.legend label {
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 6px;
|
|
165
|
+
padding: 1px 0;
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
user-select: none;
|
|
168
|
+
}
|
|
169
|
+
.legend .swatch {
|
|
170
|
+
width: 10px;
|
|
171
|
+
height: 10px;
|
|
172
|
+
border-radius: 3px;
|
|
173
|
+
flex: none;
|
|
174
|
+
}
|
|
175
|
+
.legend .count { margin-left: auto; color: var(--muted); font-size: 11px; }
|
|
176
|
+
.legend .help-badge {
|
|
177
|
+
flex: none;
|
|
178
|
+
width: 13px;
|
|
179
|
+
height: 13px;
|
|
180
|
+
border-radius: 50%;
|
|
181
|
+
border: 1px solid var(--border-subtle);
|
|
182
|
+
color: var(--muted-strong);
|
|
183
|
+
font-size: 9px;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
line-height: 11px;
|
|
186
|
+
text-align: center;
|
|
187
|
+
user-select: none;
|
|
188
|
+
opacity: 0.5;
|
|
189
|
+
}
|
|
190
|
+
.legend .help-badge:hover,
|
|
191
|
+
.legend .help-badge:focus {
|
|
192
|
+
background: var(--surface-hover);
|
|
193
|
+
color: var(--text-on-control);
|
|
194
|
+
border-color: var(--muted);
|
|
195
|
+
outline: none;
|
|
196
|
+
}
|
|
197
|
+
.legend label.master {
|
|
198
|
+
margin-bottom: 4px;
|
|
199
|
+
padding-bottom: 4px;
|
|
200
|
+
border-bottom: 1px solid var(--border);
|
|
201
|
+
color: var(--muted-strong);
|
|
202
|
+
}
|
|
203
|
+
.legend .swatch.spacer { background: transparent; }
|
|
204
|
+
|
|
205
|
+
.kind-tooltip {
|
|
206
|
+
position: fixed;
|
|
207
|
+
z-index: 1000;
|
|
208
|
+
max-width: 260px;
|
|
209
|
+
padding: 6px 9px;
|
|
210
|
+
border-radius: 6px;
|
|
211
|
+
background: var(--tooltip-bg);
|
|
212
|
+
border: 1px solid var(--border-control);
|
|
213
|
+
color: var(--tooltip-text);
|
|
214
|
+
font-size: 11px;
|
|
215
|
+
line-height: 1.4;
|
|
216
|
+
box-shadow: var(--shadow);
|
|
217
|
+
pointer-events: none;
|
|
218
|
+
}
|
|
219
|
+
.kind-tooltip[hidden] { display: none; }
|
|
220
|
+
|
|
221
|
+
.row { display: flex; align-items: center; gap: 6px; margin: 4px 0; }
|
|
222
|
+
|
|
223
|
+
select, button {
|
|
224
|
+
background: var(--surface-raised);
|
|
225
|
+
color: var(--text-on-control);
|
|
226
|
+
border: 1px solid var(--border-control);
|
|
227
|
+
border-radius: 6px;
|
|
228
|
+
padding: 4px 8px;
|
|
229
|
+
font: inherit;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
}
|
|
232
|
+
button:hover { background: var(--surface-hover); }
|
|
233
|
+
select { flex: 1; }
|
|
234
|
+
.colour-by label { font-size: 12px; color: var(--muted-strong); white-space: nowrap; }
|
|
235
|
+
|
|
236
|
+
#details-body { font-size: 12px; }
|
|
237
|
+
#details-body .id { color: var(--muted); font-size: 10px; word-break: break-all; }
|
|
238
|
+
#details-body .kind-tag {
|
|
239
|
+
display: inline-block;
|
|
240
|
+
padding: 1px 6px;
|
|
241
|
+
border-radius: 4px;
|
|
242
|
+
font-size: 10px;
|
|
243
|
+
color: var(--on-swatch);
|
|
244
|
+
font-weight: 600;
|
|
245
|
+
}
|
|
246
|
+
#details-body h3 { font-size: 11px; color: var(--muted); margin: 10px 0 3px; }
|
|
247
|
+
#details-body .edge-row { padding: 1px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
248
|
+
#details-body .edge-kind { color: var(--muted); font-size: 10px; }
|
|
249
|
+
#details-body .edge-count { color: var(--num); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
250
|
+
#details-body a { color: var(--link); cursor: pointer; text-decoration: none; }
|
|
251
|
+
#details-body a:hover { text-decoration: underline; }
|
|
252
|
+
#details-body a.file-link { text-decoration: underline; word-break: break-all; }
|
|
253
|
+
#details-body a.file-link::after { content: '↗'; margin-left: 3px; font-size: 9px; opacity: 0.7; }
|
|
254
|
+
|
|
255
|
+
#runtime.empty .heat-legend,
|
|
256
|
+
#runtime.empty .heat-note,
|
|
257
|
+
#runtime.empty .hotspots-title,
|
|
258
|
+
#runtime.empty #hotspots,
|
|
259
|
+
#runtime.empty label.row { display: none; }
|
|
260
|
+
|
|
261
|
+
#communities.empty { display: none; }
|
|
262
|
+
|
|
263
|
+
#coverage { font-size: 11px; color: var(--muted-strong); margin: 2px 0 6px; }
|
|
264
|
+
|
|
265
|
+
#runtime label.row { font-size: 12px; }
|
|
266
|
+
|
|
267
|
+
.heat-legend { display: flex; align-items: center; gap: 6px; margin: 8px 0 4px; }
|
|
268
|
+
.heat-bar { flex: 1; height: 10px; border-radius: 3px; background: linear-gradient(90deg, #64748b, #fde047, #dc2626); }
|
|
269
|
+
.heat-legend .heat-min, .heat-legend .heat-max { font-size: 10px; color: var(--muted); }
|
|
270
|
+
|
|
271
|
+
.heat-note { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--muted); }
|
|
272
|
+
.heat-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; display: inline-block; }
|
|
273
|
+
.heat-swatch.unmeasured { background: var(--unmeasured-fill); border: 1px dashed var(--unmeasured-border); }
|
|
274
|
+
|
|
275
|
+
.hotspots-title { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 12px 0 4px; }
|
|
276
|
+
#hotspots .hotspot {
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
gap: 6px;
|
|
280
|
+
padding: 2px 4px;
|
|
281
|
+
border-radius: 4px;
|
|
282
|
+
cursor: pointer;
|
|
283
|
+
font-size: 12px;
|
|
284
|
+
}
|
|
285
|
+
#hotspots .hotspot:hover { background: var(--surface-raised); }
|
|
286
|
+
.hotspot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
287
|
+
.hotspot-ms { margin-left: auto; flex: none; color: var(--num); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
288
|
+
|
|
289
|
+
#details-body .runtime-block { margin-top: 8px; }
|
|
290
|
+
#details-body .runtime-block .metric { display: flex; justify-content: space-between; gap: 8px; padding: 1px 0; }
|
|
291
|
+
#details-body .runtime-block .metric span { color: var(--muted); }
|
|
292
|
+
|
|
293
|
+
#cy { flex: 1; height: 100%; }
|
|
294
|
+
|
|
295
|
+
#dropzone {
|
|
296
|
+
position: fixed;
|
|
297
|
+
inset: 0;
|
|
298
|
+
display: flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
justify-content: center;
|
|
301
|
+
font-size: 18px;
|
|
302
|
+
color: var(--text-on-control);
|
|
303
|
+
background: var(--overlay-bg);
|
|
304
|
+
border: 3px dashed var(--accent);
|
|
305
|
+
pointer-events: none;
|
|
306
|
+
opacity: 0;
|
|
307
|
+
transition: opacity .15s;
|
|
308
|
+
}
|
|
309
|
+
#dropzone.active { opacity: 1; }
|
|
310
|
+
#dropzone code { color: var(--link); }
|
|
@@ -3,21 +3,47 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>
|
|
6
|
+
<title>ts-knowledge-graph - WebView</title>
|
|
7
|
+
<script>
|
|
8
|
+
/* Resolve the theme before the stylesheet paints so there is no flash of
|
|
9
|
+
the wrong colours. js/app.js owns the toggle and persistence after this. */
|
|
10
|
+
(function () {
|
|
11
|
+
try {
|
|
12
|
+
var stored = localStorage.getItem('ktg.theme');
|
|
13
|
+
var theme = stored === 'light' || stored === 'dark'
|
|
14
|
+
? stored
|
|
15
|
+
: (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
|
16
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
</script>
|
|
7
22
|
<link rel="stylesheet" href="./css/style.css">
|
|
8
23
|
</head>
|
|
9
24
|
<body>
|
|
10
25
|
<aside id="sidebar">
|
|
11
|
-
<
|
|
26
|
+
<div class="sidebar-head">
|
|
27
|
+
<h1>Knowledge graph</h1>
|
|
28
|
+
<button id="theme-toggle" class="theme-toggle" type="button" aria-label="Switch theme"></button>
|
|
29
|
+
</div>
|
|
12
30
|
<div id="status">no data loaded</div>
|
|
13
31
|
|
|
14
32
|
<input id="search" type="search" placeholder="Search symbol or file… (Enter)" autocomplete="off">
|
|
15
33
|
<div id="search-results"></div>
|
|
16
34
|
|
|
35
|
+
<div class="row colour-by">
|
|
36
|
+
<label for="encoding-select">Colour by</label>
|
|
37
|
+
<select id="encoding-select">
|
|
38
|
+
<option value="structural">kind</option>
|
|
39
|
+
<option value="runtime">self-time (heat)</option>
|
|
40
|
+
<option value="community">community</option>
|
|
41
|
+
</select>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
17
44
|
<section id="runtime" class="empty">
|
|
18
45
|
<h2 class="foldable" data-fold="runtime">Runtime</h2>
|
|
19
46
|
<div id="coverage">no runtime data</div>
|
|
20
|
-
<label class="row"><input id="runtime-heat" type="checkbox"> heat map (size + colour by self-time)</label>
|
|
21
47
|
<label class="row"><input id="only-measured" type="checkbox"> only measured nodes</label>
|
|
22
48
|
<div class="heat-legend">
|
|
23
49
|
<span class="heat-min">cold</span>
|
|
@@ -39,11 +65,17 @@
|
|
|
39
65
|
<div id="edge-kinds" class="legend"></div>
|
|
40
66
|
</section>
|
|
41
67
|
|
|
68
|
+
<section id="communities" class="empty">
|
|
69
|
+
<h2 class="foldable" data-fold="communities">Communities</h2>
|
|
70
|
+
<div id="community-legend" class="legend"></div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
42
73
|
<section>
|
|
43
74
|
<h2 class="foldable" data-fold="options">Options</h2>
|
|
44
75
|
<label class="row"><input id="hide-isolated" type="checkbox"> hide isolated nodes</label>
|
|
45
76
|
<div class="row">
|
|
46
77
|
<select id="layout-select">
|
|
78
|
+
<option value="fcose">fcose (force, label-aware)</option>
|
|
47
79
|
<option value="cose">cose (force)</option>
|
|
48
80
|
<option value="concentric">concentric (by degree)</option>
|
|
49
81
|
<option value="breadthfirst">breadthfirst</option>
|
|
@@ -66,9 +98,12 @@
|
|
|
66
98
|
<div>drop <code>nodes.jsonl</code> + <code>edges.jsonl</code> here</div>
|
|
67
99
|
</div>
|
|
68
100
|
|
|
69
|
-
<script src="./
|
|
70
|
-
<script src="./
|
|
101
|
+
<script src="./js_autogenerated/graph_data.js"></script>
|
|
102
|
+
<script src="./js_autogenerated/kind_descriptions.js"></script>
|
|
71
103
|
<script src="https://unpkg.com/cytoscape@3/dist/cytoscape.min.js"></script>
|
|
104
|
+
<script src="https://unpkg.com/layout-base/layout-base.js"></script>
|
|
105
|
+
<script src="https://unpkg.com/cose-base/cose-base.js"></script>
|
|
106
|
+
<script src="https://unpkg.com/cytoscape-fcose/cytoscape-fcose.js"></script>
|
|
72
107
|
<script src="./js/app.js"></script>
|
|
73
108
|
</body>
|
|
74
109
|
</html>
|