kone 썸네일

마우스 오버시 썸네일 표시, 좌우 방향키로 넘기기 가능

目前為 2025-05-18 提交的版本,檢視 最新版本

// ==UserScript==
// @name         kone 썸네일
// @namespace    http://tampermonkey.net/
// @version      2.1
// @author       김머시기
// @description  마우스 오버시 썸네일 표시, 좌우 방향키로 넘기기 가능
// @match        https://kone.gg/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @license      MIT
// @run-at       document-idle
// ==/UserScript==

(async function () {
  'use strict';

  let hoverDelay = await GM_getValue('hoverDelay', 300);
  let thumbSize = await GM_getValue('thumbSize', 400);
  let autoSlide = await GM_getValue('autoSlide', false);
  const MenuID = [null, null, null];

  async function toggleAutoSlide() {
    const states = [false, 1500, 2500, 3500];
    let idx = states.indexOf(autoSlide);
    autoSlide = states[(idx + 1) % states.length];
    await GM_setValue('autoSlide', autoSlide);
    updateMenu();
  }

  async function toggleThumbSize() {
    const sizes = [200, 320, 400, 480];
    let idx = sizes.indexOf(thumbSize);
    thumbSize = sizes[(idx + 1) % sizes.length];
    await GM_setValue('thumbSize', thumbSize);
    updateMenu();
  }

  async function toggleHoverDelay() {
    const delays = [300, 500, 700, 1000];
    let idx = delays.indexOf(hoverDelay);
    hoverDelay = delays[(idx + 1) % delays.length];
    await GM_setValue('hoverDelay', hoverDelay);
    updateMenu();
  }

  function updateMenu() {
    if (MenuID[0]) GM_unregisterMenuCommand(MenuID[0]);
    MenuID[0] = GM_registerMenuCommand(
      `자동 슬라이드 : ${autoSlide === false ? '꺼짐' : `${(autoSlide / 1000).toFixed(1)}초`}`,
      toggleAutoSlide,
      { autoClose: false, title: `자동 슬라이드 ${autoSlide === false ? '꺼짐' : `${(autoSlide / 1000).toFixed(1)}초`}` }
    );

    if (MenuID[1]) GM_unregisterMenuCommand(MenuID[1]);
    MenuID[1] = GM_registerMenuCommand(
      `썸네일 크기  : ${thumbSize}px`,
      toggleThumbSize,
      { autoClose: false, title: `썸네일 크기 ${thumbSize}px` }
    );

    if (MenuID[2]) GM_unregisterMenuCommand(MenuID[2]);
    MenuID[2] = GM_registerMenuCommand(
      `마우스 오버  : ${hoverDelay}ms`,
      toggleHoverDelay,
      { autoClose: false, title: `마우스 오버 ${hoverDelay}ms` }
    );
  }

  updateMenu();

  let previewBox = document.createElement('div');
  let previewImage = document.createElement('img');
  let iframe = document.createElement('iframe');
  let currentIndex = 0;
  let imageList = [];
  let isPreviewVisible = false;
  let currentHoverTarget = null;
  let hoverTimer = null;
  let autoSlideTimer = null;

  Object.assign(previewBox.style, {
    position: 'fixed',
    pointerEvents: 'none',
    zIndex: 9999,
    display: 'none',
    border: '1px solid #ccc',
    background: '#fff',
    padding: '4px',
    boxShadow: '0 0 8px rgba(0,0,0,0.3)',
    borderRadius: '6px'
  });

  Object.assign(previewImage.style, {
    width: '100%',
    height: 'auto',
    objectFit: 'contain',
    display: 'block'
  });

  previewBox.appendChild(previewImage);
  document.body.appendChild(previewBox);

  Object.assign(iframe.style, {
    position: 'fixed',
    left: '-9999px',
    width: '1px',
    height: '1px',
    visibility: 'hidden'
  });

  document.body.appendChild(iframe);

  function applySize() {
    previewBox.style.maxWidth = thumbSize + 'px';
    previewBox.style.maxHeight = thumbSize + 'px';
    previewImage.style.maxWidth = thumbSize + 'px';
    previewImage.style.maxHeight = thumbSize + 'px';
  }

  function updateImage() {
    if (imageList.length > 0) {
      previewImage.src = imageList[currentIndex];
      previewBox.style.display = 'block';
    } else {
      previewBox.style.display = 'none';
    }
  }

  function startAutoSlide() {
    if (autoSlideTimer) clearInterval(autoSlideTimer);
    if (typeof autoSlide === 'number') {
      autoSlideTimer = setInterval(() => {
        currentIndex = (currentIndex + 1) % imageList.length;
        updateImage();
      }, autoSlide);
    }
  }

  function stopAutoSlide() {
    if (autoSlideTimer) clearInterval(autoSlideTimer);
    autoSlideTimer = null;
  }

  function onKeyDown(e) {
    if (!isPreviewVisible) return;
    if (e.key === 'ArrowRight') {
      currentIndex = (currentIndex + 1) % imageList.length;
      updateImage();
    } else if (e.key === 'ArrowLeft') {
      currentIndex = (currentIndex - 1 + imageList.length) % imageList.length;
      updateImage();
    }
  }

  function extractImagesFromIframeDocument(doc) {
    const content = doc.querySelector('.prose');
    if (!content) return [];
    return [...content.querySelectorAll('img')]
      .map(img => img.src)
      .filter(src => src && !/kone-logo|default|placeholder|data:image/.test(src));
  }

  function showPreviewAtMouse(event, url) {
    previewBox.style.top = (event.clientY + 20) + 'px';
    previewBox.style.left = (event.clientX + 20) + 'px';

    iframe.onload = () => {
      try {
        const doc = iframe.contentDocument || iframe.contentWindow.document;
        imageList = extractImagesFromIframeDocument(doc);
        currentIndex = 0;
        applySize();
        updateImage();
        isPreviewVisible = true;
        startAutoSlide();
      } catch (e) {
        console.error('iframe access error', e);
      }
    };

    iframe.src = url;

    const moveHandler = e => {
      previewBox.style.top = (e.clientY + 20) + 'px';
      previewBox.style.left = (e.clientX + 20) + 'px';
    };

    document.addEventListener('mousemove', moveHandler);
    document.addEventListener('keydown', onKeyDown);

    event.target.addEventListener('mouseleave', () => {
      previewBox.style.display = 'none';
      imageList = [];
      isPreviewVisible = false;
      clearTimeout(hoverTimer);
      stopAutoSlide();
      currentHoverTarget = null;
      iframe.src = 'about:blank';
      document.removeEventListener('mousemove', moveHandler);
      document.removeEventListener('keydown', onKeyDown);
    }, { once: true });
  }

  function handleMouseEnter(event, element, href) {
    clearTimeout(hoverTimer);
    currentHoverTarget = element;
    hoverTimer = setTimeout(() => {
      if (currentHoverTarget === element) {
        const fullUrl = href.startsWith('http') ? href : location.origin + href;
        showPreviewAtMouse(event, fullUrl);
      }
    }, hoverDelay);
  }

  function attachEvents() {
    const anchors = document.querySelectorAll('a[href^="/s/"]:not([data-preview-init])');
    anchors.forEach(link => {
      link.dataset.previewInit = '1';
      link.addEventListener('mouseenter', e => handleMouseEnter(e, link, link.getAttribute('href')));
      link.addEventListener('mouseleave', () => {
        clearTimeout(hoverTimer);
        currentHoverTarget = null;
      });
    });

    const groupLinks = document.querySelectorAll('a.group[href*="/s/"]:not([data-preview-init])');
    groupLinks.forEach(link => {
      link.dataset.previewInit = '1';
      link.addEventListener('mouseenter', e => handleMouseEnter(e, link, link.getAttribute('href')));
      link.addEventListener('mouseleave', () => {
        clearTimeout(hoverTimer);
        currentHoverTarget = null;
      });
    });
  }

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址