Comix Fit Width

Fix fit width for comix

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Comix Fit Width
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Fix fit width for comix
// @author       NakunaruZeno
// @license      MIT
// @match        https://comix.to/title/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let isFitWidth = localStorage.getItem('manga_fit_width') !== 'false';
    let fitScale = parseInt(localStorage.getItem('manga_fit_scale'));
    if (isNaN(fitScale) || fitScale < 30) fitScale = 100;

    const styleEl = document.createElement('style');
    styleEl.innerHTML = `

        body.force-fit #app-root .rpage.rpage--pb-left { padding-left: 0 !important; }


        body.force-fit #app-root .rpage {
            --rpage-max-w: var(--manga-w) !important;
            max-width: var(--manga-w) !important;
            width: 100% !important;
            margin: 0 auto !important;
            display: flex !important;
            flex-direction: column !important;
            align-items: center !important;
        }

        body.force-fit #app-root .rpage-main,
        body.force-fit #app-root .rpage-swiper,
        body.force-fit #app-root .swiper-wrapper {
            width: 100% !important;
            max-width: 100% !important;
        }

        body.force-fit #app-root .swiper-slide {
            overflow-y: auto !important;
            overflow-x: hidden !important;
            height: 100vh !important;
            height: 100dvh !important;
            width: 100% !important;
            display: flex !important;
            justify-content: center !important;
            align-items: flex-start !important;
        }

        body.force-fit #app-root .rpage-view,
        body.force-fit #app-root .swiper-zoom-container,
        body.force-fit #app-root .rpage-zoom-container,
        body.force-fit #app-root .rpage-page {
            width: 100% !important;
            max-width: 100% !important;
            height: auto !important;
            min-height: unset !important;
            margin: 0 auto !important;
            align-items: flex-start !important;
            flex-shrink: 0 !important;
        }

        body.force-fit #app-root .rpage-page__img,
        body.force-fit #app-root canvas.rpage-page__img {
            width: 100% !important;
            max-width: 100% !important;
            height: auto !important;
            display: block !important;
            object-fit: contain !important;
            margin: 0 auto !important;
        }

        body.force-fit #app-root .swiper-slide::-webkit-scrollbar { width: 5px; }
        body.force-fit #app-root .swiper-slide::-webkit-scrollbar-thumb { background: #2ecc71; border-radius: 10px; }
    `;
    document.head.appendChild(styleEl);
    const panel = document.createElement('div');
    Object.assign(panel.style, {
        position: 'fixed', bottom: '80px', right: '20px', zIndex: '99999',
        backgroundColor: '#1e272e', padding: '10px', borderRadius: '8px',
        boxShadow: '0 4px 15px rgba(0,0,0,0.5)', display: 'flex', flexDirection: 'column',
        gap: '8px', alignItems: 'center', fontFamily: 'monospace', color: '#fff', userSelect: 'none'
    });

    const toggleBtn = document.createElement('button');
    Object.assign(toggleBtn.style, {
        width: '100%', padding: '6px 12px', fontSize: '11px', fontWeight: 'bold',
        border: 'none', borderRadius: '4px', cursor: 'pointer', transition: 'background 0.2s'
    });

    const sliderContainer = document.createElement('div');
    sliderContainer.style.display = 'flex';
    sliderContainer.style.alignItems = 'center';
    sliderContainer.style.gap = '6px';

    const label = document.createElement('span');
    Object.assign(label.style, { fontSize: '11px', minWidth: '35px', textAlign: 'right' });

    const slider = document.createElement('input');
    slider.type = 'range'; slider.min = '30'; slider.max = '100'; slider.value = fitScale;
    Object.assign(slider.style, { width: '80px', cursor: 'pointer', accentColor: '#2ecc71' });

    function createBtn(text) {
        const b = document.createElement('button');
        b.textContent = text;
        Object.assign(b.style, { background: '#3d3d3d', color: '#fff', border: 'none', borderRadius: '3px', width: '20px', height: '20px', cursor: 'pointer', fontSize: '12px', lineHeight: '1' });
        return b;
    }
    const btnMinus = createBtn('-');
    const btnPlus = createBtn('+');

    sliderContainer.append(btnMinus, slider, btnPlus, label);
    panel.append(toggleBtn, sliderContainer);
    document.body.appendChild(panel);

    function updatePanelUI() {
        toggleBtn.textContent = isFitWidth ? 'Width: FIT' : 'Width: DEF';
        toggleBtn.style.backgroundColor = isFitWidth ? '#2ecc71' : '#7f8c8d';
        sliderContainer.style.visibility = isFitWidth ? 'visible' : 'hidden';
        sliderContainer.style.height = isFitWidth ? 'auto' : '0px';
        label.textContent = fitScale + '%';
    }

    function unlockScroll(e) {
        if (isFitWidth) e.stopPropagation();
    }

    function applySettings() {
        const screenWidth = document.documentElement.clientWidth || window.innerWidth;
        const targetPx = Math.floor(screenWidth * (fitScale / 100));

        document.documentElement.style.setProperty('--manga-w', targetPx + 'px');

        if (isFitWidth) {
            document.body.classList.add('force-fit');

            window.addEventListener('wheel', unlockScroll, {capture: true, passive: true});
            window.addEventListener('touchmove', unlockScroll, {capture: true, passive: true});

            const nativeRange = document.querySelector('.rpage-zoom__range');
            if (nativeRange) {
                nativeRange.setAttribute('max', '9999');
                nativeRange.value = targetPx;
            }
        } else {
            document.body.classList.remove('force-fit');
            window.removeEventListener('wheel', unlockScroll, {capture: true});
            window.removeEventListener('touchmove', unlockScroll, {capture: true});
        }
        setTimeout(() => window.dispatchEvent(new Event('resize')), 50);
    }
    let currentActiveSlide = null;
    const activeSlideObserver = new MutationObserver((mutations) => {
        if (!isFitWidth) return;
        for (let mutation of mutations) {
            if (mutation.attributeName === 'class' && mutation.target.classList) {
                if (mutation.target.classList.contains('swiper-slide-active')) {
                    if (currentActiveSlide !== mutation.target) {
                        currentActiveSlide = mutation.target;
                        currentActiveSlide.scrollTop = 0;
                    }
                }
            }
        }
    });
    activeSlideObserver.observe(document.body, {
        attributes: true,
        attributeFilter: ['class'],
        subtree: true
    });
    toggleBtn.addEventListener('click', () => {
        isFitWidth = !isFitWidth;
        localStorage.setItem('manga_fit_width', isFitWidth);
        updatePanelUI();
        applySettings();
    });

    slider.addEventListener('input', (e) => {
        fitScale = parseInt(e.target.value);
        label.textContent = fitScale + '%';
        applySettings();
    });

    slider.addEventListener('change', () => {
        localStorage.setItem('manga_fit_scale', fitScale);
    });

    btnMinus.addEventListener('click', () => {
        fitScale = Math.max(30, fitScale - 5);
        slider.value = fitScale;
        localStorage.setItem('manga_fit_scale', fitScale);
        label.textContent = fitScale + '%';
        applySettings();
    });

    btnPlus.addEventListener('click', () => {
        fitScale = Math.min(100, fitScale + 5);
        slider.value = fitScale;
        localStorage.setItem('manga_fit_scale', fitScale);
        label.textContent = fitScale + '%';
        applySettings();
    });

    const observer = new MutationObserver(() => applySettings());
    observer.observe(document.body, { childList: true, subtree: true });

    updatePanelUI();
    applySettings();
})();