From faf548ff118a7126fe8fa496bf83c11788cbe20a Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Tue, 26 May 2026 14:53:39 -0400 Subject: [PATCH 01/18] fix(search): resolve relative-path iolinks URLs for openneuro file links --- backend/src/controllers/couchdb.controller.js | 6 +- src/components/SearchPage/DatasetCard.tsx | 14 +++- yarn.lock | 78 ++++++++++--------- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/backend/src/controllers/couchdb.controller.js b/backend/src/controllers/couchdb.controller.js index 3700fc8..ab12d0f 100644 --- a/backend/src/controllers/couchdb.controller.js +++ b/backend/src/controllers/couchdb.controller.js @@ -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(", "); diff --git a/src/components/SearchPage/DatasetCard.tsx b/src/components/SearchPage/DatasetCard.tsx index 4243119..7ecefae 100644 --- a/src/components/SearchPage/DatasetCard.tsx +++ b/src/components/SearchPage/DatasetCard.tsx @@ -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 @@ -504,7 +516,7 @@ const DatasetCard: React.FC = ({ return (
  • Date: Tue, 26 May 2026 17:32:27 -0400 Subject: [PATCH 02/18] feat(bids-converter): add public BIDS Converter page and navbar link --- src/components/NavBar/NavItems.tsx | 1 + src/components/Routes.tsx | 2 + src/pages/BidsConverterPage.tsx | 198 +++++++++++++++++++++++++++++ src/types/routes.enum.ts | 1 + 4 files changed, 202 insertions(+) create mode 100644 src/pages/BidsConverterPage.tsx diff --git a/src/components/NavBar/NavItems.tsx b/src/components/NavBar/NavItems.tsx index d2882e6..c102bcf 100644 --- a/src/components/NavBar/NavItems.tsx +++ b/src/components/NavBar/NavItems.tsx @@ -319,6 +319,7 @@ const NavItems: React.FC = () => { { text: "Wiki", url: "https://neurojson.org/Wiki" }, { text: "Search", url: RoutesEnum.SEARCH }, { text: "Databases", url: RoutesEnum.DATABASES }, + { text: "BIDS Converter", url: RoutesEnum.BIDS_CONVERTER }, ].map(({ text, url }) => ( {url?.startsWith("https") ? ( diff --git a/src/components/Routes.tsx b/src/components/Routes.tsx index 5983701..10b15e9 100644 --- a/src/components/Routes.tsx +++ b/src/components/Routes.tsx @@ -1,3 +1,4 @@ +import BidsConverterPage from "pages/BidsConverterPage"; import ScrollToTop from "./ScrollToTop"; import CompleteProfile from "./User/CompleteProfile"; import CollectionDetailPage from "./User/Dashboard/CollectionDetailPage"; @@ -68,6 +69,7 @@ const Routes = () => ( element={} /> } /> + } /> diff --git a/src/pages/BidsConverterPage.tsx b/src/pages/BidsConverterPage.tsx new file mode 100644 index 0000000..76de114 --- /dev/null +++ b/src/pages/BidsConverterPage.tsx @@ -0,0 +1,198 @@ +import DropZone from "components/User/Dashboard/DatasetOrganizer/DropZone"; +import FileTree from "components/User/Dashboard/DatasetOrganizer/FileTree"; +import LLMPanel from "components/User/Dashboard/DatasetOrganizer/LLMPanel"; +import { ArrowBack, GetApp, Psychology } from "@mui/icons-material"; +import { Box, Button, Typography, Alert } from "@mui/material"; +import { Colors } from "design/theme"; +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { FileItem } from "redux/projects/types/projects.interface"; + +const BidsConverterPage: React.FC = () => { + const navigate = useNavigate(); + + const [files, setFiles] = useState([]); + const [selectedIds, setSelectedIds] = useState>(new Set()); + const [expandedIds, setExpandedIds] = useState>(new Set()); + const [showLLMPanel, setShowLLMPanel] = useState(false); + const [error, setError] = useState(null); + const [baseDirectoryPath, setBaseDirectoryPath] = useState(""); + const [evidenceBundle, setEvidenceBundle] = useState(null); + const [trioGenerated, setTrioGenerated] = useState(false); + + const updateFiles = (updater: React.SetStateAction) => + setFiles(updater); + + const updateSelectedIds = (updater: React.SetStateAction>) => + setSelectedIds(updater); + + const updateExpandedIds = (updater: React.SetStateAction>) => + setExpandedIds(updater); + + const updateBaseDirectoryPath = (path: string) => setBaseDirectoryPath(path); + + const handleExportJSON = () => { + const buildTree = (parentId: string | null): any => { + const children = files.filter((f) => f.parentId === parentId); + const result: any = {}; + children.forEach((child) => { + if (child.type === "folder" || child.type === "zip") { + result[child.name] = { + _type: child.type, + _sourcePath: baseDirectoryPath + ? `${baseDirectoryPath}/${child.sourcePath || child.name}`.replace(/\/+/g, "/") + : child.sourcePath || "", + _children: buildTree(child.id), + }; + } else { + const fileData: any = { + _type: "file", + _fileType: child.fileType || "other", + }; + if (child.sourcePath || baseDirectoryPath) { + fileData._sourcePath = baseDirectoryPath + ? `${baseDirectoryPath}/${child.sourcePath || child.name}`.replace(/\/+/g, "/") + : child.sourcePath; + } + if (child.isUserMeta) fileData._isUserMeta = true; + if (child.content) fileData._content = child.content; + if (child.contentType) fileData._contentType = child.contentType; + if (child.note) fileData._note = child.note; + result[child.name] = fileData; + } + }); + return result; + }; + + const exportData = { + _exportDate: new Date().toISOString(), + _totalFiles: files.length, + files: buildTree(null), + }; + + const blob = new Blob([JSON.stringify(exportData, null, 2)], { + type: "application/json", + }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "bids_converter_export.json"; + a.click(); + URL.revokeObjectURL(url); + }; + + return ( + + {/* Header */} + + + + + BIDS Converter + + Organize and rename your dataset files into BIDS format + + + + + + + + + + + {error && ( + setError(null)} sx={{ m: 2 }}> + {error} + + )} + + {/* Main Content */} + + + + {showLLMPanel && ( + setShowLLMPanel(false)} + /> + )} + + + + + + ); +}; + +export default BidsConverterPage; diff --git a/src/types/routes.enum.ts b/src/types/routes.enum.ts index b37a84d..a774051 100644 --- a/src/types/routes.enum.ts +++ b/src/types/routes.enum.ts @@ -4,5 +4,6 @@ enum RoutesEnum { SEARCH = "/search", // New route for the search page ABOUT = "/about", // New route for the about page DASHBOARD = "/dashboard", + BIDS_CONVERTER = "/bids-converter", } export default RoutesEnum; From 11b77bde4464355bfd13077968679dadcb56a542 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Tue, 26 May 2026 18:00:15 -0400 Subject: [PATCH 03/18] feat(bids-converter): add mode selection dialog and private/save toggle bar --- src/pages/BidsConverterPage.tsx | 234 +++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 6 deletions(-) diff --git a/src/pages/BidsConverterPage.tsx b/src/pages/BidsConverterPage.tsx index 76de114..2bbb3f0 100644 --- a/src/pages/BidsConverterPage.tsx +++ b/src/pages/BidsConverterPage.tsx @@ -1,15 +1,43 @@ import DropZone from "components/User/Dashboard/DatasetOrganizer/DropZone"; import FileTree from "components/User/Dashboard/DatasetOrganizer/FileTree"; import LLMPanel from "components/User/Dashboard/DatasetOrganizer/LLMPanel"; -import { ArrowBack, GetApp, Psychology } from "@mui/icons-material"; -import { Box, Button, Typography, Alert } from "@mui/material"; +import UserLogin from "components/User/UserLogin"; +import UserSignup from "components/User/UserSignup"; +import { + ArrowBack, + GetApp, + Psychology, + LockOutlined, + CloudUpload, +} from "@mui/icons-material"; +import { + Box, + Button, + Typography, + Alert, + Dialog, + DialogContent, + DialogTitle, + ToggleButton, + ToggleButtonGroup, +} from "@mui/material"; import { Colors } from "design/theme"; -import React, { useState } from "react"; +import { useAppSelector } from "hooks/useAppSelector"; +import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import { AuthSelector } from "redux/auth/auth.selector"; import { FileItem } from "redux/projects/types/projects.interface"; +type Mode = "private" | "save"; + const BidsConverterPage: React.FC = () => { const navigate = useNavigate(); + const { isLoggedIn } = useAppSelector(AuthSelector); + + const [modeChosen, setModeChosen] = useState(false); + const [mode, setMode] = useState("private"); + const [loginOpen, setLoginOpen] = useState(false); + const [signupOpen, setSignupOpen] = useState(false); const [files, setFiles] = useState([]); const [selectedIds, setSelectedIds] = useState>(new Set()); @@ -20,15 +48,42 @@ const BidsConverterPage: React.FC = () => { const [evidenceBundle, setEvidenceBundle] = useState(null); const [trioGenerated, setTrioGenerated] = useState(false); + // After login succeeds in save mode, redirect to dashboard to create a project + useEffect(() => { + if (isLoggedIn && mode === "save") { + navigate("/dashboard"); + } + }, [isLoggedIn, mode, navigate]); + + const handleChoosePrivate = () => { + setMode("private"); + setModeChosen(true); + }; + + const handleChooseSave = () => { + setMode("save"); + if (isLoggedIn) { + navigate("/dashboard"); + } else { + setLoginOpen(true); + } + }; + + const handleModeBarChange = (_: React.MouseEvent, next: Mode | null) => { + if (!next || next === mode) return; + if (next === "save") { + handleChooseSave(); + } else { + setMode("private"); + } + }; + const updateFiles = (updater: React.SetStateAction) => setFiles(updater); - const updateSelectedIds = (updater: React.SetStateAction>) => setSelectedIds(updater); - const updateExpandedIds = (updater: React.SetStateAction>) => setExpandedIds(updater); - const updateBaseDirectoryPath = (path: string) => setBaseDirectoryPath(path); const handleExportJSON = () => { @@ -148,6 +203,63 @@ const BidsConverterPage: React.FC = () => { + {/* Mode indicator bar */} + + + + + Private Mode + + + + Save to Account + + + + + {mode === "private" + ? "Files are processed locally. Nothing is uploaded. All data is lost when you close this page." + : "Log in to save your work to a project on your account."} + + + {error && ( setError(null)} sx={{ m: 2 }}> {error} @@ -191,6 +303,116 @@ const BidsConverterPage: React.FC = () => { setExpandedIds={updateExpandedIds} /> + + {/* Welcome dialog — shown on first load before user starts working */} + + + How would you like to use BIDS Converter? + + + + {/* Private Mode card */} + + + + Private Mode + + + Work entirely in your browser. No files are uploaded to any + server. All data will be lost when you close the page. + + + + {/* Save to Account card */} + + + + Save to Account + + + Log in to save your work to a project. You can resume it any + time from your dashboard. + + + + + + + { + setLoginOpen(false); + // If user closes login without logging in, fall back to private mode + if (!isLoggedIn) { + setMode("private"); + setModeChosen(true); + } + }} + onSwitchToSignup={() => { + setLoginOpen(false); + setSignupOpen(true); + }} + /> + { + setSignupOpen(false); + if (!isLoggedIn) { + setMode("private"); + setModeChosen(true); + } + }} + onSwitchToLogin={() => { + setSignupOpen(false); + setLoginOpen(true); + }} + /> ); }; From 7dbb33555d39d2d0f7819ce8b0e5c80d3a236f44 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 27 May 2026 11:47:49 -0400 Subject: [PATCH 04/18] feat(dashboard): open Projects tab directly from BIDS Converter redirect --- src/components/User/UserDashboard.tsx | 20 +++++++++++++++++++- src/pages/BidsConverterPage.tsx | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/User/UserDashboard.tsx b/src/components/User/UserDashboard.tsx index 21d4e53..42eab48 100644 --- a/src/components/User/UserDashboard.tsx +++ b/src/components/User/UserDashboard.tsx @@ -24,6 +24,7 @@ import { import { Colors } from "design/theme"; import { useAppSelector } from "hooks/useAppSelector"; import React, { useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; import { AuthSelector } from "redux/auth/auth.selector"; interface TabPanelProps { @@ -48,12 +49,29 @@ function TabPanel(props: TabPanelProps) { ); } +const TAB_INDEX: Record = { + profile: 0, + security: 1, + collections: 2, + liked: 3, + projects: 4, + settings: 5, +}; + +const TAB_NAME = Object.fromEntries( + Object.entries(TAB_INDEX).map(([k, v]) => [v, k]) +) as Record; + const UserDashboard: React.FC = () => { - const [tabValue, setTabValue] = useState(0); + const location = useLocation(); + const navigate = useNavigate(); + const tabParam = new URLSearchParams(location.search).get("tab") ?? ""; + const [tabValue, setTabValue] = useState(TAB_INDEX[tabParam] ?? 0); const { user } = useAppSelector(AuthSelector); const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { setTabValue(newValue); + navigate(`/dashboard?tab=${TAB_NAME[newValue]}`, { replace: true }); }; if (!user) { diff --git a/src/pages/BidsConverterPage.tsx b/src/pages/BidsConverterPage.tsx index 2bbb3f0..1c57d16 100644 --- a/src/pages/BidsConverterPage.tsx +++ b/src/pages/BidsConverterPage.tsx @@ -51,7 +51,7 @@ const BidsConverterPage: React.FC = () => { // After login succeeds in save mode, redirect to dashboard to create a project useEffect(() => { if (isLoggedIn && mode === "save") { - navigate("/dashboard"); + navigate("/dashboard?tab=projects"); } }, [isLoggedIn, mode, navigate]); @@ -63,7 +63,7 @@ const BidsConverterPage: React.FC = () => { const handleChooseSave = () => { setMode("save"); if (isLoggedIn) { - navigate("/dashboard"); + navigate("/dashboard?tab=projects"); } else { setLoginOpen(true); } From 71e5ffb0eba4571ba65ee1bf6244679a874a80ec Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 27 May 2026 12:00:44 -0400 Subject: [PATCH 05/18] feat(bids-converter): add private mode visual indicator with slate grey background --- src/pages/BidsConverterPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/BidsConverterPage.tsx b/src/pages/BidsConverterPage.tsx index 1c57d16..1f27008 100644 --- a/src/pages/BidsConverterPage.tsx +++ b/src/pages/BidsConverterPage.tsx @@ -142,7 +142,9 @@ const BidsConverterPage: React.FC = () => { display: "flex", flexDirection: "column", height: "100vh", - background: "linear-gradient(180deg,#f6f7fb 0%, #aeb6e8 100%)", + background: mode === "private" + ? "linear-gradient(180deg, #eceff1 0%, #90a4ae 100%)" + : "linear-gradient(180deg,#f6f7fb 0%, #aeb6e8 100%)", }} > {/* Header */} @@ -213,7 +215,7 @@ const BidsConverterPage: React.FC = () => { display: "flex", alignItems: "center", gap: 2, - backgroundColor: "white", + backgroundColor: mode === "private" ? "#eceff1" : "white", }} > Date: Wed, 27 May 2026 15:11:48 -0400 Subject: [PATCH 06/18] feat(bids-converter): improve UI labels, tooltips, and private mode UX --- .../Dashboard/DatasetOrganizer/LLMPanel.tsx | 83 ++++++++++++++----- .../User/Dashboard/DatasetOrganizer/index.tsx | 3 +- src/pages/BidsConverterPage.tsx | 50 ++++++++++- 3 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index 9658ec6..1e49428 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -13,6 +13,7 @@ import { Download, AutoAwesome, DriveFileMove, + InfoOutlined, } from "@mui/icons-material"; import { Box, @@ -27,6 +28,7 @@ import { CircularProgress, IconButton, Alert, + Tooltip, } from "@mui/material"; import { Colors } from "design/theme"; import { dump as yamlDump } from "js-yaml"; @@ -46,6 +48,7 @@ interface LLMPanelProps { setTrioGenerated: (value: boolean) => void; // ✅ Add updateFiles: (updater: React.SetStateAction) => void; // ✅ Add onClose: () => void; + isPrivateMode?: boolean; } interface LLMProvider { @@ -77,28 +80,27 @@ const llmProviders: Record = { baseUrl: "https://api.groq.com/openai/v1/chat/completions", models: [ { id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B" }, + { id: "llama-3.1-70b-versatile", name: "Llama 3.1 70B" }, { id: "llama-3.1-8b-instant", name: "Llama 3.1 8B (Fast)" }, - { id: "mixtral-8x7b-32768", name: "Mixtral 8x7B" }, + { id: "gemma2-9b-it", name: "Gemma 2 9B" }, ], }, openrouter: { name: "OpenRouter (Free models available)", baseUrl: "https://openrouter.ai/api/v1/chat/completions", models: [ - { - id: "meta-llama/llama-3.1-8b-instruct:free", - name: "Llama 3.1 8B (Free)", - }, - { id: "google/gemma-2-9b-it:free", name: "Gemma 2 9B (Free)" }, - { id: "mistralai/mistral-7b-instruct:free", name: "Mistral 7B (Free)" }, + { id: "meta-llama/llama-3.3-70b-instruct:free", name: "Llama 3.3 70B (Free)" }, + { id: "google/gemma-3n-e4b-it:free", name: "Gemma 3n 4B (Free)" }, + { id: "mistralai/mistral-small-3.2-24b-instruct:free", name: "Mistral Small 3.2 24B (Free)" }, ], }, anthropic: { name: "Anthropic Claude (Paid)", baseUrl: "https://api.anthropic.com/v1/messages", models: [ - { id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4" }, - { id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku" }, + { id: "claude-opus-4-7", name: "Claude Opus 4.7" }, + { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, + { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5" }, ], isAnthropic: true, }, @@ -106,8 +108,11 @@ const llmProviders: Record = { name: "OpenAI (Paid)", baseUrl: "https://api.openai.com/v1/chat/completions", models: [ - { id: "gpt-4o-mini", name: "GPT-4o Mini" }, + { id: "gpt-5.5", name: "GPT-5.5" }, + { id: "gpt-5.4", name: "GPT-5.4" }, + { id: "gpt-5.4-mini", name: "GPT-5.4 Mini" }, { id: "gpt-4o", name: "GPT-4o" }, + { id: "gpt-4o-mini", name: "GPT-4o Mini" }, ], }, }; @@ -122,9 +127,10 @@ const LLMPanel: React.FC = ({ setTrioGenerated, // ✅ Add updateFiles, // ✅ Add onClose, + isPrivateMode = false, }) => { - const [provider, setProvider] = useState("ollama"); - const [model, setModel] = useState("qwen3-coder-next:latest"); + const [provider, setProvider] = useState(isPrivateMode ? "groq" : "ollama"); + const [model, setModel] = useState(isPrivateMode ? "llama-3.3-70b-versatile" : "qwen3-coder-next:latest"); // const [ollamaUrl, setOllamaUrl] = useState( // "http://jin.neu.edu:11434" // ); @@ -1208,7 +1214,7 @@ const LLMPanel: React.FC = ({ }); updateFiles((prev) => [...prev, ...outputFiles]); - setStatus("✓ Saved to VFS. Click 'Save Changes' to persist to database."); + setStatus(isPrivateMode ? "✓ Saved to VFS." : "✓ Saved to VFS. Click 'Save Changes' to persist to database."); }; // const handleSaveZip = async () => { // const zip = new JSZip(); @@ -1350,7 +1356,38 @@ const LLMPanel: React.FC = ({ sx={{ display: "flex", alignItems: "center", gap: 1 }} > - AI-Generated BIDS Conversion Script + AI Assistant + + + + + @@ -1379,11 +1416,13 @@ const LLMPanel: React.FC = ({ setModel(llmProviders[e.target.value].models[0].id); }} > - {Object.entries(llmProviders).map(([key, p]) => ( - - {p.name} - - ))} + {Object.entries(llmProviders) + .filter(([key]) => !(isPrivateMode && key === "ollama")) + .map(([key, p]) => ( + + {p.name} + + ))} @@ -1567,8 +1606,8 @@ const LLMPanel: React.FC = ({ {generatingTrio ? "Generating..." : trioGenerated - ? "✓ 2. Generate BIDS Trio" - : "2. Generate BIDS Trio"} + ? "✓ 2. Generate BIDS Metadata Files" + : "2. Generate BIDS Metadata Files"} {/* = ({ "&.Mui-disabled": { background: "#e0e0e0", color: "#9e9e9e" }, }} > - {loading ? "Generating..." : "3. Generate BIDSPlan.yaml"} + {loading ? "Generating..." : "3. Generate Conversion Package"} {/* {/* - BIDS Converter + + BIDS Converter + + How to use: + +
  • Drop your dataset files into the workspace.
  • +
  • Enter the number of subjects, modality, and base directory path.
  • +
  • The AI will analyze your files and generate a BIDS conversion plan.
  • +
  • Download and run the script locally to reorganize your data into BIDS format.
  • + + + } + placement="bottom-start" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "white", + color: Colors.darkPurple, + border: `1px solid ${Colors.lightGray}`, + boxShadow: 3, + fontSize: "0.875rem", + lineHeight: 1.5, + p: 1.5, + maxWidth: 320, + }, + }, + arrow: { + sx: { + color: "white", + "&::before": { border: `1px solid ${Colors.lightGray}` }, + }, + }, + }} + > + + + + + Organize and rename your dataset files into BIDS format @@ -180,14 +224,13 @@ const BidsConverterPage: React.FC = () => { variant="contained" startIcon={} onClick={() => setShowLLMPanel(!showLLMPanel)} - disabled={files.length === 0} sx={{ backgroundColor: Colors.purple, color: Colors.lightGray, "&:hover": { backgroundColor: Colors.purple, border: "none" }, }} > - Generate BIDS Plan + AI Assistant - {/* */} @@ -1755,11 +1745,13 @@ const LLMPanel: React.FC = ({ color: "#d4d4d4", }} > - {/* {generatedScript || - 'Configure your LLM provider and click "Generate Script"...'} */} - {bidsPlan || - generatedScript || - 'Configure your LLM provider and click "Generate BIDSPlan.yaml"...'} + {bidsPlan || generatedScript || ( + + {status && !error + ? status + : 'Fill in the fields on the left and follow the steps to generate your conversion package...'} + + )} From cf2e445020ad22e35524d506a43976fb4c666694 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 27 May 2026 16:11:17 -0400 Subject: [PATCH 08/18] fix(llm-panel): clear bidsPlan on step 2 re-run so progress shows in output panel --- src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx | 1 + src/components/User/Dashboard/DatasetOrganizer/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index bb8d69a..6b3c007 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -218,6 +218,7 @@ const LLMPanel: React.FC = ({ setAbortController(controller); setGeneratingTrio(true); setError(null); + setBidsPlan(""); setStatus("Generating BIDS trio files..."); try { diff --git a/src/components/User/Dashboard/DatasetOrganizer/index.tsx b/src/components/User/Dashboard/DatasetOrganizer/index.tsx index 39c2f45..5a361a9 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/index.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/index.tsx @@ -282,7 +282,7 @@ const DatasetOrganizer: React.FC = () => { > AI Assistant - {/* */} + + */} @@ -1728,9 +1733,13 @@ const LLMPanel: React.FC = ({ startIcon={} onClick={handleSaveZip} disabled={!bidsPlan || !trioGenerated} - sx={{ color: Colors.darkGreen, borderColor: Colors.darkGreen }} + sx={{ + color: Colors.purple, + borderColor: Colors.purple, + "&:hover": { borderColor: Colors.purple }, + }} > - Preview in File Tree + Preview Conversion Package in File Tree From 60f51ade5d684d0c1eafcd5c647ac7a5f1d7aded Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 27 May 2026 17:56:39 -0400 Subject: [PATCH 10/18] feat(ollama): require authentication on Ollama proxy endpoints --- backend/src/routes/ollama.routes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/routes/ollama.routes.js b/backend/src/routes/ollama.routes.js index ff1fd94..38bdcb4 100644 --- a/backend/src/routes/ollama.routes.js +++ b/backend/src/routes/ollama.routes.js @@ -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; From 68218d5b000149565f7f86f05aaf775235376ef9 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 27 May 2026 18:11:41 -0400 Subject: [PATCH 11/18] feat(ollama): add public endpoint with 10 requests/day IP rate limit --- backend/package-lock.json | 26 ++++++++++++++++++---- backend/package.json | 1 + backend/src/routes/ollama.public.routes.js | 17 ++++++++++++++ backend/src/server.js | 2 ++ 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 backend/src/routes/ollama.public.routes.js diff --git a/backend/package-lock.json b/backend/package-lock.json index 70f2f74..9bb2c58 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,6 +15,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", @@ -1181,6 +1182,24 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1742,11 +1761,10 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", - "optional": true, "engines": { "node": ">= 12" } diff --git a/backend/package.json b/backend/package.json index fa156b3..45f386a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -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", diff --git a/backend/src/routes/ollama.public.routes.js b/backend/src/routes/ollama.public.routes.js new file mode 100644 index 0000000..e9fc0f1 --- /dev/null +++ b/backend/src/routes/ollama.public.routes.js @@ -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: 10, + standardHeaders: true, + legacyHeaders: false, + message: { error: "Daily request limit reached. You can send up to 10 requests per day from this IP." }, +}); + +router.post("/chat", dailyLimit, proxyChat); +router.get("/tags", dailyLimit, getTags); + +module.exports = router; diff --git a/backend/src/server.js b/backend/src/server.js index 5e6cb6c..d0d043d 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -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; @@ -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) => { From 1aa509af696ef0b92f28489b31b5f08276a7f014 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Fri, 29 May 2026 11:31:07 -0400 Subject: [PATCH 12/18] feat(llm-panel): add local Ollama provider for private mode with configurable URL --- .../Dashboard/DatasetOrganizer/LLMPanel.tsx | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index a37ba9e..d858c35 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -73,7 +73,18 @@ const llmProviders: Record = { { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B" }, ], noApiKey: true, - // customUrl: true, + }, + "local-ollama": { + name: "Ollama (Your Local Machine)", + baseUrl: "http://localhost:11434/v1/chat/completions", + models: [ + { id: "llama3.2:latest", name: "Llama 3.2" }, + { id: "llama3.1:latest", name: "Llama 3.1" }, + { id: "qwen2.5-coder:latest", name: "Qwen 2.5 Coder" }, + { id: "mistral:latest", name: "Mistral" }, + { id: "gemma3:latest", name: "Gemma 3" }, + ], + noApiKey: true, }, groq: { name: "Groq (Free API Key - 14,400 req/day)", @@ -129,8 +140,9 @@ const LLMPanel: React.FC = ({ onClose, isPrivateMode = false, }) => { - const [provider, setProvider] = useState(isPrivateMode ? "groq" : "ollama"); - const [model, setModel] = useState(isPrivateMode ? "llama-3.3-70b-versatile" : "qwen3-coder-next:latest"); + const [provider, setProvider] = useState(isPrivateMode ? "local-ollama" : "ollama"); + const [model, setModel] = useState(isPrivateMode ? "llama3.2:latest" : "qwen3-coder-next:latest"); + const [localOllamaUrl, setLocalOllamaUrl] = useState("http://localhost:11434"); // const [ollamaUrl, setOllamaUrl] = useState( // "http://jin.neu.edu:11434" // ); @@ -160,7 +172,9 @@ const LLMPanel: React.FC = ({ provider, model, apiKey, - baseUrl: currentProvider.baseUrl, + baseUrl: provider === "local-ollama" + ? `${localOllamaUrl}/v1/chat/completions` + : currentProvider.baseUrl, isAnthropic: currentProvider.isAnthropic, noApiKey: currentProvider.noApiKey, }); @@ -1421,7 +1435,7 @@ const LLMPanel: React.FC = ({ }} > {Object.entries(llmProviders) - .filter(([key]) => !(isPrivateMode && key === "ollama")) + .filter(([key]) => isPrivateMode ? key !== "ollama" : key !== "local-ollama") .map(([key, p]) => ( {p.name} @@ -1445,17 +1459,17 @@ const LLMPanel: React.FC = ({ - {/* Ollama Server URL field */} - {/* {provider === "ollama" && ( + {provider === "local-ollama" && ( setOllamaUrl(e.target.value)} + label="Ollama URL" + value={localOllamaUrl} + onChange={(e) => setLocalOllamaUrl(e.target.value)} placeholder="http://localhost:11434" + helperText="Default port is 11434. Change if your Ollama runs on a different port." sx={{ mb: 2 }} /> - )} */} + )} {/* Base Directory Path field (shows for ALL providers) */} Date: Fri, 29 May 2026 11:42:20 -0400 Subject: [PATCH 13/18] feat(navbar): add AutoBIDSify dropdown with GitHub link and web app; update dialog title and description --- src/components/NavBar/NavItems.tsx | 92 ++++++++++++++++++++++++++++-- src/pages/BidsConverterPage.tsx | 5 +- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/components/NavBar/NavItems.tsx b/src/components/NavBar/NavItems.tsx index c102bcf..bfbb9b3 100644 --- a/src/components/NavBar/NavItems.tsx +++ b/src/components/NavBar/NavItems.tsx @@ -35,11 +35,13 @@ const NavItems: React.FC = () => { const [signupOpen, setSignupOpen] = useState(false); // Resources dropdown state - const [resourcesAnchor, setResourcesAnchor] = useState( - null - ); + const [resourcesAnchor, setResourcesAnchor] = useState(null); const resourcesOpen = Boolean(resourcesAnchor); + // AutoBIDSify dropdown state + const [autobidsifyAnchor, setAutobidsifyAnchor] = useState(null); + const autobidsifyOpen = Boolean(autobidsifyAnchor); + const handleLogout = () => { dispatch(logoutUser()); navigate("/"); @@ -53,6 +55,14 @@ const NavItems: React.FC = () => { setResourcesAnchor(null); }; + const handleAutobidsifyClick = (event: React.MouseEvent) => { + setAutobidsifyAnchor(event.currentTarget); + }; + + const handleAutobidsifyClose = () => { + setAutobidsifyAnchor(null); + }; + const resourcesMenu = [ { category: "Converter", @@ -319,7 +329,6 @@ const NavItems: React.FC = () => { { text: "Wiki", url: "https://neurojson.org/Wiki" }, { text: "Search", url: RoutesEnum.SEARCH }, { text: "Databases", url: RoutesEnum.DATABASES }, - { text: "BIDS Converter", url: RoutesEnum.BIDS_CONVERTER }, ].map(({ text, url }) => ( {url?.startsWith("https") ? ( @@ -379,6 +388,38 @@ const NavItems: React.FC = () => { ))} + {/* AutoBIDSify Dropdown */} + + + + AutoBIDSify + + + + + {/* Resources Dropdown */} { ))} + + {/* AutoBIDSify Dropdown Menu */} + + { + 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) + + { + navigate(RoutesEnum.BIDS_CONVERTER); + handleAutobidsifyClose(); + }} + sx={{ + fontSize: "0.9rem", + color: Colors.white, + "&:hover": { bgcolor: Colors.purpleGrey, color: Colors.darkPurple }, + }} + > + AutoBIDSify Web + + + setLoginOpen(false)} diff --git a/src/pages/BidsConverterPage.tsx b/src/pages/BidsConverterPage.tsx index 70b4a46..b528567 100644 --- a/src/pages/BidsConverterPage.tsx +++ b/src/pages/BidsConverterPage.tsx @@ -353,9 +353,12 @@ const BidsConverterPage: React.FC = () => { {/* Welcome dialog — shown on first load before user starts working */} - How would you like to use BIDS Converter? + How would you like to use AutoBIDSify? + + An LLM-powered tool for automatically converting neuroimaging datasets into BIDS-compliant format. + Date: Fri, 29 May 2026 11:59:49 -0400 Subject: [PATCH 14/18] feat(autobidsify): rebrand BIDS Converter to AutoBIDSify with navbar dropdown, updated labels, and GitHub links --- .../User/Dashboard/DatasetOrganizer/index.tsx | 51 +++++++++++++++++++ src/components/User/Dashboard/ProjectsTab.tsx | 4 +- src/pages/BidsConverterPage.tsx | 22 ++++++-- src/types/routes.enum.ts | 2 +- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/index.tsx b/src/components/User/Dashboard/DatasetOrganizer/index.tsx index 5a361a9..796fbaa 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/index.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/index.tsx @@ -14,6 +14,8 @@ import { DialogContent, DialogActions, DialogContentText, + Chip, + Tooltip, } from "@mui/material"; import { Colors } from "design/theme"; import { useAppDispatch } from "hooks/useAppDispatch"; @@ -263,6 +265,55 @@ const DatasetOrganizer: React.FC = () => { {currentProject.description} )} + + An LLM-powered tool for automatically converting neuroimaging datasets into BIDS-compliant format. + window.open("https://github.com/COTILab/autobidsify", "_blank")} + > + Learn more + + + } + placement="bottom-start" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "white", + color: Colors.darkPurple, + border: `1px solid ${Colors.lightGray}`, + boxShadow: 3, + fontSize: "0.875rem", + p: 1.5, + maxWidth: 320, + }, + }, + arrow: { + sx: { + color: "white", + "&::before": { border: `1px solid ${Colors.lightGray}` }, + }, + }, + }} + > + window.open("https://github.com/COTILab/autobidsify", "_blank")} + sx={{ + mt: 0.5, + backgroundColor: Colors.purple, + color: Colors.white, + fontSize: "0.7rem", + cursor: "pointer", + "&:hover": { backgroundColor: Colors.secondaryPurple }, + }} + /> + diff --git a/src/components/User/Dashboard/ProjectsTab.tsx b/src/components/User/Dashboard/ProjectsTab.tsx index 6095c9b..ceaa91b 100644 --- a/src/components/User/Dashboard/ProjectsTab.tsx +++ b/src/components/User/Dashboard/ProjectsTab.tsx @@ -199,10 +199,10 @@ const ProjectsTab: React.FC = ({ userId }) => { > - Dataset Organizer Projects + AutoBIDSify Projects - Organize and convert your neuroimaging datasets to BIDS format + Organize and convert your neuroimaging datasets to BIDS format using AutoBIDSify - BIDS Converter + AutoBIDSify @@ -214,7 +214,15 @@ const BidsConverterPage: React.FC = () => { - Organize and rename your dataset files into BIDS format + An LLM-powered tool for automatically converting neuroimaging datasets into BIDS-compliant format.{" "} + window.open("https://github.com/COTILab/autobidsify", "_blank")} + sx={{ color: Colors.purple, cursor: "pointer", textDecoration: "underline" }} + > + Learn more + @@ -357,7 +365,15 @@ const BidsConverterPage: React.FC = () => { - An LLM-powered tool for automatically converting neuroimaging datasets into BIDS-compliant format. + An LLM-powered tool for automatically converting neuroimaging datasets into BIDS-compliant format.{" "} + window.open("https://github.com/COTILab/autobidsify", "_blank")} + sx={{ color: Colors.purple, cursor: "pointer", textDecoration: "underline" }} + > + Learn more + Date: Fri, 29 May 2026 12:14:11 -0400 Subject: [PATCH 15/18] feat(llm-panel): add local AI support for Ollama, LM Studio, and Jan with custom model input --- .../Dashboard/DatasetOrganizer/LLMPanel.tsx | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index d858c35..84e1a7a 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -75,14 +75,19 @@ const llmProviders: Record = { noApiKey: true, }, "local-ollama": { - name: "Ollama (Your Local Machine)", + name: "Local AI (Ollama / LM Studio / Jan)", baseUrl: "http://localhost:11434/v1/chat/completions", models: [ - { id: "llama3.2:latest", name: "Llama 3.2" }, - { id: "llama3.1:latest", name: "Llama 3.1" }, - { id: "qwen2.5-coder:latest", name: "Qwen 2.5 Coder" }, - { id: "mistral:latest", name: "Mistral" }, - { id: "gemma3:latest", name: "Gemma 3" }, + { id: "llama3.2:latest", name: "Llama 3.2 (Ollama)" }, + { id: "llama3.1:latest", name: "Llama 3.1 (Ollama)" }, + { id: "qwen2.5-coder:latest", name: "Qwen 2.5 Coder (Ollama)" }, + { id: "mistral:latest", name: "Mistral (Ollama)" }, + { id: "gemma3:latest", name: "Gemma 3 (Ollama)" }, + { id: "llama-3.2-3b-instruct", name: "Llama 3.2 3B (LM Studio)" }, + { id: "llama-3.1-8b-instruct", name: "Llama 3.1 8B (LM Studio)" }, + { id: "mistral-7b-instruct-v0.3", name: "Mistral 7B (LM Studio)" }, + { id: "llama3.2:3b", name: "Llama 3.2 3B (Jan)" }, + { id: "mistral:7b", name: "Mistral 7B (Jan)" }, ], noApiKey: true, }, @@ -1459,6 +1464,25 @@ const LLMPanel: React.FC = ({ + {provider === "local-ollama" && ( + { + if (e.target.value.trim()) setModel(e.target.value.trim()); + }} + /> + )} + + {isPrivateMode && provider !== "local-ollama" && ( + + Your file information will be sent to {currentProvider.name}, an external AI service. Switch to Local AI (Ollama / LM Studio / Jan) to keep everything local. + + )} + {provider === "local-ollama" && ( = ({ value={localOllamaUrl} onChange={(e) => setLocalOllamaUrl(e.target.value)} placeholder="http://localhost:11434" - helperText="Default port is 11434. Change if your Ollama runs on a different port." + helperText="Ollama: port 11434 · LM Studio: port 1234 · Jan: port 1337" sx={{ mb: 2 }} /> )} From 1dfc255dbdef999b7db756ad0d737149a21f0e22 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Fri, 29 May 2026 12:46:13 -0400 Subject: [PATCH 16/18] feat(ollama): use qwen3.6:27b on server for LLM, hide model selector --- backend/src/controllers/ollama.controller.js | 5 +++-- backend/src/routes/ollama.public.routes.js | 2 +- backend/src/routes/ollama.routes.js | 2 +- .../Dashboard/DatasetOrganizer/LLMPanel.tsx | 22 +++++++++++++------ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/backend/src/controllers/ollama.controller.js b/backend/src/controllers/ollama.controller.js index 84bb3bb..fe0c4e8 100644 --- a/backend/src/controllers/ollama.controller.js +++ b/backend/src/controllers/ollama.controller.js @@ -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(); diff --git a/backend/src/routes/ollama.public.routes.js b/backend/src/routes/ollama.public.routes.js index e9fc0f1..b8981a0 100644 --- a/backend/src/routes/ollama.public.routes.js +++ b/backend/src/routes/ollama.public.routes.js @@ -12,6 +12,6 @@ const dailyLimit = rateLimit({ }); router.post("/chat", dailyLimit, proxyChat); -router.get("/tags", dailyLimit, getTags); +// router.get("/tags", dailyLimit, getTags); module.exports = router; diff --git a/backend/src/routes/ollama.routes.js b/backend/src/routes/ollama.routes.js index 38bdcb4..be86d75 100644 --- a/backend/src/routes/ollama.routes.js +++ b/backend/src/routes/ollama.routes.js @@ -4,6 +4,6 @@ const { proxyChat, getTags } = require("../controllers/ollama.controller"); const { requireAuth } = require("../middleware/auth.middleware"); router.post("/chat", requireAuth, proxyChat); -router.get("/tags", requireAuth, getTags); +// router.get("/tags", requireAuth, getTags); module.exports = router; diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index 84e1a7a..c574bf5 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -62,15 +62,15 @@ interface LLMProvider { const llmProviders: Record = { ollama: { - name: "Ollama (Local Server)", - // baseUrl: "http://localhost:11434/v1/chat/completions", + name: "Ollama (NeuroJSON Server)", baseUrl: "", models: [ - { id: "qwen3-coder-next:latest", name: "Qwen 3 Coder Next" }, - { id: "qwen3-coder-careful:latest", name: "Qwen 3 Coder Careful" }, - { id: "qwen3.5:9b", name: "Qwen 3.5 9B" }, - { id: "qwen2.5-coder:latest", name: "Qwen 2.5 Coder (7.6B)" }, - { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B" }, + { id: "qwen3.6:27b", name: "Qwen 3.6 27B" }, + // { id: "qwen3-coder-next:latest", name: "Qwen 3 Coder Next" }, + // { id: "qwen3-coder-careful:latest", name: "Qwen 3 Coder Careful" }, + // { id: "qwen3.5:9b", name: "Qwen 3.5 9B" }, + // { id: "qwen2.5-coder:latest", name: "Qwen 2.5 Coder (7.6B)" }, + // { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B" }, ], noApiKey: true, }, @@ -1449,6 +1449,13 @@ const LLMPanel: React.FC = ({ + {provider === "ollama" && ( + + Using qwen3.6:27b on NeuroJSON server + + )} + + {provider !== "ollama" && ( Model + )} {provider === "local-ollama" && ( Date: Fri, 29 May 2026 12:47:30 -0400 Subject: [PATCH 17/18] fix(ollama): increase public endpoint rate limit to 20 requests/day --- backend/src/routes/ollama.public.routes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/routes/ollama.public.routes.js b/backend/src/routes/ollama.public.routes.js index b8981a0..c395e51 100644 --- a/backend/src/routes/ollama.public.routes.js +++ b/backend/src/routes/ollama.public.routes.js @@ -5,10 +5,10 @@ const { proxyChat, getTags } = require("../controllers/ollama.controller"); const dailyLimit = rateLimit({ windowMs: 24 * 60 * 60 * 1000, // 24 hours - max: 10, + max: 20, standardHeaders: true, legacyHeaders: false, - message: { error: "Daily request limit reached. You can send up to 10 requests per day from this IP." }, + message: { error: "Daily request limit reached. You can send up to 20 requests per day from this IP." }, }); router.post("/chat", dailyLimit, proxyChat); From 60cfdb0e5050fe33aa20d0ab43de8ddc724884c9 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Mon, 1 Jun 2026 10:42:02 -0400 Subject: [PATCH 18/18] fix(preview): make 2D chart responsive to container width --- package.json | 4 +- src/utils/preview.js | 22 ++++- yarn.lock | 215 ++++++++++++++++++++++--------------------- 3 files changed, 135 insertions(+), 106 deletions(-) diff --git a/package.json b/package.json index 6f414ef..aac37d2 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/utils/preview.js b/src/utils/preview.js index 7e4dd6d..231a85e 100644 --- a/src/utils/preview.js +++ b/src/utils/preview.js @@ -34,6 +34,7 @@ var lastvolumedata = null; var lastvolumedim = []; var lastclim = 0; var uplotInstance = null; +var uplotResizeObserver = null; var reqid = undefined; var canvas = null; @@ -135,6 +136,10 @@ function destroyPreview() { lastvolumedata = null; texture = undefined; + if (uplotResizeObserver !== null) { + uplotResizeObserver.disconnect(); + uplotResizeObserver = null; + } if (uplotInstance !== null) { uplotInstance.destroy(); uplotInstance = null; @@ -384,7 +389,7 @@ function dopreview(key, idx, isinternal, hastime) { // "Preview for " + // (isinternal ? intdata[idx][3] : window.extdata[idx][3]), "Data Preview", - width: 1100, + width: Math.max(300, $("#chartpanel").width() - 24), height: 400, series: [{}, {}], axes: [ @@ -507,6 +512,21 @@ function dopreview(key, idx, isinternal, hastime) { // }); } + if (uplotResizeObserver !== null) { + uplotResizeObserver.disconnect(); + } + uplotResizeObserver = new ResizeObserver(() => { + requestAnimationFrame(() => { + if (uplotInstance) { + uplotInstance.setSize({ + width: Math.max(300, $("#chartpanel").width() - 24), + height: 400, + }); + } + }); + }); + uplotResizeObserver.observe(document.getElementById("chartpanel")); + // for spinner // --- Signal React that 2D preview is ready --- window.__previewType = "2d"; diff --git a/yarn.lock b/yarn.lock index e881a21..ec84ee7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1122,7 +1122,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.27.1" -"@babel/runtime@7.26.10", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@7.26.10", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.9", "@babel/runtime@^7.28.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== @@ -2895,12 +2895,12 @@ dependencies: "@types/node" "*" -"@types/hast@^2.0.0": - version "2.3.10" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.10.tgz#5c9d9e0b304bbb8879b857225c5ebab2d81d7643" - integrity sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw== +"@types/hast@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== dependencies: - "@types/unist" "^2" + "@types/unist" "*" "@types/hoist-non-react-statics@^3.3.1": version "3.3.6" @@ -3030,6 +3030,11 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== +"@types/prismjs@^1.0.0": + version "1.26.6" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.6.tgz#6ea27c126d645319ae4f7055eda63a9e835c0187" + integrity sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw== + "@types/prop-types@*", "@types/prop-types@^15.7.12": version "15.7.15" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" @@ -3158,7 +3163,12 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/unist@^2": +"@types/unist@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/unist@^2.0.0": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== @@ -4435,20 +4445,20 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.2.tgz#81385bb071af4df774bff8721d0ca15ef29ea0bb" integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== check-types@^11.2.3: version "11.2.3" @@ -4641,10 +4651,10 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -comma-separated-tokens@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" - integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== commander@^11.1.0: version "11.1.0" @@ -5415,6 +5425,13 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== +decode-named-character-reference@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz#3e40603760874c2e5867691b599d73a7da25b53f" + integrity sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q== + dependencies: + character-entities "^2.0.0" + decode-uri-component@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5" @@ -7205,21 +7222,23 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hast-util-parse-selector@^2.0.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" - integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" -hastscript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" - integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== +hastscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff" + integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== dependencies: - "@types/hast" "^2.0.0" - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" he@^1.2.0: version "1.2.0" @@ -7521,18 +7540,18 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== -is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" is-arguments@^1.1.1: version "1.2.0" @@ -7633,10 +7652,10 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" @@ -7689,10 +7708,10 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" @@ -9713,17 +9732,18 @@ parent-module@^2.0.0: dependencies: callsites "^3.1.0" -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" @@ -10518,16 +10538,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prismjs@^1.27.0: +prismjs@^1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9" integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw== -prismjs@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" - integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -10564,12 +10579,10 @@ prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -property-information@^5.0.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" - integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== - dependencies: - xtend "^4.0.0" +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== proxy-addr@~2.0.7: version "2.0.7" @@ -10874,17 +10887,17 @@ react-scripts@^5.0.1: optionalDependencies: fsevents "^2.3.2" -react-syntax-highlighter@^15.6.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz#fa567cb0a9f96be7bbccf2c13a3c4b5657d9543e" - integrity sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg== +react-syntax-highlighter@16.1.1: + version "16.1.1" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz#928459855d375f5cfc8e646071e20d541cebcb52" + integrity sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA== dependencies: - "@babel/runtime" "^7.3.1" + "@babel/runtime" "^7.28.4" highlight.js "^10.4.1" highlightjs-vue "^1.0.0" lowlight "^1.17.0" - prismjs "^1.27.0" - refractor "^3.6.0" + prismjs "^1.30.0" + refractor "^5.0.0" react-textarea-autosize@^8.3.2: version "8.5.9" @@ -11009,14 +11022,15 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: get-proto "^1.0.1" which-builtin-type "^1.2.1" -refractor@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" - integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== +refractor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-5.0.0.tgz#85daf0448a6d947f5361796eb22c31733d61d904" + integrity sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw== dependencies: - hastscript "^6.0.0" - parse-entities "^2.0.0" - prismjs "~1.27.0" + "@types/hast" "^3.0.0" + "@types/prismjs" "^1.0.0" + hastscript "^9.0.0" + parse-entities "^4.0.0" regenerate-unicode-properties@^10.2.0: version "10.2.0" @@ -11707,10 +11721,10 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== spdy-transport@^3.0.0: version "3.0.0" @@ -12724,10 +12738,10 @@ update-browserslist-db@^1.1.3: escalade "^3.2.0" picocolors "^1.1.1" -uplot@1.6.17: - version "1.6.17" - resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.17.tgz#1f8fc07a0e48008798beca463523621ad66dcc46" - integrity sha512-WHNHvDCXURn+Qwb3QUUzP6rOxx+3kUZUspREyhkqmXCxFIND99l5z9intTh+uPEt+/EEu7lCaMjSd1uTfuTXfg== +uplot@1.6.32: + version "1.6.32" + resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.32.tgz#c800a63b432bad692d6d746f44f0882aa73a49ae" + integrity sha512-KIMVnG68zvu5XXUbC4LQEPnhwOxBuLyW1AHtpm6IKTXImkbLgkMy+jabjLgSLMasNuGGzQm/ep3tOkyTxpiQIw== uri-js@^4.2.2: version "4.4.1" @@ -13451,11 +13465,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - xtend@~2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b"