Web-Font-Rendering-Core

全端统一交互:强制单行横向滚动预览,PC/移动端体验一致。支持全量画廊与静默坏图过滤。

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Web-Font-Rendering-Core
// @namespace    http://tampermonkey.net/
// @version      2.6.0
// @description  全端统一交互:强制单行横向滚动预览,PC/移动端体验一致。支持全量画廊与静默坏图过滤。
// @author       Gemini-Adaptive
// @license      MIT
// @match        *://t66y.com/*
// @match        *://www.t66y.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

;(() => {
    const countRegex = /\d+[pP]/;

    const style = document.createElement('style');
    style.textContent = `
        /* --- 1. 统一列表预览容器 --- */
        .sys-list-container {
            display: flex;
            gap: 12px;
            margin: 15px 0;
            padding: 5px 2px 15px 2px; /* 底部预留空间给滚动条 */
            clear: both;
            flex-wrap: nowrap;
            overflow-x: auto;
            overflow-y: hidden;
            -webkit-overflow-scrolling: touch;
            /* 改善 PC 端滚动条样式,不隐藏它以便于鼠标拖动 */
            scrollbar-width: thin;
            scrollbar-color: #555 #222;
        }

        /* Webkit 滚动条美化 (PC端) */
        .sys-list-container::-webkit-scrollbar {
            height: 6px;
        }
        .sys-list-container::-webkit-scrollbar-track {
            background: #1a1a1a;
            border-radius: 10px;
        }
        .sys-list-container::-webkit-scrollbar-thumb {
            background: #444;
            border-radius: 10px;
        }
        .sys-list-container::-webkit-scrollbar-thumb:hover {
            background: #666;
        }

        /* --- 2. 统一缩略图尺寸 --- */
        .sys-list-thumb {
            height: 280px; /* 统一高度,兼顾视觉冲击力与排版 */
            width: auto;
            flex: 0 0 auto;
            border-radius: 8px;
            object-fit: cover;
            background: #222;
            cursor: zoom-in;
            box-shadow: 0 4px 12px rgba(0,0,0,0.4);
            transition: transform 0.2s;
        }
        .sys-list-thumb:hover {
            transform: translateY(-2px);
        }

        /* --- 3. 查看器与画廊 (保持高质交互) --- */
        .sys-viewer-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0, 0, 0, 0.98); display: flex; flex-direction: column;
            align-items: center; justify-content: center; z-index: 999999;
            opacity: 0; pointer-events: none; transition: opacity 0.3s ease;
            outline: none; touch-action: none;
        }
        .sys-viewer-overlay.active { opacity: 1; pointer-events: auto; }
        .sys-viewer-stage { flex: 1; width: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; }
        .sys-viewer-node { max-width: 98%; max-height: 98%; object-fit: contain; transition: transform 0.3s cubic-bezier(0.2, 0, 0.2, 1), opacity 0.3s; opacity: 0; will-change: transform; cursor: zoom-out; }
        .sys-viewer-overlay.active .sys-viewer-node.loaded { opacity: 1; }

        .sys-viewer-error { display: none; color: #888; text-align: center; }
        .sys-viewer-overlay.img-error .sys-viewer-error { display: block; }
        .sys-viewer-overlay.img-error .sys-viewer-node { display: none; }

        .sys-viewer-gallery {
            width: 100%; height: 110px; background: rgba(0,0,0,0.8);
            display: flex; gap: 8px; padding: 10px; overflow-x: auto;
            border-top: 1px solid rgba(255,255,255,0.1); backdrop-filter: blur(15px);
            scrollbar-width: none;
        }
        .sys-viewer-gallery::-webkit-scrollbar { display: none; }
        .sys-gallery-item { height: 90px; width: auto; flex: 0 0 auto; border-radius: 4px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; opacity: 0.5; object-fit: cover; }
        .sys-gallery-item.current { border-color: #fff; opacity: 1; transform: scale(1.05); }

        .sys-loader { position: absolute; width: 40px; height: 40px; border: 3px solid rgba(255,255,255,0.1); border-radius: 50%; border-top-color: #fff; animation: sys-spin 0.8s linear infinite; display: none; }
        .sys-viewer-overlay.loading .sys-loader { display: block; }
        @keyframes sys-spin { to { transform: rotate(360deg); } }
    `;
    document.head.appendChild(style);

    // --- 结构初始化与逻辑 (逻辑部分保持健壮) ---
    const overlay = document.createElement('div');
    overlay.className = 'sys-viewer-overlay';
    overlay.tabIndex = 0;
    const stage = document.createElement('div');
    stage.className = 'sys-viewer-stage';
    const gallery = document.createElement('div');
    gallery.className = 'sys-viewer-gallery';
    const loader = document.createElement('div');
    loader.className = 'sys-loader';
    const errorTip = document.createElement('div');
    errorTip.className = 'sys-viewer-error';
    errorTip.innerHTML = '<div style="font-size:30px;">⚠️</div>加载失败';
    const bigImg = document.createElement('img');
    bigImg.className = 'sys-viewer-node';

    stage.append(loader, errorTip, bigImg);
    overlay.append(stage, gallery);
    document.body.appendChild(overlay);

    let currentSrcList = [];
    let currentImgIndex = -1;
    let startX = 0, deltaX = 0, isDragging = false;

    const setupImageErrorFilter = (imgNode) => {
        imgNode.onerror = () => { imgNode.style.display = 'none'; };
    };

    const updateViewer = (index) => {
        if (index < 0 || index >= currentSrcList.length) {
            bigImg.style.transform = `translateX(0px)`;
            return;
        }
        currentImgIndex = index;
        overlay.classList.add('loading');
        overlay.classList.remove('img-error');
        bigImg.classList.remove('loaded');
        bigImg.style.transition = 'none';
        bigImg.style.transform = `translateX(0px) scale(0.98)`;
        bigImg.src = currentSrcList[currentImgIndex];

        Array.from(gallery.children).forEach((item, idx) => {
            const isCurrent = idx === currentImgIndex;
            item.classList.toggle('current', isCurrent);
            if (isCurrent) item.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
        });
    };

    const releaseView = () => {
        overlay.classList.remove('active', 'loading', 'img-error');
        document.body.style.overflow = '';
        setTimeout(() => { bigImg.src = ''; gallery.innerHTML = ''; }, 300);
    };

    // 事件绑定
    bigImg.onclick = (e) => { e.stopPropagation(); releaseView(); };
    overlay.onclick = (e) => { if(e.target === stage || e.target === overlay) releaseView(); };
    overlay.onkeydown = (e) => {
        if (e.key === 'Escape') releaseView();
        if (e.key === 'ArrowLeft') updateViewer(currentImgIndex - 1);
        if (e.key === 'ArrowRight') updateViewer(currentImgIndex + 1);
    };

    // 触摸滑动逻辑
    stage.addEventListener('touchstart', (e) => {
        if (e.touches.length > 1) return;
        startX = e.touches[0].clientX;
        deltaX = 0; isDragging = true;
        bigImg.style.transition = 'none';
    }, { passive: false });

    stage.addEventListener('touchmove', (e) => {
        if (!isDragging) return;
        deltaX = e.touches[0].clientX - startX;
        if (Math.abs(deltaX) > 5) {
            e.preventDefault();
            bigImg.style.transform = `translateX(${deltaX}px)`;
        }
    }, { passive: false });

    stage.addEventListener('touchend', () => {
        if (!isDragging) return;
        isDragging = false;
        bigImg.style.transition = 'transform 0.3s cubic-bezier(0.2, 0, 0.2, 1)';
        if (Math.abs(deltaX) > window.innerWidth * 0.15) {
            deltaX > 0 ? updateViewer(currentImgIndex - 1) : updateViewer(currentImgIndex + 1);
        } else {
            bigImg.style.transform = `translateX(0px)`;
        }
    }, { passive: false });

    bigImg.onload = () => { overlay.classList.remove('loading', 'img-error'); bigImg.classList.add('loaded'); bigImg.style.transform = `translateX(0px) scale(1)`; };
    bigImg.onerror = () => { overlay.classList.remove('loading'); overlay.classList.add('img-error'); };

    const openViewer = async (targetSrc, containerSelector, imgSelector) => {
        const container = document.querySelector(containerSelector);
        let imgs = container ? Array.from(container.querySelectorAll(imgSelector)) : [];
        currentSrcList = imgs.map(img => img.getAttribute('ess-data') || img.src).filter(s => s && !s.includes('adblo_ck'));

        if (currentSrcList.length < 15 && container && container.dataset.fullurl) {
             overlay.classList.add('loading');
             try {
                const res = await fetch(container.dataset.fullurl);
                const text = await res.text();
                const doc = new DOMParser().parseFromString(text, 'text/html');
                const area = doc.querySelector('#conttpc');
                if (area) {
                    const fullImgs = Array.from(area.querySelectorAll('img[ess-data]')).map(el => {
                         let s = el.getAttribute("ess-data");
                         return s.startsWith('//') ? 'https:' + s : s;
                    }).filter(s => s && !s.includes('adblo_ck'));
                    if (fullImgs.length > currentSrcList.length) currentSrcList = fullImgs;
                }
             } catch (e) {}
             overlay.classList.remove('loading');
        }

        currentImgIndex = currentSrcList.indexOf(targetSrc);
        if (currentImgIndex === -1) currentImgIndex = 0;

        if (currentSrcList.length > 0) {
            gallery.innerHTML = '';
            currentSrcList.forEach((src, idx) => {
                const thumb = document.createElement('img');
                thumb.className = 'sys-gallery-item';
                thumb.src = src;
                thumb.onclick = (e) => { e.stopPropagation(); updateViewer(idx); };
                setupImageErrorFilter(thumb);
                gallery.appendChild(thumb);
            });
            document.body.style.overflow = 'hidden';
            overlay.classList.add('active');
            overlay.focus();
            updateViewer(currentImgIndex);
        }
    };

    async function fetchPreview(item, url, title) {
        if (!countRegex.test(title) || item.dataset.proc === "1") return;
        item.dataset.proc = "1";
        try {
            const res = await fetch(url);
            const text = await res.text();
            const doc = new DOMParser().parseFromString(text, 'text/html');
            const area = doc.querySelector('#conttpc');
            if (!area) return;
            const allImgs = Array.from(area.querySelectorAll('img[ess-data]'));
            if (!allImgs.length) return;

            const box = document.createElement("div");
            box.className = "sys-list-container";
            box.dataset.fullurl = url;

            allImgs.slice(0, 12).forEach(el => {
                let s = el.getAttribute("ess-data");
                if (s.startsWith('//')) s = 'https:' + s;
                const t = document.createElement("img");
                t.src = s;
                t.className = "sys-list-thumb";
                t.loading = "lazy";
                t.onclick = (e) => {
                    e.preventDefault(); e.stopPropagation();
                    openViewer(s, `.sys-list-container[data-fullurl="${url}"]`, '.sys-list-thumb');
                };
                setupImageErrorFilter(t);
                box.appendChild(t);
            });
            item.querySelector("h3").after(box);
        } catch (e) {}
    }

    // --- 初始化 ---
    const loc = window.location.href;
    if (loc.includes('/htm_data/')) {
        setTimeout(() => {
            const root = document.querySelector('#conttpc, .tpc_content');
            if (!root) return;
            root.querySelectorAll('img').forEach(img => {
                const src = img.getAttribute('ess-data') || img.src;
                if (!src || src.includes('adblo_ck') || src.includes('common/')) return;
                img.classList.add('sys-detail-node');
                img.onclick = (e) => { e.preventDefault(); openViewer(src, '#conttpc, .tpc_content', 'img.sys-detail-node'); };
            });
        }, 500);
    } else {
        const obs = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const a = entry.target.querySelector("h3 > a");
                    if (a) fetchPreview(entry.target, a.href, a.innerText);
                    obs.unobserve(entry.target);
                }
            });
        }, { rootMargin: '800px' });
        document.querySelectorAll("tr.tac, tr.tal").forEach(r => { if (r.querySelector("h3 > a")) obs.observe(r); });
    }
})();