Compare commits

...

38 commits
2.2.2 ... main

Author SHA1 Message Date
Aryan Gupta
9d87e20913 2.5.2 2026-06-29 22:16:20 +10:00
Aryan Gupta
fb329eff80 perf: Add performance toggle and verify everything works. 2026-06-29 22:16:08 +10:00
Aryan Gupta
3e19c5591d perf: A lot of other performance changes + QOL changes 2026-06-29 21:55:05 +10:00
Aryan Gupta
9c07f557b9 perf: Lots of performance optimizations 2026-06-29 21:45:22 +10:00
Aryan Gupta
7f0611b5f7
Update blog post link in README 2026-06-29 21:23:00 +10:00
Aryan Gupta
3f74040140 2.5.1 2026-06-29 21:18:06 +10:00
Aryan Gupta
3345417088 feat: implement 3D graph view component with settings and force-directed layout rendering 2026-06-29 21:12:47 +10:00
Aryan Gupta
3de1374988 feat: implement initial 3D graph view architecture with core data types and rendering logic 2026-06-29 21:10:38 +10:00
Aryan Gupta
8203694117 feat: initialize 3d-graph-view component with interactive settings panels and force-directed graph controls 2026-06-29 21:08:15 +10:00
Aryan Gupta
0c67d1e5c1 2.5.0 2026-06-16 16:18:12 +10:00
Aryan Gupta
aa6db7e820 feat: advanced filters, node dragging, performance optimizations, and settings spacing 2026-06-16 16:18:04 +10:00
Aryan Gupta
255f613e24
Update blog post link in README.md 2026-06-14 10:58:17 +10:00
Aryan Gupta
f1ca5e4f9b 2.4.1 2025-08-01 23:42:49 +10:00
Aryan Gupta
68894546c8 Quick fix for lag? 2025-08-01 23:41:26 +10:00
Aryan Gupta
4891d17b75 Fix build issues 2025-08-01 18:56:43 +10:00
Aryan Gupta
0b98909b5c 2.4.0 2025-08-01 18:44:32 +10:00
Aryan Gupta
c81b616d2e Phase 3 complete 2025-08-01 18:41:39 +10:00
Aryan Gupta
c119a1c925 Will this fix it? 2025-08-01 18:23:18 +10:00
Aryan Gupta
8c5f9520c5 Phase 3 is nearly complete 2025-08-01 18:19:02 +10:00
Aryan Gupta
a2ccb7965c Phase 1&2 tested and completed 2025-08-01 18:03:53 +10:00
Aryan Gupta
b92cc8e659 2.3.0 2025-07-31 20:57:15 +10:00
Apoo711
e16bfcc28a
Merge pull request #9 from Apoo711/feature/fading-node-labels
Feature: Fading Node Titles
2025-07-31 20:55:22 +10:00
Aryan Gupta
67ee1d47c8 oopsie 2025-07-31 20:50:23 +10:00
Aryan Gupta
f893427cf8 Set #2 of suggestions 2025-07-31 20:49:41 +10:00
Aryan Gupta
0947a20efe Address Sourcery Suggestions 2025-07-31 20:42:31 +10:00
Aryan Gupta
dffbe72292 Phase 3 is now complete 2025-07-31 20:29:54 +10:00
Aryan Gupta
bbe86ebdae Phase 1&2 Complete, the implementation is now functional, raycasting will be added in phase 3 2025-07-31 20:16:53 +10:00
Aryan Gupta
58c29d88b8 Update installation Instructions 2025-07-23 16:59:59 +10:00
Aryan Gupta
3dc51de016 Reformat the README.md a little bit 2025-07-22 18:20:18 +10:00
Aryan Gupta
437b5802aa So the readme didnt update... 2025-07-22 18:16:12 +10:00
Aryan Gupta
af51690db6 Final attempt #2 2025-07-22 18:14:00 +10:00
Aryan Gupta
59dd2819a8 Final attempt, if it doesnt work then it doesnt work. 2025-07-22 18:07:42 +10:00
Aryan Gupta
c8753e4f1e Big URL maybe this will work? 2025-07-22 18:05:47 +10:00
Aryan Gupta
2c6788cdd1 maybe this will fix it? 2025-07-22 18:03:29 +10:00
Aryan Gupta
54a3cf0178 maybe this will fix it? 2025-07-22 17:57:46 +10:00
Aryan Gupta
fdbd5fe589 Encode video file for a 2mb size reduction 2025-07-22 17:54:37 +10:00
Aryan Gupta
e78ff572f5 Add basic preview video 2025-07-21 15:35:35 +10:00
Aryan Gupta
6749160446 Fix sentence casing 2025-07-20 13:19:43 +10:00
14 changed files with 1219 additions and 311 deletions

View file

@ -1,10 +1,13 @@
# 3D Graph for Obsidian
![hero](assets/hero.png)
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/)*
---
![img.png](assets/hero.png)
💡 *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.
![My Video](assets/output.gif)
*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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

BIN
assets/preview.mp4 Normal file

Binary file not shown.

View file

@ -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,
});

View file

@ -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() {

View file

@ -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
View file

@ -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": {

View file

@ -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"
}
}

View file

@ -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'); }));
}
}

View file

@ -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 {

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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",

View file

@ -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"
}