diff --git a/background.js b/background.js index 007c7c2..e104e46 100644 --- a/background.js +++ b/background.js @@ -639,24 +639,42 @@ async function persistTabResult(tab, result) { } catch {} } +// URL fragment (hash) never affects server response or our ID extraction. Strip it +// when comparing URLs so in-page anchor jumps (e.g. `#gallery-1`, `#gallery-2`) don't +// invalidate the tab cache or re-trigger scans. See exoticaz.to gallery navigation. +function stripHash(url) { + if (typeof url !== "string") return url; + const i = url.indexOf("#"); + return i === -1 ? url : url.slice(0, i); +} + +// Tracks the last full URL we saw per tab so we can detect hash-only changes +// (which Chrome still reports via `chrome.tabs.onUpdated` with `changeInfo.url`). +const tabLastUrl = new Map(); // tabId -> last full url seen + async function getTabResult(tab) { if (!tab || tab.id == null) return null; try { const got = await chrome.storage.session.get(tabCacheKey(tab.id)); const entry = got[tabCacheKey(tab.id)]; if (!entry) return null; - if (entry.url !== (tab.url || "")) return null; + if (stripHash(entry.url) !== stripHash(tab.url || "")) return null; return entry; } catch { return null; } } chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { - if (changeInfo.url) chrome.storage.session.remove(tabCacheKey(tabId)); + if (!changeInfo.url) return; + const prev = tabLastUrl.get(tabId); + // Hash-only change → keep the cached result; same page, just a fragment jump. + if (prev && stripHash(prev) === stripHash(changeInfo.url)) return; + chrome.storage.session.remove(tabCacheKey(tabId)); }); chrome.tabs.onRemoved.addListener((tabId) => { stopBadgeSpinner(tabId); chrome.storage.session.remove(tabCacheKey(tabId)); lastAutoCheck.delete(tabId); + tabLastUrl.delete(tabId); const t = pendingSpaTimer.get(tabId); if (t) { clearTimeout(t); pendingSpaTimer.delete(tabId); } }); @@ -999,6 +1017,7 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.status === "complete") { const t = pendingSpaTimer.get(tabId); if (t) { clearTimeout(t); pendingSpaTimer.delete(tabId); } + tabLastUrl.set(tabId, tab.url); maybeAutoCheck(tabId, tab, "complete"); return; } @@ -1006,6 +1025,11 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { // the page has a chance to update its title/DOM, and so a real reload (which // also emits a url change before complete) doesn't double-fire. if (typeof changeInfo.url === "string") { + const prevUrl = tabLastUrl.get(tabId); + tabLastUrl.set(tabId, changeInfo.url); + // Hash-only change (e.g. `#gallery-2` anchor jump) is purely client-side UI + // state. Suppress the SPA scan trigger so anchor navigation doesn't re-fire. + if (prevUrl && stripHash(prevUrl) === stripHash(changeInfo.url)) return; const prev = pendingSpaTimer.get(tabId); if (prev) clearTimeout(prev); const handle = setTimeout(() => { diff --git a/manifest.json b/manifest.json index 853f819..4dbdbf0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "rclone-jav", - "version": "0.1.52", + "version": "0.1.53", "description": "Check current page title against your rc-jav library via native messaging.", "permissions": [ "nativeMessaging",