diff --git a/dist/js/docsView.js b/dist/js/docsView.js index 4f4d3868..a6756202 100644 --- a/dist/js/docsView.js +++ b/dist/js/docsView.js @@ -322,6 +322,7 @@ function setupScrollSpy(container) { const headings = Array.from( container.querySelectorAll("h2[id], h3[id], h4[id], h5[id]") ); + console.log(`[Spy] setup found ${headings.length} headings`); if (headings.length === 0) { return; } diff --git a/dist/js/feedEngine/sorters.js b/dist/js/feedEngine/sorters.js index c4620509..f4b1e9e8 100644 --- a/dist/js/feedEngine/sorters.js +++ b/dist/js/feedEngine/sorters.js @@ -301,6 +301,13 @@ export function createExploreDiversitySorter({ const selected = []; const remaining = [...candidates]; + // Cache max similarity for each remaining candidate against the selected set + // Map + const similarityCache = new Map(); + for (const candidate of remaining) { + similarityCache.set(candidate, { penalty: 0, similarItemId: "" }); + } + while (remaining.length > 0) { let bestCandidate = null; let bestMmr = Number.NEGATIVE_INFINITY; @@ -326,22 +333,7 @@ export function createExploreDiversitySorter({ for (const candidate of remaining) { const rawScore = normalizeScore(candidate?.metadata?.exploreScore); - let penalty = 0; - let similarItemId = ""; - - if (selected.length > 0) { - const candidateVector = vectors.get(candidate); - for (const selectedItem of selected) { - const similarity = cosineSimilarity( - candidateVector, - vectors.get(selectedItem), - ); - if (similarity > penalty) { - penalty = similarity; - similarItemId = stableVideoId(selectedItem); - } - } - } + const { penalty, similarItemId } = similarityCache.get(candidate); const mmrScore = normalizedLambda * rawScore - (1 - normalizedLambda) * penalty; @@ -404,6 +396,22 @@ export function createExploreDiversitySorter({ const index = remaining.indexOf(bestCandidate); if (index >= 0) { remaining.splice(index, 1); + similarityCache.delete(bestCandidate); + } + + // Update similarity cache for remaining candidates against the newly selected item + const bestCandidateVector = vectors.get(bestCandidate); + for (const candidate of remaining) { + const candidateVector = vectors.get(candidate); + const similarity = cosineSimilarity( + candidateVector, + bestCandidateVector, + ); + const cache = similarityCache.get(candidate); + if (similarity > cache.penalty) { + cache.penalty = similarity; + cache.similarItemId = stableVideoId(bestCandidate); + } } } diff --git a/dist/js/state/profileCache.js b/dist/js/state/profileCache.js index eacf26f4..1fd3e253 100644 --- a/dist/js/state/profileCache.js +++ b/dist/js/state/profileCache.js @@ -26,7 +26,7 @@ function sanitizeProfileString(value) { class ProfileCache { constructor() { this.activePubkey = null; - this.memoryCache = new Map(); // For runtime decrypted data: pubkey:section -> data + this.memoryCache = new Map(); // For runtime decrypted data: pubkey -> Map(section -> data) this.listeners = new Set(); } @@ -240,9 +240,11 @@ class ProfileCache { getProfileData(pubkey, section) { // 1. Check memory cache (runtime data) - const memKey = `${pubkey}:${section}`; - if (this.memoryCache.has(memKey)) { - return this.memoryCache.get(memKey); + if (this.memoryCache.has(pubkey)) { + const userCache = this.memoryCache.get(pubkey); + if (userCache.has(section)) { + return userCache.get(section); + } } // 2. Load from persistence @@ -273,7 +275,10 @@ class ProfileCache { } // Populate memory cache to avoid repeat parsing - this.memoryCache.set(memKey, parsed); + if (!this.memoryCache.has(pubkey)) { + this.memoryCache.set(pubkey, new Map()); + } + this.memoryCache.get(pubkey).set(section, parsed); return parsed; } } catch (error) { @@ -302,39 +307,42 @@ class ProfileCache { } // Update memory cache - const memKey = `${pubkey}:${section}`; - this.memoryCache.set(memKey, data); + if (!this.memoryCache.has(pubkey)) { + this.memoryCache.set(pubkey, new Map()); + } + this.memoryCache.get(pubkey).set(section, data); this.emit("update", { pubkey, section, data }); this.emit("partition-updated", { pubkey, key: section }); } setMemoryDataForPubkey(pubkey, section, data) { - const memKey = `${pubkey}:${section}`; - this.memoryCache.set(memKey, data); + if (!this.memoryCache.has(pubkey)) { + this.memoryCache.set(pubkey, new Map()); + } + this.memoryCache.get(pubkey).set(section, data); + this.emit("update", { pubkey, section, data }); } setMemoryData(section, data) { if (!this.activePubkey) return; - const key = `${this.activePubkey}:${section}`; - this.memoryCache.set(key, data); + if (!this.memoryCache.has(this.activePubkey)) { + this.memoryCache.set(this.activePubkey, new Map()); + } + this.memoryCache.get(this.activePubkey).set(section, data); } getMemoryData(section) { if (!this.activePubkey) return null; - const key = `${this.activePubkey}:${section}`; - return this.memoryCache.get(key); + if (this.memoryCache.has(this.activePubkey)) { + return this.memoryCache.get(this.activePubkey).get(section); + } + return undefined; } clearMemoryCache(pubkey) { - // Clear all entries starting with pubkey: - const prefix = `${pubkey}:`; - for (const key of this.memoryCache.keys()) { - if (key.startsWith(prefix)) { - this.memoryCache.delete(key); - } - } + this.memoryCache.delete(pubkey); } clearSignerRuntime(pubkey) {