Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dd513cf
Refactor StatisticsBanner with reusable StatItem component for cleane…
elainefan331 Jul 25, 2025
0001f5f
fix: make the nav items responsive to avoid two-line layout on screen…
elainefan331 Jul 25, 2025
5f754f6
feat: add tooptip to v1 for legacy site info
elainefan331 Jul 25, 2025
6c23312
css: update about page videos layout; refs #66
elainefan331 Jul 26, 2025
ff2cd95
feat: enable smooth scroll to tutorial videos when clicking icon butt…
elainefan331 Jul 27, 2025
219cf4a
feat: add download video to the about page; closes #66
elainefan331 Aug 4, 2025
d9847ab
fix: resolve issue with fNIRS data display to ensure correct rendering
elainefan331 Aug 12, 2025
c3ed4b6
fix: handle cached fNIRS/time-series data in preview to prevent stuck…
elainefan331 Aug 12, 2025
0704c19
fix: ensures is2DPreviewCandidate always relies on the preview.js output
elainefan331 Aug 14, 2025
0479086
refactor: clean up redundant logic in dataset detail page
elainefan331 Aug 14, 2025
94f0c02
feat: update section3 img to 4 clickable images
elainefan331 Aug 15, 2025
40da71b
feat: add src links for section3 images
elainefan331 Aug 15, 2025
2d2fc39
fix: update the img path of section3
elainefan331 Aug 15, 2025
fc9a0ec
feat: add ScrollToTop component to reset scroll on route change
elainefan331 Aug 18, 2025
af01303
feat: update the atlas img in section3
elainefan331 Aug 18, 2025
7fd733e
Merge pull request #78 from NeuroJSON/dev-fan
elainefan331 Aug 18, 2025
dccd1e0
feat: display AISummary section in dataset detail header
elainefan331 Aug 20, 2025
aecb924
feat: use database logo as card bg on database page
elainefan331 Aug 20, 2025
c55f31b
feat: split subjects json into folder style tree(test)
elainefan331 Aug 21, 2025
c6b37b8
feat: add AI summary title in dataset page
elainefan331 Aug 21, 2025
7206110
feat: display database logo as Avatar instead of card background
elainefan331 Aug 21, 2025
ae58f53
Merge pull request #79 from NeuroJSON/dev-fan
elainefan331 Aug 22, 2025
0e0ed3e
feat: add section dividers to separate features in about page
elainefan331 Aug 25, 2025
73b301f
feat: add convert icon and video in about page
elainefan331 Aug 25, 2025
72b8b6f
feat: add four preview videos in about page
elainefan331 Aug 25, 2025
186d8f0
Merge pull request #84 from NeuroJSON/dev-fan
elainefan331 Aug 25, 2025
335f88d
feat: modify readme
elainefan331 Aug 26, 2025
a97bc80
feat: modify the readme content
elainefan331 Aug 26, 2025
7c9554a
readme updated: add content of download neuroj via docker
elainefan331 Aug 26, 2025
6f6fdcf
Merge pull request #86 from NeuroJSON/dev-fan
elainefan331 Aug 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 103 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,123 @@
# Getting Started with Create React App
# NeuroJSON.io

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
**Free Data Worth Sharing**

