Merge 2960809bef into 114b56d829
This commit is contained in:
commit
b42b71d79c
36 changed files with 3619 additions and 566 deletions
669
cognee-frontend/package-lock.json
generated
669
cognee-frontend/package-lock.json
generated
|
|
@ -12,11 +12,14 @@
|
|||
"classnames": "^2.5.1",
|
||||
"culori": "^4.0.1",
|
||||
"d3-force-3d": "^3.0.6",
|
||||
"next": "^16.1.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"next": "15.3.3",
|
||||
"ngraph.forcelayout": "^3.3.1",
|
||||
"ngraph.graph": "^20.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-force-graph-2d": "^1.27.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"three": "^0.175.0",
|
||||
"troika-three-text": "^0.52.4",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -24,8 +27,9 @@
|
|||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@types/culori": "^4.0.0",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/three": "^0.175.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "^16.0.4",
|
||||
|
|
@ -1678,11 +1682,31 @@
|
|||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
"node_modules/@types/stats.js": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.175.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.175.0.tgz",
|
||||
"integrity": "sha512-ldMSBgtZOZ3g9kJ3kOZSEtZIEITmJOzu8eKVpkhf036GuNkM4mt0NXecrjCn5tMm1OblOF7dZehlaDypBfNokw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
"@types/stats.js": "*",
|
||||
"@types/webxr": "*",
|
||||
"@webgpu/types": "*",
|
||||
"fflate": "~0.8.2",
|
||||
"meshoptimizer": "~0.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/three/node_modules/@tweenjs/tween.js": {
|
||||
"version": "23.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
||||
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
|
|
@ -1691,6 +1715,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/webxr": {
|
||||
"version": "0.5.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.48.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz",
|
||||
|
|
@ -2237,6 +2267,12 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.66",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.66.tgz",
|
||||
"integrity": "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/accessor-fn": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz",
|
||||
|
|
@ -2569,6 +2605,14 @@
|
|||
"url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
|
||||
}
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
|
|
@ -4031,6 +4075,12 @@
|
|||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
|
|
@ -5763,447 +5813,11 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
||||
"integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/debug": "^4.0.0",
|
||||
"debug": "^4.0.0",
|
||||
"decode-named-character-reference": "^1.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-core-commonmark": "^2.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-combine-extensions": "^2.0.0",
|
||||
"micromark-util-decode-numeric-character-reference": "^2.0.0",
|
||||
"micromark-util-encode": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0",
|
||||
"micromark-util-resolve-all": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-subtokenize": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-core-commonmark": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
|
||||
"integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-named-character-reference": "^1.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-factory-destination": "^2.0.0",
|
||||
"micromark-factory-label": "^2.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-factory-title": "^2.0.0",
|
||||
"micromark-factory-whitespace": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-classify-character": "^2.0.0",
|
||||
"micromark-util-html-tag-name": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0",
|
||||
"micromark-util-resolve-all": "^2.0.0",
|
||||
"micromark-util-subtokenize": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-destination": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
|
||||
"integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-label": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
|
||||
"integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-space": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
|
||||
"integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-title": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
|
||||
"integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-whitespace": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
|
||||
"integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-character": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
|
||||
"integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-chunked": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
|
||||
"integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-symbol": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-classify-character": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
|
||||
"integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-combine-extensions": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
|
||||
"integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-decode-numeric-character-reference": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
|
||||
"integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-symbol": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-decode-string": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
|
||||
"integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-named-character-reference": "^1.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-decode-numeric-character-reference": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-encode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
|
||||
"integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromark-util-html-tag-name": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
|
||||
"integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromark-util-normalize-identifier": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
|
||||
"integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-symbol": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-resolve-all": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
|
||||
"integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-sanitize-uri": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
|
||||
"integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-encode": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-subtokenize": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
|
||||
"integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-util-symbol": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
|
||||
"integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromark-util-types": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
|
||||
"integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "GitHub Sponsors",
|
||||
"url": "https://github.com/sponsors/unifiedjs"
|
||||
},
|
||||
{
|
||||
"type": "OpenCollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
"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==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
|
|
@ -6371,12 +5985,38 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"node_modules/ngraph.events": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.4.0.tgz",
|
||||
"integrity": "sha512-NeDGI4DSyjBNBRtA86222JoYietsmCXbs8CEB0dZ51Xeh4lhVl1y3wpWLumczvnha8sFQIW4E0vvVWwgmX2mGw=="
|
||||
},
|
||||
"node_modules/ngraph.forcelayout": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.forcelayout/-/ngraph.forcelayout-3.3.1.tgz",
|
||||
"integrity": "sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==",
|
||||
"dependencies": {
|
||||
"ngraph.events": "^1.0.0",
|
||||
"ngraph.merge": "^1.0.0",
|
||||
"ngraph.random": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngraph.graph": {
|
||||
"version": "20.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.graph/-/ngraph.graph-20.1.0.tgz",
|
||||
"integrity": "sha512-1jorNgIc0Kg0L9bTNN4+RCrVvbZ+4pqGVMrbhX3LLyqYcRdLvAQRRnxddmfj9l5f6Eq59SUTfbYZEm8cktiE7Q==",
|
||||
"dependencies": {
|
||||
"ngraph.events": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ngraph.merge": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.merge/-/ngraph.merge-1.0.0.tgz",
|
||||
"integrity": "sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg=="
|
||||
},
|
||||
"node_modules/ngraph.random": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.random/-/ngraph.random-1.2.0.tgz",
|
||||
"integrity": "sha512-4EUeAGbB2HWX9njd6bP6tciN6ByJfoaAvmVL9QTaZSeXrW46eNGA9GajiXiPBbvFqxUWFkEbyo6x5qsACUuVfA=="
|
||||
},
|
||||
"node_modules/oauth4webapi": {
|
||||
"version": "3.8.3",
|
||||
|
|
@ -6917,37 +6557,12 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-parse": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
||||
"integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-rehype": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
|
||||
"integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-to-hast": "^13.0.0",
|
||||
"unified": "^11.0.0",
|
||||
"vfile": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
|
|
@ -7594,6 +7209,12 @@
|
|||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.175.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.175.0.tgz",
|
||||
"integrity": "sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
|
|
@ -7662,26 +7283,33 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
"integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
"node_modules/troika-three-text": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.0.2",
|
||||
"troika-three-utils": "^0.52.4",
|
||||
"troika-worker-utils": "^0.52.0",
|
||||
"webgl-sdf-generator": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/trough": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
|
||||
"integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
"node_modules/troika-three-utils": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-worker-utils": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw=="
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||
|
|
@ -8068,33 +7696,10 @@
|
|||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
"integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^3.0.0",
|
||||
"vfile-message": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile-message": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
|
||||
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^3.0.0",
|
||||
"unist-util-stringify-position": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
"node_modules/webgl-sdf-generator": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
||||
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@
|
|||
"classnames": "^2.5.1",
|
||||
"culori": "^4.0.1",
|
||||
"d3-force-3d": "^3.0.6",
|
||||
"next": "^16.1.7",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"next": "15.3.3",
|
||||
"ngraph.forcelayout": "^3.3.1",
|
||||
"ngraph.graph": "^20.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-force-graph-2d": "^1.27.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"three": "^0.175.0",
|
||||
"troika-three-text": "^0.52.4",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -25,8 +28,9 @@
|
|||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@types/culori": "^4.0.0",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/three": "^0.175.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "^16.0.4",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
||||
import { useBoolean } from "@/utils";
|
||||
import { Accordion, CTAButton, GhostButton, IconButton, Input, Modal, PopupMenu } from "@/ui/elements";
|
||||
|
|
@ -258,15 +259,12 @@ export default function DatasetsAccordion({
|
|||
tools={(
|
||||
<IconButton className="relative">
|
||||
<PopupMenu>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer relative">
|
||||
<input tabIndex={-1} type="file" multiple onChange={handleAddFiles.bind(null, dataset)} className="absolute w-full h-full cursor-pointer opacity-0" />
|
||||
<span>add data</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 items-start">
|
||||
<div onClick={() => handleDatasetRemove(dataset)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">delete</div>
|
||||
<div className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer relative">
|
||||
<input tabIndex={-1} type="file" multiple onChange={handleAddFiles.bind(null, dataset)} className="absolute w-full h-full cursor-pointer opacity-0" />
|
||||
<span>add data</span>
|
||||
</div>
|
||||
<Link target="_blank" href={`/visualize/${dataset.id}`}>visualize</Link>
|
||||
<div onClick={() => handleDatasetRemove(dataset)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">delete</div>
|
||||
</PopupMenu>
|
||||
</IconButton>
|
||||
)}
|
||||
|
|
|
|||
95
cognee-frontend/src/app/visualize/[datasetId]/page.tsx
Normal file
95
cognee-frontend/src/app/visualize/[datasetId]/page.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { fetch } from "@/utils";
|
||||
import { adaptCogneeGraphData, validateCogneeGraphResponse } from "@/lib/adaptCogneeGraphData";
|
||||
import { CogneeGraphResponse } from "@/types/CogneeAPI";
|
||||
import MemoryGraphVisualization from "@/ui/elements/MemoryGraphVisualization";
|
||||
import { Edge, Node } from "@/ui/rendering/graph/types";
|
||||
|
||||
interface VisualizePageProps {
|
||||
params: { datasetId: string };
|
||||
}
|
||||
|
||||
export default function Page({ params }: VisualizePageProps) {
|
||||
const [graphData, setGraphData] = useState<{ nodes: Node[], edges: Edge[] } | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function getData() {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const datasetId = (await params).datasetId;
|
||||
const response = await fetch(`/v1/datasets/${datasetId}/graph`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch graph data: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const apiData = await response.json();
|
||||
|
||||
// Validate API response
|
||||
if (!validateCogneeGraphResponse(apiData)) {
|
||||
throw new Error("Invalid graph data format from API");
|
||||
}
|
||||
|
||||
// Adapt Cognee API format to visualization format
|
||||
const adaptedData = adaptCogneeGraphData(apiData as CogneeGraphResponse);
|
||||
setGraphData(adaptedData);
|
||||
} catch (err) {
|
||||
console.error("Error loading graph data:", err);
|
||||
setError(err instanceof Error ? err.message : "Unknown error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
getData();
|
||||
}, [params]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-gray-900 via-black to-purple-900">
|
||||
<div className="text-center">
|
||||
<div className="text-6xl mb-4 animate-spin">⚛️</div>
|
||||
<div className="text-2xl font-bold text-white">Loading graph data...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-gray-900 via-black to-purple-900">
|
||||
<div className="text-center max-w-md p-6">
|
||||
<div className="text-6xl mb-4">⚠️</div>
|
||||
<div className="text-2xl font-bold text-white mb-2">Error Loading Graph</div>
|
||||
<div className="text-gray-400">{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!graphData || graphData.nodes.length === 0) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-gray-900 via-black to-purple-900">
|
||||
<div className="text-center max-w-md p-6">
|
||||
<div className="text-6xl mb-4">📊</div>
|
||||
<div className="text-2xl font-bold text-white mb-2">No Graph Data</div>
|
||||
<div className="text-gray-400">This dataset has no graph data to visualize.</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MemoryGraphVisualization
|
||||
nodes={graphData.nodes}
|
||||
edges={graphData.edges}
|
||||
title="Cognee Memory Graph"
|
||||
showControls={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
184
cognee-frontend/src/app/visualize/demo/README.md
Normal file
184
cognee-frontend/src/app/visualize/demo/README.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# Graph Visualization Demo
|
||||
|
||||
An isolated, interactive demo of Cognee's Three.js-based graph visualization with a rich AI/ML knowledge graph dataset.
|
||||
|
||||
## Features
|
||||
|
||||
### 🎨 Visual Design
|
||||
- **Vibrant Color Palette**: 10 distinct colors for different node types
|
||||
- **Dark Theme**: Optimized background (#0a0a0f) for maximum contrast
|
||||
- **Metaball Rendering**: Smooth, organic blob visualization of node clusters
|
||||
- **Responsive Labels**: Context-aware labels that appear on hover and zoom
|
||||
|
||||
### 🎯 Interactive Controls
|
||||
- **Pan**: Click and drag to move around the graph
|
||||
- **Zoom**: Scroll to zoom in (6x max) or out (0.5x min)
|
||||
- **Hover**: Mouse over nodes to see labels and connections
|
||||
- **Click**: Select nodes to highlight their relationships
|
||||
- **Smooth Animation**: Fluid camera motion with optimized damping
|
||||
|
||||
### 📊 UI Components
|
||||
- **Legend Panel**: Categorizes nodes by type with color coding
|
||||
- **Statistics**: Real-time graph metrics (nodes, edges, connections)
|
||||
- **Instructions Overlay**: Quick reference for interaction methods
|
||||
- **Toggle Controls**: Show/hide legend and stats as needed
|
||||
|
||||
## Dataset
|
||||
|
||||
The demo includes a comprehensive **AI/ML Knowledge Graph** with:
|
||||
|
||||
### Node Types (52 total)
|
||||
- **Concepts** (6): AI, Machine Learning, Deep Learning, NLP, CV, RL
|
||||
- **Algorithms** (10): SVM, Decision Trees, K-Means, Q-Learning, etc.
|
||||
- **Architectures** (12): CNN, RNN, Transformer, GAN, VAE, etc.
|
||||
- **Technologies** (9): BERT, GPT, ResNet, YOLO, Word2Vec, etc.
|
||||
- **Applications** (5): Chatbots, Autonomous Vehicles, Medical Imaging, etc.
|
||||
- **Data** (4): Datasets, Feature Engineering, Augmentation, Normalization
|
||||
- **Optimization** (5): Gradient Descent, Adam, Backprop, Regularization, Dropout
|
||||
|
||||
### Relationships (56 edges)
|
||||
- Hierarchical: "is subfield of", "type of", "variant of"
|
||||
- Functional: "implements", "uses", "powered by", "trains"
|
||||
- Application: "application of", "task in", "used in"
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Architecture
|
||||
```
|
||||
GraphVisualization Component
|
||||
↓
|
||||
animate.ts (Main Render Loop)
|
||||
↓
|
||||
┌─────────────────┬─────────────────┬─────────────────┐
|
||||
│ Graph Layout │ Rendering │ Interaction │
|
||||
│ (ngraph) │ (Three.js) │ (Picking) │
|
||||
├─────────────────┼─────────────────┼─────────────────┤
|
||||
│ • Force layout │ • Node swarm │ • Mouse hover │
|
||||
│ • 800 iterations│ • Edge mesh │ • Click select │
|
||||
│ • Spring physics│ • Metaballs │ • Label display │
|
||||
│ │ • Density cloud │ • Pan/Zoom │
|
||||
└─────────────────┴─────────────────┴─────────────────┘
|
||||
```
|
||||
|
||||
### Performance Optimizations
|
||||
- **GPU-Accelerated**: All rendering uses WebGL shaders
|
||||
- **Instanced Rendering**: Nodes rendered in a single draw call
|
||||
- **Texture-Based Positions**: Node positions stored in GPU texture
|
||||
- **Culling**: Labels only shown for visible/hovered nodes
|
||||
- **Adaptive Layout**: Physics stabilizes after initial iterations
|
||||
|
||||
### Scalability
|
||||
The implementation is designed to handle:
|
||||
- ✅ **100+ nodes**: Excellent performance
|
||||
- ✅ **500+ nodes**: Good performance with metaballs
|
||||
- ✅ **1000+ nodes**: Recommended to reduce metaball density
|
||||
- ⚠️ **5000+ nodes**: Consider simplified rendering mode
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The visualization accepts a `config` prop:
|
||||
|
||||
```typescript
|
||||
config={{
|
||||
fontSize: 12, // Label font size (default: 10)
|
||||
}}
|
||||
```
|
||||
|
||||
### Force Layout Parameters (in animate.ts)
|
||||
```typescript
|
||||
{
|
||||
dragCoefficient: 0.8, // Node movement resistance
|
||||
springLength: 180, // Ideal distance between connected nodes
|
||||
springCoefficient: 0.25, // Connection strength
|
||||
gravity: -1200, // Repulsion force
|
||||
}
|
||||
```
|
||||
|
||||
### Camera Controls
|
||||
```typescript
|
||||
{
|
||||
minZoom: 0.5, // Maximum zoom out
|
||||
maxZoom: 6, // Maximum zoom in
|
||||
dampingFactor: 0.08, // Camera smoothness
|
||||
}
|
||||
```
|
||||
|
||||
## How to Use in Development
|
||||
|
||||
1. **Start the frontend**:
|
||||
```bash
|
||||
cd cognee-frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **Navigate to the demo**:
|
||||
```
|
||||
http://localhost:3000/visualize/demo
|
||||
```
|
||||
|
||||
3. **Interact with the graph**:
|
||||
- Hover over nodes to see labels
|
||||
- Zoom in to see more connections
|
||||
- Click to select nodes
|
||||
- Toggle legend/stats panels
|
||||
|
||||
## Extending the Demo
|
||||
|
||||
### Add More Nodes
|
||||
```typescript
|
||||
mockNodes.push({
|
||||
id: "new-concept",
|
||||
label: "New Concept",
|
||||
type: "Concept"
|
||||
});
|
||||
```
|
||||
|
||||
### Add Connections
|
||||
```typescript
|
||||
mockEdges.push({
|
||||
id: "e-new",
|
||||
source: "new-concept",
|
||||
target: "ai",
|
||||
label: "related to"
|
||||
});
|
||||
```
|
||||
|
||||
### Customize Colors
|
||||
Update the `typeColors` mapping in the demo page:
|
||||
```typescript
|
||||
const typeColors: Record<string, string> = {
|
||||
"YourType": "#YOUR_COLOR",
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
- [ ] Search functionality to find and highlight nodes
|
||||
- [ ] Filter nodes by type
|
||||
- [ ] Export graph as image/SVG
|
||||
- [ ] Node clustering by community detection
|
||||
- [ ] Time-based animation of graph evolution
|
||||
- [ ] 3D visualization mode
|
||||
- [ ] Multi-graph comparison view
|
||||
|
||||
## Related Files
|
||||
|
||||
- `src/ui/elements/GraphVisualization.tsx` - Main component wrapper
|
||||
- `src/ui/rendering/animate.ts` - Render loop and Three.js setup
|
||||
- `src/ui/rendering/graph/createGraph.ts` - Graph creation from data
|
||||
- `src/ui/rendering/materials/` - Shader materials for visual effects
|
||||
- `src/ui/rendering/meshes/` - Mesh generation for nodes/edges/labels
|
||||
|
||||
## Performance Tips
|
||||
|
||||
For large graphs (1000+ nodes):
|
||||
1. Reduce `densityCloudTarget` resolution (line 135 in animate.ts)
|
||||
2. Decrease label display limit (line 372)
|
||||
3. Consider disabling metaball rendering for very large graphs
|
||||
4. Use node clustering/aggregation for massive datasets
|
||||
|
||||
## License
|
||||
|
||||
Part of the Cognee project - Apache 2.0 License
|
||||
87
cognee-frontend/src/app/visualize/demo/page.tsx
Normal file
87
cognee-frontend/src/app/visualize/demo/page.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { generateOntologyGraph } from "@/lib/generateOntologyGraph";
|
||||
import MemoryGraphVisualization from "@/ui/elements/MemoryGraphVisualization";
|
||||
|
||||
type GraphMode = "small" | "medium" | "large";
|
||||
|
||||
export default function VisualizationDemoPage() {
|
||||
const [graphMode, setGraphMode] = useState<GraphMode>("medium");
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
|
||||
// Generate graph based on mode
|
||||
const { nodes, edges } = useMemo(() => {
|
||||
console.log(`Generating ${graphMode} ontology graph...`);
|
||||
setIsGenerating(true);
|
||||
|
||||
let result;
|
||||
switch (graphMode) {
|
||||
case "small":
|
||||
result = { ...generateOntologyGraph("simple"), clusters: new Map() };
|
||||
break;
|
||||
case "medium":
|
||||
result = generateOntologyGraph("medium");
|
||||
break;
|
||||
case "large":
|
||||
result = generateOntologyGraph("complex");
|
||||
break;
|
||||
}
|
||||
|
||||
setTimeout(() => setIsGenerating(false), 500);
|
||||
return result;
|
||||
}, [graphMode]);
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen">
|
||||
{isGenerating ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/90 z-50 backdrop-blur-sm">
|
||||
<div className="text-center">
|
||||
<div className="relative">
|
||||
<div className="text-6xl mb-4 animate-spin">⚛️</div>
|
||||
<div className="absolute inset-0 text-6xl mb-4 animate-ping opacity-20">⚛️</div>
|
||||
</div>
|
||||
<div className="text-2xl font-bold mb-2 bg-gradient-to-r from-purple-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
Building Knowledge Graph...
|
||||
</div>
|
||||
<div className="text-gray-400">
|
||||
Creating {
|
||||
graphMode === "small" ? "~500" :
|
||||
graphMode === "medium" ? "~1,000" :
|
||||
"~1,500"
|
||||
} interconnected nodes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Mode Selector Overlay */}
|
||||
<div className="absolute top-6 left-6 z-10 pointer-events-auto">
|
||||
<div className="flex gap-1 bg-black/70 backdrop-blur-md rounded-lg p-1 border border-purple-500/30">
|
||||
{(["small", "medium", "large"] as GraphMode[]).map((mode) => (
|
||||
<button
|
||||
key={mode}
|
||||
onClick={() => setGraphMode(mode)}
|
||||
className={`flex-1 px-3 py-2 rounded transition-all text-sm font-medium ${
|
||||
graphMode === mode
|
||||
? "bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg shadow-purple-500/50"
|
||||
: "hover:bg-white/10 text-gray-300"
|
||||
}`}
|
||||
>
|
||||
{mode === "small" && "500"}
|
||||
{mode === "medium" && "1K"}
|
||||
{mode === "large" && "1.5K"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MemoryGraphVisualization
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
title="Memory Retrieval Debugger (Demo)"
|
||||
showControls={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
45
cognee-frontend/src/types/CogneeAPI.ts
Normal file
45
cognee-frontend/src/types/CogneeAPI.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Type definitions for Cognee API responses
|
||||
* Based on Cognee SDK data point model and graph API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cognee DataPoint representation from API
|
||||
* Corresponds to: cognee/infrastructure/engine/models/DataPoint.py
|
||||
*/
|
||||
export interface CogneeDataPoint {
|
||||
id: string; // UUID
|
||||
label: string; // Display name
|
||||
type: string; // Node type (Entity, EntityType, DocumentChunk, etc.)
|
||||
properties?: Record<string, any>; // Additional metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Cognee Edge representation from API
|
||||
* Corresponds to: cognee/infrastructure/engine/models/Edge.py
|
||||
*/
|
||||
export interface CogneeEdge {
|
||||
source: string; // Source node UUID
|
||||
target: string; // Target node UUID
|
||||
label: string; // Relationship type
|
||||
weight?: number; // Optional weight
|
||||
weights?: Record<string, number>; // Optional multiple weights
|
||||
properties?: Record<string, any>; // Additional properties
|
||||
}
|
||||
|
||||
/**
|
||||
* Cognee Graph API response format
|
||||
* From: /api/v1/datasets/{dataset_id}/graph
|
||||
*/
|
||||
export interface CogneeGraphResponse {
|
||||
nodes: CogneeDataPoint[];
|
||||
edges: CogneeEdge[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cognee API error response
|
||||
*/
|
||||
export interface CogneeAPIError {
|
||||
detail: string;
|
||||
status_code: number;
|
||||
}
|
||||
165
cognee-frontend/src/types/NodeSet.ts
Normal file
165
cognee-frontend/src/types/NodeSet.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Node Sets: The primary abstraction for grouping nodes
|
||||
* Replaces fixed types with dynamic, inferred, overlapping groups
|
||||
*/
|
||||
|
||||
export type NodeSetSource =
|
||||
| "model-inferred" // Created by AI/ML model
|
||||
| "user-defined" // Manually created by user
|
||||
| "query-result" // Result of a search query
|
||||
| "imported" // From external source
|
||||
| "set-algebra"; // Created by combining other sets
|
||||
|
||||
export type NodeSetStability =
|
||||
| "stable" // Won't change often
|
||||
| "evolving" // Changes gradually
|
||||
| "ephemeral"; // Temporary, will be removed
|
||||
|
||||
export interface NodeSet {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
|
||||
// Required properties
|
||||
nodeIds: string[]; // Member node IDs
|
||||
size: number; // Number of nodes
|
||||
definition: string; // How it was created (e.g., "semantic cluster around 'AI'")
|
||||
stability: NodeSetStability;
|
||||
source: NodeSetSource;
|
||||
lastUpdated: Date;
|
||||
|
||||
// Confidence metrics
|
||||
confidence?: number; // 0-1, how confident we are in this grouping
|
||||
cohesion?: number; // 0-1, how tightly connected members are
|
||||
|
||||
// Set algebra metadata
|
||||
parentSets?: string[]; // If created from other sets
|
||||
operation?: "union" | "intersect" | "diff";
|
||||
|
||||
// Retrieval metadata
|
||||
retrievalScore?: number; // If this set was retrieved
|
||||
retrievalSignals?: string[]; // Why it was retrieved
|
||||
|
||||
// Visual properties (for rendering)
|
||||
color?: string;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set operations
|
||||
*/
|
||||
export function unionSets(sets: NodeSet[], name: string): NodeSet {
|
||||
const allNodeIds = new Set<string>();
|
||||
sets.forEach(set => set.nodeIds.forEach(id => allNodeIds.add(id)));
|
||||
|
||||
return {
|
||||
id: `union_${Date.now()}`,
|
||||
name,
|
||||
nodeIds: Array.from(allNodeIds),
|
||||
size: allNodeIds.size,
|
||||
definition: `Union of: ${sets.map(s => s.name).join(", ")}`,
|
||||
stability: "ephemeral",
|
||||
source: "set-algebra",
|
||||
lastUpdated: new Date(),
|
||||
parentSets: sets.map(s => s.id),
|
||||
operation: "union",
|
||||
};
|
||||
}
|
||||
|
||||
export function intersectSets(sets: NodeSet[], name: string): NodeSet {
|
||||
if (sets.length === 0) {
|
||||
return {
|
||||
id: `intersect_${Date.now()}`,
|
||||
name,
|
||||
nodeIds: [],
|
||||
size: 0,
|
||||
definition: "Empty intersection",
|
||||
stability: "ephemeral",
|
||||
source: "set-algebra",
|
||||
lastUpdated: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
const intersection = new Set(sets[0].nodeIds);
|
||||
sets.slice(1).forEach(set => {
|
||||
const setIds = new Set(set.nodeIds);
|
||||
intersection.forEach(id => {
|
||||
if (!setIds.has(id)) intersection.delete(id);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
id: `intersect_${Date.now()}`,
|
||||
name,
|
||||
nodeIds: Array.from(intersection),
|
||||
size: intersection.size,
|
||||
definition: `Intersection of: ${sets.map(s => s.name).join(", ")}`,
|
||||
stability: "ephemeral",
|
||||
source: "set-algebra",
|
||||
lastUpdated: new Date(),
|
||||
parentSets: sets.map(s => s.id),
|
||||
operation: "intersect",
|
||||
};
|
||||
}
|
||||
|
||||
export function diffSets(setA: NodeSet, setB: NodeSet, name: string): NodeSet {
|
||||
const diff = new Set(setA.nodeIds);
|
||||
setB.nodeIds.forEach(id => diff.delete(id));
|
||||
|
||||
return {
|
||||
id: `diff_${Date.now()}`,
|
||||
name,
|
||||
nodeIds: Array.from(diff),
|
||||
size: diff.size,
|
||||
definition: `${setA.name} minus ${setB.name}`,
|
||||
stability: "ephemeral",
|
||||
source: "set-algebra",
|
||||
lastUpdated: new Date(),
|
||||
parentSets: [setA.id, setB.id],
|
||||
operation: "diff",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieval result with explanation
|
||||
*/
|
||||
export interface RetrievalResult {
|
||||
type: "node" | "nodeSet" | "suggestedSet";
|
||||
|
||||
// For nodes
|
||||
nodeId?: string;
|
||||
nodeLabel?: string;
|
||||
|
||||
// For node sets
|
||||
nodeSet?: NodeSet;
|
||||
|
||||
// For suggested sets
|
||||
suggestedSetDefinition?: string;
|
||||
suggestedNodeIds?: string[];
|
||||
|
||||
// Explanation (critical for trust)
|
||||
why: string; // Human-readable explanation
|
||||
similarityScore: number; // 0-1
|
||||
signals: {
|
||||
name: string; // e.g., "semantic", "recency", "provenance"
|
||||
weight: number; // Contribution to final score
|
||||
value: string | number; // The actual value
|
||||
}[];
|
||||
|
||||
// Confidence
|
||||
confidence: number; // 0-1, how confident we are in this retrieval
|
||||
}
|
||||
|
||||
/**
|
||||
* Recall simulation: "If the agent were asked X, what would be retrieved?"
|
||||
*/
|
||||
export interface RecallSimulation {
|
||||
query: string;
|
||||
rankedMemories: RetrievalResult[];
|
||||
activatedSets: NodeSet[];
|
||||
conflicts?: {
|
||||
nodeId: string;
|
||||
reason: string;
|
||||
conflictingSets: string[];
|
||||
}[];
|
||||
}
|
||||
61
cognee-frontend/src/ui/elements/GraphVisualization.tsx
Normal file
61
cognee-frontend/src/ui/elements/GraphVisualization.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
"use client";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { Edge, Node } from "@/ui/rendering/graph/types";
|
||||
import animate from "@/ui/rendering/animate";
|
||||
|
||||
// IMPROVEMENT #8: Extended config for layered view controls
|
||||
interface GraphVisualizationProps {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
className?: string;
|
||||
config?: {
|
||||
fontSize?: number;
|
||||
showNodes?: boolean; // Toggle node visibility
|
||||
showEdges?: boolean; // Toggle edge/path visibility
|
||||
showMetaballs?: boolean; // Toggle density cloud visibility
|
||||
highlightedNodeIds?: Set<string>; // Nodes to highlight (neutral-by-default)
|
||||
};
|
||||
}
|
||||
|
||||
export default function GraphVisualization({
|
||||
nodes,
|
||||
edges,
|
||||
className,
|
||||
config,
|
||||
}: GraphVisualizationProps) {
|
||||
const visualizationRef = useRef<HTMLDivElement>(null);
|
||||
const cleanupRef = useRef<(() => void) | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const visualizationContainer = visualizationRef.current;
|
||||
|
||||
if (visualizationContainer) {
|
||||
// Clean up previous visualization
|
||||
if (cleanupRef.current) {
|
||||
cleanupRef.current();
|
||||
}
|
||||
|
||||
// Clear the container
|
||||
while (visualizationContainer.firstChild) {
|
||||
visualizationContainer.removeChild(visualizationContainer.firstChild);
|
||||
}
|
||||
|
||||
// Create new visualization
|
||||
const cleanup = animate(nodes, edges, visualizationContainer, config);
|
||||
cleanupRef.current = cleanup;
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cleanupRef.current) {
|
||||
cleanupRef.current();
|
||||
}
|
||||
};
|
||||
}, [config, edges, nodes]);
|
||||
|
||||
return (
|
||||
<div className={classNames("min-w-full min-h-full", className)} ref={visualizationRef} />
|
||||
);
|
||||
}
|
||||
374
cognee-frontend/src/ui/elements/MemoryGraphVisualization.tsx
Normal file
374
cognee-frontend/src/ui/elements/MemoryGraphVisualization.tsx
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/**
|
||||
* Memory Graph Visualization
|
||||
*
|
||||
* Reusable visualization component with retrieval-first features:
|
||||
* - Node set inference and display
|
||||
* - Retrieval search with explanations
|
||||
* - Neutral-by-default highlighting
|
||||
* - Type attributes vs. inferred sets separation
|
||||
*
|
||||
* Works with any graph data (mock or real Cognee API data)
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import GraphVisualization from "@/ui/elements/GraphVisualization";
|
||||
import { Edge, Node } from "@/ui/rendering/graph/types";
|
||||
import { NodeSet } from "@/types/NodeSet";
|
||||
import { inferNodeSets, mockRetrievalSearch } from "@/lib/inferNodeSets";
|
||||
import type { RetrievalResult } from "@/types/NodeSet";
|
||||
|
||||
interface MemoryGraphVisualizationProps {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
title?: string;
|
||||
showControls?: boolean;
|
||||
}
|
||||
|
||||
export default function MemoryGraphVisualization({
|
||||
nodes,
|
||||
edges,
|
||||
title = "Memory Retrieval Debugger",
|
||||
showControls = true,
|
||||
}: MemoryGraphVisualizationProps) {
|
||||
const [showLegend, setShowLegend] = useState(true);
|
||||
|
||||
// Retrieval-first: search replaces static filtering
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [retrievalResults, setRetrievalResults] = useState<RetrievalResult[]>([]);
|
||||
|
||||
// Node sets: primary abstraction
|
||||
const [selectedNodeSet, setSelectedNodeSet] = useState<NodeSet | null>(null);
|
||||
|
||||
// Layer visibility controls
|
||||
const [showNodes, setShowNodes] = useState(true);
|
||||
const [showEdges, setShowEdges] = useState(true);
|
||||
const [showMetaballs, setShowMetaballs] = useState(false);
|
||||
|
||||
// Node Attributes section collapsed by default (secondary concern)
|
||||
const [showNodeAttributes, setShowNodeAttributes] = useState(false);
|
||||
|
||||
// Infer node sets from graph structure (CRITICAL: separate attributes from sets)
|
||||
const { typeAttributes, inferredSets } = useMemo(() => {
|
||||
return inferNodeSets(nodes, edges, {
|
||||
minSetSize: 5,
|
||||
maxSets: 15,
|
||||
});
|
||||
}, [nodes, edges]);
|
||||
|
||||
// Neutral-by-default: only highlight nodes that are selected or retrieved
|
||||
const highlightedNodeIds = useMemo(() => {
|
||||
const ids = new Set<string>();
|
||||
|
||||
// Nodes from retrieval results
|
||||
retrievalResults.forEach(result => {
|
||||
if (result.type === "node" && result.nodeId) {
|
||||
ids.add(result.nodeId);
|
||||
} else if (result.type === "nodeSet" && result.nodeSet) {
|
||||
result.nodeSet.nodeIds.forEach(id => ids.add(id));
|
||||
}
|
||||
});
|
||||
|
||||
// Nodes from selected set
|
||||
if (selectedNodeSet) {
|
||||
selectedNodeSet.nodeIds.forEach(id => ids.add(id));
|
||||
}
|
||||
|
||||
return ids;
|
||||
}, [retrievalResults, selectedNodeSet]);
|
||||
|
||||
// Handle retrieval search
|
||||
const handleSearch = (query: string) => {
|
||||
setSearchQuery(query);
|
||||
if (query.trim()) {
|
||||
const results = mockRetrievalSearch(query, nodes, inferredSets);
|
||||
setRetrievalResults(results);
|
||||
} else {
|
||||
setRetrievalResults([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setSearchQuery("");
|
||||
setRetrievalResults([]);
|
||||
setSelectedNodeSet(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-gradient-to-br from-gray-900 via-black to-purple-900 text-white">
|
||||
{/* Main Visualization */}
|
||||
<div className="flex-1 relative">
|
||||
<GraphVisualization
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
config={{
|
||||
fontSize: 11,
|
||||
showNodes,
|
||||
showEdges,
|
||||
showMetaballs,
|
||||
highlightedNodeIds,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Header */}
|
||||
<div className="absolute top-0 left-0 right-0 p-6 bg-gradient-to-b from-black/90 via-black/50 to-transparent pointer-events-none">
|
||||
<h1 className="text-4xl font-bold mb-2 bg-gradient-to-r from-purple-400 via-pink-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-gray-300">
|
||||
{highlightedNodeIds.size > 0 ? (
|
||||
<>
|
||||
<span className="text-purple-400 font-semibold">{highlightedNodeIds.size}</span> retrieved
|
||||
{" / "}
|
||||
<span className="text-gray-500">{nodes.length.toLocaleString()} total</span>
|
||||
{" • "}
|
||||
<span className="text-sm">{inferredSets.length} inferred sets</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>{nodes.length.toLocaleString()} nodes</span>
|
||||
{" • "}
|
||||
<span>{inferredSets.length} inferred sets</span>
|
||||
{" • "}
|
||||
<span className="text-gray-500">Search to retrieve</span>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{showControls && (
|
||||
<div className="absolute top-6 right-6 flex flex-col gap-3 pointer-events-auto max-w-md">
|
||||
{/* Retrieval Search */}
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="🔍 Retrieve memories... (e.g., 'AI', 'Physics')"
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-black/70 backdrop-blur-md border border-purple-500/30 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-purple-500 focus:ring-2 focus:ring-purple-500/20"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
onClick={() => handleSearch("")}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* View Controls */}
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setShowLegend(!showLegend)}
|
||||
className="flex-1 px-4 py-2 bg-black/70 hover:bg-black/90 backdrop-blur-md rounded-lg border border-purple-500/30 transition-all"
|
||||
>
|
||||
{showLegend ? "Hide" : "Show"} Panel
|
||||
</button>
|
||||
{(searchQuery || selectedNodeSet || retrievalResults.length > 0) && (
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="px-4 py-2 bg-gradient-to-r from-red-600 to-orange-600 hover:from-red-500 hover:to-orange-500 rounded-lg transition-all shadow-lg"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Layer Visibility Controls */}
|
||||
<div className="bg-black/70 backdrop-blur-md rounded-lg p-3 border border-purple-500/30">
|
||||
<div className="text-xs font-semibold text-gray-400 mb-2">Layers</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<button
|
||||
onClick={() => setShowNodes(!showNodes)}
|
||||
className={`flex items-center justify-between px-3 py-2 rounded-lg transition-all text-sm ${
|
||||
showNodes
|
||||
? "bg-purple-600/30 border border-purple-500/50"
|
||||
: "bg-white/5 border border-gray-600/30"
|
||||
}`}
|
||||
>
|
||||
<span>● Nodes</span>
|
||||
<span className="text-xs">{showNodes ? "ON" : "OFF"}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowEdges(!showEdges)}
|
||||
className={`flex items-center justify-between px-3 py-2 rounded-lg transition-all text-sm ${
|
||||
showEdges
|
||||
? "bg-amber-600/30 border border-amber-500/50"
|
||||
: "bg-white/5 border border-gray-600/30"
|
||||
}`}
|
||||
>
|
||||
<span>─ Paths</span>
|
||||
<span className="text-xs">{showEdges ? "ON" : "OFF"}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowMetaballs(!showMetaballs)}
|
||||
className={`flex items-center justify-between px-3 py-2 rounded-lg transition-all text-sm ${
|
||||
showMetaballs
|
||||
? "bg-purple-600/20 border border-purple-500/30"
|
||||
: "bg-white/5 border border-gray-600/30"
|
||||
}`}
|
||||
>
|
||||
<span>◉ Clouds</span>
|
||||
<span className="text-xs">{showMetaballs ? "ON" : "OFF"}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Side Panel */}
|
||||
{showLegend && (
|
||||
<div className="w-96 bg-black/95 backdrop-blur-xl border-l border-purple-500/20 overflow-y-auto">
|
||||
<div className="p-6 space-y-6">
|
||||
{/* Retrieval Results */}
|
||||
{retrievalResults.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
|
||||
Retrieved Memories
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{retrievalResults.slice(0, 10).map((result, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-white/5 hover:bg-white/10 p-3 rounded-lg border border-purple-500/20 transition-all"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2 mb-2">
|
||||
<div className="font-medium text-sm">
|
||||
{result.type === "node" ? result.nodeLabel : result.nodeSet?.name}
|
||||
</div>
|
||||
<div className="text-xs px-2 py-0.5 bg-purple-600/30 rounded">
|
||||
{(result.similarityScore * 100).toFixed(0)}%
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 mb-2">
|
||||
{result.why}
|
||||
</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{result.signals.map((signal, sidx) => (
|
||||
<span
|
||||
key={sidx}
|
||||
className="text-xs px-2 py-0.5 bg-black/30 rounded"
|
||||
>
|
||||
{signal.name}: {(signal.weight * 100).toFixed(0)}%
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Node Attributes - Secondary (NOT sets, just metadata) */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowNodeAttributes(!showNodeAttributes)}
|
||||
className="w-full flex items-center justify-between mb-3 text-left"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-gray-400">
|
||||
Node Attributes {showNodeAttributes ? "▼" : "▶"}
|
||||
</h3>
|
||||
<span className="text-xs text-gray-500">
|
||||
{typeAttributes.size} types
|
||||
</span>
|
||||
</button>
|
||||
{showNodeAttributes && (
|
||||
<div className="space-y-1 mb-6 pl-2">
|
||||
{Array.from(typeAttributes.entries())
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([type, count]) => (
|
||||
<div
|
||||
key={type}
|
||||
className="flex items-center justify-between text-xs px-3 py-1.5 bg-white/5 rounded"
|
||||
>
|
||||
<span className="text-gray-400">type: {type}</span>
|
||||
<span className="text-gray-500">({count})</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Inferred Node Sets - PRIMARY ABSTRACTION */}
|
||||
<div>
|
||||
<h2 className="text-xl font-bold mb-3 flex items-center justify-between">
|
||||
<span className="bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
|
||||
Node Sets
|
||||
</span>
|
||||
{selectedNodeSet && (
|
||||
<button
|
||||
onClick={() => setSelectedNodeSet(null)}
|
||||
className="text-xs px-2 py-1 bg-red-600 hover:bg-red-500 rounded"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
{inferredSets.map((nodeSet) => {
|
||||
const isSelected = selectedNodeSet?.id === nodeSet.id;
|
||||
return (
|
||||
<button
|
||||
key={nodeSet.id}
|
||||
onClick={() => setSelectedNodeSet(isSelected ? null : nodeSet)}
|
||||
className={`w-full text-left p-3 rounded-lg transition-all ${
|
||||
isSelected
|
||||
? "bg-gradient-to-r from-purple-600 to-pink-600 shadow-lg"
|
||||
: "bg-white/5 hover:bg-white/10"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="font-medium text-sm">{nodeSet.name}</div>
|
||||
<div className="text-xs px-2 py-0.5 bg-black/30 rounded">
|
||||
{nodeSet.size}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced explanatory section with explicit semantics */}
|
||||
<div className="pt-6 border-t border-purple-500/20">
|
||||
<h3 className="font-semibold mb-3 text-purple-400">Visual Elements</h3>
|
||||
<div className="space-y-2 text-sm text-gray-400">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-purple-400 mt-0.5 font-bold text-lg">●</span>
|
||||
<div>
|
||||
<strong className="text-gray-300">Node Size = Importance:</strong>
|
||||
<div className="text-xs mt-0.5">Larger = Domain/Field (structural), Smaller = Application (leaf)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-amber-400 mt-0.5">─</span>
|
||||
<div>
|
||||
<strong className="text-gray-300">Paths (zoom to see):</strong>
|
||||
<div className="text-xs mt-0.5">Relationships • Hover node to highlight connections</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-purple-400/40 mt-0.5">◉</span>
|
||||
<div>
|
||||
<strong className="text-gray-300">Background Clouds:</strong>
|
||||
<div className="text-xs mt-0.5">Conceptual Density • Visible at far zoom for cluster overview</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-cyan-400/60 mt-0.5">○</span>
|
||||
<div>
|
||||
<strong className="text-gray-300">Boundary Rings:</strong>
|
||||
<div className="text-xs mt-0.5">Type Clusters • Spatial grouping by semantic category</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import GraphVisualization, { GraphVisualizationAPI } from "@/app/(graph)/GraphVi
|
|||
import NotebookCellHeader from "./NotebookCellHeader";
|
||||
import MarkdownPreview from "./MarkdownPreview";
|
||||
import { Cell, Notebook as NotebookType } from "./types";
|
||||
import GraphVisualization from "../GraphVisualization";
|
||||
|
||||
interface NotebookProps {
|
||||
notebook: NotebookType;
|
||||
|
|
@ -371,14 +372,18 @@ function CellResult({ content }: { content: [] }) {
|
|||
if (Array.isArray(line)) {
|
||||
// Insights search returns uncommon graph data structure
|
||||
if (Array.from(line).length > 0 && Array.isArray(line[0]) && line[0][1]["relationship_name"]) {
|
||||
const data = transformInsightsGraphData(line);
|
||||
|
||||
parsedContent.push(
|
||||
<div key={line[0][1]["relationship_name"]} className="w-full h-full bg-white">
|
||||
<div key={line[0][1]["relationship_name"]} className="flex flex-col w-full h-full min-h-80 bg-white">
|
||||
<span className="text-sm pl-2 mb-4">reasoning graph</span>
|
||||
<GraphVisualization
|
||||
data={transformInsightsGraphData(line)}
|
||||
ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
|
||||
graphControls={graphControls}
|
||||
className="min-h-80"
|
||||
nodes={data.nodes}
|
||||
edges={data.edges}
|
||||
className="flex-1"
|
||||
config={{
|
||||
fontSize: 24,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -420,13 +425,15 @@ function CellResult({ content }: { content: [] }) {
|
|||
if (typeof item === "object" && item["graphs"] && typeof item["graphs"] === "object") {
|
||||
Object.entries<{ nodes: []; edges: []; }>(item["graphs"]).forEach(([datasetName, graph]) => {
|
||||
parsedContent.push(
|
||||
<div key={datasetName} className="w-full h-full bg-white">
|
||||
<div key={datasetName} className="flex flex-col w-full h-full min-h-80 bg-white">
|
||||
<span className="text-sm pl-2 mb-4">reasoning graph (datasets: {datasetName})</span>
|
||||
<GraphVisualization
|
||||
data={transformToVisualizationData(graph)}
|
||||
ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
|
||||
graphControls={graphControls}
|
||||
className="min-h-80"
|
||||
nodes={graph.nodes}
|
||||
edges={graph.edges}
|
||||
className="flex-1"
|
||||
config={{
|
||||
fontSize: 24,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -501,13 +508,6 @@ function CellResult({ content }: { content: [] }) {
|
|||
));
|
||||
};
|
||||
|
||||
function transformToVisualizationData(graph: { nodes: [], edges: [] }) {
|
||||
return {
|
||||
nodes: graph.nodes,
|
||||
links: graph.edges,
|
||||
};
|
||||
}
|
||||
|
||||
type Triplet = [{
|
||||
id: string,
|
||||
name: string,
|
||||
|
|
@ -528,8 +528,9 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
|||
type: string,
|
||||
}
|
||||
} = {};
|
||||
const links: {
|
||||
const edges: {
|
||||
[key: string]: {
|
||||
id: string,
|
||||
source: string,
|
||||
target: string,
|
||||
label: string,
|
||||
|
|
@ -548,7 +549,8 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
|||
type: triplet[2].type,
|
||||
};
|
||||
const linkKey = `${triplet[0]["id"]}_${triplet[1]["relationship_name"]}_${triplet[2]["id"]}`;
|
||||
links[linkKey] = {
|
||||
edges[linkKey] = {
|
||||
id: linkKey,
|
||||
source: triplet[0].id,
|
||||
target: triplet[2].id,
|
||||
label: triplet[1]["relationship_name"],
|
||||
|
|
@ -557,6 +559,6 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
|||
|
||||
return {
|
||||
nodes: Object.values(nodes),
|
||||
links: Object.values(links),
|
||||
edges: Object.values(edges),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
643
cognee-frontend/src/ui/rendering/animate.ts
Normal file
643
cognee-frontend/src/ui/rendering/animate.ts
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
import { Graph, Node as GraphNode, Link as GraphLink } from "ngraph.graph";
|
||||
import * as three from "three";
|
||||
import {
|
||||
Color,
|
||||
DataTexture,
|
||||
OrthographicCamera,
|
||||
RGBAFormat,
|
||||
Scene,
|
||||
UnsignedByteType,
|
||||
Vector2,
|
||||
WebGLRenderer,
|
||||
WebGLRenderTarget,
|
||||
} from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/Addons.js";
|
||||
import createForceLayout, { Layout } from "ngraph.forcelayout";
|
||||
|
||||
import { Edge, Node } from "./graph/types";
|
||||
import createGraph from "./graph/createGraph";
|
||||
|
||||
import createLabel from "./meshes/createLabel";
|
||||
import pickNodeIndex from "./picking/pickNodeIndex";
|
||||
import createEdgeMesh from "./meshes/createEdgeMesh";
|
||||
import createPickingMesh from "./meshes/createPickingMesh";
|
||||
import createNodeSwarmMesh from "./meshes/createNodeSwarmMesh";
|
||||
import createNodePositionsTexture from "./textures/createNodePositionsTexture";
|
||||
import createDensityRenderTarget from "./render-targets/createDensityRenderTarget";
|
||||
import createDensityAccumulatorMesh from "./meshes/createDensityAccumulatorMesh";
|
||||
import createMetaballMesh from "./meshes/createMetaballMesh";
|
||||
import createClusterBoundaryMesh, { ClusterInfo } from "./meshes/createClusterBoundaryMesh";
|
||||
|
||||
const INITIAL_CAMERA_DISTANCE = 2000;
|
||||
|
||||
// Extended config for layered view controls + zoom semantics
|
||||
interface Config {
|
||||
fontSize?: number;
|
||||
showNodes?: boolean;
|
||||
showEdges?: boolean;
|
||||
showMetaballs?: boolean;
|
||||
pathFilterMode?: "all" | "hoverOnly" | "strongOnly"; // Path filtering
|
||||
zoomLevel?: "far" | "mid" | "near"; // Zoom-based semantics
|
||||
highlightedNodeIds?: Set<string>; // Nodes to highlight (neutral-by-default)
|
||||
}
|
||||
|
||||
export default function animate(
|
||||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
parentElement: HTMLElement,
|
||||
config?: Config
|
||||
): () => void {
|
||||
const nodeLabelMap = new Map();
|
||||
const edgeLabelMap = new Map();
|
||||
|
||||
// Semantic color encoding: hierarchy drives saturation + brightness
|
||||
const typeColorMap: Record<string, string> = {
|
||||
"Domain": "#C4B5FD", // Bright Purple - Highest importance
|
||||
"Field": "#67E8F9", // Bright Cyan - High importance
|
||||
"Subfield": "#A78BFA", // Medium Purple - Medium-high importance
|
||||
"Concept": "#5EEAD4", // Teal - Medium importance
|
||||
"Method": "#6EE7B7", // Green - Medium importance
|
||||
"Theory": "#F9A8D4", // Pink - Medium importance
|
||||
"Technology": "#FCA5A5", // Soft Red - Lower importance
|
||||
"Application": "#71717A", // Desaturated Gray - Background/lowest importance
|
||||
};
|
||||
|
||||
// Size hierarchy: more important = larger
|
||||
const typeSizeMap: Record<string, number> = {
|
||||
"Domain": 2.5, // Largest
|
||||
"Field": 2.0,
|
||||
"Subfield": 1.6,
|
||||
"Concept": 1.2,
|
||||
"Method": 1.1,
|
||||
"Theory": 1.0,
|
||||
"Technology": 0.9,
|
||||
"Application": 0.6, // Smallest
|
||||
};
|
||||
|
||||
function getColorForType(nodeType: string): Color {
|
||||
const colorHex = typeColorMap[nodeType];
|
||||
if (colorHex) {
|
||||
return new Color(colorHex);
|
||||
}
|
||||
// Fallback for unknown types
|
||||
return new Color("#9CA3AF"); // Gray for unknown types
|
||||
}
|
||||
|
||||
const mousePosition = new Vector2();
|
||||
|
||||
// Node related data
|
||||
const nodeColors = new Float32Array(nodes.length * 3);
|
||||
const nodeSizes = new Float32Array(nodes.length); // Size per node for hierarchy
|
||||
const nodeHighlights = new Float32Array(nodes.length); // 1.0 = highlighted, 0.3 = dimmed
|
||||
const nodeIndices = new Map();
|
||||
const textureSize = Math.ceil(Math.sqrt(nodes.length));
|
||||
const nodePositionsData = new Float32Array(textureSize * textureSize * 4);
|
||||
|
||||
// Determine which nodes are highlighted
|
||||
const highlightedIds = config?.highlightedNodeIds;
|
||||
const hasHighlights = highlightedIds && highlightedIds.size > 0;
|
||||
|
||||
let nodeIndex = 0;
|
||||
function forNode(node: Node) {
|
||||
const color = getColorForType(node.type);
|
||||
nodeColors[nodeIndex * 3 + 0] = color.r;
|
||||
nodeColors[nodeIndex * 3 + 1] = color.g;
|
||||
nodeColors[nodeIndex * 3 + 2] = color.b;
|
||||
|
||||
// Set highlight state: if no highlights, all at 1.0; if highlights exist, dim non-highlighted
|
||||
if (hasHighlights) {
|
||||
nodeHighlights[nodeIndex] = highlightedIds!.has(node.id) ? 1.0 : 0.3;
|
||||
} else {
|
||||
nodeHighlights[nodeIndex] = 1.0; // All visible when no highlights
|
||||
}
|
||||
|
||||
// Store size multiplier based on type
|
||||
nodeSizes[nodeIndex] = typeSizeMap[node.type] || 1.0;
|
||||
|
||||
nodePositionsData[nodeIndex * 4 + 0] = 0.0;
|
||||
nodePositionsData[nodeIndex * 4 + 1] = 0.0;
|
||||
nodePositionsData[nodeIndex * 4 + 2] = 0.0;
|
||||
nodePositionsData[nodeIndex * 4 + 3] = 1.0;
|
||||
|
||||
nodeIndices.set(node.id, nodeIndex);
|
||||
|
||||
nodeIndex += 1;
|
||||
}
|
||||
|
||||
// Node related data
|
||||
const edgeIndices = new Float32Array(edges.length * 2);
|
||||
|
||||
let edgeIndex = 0;
|
||||
function forEdge(edge: Edge) {
|
||||
const fromIndex = nodeIndices.get(edge.source);
|
||||
const toIndex = nodeIndices.get(edge.target);
|
||||
edgeIndices[edgeIndex * 2 + 0] = fromIndex;
|
||||
edgeIndices[edgeIndex * 2 + 1] = toIndex;
|
||||
|
||||
edgeIndex += 1;
|
||||
}
|
||||
|
||||
// Graph creation and layout
|
||||
const graph = createGraph(nodes, edges, forNode, forEdge);
|
||||
|
||||
// Adaptive layout parameters based on graph size
|
||||
const nodeCount = nodes.length;
|
||||
const isLargeGraph = nodeCount > 5000;
|
||||
const isMassiveGraph = nodeCount > 15000;
|
||||
|
||||
// Apple embedding atlas style: stronger repulsion for clear cluster separation
|
||||
const graphLayout = createForceLayout(graph, {
|
||||
dragCoefficient: isMassiveGraph ? 0.95 : 0.85,
|
||||
springLength: isMassiveGraph ? 120 : isLargeGraph ? 180 : 220, // Longer springs for spacing
|
||||
springCoefficient: isMassiveGraph ? 0.12 : isLargeGraph ? 0.15 : 0.18, // Weaker springs
|
||||
gravity: isMassiveGraph ? -1200 : isLargeGraph ? -1500 : -1800, // Stronger repulsion
|
||||
});
|
||||
|
||||
// Node Mesh
|
||||
const nodePositionsTexture = createNodePositionsTexture(
|
||||
nodes,
|
||||
nodePositionsData
|
||||
);
|
||||
|
||||
const nodeSwarmMesh = createNodeSwarmMesh(
|
||||
nodes,
|
||||
nodePositionsTexture,
|
||||
nodeColors,
|
||||
nodeSizes,
|
||||
nodeHighlights,
|
||||
INITIAL_CAMERA_DISTANCE
|
||||
);
|
||||
|
||||
const edgeMesh = createEdgeMesh(
|
||||
edges,
|
||||
nodePositionsTexture,
|
||||
edgeIndices,
|
||||
INITIAL_CAMERA_DISTANCE
|
||||
);
|
||||
|
||||
// Density cloud setup - adaptive resolution for performance
|
||||
const densityCloudScene = new Scene();
|
||||
const densityResolution = isMassiveGraph ? 256 : isLargeGraph ? 384 : 512;
|
||||
const densityCloudTarget = createDensityRenderTarget(densityResolution);
|
||||
|
||||
const densityAccumulatorMesh = createDensityAccumulatorMesh(
|
||||
nodes,
|
||||
nodeColors,
|
||||
nodePositionsTexture,
|
||||
INITIAL_CAMERA_DISTANCE
|
||||
);
|
||||
|
||||
const metaballMesh = createMetaballMesh(densityCloudTarget);
|
||||
|
||||
// const densityCloudDebugMesh = createDebugViewMesh(densityCloudTarget);
|
||||
// Density cloud setup end
|
||||
|
||||
let pickedNodeIndex = -1;
|
||||
const lastPickedNodeIndex = -1;
|
||||
const pickNodeFromScene = (event: unknown) => {
|
||||
pickedNodeIndex = pickNodeIndexFromScene(event as MouseEvent);
|
||||
};
|
||||
|
||||
parentElement.addEventListener("mousemove", (event) => {
|
||||
const rect = parentElement.getBoundingClientRect();
|
||||
mousePosition.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mousePosition.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
|
||||
pickNodeFromScene(event);
|
||||
});
|
||||
|
||||
// Group nodes by type for cluster boundaries
|
||||
const nodesByType = new Map<string, Node[]>();
|
||||
nodes.forEach(node => {
|
||||
if (!nodesByType.has(node.type)) {
|
||||
nodesByType.set(node.type, []);
|
||||
}
|
||||
nodesByType.get(node.type)!.push(node);
|
||||
});
|
||||
|
||||
const scene = new Scene();
|
||||
// Apple embedding atlas style: pure black background
|
||||
scene.background = new Color("#000000");
|
||||
const renderer = new WebGLRenderer({ antialias: true });
|
||||
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(parentElement.clientWidth, parentElement.clientHeight);
|
||||
if (parentElement.children.length === 0) {
|
||||
parentElement.appendChild(renderer.domElement);
|
||||
}
|
||||
|
||||
// Setup camera
|
||||
const aspect = parentElement.clientWidth / parentElement.clientHeight;
|
||||
const frustumSize = INITIAL_CAMERA_DISTANCE;
|
||||
|
||||
const camera = new OrthographicCamera(
|
||||
(-frustumSize * aspect) / 2,
|
||||
(frustumSize * aspect) / 2,
|
||||
frustumSize / 2,
|
||||
-frustumSize / 2,
|
||||
1,
|
||||
5000
|
||||
);
|
||||
|
||||
camera.position.set(0, 0, INITIAL_CAMERA_DISTANCE);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
// Setup controls
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableRotate = false;
|
||||
controls.enablePan = true;
|
||||
controls.enableZoom = true;
|
||||
controls.screenSpacePanning = true;
|
||||
controls.minZoom = 0.5; // Allow zooming out more
|
||||
controls.maxZoom = 6; // Allow closer zoom for detail
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.08; // Smoother, more fluid motion
|
||||
controls.target.set(0, 0, 0);
|
||||
|
||||
controls.update();
|
||||
|
||||
// Handle resizing
|
||||
window.addEventListener("resize", () => {
|
||||
const aspect = parentElement.clientWidth / parentElement.clientHeight;
|
||||
camera.left = (-frustumSize * aspect) / 2;
|
||||
camera.right = (frustumSize * aspect) / 2;
|
||||
camera.top = frustumSize / 2;
|
||||
camera.bottom = -frustumSize / 2;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(parentElement.clientWidth, parentElement.clientHeight);
|
||||
});
|
||||
|
||||
// Node picking setup
|
||||
const pickingTarget = new WebGLRenderTarget(
|
||||
window.innerWidth,
|
||||
window.innerHeight,
|
||||
{
|
||||
format: RGBAFormat,
|
||||
type: UnsignedByteType,
|
||||
depthBuffer: true,
|
||||
stencilBuffer: false,
|
||||
}
|
||||
);
|
||||
const pickingScene = new Scene();
|
||||
|
||||
function pickNodeIndexFromScene(event: MouseEvent): number {
|
||||
pickingScene.add(pickingMesh);
|
||||
|
||||
const pickedNodeIndex = pickNodeIndex(
|
||||
event,
|
||||
renderer,
|
||||
pickingScene,
|
||||
camera,
|
||||
pickingTarget
|
||||
);
|
||||
|
||||
return pickedNodeIndex;
|
||||
}
|
||||
|
||||
const pickingMesh = createPickingMesh(
|
||||
nodes,
|
||||
nodePositionsTexture,
|
||||
nodeColors,
|
||||
INITIAL_CAMERA_DISTANCE
|
||||
);
|
||||
|
||||
renderer.domElement.addEventListener("mousedown", (event) => {
|
||||
const pickedNodeIndex = pickNodeIndexFromScene(event);
|
||||
console.log("Picked node index: ", pickedNodeIndex);
|
||||
});
|
||||
// Node picking setup end
|
||||
|
||||
// Adaptive layout iterations based on graph size
|
||||
const layoutIterations = isMassiveGraph ? 300 : isLargeGraph ? 500 : 800;
|
||||
console.log(`Running ${layoutIterations} layout iterations for ${nodeCount} nodes...`);
|
||||
|
||||
for (let i = 0; i < layoutIterations; i++) {
|
||||
graphLayout.step();
|
||||
|
||||
// Progress logging for large graphs
|
||||
if (isMassiveGraph && i % 50 === 0) {
|
||||
console.log(`Layout progress: ${((i / layoutIterations) * 100).toFixed(0)}%`);
|
||||
}
|
||||
}
|
||||
console.log("Layout complete!");
|
||||
|
||||
let visibleLabels: unknown[] = [];
|
||||
|
||||
// Only create entity type labels for smaller graphs (performance optimization)
|
||||
const entityTypeLabels: [string, unknown][] = [];
|
||||
if (!isMassiveGraph) {
|
||||
for (const node of nodes) {
|
||||
if (node.type === "EntityType") {
|
||||
const label = createLabel(node.label, config?.fontSize);
|
||||
entityTypeLabels.push([node.id, label]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const processingStep = 0;
|
||||
|
||||
// Performance monitoring
|
||||
let frameCount = 0;
|
||||
let lastFpsUpdate = performance.now();
|
||||
let currentFps = 60;
|
||||
|
||||
// Cluster boundaries
|
||||
let clusterBoundariesCreated = false;
|
||||
const clusterBoundaryMeshes: three.Mesh[] = [];
|
||||
|
||||
function calculateClusterBoundaries(): ClusterInfo[] {
|
||||
const clusters: ClusterInfo[] = [];
|
||||
|
||||
nodesByType.forEach((typeNodes, nodeType) => {
|
||||
if (typeNodes.length < 3) return; // Skip small clusters
|
||||
|
||||
// Calculate center and radius from actual node positions
|
||||
let sumX = 0;
|
||||
let sumY = 0;
|
||||
typeNodes.forEach(node => {
|
||||
const pos = graphLayout.getNodePosition(node.id);
|
||||
sumX += pos.x;
|
||||
sumY += pos.y;
|
||||
});
|
||||
|
||||
const center = {
|
||||
x: sumX / typeNodes.length,
|
||||
y: sumY / typeNodes.length
|
||||
};
|
||||
|
||||
// Calculate radius as max distance from center + padding
|
||||
let maxDist = 0;
|
||||
typeNodes.forEach(node => {
|
||||
const pos = graphLayout.getNodePosition(node.id);
|
||||
const dist = Math.sqrt(
|
||||
Math.pow(pos.x - center.x, 2) + Math.pow(pos.y - center.y, 2)
|
||||
);
|
||||
maxDist = Math.max(maxDist, dist);
|
||||
});
|
||||
|
||||
clusters.push({
|
||||
center,
|
||||
radius: maxDist + 350, // Apple style: more spacing between clusters
|
||||
color: getColorForType(nodeType)
|
||||
});
|
||||
});
|
||||
|
||||
return clusters;
|
||||
}
|
||||
|
||||
// Render loop
|
||||
function render() {
|
||||
// Adaptive physics updates - skip for large graphs after stabilization
|
||||
if (!isMassiveGraph || frameCount < 100) {
|
||||
graphLayout.step();
|
||||
} else if (frameCount % 2 === 0) {
|
||||
// Update physics every other frame for massive graphs
|
||||
graphLayout.step();
|
||||
}
|
||||
|
||||
// FPS monitoring
|
||||
frameCount++;
|
||||
const now = performance.now();
|
||||
if (now - lastFpsUpdate > 1000) {
|
||||
currentFps = Math.round((frameCount * 1000) / (now - lastFpsUpdate));
|
||||
frameCount = 0;
|
||||
lastFpsUpdate = now;
|
||||
if (isMassiveGraph && currentFps < 30) {
|
||||
console.warn(`Low FPS detected: ${currentFps} fps`);
|
||||
}
|
||||
}
|
||||
|
||||
controls.update();
|
||||
|
||||
// Create cluster boundaries after layout stabilizes
|
||||
if (!clusterBoundariesCreated && frameCount === 60) {
|
||||
const clusters = calculateClusterBoundaries();
|
||||
clusters.forEach(cluster => {
|
||||
const mesh = createClusterBoundaryMesh(cluster);
|
||||
clusterBoundaryMeshes.push(mesh);
|
||||
scene.add(mesh);
|
||||
});
|
||||
clusterBoundariesCreated = true;
|
||||
}
|
||||
|
||||
updateNodePositions(
|
||||
nodes,
|
||||
graphLayout,
|
||||
nodePositionsData,
|
||||
nodePositionsTexture
|
||||
);
|
||||
const textScale = Math.max(1, 4 / camera.zoom);
|
||||
|
||||
nodeSwarmMesh.material.uniforms.camDist.value = Math.floor(
|
||||
camera.zoom * 500
|
||||
);
|
||||
nodeSwarmMesh.material.uniforms.mousePos.value.set(
|
||||
mousePosition.x,
|
||||
mousePosition.y
|
||||
);
|
||||
// @ts-expect-error uniforms does exist on material
|
||||
edgeMesh.material.uniforms.camDist.value = Math.floor(camera.zoom * 500);
|
||||
// @ts-expect-error uniforms does exist on material
|
||||
edgeMesh.material.uniforms.mousePos.value.set(
|
||||
mousePosition.x,
|
||||
mousePosition.y
|
||||
);
|
||||
|
||||
// @ts-expect-error uniforms does exist on material
|
||||
pickingMesh.material.uniforms.camDist.value = Math.floor(camera.zoom * 500);
|
||||
|
||||
// Zoom-level semantics: determine what to show based on zoom
|
||||
let zoomLevel: "far" | "mid" | "near" = "mid";
|
||||
if (camera.zoom < 1.0) {
|
||||
zoomLevel = "far"; // Show clusters, domains, density
|
||||
} else if (camera.zoom > 3.0) {
|
||||
zoomLevel = "near"; // Show applications, paths, labels
|
||||
}
|
||||
|
||||
edgeMesh.renderOrder = 1;
|
||||
nodeSwarmMesh.renderOrder = 2;
|
||||
|
||||
// IMPROVEMENT #8: Conditional layer rendering based on config and zoom
|
||||
const showEdges = config?.showEdges !== false && zoomLevel !== "far"; // Hide edges when far
|
||||
const showNodes = config?.showNodes !== false; // Always show nodes
|
||||
const showMetaballs = config?.showMetaballs !== false && zoomLevel === "far"; // Only show at far zoom
|
||||
|
||||
// Path filtering based on hover
|
||||
const pathFilterMode = config?.pathFilterMode || "all";
|
||||
const shouldShowPath = pathFilterMode === "all" || (pathFilterMode === "hoverOnly" && pickedNodeIndex >= 0);
|
||||
|
||||
if (showEdges) {
|
||||
scene.add(edgeMesh);
|
||||
}
|
||||
if (showNodes) {
|
||||
scene.add(nodeSwarmMesh);
|
||||
}
|
||||
|
||||
// Metaball rendering - reduce frequency for massive graphs
|
||||
const shouldRenderMetaballs = showMetaballs && (!isMassiveGraph || frameCount % 2 === 0);
|
||||
|
||||
if (shouldRenderMetaballs) {
|
||||
// Pass 1: draw points into density texture
|
||||
renderer.setRenderTarget(densityCloudTarget);
|
||||
renderer.clear();
|
||||
densityCloudScene.clear();
|
||||
densityCloudScene.add(densityAccumulatorMesh);
|
||||
renderer.render(densityCloudScene, camera);
|
||||
|
||||
// Pass 2: render density map to screen
|
||||
renderer.setRenderTarget(null);
|
||||
renderer.clear();
|
||||
metaballMesh.renderOrder = 0;
|
||||
scene.add(metaballMesh);
|
||||
} else {
|
||||
renderer.setRenderTarget(null);
|
||||
renderer.clear();
|
||||
}
|
||||
|
||||
for (const [nodeId, label] of entityTypeLabels) {
|
||||
const nodePosition = graphLayout.getNodePosition(nodeId);
|
||||
// @ts-expect-error label is Text from troika-three-text
|
||||
label.position.set(nodePosition.x, nodePosition.y, 1.0);
|
||||
// @ts-expect-error label is Text from troika-three-text
|
||||
label.scale.setScalar(textScale);
|
||||
// @ts-expect-error label is Text from troika-three-text
|
||||
scene.add(label);
|
||||
}
|
||||
|
||||
if (pickedNodeIndex >= 0) {
|
||||
if (pickedNodeIndex !== lastPickedNodeIndex) {
|
||||
for (const label of visibleLabels) {
|
||||
// @ts-expect-error label is Text from troika-three-text
|
||||
label.visible = false;
|
||||
}
|
||||
visibleLabels = [];
|
||||
}
|
||||
|
||||
const pickedNode = nodes[pickedNodeIndex];
|
||||
|
||||
parentElement.style.cursor = "pointer";
|
||||
|
||||
const pickedNodePosition = graphLayout.getNodePosition(pickedNode.id);
|
||||
|
||||
let pickedNodeLabel = nodeLabelMap.get(pickedNode.id);
|
||||
if (!pickedNodeLabel) {
|
||||
pickedNodeLabel = createLabel(pickedNode.label, config?.fontSize);
|
||||
nodeLabelMap.set(pickedNode.id, pickedNodeLabel);
|
||||
}
|
||||
pickedNodeLabel.position.set(
|
||||
pickedNodePosition.x,
|
||||
pickedNodePosition.y,
|
||||
1.0
|
||||
);
|
||||
pickedNodeLabel.scale.setScalar(textScale);
|
||||
|
||||
// Adaptive label display based on graph size and zoom
|
||||
const minZoomForLabels = isMassiveGraph ? 4 : isLargeGraph ? 3 : 2;
|
||||
const maxLabels = isMassiveGraph ? 5 : isLargeGraph ? 10 : 15;
|
||||
|
||||
if (camera.zoom > minZoomForLabels) {
|
||||
graph.forEachLinkedNode(
|
||||
pickedNode.id,
|
||||
(otherNode: GraphNode, edge: GraphLink) => {
|
||||
if (visibleLabels.length > maxLabels) {
|
||||
return;
|
||||
}
|
||||
|
||||
let otherNodeLabel = nodeLabelMap.get(otherNode.id);
|
||||
if (!otherNodeLabel) {
|
||||
otherNodeLabel = createLabel(otherNode.data.label, config?.fontSize);
|
||||
nodeLabelMap.set(otherNode.id, otherNodeLabel);
|
||||
}
|
||||
const otherNodePosition = graphLayout.getNodePosition(otherNode.id);
|
||||
otherNodeLabel.position.set(
|
||||
otherNodePosition.x,
|
||||
otherNodePosition.y,
|
||||
1.0
|
||||
);
|
||||
|
||||
let linkLabel = edgeLabelMap.get(edge.id);
|
||||
if (!linkLabel) {
|
||||
linkLabel = createLabel(edge.data.label, config?.fontSize);
|
||||
edgeLabelMap.set(edge.id, linkLabel);
|
||||
}
|
||||
const linkPosition = graphLayout.getLinkPosition(edge.id);
|
||||
const middleLinkPosition = new Vector2(
|
||||
(linkPosition.from.x + linkPosition.to.x) / 2,
|
||||
(linkPosition.from.y + linkPosition.to.y) / 2
|
||||
);
|
||||
linkLabel.position.set(
|
||||
middleLinkPosition.x,
|
||||
middleLinkPosition.y,
|
||||
1.0
|
||||
);
|
||||
|
||||
linkLabel.visible = true;
|
||||
linkLabel.scale.setScalar(textScale);
|
||||
visibleLabels.push(linkLabel);
|
||||
otherNodeLabel.visible = true;
|
||||
otherNodeLabel.scale.setScalar(textScale);
|
||||
visibleLabels.push(otherNodeLabel);
|
||||
|
||||
scene.add(linkLabel);
|
||||
scene.add(otherNodeLabel);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pickedNodeLabel.visible = true;
|
||||
visibleLabels.push(pickedNodeLabel);
|
||||
|
||||
scene.add(pickedNodeLabel);
|
||||
} else {
|
||||
parentElement.style.cursor = "default";
|
||||
|
||||
for (const label of visibleLabels) {
|
||||
// @ts-expect-error label is Text from troika-three-text
|
||||
label.visible = false;
|
||||
}
|
||||
|
||||
visibleLabels = [];
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
|
||||
animationFrameId = requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
let animationFrameId: number;
|
||||
render();
|
||||
|
||||
// Return cleanup function
|
||||
return () => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
// Clean up cluster boundaries
|
||||
clusterBoundaryMeshes.forEach(mesh => {
|
||||
scene.remove(mesh);
|
||||
mesh.geometry.dispose();
|
||||
if (mesh.material instanceof three.Material) {
|
||||
mesh.material.dispose();
|
||||
}
|
||||
});
|
||||
graphLayout.dispose();
|
||||
renderer.dispose();
|
||||
controls.dispose();
|
||||
};
|
||||
}
|
||||
|
||||
function updateNodePositions(
|
||||
nodes: Node[],
|
||||
graphLayout: Layout<Graph>,
|
||||
nodePositionsData: Float32Array,
|
||||
nodePositionsTexture: DataTexture
|
||||
) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const p = graphLayout.getNodePosition(nodes[i].id);
|
||||
nodePositionsData[i * 4 + 0] = p.x;
|
||||
nodePositionsData[i * 4 + 1] = p.y;
|
||||
nodePositionsData[i * 4 + 2] = 0.0;
|
||||
nodePositionsData[i * 4 + 3] = 1.0;
|
||||
}
|
||||
nodePositionsTexture.needsUpdate = true;
|
||||
}
|
||||
28
cognee-frontend/src/ui/rendering/graph/createGraph.ts
Normal file
28
cognee-frontend/src/ui/rendering/graph/createGraph.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import createNgraph, { Graph } from "ngraph.graph";
|
||||
import { Edge, Node } from "./types";
|
||||
|
||||
export default function createGraph(
|
||||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
forNode?: (node: Node) => void,
|
||||
forEdge?: (node: Edge) => void
|
||||
): Graph {
|
||||
const graph = createNgraph();
|
||||
|
||||
for (const node of nodes) {
|
||||
graph.addNode(node.id, {
|
||||
id: node.id,
|
||||
label: node.label,
|
||||
});
|
||||
forNode?.(node);
|
||||
}
|
||||
for (const edge of edges) {
|
||||
graph.addLink(edge.source, edge.target, {
|
||||
id: edge.id,
|
||||
label: edge.label,
|
||||
});
|
||||
forEdge?.(edge);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
12
cognee-frontend/src/ui/rendering/graph/types.ts
Normal file
12
cognee-frontend/src/ui/rendering/graph/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface Node {
|
||||
id: string;
|
||||
label: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface Edge {
|
||||
id: string;
|
||||
label: string;
|
||||
source: string;
|
||||
target: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import { ShaderMaterial, Texture, Vector2 } from "three";
|
||||
|
||||
export function createBlurPassMaterial(
|
||||
texture: Texture,
|
||||
direction = new Vector2(1.0, 0.0)
|
||||
) {
|
||||
return new ShaderMaterial({
|
||||
uniforms: {
|
||||
densityTex: { value: texture },
|
||||
direction: { value: direction }, // (1,0) = horizontal, (0,1) = vertical
|
||||
texSize: { value: new Vector2(512, 512) },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position.xy, 0.0, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
uniform sampler2D densityTex;
|
||||
uniform vec2 direction;
|
||||
uniform vec2 texSize;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vec2 texel = direction / texSize;
|
||||
float kernel[5];
|
||||
kernel[0] = 0.204164;
|
||||
kernel[1] = 0.304005;
|
||||
kernel[2] = 0.193783;
|
||||
kernel[3] = 0.072184;
|
||||
kernel[4] = 0.025864;
|
||||
|
||||
vec4 sum = texture2D(densityTex, vUv) * kernel[0];
|
||||
for (int i = 1; i < 5; i++) {
|
||||
sum += texture2D(densityTex, vUv + texel * float(i)) * kernel[i];
|
||||
sum += texture2D(densityTex, vUv - texel * float(i)) * kernel[i];
|
||||
}
|
||||
|
||||
gl_FragColor = sum;
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import * as three from "three";
|
||||
|
||||
export default function createClusterBoundaryMaterial(
|
||||
clusterColor: three.Color
|
||||
): three.ShaderMaterial {
|
||||
const material = new three.ShaderMaterial({
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
side: three.DoubleSide,
|
||||
uniforms: {
|
||||
clusterColor: { value: clusterColor },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
uniform vec3 clusterColor;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
// Apple embedding atlas style: soft circular regions
|
||||
vec2 center = vec2(0.5, 0.5);
|
||||
float dist = length(vUv - center);
|
||||
|
||||
// Soft radial gradient background
|
||||
float alpha = smoothstep(0.5, 0.25, dist) * 0.12; // More visible background
|
||||
|
||||
// Prominent boundary ring (Apple style)
|
||||
float ring = smoothstep(0.49, 0.47, dist) - smoothstep(0.51, 0.49, dist);
|
||||
alpha += ring * 0.25; // More prominent border
|
||||
|
||||
// Lighter, more vibrant colors for Apple aesthetic
|
||||
vec3 bgColor = clusterColor * 1.1;
|
||||
|
||||
gl_FragColor = vec4(bgColor, alpha);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
return material;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { ShaderMaterial, Texture } from "three";
|
||||
|
||||
export function createDebugViewMaterial(fieldTexture: Texture) {
|
||||
return new ShaderMaterial({
|
||||
uniforms: {
|
||||
fieldTex: { value: fieldTexture },
|
||||
},
|
||||
vertexShader: `
|
||||
// void main() {
|
||||
// gl_Position = vec4(position, 1.0);
|
||||
// }
|
||||
varying vec2 vUv;
|
||||
void main() { vUv = uv; gl_Position = vec4(position.xy, 0.0, 1.0); }
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform sampler2D fieldTex;
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
// gl_FragColor = texture2D(fieldTex, vUv);
|
||||
|
||||
float field = texture2D(fieldTex, vUv).r;
|
||||
field = pow(field * 2.0, 0.5); // optional tone mapping
|
||||
gl_FragColor = vec4(vec3(field), 1.0);
|
||||
}
|
||||
|
||||
// precision highp float;
|
||||
// uniform sampler2D fieldTex;
|
||||
|
||||
// void main() {
|
||||
// vec2 uv = gl_FragCoord.xy / vec2(textureSize(fieldTex, 0));
|
||||
// float field = texture2D(fieldTex, uv).r;
|
||||
// // visualize the field as grayscale
|
||||
// gl_FragColor = vec4(vec3(field), 1.0);
|
||||
// }
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import { AdditiveBlending, DataTexture, ShaderMaterial } from "three";
|
||||
|
||||
export default function createDensityAccumulatorMaterial(
|
||||
nodePositionsTexture: DataTexture,
|
||||
initialCameraDistance: number
|
||||
) {
|
||||
const densityCloudMaterial = new ShaderMaterial({
|
||||
depthWrite: false,
|
||||
depthTest: false,
|
||||
transparent: true,
|
||||
blending: AdditiveBlending,
|
||||
uniforms: {
|
||||
nodePositionsTexture: {
|
||||
value: nodePositionsTexture,
|
||||
},
|
||||
textureSize: {
|
||||
value: nodePositionsTexture.image.width,
|
||||
},
|
||||
camDist: {
|
||||
value: initialCameraDistance,
|
||||
},
|
||||
radius: { value: 0.05 },
|
||||
},
|
||||
vertexShader: `
|
||||
uniform sampler2D nodePositionsTexture;
|
||||
uniform float textureSize;
|
||||
uniform float camDist;
|
||||
attribute vec3 nodeColor;
|
||||
varying vec3 vColor;
|
||||
varying vec2 vUv;
|
||||
varying float nodeSize;
|
||||
|
||||
vec3 getNodePos(float idx) {
|
||||
float fx = mod(idx, textureSize);
|
||||
float fy = floor(idx / textureSize);
|
||||
vec2 uv = (vec2(fx, fy) + 0.5) / textureSize;
|
||||
return texture2D(nodePositionsTexture, uv).xyz;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vColor = nodeColor;
|
||||
vec3 nodePos = getNodePos(float(gl_InstanceID));
|
||||
|
||||
float baseNodeSize = 8.0;
|
||||
|
||||
// Normalize camera distance into [0,1]
|
||||
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||
nodeSize = baseNodeSize * mix(10.0, 12.0, t);
|
||||
|
||||
vec3 transformed = nodePos + position * nodeSize;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
varying vec2 vUv;
|
||||
varying float nodeSize;
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vec2 pCoord = vUv - 0.5;
|
||||
float distSq = dot(pCoord, pCoord) * 4.0;
|
||||
|
||||
if (distSq > 1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float radiusSq = (nodeSize / 2.0) * (nodeSize / 2.0);
|
||||
float falloff = max(0.0, 1.0 - distSq);
|
||||
float influence = radiusSq * falloff * falloff;
|
||||
vec3 accumulatedColor = vColor * influence;
|
||||
|
||||
gl_FragColor = vec4(accumulatedColor, influence);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
return densityCloudMaterial;
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import * as three from "three";
|
||||
|
||||
export default function createEdgeMaterial(
|
||||
texture: three.DataTexture,
|
||||
initialCameraDistance: number
|
||||
): three.ShaderMaterial {
|
||||
const material = new three.ShaderMaterial({
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
blending: three.AdditiveBlending,
|
||||
uniforms: {
|
||||
nodePosTex: { value: texture },
|
||||
textureSize: { value: texture.image.width },
|
||||
camDist: { value: initialCameraDistance },
|
||||
mousePos: { value: new three.Vector2(9999, 9999) }, // start offscreen
|
||||
// Apple embedding atlas style: soft pastel edges
|
||||
color: { value: new three.Color("#FCD34D") }, // Soft amber for minimalist aesthetic
|
||||
},
|
||||
vertexShader: `
|
||||
attribute vec2 edgeIndices;
|
||||
uniform sampler2D nodePosTex;
|
||||
uniform float textureSize;
|
||||
uniform float camDist;
|
||||
uniform vec2 mousePos;
|
||||
|
||||
varying float vFade;
|
||||
varying float vHighlight;
|
||||
varying float vEdgePosition; // IMPROVEMENT #2: For directional gradient
|
||||
|
||||
vec3 getNodePos(float idx) {
|
||||
float x = mod(idx, textureSize);
|
||||
float y = floor(idx / textureSize);
|
||||
vec2 uv = (vec2(x, y) + 0.5) / textureSize;
|
||||
return texture2D(nodePosTex, uv).xyz;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 start = getNodePos(edgeIndices.x);
|
||||
vec3 end = getNodePos(edgeIndices.y);
|
||||
vec3 nodePos = mix(start, end, position.x);
|
||||
|
||||
// IMPROVEMENT #2: Pass edge position for gradient
|
||||
vEdgePosition = position.x;
|
||||
|
||||
// Project world-space position to clip-space
|
||||
vec4 clipPos = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||
vec3 ndc = clipPos.xyz / clipPos.w; // normalized device coordinates [-1,1]
|
||||
|
||||
float distanceFromMouse = length(ndc.xy - mousePos);
|
||||
vHighlight = smoothstep(0.2, 0.0, distanceFromMouse);
|
||||
|
||||
// Apple embedding atlas style: subtle edge opacity
|
||||
vFade = smoothstep(500.0, 1500.0, camDist);
|
||||
vFade = 0.25 * clamp(vFade, 0.0, 1.0); // Subtle for clean aesthetic
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
uniform vec3 color;
|
||||
varying float vFade;
|
||||
varying float vHighlight;
|
||||
varying float vEdgePosition; // IMPROVEMENT #2: For directional gradient
|
||||
|
||||
void main() {
|
||||
// IMPROVEMENT #2: Directional gradient from start to end
|
||||
// Brighter at start, slightly darker at end for flow direction
|
||||
float gradientFactor = 1.0 - (vEdgePosition * 0.3); // 30% dimming from start to end
|
||||
|
||||
// IMPROVEMENT #2: Add subtle glow effect
|
||||
vec3 glowColor = vec3(1.0, 0.9, 0.7); // Warm white glow
|
||||
vec3 baseColor = color * gradientFactor;
|
||||
vec3 finalColor = mix(baseColor, glowColor, vHighlight * 0.9);
|
||||
|
||||
// IMPROVEMENT #2: Increased visibility and glow
|
||||
float baseAlpha = vFade * 1.5; // Increased visibility
|
||||
float alpha = mix(baseAlpha, 0.95, vHighlight); // Stronger highlight
|
||||
|
||||
gl_FragColor = vec4(finalColor, alpha);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
return material;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { ShaderMaterial, Texture } from "three";
|
||||
|
||||
export function createMetaballMaterial(fieldTexture: Texture) {
|
||||
return new ShaderMaterial({
|
||||
transparent: true,
|
||||
uniforms: {
|
||||
fieldTex: { value: fieldTexture },
|
||||
threshold: { value: 25000.0 },
|
||||
smoothing: { value: 5000.0 },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
varying vec2 vUv;
|
||||
uniform float threshold;
|
||||
uniform float smoothing;
|
||||
uniform sampler2D fieldTex;
|
||||
|
||||
void main() {
|
||||
vec4 fieldData = texture2D(fieldTex, vUv);
|
||||
vec3 accumulatedColor = fieldData.rgb;
|
||||
float totalInfluence = fieldData.a;
|
||||
|
||||
vec3 finalColor = vec3(0.0);
|
||||
|
||||
if (totalInfluence > 0.0) {
|
||||
finalColor = accumulatedColor / totalInfluence;
|
||||
}
|
||||
|
||||
// Apple embedding atlas style: very subtle density clouds
|
||||
float alphaEdge = smoothstep(threshold - smoothing, threshold + smoothing, totalInfluence);
|
||||
float alpha = alphaEdge * 0.08; // Very subtle for clean Apple aesthetic
|
||||
|
||||
if (alpha < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(finalColor, alpha);
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
import * as three from "three";
|
||||
|
||||
export default function createNodeSwarmMaterial(
|
||||
nodePositionsTexture: three.DataTexture,
|
||||
initialCameraDistance: number
|
||||
) {
|
||||
const material = new three.ShaderMaterial({
|
||||
transparent: true,
|
||||
uniforms: {
|
||||
nodePosTex: { value: nodePositionsTexture },
|
||||
textureSize: { value: nodePositionsTexture.image.width },
|
||||
camDist: { value: initialCameraDistance },
|
||||
mousePos: { value: new three.Vector2(9999, 9999) }, // start offscreen
|
||||
},
|
||||
vertexShader: `
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D nodePosTex;
|
||||
uniform float textureSize;
|
||||
uniform float camDist;
|
||||
uniform vec2 mousePos;
|
||||
attribute vec3 nodeColor;
|
||||
attribute float nodeSize; // Hierarchy-based size multiplier
|
||||
attribute float nodeHighlight; // Selection-based highlight (1.0 = selected, 0.3 = dimmed)
|
||||
varying vec3 vColor;
|
||||
varying float vHighlight;
|
||||
varying float vSelectionHighlight;
|
||||
varying vec2 vUv; // IMPROVEMENT #4: For radial halo effect
|
||||
|
||||
vec3 getNodePos(float idx) {
|
||||
float size = textureSize;
|
||||
float fx = mod(idx, size);
|
||||
float fy = floor(idx / size);
|
||||
vec2 uv = (vec2(fx, fy) + 0.5) / size;
|
||||
return texture2D(nodePosTex, uv).xyz;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vColor = nodeColor;
|
||||
vSelectionHighlight = nodeHighlight;
|
||||
vec3 nodePos = getNodePos(float(gl_InstanceID));
|
||||
|
||||
// IMPROVEMENT #4: Pass UV coordinates for halo effect
|
||||
vUv = position.xy * 0.5 + 0.5; // Convert from [-1,1] to [0,1]
|
||||
|
||||
// Project world-space position to clip-space
|
||||
vec4 clipPos = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||
vec3 ndc = clipPos.xyz / clipPos.w; // normalized device coordinates [-1,1]
|
||||
|
||||
float distanceFromMouse = length(ndc.xy - mousePos);
|
||||
vHighlight = smoothstep(0.2, 0.0, distanceFromMouse);
|
||||
|
||||
// Hierarchy-based sizing: base size * type size multiplier
|
||||
float baseNodeSize = 7.0;
|
||||
|
||||
// Normalize camera distance into [0,1]
|
||||
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||
float finalSize = baseNodeSize * nodeSize * mix(1.0, 1.2, t); // Apply hierarchy multiplier
|
||||
|
||||
vec3 transformed = nodePos + position * finalSize;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
varying vec3 vColor;
|
||||
varying float vHighlight;
|
||||
varying float vSelectionHighlight;
|
||||
varying vec2 vUv; // IMPROVEMENT #4: For radial halo effect
|
||||
|
||||
void main() {
|
||||
// Apple embedding atlas style: subtle radial glow
|
||||
vec2 center = vec2(0.5, 0.5);
|
||||
float distFromCenter = length(vUv - center) * 2.0;
|
||||
|
||||
// Create sharp node with very subtle glow
|
||||
float coreRadius = 0.75; // Slightly larger core
|
||||
float haloRadius = 1.0;
|
||||
|
||||
// Core node (solid)
|
||||
float core = 1.0 - smoothstep(0.0, coreRadius, distFromCenter);
|
||||
|
||||
// Very subtle outer glow (Apple aesthetic)
|
||||
float halo = smoothstep(haloRadius, coreRadius, distFromCenter);
|
||||
|
||||
// Subtle color mixing
|
||||
vec3 haloColor = vColor * 1.15; // Subtle brightness increase
|
||||
vec3 baseColor = mix(vColor, vec3(1.0), vHighlight * 0.4);
|
||||
vec3 finalColor = mix(haloColor, baseColor, core);
|
||||
|
||||
// Alpha with subtle glow
|
||||
float alpha = mix(halo * 0.4, 1.0, core); // Reduced halo opacity
|
||||
|
||||
// Apply selection-based dimming (neutral-by-default)
|
||||
alpha *= vSelectionHighlight;
|
||||
|
||||
gl_FragColor = vec4(finalColor, alpha);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
return material;
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import * as three from "three";
|
||||
|
||||
export default function createPickingMaterial(
|
||||
nodePositionsTexture: three.DataTexture,
|
||||
initialCameraDistance: number
|
||||
) {
|
||||
const pickingMaterial = new three.ShaderMaterial({
|
||||
depthTest: true,
|
||||
depthWrite: true,
|
||||
transparent: false,
|
||||
blending: three.NoBlending,
|
||||
uniforms: {
|
||||
nodePosTex: { value: nodePositionsTexture },
|
||||
textureSize: { value: nodePositionsTexture.image.width },
|
||||
camDist: { value: initialCameraDistance },
|
||||
},
|
||||
vertexShader: `
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D nodePosTex;
|
||||
uniform float textureSize;
|
||||
uniform float camDist;
|
||||
varying vec3 vColor;
|
||||
|
||||
vec3 getNodePos(float idx) {
|
||||
float size = textureSize;
|
||||
float fx = mod(idx, size);
|
||||
float fy = floor(idx / size);
|
||||
vec2 uv = (vec2(fx, fy) + 0.5) / size;
|
||||
return texture2D(nodePosTex, uv).xyz;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float id = float(gl_InstanceID);
|
||||
vec3 nodePos = getNodePos(id);
|
||||
vColor = vec3(
|
||||
mod(id, 256.0) / 255.0,
|
||||
mod(floor(id / 256.0), 256.0) / 255.0,
|
||||
floor(id / 65536.0) / 255.0
|
||||
);
|
||||
// vColor = vec3(fract(sin(id * 12.9898) * 43758.5453));
|
||||
|
||||
float baseNodeSize = 4.0;
|
||||
|
||||
// Normalize camera distance into [0,1]
|
||||
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||
float nodeSize = baseNodeSize * mix(1.0, 2.0, t);
|
||||
|
||||
vec3 transformed = nodePos + position * nodeSize;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(vColor, 1.0);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
return pickingMaterial;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import * as three from "three";
|
||||
import createClusterBoundaryMaterial from "../materials/createClusterBoundaryMaterial";
|
||||
|
||||
export interface ClusterInfo {
|
||||
center: { x: number; y: number };
|
||||
radius: number;
|
||||
color: three.Color;
|
||||
}
|
||||
|
||||
export default function createClusterBoundaryMesh(
|
||||
cluster: ClusterInfo
|
||||
): three.Mesh {
|
||||
// Create a circle geometry for the cluster boundary
|
||||
const geometry = new three.PlaneGeometry(
|
||||
cluster.radius * 2.5, // Make it larger to encompass the cluster
|
||||
cluster.radius * 2.5
|
||||
);
|
||||
|
||||
const material = createClusterBoundaryMaterial(cluster.color);
|
||||
const mesh = new three.Mesh(geometry, material);
|
||||
|
||||
// Position the mesh at the cluster center
|
||||
mesh.position.set(cluster.center.x, cluster.center.y, -100); // Behind everything else
|
||||
mesh.renderOrder = -1;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { Mesh, PlaneGeometry, WebGLRenderTarget } from "three";
|
||||
import { createDebugViewMaterial } from "../materials/createDebugViewMaterial";
|
||||
|
||||
export default function createDebugViewMesh(renderTarget: WebGLRenderTarget) {
|
||||
const debugQuad = new Mesh(
|
||||
new PlaneGeometry(2, 2),
|
||||
createDebugViewMaterial(renderTarget.texture)
|
||||
);
|
||||
|
||||
debugQuad.frustumCulled = false;
|
||||
|
||||
return debugQuad;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
InstancedBufferAttribute,
|
||||
InstancedMesh,
|
||||
DataTexture,
|
||||
PlaneGeometry,
|
||||
} from "three";
|
||||
import { Node } from "../graph/types";
|
||||
import createDensityAccumulatorMaterial from "../materials/createDensityAccumulatorMaterial";
|
||||
|
||||
export default function createDensityAccumulatorMesh(
|
||||
nodes: Node[],
|
||||
nodeColors: Float32Array,
|
||||
nodePositionsTexture: DataTexture,
|
||||
initialCameraDistance: number
|
||||
) {
|
||||
const geometry = new PlaneGeometry(2, 2);
|
||||
|
||||
const material = createDensityAccumulatorMaterial(
|
||||
nodePositionsTexture,
|
||||
initialCameraDistance
|
||||
);
|
||||
|
||||
geometry.setAttribute(
|
||||
"nodeColor",
|
||||
new InstancedBufferAttribute(nodeColors, 3)
|
||||
);
|
||||
|
||||
const mesh = new InstancedMesh(geometry, material, nodes.length);
|
||||
|
||||
mesh.frustumCulled = false;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
35
cognee-frontend/src/ui/rendering/meshes/createEdgeMesh.ts
Normal file
35
cognee-frontend/src/ui/rendering/meshes/createEdgeMesh.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import * as three from "three";
|
||||
import createEdgeMaterial from "../materials/createEdgeMaterial";
|
||||
import { Edge } from "../graph/types";
|
||||
|
||||
export default function createEdgeMesh(
|
||||
edges: Edge[],
|
||||
nodePositionTexture: three.DataTexture,
|
||||
edgeIndices: Float32Array,
|
||||
initialCameraDistance: number
|
||||
): three.LineSegments {
|
||||
const numberOfEdges = edges.length;
|
||||
|
||||
const instGeom = new three.InstancedBufferGeometry();
|
||||
instGeom.setAttribute(
|
||||
"position",
|
||||
new three.BufferAttribute(new Float32Array([0, 0, 0, 1, 0, 0]), 3)
|
||||
);
|
||||
// instGeom.index = baseGeom.index;
|
||||
instGeom.instanceCount = numberOfEdges;
|
||||
|
||||
instGeom.setAttribute(
|
||||
"edgeIndices",
|
||||
new three.InstancedBufferAttribute(edgeIndices, 2)
|
||||
);
|
||||
|
||||
const material = createEdgeMaterial(
|
||||
nodePositionTexture,
|
||||
initialCameraDistance
|
||||
);
|
||||
|
||||
const edgeMesh = new three.LineSegments(instGeom, material);
|
||||
edgeMesh.frustumCulled = false;
|
||||
|
||||
return edgeMesh;
|
||||
}
|
||||
24
cognee-frontend/src/ui/rendering/meshes/createLabel.ts
Normal file
24
cognee-frontend/src/ui/rendering/meshes/createLabel.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { Color } from "three";
|
||||
import { Text } from "troika-three-text";
|
||||
|
||||
const LABEL_FONT_SIZE = 14;
|
||||
|
||||
export default function createLabel(text = "", fontSize = LABEL_FONT_SIZE): Text {
|
||||
const label = new Text();
|
||||
label.text = text;
|
||||
label.fontSize = fontSize;
|
||||
label.color = new Color("#ffffff");
|
||||
label.strokeColor = new Color("#ffffff");
|
||||
label.outlineWidth = 2;
|
||||
label.outlineColor = new Color("#000000");
|
||||
label.outlineOpacity = 0.5;
|
||||
label.anchorX = "center";
|
||||
label.anchorY = "middle";
|
||||
label.visible = true;
|
||||
label.frustumCulled = false;
|
||||
label.renderOrder = 5;
|
||||
label.maxWidth = 200;
|
||||
label.sync();
|
||||
|
||||
return label;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Mesh, PlaneGeometry, WebGLRenderTarget } from "three";
|
||||
import { createMetaballMaterial } from "../materials/createMetaballMaterial";
|
||||
|
||||
export default function createMetaballMesh(
|
||||
fieldRenderTarget: WebGLRenderTarget
|
||||
) {
|
||||
const quadGeo = new PlaneGeometry(2, 2);
|
||||
|
||||
const metaballMat = createMetaballMaterial(fieldRenderTarget.texture);
|
||||
|
||||
const quad = new Mesh(quadGeo, metaballMat);
|
||||
quad.frustumCulled = false;
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import {
|
||||
Mesh,
|
||||
DataTexture,
|
||||
CircleGeometry,
|
||||
InstancedBufferAttribute,
|
||||
InstancedBufferGeometry,
|
||||
} from "three";
|
||||
import { Node } from "../graph/types";
|
||||
import createNodeSwarmMaterial from "../materials/createNodeSwarmMaterial";
|
||||
|
||||
export default function createNodeSwarmMesh(
|
||||
nodes: Node[],
|
||||
nodePositionsTexture: DataTexture,
|
||||
nodeColors: Float32Array,
|
||||
nodeSizes: Float32Array,
|
||||
nodeHighlights: Float32Array,
|
||||
initialCameraDistance: number
|
||||
) {
|
||||
const nodeGeom = new CircleGeometry(2, 16);
|
||||
const geom = new InstancedBufferGeometry();
|
||||
geom.index = nodeGeom.index;
|
||||
geom.instanceCount = nodes.length;
|
||||
|
||||
geom.setAttribute("position", nodeGeom.attributes.position);
|
||||
geom.setAttribute("uv", nodeGeom.attributes.uv);
|
||||
geom.setAttribute("nodeColor", new InstancedBufferAttribute(nodeColors, 3));
|
||||
geom.setAttribute("nodeSize", new InstancedBufferAttribute(nodeSizes, 1));
|
||||
geom.setAttribute("nodeHighlight", new InstancedBufferAttribute(nodeHighlights, 1));
|
||||
|
||||
const material = createNodeSwarmMaterial(
|
||||
nodePositionsTexture,
|
||||
initialCameraDistance
|
||||
);
|
||||
|
||||
const nodeSwarmMesh = new Mesh(geom, material);
|
||||
nodeSwarmMesh.frustumCulled = false;
|
||||
|
||||
return nodeSwarmMesh;
|
||||
}
|
||||
35
cognee-frontend/src/ui/rendering/meshes/createPickingMesh.ts
Normal file
35
cognee-frontend/src/ui/rendering/meshes/createPickingMesh.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import {
|
||||
Mesh,
|
||||
DataTexture,
|
||||
CircleGeometry,
|
||||
InstancedBufferGeometry,
|
||||
InstancedBufferAttribute,
|
||||
} from "three";
|
||||
import { Node } from "../graph/types";
|
||||
import createPickingMaterial from "../materials/createPickingMaterial";
|
||||
|
||||
export default function createPickingMesh(
|
||||
nodes: Node[],
|
||||
nodePositionsTexture: DataTexture,
|
||||
nodeColors: Float32Array,
|
||||
initialCameraDistance: number
|
||||
): Mesh {
|
||||
const nodeGeom = new CircleGeometry(2, 16);
|
||||
const geom = new InstancedBufferGeometry();
|
||||
geom.index = nodeGeom.index;
|
||||
geom.instanceCount = nodes.length;
|
||||
|
||||
geom.setAttribute("position", nodeGeom.attributes.position);
|
||||
geom.setAttribute("uv", nodeGeom.attributes.uv);
|
||||
geom.setAttribute("nodeColor", new InstancedBufferAttribute(nodeColors, 3));
|
||||
|
||||
const pickingMaterial = createPickingMaterial(
|
||||
nodePositionsTexture,
|
||||
initialCameraDistance
|
||||
);
|
||||
|
||||
const pickingMesh = new Mesh(geom, pickingMaterial);
|
||||
pickingMesh.frustumCulled = false;
|
||||
|
||||
return pickingMesh;
|
||||
}
|
||||
40
cognee-frontend/src/ui/rendering/picking/pickNodeIndex.ts
Normal file
40
cognee-frontend/src/ui/rendering/picking/pickNodeIndex.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import {
|
||||
OrthographicCamera,
|
||||
Scene,
|
||||
WebGLRenderer,
|
||||
WebGLRenderTarget,
|
||||
} from "three";
|
||||
|
||||
const pixelBuffer = new Uint8Array(4);
|
||||
|
||||
export default function pickNodeIndex(
|
||||
event: MouseEvent,
|
||||
renderer: WebGLRenderer,
|
||||
pickingScene: Scene,
|
||||
camera: OrthographicCamera,
|
||||
pickingRenderTarget: WebGLRenderTarget
|
||||
) {
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
// Convert from client coords to pixel coords in render target
|
||||
const x =
|
||||
((event.clientX - rect.left) / rect.width) * pickingRenderTarget.width;
|
||||
const y =
|
||||
pickingRenderTarget.height -
|
||||
((event.clientY - rect.top) / rect.height) * pickingRenderTarget.height;
|
||||
|
||||
renderer.setRenderTarget(pickingRenderTarget);
|
||||
renderer.clear();
|
||||
renderer.render(pickingScene, camera);
|
||||
renderer.readRenderTargetPixels(
|
||||
pickingRenderTarget,
|
||||
Math.floor(x),
|
||||
Math.floor(y),
|
||||
1,
|
||||
1,
|
||||
pixelBuffer
|
||||
);
|
||||
renderer.setRenderTarget(null);
|
||||
|
||||
const id = pixelBuffer[0] + pixelBuffer[1] * 256 + pixelBuffer[2] * 256 * 256;
|
||||
return id || -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { FloatType, LinearFilter, RGBAFormat, WebGLRenderTarget } from "three";
|
||||
|
||||
export default function createDensityRenderTarget(size = 512) {
|
||||
return new WebGLRenderTarget(size, size, {
|
||||
format: RGBAFormat,
|
||||
type: FloatType,
|
||||
minFilter: LinearFilter,
|
||||
magFilter: LinearFilter,
|
||||
depthBuffer: false,
|
||||
stencilBuffer: false,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import * as three from "three";
|
||||
import { Node } from "../graph/types";
|
||||
|
||||
export default function createNodePositionsTexture(
|
||||
nodes: Node[],
|
||||
nodePositionData: Float32Array
|
||||
): three.DataTexture {
|
||||
const textureSize = Math.ceil(Math.sqrt(nodes.length));
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodePositionData[i * 4 + 0] = 0.0;
|
||||
nodePositionData[i * 4 + 1] = 0.0;
|
||||
nodePositionData[i * 4 + 2] = 0.0;
|
||||
nodePositionData[i * 4 + 3] = 1.0;
|
||||
}
|
||||
|
||||
const texture = new three.DataTexture(
|
||||
nodePositionData,
|
||||
textureSize,
|
||||
textureSize,
|
||||
three.RGBAFormat,
|
||||
three.FloatType
|
||||
);
|
||||
texture.needsUpdate = true;
|
||||
texture.minFilter = three.NearestFilter;
|
||||
texture.magFilter = three.NearestFilter;
|
||||
return texture;
|
||||
}
|
||||
465
cognee-frontend/types/troika-three-text.d.ts
vendored
Normal file
465
cognee-frontend/types/troika-three-text.d.ts
vendored
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare module "troika-three-text";
|
||||
|
||||
// import type { Color, Material, Object3D, Object3DEventMap } from "three";
|
||||
|
||||
// export class BatchedText {
|
||||
// constructor(...args: any[]);
|
||||
|
||||
// add(...args: any[]): void;
|
||||
|
||||
// addText(...args: any[]): void;
|
||||
|
||||
// copy(...args: any[]): void;
|
||||
|
||||
// createDerivedMaterial(...args: any[]): void;
|
||||
|
||||
// dispose(...args: any[]): void;
|
||||
|
||||
// hasOutline(...args: any[]): void;
|
||||
|
||||
// remove(...args: any[]): void;
|
||||
|
||||
// removeText(...args: any[]): void;
|
||||
|
||||
// sync(...args: any[]): void;
|
||||
|
||||
// updateBounds(...args: any[]): void;
|
||||
|
||||
// updateMatrixWorld(...args: any[]): void;
|
||||
|
||||
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||
|
||||
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||
// }
|
||||
|
||||
// export class GlyphsGeometry {
|
||||
// constructor(...args: any[]);
|
||||
|
||||
// applyClipRect(...args: any[]): void;
|
||||
|
||||
// computeBoundingBox(...args: any[]): void;
|
||||
|
||||
// computeBoundingSphere(...args: any[]): void;
|
||||
|
||||
// updateAttributeData(...args: any[]): void;
|
||||
|
||||
// updateGlyphs(...args: any[]): void;
|
||||
// }
|
||||
|
||||
// export class Text extends Object3D<Object3DEventMap> {
|
||||
// public text: string;
|
||||
// public fontSize: number;
|
||||
// public color: Color;
|
||||
// public anchorX;
|
||||
// public anchorY;
|
||||
// public font: string;
|
||||
// public material: Material;
|
||||
|
||||
// constructor(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// clone(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// copy(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// createDerivedMaterial(...args: any[]): void;
|
||||
|
||||
// dispose(...args: any[]): void;
|
||||
|
||||
// hasOutline(...args: any[]): void;
|
||||
|
||||
// localPositionToTextCoords(...args: any[]): void;
|
||||
|
||||
// onBeforeRender(...args: any[]): void;
|
||||
|
||||
// raycast(...args: any[]): void;
|
||||
|
||||
// sync(...args: any[]): void;
|
||||
|
||||
// worldPositionToTextCoords(...args: any[]): void;
|
||||
|
||||
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||
|
||||
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||
// }
|
||||
|
||||
// export function configureTextBuilder(config: any): void;
|
||||
|
||||
// export function createTextDerivedMaterial(baseMaterial: any): any;
|
||||
|
||||
// export function dumpSDFTextures(): void;
|
||||
|
||||
// export function fontResolverWorkerModule(...args: any[]): any;
|
||||
|
||||
// export function getCaretAtPoint(textRenderInfo: any, x: any, y: any): any;
|
||||
|
||||
// export function getSelectionRects(
|
||||
// textRenderInfo: any,
|
||||
// start: any,
|
||||
// end: any
|
||||
// ): any;
|
||||
|
||||
// export function getTextRenderInfo(args: any, callback: any): any;
|
||||
|
||||
// export function preloadFont(
|
||||
// { font, characters, sdfGlyphSize }: any,
|
||||
// callback: any
|
||||
// ): void;
|
||||
|
||||
// export function typesetterWorkerModule(...args: any[]): any;
|
||||
|
||||
// export namespace BatchedText {
|
||||
// namespace DEFAULT_UP {
|
||||
// const isVector3: boolean;
|
||||
|
||||
// const x: number;
|
||||
|
||||
// const y: number;
|
||||
|
||||
// const z: number;
|
||||
|
||||
// function add(...args: any[]): void;
|
||||
|
||||
// function addScalar(...args: any[]): void;
|
||||
|
||||
// function addScaledVector(...args: any[]): void;
|
||||
|
||||
// function addVectors(...args: any[]): void;
|
||||
|
||||
// function angleTo(...args: any[]): void;
|
||||
|
||||
// function applyAxisAngle(...args: any[]): void;
|
||||
|
||||
// function applyEuler(...args: any[]): void;
|
||||
|
||||
// function applyMatrix3(...args: any[]): void;
|
||||
|
||||
// function applyMatrix4(...args: any[]): void;
|
||||
|
||||
// function applyNormalMatrix(...args: any[]): void;
|
||||
|
||||
// function applyQuaternion(...args: any[]): void;
|
||||
|
||||
// function ceil(...args: any[]): void;
|
||||
|
||||
// function clamp(...args: any[]): void;
|
||||
|
||||
// function clampLength(...args: any[]): void;
|
||||
|
||||
// function clampScalar(...args: any[]): void;
|
||||
|
||||
// function clone(...args: any[]): void;
|
||||
|
||||
// function copy(...args: any[]): void;
|
||||
|
||||
// function cross(...args: any[]): void;
|
||||
|
||||
// function crossVectors(...args: any[]): void;
|
||||
|
||||
// function distanceTo(...args: any[]): void;
|
||||
|
||||
// function distanceToSquared(...args: any[]): void;
|
||||
|
||||
// function divide(...args: any[]): void;
|
||||
|
||||
// function divideScalar(...args: any[]): void;
|
||||
|
||||
// function dot(...args: any[]): void;
|
||||
|
||||
// function equals(...args: any[]): void;
|
||||
|
||||
// function floor(...args: any[]): void;
|
||||
|
||||
// function fromArray(...args: any[]): void;
|
||||
|
||||
// function fromBufferAttribute(...args: any[]): void;
|
||||
|
||||
// function getComponent(...args: any[]): void;
|
||||
|
||||
// function length(...args: any[]): void;
|
||||
|
||||
// function lengthSq(...args: any[]): void;
|
||||
|
||||
// function lerp(...args: any[]): void;
|
||||
|
||||
// function lerpVectors(...args: any[]): void;
|
||||
|
||||
// function manhattanDistanceTo(...args: any[]): void;
|
||||
|
||||
// function manhattanLength(...args: any[]): void;
|
||||
|
||||
// function max(...args: any[]): void;
|
||||
|
||||
// function min(...args: any[]): void;
|
||||
|
||||
// function multiply(...args: any[]): void;
|
||||
|
||||
// function multiplyScalar(...args: any[]): void;
|
||||
|
||||
// function multiplyVectors(...args: any[]): void;
|
||||
|
||||
// function negate(...args: any[]): void;
|
||||
|
||||
// function normalize(...args: any[]): void;
|
||||
|
||||
// function project(...args: any[]): void;
|
||||
|
||||
// function projectOnPlane(...args: any[]): void;
|
||||
|
||||
// function projectOnVector(...args: any[]): void;
|
||||
|
||||
// function random(...args: any[]): void;
|
||||
|
||||
// function randomDirection(...args: any[]): void;
|
||||
|
||||
// function reflect(...args: any[]): void;
|
||||
|
||||
// function round(...args: any[]): void;
|
||||
|
||||
// function roundToZero(...args: any[]): void;
|
||||
|
||||
// function set(...args: any[]): void;
|
||||
|
||||
// function setComponent(...args: any[]): void;
|
||||
|
||||
// function setFromColor(...args: any[]): void;
|
||||
|
||||
// function setFromCylindrical(...args: any[]): void;
|
||||
|
||||
// function setFromCylindricalCoords(...args: any[]): void;
|
||||
|
||||
// function setFromEuler(...args: any[]): void;
|
||||
|
||||
// function setFromMatrix3Column(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixColumn(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixPosition(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixScale(...args: any[]): void;
|
||||
|
||||
// function setFromSpherical(...args: any[]): void;
|
||||
|
||||
// function setFromSphericalCoords(...args: any[]): void;
|
||||
|
||||
// function setLength(...args: any[]): void;
|
||||
|
||||
// function setScalar(...args: any[]): void;
|
||||
|
||||
// function setX(...args: any[]): void;
|
||||
|
||||
// function setY(...args: any[]): void;
|
||||
|
||||
// function setZ(...args: any[]): void;
|
||||
|
||||
// function sub(...args: any[]): void;
|
||||
|
||||
// function subScalar(...args: any[]): void;
|
||||
|
||||
// function subVectors(...args: any[]): void;
|
||||
|
||||
// function toArray(...args: any[]): void;
|
||||
|
||||
// function transformDirection(...args: any[]): void;
|
||||
|
||||
// function unproject(...args: any[]): void;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export namespace Text {
|
||||
// namespace DEFAULT_UP {
|
||||
// const isVector3: boolean;
|
||||
|
||||
// const x: number;
|
||||
|
||||
// const y: number;
|
||||
|
||||
// const z: number;
|
||||
|
||||
// function add(...args: any[]): void;
|
||||
|
||||
// function addScalar(...args: any[]): void;
|
||||
|
||||
// function addScaledVector(...args: any[]): void;
|
||||
|
||||
// function addVectors(...args: any[]): void;
|
||||
|
||||
// function angleTo(...args: any[]): void;
|
||||
|
||||
// function applyAxisAngle(...args: any[]): void;
|
||||
|
||||
// function applyEuler(...args: any[]): void;
|
||||
|
||||
// function applyMatrix3(...args: any[]): void;
|
||||
|
||||
// function applyMatrix4(...args: any[]): void;
|
||||
|
||||
// function applyNormalMatrix(...args: any[]): void;
|
||||
|
||||
// function applyQuaternion(...args: any[]): void;
|
||||
|
||||
// function ceil(...args: any[]): void;
|
||||
|
||||
// function clamp(...args: any[]): void;
|
||||
|
||||
// function clampLength(...args: any[]): void;
|
||||
|
||||
// function clampScalar(...args: any[]): void;
|
||||
|
||||
// function clone(...args: any[]): void;
|
||||
|
||||
// function copy(...args: any[]): void;
|
||||
|
||||
// function cross(...args: any[]): void;
|
||||
|
||||
// function crossVectors(...args: any[]): void;
|
||||
|
||||
// function distanceTo(...args: any[]): void;
|
||||
|
||||
// function distanceToSquared(...args: any[]): void;
|
||||
|
||||
// function divide(...args: any[]): void;
|
||||
|
||||
// function divideScalar(...args: any[]): void;
|
||||
|
||||
// function dot(...args: any[]): void;
|
||||
|
||||
// function equals(...args: any[]): void;
|
||||
|
||||
// function floor(...args: any[]): void;
|
||||
|
||||
// function fromArray(...args: any[]): void;
|
||||
|
||||
// function fromBufferAttribute(...args: any[]): void;
|
||||
|
||||
// function getComponent(...args: any[]): void;
|
||||
|
||||
// function length(...args: any[]): void;
|
||||
|
||||
// function lengthSq(...args: any[]): void;
|
||||
|
||||
// function lerp(...args: any[]): void;
|
||||
|
||||
// function lerpVectors(...args: any[]): void;
|
||||
|
||||
// function manhattanDistanceTo(...args: any[]): void;
|
||||
|
||||
// function manhattanLength(...args: any[]): void;
|
||||
|
||||
// function max(...args: any[]): void;
|
||||
|
||||
// function min(...args: any[]): void;
|
||||
|
||||
// function multiply(...args: any[]): void;
|
||||
|
||||
// function multiplyScalar(...args: any[]): void;
|
||||
|
||||
// function multiplyVectors(...args: any[]): void;
|
||||
|
||||
// function negate(...args: any[]): void;
|
||||
|
||||
// function normalize(...args: any[]): void;
|
||||
|
||||
// function project(...args: any[]): void;
|
||||
|
||||
// function projectOnPlane(...args: any[]): void;
|
||||
|
||||
// function projectOnVector(...args: any[]): void;
|
||||
|
||||
// function random(...args: any[]): void;
|
||||
|
||||
// function randomDirection(...args: any[]): void;
|
||||
|
||||
// function reflect(...args: any[]): void;
|
||||
|
||||
// function round(...args: any[]): void;
|
||||
|
||||
// function roundToZero(...args: any[]): void;
|
||||
|
||||
// function set(...args: any[]): void;
|
||||
|
||||
// function setComponent(...args: any[]): void;
|
||||
|
||||
// function setFromColor(...args: any[]): void;
|
||||
|
||||
// function setFromCylindrical(...args: any[]): void;
|
||||
|
||||
// function setFromCylindricalCoords(...args: any[]): void;
|
||||
|
||||
// function setFromEuler(...args: any[]): void;
|
||||
|
||||
// function setFromMatrix3Column(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixColumn(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixPosition(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixScale(...args: any[]): void;
|
||||
|
||||
// function setFromSpherical(...args: any[]): void;
|
||||
|
||||
// function setFromSphericalCoords(...args: any[]): void;
|
||||
|
||||
// function setLength(...args: any[]): void;
|
||||
|
||||
// function setScalar(...args: any[]): void;
|
||||
|
||||
// function setX(...args: any[]): void;
|
||||
|
||||
// function setY(...args: any[]): void;
|
||||
|
||||
// function setZ(...args: any[]): void;
|
||||
|
||||
// function sub(...args: any[]): void;
|
||||
|
||||
// function subScalar(...args: any[]): void;
|
||||
|
||||
// function subVectors(...args: any[]): void;
|
||||
|
||||
// function toArray(...args: any[]): void;
|
||||
|
||||
// function transformDirection(...args: any[]): void;
|
||||
|
||||
// function unproject(...args: any[]): void;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export namespace fontResolverWorkerModule {
|
||||
// const workerModuleData: {
|
||||
// dependencies: {
|
||||
// dependencies: any;
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// }[];
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// function onMainThread(...args: any[]): any;
|
||||
// }
|
||||
|
||||
// export namespace typesetterWorkerModule {
|
||||
// const workerModuleData: {
|
||||
// dependencies: {
|
||||
// dependencies: any;
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// }[];
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// function onMainThread(...args: any[]): any;
|
||||
// }
|
||||
465
cognee-frontend/types/troika-three-utils.d.ts
vendored
Normal file
465
cognee-frontend/types/troika-three-utils.d.ts
vendored
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare module "troika-three-utils";
|
||||
|
||||
// import type { Color, Material, Object3D, Object3DEventMap } from "three";
|
||||
|
||||
// export class BatchedText {
|
||||
// constructor(...args: any[]);
|
||||
|
||||
// add(...args: any[]): void;
|
||||
|
||||
// addText(...args: any[]): void;
|
||||
|
||||
// copy(...args: any[]): void;
|
||||
|
||||
// createDerivedMaterial(...args: any[]): void;
|
||||
|
||||
// dispose(...args: any[]): void;
|
||||
|
||||
// hasOutline(...args: any[]): void;
|
||||
|
||||
// remove(...args: any[]): void;
|
||||
|
||||
// removeText(...args: any[]): void;
|
||||
|
||||
// sync(...args: any[]): void;
|
||||
|
||||
// updateBounds(...args: any[]): void;
|
||||
|
||||
// updateMatrixWorld(...args: any[]): void;
|
||||
|
||||
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||
|
||||
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||
// }
|
||||
|
||||
// export class GlyphsGeometry {
|
||||
// constructor(...args: any[]);
|
||||
|
||||
// applyClipRect(...args: any[]): void;
|
||||
|
||||
// computeBoundingBox(...args: any[]): void;
|
||||
|
||||
// computeBoundingSphere(...args: any[]): void;
|
||||
|
||||
// updateAttributeData(...args: any[]): void;
|
||||
|
||||
// updateGlyphs(...args: any[]): void;
|
||||
// }
|
||||
|
||||
// export class Text extends Object3D<Object3DEventMap> {
|
||||
// public text: string;
|
||||
// public fontSize: number;
|
||||
// public color: Color;
|
||||
// public anchorX;
|
||||
// public anchorY;
|
||||
// public font: string;
|
||||
// public material: Material;
|
||||
|
||||
// constructor(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// clone(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// copy(...args: any[]): Object3D<Object3DEventMap>;
|
||||
|
||||
// createDerivedMaterial(...args: any[]): void;
|
||||
|
||||
// dispose(...args: any[]): void;
|
||||
|
||||
// hasOutline(...args: any[]): void;
|
||||
|
||||
// localPositionToTextCoords(...args: any[]): void;
|
||||
|
||||
// onBeforeRender(...args: any[]): void;
|
||||
|
||||
// raycast(...args: any[]): void;
|
||||
|
||||
// sync(...args: any[]): void;
|
||||
|
||||
// worldPositionToTextCoords(...args: any[]): void;
|
||||
|
||||
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||
|
||||
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||
// }
|
||||
|
||||
// export function configureTextBuilder(config: any): void;
|
||||
|
||||
// export function createTextDerivedMaterial(baseMaterial: any): any;
|
||||
|
||||
// export function dumpSDFTextures(): void;
|
||||
|
||||
// export function fontResolverWorkerModule(...args: any[]): any;
|
||||
|
||||
// export function getCaretAtPoint(textRenderInfo: any, x: any, y: any): any;
|
||||
|
||||
// export function getSelectionRects(
|
||||
// textRenderInfo: any,
|
||||
// start: any,
|
||||
// end: any
|
||||
// ): any;
|
||||
|
||||
// export function getTextRenderInfo(args: any, callback: any): any;
|
||||
|
||||
// export function preloadFont(
|
||||
// { font, characters, sdfGlyphSize }: any,
|
||||
// callback: any
|
||||
// ): void;
|
||||
|
||||
// export function typesetterWorkerModule(...args: any[]): any;
|
||||
|
||||
// export namespace BatchedText {
|
||||
// namespace DEFAULT_UP {
|
||||
// const isVector3: boolean;
|
||||
|
||||
// const x: number;
|
||||
|
||||
// const y: number;
|
||||
|
||||
// const z: number;
|
||||
|
||||
// function add(...args: any[]): void;
|
||||
|
||||
// function addScalar(...args: any[]): void;
|
||||
|
||||
// function addScaledVector(...args: any[]): void;
|
||||
|
||||
// function addVectors(...args: any[]): void;
|
||||
|
||||
// function angleTo(...args: any[]): void;
|
||||
|
||||
// function applyAxisAngle(...args: any[]): void;
|
||||
|
||||
// function applyEuler(...args: any[]): void;
|
||||
|
||||
// function applyMatrix3(...args: any[]): void;
|
||||
|
||||
// function applyMatrix4(...args: any[]): void;
|
||||
|
||||
// function applyNormalMatrix(...args: any[]): void;
|
||||
|
||||
// function applyQuaternion(...args: any[]): void;
|
||||
|
||||
// function ceil(...args: any[]): void;
|
||||
|
||||
// function clamp(...args: any[]): void;
|
||||
|
||||
// function clampLength(...args: any[]): void;
|
||||
|
||||
// function clampScalar(...args: any[]): void;
|
||||
|
||||
// function clone(...args: any[]): void;
|
||||
|
||||
// function copy(...args: any[]): void;
|
||||
|
||||
// function cross(...args: any[]): void;
|
||||
|
||||
// function crossVectors(...args: any[]): void;
|
||||
|
||||
// function distanceTo(...args: any[]): void;
|
||||
|
||||
// function distanceToSquared(...args: any[]): void;
|
||||
|
||||
// function divide(...args: any[]): void;
|
||||
|
||||
// function divideScalar(...args: any[]): void;
|
||||
|
||||
// function dot(...args: any[]): void;
|
||||
|
||||
// function equals(...args: any[]): void;
|
||||
|
||||
// function floor(...args: any[]): void;
|
||||
|
||||
// function fromArray(...args: any[]): void;
|
||||
|
||||
// function fromBufferAttribute(...args: any[]): void;
|
||||
|
||||
// function getComponent(...args: any[]): void;
|
||||
|
||||
// function length(...args: any[]): void;
|
||||
|
||||
// function lengthSq(...args: any[]): void;
|
||||
|
||||
// function lerp(...args: any[]): void;
|
||||
|
||||
// function lerpVectors(...args: any[]): void;
|
||||
|
||||
// function manhattanDistanceTo(...args: any[]): void;
|
||||
|
||||
// function manhattanLength(...args: any[]): void;
|
||||
|
||||
// function max(...args: any[]): void;
|
||||
|
||||
// function min(...args: any[]): void;
|
||||
|
||||
// function multiply(...args: any[]): void;
|
||||
|
||||
// function multiplyScalar(...args: any[]): void;
|
||||
|
||||
// function multiplyVectors(...args: any[]): void;
|
||||
|
||||
// function negate(...args: any[]): void;
|
||||
|
||||
// function normalize(...args: any[]): void;
|
||||
|
||||
// function project(...args: any[]): void;
|
||||
|
||||
// function projectOnPlane(...args: any[]): void;
|
||||
|
||||
// function projectOnVector(...args: any[]): void;
|
||||
|
||||
// function random(...args: any[]): void;
|
||||
|
||||
// function randomDirection(...args: any[]): void;
|
||||
|
||||
// function reflect(...args: any[]): void;
|
||||
|
||||
// function round(...args: any[]): void;
|
||||
|
||||
// function roundToZero(...args: any[]): void;
|
||||
|
||||
// function set(...args: any[]): void;
|
||||
|
||||
// function setComponent(...args: any[]): void;
|
||||
|
||||
// function setFromColor(...args: any[]): void;
|
||||
|
||||
// function setFromCylindrical(...args: any[]): void;
|
||||
|
||||
// function setFromCylindricalCoords(...args: any[]): void;
|
||||
|
||||
// function setFromEuler(...args: any[]): void;
|
||||
|
||||
// function setFromMatrix3Column(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixColumn(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixPosition(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixScale(...args: any[]): void;
|
||||
|
||||
// function setFromSpherical(...args: any[]): void;
|
||||
|
||||
// function setFromSphericalCoords(...args: any[]): void;
|
||||
|
||||
// function setLength(...args: any[]): void;
|
||||
|
||||
// function setScalar(...args: any[]): void;
|
||||
|
||||
// function setX(...args: any[]): void;
|
||||
|
||||
// function setY(...args: any[]): void;
|
||||
|
||||
// function setZ(...args: any[]): void;
|
||||
|
||||
// function sub(...args: any[]): void;
|
||||
|
||||
// function subScalar(...args: any[]): void;
|
||||
|
||||
// function subVectors(...args: any[]): void;
|
||||
|
||||
// function toArray(...args: any[]): void;
|
||||
|
||||
// function transformDirection(...args: any[]): void;
|
||||
|
||||
// function unproject(...args: any[]): void;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export namespace Text {
|
||||
// namespace DEFAULT_UP {
|
||||
// const isVector3: boolean;
|
||||
|
||||
// const x: number;
|
||||
|
||||
// const y: number;
|
||||
|
||||
// const z: number;
|
||||
|
||||
// function add(...args: any[]): void;
|
||||
|
||||
// function addScalar(...args: any[]): void;
|
||||
|
||||
// function addScaledVector(...args: any[]): void;
|
||||
|
||||
// function addVectors(...args: any[]): void;
|
||||
|
||||
// function angleTo(...args: any[]): void;
|
||||
|
||||
// function applyAxisAngle(...args: any[]): void;
|
||||
|
||||
// function applyEuler(...args: any[]): void;
|
||||
|
||||
// function applyMatrix3(...args: any[]): void;
|
||||
|
||||
// function applyMatrix4(...args: any[]): void;
|
||||
|
||||
// function applyNormalMatrix(...args: any[]): void;
|
||||
|
||||
// function applyQuaternion(...args: any[]): void;
|
||||
|
||||
// function ceil(...args: any[]): void;
|
||||
|
||||
// function clamp(...args: any[]): void;
|
||||
|
||||
// function clampLength(...args: any[]): void;
|
||||
|
||||
// function clampScalar(...args: any[]): void;
|
||||
|
||||
// function clone(...args: any[]): void;
|
||||
|
||||
// function copy(...args: any[]): void;
|
||||
|
||||
// function cross(...args: any[]): void;
|
||||
|
||||
// function crossVectors(...args: any[]): void;
|
||||
|
||||
// function distanceTo(...args: any[]): void;
|
||||
|
||||
// function distanceToSquared(...args: any[]): void;
|
||||
|
||||
// function divide(...args: any[]): void;
|
||||
|
||||
// function divideScalar(...args: any[]): void;
|
||||
|
||||
// function dot(...args: any[]): void;
|
||||
|
||||
// function equals(...args: any[]): void;
|
||||
|
||||
// function floor(...args: any[]): void;
|
||||
|
||||
// function fromArray(...args: any[]): void;
|
||||
|
||||
// function fromBufferAttribute(...args: any[]): void;
|
||||
|
||||
// function getComponent(...args: any[]): void;
|
||||
|
||||
// function length(...args: any[]): void;
|
||||
|
||||
// function lengthSq(...args: any[]): void;
|
||||
|
||||
// function lerp(...args: any[]): void;
|
||||
|
||||
// function lerpVectors(...args: any[]): void;
|
||||
|
||||
// function manhattanDistanceTo(...args: any[]): void;
|
||||
|
||||
// function manhattanLength(...args: any[]): void;
|
||||
|
||||
// function max(...args: any[]): void;
|
||||
|
||||
// function min(...args: any[]): void;
|
||||
|
||||
// function multiply(...args: any[]): void;
|
||||
|
||||
// function multiplyScalar(...args: any[]): void;
|
||||
|
||||
// function multiplyVectors(...args: any[]): void;
|
||||
|
||||
// function negate(...args: any[]): void;
|
||||
|
||||
// function normalize(...args: any[]): void;
|
||||
|
||||
// function project(...args: any[]): void;
|
||||
|
||||
// function projectOnPlane(...args: any[]): void;
|
||||
|
||||
// function projectOnVector(...args: any[]): void;
|
||||
|
||||
// function random(...args: any[]): void;
|
||||
|
||||
// function randomDirection(...args: any[]): void;
|
||||
|
||||
// function reflect(...args: any[]): void;
|
||||
|
||||
// function round(...args: any[]): void;
|
||||
|
||||
// function roundToZero(...args: any[]): void;
|
||||
|
||||
// function set(...args: any[]): void;
|
||||
|
||||
// function setComponent(...args: any[]): void;
|
||||
|
||||
// function setFromColor(...args: any[]): void;
|
||||
|
||||
// function setFromCylindrical(...args: any[]): void;
|
||||
|
||||
// function setFromCylindricalCoords(...args: any[]): void;
|
||||
|
||||
// function setFromEuler(...args: any[]): void;
|
||||
|
||||
// function setFromMatrix3Column(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixColumn(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixPosition(...args: any[]): void;
|
||||
|
||||
// function setFromMatrixScale(...args: any[]): void;
|
||||
|
||||
// function setFromSpherical(...args: any[]): void;
|
||||
|
||||
// function setFromSphericalCoords(...args: any[]): void;
|
||||
|
||||
// function setLength(...args: any[]): void;
|
||||
|
||||
// function setScalar(...args: any[]): void;
|
||||
|
||||
// function setX(...args: any[]): void;
|
||||
|
||||
// function setY(...args: any[]): void;
|
||||
|
||||
// function setZ(...args: any[]): void;
|
||||
|
||||
// function sub(...args: any[]): void;
|
||||
|
||||
// function subScalar(...args: any[]): void;
|
||||
|
||||
// function subVectors(...args: any[]): void;
|
||||
|
||||
// function toArray(...args: any[]): void;
|
||||
|
||||
// function transformDirection(...args: any[]): void;
|
||||
|
||||
// function unproject(...args: any[]): void;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export namespace fontResolverWorkerModule {
|
||||
// const workerModuleData: {
|
||||
// dependencies: {
|
||||
// dependencies: any;
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// }[];
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// function onMainThread(...args: any[]): any;
|
||||
// }
|
||||
|
||||
// export namespace typesetterWorkerModule {
|
||||
// const workerModuleData: {
|
||||
// dependencies: {
|
||||
// dependencies: any;
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// }[];
|
||||
// getTransferables: any;
|
||||
// id: string;
|
||||
// init: string;
|
||||
// isWorkerModule: boolean;
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// function onMainThread(...args: any[]): any;
|
||||
// }
|
||||
|
|
@ -58,6 +58,7 @@ class DataDTO(OutDTO):
|
|||
class GraphNodeDTO(OutDTO):
|
||||
id: UUID
|
||||
label: str
|
||||
type: str
|
||||
properties: dict
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue