mirror of
https://github.com/apoo711/obsidian-3d-graph.git
synced 2026-07-22 13:00:24 +00:00
Compare commits
38 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d87e20913 | ||
|
|
fb329eff80 | ||
|
|
3e19c5591d | ||
|
|
9c07f557b9 | ||
|
|
7f0611b5f7 | ||
|
|
3f74040140 | ||
|
|
3345417088 | ||
|
|
3de1374988 | ||
|
|
8203694117 | ||
|
|
0c67d1e5c1 | ||
|
|
aa6db7e820 | ||
|
|
255f613e24 | ||
|
|
f1ca5e4f9b | ||
|
|
68894546c8 | ||
|
|
4891d17b75 | ||
|
|
0b98909b5c | ||
|
|
c81b616d2e | ||
|
|
c119a1c925 | ||
|
|
8c5f9520c5 | ||
|
|
a2ccb7965c | ||
|
|
b92cc8e659 | ||
|
|
e16bfcc28a | ||
|
|
67ee1d47c8 | ||
|
|
f893427cf8 | ||
|
|
0947a20efe | ||
|
|
dffbe72292 | ||
|
|
bbe86ebdae | ||
|
|
58c29d88b8 | ||
|
|
3dc51de016 | ||
|
|
437b5802aa | ||
|
|
af51690db6 | ||
|
|
59dd2819a8 | ||
|
|
c8753e4f1e | ||
|
|
2c6788cdd1 | ||
|
|
54a3cf0178 | ||
|
|
fdbd5fe589 | ||
|
|
e78ff572f5 | ||
|
|
6749160446 |
14 changed files with 1219 additions and 311 deletions
35
README.md
35
README.md
|
|
@ -1,10 +1,13 @@
|
|||
# 3D Graph for Obsidian
|
||||

