From 134feac95a045214c67a4d2c489f44123a36d489 Mon Sep 17 00:00:00 2001 From: UnagiCodezz Date: Wed, 25 Mar 2026 11:50:37 +0530 Subject: [PATCH] migrated equations and citations to specific project folder Signed-off-by: UnagiCodezz --- server.js | 101 +++++++++++++++++++---------- src/components/MathInsertModal.jsx | 25 ++++--- src/components/citationManager.jsx | 88 ++++++++++++++++++------- src/components/easyMathInput.jsx | 21 ++++-- src/components/sideBar.jsx | 6 +- src/pages/editorPage.jsx | 2 + 6 files changed, 172 insertions(+), 71 deletions(-) diff --git a/server.js b/server.js index 28b235f..105cfe1 100644 --- a/server.js +++ b/server.js @@ -222,22 +222,22 @@ const getPdflatexPath = () => { const tinyTexBaseDir = isDev ? path.join( - __dirname, - "resources", - "TinyTex", - process.platform === "win32" - ? "win" - : process.platform === "darwin" - ? "mac" - : "linux", - ) + __dirname, + "resources", + "TinyTex", + process.platform === "win32" + ? "win" + : process.platform === "darwin" + ? "mac" + : "linux", + ) : effectiveResourcesPath ? path.join(effectiveResourcesPath, "TinyTex") : (() => { - throw new Error( - "CRITICAL: RESOURCES_PATH is missing in production! IGNORE if in developement mode", - ); - })(); + throw new Error( + "CRITICAL: RESOURCES_PATH is missing in production! IGNORE if in developement mode", + ); + })(); let binaryName = "pdflatex"; let archFolder = ""; @@ -706,9 +706,9 @@ async function getTemplateFiles(templatePath) { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); - const relPath = (relativePath - ? path.join(relativePath, entry.name) - : entry.name).replace(/\\/g, "/"); + const relPath = ( + relativePath ? path.join(relativePath, entry.name) : entry.name + ).replace(/\\/g, "/"); if (entry.isDirectory()) { // Create a .gitkeep so the folder is tracked @@ -904,7 +904,7 @@ app.post( }); } finally { // Clean up temp file - await fs.remove(tempPdfPath).catch(() => { }); + await fs.remove(tempPdfPath).catch(() => {}); } } else { // .txt or .md โ€” read as UTF-8 string @@ -1680,7 +1680,7 @@ app.post("/api/compile", async (req, res) => { try { await fs.remove(pdfPath); await fs.remove(path.join(OUTPUT_DIR, `${filename}.synctex.gz`)); - } catch (e) { } + } catch (e) {} console.log("๐Ÿ”„ Running PDFLaTeX..."); @@ -2029,7 +2029,7 @@ ${cleanLatex.replace(/[โ€นโ€บ]/g, "")} let logContent = ""; try { logContent = await fs.readFile(logPath, "utf8"); - } catch (logErr) { } + } catch (logErr) {} throw new Error(`PDF compilation failed. Log: ${logContent.slice(-500)}`); } @@ -2075,14 +2075,19 @@ ${cleanLatex.replace(/[โ€นโ€บ]/g, "")} app.post("/api/equations/save", async (req, res) => { console.log("๐Ÿ’พ Received equation save request"); try { - const { fileName, latex } = req.body; + const { fileName, latex, projectId } = req.body; if (!fileName || !latex) { return res.status(400).json({ error: "fileName and latex are required" }); } + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "equations") + : EQUATIONS_DIR; + await fs.ensureDir(targetDir); + const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9_-]/g, "_"); const fullFileName = `${sanitizedFileName}.tex`; - const filePath = path.join(EQUATIONS_DIR, fullFileName); + const filePath = path.join(targetDir, fullFileName); await fs.writeFile(filePath, latex, "utf8"); console.log("โœ… Equation saved successfully:", sanitizedFileName); @@ -2105,13 +2110,19 @@ app.post("/api/equations/save", async (req, res) => { app.get("/api/equations/list", async (req, res) => { console.log("๐Ÿ“‹ Received request to list equations"); try { - const files = await fs.readdir(EQUATIONS_DIR); + const { projectId } = req.query; + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "equations") + : EQUATIONS_DIR; + await fs.ensureDir(targetDir); + + const files = await fs.readdir(targetDir); const texFiles = files.filter((file) => file.endsWith(".tex")); console.log(`๐Ÿ“š Found ${texFiles.length} equation files`); const equations = await Promise.all( texFiles.map(async (file) => { - const filePath = path.join(EQUATIONS_DIR, file); + const filePath = path.join(targetDir, file); const content = await fs.readFile(filePath, "utf8"); const stats = await fs.stat(filePath); const fileName = path.basename(file, ".tex"); @@ -2143,9 +2154,13 @@ app.get("/api/equations/load/:filename", async (req, res) => { console.log("๐Ÿ“– Received request to load equation:", req.params.filename); try { const { filename } = req.params; + const { projectId } = req.query; + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "equations") + : EQUATIONS_DIR; const sanitizedFileName = filename.replace(/[^a-zA-Z0-9_-]/g, "_"); const fullFileName = `${sanitizedFileName}.tex`; - const filePath = path.join(EQUATIONS_DIR, fullFileName); + const filePath = path.join(targetDir, fullFileName); try { await fs.access(filePath); @@ -2178,9 +2193,13 @@ app.delete("/api/equations/:filename", async (req, res) => { console.log("๐Ÿ—‘๏ธ Received request to delete equation:", req.params.filename); try { const { filename } = req.params; + const { projectId } = req.query; + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "equations") + : EQUATIONS_DIR; const sanitizedFileName = filename.replace(/[^a-zA-Z0-9_-]/g, "_"); const fullFileName = `${sanitizedFileName}.tex`; - const filePath = path.join(EQUATIONS_DIR, fullFileName); + const filePath = path.join(targetDir, fullFileName); await fs.unlink(filePath); console.log("โœ… Equation deleted successfully:", sanitizedFileName); @@ -2514,7 +2533,7 @@ app.post("/api/citation/compile", async (req, res) => { // Delete old PDF if exists try { await fs.remove(pdfFilePath); - } catch (e) { } + } catch (e) {} // Run pdflatex console.log("๐Ÿ”„ Compiling citation..."); @@ -2530,7 +2549,7 @@ app.post("/api/citation/compile", async (req, res) => { let logContent = ""; try { logContent = await fs.readFile(logPath, "utf8"); - } catch { } + } catch {} console.error("โŒ Citation compilation failed - no PDF"); throw new Error(`PDF not generated. Log:\n${logContent.slice(-500)}`); @@ -2578,7 +2597,7 @@ app.post("/api/citation/compile", async (req, res) => { app.post("/api/citation/save", async (req, res) => { console.log("๐Ÿ’พ Received citation save request"); try { - const { fileName, citationData, latexCode } = req.body; + const { fileName, citationData, latexCode, projectId } = req.body; if (!citationData || !latexCode) { return res @@ -2593,9 +2612,15 @@ app.post("/api/citation/save", async (req, res) => { // Ensure unique filename by appending timestamp const uniqueFileName = `${sanitizedFileName}_${Date.now()}`; const fullFileName = `${uniqueFileName}.json`; - const filePath = path.join(CITATIONS_DIR, fullFileName); - const existingFiles = await fs.readdir(CITATIONS_DIR); + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "citations") + : CITATIONS_DIR; + await fs.ensureDir(targetDir); + + const filePath = path.join(targetDir, fullFileName); + + const existingFiles = await fs.readdir(targetDir); const jsonFiles = existingFiles.filter((f) => f.endsWith(".json")); const citationNumber = jsonFiles.length + 1; @@ -2629,12 +2654,18 @@ app.post("/api/citation/save", async (req, res) => { app.get("/api/citation/list", async (req, res) => { console.log("๐Ÿ“‹ Received request to list citations"); try { - const files = await fs.readdir(CITATIONS_DIR); + const { projectId } = req.query; + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "citations") + : CITATIONS_DIR; + await fs.ensureDir(targetDir); + + const files = await fs.readdir(targetDir); const jsonFiles = files.filter((file) => file.endsWith(".json")); const citations = await Promise.all( jsonFiles.map(async (file) => { - const filePath = path.join(CITATIONS_DIR, file); + const filePath = path.join(targetDir, file); const content = await fs.readFile(filePath, "utf8"); const data = JSON.parse(content); return data; @@ -2656,9 +2687,13 @@ app.delete("/api/citation/:filename", async (req, res) => { console.log("๐Ÿ—‘๏ธ Received request to delete citation:", req.params.filename); try { const { filename } = req.params; + const { projectId } = req.query; + const targetDir = projectId + ? path.join(PROJECTS_DIR, String(projectId), "citations") + : CITATIONS_DIR; const sanitizedFileName = filename.replace(/[^a-zA-Z0-9_-]/g, "_"); const fullFileName = `${sanitizedFileName}.json`; - const filePath = path.join(CITATIONS_DIR, fullFileName); + const filePath = path.join(targetDir, fullFileName); await fs.unlink(filePath); diff --git a/src/components/MathInsertModal.jsx b/src/components/MathInsertModal.jsx index 65d66b6..06c6450 100644 --- a/src/components/MathInsertModal.jsx +++ b/src/components/MathInsertModal.jsx @@ -1,22 +1,31 @@ import React from "react"; import EasyMathInput from "./easyMathInput"; -const MathInsertModal = ({ isOpen, onClose, onInsert, showInsertButton = false }) => { +const MathInsertModal = ({ + isOpen, + onClose, + onInsert, + showInsertButton = false, + projectId, +}) => { if (!isOpen) return null; return (
-
+
{/* Wrap EasyMathInput which expects to be full height */}
{ - onInsert(latex); - onClose(); - } : undefined} + onInsert={ + showInsertButton + ? (latex) => { + onInsert(latex); + onClose(); + } + : undefined + } />
diff --git a/src/components/citationManager.jsx b/src/components/citationManager.jsx index 09d9f8e..4ced0f3 100644 --- a/src/components/citationManager.jsx +++ b/src/components/citationManager.jsx @@ -29,7 +29,13 @@ const initialFormData = { format: "IEEE", }; -const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { +const CitationManager = ({ + onClose, + onInsert, + showInsertButton, + isModal, + projectId, +}) => { const { isAuthenticated } = useAuth(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("create"); @@ -40,11 +46,17 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { const [savedCitations, setSavedCitations] = useState([]); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [citationToDelete, setCitationToDelete] = useState(null); - const [alertModal, setAlertModal] = useState({ isOpen: false, title: "", message: "" }); + const [alertModal, setAlertModal] = useState({ + isOpen: false, + title: "", + message: "", + }); const [copiedItem, setCopiedItem] = useState(null); - const showAlert = (title, message) => setAlertModal({ isOpen: true, title, message }); - const closeAlert = () => setAlertModal({ isOpen: false, title: "", message: "" }); + const showAlert = (title, message) => + setAlertModal({ isOpen: true, title, message }); + const closeAlert = () => + setAlertModal({ isOpen: false, title: "", message: "" }); // Fetch citations only when on saved tab useEffect(() => { @@ -56,7 +68,9 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { const loadSavedCitations = useCallback(async () => { try { - const res = await fetch(`${API_BASE_URL}/api/citation/list`); + const res = await fetch( + `${API_BASE_URL}/api/citation/list?projectId=${projectId || ""}`, + ); const data = await res.json(); setSavedCitations(data); } catch (err) { @@ -72,7 +86,10 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { const compileCitation = useCallback(async () => { const { authors, title, year } = formData; if (!authors || !title || !year) { - showAlert("Missing Fields", "Please fill in at least Authors, Title, and Year"); + showAlert( + "Missing Fields", + "Please fill in at least Authors, Title, and Year", + ); return; } setIsCompiling(true); @@ -88,7 +105,10 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { setPreviewUrl(`${API_BASE_URL}${data.previewUrl}?t=${Date.now()}`); setLatexCode(data.latexCode); } else { - showAlert("Compilation Failed", "Compilation failed: " + (data.error || "Unknown error")); + showAlert( + "Compilation Failed", + "Compilation failed: " + (data.error || "Unknown error"), + ); } } catch (err) { console.error("Compilation error:", err); @@ -118,14 +138,21 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { fileName: autoFileName, citationData: formData, latexCode, + projectId, }), }); const data = await res.json(); if (data.success) { - showAlert("Citation Saved", `Citation saved as [${data.citationNumber}]`); + showAlert( + "Citation Saved", + `Citation saved as [${data.citationNumber}]`, + ); loadSavedCitations(); } else { - showAlert("Save Failed", "Save failed: " + (data.error || "Unknown error")); + showAlert( + "Save Failed", + "Save failed: " + (data.error || "Unknown error"), + ); } } catch (err) { console.error("Save error:", err); @@ -154,20 +181,20 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { } }; - const handleDeleteCitation = useCallback( - (fileName) => { - setCitationToDelete(fileName); - setShowDeleteConfirm(true); - }, - [], - ); + const handleDeleteCitation = useCallback((fileName) => { + setCitationToDelete(fileName); + setShowDeleteConfirm(true); + }, []); const confirmDelete = useCallback(async () => { if (!citationToDelete) return; try { - const res = await fetch(`${API_BASE_URL}/api/citation/${citationToDelete}`, { - method: "DELETE", - }); + const res = await fetch( + `${API_BASE_URL}/api/citation/${citationToDelete}?projectId=${projectId || ""}`, + { + method: "DELETE", + }, + ); if (res.ok) { loadSavedCitations(); setShowDeleteConfirm(false); @@ -266,8 +293,20 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => { // --- Render --- return ( -
-
+
+
{/* Header */}
@@ -325,7 +364,9 @@ const CitationManager = ({ onClose, onInsert, showInsertButton, isModal }) => {
{!isAuthenticated ? (
-

Sign in to search academic papers

+

+ Sign in to search academic papers +

diff --git a/src/components/easyMathInput.jsx b/src/components/easyMathInput.jsx index edd381a..5148655 100644 --- a/src/components/easyMathInput.jsx +++ b/src/components/easyMathInput.jsx @@ -24,7 +24,7 @@ import axios from "axios"; const API_BASE_URL = "http://localhost:5000"; -const EasyMathInput = ({ onClose, onInsert }) => { +const EasyMathInput = ({ onClose, onInsert, projectId }) => { const { isAuthenticated } = useAuth(); const navigate = useNavigate(); @@ -142,7 +142,9 @@ const EasyMathInput = ({ onClose, onInsert }) => { // --- API HANDLERS --- const loadSavedEquations = async () => { try { - const res = await fetch(`${API_BASE_URL}/api/equations/list`); + const res = await fetch( + `${API_BASE_URL}/api/equations/list?projectId=${projectId || ""}`, + ); if (res.ok) { const data = await res.json(); // Ensure we set an array, otherwise default to empty [] @@ -279,7 +281,11 @@ const EasyMathInput = ({ onClose, onInsert }) => { await fetch(`${API_BASE_URL}/api/equations/save`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ fileName: saveFileName, latex: latexCode }), + body: JSON.stringify({ + fileName: saveFileName, + latex: latexCode, + projectId, + }), }); setShowSaveDialog(false); loadSavedEquations(); @@ -289,9 +295,12 @@ const EasyMathInput = ({ onClose, onInsert }) => { }; const handleDeleteEquation = async (fileName) => { try { - const res = await fetch(`${API_BASE_URL}/api/equations/${fileName}`, { - method: "DELETE", - }); + const res = await fetch( + `${API_BASE_URL}/api/equations/${fileName}?projectId=${projectId || ""}`, + { + method: "DELETE", + }, + ); if (res.ok) { loadSavedEquations(); diff --git a/src/components/sideBar.jsx b/src/components/sideBar.jsx index 2aab3ec..19ee3e5 100644 --- a/src/components/sideBar.jsx +++ b/src/components/sideBar.jsx @@ -454,12 +454,16 @@ const DynamicSideBar = ({ {/* Easy Math Input Modal */} {isMathModalOpen && ( - setIsMathModalOpen(false)} /> + setIsMathModalOpen(false)} + /> )} {/* Citation Manager Modal */} {isCitationModalOpen && ( setIsCitationModalOpen(false)} /> diff --git a/src/pages/editorPage.jsx b/src/pages/editorPage.jsx index c17d86e..5092f39 100644 --- a/src/pages/editorPage.jsx +++ b/src/pages/editorPage.jsx @@ -1438,6 +1438,7 @@ const EditorPage = () => {
setActiveRightView("preview")} showInsertButton={!!insertTargetQuill || activeView === "code"} @@ -1668,6 +1669,7 @@ const EditorPage = () => { {/* Math Insert Modal for main Monaco editor */} { setShowMathModal(false);