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/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/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 new file mode 100644 index 0000000..c395e51 --- /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: 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; diff --git a/backend/src/routes/ollama.routes.js b/backend/src/routes/ollama.routes.js index ff1fd94..be86d75 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; 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) => { 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/components/NavBar/NavItems.tsx b/src/components/NavBar/NavItems.tsx index d2882e6..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", @@ -378,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/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/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 (
  • = ({ }} > - + Virtual File System @@ -631,12 +631,11 @@ const FileTree: React.FC = ({ borderColor: "divider", }} > - - Saved Outputs + BIDS Conversion Package Preview {outputFiles diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index 9658ec6..c574bf5 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 { @@ -59,46 +62,61 @@ 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, + }, + "local-ollama": { + name: "Local AI (Ollama / LM Studio / Jan)", + baseUrl: "http://localhost:11434/v1/chat/completions", + models: [ + { 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, - // customUrl: true, }, groq: { name: "Groq (Free API Key - 14,400 req/day)", 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 +124,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 +143,11 @@ 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 ? "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" // ); @@ -154,7 +177,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, }); @@ -212,6 +237,7 @@ const LLMPanel: React.FC = ({ setAbortController(controller); setGeneratingTrio(true); setError(null); + setBidsPlan(""); setStatus("Generating BIDS trio files..."); try { @@ -286,7 +312,7 @@ const LLMPanel: React.FC = ({ setTrioGenerated(true); setStatus( - "โœ“ BIDS trio files generated and added to Virtual File System!" + "โœ“ BIDS metadata files generated and added to file tree!" ); } catch (err: any) { if (err.name === "AbortError") { @@ -1100,7 +1126,10 @@ const LLMPanel: React.FC = ({ const handleSaveZip = async () => { // Add output files to VFS const timestamp = new Date().toLocaleString(); - const zipLabel = `bids_output_${new Date().toISOString().slice(0, 10)}`; + const now = new Date(); + const dateStr = now.toISOString().slice(0, 10); + const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, "-"); + const zipLabel = `bids_output_${dateStr}_${timeStr}`; const outputFiles: FileItem[] = []; const folderId = generateId(); @@ -1208,7 +1237,7 @@ const LLMPanel: React.FC = ({ }); updateFiles((prev) => [...prev, ...outputFiles]); - setStatus("โœ“ Saved to VFS. Click 'Save Changes' to persist to database."); + setStatus(isPrivateMode ? "โœ“ Added to file tree." : "โœ“ Added to file tree. Click 'Save Changes' to persist to database."); }; // const handleSaveZip = async () => { // const zip = new JSZip(); @@ -1350,7 +1379,38 @@ const LLMPanel: React.FC = ({ sx={{ display: "flex", alignItems: "center", gap: 1 }} > - AI-Generated BIDS Conversion Script + AI Assistant + + + + + @@ -1379,14 +1439,23 @@ 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" : key !== "local-ollama") + .map(([key, p]) => ( + + {p.name} + + ))} + {provider === "ollama" && ( + + Using qwen3.6:27b on NeuroJSON server + + )} + + {provider !== "ollama" && ( Model + )} + + {provider === "local-ollama" && ( + { + if (e.target.value.trim()) setModel(e.target.value.trim()); + }} + /> + )} - {/* Ollama Server URL field */} - {/* {provider === "ollama" && ( + {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" && ( setOllamaUrl(e.target.value)} + label="Ollama URL" + value={localOllamaUrl} + onChange={(e) => setLocalOllamaUrl(e.target.value)} placeholder="http://localhost:11434" + helperText="Ollama: port 11434 ยท LM Studio: port 1234 ยท Jan: port 1337" sx={{ mb: 2 }} /> - )} */} + )} {/* Base Directory Path field (shows for ALL providers) */} = ({ {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"} {/* - {/* */} @@ -1716,11 +1801,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...'} + + )} diff --git a/src/components/User/Dashboard/DatasetOrganizer/index.tsx b/src/components/User/Dashboard/DatasetOrganizer/index.tsx index ec8d97f..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 }, + }} + /> + @@ -271,7 +322,6 @@ const DatasetOrganizer: React.FC = () => { variant="contained" startIcon={} onClick={() => setShowLLMPanel(!showLLMPanel)} - disabled={files.length === 0} sx={{ backgroundColor: Colors.purple, color: Colors.lightGray, @@ -281,9 +331,9 @@ const DatasetOrganizer: React.FC = () => { }, }} > - Generate BIDS Plan + AI Assistant - {/* */} + + + + AutoBIDSify + + 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}` }, + }, + }, + }} + > + + + + + + + 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 + + + + + + + + + + + + {/* 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} + + )} + + {/* Main Content */} + + + + {showLLMPanel && ( + setShowLLMPanel(false)} + isPrivateMode={mode === "private"} + /> + )} + + + + + + {/* Welcome dialog โ€” shown on first load before user starts working */} + + + How would you like to use AutoBIDSify? + + + + 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 + + + + {/* 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); + }} + /> + + ); +}; + +export default BidsConverterPage; diff --git a/src/types/routes.enum.ts b/src/types/routes.enum.ts index b37a84d..e8c2658 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 = "/autobidsify", } export default RoutesEnum; 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 8b8ba61..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== @@ -2424,10 +2424,10 @@ redux-thunk "^2.4.2" reselect "^4.1.8" -"@remix-run/router@1.23.0": - version "1.23.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.23.0.tgz#35390d0e7779626c026b11376da6789eb8389242" - integrity sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA== +"@remix-run/router@1.23.2": + version "1.23.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.23.2.tgz#156c4b481c0bee22a19f7924728a67120de06971" + integrity sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w== "@rjsf/core@^5.24.8": version "5.24.11" @@ -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== @@ -3926,13 +3936,14 @@ axe-core@^4.10.0: integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== axios@^1.4.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.9.0.tgz#25534e3b72b54540077d33046f77e3b8d7081901" - integrity sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg== + version "1.16.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12" + integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A== dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + follow-redirects "^1.16.0" + form-data "^4.0.5" + https-proxy-agent "^5.0.1" + proxy-from-env "^2.1.0" axobject-query@^4.1.0: version "4.1.0" @@ -4434,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" @@ -4640,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" @@ -5414,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" @@ -5973,9 +5991,9 @@ es-module-lexer@^1.2.1: integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b" + integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw== dependencies: es-errors "^1.3.0" @@ -6756,11 +6774,16 @@ flux@^4.0.1: fbemitter "^3.0.0" fbjs "^3.0.1" -follow-redirects@^1.0.0, follow-redirects@^1.15.6: +follow-redirects@^1.0.0: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== +follow-redirects@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== + for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" @@ -6805,10 +6828,10 @@ form-data@^3.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.35" -form-data@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.3.tgz#608b1b3f3e28be0fccf5901fc85fb3641e5cf0ae" - integrity sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA== +form-data@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -7193,27 +7216,29 @@ has@^1.0.0: integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c" + integrity sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg== 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" @@ -7363,7 +7388,7 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -7515,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" @@ -7627,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" @@ -7683,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" @@ -9707,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" @@ -10512,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" @@ -10558,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" @@ -10573,10 +10592,10 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" + integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== psl@^1.1.33: version "1.15.0" @@ -10799,19 +10818,19 @@ react-refresh@^0.11.0: integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== react-router-dom@^6.15.0: - version "6.30.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.30.1.tgz#da2580c272ddb61325e435478566be9563a4a237" - integrity sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw== + version "6.30.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.30.3.tgz#42ae6dc4c7158bfb0b935f162b9621b29dddf740" + integrity sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag== dependencies: - "@remix-run/router" "1.23.0" - react-router "6.30.1" + "@remix-run/router" "1.23.2" + react-router "6.30.3" -react-router@6.30.1: - version "6.30.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.1.tgz#ecb3b883c9ba6dbf5d319ddbc996747f4ab9f4c3" - integrity sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ== +react-router@6.30.3: + version "6.30.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.3.tgz#994b3ccdbe0e81fe84d4f998100f62584dfbf1cf" + integrity sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw== dependencies: - "@remix-run/router" "1.23.0" + "@remix-run/router" "1.23.2" react-scripts@^5.0.1: version "5.0.1" @@ -10868,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" @@ -11003,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" @@ -11701,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" @@ -12718,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" @@ -13445,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"