diff --git a/compose/neurosynth-frontend/package-lock.json b/compose/neurosynth-frontend/package-lock.json
index 9380c6b1..45c86c5a 100644
--- a/compose/neurosynth-frontend/package-lock.json
+++ b/compose/neurosynth-frontend/package-lock.json
@@ -24,6 +24,7 @@
"@mui/styles": "^5.5.1",
"@mui/system": "^5.5.1",
"@mui/x-data-grid": "^5.10.0",
+ "@niivue/niivue": "^0.46.0",
"@reactour/tour": "^2.10.3",
"@sentry/react": "^7.48.0",
"@tanstack/react-table": "^8.20.5",
@@ -1493,6 +1494,25 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@lukeed/csprng": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
+ "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@lukeed/uuid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz",
+ "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==",
+ "dependencies": {
+ "@lukeed/csprng": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
@@ -1811,6 +1831,24 @@
"node": ">=6"
}
},
+ "node_modules/@niivue/niivue": {
+ "version": "0.46.0",
+ "resolved": "https://registry.npmjs.org/@niivue/niivue/-/niivue-0.46.0.tgz",
+ "integrity": "sha512-fw4/14lxbb4Q9qEDW+eMMfC2wfGTb0x0cgN1VnHsm1bq2kv/xXBQRQJrloQ1Ma5qNweBwbJynEEPm8fXbN8Bgw==",
+ "dependencies": {
+ "@lukeed/uuid": "^2.0.1",
+ "@ungap/structured-clone": "^1.2.0",
+ "array-equal": "^1.0.2",
+ "daikon": "^1.2.46",
+ "fflate": "^0.8.2",
+ "gl-matrix": "^3.4.3",
+ "nifti-reader-js": "^0.6.8",
+ "rxjs": "^7.8.1"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-linux-x64-gnu": "^4.18.1"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -3506,6 +3544,11 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+ },
"node_modules/@vitejs/plugin-react": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz",
@@ -3640,6 +3683,14 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/@wearemothership/dicom-character-set": {
+ "version": "1.0.4-opt.1",
+ "resolved": "https://registry.npmjs.org/@wearemothership/dicom-character-set/-/dicom-character-set-1.0.4-opt.1.tgz",
+ "integrity": "sha512-stqhnpawYHY2UZKj4RHTF71ab3q3z8S1SO9ToQKjsHQwowUdFVo6YFea93psFux3yqNbRlQjwoCdPjHcD0YQzw==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/abortcontroller-polyfill": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
@@ -3824,6 +3875,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/array-equal": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz",
+ "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -4469,6 +4528,11 @@
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"dev": true
},
+ "node_modules/cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="
+ },
"node_modules/cssstyle": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
@@ -4669,6 +4733,18 @@
"node": ">=12"
}
},
+ "node_modules/daikon": {
+ "version": "1.2.46",
+ "resolved": "https://registry.npmjs.org/daikon/-/daikon-1.2.46.tgz",
+ "integrity": "sha512-S8dTTlsWYTH3LQztjTW9KnNvxDeL2mr2cau0auLdYMJe4TrocYP1PmidHizO3rXUs+gXpBWI1PQ2qvB4b21QFw==",
+ "dependencies": {
+ "@wearemothership/dicom-character-set": "^1.0.4-opt.1",
+ "fflate": "*",
+ "jpeg-lossless-decoder-js": "2.0.7",
+ "pako": "^2.1",
+ "xss": "1.0.14"
+ }
+ },
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5542,6 +5618,11 @@
"node-fetch": "~2.6.1"
}
},
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
+ },
"node_modules/figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@@ -5797,6 +5878,11 @@
"assert-plus": "^1.0.0"
}
},
+ "node_modules/gl-matrix": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
+ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -6747,6 +6833,11 @@
"url": "https://github.com/sponsors/panva"
}
},
+ "node_modules/jpeg-lossless-decoder-js": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.0.7.tgz",
+ "integrity": "sha512-tbZlhFkKmx+JaqVMkq47SKWGuXLkIaV8fTbnhO39dYEnQrSShLGuLCGb0n6ntXjtmk6oAWGiIriWOLwj9od0yQ=="
+ },
"node_modules/js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
@@ -7405,6 +7496,14 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/nifti-reader-js": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/nifti-reader-js/-/nifti-reader-js-0.6.8.tgz",
+ "integrity": "sha512-yIKNVzYFiUcSHazoR+sd6Ka7sUmZTabaVqJRFxbdlAKR1hnPBuNP71g3AyApo37nJ3k41c632QPij5q7gF1YPQ==",
+ "dependencies": {
+ "fflate": "*"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
@@ -7654,6 +7753,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -8377,7 +8481,6 @@
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
- "dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
@@ -9816,6 +9919,26 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
+ "node_modules/xss": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz",
+ "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==",
+ "dependencies": {
+ "commander": "^2.20.3",
+ "cssfilter": "0.0.10"
+ },
+ "bin": {
+ "xss": "bin/xss"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/xss/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -10833,6 +10956,19 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "@lukeed/csprng": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
+ "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA=="
+ },
+ "@lukeed/uuid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz",
+ "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==",
+ "requires": {
+ "@lukeed/csprng": "^1.1.0"
+ }
+ },
"@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
@@ -10975,6 +11111,22 @@
}
}
},
+ "@niivue/niivue": {
+ "version": "0.46.0",
+ "resolved": "https://registry.npmjs.org/@niivue/niivue/-/niivue-0.46.0.tgz",
+ "integrity": "sha512-fw4/14lxbb4Q9qEDW+eMMfC2wfGTb0x0cgN1VnHsm1bq2kv/xXBQRQJrloQ1Ma5qNweBwbJynEEPm8fXbN8Bgw==",
+ "requires": {
+ "@lukeed/uuid": "^2.0.1",
+ "@rollup/rollup-linux-x64-gnu": "^4.18.1",
+ "@ungap/structured-clone": "^1.2.0",
+ "array-equal": "^1.0.2",
+ "daikon": "^1.2.46",
+ "fflate": "^0.8.2",
+ "gl-matrix": "^3.4.3",
+ "nifti-reader-js": "^0.6.8",
+ "rxjs": "^7.8.1"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -12278,6 +12430,11 @@
"eslint-visitor-keys": "^3.4.3"
}
},
+ "@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+ },
"@vitejs/plugin-react": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz",
@@ -12372,6 +12529,11 @@
"tinyrainbow": "^1.2.0"
}
},
+ "@wearemothership/dicom-character-set": {
+ "version": "1.0.4-opt.1",
+ "resolved": "https://registry.npmjs.org/@wearemothership/dicom-character-set/-/dicom-character-set-1.0.4-opt.1.tgz",
+ "integrity": "sha512-stqhnpawYHY2UZKj4RHTF71ab3q3z8S1SO9ToQKjsHQwowUdFVo6YFea93psFux3yqNbRlQjwoCdPjHcD0YQzw=="
+ },
"abortcontroller-polyfill": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
@@ -12497,6 +12659,11 @@
"is-array-buffer": "^3.0.4"
}
},
+ "array-equal": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz",
+ "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA=="
+ },
"asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -12955,6 +13122,11 @@
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"dev": true
},
+ "cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="
+ },
"cssstyle": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
@@ -13108,6 +13280,18 @@
"d3-transition": "2 - 3"
}
},
+ "daikon": {
+ "version": "1.2.46",
+ "resolved": "https://registry.npmjs.org/daikon/-/daikon-1.2.46.tgz",
+ "integrity": "sha512-S8dTTlsWYTH3LQztjTW9KnNvxDeL2mr2cau0auLdYMJe4TrocYP1PmidHizO3rXUs+gXpBWI1PQ2qvB4b21QFw==",
+ "requires": {
+ "@wearemothership/dicom-character-set": "^1.0.4-opt.1",
+ "fflate": "*",
+ "jpeg-lossless-decoder-js": "2.0.7",
+ "pako": "^2.1",
+ "xss": "1.0.14"
+ }
+ },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -13762,6 +13946,11 @@
"node-fetch": "~2.6.1"
}
},
+ "fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
+ },
"figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@@ -13944,6 +14133,11 @@
"assert-plus": "^1.0.0"
}
},
+ "gl-matrix": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
+ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
+ },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -14613,6 +14807,11 @@
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="
},
+ "jpeg-lossless-decoder-js": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.0.7.tgz",
+ "integrity": "sha512-tbZlhFkKmx+JaqVMkq47SKWGuXLkIaV8fTbnhO39dYEnQrSShLGuLCGb0n6ntXjtmk6oAWGiIriWOLwj9od0yQ=="
+ },
"js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
@@ -15142,6 +15341,14 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "nifti-reader-js": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/nifti-reader-js/-/nifti-reader-js-0.6.8.tgz",
+ "integrity": "sha512-yIKNVzYFiUcSHazoR+sd6Ka7sUmZTabaVqJRFxbdlAKR1hnPBuNP71g3AyApo37nJ3k41c632QPij5q7gF1YPQ==",
+ "requires": {
+ "fflate": "*"
+ }
+ },
"node-fetch": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
@@ -15303,6 +15510,11 @@
"aggregate-error": "^3.0.0"
}
},
+ "pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+ },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -15812,7 +16024,6 @@
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
- "dev": true,
"requires": {
"tslib": "^2.1.0"
}
@@ -16798,6 +17009,22 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
+ "xss": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz",
+ "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==",
+ "requires": {
+ "commander": "^2.20.3",
+ "cssfilter": "0.0.10"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ }
+ }
+ },
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
diff --git a/compose/neurosynth-frontend/package.json b/compose/neurosynth-frontend/package.json
index a11e3719..e371210c 100644
--- a/compose/neurosynth-frontend/package.json
+++ b/compose/neurosynth-frontend/package.json
@@ -19,6 +19,7 @@
"@mui/styles": "^5.5.1",
"@mui/system": "^5.5.1",
"@mui/x-data-grid": "^5.10.0",
+ "@niivue/niivue": "^0.46.0",
"@reactour/tour": "^2.10.3",
"@sentry/react": "^7.48.0",
"@tanstack/react-table": "^8.20.5",
diff --git a/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx
new file mode 100644
index 00000000..e43c5383
--- /dev/null
+++ b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx
@@ -0,0 +1,104 @@
+import { Box, Slider, Typography } from '@mui/material';
+import { useEffect, useRef, useState } from 'react';
+import { Niivue, SHOW_RENDER } from '@niivue/niivue';
+
+let niivue: Niivue;
+
+const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
+ const canvasRef = useRef(null);
+ const [thresholdPositive, setThresholdPositive] = useState(0);
+ const [thresholdNegative, setThresholdNegative] = useState(0);
+
+ const handleUpdateThresholdPositive = (event: Event, newValue: number | number[]) => {
+ if (!niivue) return;
+ setThresholdPositive(newValue as number);
+ niivue.volumes[1].cal_min = newValue as number;
+ niivue.updateGLVolume();
+ };
+
+ const handkleUpdateThresholdNegative = (event: Event, newValue: number | number[]) => {
+ if (!niivue) return;
+ setThresholdNegative(newValue as number);
+ niivue.volumes[1].cal_minNeg = -2;
+ niivue.volumes[1].cal_maxNeg = newValue as number;
+ niivue.updateGLVolume();
+ };
+
+ useEffect(() => {
+ if (!canvasRef.current) return;
+
+ const volumes = [
+ {
+ url: 'https://neurovault.org/static/images/GenericMNI.nii.gz',
+ colormap: 'gray',
+ opacity: 1,
+ },
+ {
+ url: imageURL,
+ colorMap: 'warm',
+ colormapNegative: 'winter',
+ cal_min: 0,
+ cal_max: 2,
+ cal_minNeg: -1,
+ cal_maxNeg: -2,
+ opacity: 1,
+ },
+ ];
+
+ niivue = new Niivue({
+ show3Dcrosshair: true,
+ });
+
+ niivue.opts.isColorbar = true;
+ niivue.setSliceMM(false);
+
+ niivue.attachToCanvas(canvasRef.current);
+ niivue.addVolumesFromUrl(volumes).then(() => {
+ niivue.volumes[1].alphaThreshold = 0;
+ niivue.volumes[0].colorbarVisible = false;
+ niivue.volumes[1].cal_minNeg = -1.0;
+ niivue.volumes[1].cal_maxNeg = -2.0;
+ niivue.opts.multiplanarShowRender = SHOW_RENDER.ALWAYS;
+ niivue.setInterpolation(true);
+ niivue.updateGLVolume();
+ });
+ }, [imageURL]);
+
+ return (
+
+
+
+
+ -Threshold
+
+
+
+
+
+ +Threshold
+
+
+
+
+
+
+
+
+ );
+};
+
+export default NiiVueVisualizer;
diff --git a/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx b/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx
index aff9bc8a..1d8b5182 100644
--- a/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx
+++ b/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx
@@ -1,8 +1,4 @@
-import {
- MetaAnalysisReturn,
- NeurovaultFile,
- ResultReturn,
-} from 'neurosynth-compose-typescript-sdk';
+import { MetaAnalysisReturn, NeurovaultFile, ResultReturn } from 'neurosynth-compose-typescript-sdk';
export const getResultStatus = (
metaAnalysisObj: MetaAnalysisReturn | undefined,
@@ -29,21 +25,17 @@ export const getResultStatus = (
severity: 'error',
};
- if (
- metaAnalysisResult.neurovault_collection?.files &&
- metaAnalysisResult.neurovault_collection.files.length === 0
- )
+ if (metaAnalysisResult.neurovault_collection?.files && metaAnalysisResult.neurovault_collection.files.length === 0)
return {
statusText: 'Detected run but no result found',
color: 'warning',
severity: 'warning',
};
- const allFilesAreValid = (
- metaAnalysisResult.neurovault_collection.files as Array
- ).every((file) => !!file.image_id);
- if (!allFilesAreValid)
- return { statusText: 'Latest Run Failed', color: 'error', severity: 'error' };
+ const allFilesAreValid = (metaAnalysisResult.neurovault_collection.files as Array).every(
+ (file) => !!file.image_id
+ );
+ if (!allFilesAreValid) return { statusText: 'Latest Run Failed', color: 'error', severity: 'error' };
if (!metaAnalysisObj?.neurostore_analysis?.neurostore_id) {
return {
diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx
index 3f26438b..e3902430 100644
--- a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx
+++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx
@@ -1,68 +1,51 @@
-import { Box, Link, Paper, Typography } from "@mui/material";
-import {
- MetaAnalysisReturn,
- ResultReturn,
-} from "neurosynth-compose-typescript-sdk";
-import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
-import useGetAnalysisById from "hooks/analyses/useGetAnalysisById";
-import StudyPoints from "pages/Study/components/StudyPoints";
-import { PointReturn } from "neurostore-typescript-sdk";
-import StateHandlerComponent from "components/StateHandlerComponent/StateHandlerComponent";
-import { studyPointsToStorePoints } from "pages/Study/store/StudyStore.helpers";
-import { getResultStatus } from "helpers/MetaAnalysis.helpers";
+import { Box, Link, Typography } from '@mui/material';
+import StateHandlerComponent from 'components/StateHandlerComponent/StateHandlerComponent';
+import NiiVueVisualizer from 'components/Visualizer/NiiVueVisualizer';
+import useGetAnalysisById from 'hooks/analyses/useGetAnalysisById';
+import { PointReturn } from 'neurostore-typescript-sdk';
+import { MetaAnalysisReturn, ResultReturn } from 'neurosynth-compose-typescript-sdk';
+import StudyPoints from 'pages/Study/components/StudyPoints';
+import { studyPointsToStorePoints } from 'pages/Study/store/StudyStore.helpers';
const DisplayMetaAnalysisResult: React.FC<{
metaAnalysis: MetaAnalysisReturn | undefined;
metaAnalysisResult: ResultReturn | undefined;
}> = (props) => {
- const resultStatus = getResultStatus(
- props.metaAnalysis,
- props.metaAnalysisResult
- );
-
const { data, isLoading, isError } = useGetAnalysisById(
props.metaAnalysis?.neurostore_analysis?.neurostore_id || undefined
);
- const { points, analysisSpace, analysisMap } = studyPointsToStorePoints(
- (data?.points || []) as PointReturn[]
- );
+ const { points, analysisSpace, analysisMap } = studyPointsToStorePoints((data?.points || []) as PointReturn[]);
- const neurovaultLink =
- props.metaAnalysisResult?.neurovault_collection?.url || "";
+ const neurovaultLink = props.metaAnalysisResult?.neurovault_collection?.url || '';
return (
-
-
-
- Result
-
- {resultStatus.color === "error" && (
-
-
-
- {resultStatus.statusText}
-
-
- )}
+
+
+
+ item 1
+ item 2
+ item 3
+ item 3
+ item 3
+ item 3
+ item 3
+ item 3
+
+
+
+
-
-
+
+
Neurovault
Neurovault Collection Link
@@ -76,7 +59,7 @@ const DisplayMetaAnalysisResult: React.FC<{
points={points}
/>
-
+
);
};
diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx
index 12f6f001..c488c31c 100644
--- a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx
+++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx
@@ -5,10 +5,10 @@ import { ResultReturn } from 'neurosynth-compose-typescript-sdk';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import DisplayMetaAnalysisResult from './DisplayMetaAnalysisResult';
-import DisplayMetaAnalysisSpecification from './MetaAnalysisSpecification';
import MetaAnalysisResultStatusAlert from './MetaAnalysisResultStatusAlert';
+import DisplayMetaAnalysisSpecification from './MetaAnalysisSpecification';
-const MetaAnalysisResult: React.FC = (props) => {
+const MetaAnalysisResult: React.FC = () => {
const { projectId, metaAnalysisId } = useParams<{
projectId: string;
metaAnalysisId: string;
diff --git a/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx b/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx
index 69312e60..b180f0c8 100644
--- a/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx
+++ b/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx
@@ -50,11 +50,7 @@ const ProjectPage: React.FC = (props) => {
const userCanEdit = useUserCanEdit(projectUser || undefined);
const getProjectIsError = useProjectIsError();
- useGuard(
- '/',
- 'No project found with id: ' + projectId,
- !getProjectIsLoading && getProjectIsError
- );
+ useGuard('/', 'No project found with id: ' + projectId, !getProjectIsLoading && getProjectIsError);
const tab = useMemo(() => {
if (!metaAnalysesTabEnabled) return 0;
@@ -108,9 +104,7 @@ const ProjectPage: React.FC = (props) => {
- updateProjectDescription(updatedDescription)
- }
+ onSave={(updatedDescription, label) => updateProjectDescription(updatedDescription)}
textFieldSx={{ input: { fontSize: '1.25rem' } }}
textToEdit={projectDescription || ''}
editIconIsVisible={userCanEdit}
@@ -186,8 +180,6 @@ const ProjectPage: React.FC = (props) => {
navigate(`/projects/${projectId}/project`)}
sx={{
- padding: '1rem',
- fontSize: '1.2rem',
color: tab === 0 ? '#ef8a24 !important' : 'primary.main',
fontWeight: tab === 0 ? 'bold' : 'normal',
}}
@@ -197,8 +189,6 @@ const ProjectPage: React.FC = (props) => {
navigate(`/projects/${projectId}/meta-analyses`)}
sx={{
- padding: '1rem',
- fontSize: '1.2rem',
color: tab === 1 ? '#ef8a24 !important' : 'primary.main',
fontWeight: tab === 1 ? 'bold' : 'normal',
}}