Greasy Fork

Pitchfork Reviews with r/indieheads Comments from Reddit

Load and display Reddit comments from r/indieheads on Pitchfork album review pages.

目前为 2025-04-26 提交的版本。查看 最新版本

// ==UserScript==
// @name         Pitchfork Reviews with r/indieheads Comments from Reddit
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Load and display Reddit comments from r/indieheads on Pitchfork album review pages.
// @author       TA
// @license      MIT
// @match        https://pitchfork.com/reviews/albums/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    // --- Utility Functions (developed in previous steps) ---

    /**
     * Extracts the Album Name from the Pitchfork page.
     * @returns {string|null} The album name or null if not found.
     */
    function extractAlbumName() {
        const albumElement = document.querySelector('h1[data-testid="ContentHeaderHed"]');
        return albumElement ? albumElement.textContent.trim() : null;
    }

    /**
     * Extracts the Artist Name(s) from the Pitchfork page.
     * @returns {string|string[]|null} The artist name(s) or null if not found.
     */
    function extractArtistName() {
        const artistElements = document.querySelectorAll('ul[class*="SplitScreenContentHeaderArtistWrapper"] div[class*="SplitScreenContentHeaderArtist"]');
        if (!artistElements.length) {
            return null;
        }
        const artists = Array.from(artistElements).map(el => el.textContent.trim());
        // Return a single string if only one artist, array if multiple
        return artists.length === 1 ? artists[0] : artists;
    }

    /**
     * Formats artist and album names into Reddit search query strings.
     * Returns separate queries for FRESH ALBUM and ALBUM DISCUSSION threads.
     *
     * @param {string|string[]} artistName The name of the artist(s).
     * @param {string} albumName The name of the album.
     * @returns {Object} Object with freshAlbumQuery and albumDiscussionQuery properties.
     */
    function formatAlbumSearchQueries(artistName, albumName) {
        // If artistName is an array, join with ' & ' for the query
        const formattedArtist = Array.isArray(artistName) ? artistName.join(' & ') : artistName;

        // Create simpler queries that are more likely to match
        // Remove quotes and brackets which can cause search issues
        const freshAlbumQuery = `FRESH ALBUM ${formattedArtist} ${albumName}`;
        const albumDiscussionQuery = `ALBUM DISCUSSION ${formattedArtist} ${albumName}`;

        // Return both queries separately
        return {
            freshAlbumQuery,
            albumDiscussionQuery
        };
    }

    /**
     * Constructs a Reddit search URL for the r/indieheads subreddit's JSON API endpoint.
     * Cleans the query by removing problematic characters like slashes and ampersands.
     *
     * @param {string} query The search query string.
     * @returns {string} The constructed Reddit search JSON API URL.
     */
    function buildIndieHeadsSearchJsonUrl(query) {
        // Clean the query by removing slashes, ampersands, percent signs, and plus signs with spaces
        // that might interfere with the search functionality
        const cleanedQuery = query
            .replace(/[\/&%+]/g, ' ') // Replace slashes, ampersands, percent signs, and plus signs with spaces
            .replace(/\s+/g, ' ')     // Replace multiple spaces with a single space
            .trim();                  // Remove leading/trailing spaces

        const encodedQuery = encodeURIComponent(cleanedQuery);
        const searchUrl = `https://www.reddit.com/r/indieheads/search.json?q=${encodedQuery}&restrict_sr=on&sort=relevance&t=all`;
        return searchUrl;
    }

    /**
     * Identifies relevant Reddit thread URLs from search results based on title patterns.
     * Processes FRESH ALBUM and ALBUM DISCUSSION results separately.
     * Ensures no duplicate threads are added.
     *
     * @param {Array<Object>} freshAlbumResults The results from the FRESH ALBUM search.
     * @param {Array<Object>} albumDiscussionResults The results from the ALBUM DISCUSSION search.
     * @param {string} artistName The name of the artist(s).
     * @param {string} albumName The name of the album.
     * @returns {Array<Object>} An array of objects {title: string, url: string} for all matching threads.
     */
    function identifyRelevantThreads(freshAlbumResults, albumDiscussionResults, artist, albumName) {
        const relevantThreads = [];
        // Track URLs to avoid duplicates
        const addedUrls = new Set();

        // Helper function to find the best thread from search results
        const findBestThread = (results, threadType) => {
            if (!results || !Array.isArray(results) || results.length === 0) {
                console.log(`No ${threadType} search results found.`);
                return null;
            }

            console.log(`Processing ${results.length} ${threadType} search results.`);

            // Look for an exact match first
            for (const item of results) {
                if (item.kind === "t3" && item.data && item.data.title && item.data.permalink) {
                    const title = item.data.title;
                    const url = "https://www.reddit.com" + item.data.permalink;

                    // Skip if we've already added this URL
                    if (addedUrls.has(url)) {
                        console.log(`Skipping duplicate thread: "${title}"`);
                        continue;
                    }

                    // Check if this is the right type of thread
                    if (title.toLowerCase().includes(threadType.toLowerCase()) && 
                        title.toLowerCase().includes(albumName.toLowerCase())) {
                        console.log(`Found ${threadType} thread: "${title}"`);
                        return { title, url };
                    }
                }
            }

            // If no exact match, take the first result that contains the album name
            for (const item of results) {
                if (item.kind === "t3" && item.data && item.data.title && item.data.permalink) {
                    const title = item.data.title;
                    const url = "https://www.reddit.com" + item.data.permalink;

                    // Skip if we've already added this URL
                    if (addedUrls.has(url)) {
                        console.log(`Skipping duplicate thread: "${title}"`);
                        continue;
                    }

                    if (title.toLowerCase().includes(albumName.toLowerCase())) {
                        console.log(`Found ${threadType} thread (partial match): "${title}"`);
                        return { title, url };
                    }
                }
            }

            console.log(`No matching ${threadType} thread found.`);
            return null;
        };

        // Find the best thread for each type
        const freshAlbumThread = findBestThread(freshAlbumResults, "FRESH ALBUM");

        // Add FRESH ALBUM thread if found
        if (freshAlbumThread) {
            relevantThreads.push(freshAlbumThread);
            addedUrls.add(freshAlbumThread.url); // Track the URL to avoid duplicates
            console.log(`Added FRESH ALBUM thread: "${freshAlbumThread.title}"`);
        }

        // Find ALBUM DISCUSSION thread
        const albumDiscussionThread = findBestThread(albumDiscussionResults, "ALBUM DISCUSSION");

        // Add ALBUM DISCUSSION thread if found and not a duplicate
        if (albumDiscussionThread && !addedUrls.has(albumDiscussionThread.url)) {
            relevantThreads.push(albumDiscussionThread);
            addedUrls.add(albumDiscussionThread.url);
            console.log(`Added ALBUM DISCUSSION thread: "${albumDiscussionThread.title}"`);
        } else if (albumDiscussionThread) {
            console.log(`Skipping duplicate ALBUM DISCUSSION thread: "${albumDiscussionThread.title}"`);
        }

        console.log(`Found ${relevantThreads.length} unique relevant threads`);
        return relevantThreads;
    }

    /**
     * Fetches comments from a given Reddit thread URL using the .json endpoint.
     * Note: This uses GM_xmlhttpRequest for cross-origin requests in Userscripts.
     *
     * @param {string} threadUrl The URL of the Reddit thread.
     * @returns {Promise<Array<Object>|null>} A promise that resolves with an array of comment data or null on error.
     */
    function fetchRedditComments(threadUrl) {
        console.log(`[fetchRedditComments] Attempting to fetch comments for: ${threadUrl}`);
        return new Promise((resolve, reject) => {
            // Append .json to the thread URL to get the JSON data
            const jsonUrl = threadUrl.endsWith('.json') ? threadUrl : threadUrl + '.json';

            console.log(`[fetchRedditComments] Requesting URL: ${jsonUrl}`);

            // Use GM_xmlhttpRequest for cross-origin requests
            GM_xmlhttpRequest({
                method: "GET",
                url: jsonUrl,
                onload: function(response) {
                    console.log(`[fetchRedditComments] Received response for ${jsonUrl}. Status: ${response.status}`);
                    try {
                        if (response.status === 200) {
                            console.log(`[fetchRedditComments] Response Text for ${jsonUrl}: ${response.responseText.substring(0, 500)}...`); // Log beginning of response
                            const data = JSON.parse(response.responseText);
                            console.log("[fetchRedditComments] Successfully parsed JSON response.");
                            // The JSON response for a thread includes two arrays: [submission, comments]
                            // We need the comments array (index 1)
                            if (data && data.length === 2 && data[1] && data[1].data && data[1].data.children) {
                                console.log(`[fetchRedditComments] Found comment data. Number of top-level items: ${data[1].data.children.length}`);
                                // Process the raw comment data to extract relevant info and handle replies
                                const comments = processComments(data[1].data.children);
                                console.log(`[fetchRedditComments] Processed comments. Total processed: ${comments.length}`);
                                resolve(comments);
                            } else {
                                console.error("[fetchRedditComments] Unexpected Reddit JSON structure:", data);
                                resolve(null); // Resolve with null for unexpected structure
                            }
                        } else {
                            console.error("[fetchRedditComments] Error fetching Reddit comments:", response.status, response.statusText);
                            resolve(null); // Resolve with null on HTTP error
                        }
                    } catch (e) {
                        console.error("[fetchRedditComments] Error parsing Reddit comments JSON:", e);
                        resolve(null); // Resolve with null on parsing error
                    }
                },
                onerror: function(error) {
                    console.error("[fetchRedditComments] GM_xmlhttpRequest error fetching Reddit comments:", error);
                    resolve(null); // Resolve with null on request error
                }
            });
        });
    }

    /**
     * Recursively processes raw Reddit comment data to extract relevant info and handle replies.
     * Filters out 'more' comments placeholders.
     *
     * @param {Array<Object>} rawComments The raw comment children array from Reddit API.
     * @returns {Array<Object>} An array of processed comment objects.
     */
    function processComments(rawComments) {
        const processed = [];
        if (!rawComments || !Array.isArray(rawComments)) {
            return processed;
        }

        for (const item of rawComments) {
            // Skip 'more' comments placeholders
            if (item.kind === 'more') {
                continue;
            }

            // Ensure it's a comment and has the necessary data
            if (item.kind === 't1' && item.data) {
                const commentData = item.data;
                const processedComment = {
                    author: commentData.author,
                    text: commentData.body,
                    score: commentData.score,
                    created_utc: commentData.created_utc,
                    replies: [] // Initialize replies array
                };

                // Recursively process replies if they exist
                if (commentData.replies && commentData.replies.data && commentData.replies.data.children) {
                    processedComment.replies = processComments(commentData.replies.data.children);
                }

                processed.push(processedComment);
            }
        }
        return processed;
    }

    // --- HTML Structures and Injection ---

    const REDDIT_COMMENTS_SECTION_HTML = `
        <div class="reddit-comments-section">
            <h3>Reddit Comments from r/indieheads</h3>
            <div class="reddit-comments-tabs">
                <!-- Tab buttons will be added here -->
            </div>
            <div class="reddit-comments-content">
                <!-- Comment content areas will be added here -->
                <!-- Each area will have a data-thread-id or similar to link to the tab -->
            </div>
        </div>
    `;

    /**
     * Injects HTML content after the last paragraph in the article.
     * @param {string|HTMLElement} content The HTML string or HTMLElement to inject.
     */
    function injectAfterLastParagraph(content) {
        // Find the article element
        const article = document.querySelector('article');
        if (!article) {
            console.error('Article element not found for injection');
            return;
        }

        // Find all paragraphs within the article, excluding those with class "disclaimer"
        const paragraphs = Array.from(article.querySelectorAll('p')).filter(p => 
            !p.classList.contains('disclaimer') && 
            !p.closest('.disclaimer') // Also exclude paragraphs inside elements with class "disclaimer"
        );

        if (paragraphs.length === 0) {
            console.error('No valid paragraphs found in article for injection');
            return;
        }

        // Get the last paragraph
        const lastParagraph = paragraphs[paragraphs.length - 1];

        // Insert content after the last paragraph
        if (typeof content === 'string') {
            lastParagraph.insertAdjacentHTML('afterend', content);
        } else {
            lastParagraph.insertAdjacentElement('afterend', content);
        }
    }

    // Function to render comments to HTML (basic structure)
    function renderCommentsHtml(comments, level = 0) {
        let html = `<ul class="reddit-comment-list level-${level}">`;
        if (!comments || comments.length === 0) {
            html += '<li>No comments found for this thread.</li>';
        } else {
            // Filter out deleted comments
            const validComments = comments.filter(comment => 
                comment.author !== "[deleted]" && comment.text !== "[deleted]"
            );

            if (validComments.length === 0) {
                html += '<li>No valid comments found for this thread.</li>';
            } else {
                validComments.forEach(comment => {
                    html += `<li class="reddit-comment">`;
                    
                    // Add collapse button for top-level comments
                    if (level === 0) {
                        html += `<div class="comment-meta">
                            <b>${comment.author}</b> (${comment.score} points)
                            <button class="comment-collapse-button">[−]</button>
                        </div>`;
                    } else {
                        html += `<div class="comment-meta"><b>${comment.author}</b> (${comment.score} points)</div>`;
                    }
                    
                    // Process comment text for special content
                    let processedText = comment.text;
                    
                    // Process Giphy embeds first
                    processedText = processedText.replace(/!\[gif\]\(giphy\|([a-zA-Z0-9]+)(?:\|downsized)?\)/g, (match, giphyId) => {
                        return `
                            <div class="giphy-embed-container">
                                <iframe src="https://giphy.com/embed/${giphyId}" 
                                    width="480" height="270" frameBorder="0" 
                                    class="giphy-embed" allowFullScreen></iframe>
                            </div>
                        `;
                    });
                    
                    // Process Reddit image links
                    processedText = processedText.replace(/(https:\/\/preview\.redd\.it\/[a-zA-Z0-9]+\.(jpeg|jpg|png|gif)\?[^\s)]+)/g, (match, imageUrl) => {
                        return `
                            <div class="reddit-image-container">
                                <img src="${imageUrl}" alt="Reddit Image" class="reddit-inline-image" />
                            </div>
                        `;
                    });
                    
                    // Process Markdown image syntax for Reddit images
                    processedText = processedText.replace(/!\[.*?\]\((https:\/\/preview\.redd\.it\/[a-zA-Z0-9]+\.(jpeg|jpg|png|gif)\?[^\s)]+)\)/g, (match, imageUrl) => {
                        return `
                            <div class="reddit-image-container">
                                <img src="${imageUrl}" alt="Reddit Image" class="reddit-inline-image" />
                            </div>
                        `;
                    });
                    
                    // Process basic Markdown formatting
                    
                    // Bold text
                    processedText = processedText.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
                    
                    // Italic text
                    processedText = processedText.replace(/\*([^*]+)\*/g, '<em>$1</em>');
                    
                    // Block quotes - simple implementation
                    processedText = processedText.replace(/^(>|>)\s*(.*?)$/gm, '<blockquote>$2</blockquote>');
                    
                    // Parse Markdown links
                    processedText = processedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
                        return `<a href="${url}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
                    });
                    
                    // Parse plain URLs
                    processedText = processedText.replace(/(?<!["\'])(https?:\/\/[^\s<>[\]()'"]+)(?![^<]*>)/g, (match, url) => {
                        return `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
                    });
                    
                    // Handle line breaks - simple approach
                    processedText = processedText.replace(/\n\n+/g, '</p><p>');
                    processedText = processedText.replace(/\n/g, '<br>');
                    
                    // Wrap in paragraph tags if not already
                    if (!processedText.startsWith('<p>') && 
                        !processedText.startsWith('<div') && 
                        !processedText.startsWith('<blockquote') &&
                        !processedText.includes('<div class="giphy-embed-container">') &&
                        !processedText.includes('<div class="reddit-image-container">')) {
                        processedText = `<p>${processedText}</p>`;
                    }
                    
                    html += `<div class="comment-body">${processedText}</div>`;
                    if (comment.replies && comment.replies.length > 0) {
                        html += renderCommentsHtml(comment.replies, level + 1);
                    }
                    html += `</li>`;
                });
            }
        }
        html += `</ul>`;
        return html;
    }

    function setupCommentCollapse() {
        document.querySelectorAll('.comment-collapse-button').forEach(button => {
            button.addEventListener('click', function() {
                const commentLi = this.closest('.reddit-comment');
                commentLi.classList.toggle('collapsed');
                
                // Update button text
                if (commentLi.classList.contains('collapsed')) {
                    this.textContent = '[+]';
                } else {
                    this.textContent = '[−]';
                }
            });
        });
    }

    // --- CSS Styles ---
    function injectStyles() {
        const styles = `
            @media (min-width: 2400px) {
                #main-content div[class^="GridWrapper"] {
                    max-width: 2000px;
                }
            }
            .reddit-comments-section {
                margin-top: 30px;
                padding: 20px;
                border-top: 1px solid #ddd;
                font-family: inherit;
            }
            .reddit-comments-tabs {
                display: flex;
                flex-wrap: wrap;
                margin-bottom: 15px;
            }
            .reddit-tab-button {
                padding: 8px 12px;
                margin-right: 5px;
                margin-bottom: 5px;
                background: #f0f0f0;
                border: 1px solid #ccc;
                border-radius: 4px;
                cursor: pointer;
            }
            .reddit-tab-button:not(.active):hover {
                background: #f8f8f8;
            }
            .reddit-tab-button:hover, .reddit-tab-button:active, .reddit-tab-button:focus {
                text-decoration: none;
            }
            .reddit-tab-button.active {
                background: #e0e0e0;
                border-color: #aaa;
                font-weight: bold;
            }
            .reddit-comment-list {
                list-style-type: none;
                padding-left: 0;
            }
            .reddit-comment-list.level-0 {
                padding-left: 0;
                margin-left: 0;
            }
            .reddit-comment-list.level-1 {
                padding-left: 20px;
                border-left: 2px solid #eee;
            }
            .reddit-comment-list.level-2,
            .reddit-comment-list.level-3,
            .reddit-comment-list.level-4,
            .reddit-comment-list.level-5 {
                padding-left: 20px;
                border-left: 2px solid #f5f5f5;
            }
            .reddit-comment {
                margin-bottom: 15px;
            }
            .reddit-image-container {
                margin-top: 10px;
            }
            .comment-meta {
                font-size: .9em;
                margin-bottom: 5px;
                color: #666;
            }
            .comment-body {
                line-height: 1.5;
            }
            /* Markdown formatting styles */
            .comment-body strong {
                font-weight: 700;
            }
            .comment-body em {
                font-style: italic;
            }
            .comment-body blockquote {
                border-left: 3px solid #c5c1ad;
                margin: 8px 0;
                padding: 0 8px 0 12px;
                color: #646464;
                background-color: #f8f9fa;
            }
            /* Paragraph styling */
            .comment-body p {
                margin: .8em 0;
            }

            .comment-body p:first-child {
                margin-top: 0;
            }
            .comment-body p:last-child {
                margin-bottom: 0;
            }
            .comment-body blockquote p {
                margin: .4em 0;
            }
            .reddit-comment.collapsed .comment-body,
            .reddit-comment.collapsed .reddit-comment-list {
                display: none;
            }
            .reddit-comment.collapsed {
                opacity: 0.7;
            }
            .comment-collapse-button {
                background: none;
                border: none;
                color: #0079d3;
                cursor: pointer;
                font-size: 12px;
                margin-left: 5px;
                padding: 0;
            }
            .comment-collapse-button:hover {
                text-decoration: underline;
            }
        `;

        const styleElement = document.createElement('style');
        styleElement.textContent = styles;
        document.head.appendChild(styleElement);
    }


    // --- Main Execution Logic ---

    async function init() {
        console.log("Pitchfork Reddit Comments Userscript started.");

        // Inject CSS styles
        injectStyles();

        const artist = extractArtistName();
        const album = extractAlbumName();

        if (!artist || !album) {
            console.log("Could not extract artist or album name. Exiting.");
            return;
        }

        console.log(`Found Artist: ${artist}, Album: ${album}`);

        const queries = formatAlbumSearchQueries(artist, album);
        console.log(`Search queries:`, queries);

        // Make separate search requests for each query type
        const freshAlbumUrl = buildIndieHeadsSearchJsonUrl(queries.freshAlbumQuery);
        const albumDiscussionUrl = buildIndieHeadsSearchJsonUrl(queries.albumDiscussionQuery);

        console.log(`Fresh Album Search URL: ${freshAlbumUrl}`);
        console.log(`Album Discussion Search URL: ${albumDiscussionUrl}`);

        // Function to perform a search request
        const performSearch = (url) => {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    onload: function(response) {
                        try {
                            console.log(`[Search Request] Received response. Status: ${response.status}`);
                            if (response.status === 200) {
                                const searchData = JSON.parse(response.responseText);
                                console.log("[Search Request] Successfully parsed JSON response.");
                                if (searchData && searchData.data && searchData.data.children) {
                                    resolve(searchData.data.children);
                                } else {
                                    console.error("[Search Request] Unexpected Reddit search JSON structure:", searchData);
                                    resolve([]);
                                }
                            } else {
                                console.error("[Search Request] Error fetching Reddit search results:", response.status, response.statusText);
                                resolve([]);
                            }
                        } catch (e) {
                            console.error("[Search Request] Error parsing Reddit search JSON:", e);
                            resolve([]);
                        }
                    },
                    onerror: function(error) {
                        console.error("[Search Request] GM_xmlhttpRequest error fetching Reddit search results:", error);
                        resolve([]);
                    }
                });
            });
        };

        try {
            // Perform both searches in parallel
            const [freshAlbumResults, albumDiscussionResults] = await Promise.all([
                performSearch(freshAlbumUrl),
                performSearch(albumDiscussionUrl)
            ]);

            // Identify relevant threads from both result sets
            const relevantThreads = identifyRelevantThreads(
                freshAlbumResults,
                albumDiscussionResults,
                typeof artist === 'string' ? artist : artist.join(' & '), 
                album
            );

            if (relevantThreads.length === 0) {
                console.log("No relevant Reddit threads found.");
                const noThreadsMessage = document.createElement('p');
                noThreadsMessage.textContent = 'No relevant Reddit threads found for this review.';
                noThreadsMessage.style.fontStyle = 'italic';
                noThreadsMessage.style.marginTop = '20px'; // Add some spacing
                injectAfterLastParagraph(noThreadsMessage);
                return;
            }

            console.log(`Found ${relevantThreads.length} relevant thread(s):`, relevantThreads);

            // Inject the main comments section container
            injectAfterLastParagraph(REDDIT_COMMENTS_SECTION_HTML);

            const commentsSection = document.querySelector('.reddit-comments-section');
            const tabsArea = commentsSection.querySelector('.reddit-comments-tabs');
            const contentArea = commentsSection.querySelector('.reddit-comments-content');

            // Fetch comments and build tabs/content
            for (let i = 0; i < relevantThreads.length; i++) {
                const thread = relevantThreads[i];
                console.log(`Fetching comments for thread: ${thread.title} (${thread.url})`);
                const comments = await fetchRedditComments(thread.url);

                // Generate tab button
                const tabButton = document.createElement('button');
                tabButton.classList.add('reddit-tab-button');
                tabButton.textContent = thread.title + ' ';
                tabButton.setAttribute('data-thread-index', i);

                // Add a direct link icon that opens the Reddit thread in a new tab
                const linkIcon = document.createElement('a');
                linkIcon.href = thread.url;
                linkIcon.target = '_blank';
                linkIcon.rel = 'noopener noreferrer'; // Security best practice for target="_blank"
                linkIcon.innerHTML = '🔗';
                linkIcon.title = 'Open Reddit thread in new tab';
                linkIcon.style.fontSize = '0.8em';
                linkIcon.style.opacity = '0.7';
                linkIcon.style.textDecoration = 'none'; // Remove underline
                linkIcon.style.marginLeft = '5px';

                tabButton.appendChild(linkIcon);
                tabsArea.appendChild(tabButton);

                // Generate comment content area
                const threadContent = document.createElement('div');
                threadContent.classList.add('reddit-tab-content');
                threadContent.setAttribute('data-thread-index', i);
                threadContent.style.display = 'none'; // Hide by default

                if (comments) {
                    threadContent.innerHTML = renderCommentsHtml(comments);
                    // Set up collapse functionality for this tab's comments
                    setupCommentCollapse();
                } else {
                    threadContent.innerHTML = '<p>Could not load comments for this thread.</p>';
                }
                contentArea.appendChild(threadContent);

                // Activate the first tab and content by default
                if (i === 0) {
                    tabButton.classList.add('active');
                    threadContent.style.display = 'block';
                }
            }

            // Add event listeners for tab switching
            const tabButtons = tabsArea.querySelectorAll('.reddit-tab-button');
            const tabContents = contentArea.querySelectorAll('.reddit-tab-content');

            tabButtons.forEach(button => {
                button.addEventListener('click', () => {
                    const threadIndex = button.getAttribute('data-thread-index');

                    // Deactivate all tabs and hide all content
                    tabButtons.forEach(btn => btn.classList.remove('active'));
                    tabContents.forEach(content => content.style.display = 'none');

                    // Activate the clicked tab and show corresponding content
                    button.classList.add('active');
                    const activeContent = document.querySelector(`.reddit-tab-content[data-thread-index="${threadIndex}"]`);
                    activeContent.style.display = 'block';

                    // Re-initialize collapse functionality for the newly displayed tab
                    setupCommentCollapse();
                });
            });

        } catch (error) {
            console.error("Error during search process:", error);
        }
    }

    // Run the initialization function
    init();

})();