Pitchfork Reviews with r/indieheads Comments from Reddit

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

26.04.2025 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==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();

})();