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,114 @@
|
|
|
1
|
+
# filegraph3d plugin development
|
|
2
|
+
|
|
3
|
+
[](./LICENSE)
|
|
4
|
+
[](./package.json)
|
|
5
|
+
[](#six-plugin-types)
|
|
6
|
+
[](./examples/)
|
|
7
|
+
|
|
8
|
+
> Build themes, exporters, parsers, layouts, panels, and context-menu
|
|
9
|
+
> actions for [filegraph3d](https://github.com/YOUR_USER/filegraph3d).
|
|
10
|
+
|
|
11
|
+
This package contains the public API surface for filegraph3d plugins.
|
|
12
|
+
It is **MIT-licensed** — you can build and distribute plugins under any
|
|
13
|
+
license you choose, including commercially. The main filegraph3d app
|
|
14
|
+
itself is AGPL-licensed; see the top-level `LICENSE` for details.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Where to start
|
|
19
|
+
|
|
20
|
+
| If you want to… | Read this |
|
|
21
|
+
|---|---|
|
|
22
|
+
| Build your first plugin in 5 minutes | [Getting started](./docs/01-getting-started.md) |
|
|
23
|
+
| Understand how plugins work | [Concepts](./docs/02-concepts.md) |
|
|
24
|
+
| Add a new color theme | [Theme guide](./docs/types/theme.md) |
|
|
25
|
+
| Add an export format | [Exporter guide](./docs/types/exporter.md) |
|
|
26
|
+
| Look up an API method | [API reference](./docs/api-reference.md) |
|
|
27
|
+
| Fix something that's not working | [Troubleshooting](./docs/troubleshooting.md) |
|
|
28
|
+
| Copy a working example | [examples/](./examples/) |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Six plugin types
|
|
33
|
+
|
|
34
|
+
| Type | What it does | Difficulty | Example |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `theme` | A new color palette and look | ★☆☆ | [sunset-theme](./examples/sunset-theme/) |
|
|
37
|
+
| `exporter` | A new export format (Mermaid, GraphViz, etc.) | ★☆☆ | [mermaid-exporter](./examples/mermaid-exporter/) |
|
|
38
|
+
| `action` | A right-click item on graph nodes | ★☆☆ | [hello-world-plugin](./examples/hello-world-plugin/) |
|
|
39
|
+
| `parser` | Support for a new language | ★★☆ | [rust-parser](./examples/rust-parser/) |
|
|
40
|
+
| `panel` | A side panel in the app UI | ★★☆ | (in progress) |
|
|
41
|
+
| `layout` | A new graph layout algorithm | ★★★ | (in progress) |
|
|
42
|
+
|
|
43
|
+
A plugin has exactly one type — pick the smallest one that fits. You
|
|
44
|
+
can ship multiple plugins for different concerns.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Anatomy of a plugin
|
|
49
|
+
|
|
50
|
+
Every plugin is a folder containing **at least two files**:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
my-plugin/
|
|
54
|
+
├── manifest.json ← metadata (id, name, type, permissions)
|
|
55
|
+
└── main.js ← entry point (theme.css for theme plugins)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That's it. No build step required (you can use plain JS), no `npm install`
|
|
59
|
+
needed (the API surface is just a type definition for IDE support).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Where plugins live
|
|
64
|
+
|
|
65
|
+
filegraph3d looks for plugins in a per-user directory:
|
|
66
|
+
|
|
67
|
+
| OS | Path |
|
|
68
|
+
|---|---|
|
|
69
|
+
| **macOS** | `~/Library/Application Support/FileGraph 3D/plugins/` |
|
|
70
|
+
| **Windows** | `%APPDATA%\FileGraph 3D\plugins\` |
|
|
71
|
+
| **Linux** | `~/.config/FileGraph 3D/plugins/` |
|
|
72
|
+
|
|
73
|
+
You can open this folder from the app via **Settings → Appearance →
|
|
74
|
+
Open plugin folder…**.
|
|
75
|
+
|
|
76
|
+
Each plugin is its own subfolder. Restart filegraph3d after installing
|
|
77
|
+
a plugin to pick it up.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## TypeScript support (optional)
|
|
82
|
+
|
|
83
|
+
If you're using TypeScript, the types in `types.d.ts` give you full
|
|
84
|
+
IntelliSense and compile-time checks:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npm install --save-dev @filegraph3d/plugin-api
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import type { Plugin } from '@filegraph3d/plugin-api'
|
|
92
|
+
|
|
93
|
+
const plugin: Plugin = {
|
|
94
|
+
activate(ctx) {
|
|
95
|
+
ctx.log('hello from typescript')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export default plugin
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can also use plain JavaScript — the API works identically.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
This API package: **MIT** — see [LICENSE](./LICENSE).
|
|
108
|
+
|
|
109
|
+
You can publish your plugins under any license you like (MIT,
|
|
110
|
+
Apache-2.0, proprietary, etc).
|
|
111
|
+
|
|
112
|
+
The filegraph3d app itself: **AGPL-3.0** — see [`../LICENSE`](../LICENSE).
|
|
113
|
+
Personal and internal use is free; commercial redistribution requires
|
|
114
|
+
a license.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
You'll build your first filegraph3d plugin in five minutes. We'll walk
|
|
4
|
+
through two starter plugins — a **theme** (no JavaScript, just CSS)
|
|
5
|
+
and an **action** (a context-menu item that runs code).
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- filegraph3d installed and working
|
|
10
|
+
- A text editor of any kind
|
|
11
|
+
- That's it — no Node.js, no build tools, nothing to install
|
|
12
|
+
|
|
13
|
+
## 1. Open your plugin folder
|
|
14
|
+
|
|
15
|
+
In filegraph3d, click **Settings → Appearance → Open plugin folder…**.
|
|
16
|
+
|
|
17
|
+
This opens (and creates if missing) the per-user plugin directory:
|
|
18
|
+
|
|
19
|
+
| OS | Path |
|
|
20
|
+
|---|---|
|
|
21
|
+
| macOS | `~/Library/Application Support/FileGraph 3D/plugins/` |
|
|
22
|
+
| Windows | `%APPDATA%\FileGraph 3D\plugins\` |
|
|
23
|
+
| Linux | `~/.config/FileGraph 3D/plugins/` |
|
|
24
|
+
|
|
25
|
+
## 2A. Track A — make a theme (the easiest plugin)
|
|
26
|
+
|
|
27
|
+
Inside the plugin folder, create a new subfolder called `my-theme`:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
plugins/
|
|
31
|
+
└── my-theme/
|
|
32
|
+
├── manifest.json
|
|
33
|
+
└── theme.css
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**`manifest.json`:**
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"id": "my-theme",
|
|
41
|
+
"name": "My Theme",
|
|
42
|
+
"version": "1.0.0",
|
|
43
|
+
"author": "you",
|
|
44
|
+
"description": "My very first theme",
|
|
45
|
+
"type": "theme",
|
|
46
|
+
"main": "theme.css",
|
|
47
|
+
"minAppVersion": "0.10.0",
|
|
48
|
+
"license": "MIT"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**`theme.css`:**
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
body[data-theme="my-theme"] {
|
|
56
|
+
--bg: #1F1B2E;
|
|
57
|
+
--bg-glass: rgba(30, 25, 50, 0.85);
|
|
58
|
+
--fg: #F0E8FF;
|
|
59
|
+
--fg-dim: #C5B8E0;
|
|
60
|
+
--fg-mute: #8B7DB0;
|
|
61
|
+
|
|
62
|
+
--border: rgba(180, 120, 255, 0.18);
|
|
63
|
+
--border-hot: rgba(180, 120, 255, 0.55);
|
|
64
|
+
--border-edge: rgba(180, 120, 255, 0.06);
|
|
65
|
+
|
|
66
|
+
--accent: #B478FF; /* purple */
|
|
67
|
+
--accent-warm: #FFB845; /* warm gold */
|
|
68
|
+
--accent-pink: #FF6B9F;
|
|
69
|
+
--accent-cool: #78D9FF;
|
|
70
|
+
|
|
71
|
+
--decoration: 0;
|
|
72
|
+
--grain: 0.02;
|
|
73
|
+
--radius: 4px;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That's everything. Now:
|
|
78
|
+
|
|
79
|
+
1. **Quit filegraph3d completely** (Cmd/Ctrl+Q, not just close window).
|
|
80
|
+
2. **Reopen** the app.
|
|
81
|
+
3. Go to **Settings → Appearance**. You'll see "My Theme" in the
|
|
82
|
+
theme grid.
|
|
83
|
+
4. Click it.
|
|
84
|
+
|
|
85
|
+
The whole UI flips to your purple palette. You just made a theme.
|
|
86
|
+
|
|
87
|
+
→ **For the full list of CSS variables and how to tune them, read
|
|
88
|
+
the [theme guide](./types/theme.md).**
|
|
89
|
+
|
|
90
|
+
## 2B. Track B — make an action plugin (your first JS plugin)
|
|
91
|
+
|
|
92
|
+
Actions are the simplest code plugins. They add an item to the
|
|
93
|
+
right-click menu on graph nodes.
|
|
94
|
+
|
|
95
|
+
Create `plugins/my-action/`:
|
|
96
|
+
|
|
97
|
+
**`manifest.json`:**
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"id": "my-action",
|
|
102
|
+
"name": "My Action",
|
|
103
|
+
"version": "1.0.0",
|
|
104
|
+
"author": "you",
|
|
105
|
+
"description": "Shows file info when you right-click a node",
|
|
106
|
+
"type": "action",
|
|
107
|
+
"main": "main.js",
|
|
108
|
+
"minAppVersion": "0.10.0",
|
|
109
|
+
"license": "MIT",
|
|
110
|
+
"permissions": ["context-menu", "read-graph"]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**`main.js`:**
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
export default {
|
|
118
|
+
activate(ctx) {
|
|
119
|
+
ctx.log('My Action plugin loaded')
|
|
120
|
+
|
|
121
|
+
ctx.ui.registerContextMenuItem({
|
|
122
|
+
label: 'Show info for this file',
|
|
123
|
+
icon: 'ℹ',
|
|
124
|
+
action: (nodeId) => {
|
|
125
|
+
const node = ctx.graph.getNode(nodeId)
|
|
126
|
+
if (!node) {
|
|
127
|
+
ctx.toast('Node not found')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
const outs = ctx.graph.outgoing(nodeId).length
|
|
131
|
+
const ins = ctx.graph.incoming(nodeId).length
|
|
132
|
+
ctx.toast(
|
|
133
|
+
`${nodeId}\n` +
|
|
134
|
+
` ${node.loc} lines, ${node.size} bytes\n` +
|
|
135
|
+
` imports ${outs}, used by ${ins}`
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Quit filegraph3d. Reopen. Open a folder. Right-click any node in the
|
|
144
|
+
graph. You'll see "Show info for this file" in the menu. Click it.
|
|
145
|
+
|
|
146
|
+
A toast pops up with the file's stats. You just made a code plugin.
|
|
147
|
+
|
|
148
|
+
## What just happened
|
|
149
|
+
|
|
150
|
+
Every plugin follows the same shape:
|
|
151
|
+
|
|
152
|
+
1. **`manifest.json`** declares who the plugin is, what it does, and
|
|
153
|
+
what permissions it needs.
|
|
154
|
+
2. **The entry file** (either `theme.css` for themes or `main.js` for
|
|
155
|
+
code plugins) is the actual implementation.
|
|
156
|
+
3. **For code plugins**, the default export has an `activate(ctx)`
|
|
157
|
+
function. `ctx` is a curated API — see the
|
|
158
|
+
[API reference](./api-reference.md) for the full surface.
|
|
159
|
+
|
|
160
|
+
## Hot reload (limitations)
|
|
161
|
+
|
|
162
|
+
filegraph3d doesn't yet support hot-reload for plugins. Any change to
|
|
163
|
+
a plugin requires **quitting and reopening the app**.
|
|
164
|
+
|
|
165
|
+
A common workflow is:
|
|
166
|
+
|
|
167
|
+
1. Edit the plugin file in your text editor.
|
|
168
|
+
2. **Cmd/Ctrl+Q** to quit filegraph3d.
|
|
169
|
+
3. Reopen the app — your plugin reloads with the changes.
|
|
170
|
+
|
|
171
|
+
(Hot reload is on the roadmap; until then, the quit-restart cycle is
|
|
172
|
+
fast — a few seconds.)
|
|
173
|
+
|
|
174
|
+
## Next steps
|
|
175
|
+
|
|
176
|
+
Now that you have a working plugin:
|
|
177
|
+
|
|
178
|
+
- **[Concepts](./02-concepts.md)** — understand the plugin lifecycle,
|
|
179
|
+
manifest, and permission system in depth.
|
|
180
|
+
- **[Theme guide](./types/theme.md)** — every CSS variable you can override.
|
|
181
|
+
- **[Exporter guide](./types/exporter.md)** — add a new export format.
|
|
182
|
+
- **[API reference](./api-reference.md)** — every method on `ctx`.
|
|
183
|
+
- **[Troubleshooting](./troubleshooting.md)** — what to do when things break.
|
|
184
|
+
|
|
185
|
+
## Got stuck?
|
|
186
|
+
|
|
187
|
+
If your plugin doesn't show up:
|
|
188
|
+
|
|
189
|
+
1. Check **Settings → Appearance → Open plugin folder…** is the actual
|
|
190
|
+
path you put files in.
|
|
191
|
+
2. Make sure each plugin is in its own **subfolder** (not loose files).
|
|
192
|
+
3. Make sure `manifest.json` is **valid JSON** — paste it into
|
|
193
|
+
`jsonlint.com` if unsure.
|
|
194
|
+
4. Open **DevTools** (View → Toggle Developer Tools) and look at the
|
|
195
|
+
Console — plugin load errors are logged there.
|
|
196
|
+
|
|
197
|
+
More in [troubleshooting](./troubleshooting.md).
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
Once you've built [your first plugin](./01-getting-started.md), the
|
|
4
|
+
pieces below explain why the system is shaped the way it is.
|
|
5
|
+
|
|
6
|
+
- [Plugin types](#plugin-types)
|
|
7
|
+
- [The manifest](#the-manifest)
|
|
8
|
+
- [Lifecycle](#lifecycle)
|
|
9
|
+
- [The `ctx` object](#the-ctx-object)
|
|
10
|
+
- [Permissions](#permissions)
|
|
11
|
+
- [Disposables](#disposables)
|
|
12
|
+
- [Plugin storage](#plugin-storage)
|
|
13
|
+
- [The event bus](#the-event-bus)
|
|
14
|
+
|
|
15
|
+
## Plugin types
|
|
16
|
+
|
|
17
|
+
A plugin has exactly one `type` declared in its manifest. The type
|
|
18
|
+
determines what API surface the plugin gets and where its entry file
|
|
19
|
+
lives.
|
|
20
|
+
|
|
21
|
+
| Type | Entry file | What it adds |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `theme` | `theme.css` | Visual theme (color variables, decorations) |
|
|
24
|
+
| `exporter` | `main.js` | A new "Export As…" format |
|
|
25
|
+
| `parser` | `main.js` | Support for a new file extension/language |
|
|
26
|
+
| `layout` | `main.js` | A new graph layout algorithm |
|
|
27
|
+
| `panel` | `main.js` | A side panel in the right rail |
|
|
28
|
+
| `action` | `main.js` | A right-click item on graph nodes |
|
|
29
|
+
|
|
30
|
+
Pick the smallest type that fits. If you want both a theme and a
|
|
31
|
+
context-menu action, ship them as **two separate plugins**.
|
|
32
|
+
|
|
33
|
+
## The manifest
|
|
34
|
+
|
|
35
|
+
`manifest.json` is the only required file besides the entry. Every
|
|
36
|
+
plugin must have one.
|
|
37
|
+
|
|
38
|
+
### Required fields
|
|
39
|
+
|
|
40
|
+
| Field | Type | Notes |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `id` | string | Unique. Use kebab-case (`my-plugin`, not `My Plugin!`). Must match `[a-z0-9][a-z0-9-_]*`. |
|
|
43
|
+
| `name` | string | Display name shown to users. |
|
|
44
|
+
| `version` | string | Semver (`1.0.0`, `0.3.1`, etc.). |
|
|
45
|
+
| `type` | string | One of the six plugin types above. |
|
|
46
|
+
| `main` | string | Path to the entry file, relative to the plugin folder. |
|
|
47
|
+
|
|
48
|
+
### Recommended fields
|
|
49
|
+
|
|
50
|
+
| Field | Type | Notes |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `author` | string | Your name (no email required). Defaults to `"unknown"`. |
|
|
53
|
+
| `description` | string | One-line summary shown in the plugin list. |
|
|
54
|
+
| `minAppVersion` | string | Earliest filegraph3d version this plugin supports. |
|
|
55
|
+
| `license` | string | SPDX identifier (`MIT`, `Apache-2.0`, etc.). |
|
|
56
|
+
|
|
57
|
+
### Optional fields
|
|
58
|
+
|
|
59
|
+
| Field | Type | Notes |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `homepage` | string | URL for the plugin's documentation or repo. |
|
|
62
|
+
| `permissions` | string[] | What capabilities the plugin needs — see [Permissions](#permissions). |
|
|
63
|
+
|
|
64
|
+
### Path safety
|
|
65
|
+
|
|
66
|
+
`main` must point to a file **inside** the plugin folder. Paths
|
|
67
|
+
containing `..` or absolute paths are rejected at load time. So
|
|
68
|
+
`main: "../../etc/passwd"` won't work — and that's by design.
|
|
69
|
+
|
|
70
|
+
### Example: a full manifest
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"id": "rust-parser",
|
|
75
|
+
"name": "Rust Parser",
|
|
76
|
+
"version": "1.0.0",
|
|
77
|
+
"author": "you",
|
|
78
|
+
"description": "Parse `use` statements from .rs files",
|
|
79
|
+
"type": "parser",
|
|
80
|
+
"main": "main.js",
|
|
81
|
+
"minAppVersion": "0.10.0",
|
|
82
|
+
"license": "MIT",
|
|
83
|
+
"homepage": "https://github.com/you/rust-parser",
|
|
84
|
+
"permissions": ["parse"]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Lifecycle
|
|
89
|
+
|
|
90
|
+
Code plugins have two lifecycle hooks: `activate` and `deactivate`.
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
export default {
|
|
94
|
+
activate(ctx) {
|
|
95
|
+
// Called once when the plugin is loaded.
|
|
96
|
+
// Register everything you need here.
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
deactivate() {
|
|
100
|
+
// Optional. Called when the plugin is unloaded.
|
|
101
|
+
// Clean up timers, intervals, custom DOM elements you created
|
|
102
|
+
// directly, etc.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Right now, "loaded" means **the app started and discovered this plugin
|
|
108
|
+
in the plugin folder**. There's no in-app enable/disable toggle yet
|
|
109
|
+
(planned). So in practice `activate()` runs once at startup and
|
|
110
|
+
`deactivate()` is mostly a forward-compatible hook.
|
|
111
|
+
|
|
112
|
+
### What `activate` should do
|
|
113
|
+
|
|
114
|
+
- Register handlers (`ctx.ui.registerContextMenuItem`, etc.)
|
|
115
|
+
- Subscribe to events (`ctx.events.on`)
|
|
116
|
+
- Set up timers if needed
|
|
117
|
+
- Store an unsubscribe handle so `deactivate` can clean up
|
|
118
|
+
|
|
119
|
+
### What `activate` should NOT do
|
|
120
|
+
|
|
121
|
+
- **Don't** do heavy work synchronously — it blocks app startup.
|
|
122
|
+
- **Don't** read large files or scan directories — defer to user
|
|
123
|
+
interaction.
|
|
124
|
+
- **Don't** throw — the app catches it, but the plugin won't load.
|
|
125
|
+
|
|
126
|
+
### Theme plugins
|
|
127
|
+
|
|
128
|
+
Themes don't have lifecycle hooks. They're just CSS, injected once at
|
|
129
|
+
startup. Override the `body[data-theme="your-id"]` selector and you're
|
|
130
|
+
done.
|
|
131
|
+
|
|
132
|
+
## The `ctx` object
|
|
133
|
+
|
|
134
|
+
Every `activate(ctx)` call receives a context object. It's the only
|
|
135
|
+
way a plugin should talk to the app — don't reach into `window`
|
|
136
|
+
globals or DOM internals, those aren't stable across versions.
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
ctx.manifest // your own manifest (handy for version checks)
|
|
140
|
+
ctx.appVersion // host filegraph3d version
|
|
141
|
+
|
|
142
|
+
ctx.graph // read-only access to nodes/edges/selection
|
|
143
|
+
ctx.ui // register UI: panels, context-menu items, commands
|
|
144
|
+
ctx.exporters // register export formats
|
|
145
|
+
ctx.parsers // register language parsers
|
|
146
|
+
ctx.layouts // register layout algorithms
|
|
147
|
+
ctx.events // subscribe to app events (selection, snapshot, etc.)
|
|
148
|
+
|
|
149
|
+
ctx.storage // persistent per-plugin key-value store
|
|
150
|
+
ctx.toast(msg) // show user a notification
|
|
151
|
+
ctx.log(...args) // log to plugin console (prefixed with plugin id)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For each property's full surface, see the
|
|
155
|
+
[API reference](./api-reference.md).
|
|
156
|
+
|
|
157
|
+
## Permissions
|
|
158
|
+
|
|
159
|
+
Plugins must declare what they need in `manifest.permissions`. Attempting
|
|
160
|
+
to call an API without the matching permission throws an error.
|
|
161
|
+
|
|
162
|
+
| Permission | Required for |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `read-files` | `ctx.graph.readFile(id)` |
|
|
165
|
+
| `read-graph` | Always granted (nodes/edges access). Declaring it is good form. |
|
|
166
|
+
| `modify-graph` | Reserved for future use (adding nodes/edges from plugins). |
|
|
167
|
+
| `ui-panel` | `ctx.ui.registerPanel(...)` |
|
|
168
|
+
| `context-menu` | `ctx.ui.registerContextMenuItem(...)` |
|
|
169
|
+
| `export` | `ctx.exporters.register(...)` |
|
|
170
|
+
| `parse` | `ctx.parsers.register(...)` |
|
|
171
|
+
|
|
172
|
+
### Why bother?
|
|
173
|
+
|
|
174
|
+
Two reasons:
|
|
175
|
+
|
|
176
|
+
1. **Self-documentation.** Anyone reading your manifest knows what the
|
|
177
|
+
plugin can do without reading the code.
|
|
178
|
+
2. **Future-proofing.** filegraph3d may eventually show a permission
|
|
179
|
+
prompt to users before enabling a plugin (Obsidian and VS Code both
|
|
180
|
+
have similar mechanisms). Declaring permissions now means your
|
|
181
|
+
plugin won't break when that lands.
|
|
182
|
+
|
|
183
|
+
### Example
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"id": "my-plugin",
|
|
188
|
+
...
|
|
189
|
+
"permissions": ["read-files", "context-menu"]
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This plugin can read file contents and add right-click items. It
|
|
194
|
+
cannot register an exporter or a panel — if it tries, `ctx.exporters.
|
|
195
|
+
register(...)` throws.
|
|
196
|
+
|
|
197
|
+
## Disposables
|
|
198
|
+
|
|
199
|
+
Several API methods return a `{ dispose() }` handle. Calling `dispose()`
|
|
200
|
+
removes the registration.
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
const panel = ctx.ui.registerPanel({
|
|
204
|
+
id: 'my-panel',
|
|
205
|
+
title: 'My Panel',
|
|
206
|
+
render(container) { container.textContent = 'hello' }
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// Later, if you want to remove the panel:
|
|
210
|
+
panel.dispose()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
If you don't dispose explicitly, the app does it for you when the
|
|
214
|
+
plugin is unloaded. But if your plugin shows/hides UI based on state,
|
|
215
|
+
you'll want to call `dispose()` yourself.
|
|
216
|
+
|
|
217
|
+
## Plugin storage
|
|
218
|
+
|
|
219
|
+
Each plugin gets a private key-value store backed by `localStorage`:
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
ctx.storage.set('lastSelection', { nodeId: 'src/index.js', at: Date.now() })
|
|
223
|
+
|
|
224
|
+
// Next time the plugin loads:
|
|
225
|
+
const last = ctx.storage.get('lastSelection')
|
|
226
|
+
if (last) ctx.log(`Last selection was ${last.nodeId}`)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Keys are namespaced per plugin id — you can't accidentally clash with
|
|
230
|
+
another plugin or with the app itself.
|
|
231
|
+
|
|
232
|
+
Values must be JSON-serializable. Don't put DOM nodes, functions, or
|
|
233
|
+
circular structures in there.
|
|
234
|
+
|
|
235
|
+
## The event bus
|
|
236
|
+
|
|
237
|
+
Plugins can subscribe to app-level events. These fire whenever the
|
|
238
|
+
relevant state changes.
|
|
239
|
+
|
|
240
|
+
| Event | When it fires | Payload |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `snapshot:applied` | New graph data loaded (folder opened or rescanned) | `{ root: string }` |
|
|
243
|
+
| `selection:changed` | User clicked a node or selected something | `string \| null` (node id) |
|
|
244
|
+
| `filter:changed` | Search text or filter state changed | (no payload) |
|
|
245
|
+
| `focus:changed` | Hovered/focused node changed | `string \| null` |
|
|
246
|
+
| `graph:cleared` | Folder was closed | (no payload) |
|
|
247
|
+
| `activeset:changed` | User starred a file or toggled a pipeline | (no payload) |
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
const off = ctx.events.on('selection:changed', (nodeId) => {
|
|
251
|
+
if (nodeId) ctx.log('user picked', nodeId)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Unsubscribe:
|
|
255
|
+
off()
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`ctx.events.on()` returns an unsubscribe function. Save it and call it
|
|
259
|
+
from `deactivate()`, or it'll keep firing after your plugin is gone.
|
|
260
|
+
|
|
261
|
+
## What's next
|
|
262
|
+
|
|
263
|
+
You now know enough to build any plugin type. The next docs are
|
|
264
|
+
type-specific:
|
|
265
|
+
|
|
266
|
+
- [Theme guide](./types/theme.md)
|
|
267
|
+
- [Exporter guide](./types/exporter.md)
|
|
268
|
+
- [API reference](./api-reference.md) — the precise surface
|
|
269
|
+
- [Troubleshooting](./troubleshooting.md) — when things go wrong
|