mirror of
https://github.com/apoo711/obsidian-3d-graph.git
synced 2026-07-22 13:00:24 +00:00
feat: advanced filters, node dragging, performance optimizations, and settings spacing
This commit is contained in:
parent
255f613e24
commit
aa6db7e820
7 changed files with 284 additions and 180 deletions
165
package-lock.json
generated
165
package-lock.json
generated
|
|
@ -9,35 +9,36 @@
|
|||
"version": "2.4.1",
|
||||
"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,
|
||||
|
|
@ -46,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,
|
||||
|
|
@ -649,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": {
|
||||
|
|
@ -878,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"
|
||||
|
|
@ -936,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,
|
||||
|
|
@ -1008,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,
|
||||
|
|
@ -1768,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
|
||||
|
|
@ -2020,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": {
|
||||
|
|
@ -2113,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": {
|
||||
|
|
@ -2137,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"
|
||||
},
|
||||
|
|
@ -2158,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,
|
||||
|
|
@ -2235,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": {
|
||||
|
|
@ -2245,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": {
|
||||
|
|
@ -2371,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": {
|
||||
|
|
@ -2396,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",
|
||||
|
|
@ -2602,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
|
||||
|
|
@ -2632,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",
|
||||
|
|
@ -2662,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",
|
||||
|
|
@ -2677,7 +2680,7 @@
|
|||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.168"
|
||||
"three": ">=0.179"
|
||||
}
|
||||
},
|
||||
"node_modules/three-spritetext": {
|
||||
|
|
@ -2770,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": {
|
||||
|
|
@ -2780,7 +2783,7 @@
|
|||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -13,18 +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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
.addOption('path', 'Path')
|
||||
.addOption('tag', 'Tag')
|
||||
.setValue(filter.type)
|
||||
.onChange(async (value: 'path' | 'tag') => {
|
||||
filter.type = value;
|
||||
.onChange(async (value: string) => {
|
||||
filter.type = value as 'path' | 'tag';
|
||||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}))
|
||||
|
|
@ -65,14 +65,15 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: true });
|
||||
}, 500, true)))
|
||||
.addToggle(toggle => toggle
|
||||
.setTooltip("Invert filter (NOT)")
|
||||
.setValue(filter.inverted)
|
||||
.onChange(async (value) => {
|
||||
filter.inverted = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.triggerUpdate({ redrawData: true, useCache: 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')
|
||||
|
|
@ -88,7 +89,7 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
.addButton(button => button
|
||||
.setButtonText('Add new filter')
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.filters.push({ type: 'path', value: '', inverted: false });
|
||||
this.plugin.settings.filters.push({ type: 'path', value: '', enabled: true });
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
|
|
@ -158,11 +159,11 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl).setName('Appearance').setHeading();
|
||||
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()
|
||||
|
|
@ -175,6 +176,8 @@ export class Graph3DSettingsTab extends PluginSettingTab {
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export interface ColorGroup {
|
|||
export interface Filter {
|
||||
type: 'path' | 'tag';
|
||||
value: string;
|
||||
inverted: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export enum NodeShape { Sphere = 'Sphere', Cube = 'Cube', Pyramid = 'Pyramid', Tetrahedron = 'Tetrahedron' }
|
||||
|
|
@ -42,6 +42,7 @@ export interface Graph3DPluginSettings {
|
|||
attachmentShape: NodeShape;
|
||||
// Labels
|
||||
showNodeLabels: boolean;
|
||||
showLabelsOnHoverOnly: boolean;
|
||||
labelDistance: number;
|
||||
labelFadeThreshold: number;
|
||||
labelTextSize: number;
|
||||
|
|
@ -92,6 +93,7 @@ export const DEFAULT_SETTINGS: Graph3DPluginSettings = {
|
|||
attachmentShape: NodeShape.Cube,
|
||||
// Labels
|
||||
showNodeLabels: true,
|
||||
showLabelsOnHoverOnly: false,
|
||||
labelDistance: 150,
|
||||
labelFadeThreshold: 0.8,
|
||||
labelTextSize: 2.5,
|
||||
|
|
|
|||
239
src/view.ts
239
src/view.ts
|
|
@ -1,5 +1,5 @@
|
|||
// src/view.ts
|
||||
import { ItemView, WorkspaceLeaf, TFile, Setting, setIcon, debounce } from 'obsidian';
|
||||
import { ItemView, WorkspaceLeaf, TFile, Setting, setIcon, debounce, getAllTags } from 'obsidian';
|
||||
import ForceGraph3D from '3d-force-graph';
|
||||
import * as THREE from 'three';
|
||||
import SpriteText from 'three-spritetext';
|
||||
|
|
@ -57,12 +57,53 @@ export class Graph3DView extends ItemView {
|
|||
// Keyboard controls state
|
||||
private pressedKeys = new Set<string>();
|
||||
|
||||
private fileContentCache = new Map<string, { mtime: number, content: string }>();
|
||||
private frustum = new THREE.Frustum();
|
||||
private projScreenMatrix = new THREE.Matrix4();
|
||||
private geometryCache = new Map<NodeShape, THREE.BufferGeometry>();
|
||||
private materialCache = new Map<string, THREE.MeshLambertMaterial>();
|
||||
private controlsListenerAdded = false;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, plugin: Graph3DPlugin) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
this.settings = plugin.settings;
|
||||
}
|
||||
|
||||
private getSharedGeometry(shape: NodeShape): THREE.BufferGeometry {
|
||||
let geometry = this.geometryCache.get(shape);
|
||||
if (!geometry) {
|
||||
switch (shape) {
|
||||
case NodeShape.Cube: geometry = new THREE.BoxGeometry(1, 1, 1); break;
|
||||
case NodeShape.Pyramid: geometry = new THREE.ConeGeometry(1 / 1.5, 1, 4); break;
|
||||
case NodeShape.Tetrahedron: geometry = new THREE.TetrahedronGeometry(1 / 1.2); break;
|
||||
default: geometry = new THREE.SphereGeometry(0.5);
|
||||
}
|
||||
this.geometryCache.set(shape, geometry);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
|
||||
private getSharedMaterial(color: string): THREE.MeshLambertMaterial {
|
||||
let material = this.materialCache.get(color);
|
||||
if (!material) {
|
||||
material = new THREE.MeshLambertMaterial({
|
||||
color: color,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
this.materialCache.set(color, material);
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
private clearResourceCaches() {
|
||||
this.geometryCache.forEach(g => g.dispose());
|
||||
this.geometryCache.clear();
|
||||
this.materialCache.forEach(m => m.dispose());
|
||||
this.materialCache.clear();
|
||||
}
|
||||
|
||||
getViewType() { return VIEW_TYPE_3D_GRAPH; }
|
||||
getDisplayText() { return "3d graph"; }
|
||||
|
||||
|
|
@ -94,6 +135,17 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
private addLocalControls() {
|
||||
const controlsContainer = this.contentEl.createEl('div', { cls: 'graph-3d-controls-container' });
|
||||
|
||||
// Add Reset View button
|
||||
const resetViewButton = controlsContainer.createEl('div', { cls: 'graph-3d-reset-toggle' });
|
||||
setIcon(resetViewButton, 'refresh-cw');
|
||||
resetViewButton.setAttribute('aria-label', 'Reset view');
|
||||
resetViewButton.addEventListener('click', () => {
|
||||
if (this.graph && this.isGraphInitialized) {
|
||||
this.graph.zoomToFit(800);
|
||||
}
|
||||
});
|
||||
|
||||
this.settingsToggleButton = controlsContainer.createEl('div', { cls: 'graph-3d-settings-toggle' });
|
||||
setIcon(this.settingsToggleButton, 'settings');
|
||||
this.settingsToggleButton.setAttribute('aria-label', 'Graph settings');
|
||||
|
|
@ -141,9 +193,8 @@ export class Graph3DView extends ItemView {
|
|||
.addDropdown(dropdown => dropdown
|
||||
.addOption('path', 'Path')
|
||||
.addOption('tag', 'Tag')
|
||||
.setValue(filter.type)
|
||||
.onChange(async (value: 'path' | 'tag') => {
|
||||
filter.type = value;
|
||||
.onChange(async (value: string) => {
|
||||
filter.type = value as 'path' | 'tag';
|
||||
await this.plugin.saveSettings();
|
||||
this.updateData({ useCache: true });
|
||||
}))
|
||||
|
|
@ -155,14 +206,15 @@ export class Graph3DView extends ItemView {
|
|||
await this.plugin.saveSettings();
|
||||
this.updateData({ useCache: true });
|
||||
}, 500, true)))
|
||||
.addToggle(toggle => toggle
|
||||
.setTooltip("Invert filter (NOT)")
|
||||
.setValue(filter.inverted)
|
||||
.onChange(async (value) => {
|
||||
filter.inverted = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.updateData({ useCache: true });
|
||||
}))
|
||||
.addToggle(toggle => {
|
||||
toggle.setTooltip("Enable/Disable filter")
|
||||
.setValue(filter.enabled)
|
||||
.onChange(async (value) => {
|
||||
filter.enabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.updateData({ useCache: true });
|
||||
});
|
||||
})
|
||||
.addExtraButton(button => button
|
||||
.setIcon('cross')
|
||||
.setTooltip('Remove filter')
|
||||
|
|
@ -178,7 +230,7 @@ export class Graph3DView extends ItemView {
|
|||
.addButton(button => button
|
||||
.setButtonText('Add new filter')
|
||||
.onClick(async () => {
|
||||
this.settings.filters.push({ type: 'path', value: '', inverted: false });
|
||||
this.settings.filters.push({ type: 'path', value: '', enabled: true });
|
||||
await this.plugin.saveSettings();
|
||||
this.renderSettingsPanel();
|
||||
}));
|
||||
|
|
@ -276,11 +328,11 @@ export class Graph3DView extends ItemView {
|
|||
.onChange(async (v) => { this.settings.linkThickness = v; await updateDisplayAndColors(); }));
|
||||
|
||||
new Setting(container).setName('Node shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.settings.nodeShape)
|
||||
.onChange(async(value: NodeShape) => {this.settings.nodeShape = value; await updateDisplayAndColors()}));
|
||||
.onChange(async(value: string) => {this.settings.nodeShape = value as NodeShape; await updateDisplayAndColors()}));
|
||||
new Setting(container).setName('Tag shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.settings.tagShape)
|
||||
.onChange(async(value: NodeShape) => {this.settings.tagShape = value; await updateDisplayAndColors()}));
|
||||
.onChange(async(value: string) => {this.settings.tagShape = value as NodeShape; await updateDisplayAndColors()}));
|
||||
new Setting(container).setName('Attachment shape').addDropdown(dd => dd.addOptions(NodeShape).setValue(this.settings.attachmentShape)
|
||||
.onChange(async(value: NodeShape) => {this.settings.attachmentShape = value; await updateDisplayAndColors()}));
|
||||
.onChange(async(value: string) => {this.settings.attachmentShape = value as NodeShape; await updateDisplayAndColors()}));
|
||||
}
|
||||
|
||||
private renderLabelSettings(container: HTMLElement) {
|
||||
|
|
@ -300,6 +352,15 @@ export class Graph3DView extends ItemView {
|
|||
this.updateDisplay();
|
||||
}));
|
||||
|
||||
new Setting(container)
|
||||
.setName('Show labels on hover/highlight only')
|
||||
.addToggle(toggle => toggle.setValue(this.settings.showLabelsOnHoverOnly)
|
||||
.onChange(async (value) => {
|
||||
this.settings.showLabelsOnHoverOnly = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.updateDisplay();
|
||||
}));
|
||||
|
||||
new Setting(container)
|
||||
.setName('Label distance')
|
||||
.addSlider(s => s.setLimits(50, 500, 10).setValue(this.settings.labelDistance).setDynamicTooltip()
|
||||
|
|
@ -471,6 +532,19 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
const Graph = (ForceGraph3D as any).default || ForceGraph3D;
|
||||
this.graph = Graph()(this.graphContainer)
|
||||
.enableNodeDrag(true)
|
||||
.onNodeDrag((node: GraphNode) => {
|
||||
const controls = this.graph.controls();
|
||||
if (controls) {
|
||||
controls.enabled = false;
|
||||
}
|
||||
})
|
||||
.onNodeDragEnd((node: GraphNode) => {
|
||||
const controls = this.graph.controls();
|
||||
if (controls) {
|
||||
controls.enabled = true;
|
||||
}
|
||||
})
|
||||
.onNodeClick((node: GraphNode, event: MouseEvent) => this.handleNodeClick(node, event))
|
||||
.onNodeHover((node: GraphNode | null) => this.handleNodeHover(node))
|
||||
.linkCurvature((link: ProcessedGraphLink) => this.getLinkCurvature(link))
|
||||
|
|
@ -580,22 +654,13 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
this.graph.resumeAnimation();
|
||||
} else {
|
||||
if (this.graph && typeof this.graph._destructor === 'function') {
|
||||
this.graph._destructor();
|
||||
}
|
||||
const Graph = (ForceGraph3D as any).default || ForceGraph3D;
|
||||
this.graph = Graph()(this.graphContainer)
|
||||
.onNodeClick((node: GraphNode) => this.handleNodeClick(node))
|
||||
.graphData({ nodes: [], links: [] });
|
||||
|
||||
this.initializeForces();
|
||||
|
||||
this.graph.graphData({ nodes: [], links: [] });
|
||||
this.colorCache.clear();
|
||||
const bgColor = this.settings.useThemeColors
|
||||
? this.getCssColor('--background-primary', '#000000')
|
||||
: this.settings.backgroundColor;
|
||||
this.graph.backgroundColor(bgColor);
|
||||
this.messageEl.setText("No search results found.");
|
||||
this.messageEl.setText("No search results or filters matched.");
|
||||
this.messageEl.addClass('is-visible');
|
||||
this.graph.pauseAnimation();
|
||||
}
|
||||
|
|
@ -616,14 +681,10 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
this.graph.graphData().nodes.forEach((node: GraphNode) => {
|
||||
const mesh = this.nodeMeshes.get(node);
|
||||
if (mesh && mesh.material) {
|
||||
if (mesh) {
|
||||
const color = this.getNodeColor(node);
|
||||
if (color) {
|
||||
try {
|
||||
(mesh.material as THREE.MeshLambertMaterial).color.set(color);
|
||||
} catch (e) {
|
||||
console.error(`3D Graph: Invalid color '${color}' for node`, node, e);
|
||||
}
|
||||
mesh.material = this.getSharedMaterial(color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -758,31 +819,13 @@ export class Graph3DView extends ItemView {
|
|||
default: shape = this.settings.nodeShape; size = this.settings.nodeSize;
|
||||
}
|
||||
|
||||
let geometry: THREE.BufferGeometry;
|
||||
const geometry = this.getSharedGeometry(shape);
|
||||
const s = size * 1.5;
|
||||
|
||||
switch (shape) {
|
||||
case NodeShape.Cube: geometry = new THREE.BoxGeometry(s, s, s); break;
|
||||
case NodeShape.Pyramid: geometry = new THREE.ConeGeometry(s / 1.5, s, 4); break;
|
||||
case NodeShape.Tetrahedron: geometry = new THREE.TetrahedronGeometry(s / 1.2); break;
|
||||
default: geometry = new THREE.SphereGeometry(s / 2);
|
||||
}
|
||||
|
||||
const color = this.getNodeColor(node);
|
||||
const material = new THREE.MeshLambertMaterial({
|
||||
color: '#ffffff',
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, this.getSharedMaterial(color));
|
||||
mesh.scale.set(s, s, s);
|
||||
|
||||
try {
|
||||
material.color.set(color);
|
||||
} catch (e) {
|
||||
console.error(`3D Graph: Could not set material color to '${color}'`, e);
|
||||
material.color.set(this.settings.colorNode);
|
||||
}
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
this.nodeMeshes.set(node, mesh);
|
||||
group.add(mesh);
|
||||
|
||||
|
|
@ -824,6 +867,10 @@ export class Graph3DView extends ItemView {
|
|||
controls.rotateSpeed = rotateSpeed;
|
||||
controls.panSpeed = panSpeed;
|
||||
controls.zoomSpeed = zoomSpeed;
|
||||
if (!this.controlsListenerAdded) {
|
||||
controls.addEventListener('change', () => this.updateLabels());
|
||||
this.controlsListenerAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -835,19 +882,32 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
if (!nodes || !camera) return;
|
||||
|
||||
this.projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
||||
this.frustum.setFromProjectionMatrix(this.projScreenMatrix);
|
||||
|
||||
if (this.settings.labelOcclusion && this.occludersCacheDirty) {
|
||||
this.cachedOccluders = nodes.map((n: GraphNode) => this.nodeMeshes.get(n)).filter(Boolean) as THREE.Mesh[];
|
||||
this.occludersCacheDirty = false;
|
||||
}
|
||||
|
||||
const raycastCullDistanceSq = this.RAYCAST_CULL_DISTANCE * this.RAYCAST_CULL_DISTANCE;
|
||||
const relevantOccluders = this.settings.labelOcclusion
|
||||
? this.cachedOccluders.filter(mesh => camera.position.distanceTo(mesh.position) < this.RAYCAST_CULL_DISTANCE)
|
||||
? this.cachedOccluders.filter(mesh => camera.position.distanceToSquared(mesh.position) < raycastCullDistanceSq)
|
||||
: [];
|
||||
|
||||
nodes.forEach((node: GraphNode) => {
|
||||
const sprite = this.nodeSprites.get(node);
|
||||
if (!sprite) return;
|
||||
|
||||
if (this.settings.showLabelsOnHoverOnly) {
|
||||
const isHovered = this.hoveredNode && this.hoveredNode.id === node.id;
|
||||
const isHighlighted = this.highlightedNodes.has(node.id);
|
||||
if (!isHovered && !isHighlighted) {
|
||||
sprite.visible = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.__threeObj) {
|
||||
node.__threeObj.getWorldPosition(this.reusableNodePosition);
|
||||
} else {
|
||||
|
|
@ -855,6 +915,11 @@ export class Graph3DView extends ItemView {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.frustum.containsPoint(this.reusableNodePosition)) {
|
||||
sprite.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const distance = camera.position.distanceTo(this.reusableNodePosition);
|
||||
|
||||
const visibleDistance = this.settings.labelDistance;
|
||||
|
|
@ -947,6 +1012,7 @@ export class Graph3DView extends ItemView {
|
|||
this.updateColors();
|
||||
this.graph.linkWidth(this.graph.linkWidth());
|
||||
this.graph.linkDirectionalParticles(this.graph.linkDirectionalParticles());
|
||||
this.updateLabels();
|
||||
}
|
||||
|
||||
private handleNodeHover(node: GraphNode | null) {
|
||||
|
|
@ -977,6 +1043,7 @@ export class Graph3DView extends ItemView {
|
|||
this.updateColors();
|
||||
this.graph.linkWidth(this.graph.linkWidth());
|
||||
this.graph.linkDirectionalParticles(this.graph.linkDirectionalParticles());
|
||||
this.updateLabels();
|
||||
}
|
||||
|
||||
private getLinkCurvature(link: ProcessedGraphLink) {
|
||||
|
|
@ -997,6 +1064,9 @@ export class Graph3DView extends ItemView {
|
|||
}
|
||||
if (filter.type === 'tag') {
|
||||
const tagToMatch = filterValue.startsWith('#') ? filterValue.substring(1) : filterValue;
|
||||
if (node.type === NodeType.Tag) {
|
||||
return node.name.toLowerCase() === `#${tagToMatch}`;
|
||||
}
|
||||
return node.tags?.some(tag => tag.toLowerCase() === tagToMatch) ?? false;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1010,14 +1080,25 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
const allNodesMap = new Map<string, GraphNode>();
|
||||
|
||||
const needsContent = !!searchQuery || this.settings.groups.some(g => {
|
||||
const q = g.query.toLowerCase();
|
||||
return q && !q.startsWith('path:') && !q.startsWith('tag:') && !q.startsWith('file:');
|
||||
});
|
||||
|
||||
for (const file of allFiles) {
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
const tags = cache?.tags?.map(t => t.tag.substring(1)) || [];
|
||||
const tags = cache ? (getAllTags(cache) || []).map(t => t.startsWith('#') ? t.substring(1) : t) : [];
|
||||
const type = file.extension === 'md' ? NodeType.File : NodeType.Attachment;
|
||||
|
||||
let content = '';
|
||||
if (type === NodeType.File) {
|
||||
content = await this.app.vault.cachedRead(file);
|
||||
if (type === NodeType.File && needsContent) {
|
||||
const cached = this.fileContentCache.get(file.path);
|
||||
if (cached && cached.mtime === file.stat.mtime) {
|
||||
content = cached.content;
|
||||
} else {
|
||||
content = await this.app.vault.cachedRead(file);
|
||||
this.fileContentCache.set(file.path, { mtime: file.stat.mtime, content });
|
||||
}
|
||||
}
|
||||
|
||||
allNodesMap.set(file.path, { id: file.path, name: file.basename, filename: file.name, type, tags, content });
|
||||
|
|
@ -1025,9 +1106,29 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
|
||||
const allLinks: { source: string, target: string }[] = [];
|
||||
const existingLinks = new Set<string>();
|
||||
|
||||
for (const sourcePath in resolvedLinks) {
|
||||
for (const targetPath in resolvedLinks[sourcePath]) {
|
||||
allLinks.push({ source: sourcePath, target: targetPath });
|
||||
existingLinks.add(`${sourcePath}->${targetPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Also extract links from frontmatter properties (Properties section)
|
||||
for (const file of allFiles) {
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (cache?.frontmatterLinks) {
|
||||
for (const linkCache of cache.frontmatterLinks) {
|
||||
const destFile = this.app.metadataCache.getFirstLinkpathDest(linkCache.link, file.path);
|
||||
if (destFile) {
|
||||
const linkKey = `${file.path}->${destFile.path}`;
|
||||
if (!existingLinks.has(linkKey)) {
|
||||
allLinks.push({ source: file.path, target: destFile.path });
|
||||
existingLinks.add(linkKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1050,12 +1151,11 @@ export class Graph3DView extends ItemView {
|
|||
let finalNodes = Array.from(allNodesMap.values());
|
||||
|
||||
// Advanced Filtering Logic
|
||||
const positiveFilters = filters.filter(f => !f.inverted && f.value.trim() !== '');
|
||||
const negativeFilters = filters.filter(f => f.inverted && f.value.trim() !== '');
|
||||
const activeFilters = filters.filter(f => f.enabled && f.value.trim() !== '');
|
||||
|
||||
if (positiveFilters.length > 0) {
|
||||
if (activeFilters.length > 0) {
|
||||
const nodesToKeep = new Set<GraphNode>();
|
||||
positiveFilters.forEach(filter => {
|
||||
activeFilters.forEach(filter => {
|
||||
finalNodes.forEach(node => {
|
||||
if (this.matchesFilter(node, filter)) {
|
||||
nodesToKeep.add(node);
|
||||
|
|
@ -1065,12 +1165,6 @@ export class Graph3DView extends ItemView {
|
|||
finalNodes = Array.from(nodesToKeep);
|
||||
}
|
||||
|
||||
if (negativeFilters.length > 0) {
|
||||
finalNodes = finalNodes.filter(node => {
|
||||
return !negativeFilters.some(filter => this.matchesFilter(node, filter));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (searchQuery) {
|
||||
const lowerCaseFilter = searchQuery.toLowerCase();
|
||||
|
|
@ -1111,12 +1205,7 @@ export class Graph3DView extends ItemView {
|
|||
|
||||
private cleanupNode(node: GraphNode, options: { cleanMesh?: boolean, cleanGroup?: boolean } = { cleanMesh: true, cleanGroup: true }) {
|
||||
if (options.cleanMesh) {
|
||||
const mesh = this.nodeMeshes.get(node);
|
||||
if (mesh) {
|
||||
mesh.geometry?.dispose();
|
||||
(mesh.material as THREE.Material)?.dispose();
|
||||
this.nodeMeshes.delete(node);
|
||||
}
|
||||
this.nodeMeshes.delete(node);
|
||||
}
|
||||
|
||||
const sprite = this.nodeSprites.get(node);
|
||||
|
|
@ -1152,5 +1241,7 @@ export class Graph3DView extends ItemView {
|
|||
if (this.messageEl) {
|
||||
this.messageEl.remove();
|
||||
}
|
||||
this.clearResourceCaches();
|
||||
this.controlsListenerAdded = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
styles.css
10
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,7 +77,7 @@
|
|||
|
||||
.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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue