Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
faf548f
fix(search): resolve relative-path iolinks URLs for openneuro file links
elainefan331 May 26, 2026
3761e82
feat(bids-converter): add public BIDS Converter page and navbar link
elainefan331 May 26, 2026
11b77bd
feat(bids-converter): add mode selection dialog and private/save togg…
elainefan331 May 26, 2026
7dbb335
feat(dashboard): open Projects tab directly from BIDS Converter redirect
elainefan331 May 27, 2026
71e5ffb
feat(bids-converter): add private mode visual indicator with slate gr…
elainefan331 May 27, 2026
d0537a4
feat(bids-converter): improve UI labels, tooltips, and private mode UX
elainefan331 May 27, 2026
b023711
feat(llm-panel): improve AI Assistant UI labels and UX
elainefan331 May 27, 2026
cf2e445
fix(llm-panel): clear bidsPlan on step 2 re-run so progress shows in …
elainefan331 May 27, 2026
028ee23
feat(llm-panel): UI polish for file tree and output panel
elainefan331 May 27, 2026
60f51ad
feat(ollama): require authentication on Ollama proxy endpoints
elainefan331 May 27, 2026
68218d5
feat(ollama): add public endpoint with 10 requests/day IP rate limit
elainefan331 May 27, 2026
1aa509a
feat(llm-panel): add local Ollama provider for private mode with conf…
elainefan331 May 29, 2026
72b6b97
feat(navbar): add AutoBIDSify dropdown with GitHub link and web app; …
elainefan331 May 29, 2026
f344482
feat(autobidsify): rebrand BIDS Converter to AutoBIDSify with navbar …
elainefan331 May 29, 2026
a69ff4a
feat(llm-panel): add local AI support for Ollama, LM Studio, and Jan …
elainefan331 May 29, 2026
1dfc255
feat(ollama): use qwen3.6:27b on server for LLM, hide model selector
elainefan331 May 29, 2026
34d0260
fix(ollama): increase public endpoint rate limit to 20 requests/day
elainefan331 May 29, 2026
60cfdb0
fix(preview): make 2D chart responsive to container width
elainefan331 Jun 1, 2026
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
26 changes: 22 additions & 4 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"express-rate-limit": "^8.5.2",
"jsonwebtoken": "^9.0.2",
"nanoid": "^3.3.11",
"nodemailer": "^7.0.11",
Expand Down
6 changes: 5 additions & 1 deletion backend/src/controllers/couchdb.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,11 @@ const getDatasetFilesManifest = async (req, res) => {
}
);

const files = rows.filter((r) => r.url);
const resolveUrl = (url) => {
if (!url || url.startsWith("http")) return url;
return `https://neurojson.org/io/stat.cgi?action=get&db=${dbName}&doc=${dsName}&${url}`;
};
const files = rows.filter((r) => r.url).map((r) => ({ ...r, url: resolveUrl(r.url) }));
const urls = files.map((r) => r.url);
const baseName = `${dbName}_${dsName}_${exts.join("_")}`;
const extLabel = exts.join(", ");
Expand Down
5 changes: 3 additions & 2 deletions backend/src/controllers/ollama.controller.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const OLLAMA_BASE_URL = "http://jin.neu.edu:11434";
const OLLAMA_MODEL = "qwen3.6:27b";

const proxyChat = async (req, res) => {
console.log("🟣 [Ollama] proxyChat hit — model:", req.body.model);
console.log("🟣 [Ollama] proxyChat hit — model:", OLLAMA_MODEL);
try {
const response = await fetch(`${OLLAMA_BASE_URL}/v1/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(req.body),
body: JSON.stringify({ ...req.body, model: OLLAMA_MODEL }),
});

const data = await response.json();
Expand Down
17 changes: 17 additions & 0 deletions backend/src/routes/ollama.public.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const express = require("express");
const router = express.Router();
const rateLimit = require("express-rate-limit");
const { proxyChat, getTags } = require("../controllers/ollama.controller");

const dailyLimit = rateLimit({
windowMs: 24 * 60 * 60 * 1000, // 24 hours
max: 20,
standardHeaders: true,
legacyHeaders: false,
message: { error: "Daily request limit reached. You can send up to 20 requests per day from this IP." },
});

router.post("/chat", dailyLimit, proxyChat);
// router.get("/tags", dailyLimit, getTags);

module.exports = router;
5 changes: 3 additions & 2 deletions backend/src/routes/ollama.routes.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const express = require("express");
const router = express.Router();
const { proxyChat, getTags } = require("../controllers/ollama.controller");
const { requireAuth } = require("../middleware/auth.middleware");

router.post("/chat", proxyChat);
router.get("/tags", getTags);
router.post("/chat", requireAuth, proxyChat);
// router.get("/tags", requireAuth, getTags);

module.exports = router;
2 changes: 2 additions & 0 deletions backend/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const datasetsRoutes = require("./routes/datasets.routes");
const collectionRoutes = require("./routes/collection.route");
const projectRoutes = require("./routes/projects.routes");
const ollamaRoutes = require("./routes/ollama.routes");
const ollamaPublicRoutes = require("./routes/ollama.public.routes");

const app = express();
const PORT = process.env.PORT || 5000;
Expand Down Expand Up @@ -51,6 +52,7 @@ app.use("/api/v1/datasets", datasetsRoutes);
app.use("/api/v1/collections", collectionRoutes);
app.use("/api/v1/projects", projectRoutes);
app.use("/api/v1/ollama", ollamaRoutes);
app.use("/api/v1/ollama-public", ollamaPublicRoutes);

// health check endpoint
app.get("/api/health", async (req, res) => {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@
"react-redux": "^8.1.2",
"react-router-dom": "^6.15.0",
"react-scripts": "^5.0.1",
"react-syntax-highlighter": "^15.6.1",
"react-syntax-highlighter": "16.1.1",
"sharp": "^0.33.5",
"stats-js": "^1.0.1",
"stats.js": "0.17.0",
"three": "0.145.0",
"typescript": "^5.1.6",
"uplot": "1.6.17",
"uplot": "1.6.32",
"web-vitals": "^2.1.0",
"xlsx": "^0.18.5"
},
Expand Down
91 changes: 88 additions & 3 deletions src/components/NavBar/NavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ const NavItems: React.FC = () => {
const [signupOpen, setSignupOpen] = useState(false);

// Resources dropdown state
const [resourcesAnchor, setResourcesAnchor] = useState<null | HTMLElement>(
null
);
const [resourcesAnchor, setResourcesAnchor] = useState<null | HTMLElement>(null);
const resourcesOpen = Boolean(resourcesAnchor);

// AutoBIDSify dropdown state
const [autobidsifyAnchor, setAutobidsifyAnchor] = useState<null | HTMLElement>(null);
const autobidsifyOpen = Boolean(autobidsifyAnchor);

const handleLogout = () => {
dispatch(logoutUser());
navigate("/");
Expand All @@ -53,6 +55,14 @@ const NavItems: React.FC = () => {
setResourcesAnchor(null);
};

const handleAutobidsifyClick = (event: React.MouseEvent<HTMLElement>) => {
setAutobidsifyAnchor(event.currentTarget);
};

const handleAutobidsifyClose = () => {
setAutobidsifyAnchor(null);
};

const resourcesMenu = [
{
category: "Converter",
Expand Down Expand Up @@ -378,6 +388,38 @@ const NavItems: React.FC = () => {
</Grid>
))}

{/* AutoBIDSify Dropdown */}
<Grid item>
<Box
onClick={handleAutobidsifyClick}
sx={{ display: "flex", alignItems: "center", gap: 0.5, cursor: "pointer" }}
>
<Typography
align="center"
fontWeight={600}
lineHeight={"1.5rem"}
letterSpacing={"0.05rem"}
sx={{
fontSize: { xs: "0.8rem", sm: "1rem" },
color: Colors.white,
transition: "color 0.3s ease, transform 0.3s ease",
textTransform: "uppercase",
"&:hover": { transform: "scale(1.2)" },
}}
>
AutoBIDSify
</Typography>
<KeyboardArrowDownIcon
sx={{
color: Colors.white,
fontSize: "1.2rem",
transition: "transform 0.3s ease",
transform: autobidsifyOpen ? "rotate(180deg)" : "rotate(0deg)",
}}
/>
</Box>
</Grid>

{/* Resources Dropdown */}
<Grid item>
<Box
Expand Down Expand Up @@ -511,6 +553,49 @@ const NavItems: React.FC = () => {
</Box>
))}
</Menu>

{/* AutoBIDSify Dropdown Menu */}
<Menu
anchorEl={autobidsifyAnchor}
open={autobidsifyOpen}
onClose={handleAutobidsifyClose}
PaperProps={{
sx: {
bgcolor: Colors.darkPurple,
color: Colors.white,
minWidth: "220px",
mt: 1,
},
}}
>
<MenuItem
onClick={() => {
window.open("https://github.com/COTILab/autobidsify", "_blank");
handleAutobidsifyClose();
}}
sx={{
fontSize: "0.9rem",
color: Colors.white,
"&:hover": { bgcolor: Colors.purpleGrey, color: Colors.darkPurple },
}}
>
AutoBIDSify (GitHub)
</MenuItem>
<MenuItem
onClick={() => {
navigate(RoutesEnum.BIDS_CONVERTER);
handleAutobidsifyClose();
}}
sx={{
fontSize: "0.9rem",
color: Colors.white,
"&:hover": { bgcolor: Colors.purpleGrey, color: Colors.darkPurple },
}}
>
AutoBIDSify Web
</MenuItem>
</Menu>

<UserLogin
open={loginOpen}
onClose={() => setLoginOpen(false)}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Routes.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BidsConverterPage from "pages/BidsConverterPage";
import ScrollToTop from "./ScrollToTop";
import CompleteProfile from "./User/CompleteProfile";
import CollectionDetailPage from "./User/Dashboard/CollectionDetailPage";
Expand Down Expand Up @@ -68,6 +69,7 @@ const Routes = () => (
element={<CollectionDetailPage />}
/>
<Route path="/projects/:projectId" element={<DatasetOrganizer />} />
<Route path={RoutesEnum.BIDS_CONVERTER} element={<BidsConverterPage />} />
</Route>
</RouterRoutes>
</>
Expand Down
14 changes: 13 additions & 1 deletion src/components/SearchPage/DatasetCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ interface DatasetCardProps {
}

/** ---------- utility helpers ---------- **/
// Some iolinks records (older openneuro links view) store a relative path like
// "file=sub-01/anat/sub-01_T1w.nii&size=1" instead of a full stat.cgi URL.
// Reconstruct the full URL so the browser can follow it.
const resolveFileUrl = (
dbname: string,
dsname: string,
url?: string
): string => {
if (!url) return "";
if (url.startsWith("http")) return url;
return `https://neurojson.org/io/stat.cgi?action=get&db=${dbname}&doc=${dsname}&${url}`;
};
const normalize = (s: string) =>
s
?.replace(/[\u2018\u2019\u2032]/g, "'") // curly → straight
Expand Down Expand Up @@ -504,7 +516,7 @@ const DatasetCard: React.FC<DatasetCardProps> = ({
return (
<li key={i}>
<MuiLink
href={v.url}
href={resolveFileUrl(dbname, dsname, v.url)}
target="_blank"
rel="noopener noreferrer"
underline="hover"
Expand Down
9 changes: 4 additions & 5 deletions src/components/User/Dashboard/DatasetOrganizer/FileTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ const FileTree: React.FC<FileTreeProps> = ({
}}
>
<Box>
<Typography variant="subtitle2" fontWeight="600">
<Typography variant="subtitle2" fontWeight={800}>
Virtual File System
</Typography>
<Typography variant="caption" color="text.secondary">
Expand Down Expand Up @@ -631,12 +631,11 @@ const FileTree: React.FC<FileTreeProps> = ({
borderColor: "divider",
}}
>
<FolderSpecial sx={{ color: Colors.darkGreen, fontSize: 16 }} />
<Typography
variant="caption"
sx={{ color: Colors.darkGreen, fontWeight: 600 }}
variant="body2"
sx={{ color: Colors.darkPurple, fontWeight: 400 }}
>
Saved Outputs
BIDS Conversion Package Preview
</Typography>
</Box>
{outputFiles
Expand Down
Loading
Loading