Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
67354bb
feat(autobidsify): parse .mat v7.3 files and rename local AI URL label
elainefan331 Jun 2, 2026
5ac48c8
fix(llm): route local-ollama to localhost, not backend proxy
elainefan331 Jun 2, 2026
69a4dd8
fix(llm): rename local-ollama provider to local-ai for clarity
elainefan331 Jun 2, 2026
3bb9d77
refactor(ollama): rename chat endpoint to bidsify
elainefan331 Jun 2, 2026
ec920b6
feat(llm): fetch local AI models dynamically
elainefan331 Jun 2, 2026
8b67e8c
revert(llm): remove local model fetch button — blocked by browser COR…
elainefan331 Jun 2, 2026
82a7d3e
feat(dropzone): add Browse Folder button with full folder tree struct…
elainefan331 Jun 3, 2026
bc49537
feat(connector): route local-ai through connector when available; det…
elainefan331 Jun 3, 2026
f69e584
feat(autobidsify): replace connector with standalone executor; remove…
elainefan331 Jun 8, 2026
ef8580d
fix(autobidsify): simplify local AI origin hint to one-line info alert
elainefan331 Jun 8, 2026
7b1103e
feat(autobidsify): add Download Executor button with OS-aware GitHub …
elainefan331 Jun 8, 2026
3a4097b
feat(autobidsify): add Next Steps card with download buttons and exec…
elainefan331 Jun 8, 2026
9d19e8c
feat(autobidsify): rename preview button; remove header executor button
elainefan331 Jun 8, 2026
3f0da42
feat(about): add autobidsify demo video to about page
elainefan331 Jun 9, 2026
ad2c8fe
fix(llmpanel): prevent content collapse on resize by adding minHeight…
elainefan331 Jun 9, 2026
1fb9d3b
feat(filetree): move root directory path field into Virtual File Syst…
elainefan331 Jun 9, 2026
f46f6da
feat(filetree): show typed root path in directory alert message
elainefan331 Jun 9, 2026
941a980
fix(export): prevent duplicate folder name in _sourcePath when baseDi…
elainefan331 Jun 9, 2026
8ff658c
fix(export): handle root folder exact match in resolveSourcePath
elainefan331 Jun 9, 2026
2b24ea9
feat(autobidsify): rename executor to converter; update button labels…
elainefan331 Jun 9, 2026
ba5f835
fix(email): catch Ethereal API failure in dev so server doesn't crash
elainefan331 Jun 9, 2026
1561140
feat(autobidsify): add how-to tooltip in project workspace; rename pa…
elainefan331 Jun 9, 2026
5038246
Merge pull request #132 from NeuroJSON/dev-fan
elainefan331 Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions backend/services/email.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ class EmailService {
}

async createTestTransporter() {
const testAccount = await nodemailer.createTestAccount();
this.transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false,
auth: {
user: testAccount.user,
pass: testAccount.pass,
},
});
console.log("📧 Using Ethereal email for development");
console.log("Preview emails at: https://ethereal.email");
try {
const testAccount = await nodemailer.createTestAccount();
this.transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false,
auth: {
user: testAccount.user,
pass: testAccount.pass,
},
});
console.log("📧 Using Ethereal email for development");
console.log("Preview emails at: https://ethereal.email");
} catch (err) {
console.warn("⚠️ Ethereal unavailable, email disabled in dev:", err.message);
this.transporter = null;
}
}

