Convert Comick Sidebar to Rows

Converts sidebar sections to rows.

// ==UserScript==
// @name         Convert Comick Sidebar to Rows
// @namespace    https://github.com/GooglyBlox
// @version      1.0
// @description  Converts sidebar sections to rows.
// @author       GooglyBlox
// @match        https://comick.io/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    function injectGlobalCss() {
        const styleId = 'comick-sidebar-fix';
        if (document.getElementById(styleId)) return;

        const css = `
            body > #__next > div > div.float-right {
                display: none !important;
            }
            main[id="main"].md\\:w-8\\/12 {
                width: 100% !important;
                max-width: none !important;
                float: none !important;
            }
            main[id="main"] > div {
                width: 100% !important;
                max-width: none !important;
            }
        `;
        const style = document.createElement('style');
        style.id = styleId;
        style.textContent = css;
        document.head.appendChild(style);
    }

    async function convertSidebarContent() {
        const mainElement = document.querySelector('main[id="main"]');
        const sidebarDiv = document.querySelector('div.float-right.w-4\\/12.xl\\:w-3\\/12');
        if (!mainElement || !sidebarDiv) return;

        const animeSection = findAnimeSectionContainer();
        if (!animeSection) return;

        const seeMoreButtons = Array.from(sidebarDiv.querySelectorAll('div')).filter(div => {
            const textElement = div.querySelector('.flex-grow.font-semibold');
            return textElement && textElement.textContent.trim() === 'See More' && !div.closest('a[href]');
        });

        seeMoreButtons.forEach(button => button.click());
        await new Promise(resolve => setTimeout(resolve, 500));

        let convertedSections = [];
        const sidebarSections = sidebarDiv.querySelectorAll('#ranking');
        for (let sectionIndex = 0; sectionIndex < sidebarSections.length; sectionIndex++) {
            const section = sidebarSections[sectionIndex];
            const header = section.querySelector('.flex.items-center.justify-between');
            const content = section.querySelector('.space-y-2.pb-5.mt-4');
            if (!header || !content) continue;

            const titleElements = header.querySelectorAll('h2');
            if (titleElements.length > 1) {
                const recentlyAddedItems = content.querySelectorAll('a[href^="/comic/"]:not(.hidden)');
                if (recentlyAddedItems.length > 0) {
                    convertedSections.push(createRowSection('Recently Added', recentlyAddedItems, convertedSections.length, false, '/search'));
                }

                const completeSeriesButton = Array.from(titleElements).find(el => el.textContent.trim() === 'Complete Series');
                if (completeSeriesButton) {
                    completeSeriesButton.click();
                    await new Promise(resolve => setTimeout(resolve, 300));
                    const completeSeriesItems = content.querySelectorAll('a[href^="/comic/"]:not(.hidden)');
                    if (completeSeriesItems.length > 0) {
                        convertedSections.push(createRowSection('Complete Series', completeSeriesItems, convertedSections.length, false, '/search?status=2'));
                    }
                    const recentlyAddedButton = Array.from(titleElements).find(el => el.textContent.trim() === 'Recently Added');
                    if (recentlyAddedButton) recentlyAddedButton.click();
                }
            }
        }

        if (convertedSections.length === 0) return;

        let insertAfter = animeSection;
        convertedSections.forEach(section => {
            insertAfter.insertAdjacentElement('afterend', section);
            insertAfter = section;
        });
        sidebarDiv.remove();
    }

    function findAnimeSectionContainer() {
        const animeHeaders = Array.from(document.querySelectorAll('h2')).filter(h2 => h2.textContent.trim() === 'Adapted to Anime');
        if (animeHeaders.length === 0) return null;
        let container = animeHeaders[0];
        while (container && container.parentElement) {
            if (container.classList.contains('mt-3') || container.parentElement === document.querySelector('main[id="main"]')) break;
            container = container.parentElement;
        }
        return container;
    }

    function createRowSection(title, items, sectionIndex, includeRankings = false, viewAllUrl = '/search') {
        const section = document.createElement('div');
        section.className = 'mt-8';
        section.setAttribute('data-section-title', title);
        const header = document.createElement('div');
        header.className = 'flex justify-between items-center flex-nowrap mb-4 md:mb-6';
        const headerLeft = document.createElement('div');
        headerLeft.className = 'flex items-center';
        const titleElement = document.createElement('h2');
        const gradients = [{ light: 'rgb(209, 138, 255)', dark: 'rgb(128, 45, 217)' }, { light: 'rgb(146, 195, 255)', dark: 'rgb(56, 125, 220)' }, { light: 'rgb(74, 227, 134)', dark: 'rgb(14, 157, 64)' }, { light: 'rgb(253, 186, 100)', dark: 'rgb(221, 116, 30)' }];
        const gradient = gradients[sectionIndex % gradients.length];
        titleElement.className = 'md:font-bold line-clamp-1';
        titleElement.textContent = title;
        titleElement.style.background = `linear-gradient(to right, ${gradient.light}, ${gradient.dark})`;
        titleElement.style.webkitBackgroundClip = 'text';
        titleElement.style.backgroundClip = 'text';
        titleElement.style.color = 'transparent';
        headerLeft.appendChild(titleElement);
        const headerRight = document.createElement('div');
        headerRight.className = 'flex items-center';
        const viewAllLink = document.createElement('a');
        viewAllLink.href = viewAllUrl;
        viewAllLink.className = 'hover:cursor-pointer rounded-full text-xs mr-2 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 hover:underline';
        viewAllLink.textContent = 'View all';
        headerRight.appendChild(viewAllLink);
        headerRight.appendChild(createMenuContainer(section, title));
        header.appendChild(headerLeft);
        header.appendChild(headerRight);
        section.appendChild(header);
        section.appendChild(createSectionContent(items, includeRankings));
        return section;
    }

    function createMenuContainer(section, title) {
        const menuContainer = document.createElement('div');
        menuContainer.className = 'hover:cursor-pointer rounded-full';
        const menuButton = document.createElement('div');
        menuButton.className = 'relative inline-block text-left top-0.5';
        const button = document.createElement('button');
        button.type = 'button';
        button.setAttribute('aria-haspopup', 'menu');
        button.setAttribute('aria-expanded', 'false');
        button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" class="w-5 h-5 md:w-6 md:h-6 lg:w-7 lg:h-7 text-gray-500 hover:dark:text-gray-400 hover:text-gray-600 z-10 stroke-2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"></path></svg>`;
        const menu = document.createElement('div');
        menu.className = 'absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white dark:bg-gray-750 dark:text-gray-200 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none shadow-gray-500 dark:shadow-gray-900 hidden';
        menu.innerHTML = `<div role="none"><span class="block px-4 py-2 text-sm cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600" role="menuitem">Minimize</span></div>`;
        let menuOpen = false;
        button.addEventListener('click', (e) => {
            e.stopPropagation();
            menuOpen = !menuOpen;
            menu.classList.toggle('hidden', !menuOpen);
            button.setAttribute('aria-expanded', menuOpen.toString());
        });
        document.addEventListener('click', () => {
            if (menuOpen) {
                menu.classList.add('hidden');
                button.setAttribute('aria-expanded', 'false');
                menuOpen = false;
            }
        });
        menu.querySelector('span[role="menuitem"]').addEventListener('click', () => {
            minimizeSection(section, title);
            menu.classList.add('hidden');
            button.setAttribute('aria-expanded', 'false');
            menuOpen = false;
        });
        menuButton.appendChild(button);
        menuButton.appendChild(menu);
        menuContainer.appendChild(menuButton);
        return menuContainer;
    }

    function createSectionContent(items, includeRankings) {
        const navigationWrapper = document.createElement('div');
        navigationWrapper.className = 'navigation-wrapper relative';
        const scrollContainer = document.createElement('div');
        scrollContainer.className = 'flex overflow-x-scroll hide-scroll-bar h-32 md:h-36 lg:h-48 xl:h-56';
        const innerContainer = document.createElement('div');
        innerContainer.style.width = `${Math.min(items.length * 184, 18400)}px`;
        innerContainer.style.height = '100%';
        innerContainer.style.position = 'relative';
        Array.from(items).slice(0, 100).forEach((item, itemIndex) => {
            innerContainer.appendChild(createHorizontalCard(item, itemIndex, includeRankings));
        });
        scrollContainer.appendChild(innerContainer);
        const leftArrow = document.createElement('div');
        leftArrow.className = 'absolute top-0 left-0 z-10 bg-opacity-80 h-full w-8 lg:w-10 xl:w-14 rounded arrow arrow--left select-none dark:hover:bg-gray-500/30 hover:bg-gray-100/30 bg-opacity-60 cursor-pointer hidden';
        leftArrow.innerHTML = `<div class="top-1/2 -translate-y-1/2 absolute left-1 dark:bg-gray-600 bg-white dark:hover:bg-gray-500 hover:bg-gray-100 rounded-full p-2 md:p-3 flex items-center opacity-80"><svg class=" w-3 h-3 lg:w-4 lg:h-4 fill-current text-gray-500 dark:text-gray-200 bg-opacity-60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z"></path></svg></div>`;
        const rightArrow = document.createElement('div');
        rightArrow.className = 'absolute top-0 right-0 z-10 bg-opacity-80 h-full w-8 lg:w-10 xl:w-14 rounded arrow select-none arrow--right dark:hover:bg-gray-500/30 hover:bg-gray-100/30 bg-opacity-60 cursor-pointer';
        rightArrow.innerHTML = `<div class="top-1/2 -translate-y-1/2 absolute right-1 dark:bg-gray-600 bg-white dark:hover:bg-gray-500 hover:bg-gray-100 rounded-full flex items-center opacity-80 p-2 md:p-3"><svg class="w-3 h-3 lg:w-4 lg:h-4 fill-current text-gray-500 dark:text-gray-200 hover:brightness-125 bg-opacity-60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M5 3l3.057-3 11.943 12-11.943 12-3.057-3 9-9z"></path></svg></div>`;
        leftArrow.addEventListener('click', () => scrollContainer.scrollBy({ left: -920, behavior: 'smooth' }));
        rightArrow.addEventListener('click', () => scrollContainer.scrollBy({ left: 920, behavior: 'smooth' }));
        scrollContainer.addEventListener('scroll', () => {
            const { scrollLeft, scrollWidth, clientWidth } = scrollContainer;
            leftArrow.classList.toggle('hidden', scrollLeft <= 0);
            const atEnd = scrollLeft + clientWidth >= scrollWidth - 10;
            rightArrow.style.opacity = atEnd ? '0.5' : '1';
            rightArrow.style.pointerEvents = atEnd ? 'none' : 'auto';
        });
        navigationWrapper.appendChild(scrollContainer);
        navigationWrapper.appendChild(leftArrow);
        navigationWrapper.appendChild(rightArrow);
        return navigationWrapper;
    }

    function minimizeSection(section, title) {
        const content = section.querySelector('.navigation-wrapper');
        const header = section.querySelector('.flex.justify-between');
        if (!content || !header) return;
        content.style.display = 'none';
        header.style.display = 'none';
        const minimizedView = document.createElement('div');
        minimizedView.className = 'relative mb-4';
        minimizedView.innerHTML = `<div class="absolute inset-0 flex items-center" aria-hidden="true"><div class="w-full border-t border-gray-300 dark:border-gray-600"></div></div><div class="relative flex justify-center"><button type="button" class="inline-flex items-center gap-x-1.5 rounded-full bg-white px-3 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:text-gray-300 dark:ring-gray-600 dark:bg-gray-700"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="-ml-1 -mr-0.5 h-5 w-5 text-gray-400"><path d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"></path></svg>${title}</button></div>`;
        minimizedView.querySelector('button').addEventListener('click', () => {
            content.style.display = '';
            header.style.display = '';
            minimizedView.remove();
        });
        section.appendChild(minimizedView);
    }

    function createHorizontalCard(originalItem, index, includeRankings = false) {
        const card = document.createElement('div');
        card.className = 'w-24 h-32 md:w-28 md:h-36 lg:w-36 lg:h-48 xl:w-44 xl:h-56 overflow-hidden';
        card.style.cssText = `position: absolute; top: 0px; left: 0px; height: 100%; width: 184px; transform: translateX(${index * 184}px);`;
        const link = document.createElement('a');
        link.href = originalItem.href;
        link.title = originalItem.title || '';
        link.style.overflow = 'visible';
        const cardInner = document.createElement('div');
        cardInner.className = 'relative w-24 h-32 md:w-28 md:h-36 lg:w-36 lg:h-48 xl:w-44 xl:h-56 overflow-hidden';
        const originalImg = originalItem.querySelector('img');
        if (originalImg) {
            const img = originalImg.cloneNode(true);
            img.className = 'select-none rounded object-cover object-top w-24 md:w-28 lg:w-36 xl:w-44 h-32 md:h-36 lg:h-48 xl:h-56 active:brightness-75';
            cardInner.appendChild(img);
        }
        const titleText = originalItem.querySelector('.text-ellipsis')?.textContent?.trim() || link.title;
        if (titleText) {
            const overlay = document.createElement('div');
            overlay.className = 'h-12 bottom-0 absolute overlay font-semibold text-gray-200 w-full text-xs md:text-xs lg:text-sm px-1 md:px-2 pt-5 text-center truncate';
            overlay.textContent = titleText;
            cardInner.appendChild(overlay);
        }
        if (includeRankings) {
            const ranking = document.createElement('div');
            const rankingColors = ['bg-red-500/90', 'bg-orange-500/90', 'bg-yellow-500/90'];
            ranking.className = `absolute top-0 left-0 flex items-center justify-center text-xl md:text-2xl font-semibold z-10 text-white w-6 h-6 md:w-8 md:h-8 rounded-tl rounded-br ${index < 3 ? rankingColors[index] : 'bg-gray-500/90'}`;
            ranking.textContent = (index + 1).toString();
            cardInner.appendChild(ranking);
        }
        link.appendChild(cardInner);
        card.appendChild(link);
        return card;
    }

    let isRunning = false;
    async function runConversion() {
        if (isRunning || document.querySelector('[data-section-title]')) {
            return;
        }
        isRunning = true;

        try {
            const sidebar = document.querySelector('div.float-right.w-4\\/12.xl\\:w-3\\/12');
            if (sidebar) {
                await convertSidebarContent();
            }
        } finally {
            isRunning = false;
        }
    }

    const handlePageChange = () => {
        injectGlobalCss();
        runConversion();
    };

    const observer = new MutationObserver(handlePageChange);

    handlePageChange();

    observer.observe(document.body, { childList: true, subtree: true });

})();