diff --git a/scripts/utils.js b/scripts/utils.js index cffa7f0..4742aef 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1613,13 +1613,24 @@ syncCustomTopNavActiveState(); } + function isCustomMenuCardTreeTarget(target) { + if (!target || target.nodeType !== 1 || typeof target.closest !== 'function') return false; + if (target.closest('.skinHeader, .mainDrawer, .navDrawer, #app-user-menu, .customMenuOptions, .adminMenuOptions, .userMenuOptions, .headerTabs, .headerRight')) { + return false; + } + return !!(target.closest('.itemsContainer') || target.closest('.card')); + } + function ensureCustomMenuLinkObserver() { if (customMenuLinkObserver || typeof MutationObserver === 'undefined' || !document.body) { return; } let scheduled = false; - customMenuLinkObserver = new MutationObserver(() => { + customMenuLinkObserver = new MutationObserver((mutations) => { + const needsReapply = mutations.some((mutation) => !isCustomMenuCardTreeTarget(mutation.target)); + if (!needsReapply) return; + if (scheduled) return; scheduled = true; requestAnimationFrame(() => { diff --git a/scripts/watchlist.js b/scripts/watchlist.js index 4e2e450..9f58c31 100644 --- a/scripts/watchlist.js +++ b/scripts/watchlist.js @@ -7606,45 +7606,71 @@ In the Custom Tabs plugin, add a new tab with the following HTML content: }); } - // Set up MutationObserver to watch for new overlay containers + function processWatchlistOverlaysInRoot(root) { + if (!root || root.nodeType !== Node.ELEMENT_NODE) return; + if (root.classList?.contains('cardOverlayContainer')) { + const buttonContainer = root.querySelector('.cardOverlayButton-br'); + if (buttonContainer) addWatchlistButton(root); + return; + } + const overlayContainers = root.querySelectorAll?.('.cardOverlayContainer'); + if (!overlayContainers?.length) return; + overlayContainers.forEach((overlayContainer) => { + const buttonContainer = overlayContainer.querySelector('.cardOverlayButton-br'); + if (buttonContainer) addWatchlistButton(overlayContainer); + }); + } + function setupWatchlistButtonObserver() { - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.type === 'childList') { - // Check for added nodes - mutation.addedNodes.forEach((node) => { - if (node.nodeType === Node.ELEMENT_NODE) { - // Check if the added node is an overlay container - if (node.classList && node.classList.contains('cardOverlayContainer')) { - const buttonContainer = node.querySelector('.cardOverlayButton-br'); - if (buttonContainer) { - addWatchlistButton(node); - } - } - - // Check for overlay containers within the added node - const overlayContainers = node.querySelectorAll && node.querySelectorAll('.cardOverlayContainer'); - if (overlayContainers && overlayContainers.length > 0) { - overlayContainers.forEach((overlayContainer) => { - const buttonContainer = overlayContainer.querySelector('.cardOverlayButton-br'); - if (buttonContainer) { - addWatchlistButton(overlayContainer); - } - }); - } - } - }); + /** @type {WeakMap} */ + const itemsContainerObservers = new WeakMap(); + /** @type {WeakSet} */ + const observedContainers = new WeakSet(); + + function attachItemsContainerObserver(itemsContainer) { + if (!itemsContainer || observedContainers.has(itemsContainer)) return; + observedContainers.add(itemsContainer); + + const shallow = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node.nodeType !== Node.ELEMENT_NODE) continue; + processWatchlistOverlaysInRoot(node); + } } }); + shallow.observe(itemsContainer, { childList: true, subtree: false }); + itemsContainerObservers.set(itemsContainer, shallow); + processWatchlistOverlaysInRoot(itemsContainer); + } + + function discoverItemsContainers(root) { + if (!root || root.nodeType !== Node.ELEMENT_NODE) return; + if (root.classList?.contains('itemsContainer')) { + attachItemsContainerObserver(root); + } + root.querySelectorAll?.('.itemsContainer').forEach(attachItemsContainerObserver); + } + + const discoveryObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node.nodeType !== Node.ELEMENT_NODE) continue; + // Skip deep card-internal inserts (handled by shallow itemsContainer observers) + if (node.closest?.('.card') && !node.classList?.contains('itemsContainer')) { + continue; + } + discoverItemsContainers(node); + } + } }); - - // Start observing - observer.observe(document.body, { + + discoveryObserver.observe(document.body, { childList: true, subtree: true }); - - // Process any existing overlay containers + + discoverItemsContainers(document.body); processExistingOverlayContainers(); }