async sendVerificationEmail(user, token) {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/routes/ollama.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const router = express.Router();
const { proxyChat, getTags } = require("../controllers/ollama.controller");
const { requireAuth } = require("../middleware/auth.middleware");

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

module.exports = router;
68 changes: 46 additions & 22 deletions src/components/User/Dashboard/DatasetOrganizer/DropZone.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// src/components/DatasetOrganizer/DropZone.tsx
import { processFile, processFolder, processZip } from "./utils/fileProcessors";
import { processFile, processFolder, processFolderFromFiles, processZip } from "./utils/fileProcessors";
import { CloudUpload, Add, CheckCircle } from "@mui/icons-material";
import {
Box,
Typography,
Paper,
Button,
TextField,
CircularProgress,
} from "@mui/material";
import { Colors } from "design/theme";
Expand Down Expand Up @@ -38,6 +37,7 @@ const DropZone: React.FC<DropZoneProps> = ({
const [isDragging, setIsDragging] = useState(false);
const [isProcessing, setIsProcessing] = useState(false); // ← add
const fileInputRef = useRef<HTMLInputElement>(null);
const folderInputRef = useRef<HTMLInputElement>(null);
// const [basePath, setBasePath] = useState<string>(""); // change

const handleDragOver = (e: React.DragEvent) => {
Expand Down Expand Up @@ -117,6 +117,24 @@ const DropZone: React.FC<DropZoneProps> = ({
}
};

const handleFolderSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = Array.from(e.target.files || []);
if (selectedFiles.length === 0) return;
setIsProcessing(true);

// Auto-detect root folder name from webkitRelativePath (e.g. "myDataset/sub-01/file.nii")
const rootFolder = selectedFiles[0].webkitRelativePath.split("/")[0];
if (rootFolder && !baseDirectoryPath) setBaseDirectoryPath(rootFolder);

try {
const items = await processFolderFromFiles(selectedFiles, baseDirectoryPath);
setFiles((prev) => [...prev, ...items]);
} finally {
setIsProcessing(false);
e.target.value = "";
}
};

return (
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* Show file count if files exist */}
Expand All @@ -136,7 +154,7 @@ const DropZone: React.FC<DropZoneProps> = ({
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onClick={() => fileInputRef.current?.click()}
onClick={(e) => { if (e.target === e.currentTarget) fileInputRef.current?.click(); }}
sx={{
border: `2px dashed ${isDragging ? Colors.purple : Colors.lightGray}`,
borderRadius: 2,
Expand Down Expand Up @@ -198,13 +216,24 @@ const DropZone: React.FC<DropZoneProps> = ({
📁 Folders • 🗜️ ZIP files • 📄 Documents (.json, .txt, .md) • 📊
Office (.docx, .pdf, .xlsx)
</Typography>
<Button
variant="outlined"
startIcon={<Add />}
sx={{ borderColor: Colors.purple, color: Colors.purple }}
>
Or Click to Browse
</Button>
<Box sx={{ display: "flex", gap: 1, justifyContent: "center" }}>
<Button
variant="outlined"
startIcon={<Add />}
onClick={(e) => { e.stopPropagation(); fileInputRef.current?.click(); }}
sx={{ borderColor: Colors.purple, color: Colors.purple }}
>
Browse Files
</Button>
<Button
variant="outlined"
startIcon={<Add />}
onClick={(e) => { e.stopPropagation(); folderInputRef.current?.click(); }}
sx={{ borderColor: Colors.purple, color: Colors.purple }}
>
Browse Folder
</Button>
</Box>
</>
)}
<input
Expand All @@ -215,19 +244,14 @@ const DropZone: React.FC<DropZoneProps> = ({
onChange={handleFileSelect}
accept=".nii,.nii.gz,.snirf,.h5,.hdf5,.jnii,.jmsh,.json,.txt,.md,.zip,.docx,.pdf,.xlsx,.xls,.mat,.dcm,.nirs"
/>
<input
ref={folderInputRef}
type="file"
hidden
onChange={handleFolderSelect}
{...{ webkitdirectory: "", mozdirectory: "" } as any}
/>
</Paper>
<TextField
label="Directory Path (actual data path)"
placeholder="example: /Users/username/Desktop/Downloads"
// value={basePath} // change
// onChange={(e) => setBasePath(e.target.value)} //change
value={baseDirectoryPath} // ✅ CHANGE: Use prop
onChange={(e) => setBaseDirectoryPath(e.target.value)} // ✅ CHANGE: Use prop setter
fullWidth
size="small"
sx={{ mb: 2 }}
helperText="Enter the folder path where these files are located"
/>
</Box>
);
};
Expand Down
21 changes: 20 additions & 1 deletion src/components/User/Dashboard/DatasetOrganizer/FileTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Paper,
Button,
TextField,
Alert,
Dialog,
DialogTitle,
DialogContent,
Expand All @@ -37,6 +38,8 @@ interface FileTreeProps {
setSelectedIds: React.Dispatch<React.SetStateAction<Set<string>>>;
expandedIds: Set<string>;
setExpandedIds: React.Dispatch<React.SetStateAction<Set<string>>>;
baseDirectoryPath: string;
setBaseDirectoryPath: (path: string) => void;
}

const FileTree: React.FC<FileTreeProps> = ({
Expand All @@ -46,6 +49,8 @@ const FileTree: React.FC<FileTreeProps> = ({
setSelectedIds,
expandedIds,
setExpandedIds,
baseDirectoryPath,
setBaseDirectoryPath,
}) => {
const [noteDialogOpen, setNoteDialogOpen] = useState(false);
const [editingNoteId, setEditingNoteId] = useState<string | null>(null);
Expand Down Expand Up @@ -521,6 +526,20 @@ const FileTree: React.FC<FileTreeProps> = ({
// alignItems: "center",
}}
>
<TextField
label="Root Directory Path"
placeholder="/Users/username/Desktop/my-dataset"
value={baseDirectoryPath}
onChange={(e) => setBaseDirectoryPath(e.target.value)}
size="small"
sx={{ mb: 0.75 }}
fullWidth
/>
<Alert severity="info" sx={{ py: 0.5, mb: 1.5, fontSize: "0.75rem" }}>
{baseDirectoryPath
? <>All dropped files must be inside <Box component="span" sx={{ fontFamily: "monospace", fontWeight: 600 }}>{baseDirectoryPath}</Box> on your machine.</>
: "All dropped files must be inside this root folder on your machine."}
</Alert>
<Box>
<Typography variant="subtitle2" fontWeight={800}>
Virtual File System
Expand Down Expand Up @@ -635,7 +654,7 @@ const FileTree: React.FC<FileTreeProps> = ({
variant="body2"
sx={{ color: Colors.darkPurple, fontWeight: 400 }}
>
BIDS Conversion Package Preview
BIDS Conversion Bundle Preview
</Typography>
</Box>
{outputFiles
Expand Down
Loading
Loading