From f2a12f1fbf46624225da8062eef62dc89b484355 Mon Sep 17 00:00:00 2001 From: Meik Date: Thu, 26 Feb 2026 00:20:01 +0100 Subject: [PATCH] Only place below sort row when adjacent to actions --- extension/content.js | 111 ++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/extension/content.js b/extension/content.js index aacfd94..1b73344 100644 --- a/extension/content.js +++ b/extension/content.js @@ -299,13 +299,6 @@ function getDirectActionAnchorInPost(postElement, buttonBar) { return getTrackerInsertionAnchorInPost(postElement, buttonBar); } -function isNodeAfter(node, referenceNode) { - if (!node || !referenceNode || node === referenceNode) { - return false; - } - return Boolean(referenceNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING); -} - function getTopLevelAnchorWithinPost(postElement, element) { if (!postElement || !element || !postElement.contains(element)) { return null; @@ -319,58 +312,78 @@ function getTopLevelAnchorWithinPost(postElement, element) { return anchor && anchor.parentElement === postElement ? anchor : null; } -function findCommentSortAnchorInPost(postElement, actionAnchor = null) { - if (!postElement) { - return null; +function isLikelyCommentSortNode(node) { + if (!node || !isElementVisible(node) || node.closest('.fb-tracker-ui')) { + return false; } const keywords = ['relevantes zuerst', 'most relevant', 'neueste', 'newest']; - const selectorCandidates = postElement.querySelectorAll([ - '[aria-label*="relevantes zuerst" i]', - '[aria-label*="most relevant" i]', - '[aria-label*="neueste" i]', - '[aria-label*="newest" i]' - ].join(', ')); + const text = (node.textContent || '').trim().toLowerCase(); + const ariaLabel = (node.getAttribute('aria-label') || '').trim().toLowerCase(); - const candidates = new Set(selectorCandidates); - postElement.querySelectorAll('[role="button"], button').forEach((node) => { - const text = (node.textContent || '').trim().toLowerCase(); - if (text && text.length <= 80 && keywords.some((keyword) => text.includes(keyword))) { - candidates.add(node); - } - }); + if (ariaLabel && keywords.some((keyword) => ariaLabel.includes(keyword))) { + return true; + } - const anchors = []; - candidates.forEach((candidate) => { - if (!candidate || !isElementVisible(candidate) || candidate.closest('.fb-tracker-ui')) { - return; - } + if (text && text.length <= 120 && keywords.some((keyword) => text.includes(keyword))) { + return true; + } - let anchor = getTopLevelAnchorWithinPost(postElement, candidate) || candidate; - if (anchor === actionAnchor) { - anchor = candidate; - } - if (!anchor || !anchor.parentElement) { - return; - } - if (actionAnchor && anchor !== actionAnchor && !isNodeAfter(anchor, actionAnchor)) { - return; - } + return false; +} - anchors.push(anchor); - }); +function hasCommentContentSignal(node) { + if (!node) { + return false; + } - if (!anchors.length) { + if (node.matches && node.matches('[data-testid*="comment" i], [data-scope="comment"]')) { + return true; + } + + return Boolean( + node.querySelector( + '[data-testid*="comment" i], [data-scope="comment"], [aria-roledescription*="comment" i], [aria-roledescription*="kommentar" i], div[role="textbox"][contenteditable="true"]' + ) + ); +} + +function findAdjacentCommentSortAnchor(postElement, actionAnchor = null) { + if (!postElement || !actionAnchor || !postElement.contains(actionAnchor)) { return null; } - anchors.sort((a, b) => { - if (a === b) { - return 0; + let sibling = actionAnchor.nextElementSibling; + for (let i = 0; i < 4 && sibling; i++) { + if (sibling.classList && sibling.classList.contains('fb-tracker-ui')) { + sibling = sibling.nextElementSibling; + continue; } - return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1; - }); - return anchors[0]; + + if (isLikelyCommentSortNode(sibling)) { + return sibling; + } + + const innerSortNode = sibling.querySelector && sibling.querySelector([ + '[aria-label*="relevantes zuerst" i]', + '[aria-label*="most relevant" i]', + '[aria-label*="neueste" i]', + '[aria-label*="newest" i]', + '[role="button"]', + 'button' + ].join(', ')); + if (innerSortNode && isLikelyCommentSortNode(innerSortNode)) { + return sibling; + } + + if (hasCommentContentSignal(sibling)) { + break; + } + + sibling = sibling.nextElementSibling; + } + + return null; } const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache @@ -3921,7 +3934,7 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options = const directActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar); const inPostActionAnchor = getTopLevelAnchorWithinPost(postElement, directActionAnchor) || directActionAnchor; - const commentSortAnchor = findCommentSortAnchorInPost(postElement, inPostActionAnchor); + const commentSortAnchor = findAdjacentCommentSortAnchor(postElement, inPostActionAnchor); const preferredAnchor = commentSortAnchor || inPostActionAnchor; // Strategy 1: Insert below actions; if present, place below the sort row @@ -3980,7 +3993,7 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options = // Try to re-insert const currentDirectActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar); const currentInPostActionAnchor = getTopLevelAnchorWithinPost(postElement, currentDirectActionAnchor) || currentDirectActionAnchor; - const currentCommentSortAnchor = findCommentSortAnchorInPost(postElement, currentInPostActionAnchor); + const currentCommentSortAnchor = findAdjacentCommentSortAnchor(postElement, currentInPostActionAnchor); const currentPreferredAnchor = currentCommentSortAnchor || currentInPostActionAnchor; if (currentPreferredAnchor && currentPreferredAnchor.parentElement) { currentPreferredAnchor.parentElement.insertBefore(container, currentPreferredAnchor.nextSibling);