Fix fit width for comix
// ==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();
})();