## Available Scripts
[![Website](https://img.shields.io/badge/website-NeuroJSON.io-blue)](https://neurojson.io)
[![FAIR](https://img.shields.io/badge/FAIR-Findable%2C%20Accessible%2C%20Interoperable%2C%20Reusable-purple)](#)

In the project directory, you can run:
---

### `yarn start`
## 📖 Overview

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
[NeuroJSON.io](https://neurojson.io) is an **NIH-funded open data portal** for **neuroimaging datasets**, designed to make scientific data:

The page will reload if you make edits.\
You will also see any lint errors in the console.
- **Findable**: Fully searchable metadata and datasets
- **Accessible**: Open, lightweight JSON format
- **Interoperable**: Compatible across platforms and programming languages
- **Reusable**: Rich metadata, visualizations, and long-term viability

### `yarn test`
NeuroJSON leverages **modern web technologies and scalable NoSQL databases** and the **JSON standard** to distribute large-scale, complex imaging data in a **human- and machine-readable** form.

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
---

### `yarn build`
## 🚀 Features

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
- **Search** — Browse across multiple databases, datasets, and modalities
- **Preview** — Interact with JSON metadata and visualize imaging data (2D/3D) in browser
- **Download** — Retrieve datasets in JSON format, ready for use in Python, MATLAB/Octave, C++, and more
- **Upload** - Contribute your own datasets to NeuroJSON.io ([Steps to contribute](#-for-data-contributors))
- **REST API** — Automate your workflows with lightweight endpoints, designed for smooth integration into both local analyses and large-scale pipelines

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
---

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
## 🏁 Getting Started

### `yarn eject`
1. Visit [https://neurojson.io](https://neurojson.io)
2. Use the **search page** to find datasets or subjects of interest
3. Click any dataset to **preview** or **download** data
4. For automation, use the **[REST API](#rest-api)**

**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
---

If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
## 👩‍🔬 For Data Contributors

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
We welcome your datasets!

You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
- NeuroJSON prefers **BIDS-compliant data**
- Convert datasets to JSON using [`NeuroJSON Client(neuroj)`](https://github.com/NeuroJSON/neuroj):
- Install Docker (skip this step if you already have it): [Get Docker](https://docs.docker.com/get-docker/)
- Download neuroj via docker:
```
docker pull openjdata/neuroj:v2025
```
- Example (convert a single dataset to JSON via neuroj):
```
docker run openjdata/neuroj:v2025 neuroj -i /path/to/database/rootfolder -o /path/to/output/json/folder -db openneuro -ds ds000001 --convert
```
- See the full list of available [NeuroJSON Client commands](https://hub.docker.com/r/openjdata/neuroj)
- Watch our [`tutorial video - convert data`](https://neurojson.io/about)

## Learn More
### Steps to contribute

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
1. Download NeuroJSON Client (neuroj)
2. Convert your dataset to JSON
3. Validate metadata
4. [Open a ticket](https://github.com/NeuroJSON/registry) to upload your dataset

To learn React, check out the [React documentation](https://reactjs.org/).
Contributions ensure **long-term public availability and reusability**.

---

## 💻 For Developers

### REST API

- Lightweight endpoints for download
- JSON responses designed for integration with cloud and local workflows

Example (Load by URL with REST-API in Python):

```
pip install jdata bjdata numpy
```

```
import jdata as jd
data = jd.loadurl('https://neurojson.io:7777/openneuro/ds000001')

# List all externally linked files
links = jd.jsonpath(data, '$.._DataLink_')

# Download & cache anatomical nii.gz data for sub-01/sub-02
jd.jdlink(links, {'regex': 'anat/sub-0[12]_.*.nii'})
```

---

## 📊 Current Stats (as of latest release)

| Metric | Value |
| --------- | ----------- |
| Databases | **22** |
| Datasets | **1,529** |
| Subjects | **58,026** |
| Links | **580,857** |
| Data Size | **38 TB** |

---

## 🤝 Governance & Support

- NIH-funded data dissemination service
- Maintained by the [COTI Lab, Northeastern University](http://fanglab.org/wiki/)
- Contact: **admin@neurojson.io**

---

## 🛠 Roadmap

We’re continuing to grow NeuroJSON.io to better serve the community. Some of the upcoming directions include:

- 🔜 **Expanding databases and datasets** — broadening coverage to include more sources and subjects
- 🔜 **Enhancing visualization** — improving 2D/3D previews for richer and more intuitive exploration of data
- 🔜 **Streamlining uploads** — introducing new features to make dataset contributions more automatic and user-friendly
Binary file added public/img/about_page/convert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/section3/atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/section3/fnirs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/section3/mesh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/section3/mri.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions src/components/DatasetDetailPage/FileTree/FileTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import FileTreeRow from "./FileTreeRow";
import type { TreeNode } from "./types";
import FolderIcon from "@mui/icons-material/Folder";
import { Box, Typography } from "@mui/material";
import React from "react";

type Props = {
title: string;
tree: TreeNode[];
filesCount: number;
totalBytes: number;
onPreview: (url: string, index: number) => void;
};

const formatSize = (n: number) => {
if (n < 1024) return `${n} B`;
if (n < 1024 ** 2) return `${(n / 1024).toFixed(1)} KB`;
if (n < 1024 ** 3) return `${(n / 1024 ** 2).toFixed(2)} MB`;
if (n < 1024 ** 4) return `${(n / 1024 ** 3).toFixed(2)} GB`;
return `${(n / 1024 ** 4).toFixed(2)} TB`;
};

const FileTree: React.FC<Props> = ({
title,
tree,
filesCount,
totalBytes,
onPreview,
}) => (
<Box
sx={{
backgroundColor: "#fff",
borderRadius: 2,
border: "1px solid #e0e0e0",
height: "100%",
display: "flex",
flexDirection: "column",
minHeight: 0,
}}
>
<Box
sx={{
px: 2,
py: 1.5,
borderBottom: "1px solid #eee",
display: "flex",
alignItems: "center",
gap: 1,
flexShrink: 0,
}}
>
<FolderIcon />
<Typography sx={{ fontWeight: 700, flex: 1 }}>{title}</Typography>
<Typography variant="body2" sx={{ color: "text.secondary" }}>
Files: {filesCount} &nbsp; Size: {formatSize(totalBytes)}
</Typography>
</Box>

<Box sx={{ flex: 1, minHeight: 0, overflowY: "auto", py: 0.5 }}>
{tree.map((n) => (
<FileTreeRow key={n.path} node={n} level={0} onPreview={onPreview} />
))}
</Box>
</Box>
);

export default FileTree;
130 changes: 130 additions & 0 deletions src/components/DatasetDetailPage/FileTree/FileTreeRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import type { TreeNode } from "./types";
import { formatLeafValue, isPreviewable } from "./utils";
import DownloadIcon from "@mui/icons-material/Download";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import FolderIcon from "@mui/icons-material/Folder";
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
import VisibilityIcon from "@mui/icons-material/Visibility";
import { Box, Button, Collapse, Typography } from "@mui/material";
import React from "react";

type Props = {
node: TreeNode;
level: number;
onPreview: (url: string, index: number) => void;
};

const FileTreeRow: React.FC<Props> = ({ node, level, onPreview }) => {
const [open, setOpen] = React.useState(false);

if (node.kind === "folder") {
return (
<>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 1,
py: 0.5,
px: 1,
cursor: "pointer",
"&:hover": { backgroundColor: "rgba(0,0,0,0.04)" },
}}
onClick={() => setOpen((o) => !o)}
>
<Box sx={{ pl: level * 1.25 }}>
<FolderIcon fontSize="small" />
</Box>
<Typography sx={{ fontWeight: 600, flex: 1 }}>{node.name}</Typography>
{open ? <ExpandLess /> : <ExpandMore />}
</Box>

<Collapse in={open} timeout="auto" unmountOnExit>
{node.children.map((child) => (
<FileTreeRow
key={child.path}
node={child}
level={level + 1}
onPreview={onPreview}
/>
))}
</Collapse>
</>
);
}

return (
<Box
sx={{ display: "flex", alignItems: "flex-start", gap: 1, py: 0.5, px: 1 }}
>
<Box sx={{ pl: level * 1.25, pt: "2px" }}>
<InsertDriveFileIcon fontSize="small" />
</Box>

<Box sx={{ flex: 1, minWidth: 0, overflow: "hidden" }}>
<Typography
title={node.name}
sx={{
fontWeight: 500,
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{node.name}
</Typography>

{!node.link && node.value !== undefined && (
<Typography
title={
node.name === "_ArrayZipData_"
? "[compressed data]"
: typeof node.value === "string"
? node.value
: JSON.stringify(node.value)
}
sx={{
fontFamily: "monospace",
fontSize: "0.85rem",
color: "text.secondary",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
mt: 0.25,
}}
>
{node.name === "_ArrayZipData_"
? "[compressed data]"
: formatLeafValue(node.value)}
</Typography>
)}
</Box>

{node.link?.url && (
<Box sx={{ display: "flex", gap: 1, flexShrink: 0 }}>
<Button
size="small"
variant="text"
onClick={() => window.open(node.link!.url, "_blank")}
startIcon={<DownloadIcon fontSize="small" />}
>
Download
</Button>
{isPreviewable(node.link.url) && (
<Button
size="small"
variant="text"
startIcon={<VisibilityIcon fontSize="small" />}
onClick={() => onPreview(node.link!.url, node.link!.index)}
>
Preview
</Button>
)}
</Box>
)}
</Box>
);
};

export default FileTreeRow;
6 changes: 6 additions & 0 deletions src/components/DatasetDetailPage/FileTree/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type LinkMeta = { url: string; index: number };

// this value can be one of these types
export type TreeNode =
| { kind: "folder"; name: string; path: string; children: TreeNode[] }
| { kind: "file"; name: string; path: string; value?: any; link?: LinkMeta };
Loading