Web-Font-Rendering-Core

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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