|
||||
|
||||
A plugin for Obsidian that provides a highly customizable 3D, force-directed graph view of your vault. This offers an alternative, immersive way to visualize and explore the connections between your notes.
|
||||
|
||||
*Check out my blog post [here](https://aryan-gupta.is-a.dev/blog/2025/3d-graph-plugin/)*
|
||||
---
|
||||
|
||||

|
||||
💡 *Check out my blog post [here](https://aryan-gupta.is-a.dev/blog/2025-06-24-3d-graph-plugin/)*
|
||||
|
||||
---
|
||||
|
||||
## Why Choose This 3D Graph?
|
||||
While other 3D graph plugins exist, this one is built to offer the most **interactive and deeply customizable** experience for exploring your vault.
|
||||
|
|
@ -46,23 +49,35 @@ While other 3D graph plugins exist, this one is built to offer the most **intera
|
|||
* All settings update the graph instantly without requiring a reload.
|
||||
* Intelligently caches node positions for a smooth experience when updating data.
|
||||
|
||||

|
||||
|
||||
*To watch the video in higher resolution click [here](https://github.com/Apoo711/obsidian-3d-graph/issues/6)*
|
||||
|
||||
## How to Install
|
||||
|
||||
1. Download `main.js`, `manifest.json`, and `styles.css` from the latest [release](https://github.com/Apoo711/obsidian-3d-graph/releases).
|
||||
### Recommended Method (from Community Plugins)
|
||||
1. Open **Settings > Community plugins**.
|
||||
|
||||
2. In Obsidian, open `Settings` > `Community plugins`.
|
||||
2. Make sure "Restricted mode" is turned **off**.
|
||||
|
||||
3. Make sure "Restricted mode" is turned off.
|
||||
3. Click **Browse** and search for "New 3D Graph".
|
||||
|
||||
4. Open your vault's plugin folder by clicking the small folder icon next to "Installed plugins".
|
||||
4. Click **Install**.
|
||||
|
||||
5. Create a new folder named `new-3d-graph`.
|
||||
5. Once installed, close the community plugins window and **Enable** the plugin.
|
||||
|
||||
6. Copy the downloaded `main.js`, `manifest.json`, and `styles.css` files into this new folder.
|
||||
### Beta Installation (using BRAT)
|
||||
For those who want the latest beta features:
|
||||
|
||||
7. Go back to Obsidian's "Community plugins" settings and click the refresh button.
|
||||
1. Install the **BRAT** plugin from the Community Plugins browser.
|
||||
|
||||
8. Enable "New 3D Graph".
|
||||
2. Open the BRAT settings (`Settings` > `BRAT`).
|
||||
|
||||
3. In the "Beta Plugin List" section, click **Add Beta plugin**.
|
||||
|
||||
4. Use the repository path: `Apoo711/obsidian-3d-graph`
|
||||
|
||||
5. Enable the "3D Graph" plugin in the Community Plugins tab.
|
||||
|
||||
Once enabled, you can open the 3D Graph from the ribbon icon on the left sidebar or by using the Command Palette (`Ctrl/Cmd + P` and typing "Open 3D Graph").
|
||||
|
||||
|
|
|
|||
BIN
assets/output.gif
Normal file
BIN
assets/output.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 MiB |
BIN
assets/preview.mp4
Normal file
BIN
assets/preview.mp4
Normal file
Binary file not shown.
|
|
@ -37,6 +37,7 @@ const context = await esbuild.context({
|
|||
logLevel: "info",
|
||||
sourcemap: prod ? false : "inline",
|
||||
treeShaking: true,
|
||||
mainFields: ["module", "main"],
|
||||
outfile: "main.js",
|
||||
minify: prod,
|
||||
});
|
||||
|
|
|
|||
7
main.ts
7
main.ts
|
|
@ -22,7 +22,7 @@ export default class Graph3DPlugin extends Plugin {
|
|||
});
|
||||
|
||||
// Debounced update for live changes in the vault
|
||||
const debouncedUpdate = debounce(() => this.triggerLiveUpdate(), 300, true);
|
||||
const debouncedUpdate = debounce(() => this.triggerLiveUpdate(), 300, false);
|
||||
this.registerEvent(this.app.vault.on('create', debouncedUpdate));
|
||||
this.registerEvent(this.app.vault.on('delete', debouncedUpdate));
|
||||
this.registerEvent(this.app.vault.on('modify', debouncedUpdate));
|
||||
|
|
@ -45,6 +45,11 @@ export default class Graph3DPlugin extends Plugin {
|
|||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
this.app.workspace.getLeavesOfType(VIEW_TYPE_3D_GRAPH).forEach(leaf => {
|
||||
if (leaf.view instanceof Graph3DView) {
|
||||
leaf.view.setGroupsDirty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async activateView() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "new-3d-graph",
|
||||
"name": "New 3D Graph",
|
||||
"version": "2.2.2",
|
||||
"version": "2.5.2",
|
||||
"minAppVersion": "1.5.0",
|
||||
"description": "Visualize your Vault in 3D with a powerful, highly customizable, and filterable graph.",
|
||||
"description": "Visualize your vault in 3D with a powerful, highly customizable, and filterable graph.",
|
||||
"author": "Aryan Gupta",
|
||||
"authorUrl": "https://aryan-gupta.is-a.dev",
|
||||
"isDesktopOnly": true
|
||||
|
|
|
|||
182
package-lock.json
generated
182
package-lock.json
generated
|
|
@ -1,42 +1,44 @@
|
|||
{
|
||||
"name": "3D Graph Plugin",
|
||||
"version": "2.2.2",
|
||||
"version": "2.5.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "3D Graph Plugin",
|
||||
"version": "2.2.2",
|
||||
"version": "2.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"3d-force-graph": "^1.77.0",
|
||||
"three": "^0.177.0"
|
||||
"3d-force-graph": "^1.80.0",
|
||||
"three": "^0.184.0",
|
||||
"three-forcegraph": "^1.43.4",
|
||||
"three-spritetext": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/three": "^0.177.0",
|
||||
"@types/three": "^0.184.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"obsidian": "latest",
|
||||
"obsidian": "^1.13.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
|
||||
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
|
||||
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
|
||||
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
|
|
@ -45,9 +47,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.37.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz",
|
||||
"integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==",
|
||||
"version": "6.38.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",
|
||||
"integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
|
|
@ -648,19 +650,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.177.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.177.0.tgz",
|
||||
"integrity": "sha512-/ZAkn4OLUijKQySNci47lFO+4JLE1TihEjsGWPUT+4jWqxtwOPPEwJV1C3k5MEx0mcBPCdkFjzRzDOnHEI1R+A==",
|
||||
"version": "0.184.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz",
|
||||
"integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "~0.12.0",
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
"@types/stats.js": "*",
|
||||
"@types/webxr": "*",
|
||||
"@webgpu/types": "*",
|
||||
"@types/webxr": ">=0.5.17",
|
||||
"fflate": "~0.8.2",
|
||||
"meshoptimizer": "~0.18.1"
|
||||
"meshoptimizer": "~1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/three/node_modules/@tweenjs/tween.js": {
|
||||
|
|
@ -877,24 +878,17 @@
|
|||
"license": "ISC",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.61",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.61.tgz",
|
||||
"integrity": "sha512-w2HbBvH+qO19SB5pJOJFKs533CdZqxl3fcGonqL321VHkW7W/iBo6H8bjDy6pr/+pbMwIu5dnuaAxH7NxBqUrQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/3d-force-graph": {
|
||||
"version": "1.77.0",
|
||||
"resolved": "https://registry.npmjs.org/3d-force-graph/-/3d-force-graph-1.77.0.tgz",
|
||||
"integrity": "sha512-w2MlrCeMJxXwhz5gtRZ7mLU4xW5DD2U6VSEfFv8pvnvSNPYPuAIKjbJoZekfv7yFmMaWnNy/2RfRcgC5oGr2KQ==",
|
||||
"version": "1.80.0",
|
||||
"resolved": "https://registry.npmjs.org/3d-force-graph/-/3d-force-graph-1.80.0.tgz",
|
||||
"integrity": "sha512-tzI353gW1nXPpnC7VTa3JjMg+3cp77qOLUFO0vucPTfF+q5R6sQsNsIqVTbRIb7RSypn14nBa4yfkOe9ThxASw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accessor-fn": "1",
|
||||
"kapsule": "^1.16",
|
||||
"three": ">=0.118 <1",
|
||||
"three": ">=0.179 <1",
|
||||
"three-forcegraph": "1",
|
||||
"three-render-objects": "^1.35"
|
||||
"three-render-objects": "^1.41"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
|
@ -935,9 +929,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
|
||||
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
|
|
@ -1007,9 +1001,9 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
|
|
@ -1767,9 +1761,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true
|
||||
|
|
@ -2019,10 +2013,20 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
|
|
@ -2112,9 +2116,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
|
||||
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
|
|
@ -2136,9 +2140,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/meshoptimizer": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
|
||||
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
||||
"integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -2157,9 +2161,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
|
|
@ -2234,9 +2238,9 @@
|
|||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/obsidian": {
|
||||
"version": "1.8.7",
|
||||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz",
|
||||
"integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==",
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.13.1.tgz",
|
||||
"integrity": "sha512-qtTEA2pmhJzhuhJqzbBFRYhpIOqvW+krDYjtFynv66KbxBbumHBlsJfWw3I4jtnK/6fZwbQhCrmmDdRwXmX56w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -2244,8 +2248,8 @@
|
|||
"moment": "2.29.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
"@codemirror/state": "6.5.0",
|
||||
"@codemirror/view": "6.38.6"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
|
|
@ -2370,9 +2374,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2395,9 +2399,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.26.8",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz",
|
||||
"integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==",
|
||||
"version": "10.29.2",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz",
|
||||
"integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
|
@ -2601,9 +2605,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
|
||||
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
|
|
@ -2631,15 +2635,15 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.177.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz",
|
||||
"integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==",
|
||||
"version": "0.184.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz",
|
||||
"integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/three-forcegraph": {
|
||||
"version": "1.42.13",
|
||||
"resolved": "https://registry.npmjs.org/three-forcegraph/-/three-forcegraph-1.42.13.tgz",
|
||||
"integrity": "sha512-BoG5fB3nlAFeIyiLuFquvWIjt8DA2gdPWlqW/8V8xQcEO7otMmeN2/WWHCP7cWzKEImULxpJ6bNLmmt7TTJaiw==",
|
||||
"version": "1.43.4",
|
||||
"resolved": "https://registry.npmjs.org/three-forcegraph/-/three-forcegraph-1.43.4.tgz",
|
||||
"integrity": "sha512-FtmiZP/T16ZQaHza3JDaDn0YTXFtg9e7pGnTeU8nzu0NNkx7MpWbF/GvmpbQsWHx3rukHtkRv1fTorLPB3FDEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accessor-fn": "1",
|
||||
|
|
@ -2661,9 +2665,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/three-render-objects": {
|
||||
"version": "1.40.2",
|
||||
"resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.40.2.tgz",
|
||||
"integrity": "sha512-4LAW9HJS1XcFN4+ujAWrcGAa3UalVTrtXzeWIR9hgJnYSCDBFgGzok9cDP9sXMlw5SjtDWkH6VOnGont+RzfSw==",
|
||||
"version": "1.42.0",
|
||||
"resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.42.0.tgz",
|
||||
"integrity": "sha512-KYfkPrYGEbIK8ChFocWqOF1aAN80FBUBWVYB8mB2oBpVuVN+52FvvngVYB5ieFANQu7Rt21rPYZ/xKaAgVWWRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tweenjs/tween.js": "18 - 25",
|
||||
|
|
@ -2676,7 +2680,19 @@
|
|||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.168"
|
||||
"three": ">=0.179"
|
||||
}
|
||||
},
|
||||
"node_modules/three-spritetext": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/three-spritetext/-/three-spritetext-1.10.0.tgz",
|
||||
"integrity": "sha512-t08iP1FCU1lQh8T5MmCpdijKgas8GDHJE0LqMGBuVu3xqMMpFnEZhTlih7FlxLPQizHIGoumUSpfOlY1GO/Tgg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.86.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
|
|
@ -2757,9 +2773,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
|
@ -2767,7 +2783,7 @@
|
|||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
|
|
|
|||
16
package.json
16
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "3D Graph Plugin",
|
||||
"version": "2.2.2",
|
||||
"description": "Visualize your Vault in 3D with a powerful, highly customizable, and filterable graph.",
|
||||
"version": "2.5.2",
|
||||
"description": "Visualize your vault in 3D with a powerful, highly customizable, and filterable graph.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
|
|
@ -13,17 +13,19 @@
|
|||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/three": "^0.177.0",
|
||||
"@types/three": "^0.184.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"obsidian": "latest",
|
||||
"obsidian": "^1.13.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"3d-force-graph": "^1.77.0",
|
||||
"three": "^0.177.0"
|
||||
"3d-force-graph": "^1.80.0",
|
||||
"three": "^0.184.0",
|
||||
"three-forcegraph": "^1.43.4",
|
||||
"three-spritetext": "^1.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
132
src/settings.ts
132
src/settings.ts
|
|
@ -16,7 +16,6 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
if (options.redrawData) {
|
||||
// Pass cache and reheat options through
|
||||
leaf.view.updateData({ useCache: options.useCache, reheat: options.reheat });
|
||||
} else if (options.updateDisplay) {
|
||||
leaf.view.updateDisplay();
|
||||
|
|
@ -34,7 +33,8 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl).setName('Search').setHeading();
|
||||
new Setting(containerEl).setName('Filters').setHeading();
|
||||
|
||||
new Setting(containerEl).setName('Search term').setDesc('Only show notes containing this text.')
|
||||
.addText(text => text.setPlaceholder('Enter search term...')
|
||||
.setValue(this.plugin.settings.searchQuery)
|
||||
|
|
@ -44,18 +44,66 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}, 500, true)));
|
||||
|
||||
new Setting(containerEl).setName('Show neighboring nodes')
|
||||
.setDesc('Also show the nodes linked to the search results.')
|
||||
new Setting(containerEl).setName('Show neighboring nodes').setDesc('Show nodes that are linked to the search/filter results.')
|
||||
.addToggle(toggle => toggle.setValue(this.plugin.settings.showNeighboringNodes)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showNeighboringNodes = value;
|
||||
await this.plugin.saveSettings();
|
||||
if (this.plugin.settings.searchQuery) {
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}));
|
||||
|
||||
new Setting(containerEl).setName('Filters').setHeading();
|
||||
containerEl.createEl('p', { text: 'Use the filters below to limit the number of nodes in the graph. Filters are applied in order.', cls: 'setting-item-description' });
|
||||
|
||||
this.plugin.settings.filters.forEach((filter, index) => {
|
||||
const setting = new Setting(containerEl)
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('path', 'Path')
|
||||
.addOption('tag', 'Tag')
|
||||
.setValue(filter.type)
|
||||
.onChange(async (value: string) => {
|
||||
filter.type = value as 'path' | 'tag';
|
||||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}))
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter filter value...')
|
||||
.setValue(filter.value)
|
||||
.onChange(debounce(async (value) => {
|
||||
filter.value = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}, 500, true)))
|
||||
.addToggle(toggle => {
|
||||
toggle.setTooltip("Enable/Disable filter")
|
||||
.setValue(filter.enabled)
|
||||
.onChange(async (value) => {
|
||||
filter.enabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
});
|
||||
})
|
||||
.addExtraButton(button => button
|
||||
.setIcon('cross')
|
||||
.setTooltip('Remove filter')
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.filters.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}));
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.addButton(button => button
|
||||
.setButtonText('Add new filter')
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.filters.push({ type: 'path', value: '', enabled: true });
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
|
||||
|
||||
new Setting(containerEl).setName('General Filters').setHeading();
|
||||
new Setting(containerEl).setName('Show tags').addToggle(toggle => toggle.setValue(this.plugin.settings.showTags)
|
||||
.onChange(async (value) => { this.plugin.settings.showTags = value; await this.plugin.saveSettings(); this.triggerUpdate({ redrawData: true, useCache: true }); }));
|
||||
new Setting(containerEl).setName('Show attachments').addToggle(toggle => toggle.setValue(this.plugin.settings.showAttachments)
|
||||
|
|
@ -63,7 +111,7 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl).setName('Hide orphans').addToggle(toggle => toggle.setValue(this.plugin.settings.hideOrphans)
|
||||
.onChange(async (value) => { this.plugin.settings.hideOrphans = value; await this.plugin.saveSettings(); this.triggerUpdate({ redrawData: true, useCache: true }); }));
|
||||
|
||||
new Setting(containerEl).setName('Groups').setHeading();
|
||||
new Setting(containerEl).setName('Color Groups').setHeading();
|
||||
containerEl.createEl('p', { text: 'Color nodes with custom rules. Use "path:", "tag:", "file:", or text match. Examples: path:folder, tag:#project, file:MyNote.md, file:*.pdf', cls: 'setting-item-description' });
|
||||
|
||||
this.plugin.settings.groups.forEach((group, index) => {
|
||||
|
|
@ -118,12 +166,25 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
new Setting(containerEl).setName('Appearance').setHeading();
|
||||
new Setting(containerEl).setName('Performance Mode').setDesc('Lowers geometry detail, disables link curvature and labels for better performance.')
|
||||
.addToggle(toggle => toggle.setValue(this.plugin.settings.performanceMode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.performanceMode = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_3D_GRAPH).forEach(leaf => {
|
||||
if (leaf.view instanceof Graph3DView) {
|
||||
leaf.view.clearResourceCaches();
|
||||
}
|
||||
});
|
||||
this.triggerUpdate({ updateDisplay: true });
|
||||
}));
|
||||
|
||||
new Setting(containerEl).setName('Node shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.plugin.settings.nodeShape)
|
||||
.onChange(async(value: NodeShape) => {this.plugin.settings.nodeShape = value; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
.onChange(async(value: string) => {this.plugin.settings.nodeShape = value as NodeShape; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
new Setting(containerEl).setName('Tag shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.plugin.settings.tagShape)
|
||||
.onChange(async(value: NodeShape) => {this.plugin.settings.tagShape = value; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
.onChange(async(value: string) => {this.plugin.settings.tagShape = value as NodeShape; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
new Setting(containerEl).setName('Attachment shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.plugin.settings.attachmentShape)
|
||||
.onChange(async(value: NodeShape) => {this.plugin.settings.attachmentShape = value; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
.onChange(async(value: string) => {this.plugin.settings.attachmentShape = value as NodeShape; await this.plugin.saveSettings(); this.triggerUpdate({updateDisplay: true})}));
|
||||
new Setting(containerEl).setName('Node size').addSlider(s => s.setLimits(0.1, 5, 0.1).setValue(this.plugin.settings.nodeSize).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.nodeSize = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Tag node size').addSlider(s => s.setLimits(0.1, 5, 0.1).setValue(this.plugin.settings.tagNodeSize).setDynamicTooltip()
|
||||
|
|
@ -133,10 +194,36 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl).setName('Link thickness').addSlider(s => s.setLimits(0.1, 5, 0.1).setValue(this.plugin.settings.linkThickness).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.linkThickness = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
|
||||
new Setting(containerEl).setName('Labels').setHeading();
|
||||
new Setting(containerEl).setName('Show node labels').addToggle(toggle => toggle.setValue(this.plugin.settings.showNodeLabels)
|
||||
.onChange(async (value) => { this.plugin.settings.showNodeLabels = value; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Show labels on hover/highlight only').addToggle(toggle => toggle.setValue(this.plugin.settings.showLabelsOnHoverOnly)
|
||||
.onChange(async (value) => { this.plugin.settings.showLabelsOnHoverOnly = value; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Label distance').addSlider(s => s.setLimits(50, 500, 10).setValue(this.plugin.settings.labelDistance).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.labelDistance = v; await this.plugin.saveSettings(); }));
|
||||
new Setting(containerEl).setName('Label fade threshold').addSlider(s => s.setLimits(0.1, 1, 0.1).setValue(this.plugin.settings.labelFadeThreshold).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.labelFadeThreshold = v; await this.plugin.saveSettings(); }));
|
||||
new Setting(containerEl).setName('Label text size').addSlider(s => s.setLimits(1, 10, 0.5).setValue(this.plugin.settings.labelTextSize).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.labelTextSize = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
|
||||
new Setting(containerEl).setName('Label Text Color (Dark Theme)').addColorPicker(c => c.setValue(this.plugin.settings.labelTextColorDark).onChange(async (v) => { this.plugin.settings.labelTextColorDark = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Label Text Color (Light Theme)').addColorPicker(c => c.setValue(this.plugin.settings.labelTextColorLight).onChange(async (v) => { this.plugin.settings.labelTextColorLight = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Label Background Color').addColorPicker(c => c.setValue(this.plugin.settings.labelBackgroundColor).onChange(async (v) => { this.plugin.settings.labelBackgroundColor = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
new Setting(containerEl).setName('Label Background Opacity').addSlider(s => s.setLimits(0, 1, 0.1).setValue(this.plugin.settings.labelBackgroundOpacity).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.labelBackgroundOpacity = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateDisplay: true }); }));
|
||||
|
||||
new Setting(containerEl).setName('Prevent label occlusion').addToggle(toggle => toggle.setValue(this.plugin.settings.labelOcclusion)
|
||||
.onChange(async (value) => { this.plugin.settings.labelOcclusion = value; await this.plugin.saveSettings(); }));
|
||||
|
||||
|
||||
new Setting(containerEl).setName('Interaction').setHeading();
|
||||
new Setting(containerEl).setName("Zoom on click").setDesc("Automatically zoom in on a node when it's clicked.")
|
||||
.addToggle(toggle => toggle.setValue(this.plugin.settings.zoomOnClick)
|
||||
.onChange(async (value) => {this.plugin.settings.zoomOnClick = value; await this.plugin.saveSettings();}));
|
||||
new Setting(containerEl).setName("Use Keyboard Controls (WASD)").setDesc("Enable game-like controls for camera movement.")
|
||||
.addToggle(toggle => toggle.setValue(this.plugin.settings.useKeyboardControls)
|
||||
.onChange(async (value) => { this.plugin.settings.useKeyboardControls = value; await this.plugin.saveSettings(); this.triggerUpdate({ updateControls: true }); }));
|
||||
new Setting(containerEl).setName('Keyboard move speed').addSlider(s => s.setLimits(0.1, 10, 0.1).setValue(this.plugin.settings.keyboardMoveSpeed).setDynamicTooltip()
|
||||
.onChange(async (v) => { this.plugin.settings.keyboardMoveSpeed = v; await this.plugin.saveSettings(); }));
|
||||
new Setting(containerEl).setName("Zoom on click").addToggle(toggle => toggle.setValue(this.plugin.settings.zoomOnClick)
|
||||
.onChange(async (value) => {this.plugin.settings.zoomOnClick = value; await this.plugin.saveSettings();}));
|
||||
new Setting(containerEl).setName('Rotation speed').addSlider(s => s.setLimits(0.1, 5, 0.1).setValue(this.plugin.settings.rotateSpeed).setDynamicTooltip()
|
||||
.onChange(async (v) => {this.plugin.settings.rotateSpeed = v; await this.plugin.saveSettings(); this.triggerUpdate({ updateControls: true });}));
|
||||
new Setting(containerEl).setName('Pan speed').addSlider(s => s.setLimits(0.1, 5, 0.1).setValue(this.plugin.settings.panSpeed).setDynamicTooltip()
|
||||
|
|
@ -151,14 +238,11 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
this.triggerUpdate({ redrawData: true, useCache: false, reheat: true });
|
||||
};
|
||||
|
||||
new Setting(containerEl).setName('Center force').setDesc('How strongly nodes are pulled toward the center.')
|
||||
.addSlider(s => s.setLimits(0, 1, 0.01).setValue(this.plugin.settings.centerForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'centerForce'); }));
|
||||
new Setting(containerEl).setName('Repel force').setDesc('How strongly nodes push each other apart.')
|
||||
.addSlider(s => s.setLimits(0, 20, 0.1).setValue(this.plugin.settings.repelForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'repelForce'); }));
|
||||
new Setting(containerEl).setName('Link force').setDesc('How strongly links pull nodes together.')
|
||||
.addSlider(s => s.setLimits(0, 0.1, 0.001).setValue(this.plugin.settings.linkForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'linkForce'); }));
|
||||
new Setting(containerEl).setName('Center force').addSlider(s => s.setLimits(0, 1, 0.01).setValue(this.plugin.settings.centerForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'centerForce'); }));
|
||||
new Setting(containerEl).setName('Repel force').addSlider(s => s.setLimits(0, 20, 0.1).setValue(this.plugin.settings.repelForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'repelForce'); }));
|
||||
new Setting(containerEl).setName('Link force').addSlider(s => s.setLimits(0, 0.1, 0.001).setValue(this.plugin.settings.linkForce).setDynamicTooltip()
|
||||
.onChange(async (v) => { await forceSettingHandler(v, 'linkForce'); }));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
81
src/types.ts
81
src/types.ts
|
|
@ -1,18 +1,30 @@
|
|||
import * as THREE from 'three';
|
||||
|
||||
export interface GraphGroup {
|
||||
export interface ColorGroup {
|
||||
query: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface Filter {
|
||||
type: 'path' | 'tag';
|
||||
value: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export enum NodeShape { Sphere = 'Sphere', Cube = 'Cube', Pyramid = 'Pyramid', Tetrahedron = 'Tetrahedron' }
|
||||
|
||||
export interface Graph3DPluginSettings {
|
||||
// Search
|
||||
searchQuery: string;
|
||||
showNeighboringNodes: boolean;
|
||||
// Filters
|
||||
filters: Filter[];
|
||||
showAttachments: boolean;
|
||||
hideOrphans: boolean;
|
||||
showTags: boolean;
|
||||
searchQuery: string;
|
||||
groups: GraphGroup[];
|
||||
// Groups
|
||||
groups: ColorGroup[];
|
||||
// Display
|
||||
useThemeColors: boolean;
|
||||
colorNode: string;
|
||||
colorTag: string;
|
||||
|
|
@ -20,29 +32,52 @@ export interface Graph3DPluginSettings {
|
|||
colorLink: string;
|
||||
colorHighlight: string;
|
||||
backgroundColor: string;
|
||||
// Appearance
|
||||
nodeSize: number;
|
||||
tagNodeSize: number;
|
||||
attachmentNodeSize: number;
|
||||
linkThickness: number;
|
||||
centerForce: number;
|
||||
repelForce: number;
|
||||
linkForce: number;
|
||||
nodeShape: NodeShape;
|
||||
tagShape: NodeShape;
|
||||
attachmentShape: NodeShape;
|
||||
// Labels
|
||||
showNodeLabels: boolean;
|
||||
showLabelsOnHoverOnly: boolean;
|
||||
labelDistance: number;
|
||||
labelFadeThreshold: number;
|
||||
labelTextSize: number;
|
||||
labelTextColorLight: string;
|
||||
labelTextColorDark: string;
|
||||
labelBackgroundColor: string;
|
||||
labelBackgroundOpacity: number;
|
||||
labelOcclusion: boolean;
|
||||
// Interaction
|
||||
useKeyboardControls: boolean;
|
||||
keyboardMoveSpeed: number;
|
||||
zoomOnClick: boolean;
|
||||
showNeighboringNodes: boolean;
|
||||
rotateSpeed: number;
|
||||
panSpeed: number;
|
||||
zoomSpeed: number;
|
||||
performanceMode: boolean;
|
||||
// Forces
|
||||
centerForce: number;
|
||||
repelForce: number;
|
||||
linkForce: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Graph3DPluginSettings = {
|
||||
// Search
|
||||
searchQuery: '',
|
||||
showNeighboringNodes: true,
|
||||
performanceMode: false,
|
||||
// Filters
|
||||
filters: [],
|
||||
showAttachments: false,
|
||||
hideOrphans: false,
|
||||
showTags: false,
|
||||
searchQuery: '',
|
||||
// Groups
|
||||
groups: [],
|
||||
// Display
|
||||
useThemeColors: true,
|
||||
colorNode: '#2080F0',
|
||||
colorTag: '#9A49E8',
|
||||
|
|
@ -50,21 +85,36 @@ export const DEFAULT_SETTINGS: Graph3DPluginSettings = {
|
|||
colorLink: '#666666',
|
||||
colorHighlight: '#FFB800',
|
||||
backgroundColor: '#0E0E10',
|
||||
// Appearance
|
||||
nodeSize: 1.5,
|
||||
tagNodeSize: 1.0,
|
||||
attachmentNodeSize: 1.2,
|
||||
linkThickness: 1,
|
||||
centerForce: 0.1,
|
||||
repelForce: 10,
|
||||
linkForce: 0.01,
|
||||
nodeShape: NodeShape.Sphere,
|
||||
tagShape: NodeShape.Tetrahedron,
|
||||
attachmentShape: NodeShape.Cube,
|
||||
// Labels
|
||||
showNodeLabels: true,
|
||||
showLabelsOnHoverOnly: false,
|
||||
labelDistance: 150,
|
||||
labelFadeThreshold: 0.8,
|
||||
labelTextSize: 2.5,
|
||||
labelTextColorLight: '#000000',
|
||||
labelTextColorDark: '#ffffff',
|
||||
labelBackgroundColor: '#ffffff',
|
||||
labelBackgroundOpacity: 0.3,
|
||||
labelOcclusion: false,
|
||||
// Interaction
|
||||
useKeyboardControls: true,
|
||||
keyboardMoveSpeed: 2.0,
|
||||
zoomOnClick: true,
|
||||
showNeighboringNodes: true,
|
||||
rotateSpeed: 1.0,
|
||||
panSpeed: 1.0,
|
||||
zoomSpeed: 1.0,
|
||||
// Forces
|
||||
centerForce: 0.1,
|
||||
repelForce: 10,
|
||||
linkForce: 0.01,
|
||||
};
|
||||
|
||||
export enum NodeType { File, Tag, Attachment }
|
||||
|
|
@ -76,10 +126,15 @@ export interface GraphNode {
|
|||
type: NodeType;
|
||||
tags?: string[];
|
||||
content?: string;
|
||||
__threeObj?: THREE.Mesh;
|
||||
lowerCaseContent?: string;
|
||||
__threeObj?: THREE.Object3D;
|
||||
x?: number;
|
||||
y?: number;
|
||||
z?: number;
|
||||
// D3 Force properties for fixing node positions
|
||||
fx?: number;
|
||||
fy?: number;
|
||||
fz?: number;
|
||||
}
|
||||
|
||||
export interface GraphLink {
|
||||
|
|
|
|||
1030
src/view.ts
1030
src/view.ts
File diff suppressed because it is too large
Load diff
29
styles.css
29
styles.css
|
|
@ -35,9 +35,12 @@
|
|||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.graph-3d-settings-toggle {
|
||||
.graph-3d-settings-toggle,
|
||||
.graph-3d-reset-toggle {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
background-color: var(--background-secondary-alt);
|
||||
|
|
@ -48,7 +51,8 @@
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.graph-3d-settings-toggle:hover {
|
||||
.graph-3d-settings-toggle:hover,
|
||||
.graph-3d-reset-toggle:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
|
|
@ -73,9 +77,28 @@
|
|||
|
||||
.graph-3d-settings-panel .setting-item {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
padding: 10px 0;
|
||||
padding: 10px 16px !important;
|
||||
}
|
||||
|
||||
.graph-3d-settings-panel .setting-item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.graph-3d-counter {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
background-color: var(--background-secondary-alt);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
font-size: var(--font-xs);
|
||||
color: var(--text-muted);
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.graph-3d-counter.is-visible {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
|
|
|
|||
|
|
@ -8,5 +8,12 @@
|
|||
"2.1.0": "1.5.0",
|
||||
"2.0.1": "1.5.0",
|
||||
"2.0.0": "1.5.0",
|
||||
"1.0.0": "1.5.0"
|
||||
"1.0.0": "1.5.0",
|
||||
"2.2.3": "1.5.0",
|
||||
"2.3.0": "1.5.0",
|
||||
"2.4.0": "1.5.0",
|
||||
"2.4.1": "1.5.0",
|
||||
"2.5.0": "1.5.0",
|
||||
"2.5.1": "1.5.0",
|
||||
"2.5.2": "1.5.0"
|
||||
}
|
||||
Loading…
Reference in a new issue