Bumble Filter (Interactive Settings with Radio Buttons & Number Filter)

Highlights profiles on Bumble based on various criteria (Nice, Neutral, No-Go) with an interactive settings panel, radio button selection, and specific handling for height.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bumble Filter (Interactive Settings with Radio Buttons & Number Filter)
// @namespace    http://tampermonkey.net/
// @version      0.9.1
// @description  Highlights profiles on Bumble based on various criteria (Nice, Neutral, No-Go) with an interactive settings panel, radio button selection, and specific handling for height.
// @author       Your Name
// @match        *://*.bumble.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-idle
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- GLOBAL VARIABLES AND DEFAULT CONFIGURATION ---
    const SETTINGS_KEY = 'bumbleFilterSettings_v0_9_1'; // Updated version key

    const HIGHLIGHT_COLOR_NICE = 'lightgreen';
    const HIGHLIGHT_COLOR_INDIFFERENT = '#f0f0f0'; // Light gray for "neutral"
    const HIGHLIGHT_COLOR_NO_GO = '#ffcccb';

    const defaultSettingsTemplate = {
        education: { imageSubstring: 'education', desired: [], noGo: [], label: "Education", allKnownValues: [], type: "radio" },
        familyplans: { imageSubstring: 'familyplans', desired: [], noGo: [], label: "Family Plans", allKnownValues: [], type: "radio" },
        height: {
            imageSubstring: 'height',
            desiredMinCm: null,
            desiredMaxCm: null,
            noGoSpecificCm: [],
            label: "Height",
            allKnownValues: [],
            type: "numberRange"
        },
        exercise: { imageSubstring: 'exercise', desired: [], noGo: [], label: "Exercise", allKnownValues: [], type: "radio" },
        drinking: { imageSubstring: 'drinking', desired: [], noGo: [], label: "Drinking Habits", allKnownValues: [], type: "radio" },
        smoking: { imageSubstring: 'smoking', desired: [], noGo: [], label: "Smoking Habits", allKnownValues: [], type: "radio" },
        cannabis: { imageSubstring: 'cannabis', desired: [], noGo: [], label: "Cannabis Use", allKnownValues: [], type: "radio" },
        religion: { imageSubstring: 'religion', desired: [], noGo: [], label: "Religion", allKnownValues: [], type: "radio" },
        intentions: { imageSubstring: 'intentions', desired: [], noGo: [], label: "Intentions", allKnownValues: [], type: "radio" },
        politics: { imageSubstring: 'politics', desired: [], noGo: [], label: "Politics", allKnownValues: [], type: "radio" },
        starsign: { imageSubstring: 'starsign', desired: [], noGo: [], label: "Star Sign", allKnownValues: [], type: "radio" }
    };

    let currentSettings = {};
    const profileCardSelectors = ['.encounters-story'];

    function parseCmValue(cmString) {
        if (typeof cmString !== 'string') return null;
        const match = cmString.match(/(\d+)/);
        return match ? parseInt(match[1], 10) : null;
    }

    function loadSettings() {
        console.log("[Bumble Filter] Loading settings...");
        const storedSettings = GM_getValue(SETTINGS_KEY);
        currentSettings = JSON.parse(JSON.stringify(defaultSettingsTemplate));

        if (storedSettings) {
            console.log("[Bumble Filter] Found stored settings:", JSON.parse(JSON.stringify(storedSettings)));
            for (const key in currentSettings) {
                if (storedSettings[key]) {
                    const categoryTemplate = defaultSettingsTemplate[key];
                    const storedCategory = storedSettings[key];

                    if (categoryTemplate.type === "numberRange") {
                        currentSettings[key].desiredMinCm = storedCategory.desiredMinCm !== undefined ? (parseInt(String(storedCategory.desiredMinCm), 10) || null) : categoryTemplate.desiredMinCm;
                        currentSettings[key].desiredMaxCm = storedCategory.desiredMaxCm !== undefined ? (parseInt(String(storedCategory.desiredMaxCm), 10) || null) : categoryTemplate.desiredMaxCm;
                        currentSettings[key].noGoSpecificCm = Array.isArray(storedCategory.noGoSpecificCm) ? storedCategory.noGoSpecificCm.map(s => parseInt(String(s), 10)).filter(n => !isNaN(n)) : categoryTemplate.noGoSpecificCm;
                    } else {
                        currentSettings[key].desired = Array.isArray(storedCategory.desired) ? storedCategory.desired.map(s => String(s).toLowerCase().trim()).filter(s => s) : categoryTemplate.desired;
                        currentSettings[key].noGo = Array.isArray(storedCategory.noGo) ? storedCategory.noGo.map(s => String(s).toLowerCase().trim()).filter(s => s) : categoryTemplate.noGo;
                    }
                    currentSettings[key].allKnownValues = Array.isArray(storedCategory.allKnownValues) ? storedCategory.allKnownValues.map(s => String(s).toLowerCase().trim()).filter(s => s) : [];
                }
            }
        }
        for (const key in currentSettings) {
            const category = currentSettings[key];
            if (category.type === "radio") {
                const knownValuesSet = new Set(category.allKnownValues);
                category.desired.forEach(val => knownValuesSet.add(String(val).toLowerCase().trim()));
                category.noGo.forEach(val => knownValuesSet.add(String(val).toLowerCase().trim()));
                category.allKnownValues = Array.from(knownValuesSet).sort();
            }
        }
        console.log("[Bumble Filter] Settings loaded and merged:", JSON.parse(JSON.stringify(currentSettings)));
    }

    function saveSettings() {
        console.log("[Bumble Filter] Attempting to save settings. Current settings object before reading DOM:", JSON.parse(JSON.stringify(currentSettings)));
        for (const categoryKey in currentSettings) {
            const category = currentSettings[categoryKey];
            const panelContent = document.getElementById('bf-panel-content');
            if (!panelContent) {
                console.error("[Bumble Filter] Panel content not found for saving.");
                continue;
            }

            console.log(`[Bumble Filter] Saving category: ${categoryKey}`);

            if (category.type === "numberRange") {
                const minInput = panelContent.querySelector(`#bf-desiredMinCm-${categoryKey}`);
                const maxInput = panelContent.querySelector(`#bf-desiredMaxCm-${categoryKey}`);
                const noGoInput = panelContent.querySelector(`#bf-noGoSpecificCm-${categoryKey}`);

                category.desiredMinCm = minInput && minInput.value.trim() !== "" ? parseInt(minInput.value, 10) : null;
                category.desiredMaxCm = maxInput && maxInput.value.trim() !== "" ? parseInt(maxInput.value, 10) : null;
                category.noGoSpecificCm = noGoInput ? noGoInput.value.split('\n').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n)) : [];
            } else {
                const newDesired = [];
                const newNoGo = [];

                category.allKnownValues.forEach((value, index) => {
                    const stringValue = String(value);
                    const radioGroupName = `bf-state-${categoryKey}-${index}`;
                    const checkedRadio = panelContent.querySelector(`input[name="${radioGroupName}"]:checked`);

                    if (checkedRadio) {
                        if (checkedRadio.value === 'nice') {
                            newDesired.push(stringValue);
                        } else if (checkedRadio.value === 'nogo') {
                            newNoGo.push(stringValue);
                        }
                    }
                });
                category.desired = newDesired;
                category.noGo = newNoGo;
                console.log(`[Bumble Filter] Category ${categoryKey} after DOM read - Desired:`, [...category.desired], "NoGo:", [...category.noGo]);
            }
        }
        GM_setValue(SETTINGS_KEY, currentSettings);
        console.log("[Bumble Filter] Settings saved to GM_setValue:", JSON.parse(JSON.stringify(currentSettings)));
        document.querySelectorAll('[data-custom-filter-processed-v9-1]').forEach(card => {
            card.removeAttribute('data-custom-filter-processed-v9-1');
        });
        processVisibleProfileCards();
    }

    function resetSettings() {
        console.log("[Bumble Filter] Resetting settings to default.");
        if (confirm("Do you really want to reset all filter settings to their default values?")) {
            GM_setValue(SETTINGS_KEY, undefined);
            loadSettings();
            const panel = document.getElementById('bumble-filter-settings-panel');
            if (panel && panel.style.display === 'block') {
                 buildSettingsPanelContent(panel.querySelector('#bf-panel-content'));
            }
            document.querySelectorAll('[data-custom-filter-processed-v9-1]').forEach(card => {
                card.removeAttribute('data-custom-filter-processed-v9-1');
            });
            processVisibleProfileCards();
            alert("Settings have been reset.");
        }
    }

    function buildSettingsPanelContent(panelContentDiv) {
        if (!panelContentDiv) {
            console.error("[Bumble Filter] buildSettingsPanelContent: panelContentDiv is null");
            return;
        }
        for (const key in currentSettings) {
            const category = currentSettings[key];
            if (category.type === "radio") {
                const knownValuesSet = new Set(category.allKnownValues);
                currentSettings[key].desired.forEach(val => knownValuesSet.add(String(val).toLowerCase().trim()));
                currentSettings[key].noGo.forEach(val => knownValuesSet.add(String(val).toLowerCase().trim()));
                category.allKnownValues = Array.from(knownValuesSet).sort();
            }
        }
        console.log("[Bumble Filter] Building panel content with current settings for UI:", JSON.parse(JSON.stringify(currentSettings)));

        let contentHtml = '';
        for (const categoryKey in currentSettings) {
            const category = currentSettings[categoryKey];
            contentHtml += `<div class="bf-category-section"><h3>${category.label} (ID: ${category.imageSubstring})</h3>`;

            if (category.type === "numberRange") {
                contentHtml += `
                    <div class="bf-input-group">
                        <label for="bf-desiredMinCm-${categoryKey}">Min. desired height (cm):</label>
                        <input type="number" id="bf-desiredMinCm-${categoryKey}" value="${category.desiredMinCm === null ? '' : category.desiredMinCm}" placeholder="e.g. 170">
                    </div>
                    <div class="bf-input-group">
                        <label for="bf-desiredMaxCm-${categoryKey}">Max. desired height (cm):</label>
                        <input type="number" id="bf-desiredMaxCm-${categoryKey}" value="${category.desiredMaxCm === null ? '' : category.desiredMaxCm}" placeholder="e.g. 185">
                    </div>
                    <div class="bf-input-group">
                        <label for="bf-noGoSpecificCm-${categoryKey}">No-Go heights (cm, per line):</label>
                        <textarea id="bf-noGoSpecificCm-${categoryKey}" placeholder="e.g. 160\n195">${category.noGoSpecificCm.join('\n')}</textarea>
                    </div>`;
                if (category.allKnownValues.length > 0) {
                     contentHtml += `<p class="bf-info-text">Discovered heights (examples): ${category.allKnownValues.map(v => parseCmValue(v) + 'cm').filter(v => v !== 'nullcm').slice(0,10).join(', ')}${category.allKnownValues.length > 10 ? '...' : ''}</p>`;
                }
            } else {
                if (category.allKnownValues.length === 0) {
                    contentHtml += `<p class="bf-no-values">No values discovered for this category yet. They will be collected as you swipe.</p>`;
                }
                const sortedKnownValues = [...category.allKnownValues].sort();

                sortedKnownValues.forEach((value, index) => {
                    const stringValue = String(value);
                    const isDesired = category.desired.includes(stringValue);
                    const isNoGo = category.noGo.includes(stringValue);
                    const isNeutral = !isDesired && !isNoGo;
                    const radioGroupName = `bf-state-${categoryKey}-${index}`;

                    contentHtml += `
                        <div class="bf-value-row">
                            <span class="bf-value-label" title="${stringValue}">${stringValue.length > 25 ? stringValue.substring(0, 22) + '...' : stringValue}</span>
                            <div class="bf-radio-group">
                                <label class="bf-radio-label"><input type="radio" name="${radioGroupName}" value="nice" ${isDesired ? 'checked' : ''}> Nice</label>
                                <label class="bf-radio-label"><input type="radio" name="${radioGroupName}" value="neutral" ${isNeutral ? 'checked' : ''}> Neutral</label>
                                <label class="bf-radio-label"><input type="radio" name="${radioGroupName}" value="nogo" ${isNoGo ? 'checked' : ''}> No-Go</label>
                            </div>
                        </div>`;
                });
            }
            contentHtml += `</div>`;
        }
        panelContentDiv.innerHTML = contentHtml;
    }

    function createSettingsPanelShell() {
        let panel = document.getElementById('bumble-filter-settings-panel');
        if (panel) return panel;

        console.log("[Bumble Filter] Creating settings panel shell for the first time.");
        let panelHtml = `
            <div id="bf-panel-header">
                <h2>Bumble Filter Settings</h2>
                <button id="bf-close-panel" title="Close">X</button>
            </div>
            <div id="bf-panel-content"></div>
            <div id="bf-panel-footer">
                <button id="bf-reset-settings" title="Reset all settings to default">Reset</button>
                <button id="bf-save-settings">Save & Apply</button>
            </div>`;
        panel = document.createElement('div');
        panel.id = 'bumble-filter-settings-panel';
        panel.innerHTML = panelHtml;
        document.body.appendChild(panel);

        document.getElementById('bf-save-settings').addEventListener('click', () => saveSettings());
        document.getElementById('bf-close-panel').addEventListener('click', () => {
            const panelToClose = document.getElementById('bumble-filter-settings-panel');
            if(panelToClose) panelToClose.style.display = 'none';
        });
        document.getElementById('bf-reset-settings').addEventListener('click', () => resetSettings());
        return panel;
    }

    function addStyles() {
        GM_addStyle(`
            #bumble-filter-settings-panel {
                position: fixed; top: 50px; right: 20px; width: 480px; max-height: 85vh;
                background-color: white; border: 1px solid #ccc; box-shadow: 0 0 15px rgba(0,0,0,0.2);
                z-index: 100000 !important; display: none; font-family: Arial, sans-serif; font-size: 14px; border-radius: 8px;
                pointer-events: auto !important;
            }
            #bf-panel-header {
                background-color: #f0f0f0; padding: 10px 15px; border-bottom: 1px solid #ccc;
                display: flex; justify-content: space-between; align-items: center;
                border-top-left-radius: 8px; border-top-right-radius: 8px;
            }
            #bf-panel-header h2 { margin: 0; font-size: 16px; }
            #bf-close-panel, #bf-save-settings, #bf-open-settings-button, #bf-reset-settings {
                pointer-events: auto !important; cursor: pointer;
            }
            #bf-close-panel { background: none; border: none; font-size: 20px; padding: 0 5px;}
            #bf-panel-content { padding: 15px; overflow-y: auto; max-height: calc(85vh - 100px); }
            .bf-category-section { margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
            .bf-category-section:last-child { border-bottom: none; margin-bottom: 0; }
            .bf-category-section h3 { font-size: 14px; margin-top: 0; margin-bottom: 10px; color: #333; }
            .bf-no-values, .bf-info-text { font-style: italic; color: #777; margin-left: 10px; font-size: 12px; }
            .bf-value-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; padding: 4px; border-radius: 3px; }
            .bf-value-row:hover { background-color: #f9f9f9; }
            .bf-value-label { flex-basis: 35%; font-size: 13px; margin-right: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
            .bf-radio-group { display: flex; gap: 8px; pointer-events: auto !important; flex-basis: 65%; justify-content: flex-end; }
            .bf-radio-label { font-size: 12px; cursor: pointer; display:flex; align-items:center; pointer-events: auto !important; padding: 2px 5px; border-radius:3px; }
            .bf-radio-label:hover { background-color: #e9e9e9; }
            .bf-radio-label input[type="radio"] {
                margin-right: 3px; cursor: pointer; pointer-events: auto !important;
                z-index: 100001 !important; position: relative; appearance: auto !important;
                -webkit-appearance: auto !important; -moz-appearance: auto !important;
                opacity: 1 !important; visibility: visible !important;
                width: auto !important; height: auto !important;
            }
            .bf-input-group { margin-bottom: 10px; }
            .bf-input-group label { display: block; margin-bottom: 4px; font-weight: bold; font-size: 13px; }
            .bf-input-group input[type="number"], .bf-input-group textarea {
                width: 95%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px;
            }
            .bf-input-group textarea { min-height: 50px; resize: vertical; }
            #bf-panel-footer {
                padding: 10px 15px; display: flex; justify-content: space-between; align-items: center;
                border-top: 1px solid #ccc; background-color: #f9f9f9;
                border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;
            }
            #bf-save-settings, #bf-reset-settings {
                padding: 8px 15px; color: white; border: none;
                border-radius: 4px; font-size: 14px;
            }
            #bf-save-settings { background-color: #5cb85c; }
            #bf-save-settings:hover { background-color: #4cae4c; }
            #bf-reset-settings { background-color: #d9534f; }
            #bf-reset-settings:hover { background-color: #c9302c; }
            #bf-open-settings-button {
                position: fixed; bottom: 20px; right: 20px; width: 40px; height: 40px;
                background-color: #7B1FA2; color: white; border: none; border-radius: 50%;
                font-size: 20px; font-weight: bold; text-align: center;
                box-shadow: 0 2px 5px rgba(0,0,0,0.2); z-index: 99999 !important;
            }
            #bf-open-settings-button:hover { background-color: #6A1B9A; }
        `);
    }

    function logPills(profileCard) {
        const allTextPillsOnCard = profileCard.querySelectorAll('.pill__title > div.p-3.text-ellipsis.font-weight-medium, .pill__title > div.text-ellipsis.font-weight-medium.p-3');
        let cardMatchesAnyNiceCriteria = false;
        let cardHasAnyNoGo = false;
        let settingsModifiedByNewValues = false;

        allTextPillsOnCard.forEach(pillTextElement => {
            const pillTextOriginal = pillTextElement.textContent.trim();
            const pillTextLower = pillTextOriginal.toLowerCase().replace(/\u00a0/g, " ").trim();

            if (!pillTextLower) return;

            let pillTypeKey = null;
            let imageSrcFound = null;

            const pillTitleDiv = pillTextElement.parentElement;
            if (pillTitleDiv && pillTitleDiv.classList.contains('pill__title')) {
                const commonPillWrapper = pillTextElement.closest('li, .pill, .profile-badge-container, .user-interest, div.p-2, div.profile-v2-interest__badge') || pillTitleDiv.parentElement;
                if (commonPillWrapper) {
                    const imageElement = commonPillWrapper.querySelector('img.pill__image');
                    if (imageElement && imageElement.src) {
                        imageSrcFound = imageElement.src.toLowerCase();
                        for (const key in currentSettings) {
                            if (currentSettings[key] && currentSettings[key].imageSubstring && imageSrcFound.includes(currentSettings[key].imageSubstring.toLowerCase())) {
                                pillTypeKey = key;
                                break;
                            }
                        }
                    }
                }
            }

            if (pillTypeKey) {
                const category = currentSettings[pillTypeKey];
                const stringPillTextLower = String(pillTextLower);

                if (!category.allKnownValues.map(String).includes(stringPillTextLower)) {
                    category.allKnownValues.push(stringPillTextLower);
                    category.allKnownValues.sort();
                    settingsModifiedByNewValues = true;
                    console.log(`[Bumble Filter] New value discovered for '${category.label}': "${pillTextOriginal}"`);
                }

                let currentPillHighlightColor = HIGHLIGHT_COLOR_INDIFFERENT;
                let isNice = false;
                let isNoGo = false;

                if (category.type === "numberRange") {
                    const heightValue = parseCmValue(stringPillTextLower);
                    if (heightValue !== null) {
                        if (category.noGoSpecificCm && category.noGoSpecificCm.includes(heightValue)) {
                            isNoGo = true;
                        } else {
                            const minOk = category.desiredMinCm === null || heightValue >= category.desiredMinCm;
                            const maxOk = category.desiredMaxCm === null || heightValue <= category.desiredMaxCm;
                            if (minOk && maxOk && (category.desiredMinCm !== null || category.desiredMaxCm !== null)) {
                                isNice = true;
                            }
                        }
                    }
                } else {
                    isNice = category.desired.map(String).includes(stringPillTextLower);
                    isNoGo = category.noGo.map(String).includes(stringPillTextLower);
                }

                if (isNice) {
                    currentPillHighlightColor = HIGHLIGHT_COLOR_NICE;
                    cardMatchesAnyNiceCriteria = true;
                } else if (isNoGo) {
                    currentPillHighlightColor = HIGHLIGHT_COLOR_NO_GO;
                    cardHasAnyNoGo = true;
                }

                pillTextElement.style.backgroundColor = currentPillHighlightColor;
                pillTextElement.style.borderRadius = '5px';
                pillTextElement.style.padding = '2px 4px';
                pillTextElement.style.display = 'inline-block';
                pillTextElement.style.margin = '1px';
            }
        });

        if (settingsModifiedByNewValues) {
            const panel = document.getElementById('bumble-filter-settings-panel');
            if (panel && panel.style.display === 'block') {
                console.log("[Bumble Filter] New values discovered, refreshing panel content.");
                buildSettingsPanelContent(panel.querySelector('#bf-panel-content'));
            }
        }

        if (cardHasAnyNoGo) {
            // profileCard.style.border = `3px solid ${HIGHLIGHT_COLOR_NO_GO}`;
        } else if (cardMatchesAnyNiceCriteria) {
            // profileCard.style.border = `3px solid ${HIGHLIGHT_COLOR_NICE}`;
        }
    }

    function processVisibleProfileCards() {
        let processedSomethingOnThisRun = false;
        for (const selector of profileCardSelectors) {
            const cards = document.querySelectorAll(selector);
            if (cards.length > 0) {
                cards.forEach(card => {
                    if (!card.dataset.customFilterProcessedV9_0) { // Updated dataset version
                        logPills(card);
                        card.dataset.customFilterProcessedV9_0 = 'true';
                        processedSomethingOnThisRun = true;
                    }
                });
                if (processedSomethingOnThisRun) break;
            }
        }
    }

    const observer = new MutationObserver((mutationsList) => {
        let newNodesAddedInThisBatch = false;
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                newNodesAddedInThisBatch = true;
                break;
            }
        }
        if (newNodesAddedInThisBatch) {
            setTimeout(processVisibleProfileCards, 150);
        }
    });

    function init() {
        console.log("[Bumble Filter] Script started (v0.9.0 - Radio Buttons & Number Filter).");
        loadSettings();
        addStyles();

        if (!document.getElementById('bf-open-settings-button')) {
            const openButton = document.createElement('button');
            openButton.id = 'bf-open-settings-button';
            openButton.textContent = 'B';
            openButton.title = 'Bumble Filter Settings';
            document.body.appendChild(openButton);

            openButton.addEventListener('click', () => {
                let panel = document.getElementById('bumble-filter-settings-panel');
                if (!panel) {
                    panel = createSettingsPanelShell();
                }

                if (panel.style.display === 'block') {
                    panel.style.display = 'none';
                } else {
                    buildSettingsPanelContent(panel.querySelector('#bf-panel-content'));
                    panel.style.display = 'block';
                }
            });
        }

        let targetNode = document.querySelector('main.page__content') || document.querySelector('#main main') || document.body;
        if (targetNode) {
            processVisibleProfileCards();
            observer.observe(targetNode, { childList: true, subtree: true });
        } else {
            console.warn("[Bumble Filter] Main target node for observer not found.");
            const lateObserver = new MutationObserver(() => {
                targetNode = document.querySelector('main.page__content') || document.querySelector('#main main') || document.body;
                if (targetNode) {
                    lateObserver.disconnect();
                    processVisibleProfileCards();
                    observer.observe(targetNode, { childList: true, subtree: true });
                }
            });
            lateObserver.observe(document.body, { childList: true, subtree: true });
        }
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        init();
    } else {
        window.addEventListener('DOMContentLoaded', init);
    }

})();