From 1994ed174d425a3ac8a5631522ed0da298b103dc Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:40:58 +0000 Subject: [PATCH] perf: precalculate search strings and formatting to remove loop bottlenecks Co-authored-by: MrAlokTech <107493955+MrAlokTech@users.noreply.github.com> --- .jules/bolt.md | 3 +++ script.js | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..14e6d7c --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## $(date +%Y-%m-%d) - [Pre-calculating Derived Data in Loops] +**Learning:** Recalculating dates (`new Date()`, `.toLocaleDateString()`) and deriving search indices (`toLowerCase().includes()`) inline within render loops (e.g. `renderPDFs` and `createPDFCard`) creates a massive performance bottleneck for vanilla JS apps, significantly degrading list filtering performance when datasets are large. +**Action:** When working with derived runtime state, compute and append these values (like `_searchStr`, `_isNew`, and `_formattedDate`) *once* immediately after fetching the data from the database or cache. Crucially, apply these transient properties *after* persisting to localStorage to avoid bloating the cache size. diff --git a/script.js b/script.js index 22f399d..96d86dc 100644 --- a/script.js +++ b/script.js @@ -416,6 +416,30 @@ async function syncClassSwitcher() { renderSemesterTabs(); } +// ⚡ Bolt: Pre-calculate derived search strings and date formatting once during load +// instead of recalculating inside the high-frequency renderPDFs/createPDFCard loops. +// This avoids expensive inline string manipulation and object instantiation overhead. +function prepareSearchIndex(pdfs) { + const formatter = new Intl.DateTimeFormat("en-US", { + year: "numeric", month: "short", day: "numeric" + }); + const now = new Date(); + const ONE_WEEK = 7 * 24 * 60 * 60 * 1000; + + pdfs.forEach(pdf => { + pdf._searchStr = `${pdf.title || ""} ${pdf.description || ""} ${pdf.category || ""} ${pdf.author || ""}`.toLowerCase(); + + const uploadDateObj = new Date(pdf.uploadDate); + if (!isNaN(uploadDateObj)) { + pdf._isNew = (now - uploadDateObj) < ONE_WEEK; + pdf._formattedDate = formatter.format(uploadDateObj); + } else { + pdf._isNew = false; + pdf._formattedDate = "Unknown Date"; + } + }); +} + async function loadPDFDatabase() { if (isMaintenanceActive) return; @@ -454,6 +478,7 @@ async function loadPDFDatabase() { if (shouldUseCache) { pdfDatabase = cachedData; + prepareSearchIndex(pdfDatabase); // --- FIX: CALL THIS TO POPULATE UI --- syncClassSwitcher(); renderSemesterTabs(); @@ -477,6 +502,8 @@ async function loadPDFDatabase() { data: pdfDatabase })); + prepareSearchIndex(pdfDatabase); + // --- FIX: CALL THIS TO POPULATE UI --- syncClassSwitcher(); renderPDFs(); @@ -918,10 +945,7 @@ function renderPDFs() { matchesCategory = currentCategory === 'all' || pdf.category === currentCategory; } - const matchesSearch = pdf.title.toLowerCase().includes(searchTerm) || - pdf.description.toLowerCase().includes(searchTerm) || - pdf.category.toLowerCase().includes(searchTerm) || - pdf.author.toLowerCase().includes(searchTerm); + const matchesSearch = !pdf._searchStr ? false : pdf._searchStr.includes(searchTerm); // Update return statement to include matchesClass return matchesSemester && matchesClass && matchesCategory && matchesSearch; @@ -994,9 +1018,7 @@ function createPDFCard(pdf, favoritesList, index = 0, highlightRegex = null) { const heartIconClass = isFav ? 'fas' : 'far'; const btnActiveClass = isFav ? 'active' : ''; - const uploadDateObj = new Date(pdf.uploadDate); - const timeDiff = new Date() - uploadDateObj; - const isNew = timeDiff < (7 * 24 * 60 * 60 * 1000); // 7 days + const isNew = pdf._isNew !== undefined ? pdf._isNew : false; const newBadgeHTML = isNew ? `NEW` @@ -1011,9 +1033,7 @@ function createPDFCard(pdf, favoritesList, index = 0, highlightRegex = null) { const categoryIcon = categoryIcons[pdf.category] || 'fa-file-pdf'; // Formatting Date - const formattedDate = new Date(pdf.uploadDate).toLocaleDateString('en-US', { - year: 'numeric', month: 'short', day: 'numeric' - }); + const formattedDate = pdf._formattedDate || "Unknown Date"; // Uses global escapeHtml() now