codesynapt 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +686 -0
- package/LICENSES.md +141 -0
- package/README.md +331 -0
- package/electron/main.cjs +2849 -0
- package/electron/plugin-loader.cjs +184 -0
- package/electron/preload.cjs +108 -0
- package/package.json +216 -0
- package/packages/core/bin/codesynapt-mcp.cjs +611 -0
- package/packages/core/bin/codesynapt.cjs +1933 -0
- package/packages/core/legacy.js +300 -0
- package/packages/core/lib/control-server.cjs +1539 -0
- package/packages/core/lib/embedding.cjs +89 -0
- package/packages/core/lib/logger.cjs +63 -0
- package/packages/core/lib/search-cache.cjs +140 -0
- package/packages/core/lib/search-worker.cjs +255 -0
- package/packages/core/lib/search.cjs +211 -0
- package/packages/core/lib/symbol-graph.cjs +402 -0
- package/packages/core/lib/symbol-parser-js.cjs +542 -0
- package/packages/core/lib/symbol-parser-misc.cjs +394 -0
- package/packages/core/lib/symbol-parser-py.cjs +215 -0
- package/packages/core/lib/symbol-parser-treesitter.cjs +658 -0
- package/packages/core/lib/symbol-parser-tsc.cjs +332 -0
- package/packages/core/monorepo.js +310 -0
- package/packages/core/parser.js +2234 -0
- package/packages/core/scanner.js +623 -0
- package/plugin-api/LICENSE +21 -0
- package/plugin-api/README.md +114 -0
- package/plugin-api/docs/01-getting-started.md +197 -0
- package/plugin-api/docs/02-concepts.md +269 -0
- package/plugin-api/docs/api-reference.md +463 -0
- package/plugin-api/docs/troubleshooting.md +332 -0
- package/plugin-api/docs/types/exporter.md +377 -0
- package/plugin-api/docs/types/theme.md +312 -0
- package/plugin-api/examples/hello-world-plugin/README.md +70 -0
- package/plugin-api/examples/hello-world-plugin/main.js +36 -0
- package/plugin-api/examples/hello-world-plugin/manifest.json +12 -0
- package/plugin-api/examples/mermaid-exporter/README.md +125 -0
- package/plugin-api/examples/mermaid-exporter/main.js +58 -0
- package/plugin-api/examples/mermaid-exporter/manifest.json +12 -0
- package/plugin-api/examples/rust-parser/README.md +71 -0
- package/plugin-api/examples/rust-parser/main.js +123 -0
- package/plugin-api/examples/rust-parser/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/README.md +95 -0
- package/plugin-api/examples/sunset-theme/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/theme.css +31 -0
- package/plugin-api/package.json +20 -0
- package/plugin-api/types.d.ts +395 -0
- package/public/app.js +6837 -0
- package/public/backend.js +285 -0
- package/public/index.html +647 -0
- package/public/plugin-host.js +321 -0
- package/public/style.css +4359 -0
- package/public/vendor/three.module.js +53044 -0
- package/scripts/competitor-watch.mjs +144 -0
- package/scripts/copy-vendor.js +21 -0
- package/scripts/download-bundled-node.cjs +53 -0
- package/scripts/fuses-after-pack.cjs +34 -0
- package/scripts/license-check.js +119 -0
- package/scripts/perf-test.js +200 -0
- package/server.js +132 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Rust parser plugin for filegraph3d.
|
|
2
|
+
//
|
|
3
|
+
// Recognizes:
|
|
4
|
+
// - `use path::to::thing;` → an import to "path::to::thing"
|
|
5
|
+
// - `mod foo;` → an import to the sibling/child module
|
|
6
|
+
// - `extern crate foo;` → an import to the named crate
|
|
7
|
+
//
|
|
8
|
+
// Skips:
|
|
9
|
+
// - Statements inside line comments (`// use foo`)
|
|
10
|
+
// - Statements inside block comments (`/* use foo */`)
|
|
11
|
+
// - Use statements inside macro definitions (rare, would need a
|
|
12
|
+
// real parser; we accept some false positives)
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
activate(ctx) {
|
|
16
|
+
ctx.parsers.register({
|
|
17
|
+
name: 'Rust',
|
|
18
|
+
extensions: ['rs'],
|
|
19
|
+
parse(filePath, content) {
|
|
20
|
+
// Strip comments first so we don't match `use` statements
|
|
21
|
+
// that are commented out.
|
|
22
|
+
const stripped = stripComments(content)
|
|
23
|
+
|
|
24
|
+
const imports = []
|
|
25
|
+
|
|
26
|
+
// `use path::to::thing;` — match the path portion only.
|
|
27
|
+
// Groups, aliases, and wildcards complicate this; we capture
|
|
28
|
+
// the leading path which is enough for graph edges.
|
|
29
|
+
const useRegex = /^\s*(?:pub\s+)?use\s+([a-zA-Z_][\w:]*)/gm
|
|
30
|
+
let m
|
|
31
|
+
while ((m = useRegex.exec(stripped)) !== null) {
|
|
32
|
+
imports.push({
|
|
33
|
+
path: m[1],
|
|
34
|
+
kind: 'import',
|
|
35
|
+
line: lineNumberAt(stripped, m.index),
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// `mod foo;` — local module declaration
|
|
40
|
+
const modRegex = /^\s*(?:pub\s+)?mod\s+([a-zA-Z_]\w*)\s*;/gm
|
|
41
|
+
while ((m = modRegex.exec(stripped)) !== null) {
|
|
42
|
+
imports.push({
|
|
43
|
+
path: m[1],
|
|
44
|
+
kind: 'import',
|
|
45
|
+
line: lineNumberAt(stripped, m.index),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `extern crate foo;` — older crate import syntax
|
|
50
|
+
const externRegex = /^\s*extern\s+crate\s+([a-zA-Z_]\w*)/gm
|
|
51
|
+
while ((m = externRegex.exec(stripped)) !== null) {
|
|
52
|
+
imports.push({
|
|
53
|
+
path: m[1],
|
|
54
|
+
kind: 'import',
|
|
55
|
+
line: lineNumberAt(stripped, m.index),
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { imports }
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
ctx.log('Rust parser registered — .rs files will now be parsed')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Replace comments with spaces so regex line numbers stay accurate
|
|
68
|
+
function stripComments(src) {
|
|
69
|
+
let result = ''
|
|
70
|
+
let i = 0
|
|
71
|
+
while (i < src.length) {
|
|
72
|
+
// Line comment: //...\n
|
|
73
|
+
if (src[i] === '/' && src[i + 1] === '/') {
|
|
74
|
+
while (i < src.length && src[i] !== '\n') {
|
|
75
|
+
result += ' '
|
|
76
|
+
i++
|
|
77
|
+
}
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
// Block comment: /* ... */ (non-nested; Rust allows nested but
|
|
81
|
+
// for our purposes flat handling is sufficient)
|
|
82
|
+
if (src[i] === '/' && src[i + 1] === '*') {
|
|
83
|
+
result += ' '
|
|
84
|
+
i += 2
|
|
85
|
+
while (i < src.length - 1 && !(src[i] === '*' && src[i + 1] === '/')) {
|
|
86
|
+
result += src[i] === '\n' ? '\n' : ' '
|
|
87
|
+
i++
|
|
88
|
+
}
|
|
89
|
+
result += ' '
|
|
90
|
+
i += 2
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
// String literal — skip past it so `use` inside strings doesn't
|
|
94
|
+
// produce a false positive
|
|
95
|
+
if (src[i] === '"') {
|
|
96
|
+
result += '"'
|
|
97
|
+
i++
|
|
98
|
+
while (i < src.length && src[i] !== '"') {
|
|
99
|
+
if (src[i] === '\\' && i + 1 < src.length) {
|
|
100
|
+
result += ' '
|
|
101
|
+
i += 2
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
result += src[i] === '\n' ? '\n' : ' '
|
|
105
|
+
i++
|
|
106
|
+
}
|
|
107
|
+
result += '"'
|
|
108
|
+
i++
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
result += src[i]
|
|
112
|
+
i++
|
|
113
|
+
}
|
|
114
|
+
return result
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function lineNumberAt(str, offset) {
|
|
118
|
+
let line = 1
|
|
119
|
+
for (let i = 0; i < offset && i < str.length; i++) {
|
|
120
|
+
if (str[i] === '\n') line++
|
|
121
|
+
}
|
|
122
|
+
return line
|
|
123
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "rust-parser",
|
|
3
|
+
"name": "Rust Parser",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": "filegraph3d",
|
|
6
|
+
"description": "Parse Rust `use` statements and mod declarations to build the dependency graph",
|
|
7
|
+
"type": "parser",
|
|
8
|
+
"main": "main.js",
|
|
9
|
+
"minAppVersion": "0.10.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"permissions": ["parse"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# sunset-theme
|
|
2
|
+
|
|
3
|
+
A warm coral-orange theme inspired by sunset over the ocean. Useful
|
|
4
|
+
as a starting point for your own theme plugin — copy and modify.
|
|
5
|
+
|
|
6
|
+
## What it looks like
|
|
7
|
+
|
|
8
|
+
| Element | Color |
|
|
9
|
+
|---|---|
|
|
10
|
+
| Background | Deep brown `#1A0F0A` |
|
|
11
|
+
| Foreground | Cream `#FFE4C4` |
|
|
12
|
+
| Primary accent | Warm orange `#FF8C42` |
|
|
13
|
+
| Active set color | Amber `#FFB347` |
|
|
14
|
+
| Warning color | Coral red `#FF6B6B` |
|
|
15
|
+
|
|
16
|
+
The theme uses 4px rounded corners and no decorative corner brackets,
|
|
17
|
+
giving it a softer feel than the default Observatory theme.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
Copy this folder into your filegraph3d plugins directory:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
macOS: ~/Library/Application Support/FileGraph 3D/plugins/
|
|
25
|
+
Windows: %APPDATA%\FileGraph 3D\plugins\
|
|
26
|
+
Linux: ~/.config/FileGraph 3D/plugins/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Quit and reopen filegraph3d. Open **Settings → Appearance** — "Sunset"
|
|
30
|
+
appears in the theme grid. Click it.
|
|
31
|
+
|
|
32
|
+
## Files
|
|
33
|
+
|
|
34
|
+
- **`manifest.json`** — declares the plugin as type `theme`. Theme
|
|
35
|
+
plugins don't need any permissions because they only contribute CSS.
|
|
36
|
+
|
|
37
|
+
- **`theme.css`** — overrides CSS variables under the
|
|
38
|
+
`body[data-theme="sunset-theme"]` selector. The selector value
|
|
39
|
+
matches the `id` in the manifest.
|
|
40
|
+
|
|
41
|
+
## What to change
|
|
42
|
+
|
|
43
|
+
Open `theme.css` and tweak. Some easy starting points:
|
|
44
|
+
|
|
45
|
+
### Different color family
|
|
46
|
+
|
|
47
|
+
The whole theme is built around `--accent: #FF8C42`. Swap that for any
|
|
48
|
+
other hue and pick three colors that:
|
|
49
|
+
|
|
50
|
+
- `--bg`: a deep version of (or complement to) the accent
|
|
51
|
+
- `--fg`: a light tint of the accent or a neutral
|
|
52
|
+
- `--accent-warm`: a brighter sibling color
|
|
53
|
+
- `--accent-pink`: contrast color for warnings
|
|
54
|
+
|
|
55
|
+
### Add corner brackets
|
|
56
|
+
|
|
57
|
+
```css
|
|
58
|
+
body[data-theme="sunset-theme"] {
|
|
59
|
+
--decoration: 1; /* show corner brackets on panels */
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Make it brutalist
|
|
64
|
+
|
|
65
|
+
```css
|
|
66
|
+
body[data-theme="sunset-theme"] {
|
|
67
|
+
--decoration: 1;
|
|
68
|
+
--radius: 0; /* sharp corners */
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Lighter background
|
|
73
|
+
|
|
74
|
+
If you want a "sunrise" version instead:
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
body[data-theme="sunset-theme"] {
|
|
78
|
+
--bg: #FFF4E6;
|
|
79
|
+
--fg: #4A2810;
|
|
80
|
+
--grain: 0;
|
|
81
|
+
/* ... and adjust borders/accents accordingly */
|
|
82
|
+
}
|
|
83
|
+
body[data-theme="sunset-theme"]::before {
|
|
84
|
+
opacity: 0 !important; /* turn off the dark-mode grain */
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT — fork, modify, redistribute freely.
|
|
91
|
+
|
|
92
|
+
## More
|
|
93
|
+
|
|
94
|
+
For the complete list of CSS variables and theming guidance, see
|
|
95
|
+
[../../docs/types/theme.md](../../docs/types/theme.md).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "sunset-theme",
|
|
3
|
+
"name": "Sunset",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": "filegraph3d",
|
|
6
|
+
"description": "Warm orange-coral theme inspired by sunset over the ocean",
|
|
7
|
+
"type": "theme",
|
|
8
|
+
"main": "theme.css",
|
|
9
|
+
"minAppVersion": "0.10.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"permissions": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Sunset theme — warm coral/orange palette */
|
|
2
|
+
/* Loaded automatically when user selects "Sunset" in Appearance */
|
|
3
|
+
|
|
4
|
+
body[data-theme="sunset-theme"] {
|
|
5
|
+
--bg: #1A0F0A;
|
|
6
|
+
--bg-deep: #100805;
|
|
7
|
+
--bg-elev: rgba(40, 22, 14, 0.85);
|
|
8
|
+
--bg-glass: rgba(26, 15, 10, 0.85);
|
|
9
|
+
--bg-solid: #261810;
|
|
10
|
+
|
|
11
|
+
--border: rgba(255, 140, 66, 0.18);
|
|
12
|
+
--border-hot: rgba(255, 140, 66, 0.55);
|
|
13
|
+
--border-edge: rgba(255, 140, 66, 0.06);
|
|
14
|
+
|
|
15
|
+
--fg: #FFE4C4;
|
|
16
|
+
--fg-dim: #D8B080;
|
|
17
|
+
--fg-mute: #A88060;
|
|
18
|
+
--fg-faint: #685040;
|
|
19
|
+
|
|
20
|
+
--accent: #FF8C42; /* warm orange */
|
|
21
|
+
--accent-warm: #FFB347; /* amber */
|
|
22
|
+
--accent-pink: #FF6B6B; /* coral red */
|
|
23
|
+
--accent-cool: #E8A87C; /* dusty rose */
|
|
24
|
+
--danger: #FF4444;
|
|
25
|
+
|
|
26
|
+
--decoration: 0;
|
|
27
|
+
--grain: 0.03;
|
|
28
|
+
--motion-scale: 0.6;
|
|
29
|
+
|
|
30
|
+
--radius: 4px;
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@filegraph3d/plugin-api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "TypeScript types and helpers for filegraph3d plugins",
|
|
6
|
+
"types": "types.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"types.d.ts",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"examples/"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": ["filegraph3d", "plugin", "api", "code-visualization"],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/YOUR_USER/filegraph3d.git",
|
|
18
|
+
"directory": "plugin-api"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
// @filegraph3d/plugin-api — public API surface for plugins
|
|
2
|
+
// MIT licensed — free to use, fork, distribute.
|
|
3
|
+
|
|
4
|
+
/* ════════════════════════════════════════════════════════════
|
|
5
|
+
Plugin manifest
|
|
6
|
+
════════════════════════════════════════════════════════════ */
|
|
7
|
+
|
|
8
|
+
export interface PluginManifest {
|
|
9
|
+
/** Unique identifier (kebab-case, e.g. "tokyo-night-theme") */
|
|
10
|
+
id: string
|
|
11
|
+
|
|
12
|
+
/** Display name */
|
|
13
|
+
name: string
|
|
14
|
+
|
|
15
|
+
/** Plugin version (semver) */
|
|
16
|
+
version: string
|
|
17
|
+
|
|
18
|
+
/** Author name (no email required) */
|
|
19
|
+
author: string
|
|
20
|
+
|
|
21
|
+
/** One-line description shown in plugin list */
|
|
22
|
+
description: string
|
|
23
|
+
|
|
24
|
+
/** Plugin type — determines what API surface it gets */
|
|
25
|
+
type: 'theme' | 'exporter' | 'parser' | 'layout' | 'panel' | 'action'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Entry point file (relative to manifest.json):
|
|
29
|
+
* - For theme: a .css file
|
|
30
|
+
* - For others: a .js module
|
|
31
|
+
*/
|
|
32
|
+
main: string
|
|
33
|
+
|
|
34
|
+
/** Minimum filegraph3d app version (semver range) */
|
|
35
|
+
minAppVersion: string
|
|
36
|
+
|
|
37
|
+
/** SPDX license identifier (MIT, Apache-2.0, etc) */
|
|
38
|
+
license: string
|
|
39
|
+
|
|
40
|
+
/** Optional URL for the plugin homepage / repo */
|
|
41
|
+
homepage?: string
|
|
42
|
+
|
|
43
|
+
/** Optional: which app capabilities does this plugin need? */
|
|
44
|
+
permissions?: PluginPermission[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type PluginPermission =
|
|
48
|
+
| 'read-files' // Read source file contents
|
|
49
|
+
| 'read-graph' // Read the current node/edge state
|
|
50
|
+
| 'modify-graph' // Add nodes/edges (rare, dangerous)
|
|
51
|
+
| 'ui-panel' // Render in a side panel
|
|
52
|
+
| 'context-menu' // Add right-click items
|
|
53
|
+
| 'export' // Provide an export format
|
|
54
|
+
| 'parse' // Provide a language parser
|
|
55
|
+
|
|
56
|
+
/* ════════════════════════════════════════════════════════════
|
|
57
|
+
Plugin context — passed to every plugin's activate() function
|
|
58
|
+
════════════════════════════════════════════════════════════ */
|
|
59
|
+
|
|
60
|
+
export interface PluginContext {
|
|
61
|
+
/** This plugin's manifest */
|
|
62
|
+
manifest: PluginManifest
|
|
63
|
+
|
|
64
|
+
/** App version at load time */
|
|
65
|
+
appVersion: string
|
|
66
|
+
|
|
67
|
+
/** Read-only access to graph state */
|
|
68
|
+
graph: GraphAPI
|
|
69
|
+
|
|
70
|
+
/** UI registration */
|
|
71
|
+
ui: UIAPI
|
|
72
|
+
|
|
73
|
+
/** Export format registration */
|
|
74
|
+
exporters: ExporterRegistry
|
|
75
|
+
|
|
76
|
+
/** Parser registration */
|
|
77
|
+
parsers: ParserRegistry
|
|
78
|
+
|
|
79
|
+
/** Layout algorithm registration */
|
|
80
|
+
layouts: LayoutRegistry
|
|
81
|
+
|
|
82
|
+
/** Event subscription */
|
|
83
|
+
events: EventBus
|
|
84
|
+
|
|
85
|
+
/** Persistent per-plugin storage (localStorage-backed) */
|
|
86
|
+
storage: PluginStorage
|
|
87
|
+
|
|
88
|
+
/** Helper: show a toast message */
|
|
89
|
+
toast: (message: string) => void
|
|
90
|
+
|
|
91
|
+
/** Helper: log to plugin-specific console */
|
|
92
|
+
log: (...args: unknown[]) => void
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ════════════════════════════════════════════════════════════
|
|
96
|
+
Graph API
|
|
97
|
+
════════════════════════════════════════════════════════════ */
|
|
98
|
+
|
|
99
|
+
export interface GraphNode {
|
|
100
|
+
/** File path relative to project root */
|
|
101
|
+
id: string
|
|
102
|
+
|
|
103
|
+
/** File extension (without dot) */
|
|
104
|
+
ext: string
|
|
105
|
+
|
|
106
|
+
/** File size in bytes */
|
|
107
|
+
size: number
|
|
108
|
+
|
|
109
|
+
/** Lines of code */
|
|
110
|
+
loc: number
|
|
111
|
+
|
|
112
|
+
/** Display color (hex) */
|
|
113
|
+
hex: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface GraphEdge {
|
|
117
|
+
/** Source node id */
|
|
118
|
+
s: string
|
|
119
|
+
|
|
120
|
+
/** Target node id */
|
|
121
|
+
t: string
|
|
122
|
+
|
|
123
|
+
/** Edge kind: 'import' | 'require' | 'css' | etc */
|
|
124
|
+
k: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface GraphAPI {
|
|
128
|
+
/** Current project root path */
|
|
129
|
+
readonly root: string
|
|
130
|
+
|
|
131
|
+
/** All nodes (read-only) */
|
|
132
|
+
readonly nodes: ReadonlyArray<GraphNode>
|
|
133
|
+
|
|
134
|
+
/** All edges (read-only) */
|
|
135
|
+
readonly edges: ReadonlyArray<GraphEdge>
|
|
136
|
+
|
|
137
|
+
/** Currently selected node id (or null) */
|
|
138
|
+
readonly selectedId: string | null
|
|
139
|
+
|
|
140
|
+
/** Currently active set of file ids (or null if disabled) */
|
|
141
|
+
readonly activeSet: ReadonlySet<string> | null
|
|
142
|
+
|
|
143
|
+
/** Read file contents (requires "read-files" permission) */
|
|
144
|
+
readFile(id: string): Promise<string>
|
|
145
|
+
|
|
146
|
+
/** Get node by id, or null */
|
|
147
|
+
getNode(id: string): GraphNode | null
|
|
148
|
+
|
|
149
|
+
/** Get all outgoing edges from a node */
|
|
150
|
+
outgoing(id: string): GraphEdge[]
|
|
151
|
+
|
|
152
|
+
/** Get all incoming edges to a node */
|
|
153
|
+
incoming(id: string): GraphEdge[]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ════════════════════════════════════════════════════════════
|
|
157
|
+
UI API
|
|
158
|
+
════════════════════════════════════════════════════════════ */
|
|
159
|
+
|
|
160
|
+
export interface UIAPI {
|
|
161
|
+
/**
|
|
162
|
+
* Register a panel that appears in the right rail.
|
|
163
|
+
* Returns a handle for later removal.
|
|
164
|
+
*/
|
|
165
|
+
registerPanel(opts: PanelOptions): PanelHandle
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Register a context-menu item that appears when right-clicking
|
|
169
|
+
* a node in the graph.
|
|
170
|
+
*/
|
|
171
|
+
registerContextMenuItem(opts: ContextMenuOptions): MenuItemHandle
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Register a command (shows up in command palette if available).
|
|
175
|
+
*/
|
|
176
|
+
registerCommand(opts: CommandOptions): CommandHandle
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface PanelOptions {
|
|
180
|
+
/** Unique id within this plugin */
|
|
181
|
+
id: string
|
|
182
|
+
|
|
183
|
+
/** Title shown in panel header */
|
|
184
|
+
title: string
|
|
185
|
+
|
|
186
|
+
/** Position (default: 'right') */
|
|
187
|
+
position?: 'right' | 'left' | 'bottom'
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Render function — return HTML string or DOM element.
|
|
191
|
+
* Called when the panel becomes visible or when refresh() is called.
|
|
192
|
+
*/
|
|
193
|
+
render: (container: HTMLElement) => void
|
|
194
|
+
|
|
195
|
+
/** Whether the panel is visible by default */
|
|
196
|
+
defaultVisible?: boolean
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface PanelHandle {
|
|
200
|
+
/** Force re-render */
|
|
201
|
+
refresh(): void
|
|
202
|
+
|
|
203
|
+
/** Show the panel */
|
|
204
|
+
show(): void
|
|
205
|
+
|
|
206
|
+
/** Hide the panel */
|
|
207
|
+
hide(): void
|
|
208
|
+
|
|
209
|
+
/** Remove the panel entirely */
|
|
210
|
+
dispose(): void
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface ContextMenuOptions {
|
|
214
|
+
/** Display label */
|
|
215
|
+
label: string
|
|
216
|
+
|
|
217
|
+
/** Optional icon (single character or short string) */
|
|
218
|
+
icon?: string
|
|
219
|
+
|
|
220
|
+
/** When to show — return true to enable */
|
|
221
|
+
enabled?: (nodeId: string) => boolean
|
|
222
|
+
|
|
223
|
+
/** Click handler */
|
|
224
|
+
action: (nodeId: string) => void | Promise<void>
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface MenuItemHandle {
|
|
228
|
+
dispose(): void
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface CommandOptions {
|
|
232
|
+
/** Unique command id */
|
|
233
|
+
id: string
|
|
234
|
+
|
|
235
|
+
/** Display name in command palette */
|
|
236
|
+
name: string
|
|
237
|
+
|
|
238
|
+
/** Optional keyboard shortcut suggestion */
|
|
239
|
+
shortcut?: string
|
|
240
|
+
|
|
241
|
+
/** Handler */
|
|
242
|
+
action: () => void | Promise<void>
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface CommandHandle {
|
|
246
|
+
dispose(): void
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* ════════════════════════════════════════════════════════════
|
|
250
|
+
Exporter registration
|
|
251
|
+
════════════════════════════════════════════════════════════ */
|
|
252
|
+
|
|
253
|
+
export interface ExporterRegistry {
|
|
254
|
+
register(opts: ExporterOptions): ExporterHandle
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface ExporterOptions {
|
|
258
|
+
/** Display name (e.g. "Mermaid diagram") */
|
|
259
|
+
name: string
|
|
260
|
+
|
|
261
|
+
/** File extension (e.g. "mmd") */
|
|
262
|
+
extension: string
|
|
263
|
+
|
|
264
|
+
/** MIME type for the download */
|
|
265
|
+
mimeType: string
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Generate the export content.
|
|
269
|
+
* Receives the current graph state and returns the file content.
|
|
270
|
+
*/
|
|
271
|
+
generate: (graph: GraphAPI) => string | Promise<string>
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface ExporterHandle {
|
|
275
|
+
dispose(): void
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* ════════════════════════════════════════════════════════════
|
|
279
|
+
Parser registration
|
|
280
|
+
════════════════════════════════════════════════════════════ */
|
|
281
|
+
|
|
282
|
+
export interface ParserRegistry {
|
|
283
|
+
register(opts: ParserOptions): ParserHandle
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface ParserOptions {
|
|
287
|
+
/** Language name (e.g. "Rust") */
|
|
288
|
+
name: string
|
|
289
|
+
|
|
290
|
+
/** File extensions this parser handles (without dot) */
|
|
291
|
+
extensions: string[]
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Parse a file and return its imports.
|
|
295
|
+
* Resolution from import path to file id is done by the app.
|
|
296
|
+
*/
|
|
297
|
+
parse: (filePath: string, content: string) => ParseResult
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export interface ParseResult {
|
|
301
|
+
/** Import statements found in this file */
|
|
302
|
+
imports: ParseImport[]
|
|
303
|
+
|
|
304
|
+
/** Optional: lines of code count (if not given, app counts newlines) */
|
|
305
|
+
loc?: number
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface ParseImport {
|
|
309
|
+
/** The raw import path (e.g. "./foo", "react", "@/utils") */
|
|
310
|
+
path: string
|
|
311
|
+
|
|
312
|
+
/** Import kind */
|
|
313
|
+
kind: 'import' | 'require' | 'dynamic' | 'css' | 'asset' | 'other'
|
|
314
|
+
|
|
315
|
+
/** Source line number (optional, for debugging) */
|
|
316
|
+
line?: number
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface ParserHandle {
|
|
320
|
+
dispose(): void
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* ════════════════════════════════════════════════════════════
|
|
324
|
+
Layout algorithm registration
|
|
325
|
+
════════════════════════════════════════════════════════════ */
|
|
326
|
+
|
|
327
|
+
export interface LayoutRegistry {
|
|
328
|
+
register(opts: LayoutOptions): LayoutHandle
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface LayoutOptions {
|
|
332
|
+
/** Display name (e.g. "Hierarchical") */
|
|
333
|
+
name: string
|
|
334
|
+
|
|
335
|
+
/** Unique id */
|
|
336
|
+
id: string
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Compute positions for all nodes.
|
|
340
|
+
* Called once when this layout is activated, then again whenever
|
|
341
|
+
* the graph topology changes.
|
|
342
|
+
*
|
|
343
|
+
* Should return a Map from node id to {x, y, z} coordinates.
|
|
344
|
+
*/
|
|
345
|
+
compute: (nodes: ReadonlyArray<GraphNode>, edges: ReadonlyArray<GraphEdge>)
|
|
346
|
+
=> Map<string, { x: number; y: number; z: number }>
|
|
347
|
+
| Promise<Map<string, { x: number; y: number; z: number }>>
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface LayoutHandle {
|
|
351
|
+
dispose(): void
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* ════════════════════════════════════════════════════════════
|
|
355
|
+
Event bus
|
|
356
|
+
════════════════════════════════════════════════════════════ */
|
|
357
|
+
|
|
358
|
+
export type EventName =
|
|
359
|
+
| 'snapshot:applied' // New graph data loaded — { root: string }
|
|
360
|
+
| 'selection:changed' // User selected a node — string | null
|
|
361
|
+
| 'filter:changed' // Filter text changed
|
|
362
|
+
| 'focus:changed' // Focused node changed
|
|
363
|
+
| 'graph:cleared' // Graph was cleared
|
|
364
|
+
| 'activeset:changed' // Active set updated
|
|
365
|
+
|
|
366
|
+
export interface EventBus {
|
|
367
|
+
on<T = unknown>(event: EventName, handler: (payload: T) => void): () => void
|
|
368
|
+
off(event: EventName, handler: (payload: unknown) => void): void
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* ════════════════════════════════════════════════════════════
|
|
372
|
+
Plugin storage (persistent per-plugin key-value store)
|
|
373
|
+
════════════════════════════════════════════════════════════ */
|
|
374
|
+
|
|
375
|
+
export interface PluginStorage {
|
|
376
|
+
get<T = unknown>(key: string): T | null
|
|
377
|
+
set<T = unknown>(key: string, value: T): void
|
|
378
|
+
delete(key: string): void
|
|
379
|
+
clear(): void
|
|
380
|
+
keys(): string[]
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* ════════════════════════════════════════════════════════════
|
|
384
|
+
Plugin entry-point signature
|
|
385
|
+
════════════════════════════════════════════════════════════ */
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Every plugin's main module must default-export an object with at
|
|
389
|
+
* least an `activate` function. `deactivate` is optional, called when
|
|
390
|
+
* the user disables or uninstalls the plugin.
|
|
391
|
+
*/
|
|
392
|
+
export interface Plugin {
|
|
393
|
+
activate(ctx: PluginContext): void | Promise<void>
|
|
394
|
+
deactivate?(): void | Promise<void>
|
|
395
|
+
}
|