Yamibo 漫画阅读器

漫区卡片式预览封面,一键进入漫画阅读器,支持自动生成系列目录,智能匹配章节标题。

安裝腳本?
作者推薦腳本

您可能也會喜歡 Yamibo漫区封面卡片

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Yamibo 漫画阅读器
// @namespace    https://bbs.yamibo.com/
// @version      3.5.6
// @author       hitori酱
// @description  漫区卡片式预览封面,一键进入漫画阅读器,支持自动生成系列目录,智能匹配章节标题。
// @match        https://bbs.yamibo.com/thread-*
// @match        https://bbs.yamibo.com/forum.php?mod=viewthread&*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/cn2t.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/t2cn.js
// @run-at       document-end
// @noframes
// @license      MIT License
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB2AAAAdgB+lymcgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAr3SURBVHic7Zt7cNTVFcc/v32H7Ca72c2LhEcS5JFIgUB5JSioQCvKWOVRqSMjxAeoVaYKdapFaccq0OfUOlrKjLUUR1HGUstDMSIJRAyEAAnQvNk8NgnJPvLYTbK7v/6xyYbFZMluNgaK35nfzO7v3HvOud/fueeee3+70D9UwO8AEyDeoJcJ+C2g7G+Qgh8CtgM/Cw+TExsdToXRglSQMDpS56fL9QVTa4vY3tUpANuAjX218UdATZhKFl+R84QQawhHMW4bMWEaqjf8akicHQqYWm0k/+kV7M6uWiChrzYSP/1jDboRQqwhfGi8+xYQp47AMEINENtfG38E3BT4joDhdmC48R0Bw+3AcOM7AobbgeHGTU+ALFSKutwudpw6zq6z+Zy/bEIqSEiNjuPhKTNZPWUmUsHDtUt0c6SylANl5/m69hIV5ibMjnYANAol4/UxPHjrdB5Nn+uj3yW6eafwBH8vPEFxowmX6GaSIY6fTJ5BVvoc5BJpUH6HhIC6Vhv37n6Lk3VGj1KpR+2RqlKOVJWysyCP3Q+s5r1zp/h9XjZ1rTaf/nKZHIlEQmtbCzUtVkrarNw2dxa3dEhxuVw029u5//0dHDNW+OjPNZaTayznbwXH2ffg48SrIwL2fdAEdLld3sEbIvWkJqehVUcCYLaZOVdeRK6xnLF/fBm3KCIIAjG6GGKjYoiLjic+Jg6pzPP03KKb1rZWVEoVr+cXoteqOfT5J5ytrERERKfRcWtKGjqNZ0NmabVSVF7EyTojS997m2NrNgQcCYPOATsL8jhZZ0QfqWf25NnewQPoInRERUZ1D04kITqB+em3M/vWWSQlJDEytnfwABJBQoQ6AoVcAUCTpRWUGkREr76ewQNo1ZHMmTwbfaSe/NpL7CzIC9j/QROw62w+AGnJqUgEX3UF/z1NWXUZcpmc70+awfSJ6WhGaABQKBRIpNd+WtPT0lmUsRCVUkV5TTkFFwt8ByBISEtO9fElEAyagPOXTcikMrRqre/9ygsY642oFCpumzqPeEO8j1yp6veM4htIiE3gnvlLUI9QY2yo5nzlBR+5Vq1FJpVR1FgXsP+DJkAplSGKbm+YAjSYGygxlqCQK5gzeQ7hYb5bakEQkMvlAdnRhGtYnLkIlVJFibGEBnODVyYiIooiKllgOiEEBEyKjsPldtNsNQPgcrkoLDkDwJRx30Pj2Y9TWFLIsbPHAZDKpAhC71nMl19/SfZX2X3qP3z8MLmncgGIUEeQOT3To6/0DC6XC4BmqxmX20VqdFzA/g+agDVTZwNQVF6Ey+2iylSFvcPOyOiR3rDvcnZxqd5Im70NANlVT8poqqa2oe/wNdvMlFSV0tnVCcCouESSEpOwO+xUmqpwuV0UlRcBkDVtTsD+D5qA5anTWDD2FiytFnJO51BaUwbAhFHjvW2arE2IoohBawBAJhu42ThDHKIoYrps8t5LT52GIAiU1ZSRczoHS6uFO5PGsyx1asD+D5oAmUTCRyuyuCt5AtY2G44OB9HaaDThGm+bZptnehgi9R6jA8j+PYg1eMK6oal3zkeoIxgZE4+jw4G1zcbC5InsWb7WW20GgpDsBbSqMA49tJ6Vaekep6NifOTtDk/oq7vzgUQycLNajaeuaGlr8bk/ZuQYAFampXPwoXVoVWFB+R6yzZCAgLXDDoC++0n3oK271g9XeVaDQAjQqD2RdDUBMXoPyRaHHcHv4bZ/hGwzBHCuO5H1ZPseOJ1OAA7nfw7gswIA3gS3a98/+9XdbDX3KT/bUBu8w4SQgPauTmpsVgRBQB1+VZEjqPz2De//xY1fudPmpLbFSmtnB2rFwAurKxEyAsrNTYiIhCkVNOb7PilppCFUZnygHnsHbe0OKixNTI4ZGZSOkOWAcvNlAOyOTiy2Nl+h2x0qM16YLS20tTu6bTcFrSd0BFh6nTDWNvrIPj1ygi3bd+Lo6AxYb0dnF1u27+Q/n/nmleq63mWxrJv8YBDyCAD47NhpH9k/PtjP5q07WLT8GQqLSr336xubudxs8X5vMlsxNfQSeaa4lIXLfsrmrTvY/dEhH52Hsk94P1cMIgJCmgN68K/PvmLDmvu833+z8REKz1dwNK+QqQseJiE+GplUSlW1ibQJSZw7uguAjCWPc7H0EmMS43C6XNTUeSJpSto4tr38lI+9jw982Wvbch1FQLwmgtyTxZy7WOWVxeo05H3yFtteforUCUnUNzZTVW0iZWwCjz3cS9STax5gXFIiVdUm6hubSZ2QxNbNT5J3YAdxMb21xdnzZeSeOOM9AitrDp6AkESAiEilpZnoEWp+nrGQZw58yAvb32HfX3/pbaHAxXPrV/Hc+lU4nS5cbjdKhe+m6Oms5TydtZyOzi6kEgkyWd8l88ZX3sDtFnkhcxG/PnqQSkszblFEIgReEIUkAmpbbNidXSTp9DyaPpcx2ij2f5HPRwePeduITgeiy1MQyWTSbwz+SigV8n4Hv2dfNgc+z2NMZBRZ6XNI1unpcDmpabH02f5aCAkBPeGfojMQJpOz895VCILAmo1/4OyFSk8jEdztNhDF/hVdA8UXK1j77KsIgsDb9/yYMJmc5O4dZrCJMEQEeIwnaT3z9I6k8bw4bzGt7Q7ufXQLZy54jrNxu3G3twCBk1BYVMoPVm7A1tLGS/MWsyhlIgDJOo/NYJfCkEZAjzMAW+bfzePTM6g2Xea2lZu800F0duJuawkoEvbsyyZjyWMYaxt4Ynomr8y/2yvrsRlsMRTSCEjR+Za8by5ZwWt3LqXd3sGKp15j8eqXOF1cjujsxNVqQXR1+dVbfLGCFVkvsnztL2i3d7Ap4y7euHu5T5vkbpsVluAICMkq0LMOJ+l8t8ECApsy7mJaXCJP7v+Aw8cKmfmjDcxJn8TSO2exMHMqiaNGYoiLRZDIaDJbqa5t5NMjJ/h4/1GOfX0Gt1tkXFQ0f/7hMhanTPqG7cFOgZAQUGFuQiGVkhih7VO+KGUi59a9wJv5ObyZn0NufjG5+cVset0jVyk9L0KuLpXH62NYNyOTdTMyUUr7djVBo0UplQWdBENCQOboFEbI5X6PpJRSGc/Oms8zs24n51I52ZUl5Fwqp9pmpsnuOTBJ0ugYFakjY1QyC8beQubo5GsedkgEgVWTZ9Bkb/Pbrj+EhID3lz0y4LYCAvNGpzBvdEooTAOwc+mqoPve9L8PuOkJGPAUWLJgHNFNQ3OyM5wYMAF7/3I/vBvcsdP1jJt+CgyYgNUb/83T+/cMpS/DggETsHtfMXsvFA6lL8OC/+spUNNipbG9VQT6rZP9lVlOlVImnTROj7Wl44b8x0iNzUqH5xBmO/B8oP2dDP9/fgZ7uYGt+Fnt/EaAUiGXnjnyLuNTRl+LrOsOo6beR3VtgxPw+7sZvzkgJjrqhhx8IBhwIbT22Vf5IvfUUPoSUlz5gsUfBkxAfWMz5VWDexV9PcJvDhiVECu9VLD3GwJV4u1EaSTU7p3ncz98UTZqdQT1xZ+E2M3AEYocUH+5yUJdffDv3W4E+JsCu+yOjudTZi4T42P0PpHS2eUEFNdUvn7jdg5mB/773VCgOwdIgR1AVjA6lHj+clpDH2tsvF4plr2XId4z1yAumx8jGj+cJ45QSkVBEBpEy1GdaDmqUyrln/bV91u+9gcz+GvBoZRLxPAwqbvHUKRa5pZKBBGoHwqD1xscgChAF7AZeJ3eyvGmIOAL4AQw5Yp7s4BC4OPhcChY/A/fkn/A85DrtAAAAABJRU5ErkJggg==
// ==/UserScript==

(function() {
  'use strict';
  let isReading = false;
  let images = [];
  let bgIsBlack = true;
  let zoomLevel = 35; // 默认缩放比例35%
  let autoReaderEnabled = false; // 全局阅读器开关

  // 目录识别相关变量
  let seriesDirectory = [];
  let currentSeriesKey = '';
  let isDirectoryMode = false;
  let savedThreadTitle = '';
  let originalSeriesTitle = '';
  let searchCache = new Map();
  let directoryMemoryCache = new Map();

  // 保存原始页面HTML
  let originalPageHTML = '';
  let originalPageTitle = '';

  // 事件处理与状态
  let readerEventHandlers = {};
  let readerEventsBound = false;
  let readerToolsTimer = null;
  let unbindReaderEvents = null;

  // 缓存前缀与过期时长
  const CACHE_PREFIX = 'yamibo-directory-cache-';
  const CACHE_EXPIRY = 24 * 60 * 60 * 1000;

  // 持久化缓存索引键(用于 GM_* 环境)
  const GM_INDEX_KEY = CACHE_PREFIX + 'gm-index';

  // 通用存储封装:优先使用 GM_getValue/GM_setValue/GM_deleteValue(若环境支持),否则降级到 sessionStorage
  function storageGet(key, defaultVal = null) {
    try {
      if (typeof GM_getValue === 'function') {
        let val;
        try {
          val = GM_getValue(key, undefined);
        } catch (e) {
          val = undefined;
        }
        if (val === undefined) {
        } else if (val === null) {
          return defaultVal;
        } else if (typeof val === 'string') {
          try { return JSON.parse(val); } catch (e) { return val; }
        } else {
          return val;
        }
      }
    } catch (e) {
    }

    try {
      const raw = sessionStorage.getItem(key);
      return raw ? JSON.parse(raw) : defaultVal;
    } catch (e) {
      return defaultVal;
    }
  }

  function storageSet(key, value) {
    try {
      if (typeof GM_setValue === 'function') {
        try {
          GM_setValue(key, value);
        } catch (e) {
          try { GM_setValue(key, JSON.stringify(value)); } catch (ee) { throw ee; }
        }

        try {
          let idx = GM_getValue(GM_INDEX_KEY, undefined);
          if (idx === undefined || idx === null) idx = [];
          if (typeof idx === 'string') {
            try { idx = JSON.parse(idx); } catch (e) { idx = []; }
          }
          if (!Array.isArray(idx)) idx = [];
          if (!idx.includes(key)) { idx.push(key); GM_setValue(GM_INDEX_KEY, idx); }
        } catch (e) {
        }
        return true;
      }
    } catch (e) {
    }

    try {
      sessionStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      console.warn('⚠️ storageSet 失败:', e);
      return false;
    }
  }

  function storageRemove(key) {
    try {
      if (typeof GM_deleteValue === 'function') {
        try { GM_deleteValue(key); } catch (e) {
          try { GM_setValue(key, null); } catch (ee) {}
        }

        try {
          let idx = GM_getValue(GM_INDEX_KEY, undefined);
          if (typeof idx === 'string') {
            try { idx = JSON.parse(idx); } catch (e) { idx = []; }
          }
          if (!Array.isArray(idx)) idx = [];
          const newIdx = idx.filter(k => k !== key);
          GM_setValue(GM_INDEX_KEY, newIdx);
        } catch (e) {
        }

        return true;
      }
    } catch (e) {
    }

    try {
      sessionStorage.removeItem(key);
      return true;
    } catch (e) {
      return false;
    }
  }

  // 清理过期缓存:同时支持 sessionStorage 前缀与 GM 索引
  function cleanExpiredCache() {
    let cleanedCount = 0;

    // 1) 清理 sessionStorage 前缀缓存
    try {
      const sKeys = Object.keys(sessionStorage).filter(k => k.startsWith(CACHE_PREFIX));
      sKeys.forEach(key => {
        try {
          const raw = sessionStorage.getItem(key);
          if (!raw) return;
          const obj = JSON.parse(raw);
          const ts = obj.ts || obj.timestamp || 0;
          if (Date.now() - ts > CACHE_EXPIRY) {
            sessionStorage.removeItem(key);
            cleanedCount++;
          }
        } catch (e) {
          try { sessionStorage.removeItem(key); cleanedCount++; } catch (ee) {}
        }
      });
    } catch (e) {
    }

    // 2) 清理 GM_* 存储(使用索引枚举)
    try {
      if (typeof GM_getValue === 'function' && typeof GM_setValue === 'function') {
        let idx = GM_getValue(GM_INDEX_KEY, undefined);
        if (typeof idx === 'string') {
          try { idx = JSON.parse(idx); } catch (e) { idx = []; }
        }
        if (!Array.isArray(idx)) idx = [];

        const remaining = [];
        for (const key of idx) {
          try {
            const stored = storageGet(key, null);
            if (!stored) continue;
            const ts = stored.ts || stored.timestamp || 0;
            if (Date.now() - ts > CACHE_EXPIRY) {
              storageRemove(key);
              cleanedCount++;
            } else {
              remaining.push(key);
            }
          } catch (e) {
            try { storageRemove(key); cleanedCount++; } catch (ee) {}
          }
        }

        // 更新索引为剩余项
        try { GM_setValue(GM_INDEX_KEY, remaining); } catch (e) {}
      }
    } catch (e) {
    }

    if (cleanedCount > 0) console.log('🧹 清理了', cleanedCount, '个过期缓存 (session+GM)');
    return cleanedCount;
  }

  // 在配额不足或需要收缩时清理最旧的缓存(同时支持 sessionStorage 与 GM)
  function cleanOldestCache() {
    try {
      const entries = [];

      // 收集 sessionStorage 条目
      try {
        Object.keys(sessionStorage).forEach(key => {
          if (!key.startsWith(CACHE_PREFIX)) return;
          try {
            const obj = JSON.parse(sessionStorage.getItem(key));
            const ts = obj.ts || obj.timestamp || 0;
            entries.push({ key, ts, source: 'session' });
          } catch (e) {
            entries.push({ key, ts: 0, source: 'session' });
          }
        });
      } catch (e) {}

      // 收集 GM_* 条目 via index
      try {
        if (typeof GM_getValue === 'function') {
          let idx = GM_getValue(GM_INDEX_KEY, undefined);
          if (typeof idx === 'string') {
            try { idx = JSON.parse(idx); } catch (e) { idx = []; }
          }
          if (!Array.isArray(idx)) idx = [];

          for (const key of idx) {
            try {
              const stored = storageGet(key, null);
              const ts = stored ? (stored.ts || stored.timestamp || 0) : 0;
              entries.push({ key, ts, source: 'gm' });
            } catch (e) {
              entries.push({ key, ts: 0, source: 'gm' });
            }
          }
        }
      } catch (e) {}

      if (entries.length === 0) return 0;

      // 按时间戳排序,最旧的在前
      entries.sort((a, b) => (a.ts || 0) - (b.ts || 0));

      const toDelete = Math.ceil(entries.length / 2);
      for (let i = 0; i < toDelete; i++) {
        const e = entries[i];
        try {
          if (e.source === 'session') sessionStorage.removeItem(e.key);
          else storageRemove(e.key);
        } catch (err) {}
      }

      return toDelete;
    } catch (e) {
      console.warn('cleanOldestCache failed', e);
      return 0;
    }
  }

  // 检查是否启用了全局阅读器模式
  function checkAutoReaderStatus() {
    const stored = localStorage.getItem('yamibo-auto-reader');
    return stored === 'true';
  }

  // 设置全局阅读器模式
  function setAutoReaderStatus(enabled) {
    autoReaderEnabled = enabled;
    localStorage.setItem('yamibo-auto-reader', enabled.toString());
    console.log('🔧 全局阅读器模式:', enabled ? '开启' : '关闭');
  }

  /*** 标题标准化函数  ***/
  function normalizeSeriesTitle(rawTitle) {
    if (!rawTitle) return '';

    // 首先清理页面title的后缀
    let title = rawTitle;
    if (title.includes(' - ')) {
      title = title.split(' - ')[0].trim();
    }

    // 移除页数标注
    title = title.replace(/[((]\s*\d+\s*p\s*[))]\s*$/i, '');

    // 移除章节标识 - 参考脚本的模式
    title = title.replace(/第[\d一二三四五六七八九十百千万〇零兩两1234567890\.]+[话話章节節回卷篇]/gi, ' ');
    title = title.replace(/\d+(?:\.\d+)?\s*[话話章节節回卷篇]/gi, ' ');
    title = title.replace(/\d+(?:\.\d+)?\s*[上下前后前後左右中篇部卷期全完]+(?:\s*[++&和及與并並,,/]\s*[上下前后前後左右中篇部卷期全完]+)+/gi, ' ');
    title = title.replace(/[((][上下前后前後中全完]+(?:\s*[,,++&和及與并並/]\s*[上下前后前後中全完]+)*[))]/g, ' ');
    title = title.replace(/\d+(?:\.\d+)?\s*[上下前后前後左右中篇部卷期全完]+/gi, ' ');
    title = title.replace(/(?:\s+|[-‐‑‒–—―-~~·•_、::])?\d+(?:\.\d+)*(?:\s*[上下前后前後左右中篇部卷期話话节節全完])?\s*$/g, ' ');
    title = title.replace(/[--—–~~\u2013\u2014\s]+$/g, ' ');
    title = title.replace(/[\[\]【】()()]/g, ' ');
    title = title.replace(/\s+/g, ' ').trim();

    if (!title) {
      return rawTitle.trim();
    }
    return title;
  }

  function buildSeriesKey(title) {
    const normalized = normalizeSeriesTitle(title || '');
    const base = normalized || (title || '').trim();
    return base.toLowerCase();
  }

  function generateSeriesKey(title) {
    try {
      const k = buildSeriesKey(title || '');
      // 限制长度并移除特殊字符,避免存储键过长或包含非法字符
      return k.replace(/[\s\/:\\#\?&=\+%\*\|<>"'`]/g, '-').substring(0, 120);
    } catch (e) {
      return String(title || '').toLowerCase().substring(0, 120);
    }
  }

// 统一的排除词汇列表,避免重复定义
const EXCLUDE_WORDS = [
  '个人汉化', '汉化组', '汉化', '个人翻译', '個人翻譯', '个汉',
  '翻译', '生肉', '未翻译', '填坑组', '粮食组', '保护协会','創作百合', '创作百合',
  '熟肉', '字幕', '工作室', '提灯喵', '社团', '官方中字', '出版',
  '汉化工房', '猫岛汉化组', '和菓子漫画屋', '大友同好會', '透明声彩汉化组',
  '翻译组', '汉化委员会', '渣翻渣嵌', '合作汉化', '完结','连载','短篇',
  '合集','同人','raw','韩漫','杂志','英译','单行本','插画','特典','番外','外传','彩页','合订本'
].map(s => s.toLowerCase());

// 统一的文本过滤函数:检查是否为噪音词汇
function isNoiseText(text) {
  if (!text || typeof text !== 'string') return true;
  const t = text.toLowerCase();

  // 检查是否包含排除词汇
  for (const word of EXCLUDE_WORDS) {
    if (t.includes(word)) return true;
  }

  // 常见格式判断:纯数字视为噪音
  if (/^\d+$/.test(t)) return true;

  // 智能长度检查:CJK字符2个即有效,其他字符3个以上有效
  const hasCJK = /[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/.test(text);
  const minLength = hasCJK ? 2 : 3;
  if (t.length < minLength) return true;

  return false;
}

// 保持向后兼容的 isGenericTag 函数
function isGenericTag(text) {
  return isNoiseText(text);
}

  function getCachedDirectory(seriesKey) {
    // 🚫 临时禁用缓存读取,强制测试双语解析逻辑
    console.log('🔄 缓存读取已禁用,将执行新的双语解析逻辑');
    return null;

    if (!seriesKey) return null;

    // 1) 先检查内存缓存
    try {
      if (directoryMemoryCache && directoryMemoryCache.has(seriesKey)) {
        const cached = directoryMemoryCache.get(seriesKey);
        if (cached && cached.ts && (Date.now() - cached.ts) < CACHE_EXPIRY) {
          return cached.data || cached.d || null;
        } else {
          directoryMemoryCache.delete(seriesKey);
        }
      }
    } catch (e) {
    }

    const key = CACHE_PREFIX + seriesKey;

    // 2) 尝试 GM_getValue
    try {
      if (typeof GM_getValue === 'function') {
        let stored = GM_getValue(key, undefined);
        if (typeof stored === 'string') {
          try { stored = JSON.parse(stored); } catch (e) { /* leave as-is */ }
        }
        if (stored && (stored.d || stored.data)) {
          const ts = stored.ts || stored.timestamp || 0;
          if (Date.now() - ts < CACHE_EXPIRY) {
            try { directoryMemoryCache.set(seriesKey, { d: stored.d || stored.data, data: stored.d || stored.data, ts: ts || Date.now() }); } catch (e) {}
            return stored.d || stored.data;
          } else {
            try { storageRemove(key); } catch (e) {}
            return null;
          }
        }
      }
    } catch (e) {
    }

    // 3) 尝试 sessionStorage
    try {
      const raw = sessionStorage.getItem(key);
      if (raw) {
        let obj = raw;
        try { obj = JSON.parse(raw); } catch (e) { obj = raw; }
        if (obj && (obj.d || obj.data)) {
          const ts = obj.ts || obj.timestamp || 0;
          if (Date.now() - ts < CACHE_EXPIRY) {
            try { directoryMemoryCache.set(seriesKey, { d: obj.d || obj.data, data: obj.d || obj.data, ts: ts || Date.now() }); } catch (e) {}
            return obj.d || obj.data;
          } else {
            try { sessionStorage.removeItem(key); } catch (e) {}
            return null;
          }
        }
      }
    } catch (e) {
    }

    return null;
  }

  function setCachedDirectory(seriesKey, directory) {
    if (!seriesKey || !directory) return false;
    const key = CACHE_PREFIX + seriesKey;
    const payload = { ts: Date.now(), data: directory, d: directory };

    try { directoryMemoryCache.set(seriesKey, { d: directory, data: directory, ts: payload.ts }); } catch (e) {}

    try {
      if (typeof GM_setValue === 'function') {
        try { GM_setValue(key, payload); }
        catch (e) {
          try { GM_setValue(key, JSON.stringify(payload)); } catch (ee) { /* ignore */ }
        }
        return true;
      }
    } catch (e) {
    }

    try {
      sessionStorage.setItem(key, JSON.stringify(payload));
      return true;
    } catch (e) {
      console.warn('⚠️ setCachedDirectory 写入失败:', e);
      return false;
    }
  }

  /*** 相似度计算辅助函数  ***/

  // 简繁体标准化函数 - 使用 OpenCC 库
  let converter = null;

  function normalizeChineseVariants(text) {
    try {
      // 初始化转换器(繁体转简体)
      if (!converter && typeof OpenCC !== 'undefined') {
        converter = OpenCC.Converter({ from: 'tw', to: 'cn' });
      }

      if (converter) {
        return converter(text);
      }

      return text;
    } catch (error) {
      console.warn('简繁转换失败,使用原文本:', error);
      return text;
    }
  }

  // 提取有意义的关键词(排除常见无关词汇)
function extractMeaningfulKeywords(title) {
  const keywords = [];

  // 1. 检查明显的中外对照分隔符
  const dualMatch = title.match(/^(.+?)[\s/||/()()]+(.+?)$/);
  if (dualMatch) {
    const part1 = dualMatch[1].trim();
    const part2 = dualMatch[2].trim();
    if (part1 && part2 && part1 !== part2) {
      keywords.push(part1, part2);
      return [...new Set(keywords)];
    }
  }

  // 2. 没有分隔符的中外混合,允许拆分
  const enZhMix = title.match(/([A-Za-z][A-Za-z0-9\.\*\s]*[A-Za-z])([\u4e00-\u9fa5]{2,})/);
  if (enZhMix) {
    const en = enZhMix[1].trim();
    const zh = enZhMix[2].trim();
    if (!isNoiseText(en)) keywords.push(en);
    if (!isNoiseText(zh)) keywords.push(zh);
  }

  // 3. 继续原有的中文、日文、韩文、英文多词短语等提取
  const chineseMatches = title.match(/[\u4e00-\u9fa5]{2,8}/g);
  if (chineseMatches) {
    chineseMatches.forEach(match => {
      if (!isNoiseText(match)) keywords.push(match);
    });
  }
  const japaneseMatches = title.match(/[\u3040-\u309F\u30A0-\u30FF]{2,}/g);
  if (japaneseMatches) {
    japaneseMatches.forEach(match => {
      if (!isNoiseText(match)) keywords.push(match);
    });
  }
  const koreanMatches = title.match(/[\uAC00-\uD7AF]{2,}/g);
  if (koreanMatches) {
    koreanMatches.forEach(match => {
      if (!isNoiseText(match)) keywords.push(match);
    });
  }
  const englishMatches = title.match(/[A-Za-z]{3,}(?:\s+[A-Za-z]{2,})+/g);
  if (englishMatches) {
    englishMatches.forEach(match => {
      const cleanMatch = match.trim();
      if (cleanMatch.length >= 5 && !isNoiseText(cleanMatch)) {
        keywords.push(cleanMatch);
      }
    });
  }
  const singleEnglishMatches = title.match(/\b[A-Za-z]{5,}\b/g);
  if (singleEnglishMatches) {
    singleEnglishMatches.forEach(match => {
      if (keywords.some(k => k !== match && k.includes(match))) return;
      if (!isNoiseText(match)) {
        keywords.push(match);
      }
    });
  }


  // 去重,且如果有整体词则不保留其子词
  const finalKeywords = [];
  for (const kw of keywords) {
    if (finalKeywords.some(k => k.length > kw.length && k.includes(kw))) continue;
    finalKeywords.push(kw);
  }

  return [...new Set(finalKeywords)];
}

  // 计算核心作品名相似度
  function calculateCoreWorkSimilarity(core1, core2) {
    // 🔧 特殊处理:对于英文为主的标题,使用更严格的匹配
    const isEnglishDominant1 = /[A-Za-z]/.test(core1) && !/[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF]/.test(core1);
    const isEnglishDominant2 = /[A-Za-z]/.test(core2) && !/[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF]/.test(core2);

    if (isEnglishDominant1 && isEnglishDominant2) {
      // 对于英文为主的标题,进行词级别的严格匹配
      const words1 = core1.toLowerCase().replace(/[^\w\s]/g, ' ').split(/\s+/).filter(w => w.length > 0 && /[a-z]/.test(w));
      const words2 = core2.toLowerCase().replace(/[^\w\s]/g, ' ').split(/\s+/).filter(w => w.length > 0 && /[a-z]/.test(w));

      console.log(`   🔤 英文词汇对比: [${words1.join(', ')}] vs [${words2.join(', ')}]`);

      // 计算词汇重叠度 - 更严格的匹配逻辑
      const uniqueWords1 = [...new Set(words1)];
      const uniqueWords2 = [...new Set(words2)];
      const commonUniqueWords = uniqueWords1.filter(w => uniqueWords2.includes(w));

      if (commonUniqueWords.length === 0) {
        console.log(`   ❌ 英文标题无共同词汇,相似度为0`);
        return 0;
      }

      // 🎯 严格匹配:要求绝大部分唯一词汇都匹配
      const overlapRatio1 = commonUniqueWords.length / uniqueWords1.length;
      const overlapRatio2 = commonUniqueWords.length / uniqueWords2.length;
      const minOverlapRatio = Math.min(overlapRatio1, overlapRatio2);

      console.log(`   📊 唯一词汇: [${uniqueWords1.join(', ')}] vs [${uniqueWords2.join(', ')}]`);
      console.log(`   📊 共同词汇: [${commonUniqueWords.join(', ')}]`);
      console.log(`   📊 重叠率: ${(overlapRatio1 * 100).toFixed(1)}% & ${(overlapRatio2 * 100).toFixed(1)}% (最小: ${(minOverlapRatio * 100).toFixed(1)}%)`);

      // 🎯 更严格的匹配标准
      if (minOverlapRatio >= 0.8) {
        return 0.95; // 高匹配度:两边80%以上词汇重叠
      } else if (minOverlapRatio >= 0.6) {
        return 0.7; // 中等匹配度:两边60%以上词汇重叠
      } else {
        console.log(`   ❌ 英文词汇重叠度不足 (${(minOverlapRatio * 100).toFixed(1)}% < 60%),认为不相似`);
        return 0; // 重叠度不足
      }
    }

    // 标准化处理(包括简繁体转换)
    const clean1 = normalizeChineseVariants(core1.replace(/[^\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFa-zA-Z0-9]/g, '')).toLowerCase();
    const clean2 = normalizeChineseVariants(core2.replace(/[^\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFa-zA-Z0-9]/g, '')).toLowerCase();

    if (!clean1 || !clean2) return 0;

    console.log(`   🔤 简繁体标准化: "${clean1}" vs "${clean2}"`);

    // 1. 完全匹配
    if (clean1 === clean2) return 1.0;

    // 2. 包含关系
    if (clean1.includes(clean2) || clean2.includes(clean1)) {
      const longer = clean1.length > clean2.length ? clean1 : clean2;
      const shorter = clean1.length <= clean2.length ? clean1 : clean2;
      return shorter.length / longer.length * 0.95; // 包含关系给0.95分
    }

    // 3. 计算最长公共子串
    let maxCommonLength = 0;
    for (let i = 0; i < clean1.length; i++) {
      for (let j = 0; j < clean2.length; j++) {
        let commonLength = 0;
        while (i + commonLength < clean1.length &&
               j + commonLength < clean2.length &&
               clean1[i + commonLength] === clean2[j + commonLength]) {
          commonLength++;
        }
        maxCommonLength = Math.max(maxCommonLength, commonLength);
      }
    }

    // 4. 基于最长公共子串计算相似度
    const minLength = Math.min(clean1.length, clean2.length);
    if (maxCommonLength >= 4) { // 至少4个字符相同才认为有相似性
      const similarity = (maxCommonLength / minLength) * 0.9; // 最高0.9分
      return similarity;
    }

    return 0;
  }

  // 检查关键词匹配(排除翻译组等无关信息)
  function checkKeywordMatch(core1, core2) {
    // 提取有意义的关键词片段(4字符以上)
    const keywords1 = extractMeaningfulKeywords(core1);
    const keywords2 = extractMeaningfulKeywords(core2);

    console.log(`   🔍 关键词提取: "${keywords1.join('", "')}" vs "${keywords2.join('", "')}"`);

    let bestMatch = 0;

    // 检查直接关键词匹配
    for (const kw1 of keywords1) {
      for (const kw2 of keywords2) {
        if (kw1.length >= 2 && kw2.length >= 2) {
if (kw1 === kw2) {
  console.log(`   🎯 完全匹配: "${kw1}"`);
  return 0.95; // 完全匹配给高分
} else if (kw1.includes(kw2) || kw2.includes(kw1)) {
  const longer = kw1.length > kw2.length ? kw1 : kw2;
  const shorter = kw1.length <= kw2.length ? kw1 : kw2;
  const match = shorter.length / longer.length;
  bestMatch = Math.max(bestMatch, match * 0.9);
  console.log(`   🎯 包含匹配: "${kw1}" ⟷ "${kw2}" (${(match * 90).toFixed(1)}%)`);
}
        }
      }
    }

    // 如果核心1和核心2都是字符串对象且包含双语信息,进行交叉匹配
    if (typeof core1 === 'object' && core1._dualLanguage && typeof core2 === 'object' && core2._dualLanguage) {
      // 双语对双语:交叉匹配所有组合
      const pairs = [
        [core1._dualLanguage.chinese, core2._dualLanguage.chinese],
        [core1._dualLanguage.foreign, core2._dualLanguage.foreign],
        [core1._dualLanguage.chinese, core2._dualLanguage.foreign],
        [core1._dualLanguage.foreign, core2._dualLanguage.chinese]
      ];

      for (const [term1, term2] of pairs) {
        if (term1 && term2) {
          const kw1Set = extractMeaningfulKeywords(term1);
          const kw2Set = extractMeaningfulKeywords(term2);

          for (const kw1 of kw1Set) {
            for (const kw2 of kw2Set) {
              if (kw1.length >= 2 && kw2.length >= 2) {
                if (kw1 === kw2 || kw1.includes(kw2) || kw2.includes(kw1)) {
                  console.log(`   🌐 双语交叉匹配: "${kw1}" ⟷ "${kw2}"`);
                  return 0.92; // 双语匹配给高分
                }
              }
            }
          }
        }
      }
    } else if (typeof core1 === 'object' && core1._dualLanguage) {
      // 双语对单语:用双语的两个部分分别匹配单语
      const singleTerms = extractMeaningfulKeywords(core2.toString());
      const dualTerms = [
        ...extractMeaningfulKeywords(core1._dualLanguage.chinese || ''),
        ...extractMeaningfulKeywords(core1._dualLanguage.foreign || '')
      ];
      for (const dTerm of dualTerms) {
        for (const sTerm of singleTerms) {
          if (dTerm.length >= 2 && sTerm.length >= 2) {
            if (dTerm === sTerm) {
              console.log(`   🎯 双语单语完全匹配: "${dTerm}"`);
              return 0.95;
            } else if (dTerm.includes(sTerm) || sTerm.includes(dTerm)) {
              const longer = dTerm.length > sTerm.length ? dTerm : sTerm;
              const shorter = dTerm.length <= sTerm.length ? dTerm : sTerm;
              const match = shorter.length / longer.length;
              bestMatch = Math.max(bestMatch, match * 0.9);
              console.log(`   🎯 双语单语包含匹配: "${dTerm}" ⟷ "${sTerm}" (${(match * 90).toFixed(1)}%)`);
            }
          }
        }
      }
    } else if (typeof core2 === 'object' && core2._dualLanguage) {
      // 单语对双语:用单语匹配双语的两个部分
      const singleTerms = extractMeaningfulKeywords(core1.toString());
      const dualTerms = [
        ...extractMeaningfulKeywords(core2._dualLanguage.chinese || ''),
        ...extractMeaningfulKeywords(core2._dualLanguage.foreign || '')
      ];
      for (const sTerm of singleTerms) {
        for (const dTerm of dualTerms) {
          if (sTerm.length >= 2 && dTerm.length >= 2) {
            if (sTerm === dTerm) {
              console.log(`   🎯 单语双语完全匹配: "${sTerm}"`);
              return 0.95;
            } else if (sTerm.includes(dTerm) || dTerm.includes(sTerm)) {
              const longer = sTerm.length > dTerm.length ? sTerm : dTerm;
              const shorter = sTerm.length <= dTerm.length ? sTerm : dTerm;
              const match = shorter.length / longer.length;
              bestMatch = Math.max(bestMatch, match * 0.9);
              console.log(`   🎯 单语双语包含匹配: "${sTerm}" ⟷ "${dTerm}" (${(match * 90).toFixed(1)}%)`);
            }
          }
        }
      }
    }

    return bestMatch;
  }

  // 提取核心作品名
function extractCoreWorkName(title) {
  console.log(`   🔍 优化版标题处理: "${title}"`);
  if (!title || typeof title !== 'string') return '';

  // 1. 移除开头的【】[]标签和紧跟的()()
  let step1 = title
    .replace(/^(\s*【[^】]*】\s*)+/g, '') // 移除开头【】
    .replace(/^(\s*\[[^\]]*\]\s*)+/g, '') // 移除开头[]
    .replace(/^(\s*[\[【((][^\]】))]*[\]】))]\s*)+/g, '') // 移除开头的所有括号
    .replace(/^[\]】))]\s*/, '') // 清理可能残留的右括号
    .trim();

  // 处理《》书名号
  if (step1.includes('《') && step1.includes('》')) {
    const bookMatch = step1.match(/《([^》]+)》/);
    if (bookMatch) {
      const bookTitle = bookMatch[1].trim();
      step1 = step1.replace(/《[^》]+》/, bookTitle); // 替换《》为内容
      console.log(`   📚 提取书名号内容: "${bookTitle}"`);
    }
  }

  console.log(`   🧹 移除开头标签和括号: "${step1}"`);

  // 2. 处理|分隔的双语结构
  if (step1.includes('|')) {
    const parts = step1.split('|').map(p => p.trim());
    console.log(`   🌐 检测到|分隔双语: [${parts.map(p => `"${p}"`).join(', ')}]`);
    const validParts = parts.filter(p => p.length > 0);
    if (validParts.length > 0) {
      // 返回双语对象结构
      const result = new String(validParts[0]);
      if (validParts.length > 1) {
        result._dualLanguage = {
          chinese: validParts.find(p => /[\u4e00-\u9fa5]/.test(p)) || validParts[0],
          foreign: validParts.find(p => !/[\u4e00-\u9fa5]/.test(p)) || validParts[1]
        };
      }
      return result;
    }
  }

  // 3. 特殊处理:提取末尾的日文标题
  const endJapaneseMatch = step1.match(/【[^】]*】([ひらがなカタカナ\u3040-\u309F\u30A0-\u30FF\u4e00-\u9fa5ー~〜]+)$/);
  if (endJapaneseMatch) {
    const japaneseTitle = endJapaneseMatch[1].trim();
    console.log(`   🎌 提取末尾日文标题: "${japaneseTitle}"`);
    return japaneseTitle;
  }

  // 4. 清理尾部的汉化组信息
  step1 = step1.replace(/\s*(kakukuroi|汉化组|个人汉化|翻译组|汉化|翻译).*$/i, '').trim();

  // 5. 处理/分隔的多个关键词
  if (step1.includes('/') && !step1.includes('【') && !step1.includes('】')) {
    const slashMatch = step1.match(/^([^\/【】]+)\s*\/\s*([^\/【】]+?)(?:\s+\d+)?$/);
    if (slashMatch) {
      const part1 = slashMatch[1].trim();
      const part2 = slashMatch[2].trim();

      if (!part1.includes('同人志') && !part2.includes('【')) {
        console.log(`   🔀 检测到/分隔关键词: "${part1}" / "${part2}"`);
        const result = new String(part1);
        result._dualLanguage = {
          chinese: part1,
          foreign: part2
        };
        return result;
      }
    }
  }

  // 6. 检测章节分隔,确定主体书名范围
  let mainContent = step1;

  // 策略A: 匹配明确的章节标记
  const explicitChapterMatch = step1.match(/^(.+?)\s*(?:第?\d+(?:\.\d+)?\s*[话話章节節回卷篇期]|番外|外传|omake|extra|短篇|\d+\s*「)/i);
  if (explicitChapterMatch) {
    mainContent = explicitChapterMatch[1].trim();
    console.log(`   📖 检测到明确章节标记,主体书名: "${mainContent}"`);
  } else {
    // 策略B: 检测各种隐式章节模式
    let chapterDetected = false;

    // B1: "作品名+数字+空格+小标题"
    const spaceChapterMatch = step1.match(/^([\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFA-Za-z\s]+?)(\d{1,3})\s+(.+)$/);
    if (spaceChapterMatch && !chapterDetected) {
      const baseName = spaceChapterMatch[1].trim();
      const chapterNum = spaceChapterMatch[2];
      const subtitle = spaceChapterMatch[3].trim();

      // 验证这确实是章节模式:基础名应该是合理的作品名(至少3字符)
      if (baseName.length >= 3 && chapterNum.length <= 3) {
        mainContent = baseName;
        chapterDetected = true;
        console.log(`   📖 检测到空格分隔章节: 作品名"${baseName}" + 章节"${chapterNum}" + 小标题"${subtitle}"`);
      }
    }

    // B2: "作品名+数字+其他内容"
    const noSpaceChapterMatch = step1.match(/^([\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFA-Za-z]+?)(\d{1,3})([\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFA-Za-z].+)$/);
    if (noSpaceChapterMatch && !chapterDetected) {
      const baseName = noSpaceChapterMatch[1].trim();
      const chapterNum = noSpaceChapterMatch[2];
      const subtitle = noSpaceChapterMatch[3].trim();

      // 更严格的验证:确保基础名至少4字符,且副标题不是纯数字
      if (baseName.length >= 4 && chapterNum.length <= 3 && !/^\d+$/.test(subtitle)) {
        mainContent = baseName;
        chapterDetected = true;
        console.log(`   📖 检测到紧贴章节: 作品名"${baseName}" + 章节"${chapterNum}" + 小标题"${subtitle}"`);
      }
    }

    // B3: "作品名 + 空格 + 数字 + 小标题"
    const spaceBeforeChapterMatch = step1.match(/^([\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FFA-Za-z]+)\s+(\d{1,3})\s*(.*)$/);
    if (spaceBeforeChapterMatch && !chapterDetected) {
      const baseName = spaceBeforeChapterMatch[1].trim();
      const chapterNum = spaceBeforeChapterMatch[2];
      const subtitle = spaceBeforeChapterMatch[3].trim();

      // 验证:基础名合理,章节号不超过3位
      if (baseName.length >= 3 && chapterNum.length <= 3) {
        mainContent = baseName;
        chapterDetected = true;
        console.log(`   📖 检测到前置空格章节: 作品名"${baseName}" + 章节"${chapterNum}" + 小标题"${subtitle}"`);
      }
    }
  }

  // 7. 移除版本号
  mainContent = mainContent.replace(/(\d+(?:\.\d+)+)(?=\s|$|[\((【])/g, '').trim();

  // 8. 检查中间的括号
  const middleBrackets = mainContent.match(/[\[【((]([^\]】))]+)[\]】))]/g);
  let meaningfulBrackets = [];

  if (middleBrackets) {
    console.log(`   🔍 发现括号: ${middleBrackets.join(', ')}`);

    middleBrackets.forEach(bracket => {
      const content = bracket.slice(1, -1).trim();

      // 移除括号内的版本号和感叹号
      const cleanContent = content.replace(/\d+(?:\.\d+)+/g, '').replace(/[!!]$/, '').trim();

      const hasJapanese = /[\u3040-\u309F\u30A0-\u30FF]/.test(cleanContent);
      const hasKorean = /[\uAC00-\uD7AF]/.test(cleanContent);
      const hasChinese = /[\u4e00-\u9fa5]/.test(cleanContent);
      const hasLongEnglish = /[A-Za-z]/.test(cleanContent) && cleanContent.length > 4;

      // 检查原始内容中是否包含有效的中文(去除版本号后)
      const originalChinese = /[\u4e00-\u9fa5]/.test(content);
      const chineseAfterClean = content.replace(/\d+(?:\.\d+)+/g, '').replace(/[!!]$/, '').trim();
      const hasValidChinese = originalChinese && /[\u4e00-\u9fa5]/.test(chineseAfterClean) && chineseAfterClean.length >= 2;

      // 优化:更宽松的判断条件
      const isValidContent = hasJapanese || hasKorean || hasLongEnglish || hasValidChinese;

      // 排除明显的作者标识和展会标签(但不排除有版本号的中文词汇)
      const isAuthorOrTag = /^[A-Za-z\s]+\d+$/.test(content) || // Key Island13
                           /[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF]+\([A-Za-z0-9]+\)/.test(content) || // 壁発狂(kao)
                           (/^[\u4e00-\u9fa5A-Za-z0-9]{1,2}$/.test(cleanContent) && !hasValidChinese) || // 短标识符如"苍莓"等,但保留带版本号的中文
                           /汉化|翻译|合同志|短篇|版本/i.test(cleanContent) ||
                           cleanContent === '';

      if (isValidContent && !isAuthorOrTag) {
        // 如果有有效中文,使用清理后的版本
        const finalContent = hasValidChinese ? chineseAfterClean : cleanContent;
        meaningfulBrackets.push(finalContent);
        console.log(`   💎 提取对照内容: "${finalContent}"`);
      } else {
        console.log(`   🚫 忽略: "${content}" (${isAuthorOrTag ? '作者/标签' : '短标识符'})`);
      }
    });
  }

  // 9. 生成最终结果
  let body = mainContent.replace(/[\[【((][^\]】))]*[\]】))]/g, '').trim();

  // 处理各种语言混合情况

  // 韩文+中文混合
  const koreanChineseMatch = body.match(/^([\uAC00-\uD7AF]+)([\u4e00-\u9fa5]+)$/);
  if (koreanChineseMatch) {
    const koreanPart = koreanChineseMatch[1].trim();
    const chinesePart = koreanChineseMatch[2].trim();
    console.log(`   🇰🇷 检测到韩中混合: 韩文"${koreanPart}" + 中文"${chinesePart}"`);
    const result = new String(koreanPart);
    result._dualLanguage = {
      chinese: chinesePart,
      foreign: koreanPart
    };
    return result;
  }

  // 中文+韩文混合
  const chineseKoreanMatch = body.match(/^([\u4e00-\u9fa5]+)\s*([\uAC00-\uD7AF\s]+)$/);
  if (chineseKoreanMatch) {
    const chinesePart = chineseKoreanMatch[1].trim();
    const koreanPart = chineseKoreanMatch[2].trim();
    console.log(`   🇰🇷 检测到中韩混合: 中文"${chinesePart}" + 韩文"${koreanPart}"`);
    const result = new String(chinesePart);
    result._dualLanguage = {
      chinese: chinesePart,
      foreign: koreanPart
    };
    return result;
  }

  // 日文+中文混合
  const japaneseChineseMatch = body.match(/^([\u3040-\u309F\u30A0-\u30FF~〜~ー]+)\s+([\u4e00-\u9fa5~〜~]+)$/);
  if (japaneseChineseMatch) {
    const japanesePart = japaneseChineseMatch[1].trim();
    const chinesePart = japaneseChineseMatch[2].trim();
    console.log(`   🇯🇵 检测到日中混合: 日文"${japanesePart}" + 中文"${chinesePart}"`);
    const result = new String(japanesePart);
    result._dualLanguage = {
      chinese: chinesePart,
      foreign: japanesePart
    };
    return result;
  }

  // 处理中文+英文混合
  const chineseEnglishMatch = body.match(/^([\u4e00-\u9fa5!!]+)\s+([A-Za-z][A-Za-z\s!]*[A-Za-z!])$/);
  if (chineseEnglishMatch) {
    const chinesePart = chineseEnglishMatch[1].replace(/[!!]$/, '').trim();
    const englishPart = chineseEnglishMatch[2].replace(/[!!]$/, '').trim();
    console.log(`   🔀 检测到中英混合: 中文"${chinesePart}" + 英文"${englishPart}"`);
    const result = new String(chinesePart);
    result._dualLanguage = {
      chinese: chinesePart,
      foreign: englishPart
    };
    return result;
  }

  // 处理英文+中文混合的情况
  const mixedMatch = body.match(/^([A-Za-z][A-Za-z0-9\.\*\s]*[A-Za-z])([\u4e00-\u9fa5].*)?$/);
  if (mixedMatch && mixedMatch[2]) {
    // 英文部分和中文部分分开
    const englishPart = mixedMatch[1].trim();
    const chinesePart = mixedMatch[2].trim();

    console.log(`   🔀 检测到英中混合: 英文"${englishPart}" + 中文"${chinesePart}"`);

    // 判断中文部分是否是章节标题
    if (/能和你在一起就好|总有一天|第\d+话/.test(chinesePart)) {
      console.log(`   � 中文部分是章节标题,只保留英文`);
      return englishPart;
    } else {
      console.log(`   📖 中文部分是作品名,创建双语结构`);
      const result = new String(englishPart);
      result._dualLanguage = {
        chinese: chinesePart,
        foreign: englishPart
      };
      return result;
    }
  }

  // 只保留核心标题(移除数字、「」等)
  const cleanBody = body
    .replace(/\s*\d+\s*「[^」]*」.*$/g, '')
    .replace(/\s*\d+\s*$/, '')
    .trim();

  if (cleanBody !== body) {
    console.log(`   ✂️ 清理章节信息: "${cleanBody}"`);
  }

  // 如果有有意义的括号内容,创建双语结构
  if (meaningfulBrackets.length > 0 && cleanBody) {
    const result = new String(cleanBody);
    const chineseBracket = meaningfulBrackets.find(b => /[\u4e00-\u9fa5]/.test(b));
    const foreignBracket = meaningfulBrackets.find(b => !/[\u4e00-\u9fa5]/.test(b));

    console.log(`   🔍 括号分析: 中文=${chineseBracket} 外文=${foreignBracket} 主体=${cleanBody}`);

    if (chineseBracket || foreignBracket) {
      result._dualLanguage = {
        chinese: chineseBracket || cleanBody,
        foreign: foreignBracket || cleanBody
      };
      console.log(`   🌐 创建双语结构: 中文="${result._dualLanguage.chinese}" 外文="${result._dualLanguage.foreign}"`);
    }
    return result;
  }

  console.log(`   🎯 最终结果: "${cleanBody || step1 || title}"`);
  return cleanBody || step1 || title || '';
    const meaningfulBracketMatch = workName.match(/^([^((]+)[((]([^))]*[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF]+[^))]*)[))](.*)$/);
    if (meaningfulBracketMatch) {
      const beforeBracket = meaningfulBracketMatch[1].trim();
      const insideBracket = meaningfulBracketMatch[2].trim();
      const afterBracket = meaningfulBracketMatch[3].trim();

      // 检查括号内是否包含无关描述词
      const isDescriptive = /短篇|合同志|合集|特典|番外|彩页|后记|前记|完结|连载|单行本|杂志|raw|生肉|汉化|翻译/.test(insideBracket);

      if (!isDescriptive && insideBracket.length >= 2) {
        // 保留有意义的括号对照
        workName = beforeBracket + '(' + insideBracket + ')';
        console.log(`   🌐 保留有意义的括号对照: "${workName}"`);
      } else {
        // 移除描述性括号内容
        workName = beforeBracket + (afterBracket ? ' ' + afterBracket : '');
        console.log(`   🗑️ 移除描述性括号内容: "${insideBracket}"`);
      }
    } else {
      // 移除其他尾部括号内容
      workName = workName.replace(/\s*[((][^))]*[))].*$/, '');
    }

    // 2) 移除「」『』等引号中的副标题
    workName = workName.replace(/\s*[「『〖〈《"'][^」』〗〉》"']*[」』〗〉》"'].*$/, '');

    // 3) 移除章节相关信息(包含 "第X部/话/卷" 等模式)
    workName = workName.replace(/\s+(第\d+[部卷话篇章节期回集]|第[一二三四五六七八九十]+[部卷话篇章节期回集]).*$/, '');
    workName = workName.replace(/\s+\d+(?:\.\d+)?\s*(话|篇|期|回|集|章|节|卷|部|上|下|前|后|中|完|全|特|番外|彩页|特典|后记|前记|完结).*$/, '');

  }

  // 智能相似度匹配函数 - 专注核心作品名匹配
  function calculateSimilarity(searchTitle, resultTitle) {
    console.log(`🔄 计算相似度: "${searchTitle}" vs "${resultTitle}"`);

    // 提取两个标题的核心作品名(去除翻译组、章节号等干扰信息)
    const searchCore = extractCoreWorkName(searchTitle);
    const resultCore = extractCoreWorkName(resultTitle);

    console.log(`   📝 搜索核心: "${searchCore}"`);
    console.log(`   📝 结果核心: "${resultCore}"`);

    // 调试:检查是否有双语信息
    if (typeof searchCore === 'object' && searchCore._dualLanguage) {
      console.log(`   🌐 搜索核心双语信息: 中文="${searchCore._dualLanguage.chinese}" 外文="${searchCore._dualLanguage.foreign}"`);
    }
    if (typeof resultCore === 'object' && resultCore._dualLanguage) {
      console.log(`   🌐 结果核心双语信息: 中文="${resultCore._dualLanguage.chinese}" 外文="${resultCore._dualLanguage.foreign}"`);
    }

    // 智能长度检查:CJK字符2个即有效,英文需要3个
    function isValidCore(core) {
      if (!core) return false;
      const coreStr = core.toString();
      const hasCJK = /[\u4e00-\u9fa5\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/.test(coreStr);
      return hasCJK ? coreStr.length >= 2 : coreStr.length >= 3;
    }

    if (!isValidCore(searchCore) || !isValidCore(resultCore)) {
      console.log(`   ❌ 核心标题无效,跳过`);
      return 0;
    }

    // 检查是否有关键词直接匹配(排除翻译组等无关词汇)
    const keywordMatch = checkKeywordMatch(searchCore, resultCore);
    if (keywordMatch > 0) {
      console.log(`   ✅ 关键词匹配度: ${(keywordMatch * 100).toFixed(1)}%`);
      return keywordMatch;
    }

    // 计算核心作品名的相似度
    const similarity = calculateCoreWorkSimilarity(searchCore, resultCore);
    console.log(`   ✅ 核心作品相似度: ${(similarity * 100).toFixed(1)}%`);

    return similarity;
  }

  /*** 目录识别功能 ***/
  async function searchSeriesDirectory(seriesTitle) {
    if (!seriesTitle) {
      console.log('❌ 搜索失败: 标题为空');
      return [];
    }

    console.log('🔍 开始搜索系列目录:', seriesTitle);

    // 动态获取当前帖子所属版块fid(30/37等)
    const currentForumId = detectForumId();
    const forumName = getForumName(currentForumId);
    console.log(`🎯 搜索限定在: ${forumName} (fid=${currentForumId})`);

    // 第一步:智能提取搜索关键词
    function generateSmartSearchTerms(title) {
      console.log('🎯 智能分析标题:', title);
      const searchTerms = new Set();

      // 策略1: 提取【】中的完整作品名(最高优先级)
      const titleBrackets = title.match(/【([^】]*[^汉化组个人翻译Sub字幕工作室提灯喵社团][^】]*)】/g);
      if (titleBrackets) {
        titleBrackets.forEach(bracket => {
          const content = bracket.slice(1, -1).trim();
          if (content.length >= 6 && content.length <= 25) {
            // 完整作品名
            searchTerms.add(content);
            console.log(`✅ 完整作品名: "${content}"`);

            // 如果作品名超过12字,也添加前8字的版本
            if (content.length > 12) {
              const shortVersion = content.substring(0, 8);
              searchTerms.add(shortVersion);
              console.log(`✅ 短版本作品名: "${shortVersion}"`);
            }
          }
        });
      }

      // 策略2: 提取最长连续中文片段
      const chineseSegments = title.match(/[\u4e00-\u9fa5]{4,}/g);
      if (chineseSegments) {
        // 按长度排序,取最长的2个
        const sortedSegments = chineseSegments
          .filter(seg => seg.length >= 4 && seg.length <= 15)
          .filter(seg => !isNoiseText(seg))
          .sort((a, b) => b.length - a.length)
          .slice(0, 2);

        sortedSegments.forEach(seg => {
          searchTerms.add(seg);
          console.log(`✅ 中文片段: "${seg}"`);
        });
      }

      // 策略3: 如果前面没找到好的关键词,尝试日文片段
      if (searchTerms.size === 0) {
        const japaneseSegments = title.match(/[\u3040-\u309F\u30A0-\u30FF]{4,}/g);
        if (japaneseSegments) {
          const longestJapanese = japaneseSegments
            .sort((a, b) => b.length - a.length)[0];
          if (longestJapanese && longestJapanese.length >= 4) {
            searchTerms.add(longestJapanese.substring(0, 8));
            console.log(`✅ 日文片段: "${longestJapanese.substring(0, 8)}"`);
          }
        }
      }

      // 转换为数组并限制数量
      const result = Array.from(searchTerms).slice(0, 3);
      console.log('🎯 最终搜索关键词:', result);
      return result;
    }

    // 优化关键词提取:智能提取作品名
    function extractWorkName(title) {
      // 先移除未翻译标签
      let cleanTitle = title.replace(/^\[未翻译\]\s*|\[生肉\]\s*|\[RAW\]\s*/i, '');

      // 智能提取括号中的标题信息
      const bracketContents = [];

      // 提取【】中的内容,区分汉化组和标题
      const doubleBrackets = cleanTitle.match(/【([^】]+)】/g);
      if (doubleBrackets) {
        doubleBrackets.forEach(match => {
          const content = match.slice(1, -1);
          // 如果包含"汉化"、"组"、"个人"等关键词,认为是汉化组信息
          if (!/汉化|组|个人|翻译|Sub|字幕|工作室/i.test(content)) {
            bracketContents.push(content);
          }
        });
      }

      // 提取[]中的标题内容(排除明显的作者名)
      const squareBrackets = cleanTitle.match(/\[([^\]]+)\]/g);
      if (squareBrackets) {
        squareBrackets.forEach(match => {
          const content = match.slice(1, -1);
          // 排除明显的作者名(通常较短且不含中文标点)
          if (content.length > 3 && !/^[A-Za-z0-9\s]+$/.test(content)) {
            bracketContents.push(content);
          }
        });
      }

      // 去除所有括号和汉化组信息后的主体内容
      let mainTitle = cleanTitle
        .replace(/【[^】]*汉化[^】]*】/gi, '')  // 移除汉化组信息
        .replace(/【[^】]*组[^】]*】/gi, '')    // 秮除包含"组"的信息
        .replace(/【[^】]*个人[^】]*】/gi, '')  // 移除个人翻译信息
        .replace(/\[[^\]]*\]/g, '')           // 秘除所有方括号内容
        .replace(/【[^】]*】/g, '')            // 秮除剩余的双括号内容
        .trim();

      // 去除章节信息
      mainTitle = mainTitle.replace(/\d{1,3}(?:\.\d+)?\s*[话話章节節回卷篇部].*$/i, '');
      mainTitle = mainTitle.replace(/第\d+[话話章节節回卷篇部].*$/i, '');

      // 清理标点和空格
      mainTitle = mainTitle.replace(/[、,,。!?!?;;::\s]+$/, '').trim();

      // 生成候选标题列表
      const candidates = [];

      // 1. 主标题
      if (mainTitle) {
        candidates.push(mainTitle);
      }

      // 2. 括号中的标题内容
      bracketContents.forEach(content => {
        if (content.length > 3) {
          candidates.push(content);
        }
      });

      // 3. 如果主标题包含日文,尝试提取中文部分
      if (mainTitle && /[\u3040-\u309F\u30A0-\u30FF]/.test(mainTitle)) {
        const chinesePart = mainTitle.replace(/[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]*[\u3040-\u309F\u30A0-\u30FF]+[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]*/g, '').trim();
        if (chinesePart && chinesePart.length > 2) {
          candidates.push(chinesePart);
        }
      }

      // 返回最长的候选标题
      const bestCandidate = candidates.reduce((longest, current) =>
        current.length > longest.length ? current : longest, '');

      return bestCandidate || mainTitle || cleanTitle;
    }

    // 生成搜索关键词 - 智能分词策略,优先中文,限制长度,最多2个搜索词
    function generateSearchTerms(title) {
      console.log('🎯 分析标题:', title);

      // ✨ 优先使用 extractCoreWorkName 提取核心作品名
      const coreWorkName = extractCoreWorkName(title);
      if (coreWorkName && coreWorkName.length >= 2) {
        console.log('✅ 使用核心作品名作为搜索词:', coreWorkName);

        const searchTerms = [];

        // 🔍 检查是否从 extractCoreWorkName 返回了双语信息
        const coreWorkNameStr = coreWorkName.toString ? coreWorkName.toString() : String(coreWorkName);
        let mainTerm = coreWorkNameStr.replace(/[!!??]+$/, '').trim();
        let alternateTerm = null;

        if (coreWorkName._dualLanguage) {
          console.log('🌐 检测到核心提取的双语信息:', coreWorkName._dualLanguage);
          const chineseTerm = coreWorkName._dualLanguage.chinese.replace(/[!!??]+$/, '').trim();
          const foreignTerm = coreWorkName._dualLanguage.foreign;

          // 🎯 优先原文,再中文
          mainTerm = foreignTerm;  // 外文作主词 (优先搜索)
          alternateTerm = chineseTerm;  // 中文作备选 (次优搜索)

          // 设置双语搜索标记
          searchTerms._dualLanguageSearch = {
            chinese: chineseTerm,
            foreign: foreignTerm,
            priority: 'foreign'
          };

          console.log(`🌐 使用双语搜索 (优先外文): 外文"${foreignTerm}" / 中文"${chineseTerm}"`);
        }

        // 🔧 清理主词的标点符号
        mainTerm = mainTerm.replace(/[!!??]+$/, '').trim();

        // 🔧 特殊处理:英文开头+中文章节标题的情况
        const englishPrefixMatch = coreWorkNameStr.match(/^([A-Za-z*]+)(.*)$/);
        if (englishPrefixMatch && englishPrefixMatch[2]) {
          const englishPart = englishPrefixMatch[1];
          const chinesePart = englishPrefixMatch[2];

          // 检查中文部分是否包含章节描述词
          if (/^[\u4e00-\u9fa5]/.test(chinesePart) && /[能和你在一起就好|希望|梦想|故事|物语|生活|日常|短篇|合同志]/.test(chinesePart)) {
            console.log(`🎯 检测到英文+中文章节标题模式,优先英文: "${englishPart}"`);
            mainTerm = englishPart;
            // 不设置 alternateTerm,只使用英文部分
          }
        }

        // 如果已经有双语信息,跳过其他处理
        if (!coreWorkName._dualLanguage) {
          const parenMatch = coreWorkNameStr.match(/^([^((]+)[((]([^))]+)[))]/);
          if (parenMatch) {
          const part1 = parenMatch[1].trim();
          const part2 = parenMatch[2].trim();
          const hasChinese1 = /[\u4e00-\u9fa5]/.test(part1);
          const hasChinese2 = /[\u4e00-\u9fa5]/.test(part2);

          if (!hasChinese1 && hasChinese2) {
            // 英文(中文) - 主词用英文,备选用中文
            mainTerm = part1.replace(/[\d\.]+$/, '').trim();
            alternateTerm = part2.replace(/[\d\.]+$/, '').trim();
            console.log(`🔄 英文主词+中文备选: "${mainTerm}" / "${alternateTerm}"`);

            // 设置双语搜索标记
            searchTerms._dualLanguageSearch = {
              chinese: alternateTerm,
              foreign: mainTerm
            };
          } else if (hasChinese1 && !hasChinese2) {
            // 中文(英文) - 主词用中文,备选用英文
            mainTerm = part1;
            alternateTerm = part2;
            console.log(`🔄 中文主词+英文备选: "${mainTerm}" / "${alternateTerm}"`);

            // 设置双语搜索标记
            searchTerms._dualLanguageSearch = {
              chinese: mainTerm,
              foreign: alternateTerm
            };
          } else if (hasChinese1 && hasChinese2) {
            // 日文(中文) - 优先中文
            mainTerm = part2;
            alternateTerm = part1;
            console.log(`🔄 中文主词+日文备选: "${mainTerm}" / "${alternateTerm}"`);

            // 设置双语搜索标记
            searchTerms._dualLanguageSearch = {
              chinese: mainTerm,
              foreign: alternateTerm
            };
          }
        }

        // 处理各种分隔符的对照翻译:/ | () ()
        else {
          let parts = null;
          let separator = '';

          // 检测分隔符并拆分
          if (coreWorkNameStr.includes('/')) {
            parts = coreWorkNameStr.split('/').map(p => p.trim());
            separator = '/';
          } else if (coreWorkNameStr.includes('|')) {
            parts = coreWorkNameStr.split('|').map(p => p.trim());
            separator = '|';
          }

          if (parts && parts.length === 2) {
            const [part1, part2] = parts;
            const hasChinese1 = /[\u4e00-\u9fa5]/.test(part1);
            const hasChinese2 = /[\u4e00-\u9fa5]/.test(part2);
            const hasJapanese1 = /[\u3040-\u309F\u30A0-\u30FF]/.test(part1);
            const hasJapanese2 = /[\u3040-\u309F\u30A0-\u30FF]/.test(part2);
            const hasEnglish1 = /[A-Za-z]/.test(part1);
            const hasEnglish2 = /[A-Za-z]/.test(part2);

            // 判断语言类型 - 修复语言检测逻辑
            // 主要看字符比例,而不是严格排他性
            const chineseRatio1 = (part1.match(/[\u4e00-\u9fa5]/g) || []).length / part1.length;
            const chineseRatio2 = (part2.match(/[\u4e00-\u9fa5]/g) || []).length / part2.length;
            const japaneseRatio1 = (part1.match(/[\u3040-\u309F\u30A0-\u30FF]/g) || []).length / part1.length;
            const japaneseRatio2 = (part2.match(/[\u3040-\u309F\u30A0-\u30FF]/g) || []).length / part2.length;

            const isChinese1 = chineseRatio1 > 0.6 && japaneseRatio1 < 0.3;
            const isChinese2 = chineseRatio2 > 0.6 && japaneseRatio2 < 0.3;
            const isJapanese1 = japaneseRatio1 > 0.3 || (hasJapanese1 && chineseRatio1 < 0.6);
            const isJapanese2 = japaneseRatio2 > 0.3 || (hasJapanese2 && chineseRatio2 < 0.6);
            const isEnglish1 = hasEnglish1 && !hasChinese1 && !hasJapanese1;
            const isEnglish2 = hasEnglish2 && !hasChinese2 && !hasJapanese2;
            const isForeign1 = isJapanese1 || isEnglish1;
            const isForeign2 = isJapanese2 || isEnglish2;

            console.log(`🔍 语言检测结果: part1="${part1}" (中文:${chineseRatio1.toFixed(2)} 日文:${japaneseRatio1.toFixed(2)}) part2="${part2}" (中文:${chineseRatio2.toFixed(2)} 日文:${japaneseRatio2.toFixed(2)})`);
            console.log(`🔍 判定结果: part1=${isChinese1?'中文':isForeign1?'外文':'混合'} part2=${isChinese2?'中文':isForeign2?'外文':'混合'}`);

            // 策略1:中外文对照 → 分别提取作为双语搜索 (优先原文)
            if ((isChinese1 && isForeign2) || (isForeign1 && isChinese2)) {
              const chineseTerm = isChinese1 ? part1 : part2;
              const foreignTerm = isForeign1 ? part1 : part2;
              console.log(`🌐 ${separator}分隔-中外对照: 外文"${foreignTerm}" / 中文"${chineseTerm}"`);

              // 🎯 优先原文,再中文 - 按用户要求调整搜索顺序
              mainTerm = foreignTerm;  // 外文作主词 (优先搜索)
              alternateTerm = chineseTerm;  // 中文作备选 (次优搜索)

              // 标记为需要双语搜索策略
              searchTerms._dualLanguageSearch = {
                chinese: chineseTerm,
                foreign: foreignTerm,
                priority: 'foreign'  // 标记优先搜索外文
              };
            }
            // 策略2:日中对照 → 优先日文原文
            else if ((isJapanese1 && isChinese2) || (isChinese1 && isJapanese2)) {
              const chineseTerm = isChinese1 ? part1 : part2;
              const japaneseTerm = isJapanese1 ? part1 : part2;
              console.log(`🌐 ${separator}分隔-日中对照: 日文"${japaneseTerm}" / 中文"${chineseTerm}"`);

              // 🎯 优先日文原文,再中文
              mainTerm = japaneseTerm;  // 日文作主词 (优先搜索)
              alternateTerm = chineseTerm;  // 中文作备选 (次优搜索)

              // 标记为需要双语搜索策略
              searchTerms._dualLanguageSearch = {
                chinese: chineseTerm,
                foreign: japaneseTerm,
                priority: 'foreign'  // 标记优先搜索外文
              };
            }
            // 策略3:两组都是中文 → 优先取前面的词
            else if (isChinese1 && isChinese2) {
              mainTerm = part1;
              alternateTerm = part2;
              console.log(`🀄 ${separator}分隔-双中文,优先前词: "${mainTerm}" / "${alternateTerm}"`);
            }
            // 策略4:其他情况 → 选择较长的
            else {
              mainTerm = part1.length >= part2.length ? part1 : part2;
              alternateTerm = part1.length >= part2.length ? part2 : part1;
              console.log(`🔧 ${separator}分隔-选长词: "${mainTerm}" / "${alternateTerm}"`);
            }
          }
        }
        } // 关闭 if (!coreWorkName._dualLanguage)

        // 🎯 添加主搜索词 - 支持2字中文
        if (mainTerm && (mainTerm.length >= 2)) {
          // 对中文降低长度要求到2字,其他语言保持3字符
          const hasChinese = /[\u4e00-\u9fa5]/.test(mainTerm);
          if (hasChinese && mainTerm.length >= 2) {
            searchTerms.push(mainTerm);
          } else if (!hasChinese && mainTerm.length >= 3) {
            searchTerms.push(mainTerm);
          }
        }

        // 🎯 添加备选搜索词 - 支持2字中文
        if (alternateTerm && (alternateTerm.length >= 2)) {
          // 对中文降低长度要求到2字,其他语言保持3字符
          const hasChinese = /[\u4e00-\u9fa5]/.test(alternateTerm);
          if (hasChinese && alternateTerm.length >= 2) {
            searchTerms.push(alternateTerm);
          } else if (!hasChinese && alternateTerm.length >= 3) {
            searchTerms.push(alternateTerm);
          }
        }

        // 检查是否为纯英文作品名
        const isPureEnglish = /^[A-Za-z\s]+$/.test(coreWorkNameStr);

        if (isPureEnglish) {
          // 英文作品名:检查是否包含多个单词
          const words = coreWorkNameStr.trim().split(/\s+/);

          if (words.length > 1) {
            // ✨ 策略改变:直接使用带空格的原始版本搜索
            // 论坛搜索会把空格当作"AND"条件,能找到包含所有词的结果
            console.log(`✅ 英文多词(保持空格): "${coreWorkNameStr}"`);

            // ✨ 移除备选首词逻辑,避免过度分割
            // 保持完整的英文标题,不添加单词片段
            console.log(`🚫 跳过首词分割,保持完整标题`);
          } else {
            // 单个英文单词
            console.log(`✅ 单个英文词: "${coreWorkNameStr}"`);
          }

          // 纯英文标题不启用双语搜索
          searchTerms._pureEnglish = true;
        } else {
          // 中文/日文/混合作品名
          if (!coreWorkName._dualLanguage) {
            // 只有在没有双语信息时才添加原始作品名
            searchTerms.push(coreWorkNameStr);
          }

          // ✨ 如果原标题包含括号中的英文,也添加为双语搜索
          const bracketEnglish = title.match(/[((]([A-Za-z\s!?]+)[))]/);
          if (bracketEnglish) {
            const englishName = bracketEnglish[1].trim().replace(/[!?]+$/, '');
            if (englishName.length >= 3) {
              // 🎯 优先英文,再中文
              searchTerms.length = 0; // 清空原有的
              searchTerms.push(englishName);  // 英文优先
              searchTerms.push(coreWorkNameStr);  // 中文其次

              // 设置双语搜索标记
              searchTerms._dualLanguageSearch = {
                chinese: coreWorkNameStr,
                foreign: englishName,
                priority: 'foreign'
              };

              console.log(`🌐 括号中英对照 (优先英文): 英文"${englishName}" / 中文"${coreWorkNameStr}"`);
            }
          }

          // 优化长度控制:主词过长(>12字符)自动生成6字符短版本
          if (coreWorkNameStr.length > 12 && searchTerms.length === 1) {
            const shortVersion = coreWorkNameStr.substring(0, 6);
            // 确保短版本不会太短造成搜索泛化(至少4个有效字符)
            if (shortVersion.length >= 4 && shortVersion.replace(/\s+/g, '').length >= 3) {
              searchTerms.push(shortVersion);
              console.log('✅ 主词过长,添加6字符短版本作为备选:', shortVersion);
            }
          }
        }

        // 去重处理并保留元信息
        const uniqueTerms = Array.from(new Set(searchTerms.filter(Boolean)));
        if (searchTerms._dualLanguageSearch) uniqueTerms._dualLanguageSearch = searchTerms._dualLanguageSearch;
        if (searchTerms._pureEnglish) uniqueTerms._pureEnglish = searchTerms._pureEnglish;

        console.log('🎯 最终搜索关键词:', uniqueTerms);
        return uniqueTerms.slice(0, 2);
      }

      // 如果核心提取失败,使用原有的备用逻辑
      console.log('⚠️ 核心作品名提取失败,使用备用逻辑');

      // 移除常见的噪音标签
      let cleanTitle = title
        .replace(/^\[未翻译\]\s*|\[生肉\]\s*|\[RAW\]\s*/i, '')
        .replace(/【[^】]*】/g, (match) => {
          const content = match.slice(1, -1);
          return isNoiseText(content) ? '' : match;
        });

      const candidates = {
        chinese: [], // 中文标题候选
        japanese: [], // 日文标题候选
        mixed: [] // 混合标题候选
      };

      // 策略1: 提取【】中的核心作品名
      const doubleBrackets = cleanTitle.match(/【([^】]+)】/g);
      if (doubleBrackets) {
        doubleBrackets.forEach(match => {
          const content = match.slice(1, -1).trim();
          // 统一使用 isNoiseText 过滤
          if (content.length >= 3 && content.length <= 20 && !isNoiseText(content)) {

            // 判断内容类型
            const hasChinese = /[\u4e00-\u9fa5]/.test(content);
            const hasJapanese = /[\u3040-\u309F\u30A0-\u30FF]/.test(content);

            if (hasChinese && hasJapanese) {
              candidates.mixed.push(content);
            } else if (hasChinese) {
              candidates.chinese.push(content);
            } else if (hasJapanese) {
              candidates.japanese.push(content);
            }

            console.log(`✅ 找到【】中的标题: "${content}" (类型: ${hasChinese ? '中' : ''}${hasJapanese ? '日' : ''})`);
          }
        });
      }

      // 策略2: 提取最长的纯中文片段(统一使用 isNoiseText 过滤)
      const chineseMatches = cleanTitle.match(/[\u4e00-\u9fa5]{3,}/g);
      if (chineseMatches) {
        chineseMatches
          .filter(match => match.length >= 3)
          .filter(match => !isNoiseText(match))
          .forEach(match => {
            candidates.chinese.push(match);
          });
      }

      // 策略3: 提取最长的日文片段
      const japaneseMatches = cleanTitle.match(/[\u3040-\u309F\u30A0-\u30FF]{3,}/g);
      if (japaneseMatches) {
        japaneseMatches
          .filter(match => match.length >= 3)
          .forEach(match => {
            candidates.japanese.push(match);
          });
      }

      // 智能选择搜索词
      const finalTerms = [];

      // 优先级1: 中文标题
      if (candidates.chinese.length > 0) {
        const bestChinese = candidates.chinese
          .sort((a, b) => b.length - a.length)[0];

        // 如果中文标题超过12字,截取前6字
        let searchTerm = bestChinese;
        if (searchTerm.length > 12) {
          searchTerm = searchTerm.substring(0, 6);
          console.log(`🔤 中文标题过长,截取前6字: "${bestChinese}" -> "${searchTerm}"`);
        }
        finalTerms.push(searchTerm);
        console.log('✅ 优先使用中文标题:', searchTerm);
      }

      // 优先级2: 如果没有中文标题,使用日文标题
      if (finalTerms.length === 0 && candidates.japanese.length > 0) {
        const bestJapanese = candidates.japanese
          .sort((a, b) => b.length - a.length)[0];

        // 如果日文标题超过12字,截取前6字
        let searchTerm = bestJapanese;
        if (searchTerm.length > 12) {
          searchTerm = searchTerm.substring(0, 6);
          console.log(`🔤 日文标题过长,截取前6字: "${bestJapanese}" -> "${searchTerm}"`);
        }
        finalTerms.push(searchTerm);
        console.log('✅ 使用日文标题:', searchTerm);
      }

      // 优先级3: 混合标
      if (finalTerms.length === 0 && candidates.mixed.length > 0) {
        const bestMixed = candidates.mixed
          .sort((a, b) => b.length - a.length)[0];

        // 尝试提取中文部分
        const chinesePart = bestMixed.match(/[\u4e00-\u9fa5]+/g);
        if (chinesePart && chinesePart.length > 0) {
          let searchTerm = chinesePart.join('');
          if (searchTerm.length > 12) {
            searchTerm = searchTerm.substring(0, 6);
          }
          finalTerms.push(searchTerm);
          console.log('✅ 从混合标题提取中文部分:', searchTerm);
        } else {
          let searchTerm = bestMixed;
          if (searchTerm.length > 12) {
            searchTerm = searchTerm.substring(0, 6);
          }
          finalTerms.push(searchTerm);
          console.log('✅ 使用混合标题:', searchTerm);
        }
      }

      // 兜底策略:如果都没找到,使用清理后的标题
      if (finalTerms.length === 0) {
        let baseTitle = cleanTitle
          .replace(/第?\d+(?:\.\d+)?\s*[话話章节節回卷篇期].*$/i, '')
          .replace(/\d+(?:\.\d+)?\s*$/, '')
          .replace(/[【】\[\]()()]/g, ' ')
          .replace(/[、,,。!?!?;;::\s]+/g, ' ')
          .trim();

        if (baseTitle && baseTitle.length >= 3) {
          // 截取处理
          if (baseTitle.length > 10) {
            baseTitle = baseTitle.substring(0, 6);
            console.log(`🔤 兜底标题过长,截取前6字: "${baseTitle}"`);
          }
          finalTerms.push(baseTitle);
          console.log('✅ 使用清理后的标题:', baseTitle);
        }
      }

      // 可选的第二个搜索词:如果第一个词是从长标题截取的,可以尝试更短的核心词
      if (finalTerms.length === 1 && finalTerms[0].length >= 5) {
        const firstTerm = finalTerms[0];
        // 尝试提取更短的核心词(3-4字)
        if (firstTerm.length > 4) {
          const shorterTerm = firstTerm.substring(0, 4);
          // 避免重复添加
          if (shorterTerm !== firstTerm) {
            finalTerms.push(shorterTerm);
            console.log('✅ 添加更短的核心词作为备选:', shorterTerm);
          }
        }
      }

      // 去重处理
      const uniqueTerms = Array.from(new Set(finalTerms.filter(Boolean)));

      console.log('🎯 最终搜索关键词:', uniqueTerms);
      return uniqueTerms.slice(0, 2); // 最多返回2个搜索词
    }

    // 🌐 双语搜索辅助函数:执行单次搜索并返回结果数量
    async function performSingleSearch(keyword, forumId) {
      if (!keyword) return [];

      try {
        console.log(`🔎 单次搜索测试: "${keyword}"`);

        // 获取搜索页面和FormHash
        const searchPageResponse = await fetch('https://bbs.yamibo.com/search.php', {
          method: 'GET',
          credentials: 'include',
          headers: {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
            'Referer': window.location.href,
            'User-Agent': navigator.userAgent
          }
        });

        let formHash = '';
        if (searchPageResponse.ok) {
          const searchPageHtml = await searchPageResponse.text();
          const formHashMatch = searchPageHtml.match(/name="formhash"\s+value="([^"]+)"/);
          if (formHashMatch) formHash = formHashMatch[1];
        }

        await new Promise(resolve => setTimeout(resolve, 1500)); // 防止过快请求

        // 执行搜索
        const formData = new FormData();
        formData.append('formhash', formHash);
        formData.append('srchtxt', keyword);
        if (forumId) formData.append('srchfid[]', forumId);
        formData.append('orderby', 'dateline');
        formData.append('ascdesc', 'desc');
        formData.append('searchsubmit', 'yes');

        const response = await fetch('https://bbs.yamibo.com/search.php?mod=forum', {
          method: 'POST',
          credentials: 'include',
          headers: {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Origin': 'https://bbs.yamibo.com',
            'Referer': 'https://bbs.yamibo.com/search.php',
            'User-Agent': navigator.userAgent
          },
          body: formData
        });

        if (response.ok) {
          const html = await response.text();
          const parser = new DOMParser();
          const doc = parser.parseFromString(html, 'text/html');

          // 提取搜索结果
          const results = [];
          const threadElements = doc.querySelectorAll('#threadlisttableid tbody[id^="normalthread_"]');

          threadElements.forEach(thread => {
            const titleElement = thread.querySelector('th a[href*="thread-"]');
            if (titleElement) {
              results.push({
                title: titleElement.textContent.trim(),
                url: titleElement.href
              });
            }
          });

          console.log(`📊 "${keyword}" 搜索到 ${results.length} 个结果`);
          return results;
        }

        return [];
      } catch (error) {
        console.warn(`⚠️ 单次搜索"${keyword}"失败:`, error);
        return [];
      }
    }

    // 生成搜索关键词
    let searchTerms = generateSearchTerms(seriesTitle);

    // 检查是否有双语搜索标记(仅在真正的双语对照时启用)
    if (searchTerms && searchTerms._dualLanguageSearch && !searchTerms._pureEnglish) {
      const { chinese, foreign } = searchTerms._dualLanguageSearch;
      console.log(`🌐 检测到中外文对照,启用双语搜索: 中文"${chinese}" vs 外文"${foreign}"`);

      try {
        // 分别搜索中文和外文,比较结果数量
        const chineseResults = await performSingleSearch(chinese, currentForumId);
        const foreignResults = await performSingleSearch(foreign, currentForumId);

        console.log(`📊 搜索结果对比: 中文"${chinese}"=${chineseResults.length}个 / 外文"${foreign}"=${foreignResults.length}个`);

        // 根据搜索结果优化搜索词顺序
        if (foreignResults.length > chineseResults.length) {
          console.log(`✅ 外文搜索结果更多,优先使用外文关键词: "${foreign}"`);
          searchTerms = [foreign, chinese];
        } else {
          console.log(`✅ 中文搜索结果更优,优先使用中文关键词: "${chinese}"`);
          searchTerms = [chinese, foreign];
        }

        delete searchTerms._dualLanguageSearch;
        console.log('🎯 双语搜索优化后关键词:', searchTerms);
      } catch (error) {
        console.warn('⚠️ 双语搜索比较失败,使用默认策略:', error);
        delete searchTerms._dualLanguageSearch;
      }
    }

    if (!searchTerms || searchTerms.length === 0) {
      searchTerms = generateSmartSearchTerms(seriesTitle);
      console.log('📝 使用传统搜索逻辑生成关键词:', searchTerms);
    }

    console.log('📝 使用搜索方式获取完整系列目录...');

    const searchResults = new Map();

    try {
      const cacheKey = seriesTitle;
      if (searchCache.has(cacheKey)) {
        console.log('📦 使用内存缓存结果:', searchCache.get(cacheKey).length, '个章节');
        return searchCache.get(cacheKey);
      }

      // 检查持久化缓存
      const seriesKey = generateSeriesKey(seriesTitle);
      const cachedDirectory = getCachedDirectory(seriesKey);
      if (cachedDirectory && cachedDirectory.length > 0) {
        console.log('📦 使用持久化缓存结果:', cachedDirectory.length, '个章节');
        searchCache.set(cacheKey, cachedDirectory);
        return cachedDirectory;
      }



      // 检测503错误频率,如果连续失败则停止搜索
      console.log('⚠️ 注意:论坛有搜索频率限制,将串行搜索以避免503错误');

      let consecutiveFailures = 0;
      const maxFailures = 1; // 减少到1次失败后就切换策略
      let searchBlocked = false;

      for (let i = 0; i < Math.min(searchTerms.length, 2); i++) { // 限制最多2次搜索
        const searchTerm = searchTerms[i];

        // 如果连续失败次数过多,停止搜索
        if (consecutiveFailures >= maxFailures) {
          console.log(`❌ 连续失败 ${maxFailures} 次,停止搜索以避免被论坛限制`);
          break;
        }

        // ✅ 如果已经找到足够的结果,跳过后续搜索
        if (searchResults.size > 0) {
          console.log(`✅ 已找到 ${searchResults.size} 个结果,跳过后续搜索`);
          break;
        }

        // 如果不是第一个搜索,等待12秒避免频率限制
        if (i > 0) {
          console.log(`⏱️ 等待12秒后进行下一个搜索 (${i+1}/${Math.min(searchTerms.length, 2)})...`);
          await new Promise(resolve => setTimeout(resolve, 12000));
        }

        try {
          console.log('🔎 当前搜索关键词:', searchTerm);

          console.log('📝 获取搜索页面...');
          const searchPageResponse = await fetch('https://bbs.yamibo.com/search.php', {
            method: 'GET',
            credentials: 'include',
            headers: {
              'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
              'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
              'Cache-Control': 'no-cache',
              'Pragma': 'no-cache',
              'Referer': window.location.href,
              'User-Agent': navigator.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
            }
          });

          let formHash = '';
          if (searchPageResponse.ok) {
            const searchPageHtml = await searchPageResponse.text();
            const formHashMatch = searchPageHtml.match(/name="formhash"\s+value="([^"]+)"/);
            if (formHashMatch) {
              formHash = formHashMatch[1];
              console.log('✅ 获取到新的FormHash:', formHash);
            }
          }

          // 等待一小段时间模拟人工操作
          await new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000));

          // 使用动态获取的fid
          const formData = new FormData();
          formData.append('formhash', formHash);
          formData.append('srchtxt', searchTerm);
          formData.append('srchfid[]', currentForumId); // 动态版块ID
          formData.append('orderby', 'dateline');
          formData.append('ascdesc', 'desc');
          formData.append('searchsubmit', 'yes');

          console.log('🔍 发送搜索请求...');
          console.log('📝 搜索参数详情:', {
            formhash: formHash,
            srchtxt: searchTerm,
            srchfid: currentForumId,
            orderby: 'dateline',
            ascdesc: 'desc'
          });

          // 先尝试GET搜索(模拟手动搜索)
          const encodedSearchTerm = encodeURIComponent(searchTerm);
          const getUrl = `https://bbs.yamibo.com/search.php?mod=forum&srchtxt=${encodedSearchTerm}&formhash=${formHash}&srchfid=${currentForumId}&orderby=dateline&ascdesc=desc&searchsubmit=yes`;
          console.log('🌐 尝试GET搜索URL:', getUrl);

          let response = await fetch(getUrl, {
            method: 'GET',
            credentials: 'include',
            headers: {
              'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
              'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
              'Cache-Control': 'no-cache',
              'Referer': 'https://bbs.yamibo.com/search.php',
              'User-Agent': navigator.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
            }
          });

          // 如果GET失败,回退到POST
          if (!response.ok) {
            console.log('❌ GET搜索失败,尝试POST搜索...');
            response = await fetch('https://bbs.yamibo.com/search.php?mod=forum', {
              method: 'POST',
              credentials: 'include',
              headers: {
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
                'Origin': 'https://bbs.yamibo.com',
                'Referer': 'https://bbs.yamibo.com/search.php',
                'Sec-Fetch-Dest': 'document',
                'Sec-Fetch-Mode': 'navigate',
                'Sec-Fetch-Site': 'same-origin',
                'Sec-Fetch-User': '?1',
                'Upgrade-Insecure-Requests': '1',
                'User-Agent': navigator.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
              },
              body: formData
            });
          } else {
            console.log('✅ GET搜索请求成功');
          }

          if (!response.ok) {
            console.log(`❌ 搜索请求失败: ${response.status} - ${response.statusText}`);
            consecutiveFailures++;

            if (response.status === 503) {
              console.log('⚠️ 搜索服务暂时不可用,可能是论坛维护中');
            }
            continue;
          }

          const html = await response.text();
          if (html.includes('需要登录')) {
            console.log('❌ 需要登录');
            continue;
          }

          const parser = new DOMParser();
          const doc = parser.parseFromString(html, 'text/html');

          // 调试:检查页面标题和主要内容
          const pageTitle = doc.querySelector('title')?.textContent || '无标题';
          console.log('📄 搜索结果页面标题:', pageTitle);

          // 检查是否有搜索结果统计信息
          const resultStats = doc.querySelector('.pg .xi2, .ptm .xi2, .pages .xi2');
          if (resultStats) {
            console.log('📊 搜索统计信息:', resultStats.textContent.trim());
          }

          // 改进的搜索结果提取 - 支持多页和多种选择器
          const pageSearchResults = [];

          // 尝试多种可能的选择器(累积式)
          const selectors = [
            '#threadlist li.pbw h3.xs3 a',                    // 原有主选择器
            '#threadlist ul li.pbw h3.xs3 a',                // 百合会搜索结果的实际结构!
            '#threadlist .slst ul li.pbw h3 a',              // 搜索结果页面的完整路径
            '#threadlist li h3.xs3 a',                       // 简化版本的搜索结果
            '#threadlist .xst',                               // 标准搜索结果
            'a.xst',                                         // 通用搜索结果链接
            '.xst',                                          // 搜索结果标题类
            'a[href*="thread-"]',                            // 包含thread的链接
            'a[href*="tid="]',                               // 包含tid的链接
            '.s.xst',                                        // 另一种搜索结果格式
            '#threadlist tbody .subject a',                  // 表格形式的搜索结果
            '.threadlist .title a',                          // 标题链接
            'h3 a[href*="thread"]',                         // h3中的帖子链接
            'tbody[id^="normalthread"] .xst',                // 论坛标准列表格式
            'tbody[id^="normalthread"] a[href*="thread"]',   // 标准帖子列表中的链接
            '.ptm .xst',                                     // 搜索结果页面格式
            '#threadlisttableid .xst',                       // 帖子列表表格中的标题
            '#threadlisttableid a[href*="thread"]',          // 帖子列表表格中的链接
            '.bl .xst',                                      // 搜索结果中的标题
            'table .subject a',                              // 表格中的主题链接
            '.ptm a[href*="thread"]'                         // 搜索结果中的所有帖子链接
          ];

          let foundResults = false;
          selectors.forEach(selector => {
            const elements = doc.querySelectorAll(selector);
            if (elements.length > 0) {
              console.log(`✅ 选择器 "${selector}" 找到 ${elements.length} 个结果`);
              pageSearchResults.push(...Array.from(elements));
              foundResults = true;
            }
          });

          if (!foundResults) {
            console.log('❌ 所有选择器都未找到结果,打印页面结构用于调试...');

            // 调试信息:检查页面是否包含搜索词
            const bodyText = doc.body?.textContent || '';
            const searchTermLower = searchTerm.toLowerCase();
            const containsSearchTerm = bodyText.toLowerCase().includes(searchTermLower);
            console.log(`🔍 页面是否包含搜索词"${searchTerm}": ${containsSearchTerm}`);

            // 检查页面是否有错误信息
            const errorMsg = doc.querySelector('.error, .alert, .notice');
            if (errorMsg) {
              console.log('⚠️ 页面错误信息:', errorMsg.textContent.trim());
            }

            // 打印页面的主要结构
            const allLinks = doc.querySelectorAll('a[href*="thread"], a[href*="tid="]');
            console.log(`📋 页面中所有thread链接数量: ${allLinks.length}`);
            if (allLinks.length > 0) {
              console.log('📋 前5个thread链接:');
              Array.from(allLinks).slice(0, 5).forEach((link, idx) => {
                console.log(`   ${idx + 1}. "${link.textContent.trim()}" (${link.href})`);
              });
            }

            // 检查是否有特殊的搜索结果结构
            const searchResultContainer = doc.querySelector('#threadlist, #threadlisttableid, .searchresult, .ptm');
            if (searchResultContainer) {
              console.log('📋 找到搜索结果容器:', searchResultContainer.tagName + (searchResultContainer.id ? '#' + searchResultContainer.id : '') + (searchResultContainer.className ? '.' + searchResultContainer.className.replace(/\s+/g, '.') : ''));

              // 检查容器内的链接
              const containerLinks = searchResultContainer.querySelectorAll('a');
              console.log(`📋 容器内链接数量: ${containerLinks.length}`);
            }
          }

          // 去重处理
          const uniqueResults = [];
          const seenUrls = new Set();
          pageSearchResults.forEach(link => {
            const href = link.href;
            if (href && !seenUrls.has(href)) {
              seenUrls.add(href);
              uniqueResults.push(link);
            }
          });

          console.log(`📊 搜索结果汇总: 原始 ${pageSearchResults.length} 个,去重后 ${uniqueResults.length} 个`);

          // 检查是否有下一页,并获取前2页的结果
          const hasNextPage = doc.querySelector('.nxt, .next, a[href*="page=2"]');
          if (hasNextPage && uniqueResults.length > 0) {
            console.log('📄 检测到搜索结果有多页,尝试获取第2页...');

            try {
              await new Promise(resolve => setTimeout(resolve, 2000));

              const currentUrl = new URL(response.url);
              currentUrl.searchParams.set('page', '2');

              const page2Response = await fetch(currentUrl.toString(), {
                method: 'GET',
                credentials: 'include',
                headers: {
                  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                  'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
                  'Referer': response.url,
                  'User-Agent': navigator.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
                }
              });

              if (page2Response.ok) {
                const page2Html = await page2Response.text();
                const page2Doc = new DOMParser().parseFromString(page2Html, 'text/html');

                // 提取第2页结果
                const page2Results = [];
                selectors.forEach(selector => {
                  const elements = page2Doc.querySelectorAll(selector);
                  if (elements.length > 0) {
                    page2Results.push(...Array.from(elements));
                  }
                });

                // 去重并合并第2页结果
                page2Results.forEach(link => {
                  const href = link.href;
                  if (href && !seenUrls.has(href)) {
                    seenUrls.add(href);
                    uniqueResults.push(link);
                  }
                });

                console.log(`✅ 第2页找到额外 ${page2Results.length} 个结果,总计 ${uniqueResults.length} 个`);
              } else {
                console.log('❌ 获取第2页失败:', page2Response.status);
              }
            } catch (error) {
              console.warn('⚠️ 获取第2页时出错:', error.message);
            }
          }

          // 使用去重后的结果
          const finalResults = uniqueResults;

          const currentResults = finalResults.map(link => {
            const originalTitle = link.textContent.trim();
            let url = link.href;

            if (!originalTitle || originalTitle === '快速') return null;

            if (url.startsWith('/')) {
              url = `https://bbs.yamibo.com${url}`;
            } else if (url.startsWith('forum.php')) {
              url = `https://bbs.yamibo.com/${url}`;
            }

            const threadId = url.match(/tid=(\d+)/)?.[1] || url.match(/thread-(\d+)-/)?.[1];
            if (!threadId) return null;

            // ✨ 智能匹配过滤(针对多单词英文搜索优化)
            const hasEnglish = /[A-Za-z]/.test(searchTerm);
            if (hasEnglish && searchTerm.length >= 4) {
              const titleLower = originalTitle.toLowerCase();
              const searchLower = searchTerm.toLowerCase();

              // 对于多单词英文搜索,检查是否包含所有关键单词
              if (searchTerm.includes(' ')) {
                const searchWords = searchTerm.toLowerCase().split(/\s+/).filter(word => word.length > 2);
                const allWordsFound = searchWords.every(word => titleLower.includes(word));

                if (!allWordsFound) {
                  console.log(`   ❌ 多词过滤: "${originalTitle}" 不包含所有关键词 [${searchWords.join(', ')}]`);
                  return null;
                } else {
                  console.log(`   ✅ 多词匹配: "${originalTitle}" 包含所有关键词 [${searchWords.join(', ')}]`);
                }
              } else {
                // 单词搜索,直接检查包含关系
                if (!titleLower.includes(searchLower)) {
                  console.log(`   ❌ 单词过滤: "${originalTitle}" 不包含 "${searchTerm}"`);
                  return null;
                } else {
                  console.log(`   ✅ 单词匹配: "${originalTitle}" 包含 "${searchTerm}"`);
                }
              }
            }

            // 直接使用原始标题进行相似度计算,不要预先清理
            const similarity = calculateSimilarity(seriesTitle, originalTitle);

            return {
              threadId,
              title: originalTitle,
              originalTitle: originalTitle,
              url,
              normalizedTitle: normalizeSeriesTitle(originalTitle),
              similarity,
              searchTerm
            };
          }).filter(Boolean);

          // 合并当前搜索结果
          currentResults.forEach(item => {
            const existing = searchResults.get(item.threadId);
            if (!existing || item.similarity > existing.similarity) {
              searchResults.set(item.threadId, item);
            }
          });

          console.log(`✅ 搜索词 "${searchTerm}" 完成,找到 ${currentResults.length} 个结果`);

          // 详细显示搜索结果
          if (currentResults.length > 0) {
            console.log(`   搜索结果详情:`);
            currentResults.slice(0, 5).forEach((result, idx) => {
              console.log(`     ${idx + 1}. "${result.title}" (相似度: ${(result.similarity * 100).toFixed(1)}%)`);
            });
          } else {
            console.log(`   ⚠️ 搜索词 "${searchTerm}" 未找到任何结果`);

            // 🔄 回退策略:对于多单词英文搜索无结果,立即尝试首个单词搜索
            if (/^[A-Za-z\s]+$/.test(searchTerm) && searchTerm.includes(' ')) {
              const firstWord = searchTerm.split(/\s+/)[0];
              if (firstWord.length >= 4 && !searchTerms.includes(firstWord)) {
                console.log(`🔄 多词英文搜索无结果,立即尝试首词: "${firstWord}"`);

                try {
                  // 等待短暂间隔后搜索首词
                  await new Promise(resolve => setTimeout(resolve, 3000));

                  // 递归调用相同的搜索逻辑(复用现有的表单构建和请求逻辑)
                  const backupFormData = new FormData();
                  backupFormData.append('formhash', formHash);
                  backupFormData.append('srchtxt', firstWord);
                  backupFormData.append('srchfid[]', currentForumId);
                  backupFormData.append('orderby', 'dateline');
                  backupFormData.append('ascdesc', 'desc');
                  backupFormData.append('searchsubmit', 'yes');

                  const backupResponse = await fetch('https://bbs.yamibo.com/search.php?mod=forum', {
                    method: 'POST',
                    credentials: 'include',
                    headers: {
                      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                      'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
                      'Origin': 'https://bbs.yamibo.com',
                      'Referer': 'https://bbs.yamibo.com/search.php',
                      'User-Agent': navigator.userAgent
                    },
                    body: backupFormData
                  });

                  if (backupResponse.ok) {
                    const backupHtml = await backupResponse.text();
                    const backupDoc = new DOMParser().parseFromString(backupHtml, 'text/html');

                    const backupResults = [];
                    const selectors = [
                      '#threadlist li.pbw h3.xs3 a',
                      '#threadlist .xst',
                      'a.xst',
                      'a[href*="thread-"]'
                    ];

                    selectors.forEach(selector => {
                      const elements = backupDoc.querySelectorAll(selector);
                      if (elements.length > 0) {
                        backupResults.push(...Array.from(elements));
                      }
                    });

                    const processedBackupResults = backupResults.map(link => {
                      const originalTitle = link.textContent.trim();
                      let url = link.href;
                      if (!originalTitle || originalTitle === '快速') return null;
                      if (url.startsWith('/')) url = `https://bbs.yamibo.com${url}`;

                      const threadId = url.match(/tid=(\d+)/)?.[1] || url.match(/thread-(\d+)-/)?.[1];
                      if (!threadId) return null;

                      // 对回退结果进行更严格的过滤:必须包含原搜索词的多个关键词
                      const titleLower = originalTitle.toLowerCase();
                      const originalWords = searchTerm.toLowerCase().split(/\s+/).filter(w => w.length > 2);
                      const matchCount = originalWords.filter(word => titleLower.includes(word)).length;

                      // 只保留包含原搜索词至少一半关键词的结果
                      if (matchCount < Math.ceil(originalWords.length / 2)) {
                        return null;
                      }

                      const similarity = calculateSimilarity(seriesTitle, originalTitle);
                      return {
                        threadId,
                        title: originalTitle,
                        originalTitle: originalTitle,
                        url,
                        normalizedTitle: normalizeSeriesTitle(originalTitle),
                        similarity,
                        searchTerm: `${firstWord}(回退)`
                      };
                    }).filter(Boolean);

                    if (processedBackupResults.length > 0) {
                      console.log(`🎯 首词回退搜索找到 ${processedBackupResults.length} 个相关结果`);
                      processedBackupResults.forEach(item => {
                        const existing = searchResults.get(item.threadId);
                        if (!existing || item.similarity > existing.similarity) {
                          searchResults.set(item.threadId, item);
                        }
                      });
                    }
                  }
                } catch (backupError) {
                  console.warn('⚠️ 首词回退搜索失败:', backupError.message);
                }
              }
            }
          }

          consecutiveFailures = 0;

        } catch (error) {
          console.warn(`❌ 搜索词 "${searchTerm}" 失败:`, error.message);
          consecutiveFailures++;
          // 继续下一个搜索词
        }
      }

      // 智能筛选和排序
      let directory = Array.from(searchResults.values())
        .filter(item => item.similarity > 0.7);

      // 简单按发帖时间排序(threadId小的是老帖子,排在前面)
      directory.sort((a, b) => {
        const threadIdA = parseInt(a.threadId);
        const threadIdB = parseInt(b.threadId);
        console.log(`🔄 排序比较: "${a.title}" (ID:${threadIdA}) vs "${b.title}" (ID:${threadIdB})`);
        console.log(`   按threadId排序: ${threadIdA} - ${threadIdB} = ${threadIdA - threadIdB}`);
        return threadIdA - threadIdB;
      });

      console.log(`📊 智能筛选后: 找到 ${directory.length} 个相关章节`);
      directory.forEach(item => {
        const chapterNum = extractChapterNumber(item.title);
        console.log(`   "${item.title}" (章节号: ${chapterNum}, 相似度: ${(item.similarity *  100).toFixed(1)}%)`);
      });

      if (directory.length > 0) {
        // 缓存成功的搜索结果
        searchCache.set(cacheKey, directory);

        // 同时保存到持久化缓存
        const seriesKey = generateSeriesKey(seriesTitle);
        setCachedDirectory(seriesKey, directory);

        return directory;
      }

      console.log('❌ 所有搜索词都未找到相关章节');

      // 只有在第一轮搜索完全失败时才尝试备用策略
      if (directory.length === 0 && consecutiveFailures < maxFailures) {
        console.log('🔄 第一轮搜索无结果,尝试简单备用搜索...');

        // 生成一个简单的备用搜索词
        let backupTerm = '';

        // 优先提取最长的中文片段(过滤噪音词)
        const chineseMatches = seriesTitle.match(/[\u4e00-\u9fa5]{4,}/g);
        if (chineseMatches && chineseMatches.length > 0) {
          // 找到第一个不是噪音的中文片段
          const validChinese = chineseMatches
            .sort((a, b) => b.length - a.length)
            .find(match => !isNoiseText(match));

          if (validChinese) {
            backupTerm = validChinese.substring(0, 5);
            console.log('🔤 备用搜索使用中文片段:', backupTerm);
          } else {
            console.log('❌ 所有中文片段都被判定为噪音,跳过备用搜索');
          }
        } else {
          // 如果没有中文,提取日文片段
          const japaneseMatches = seriesTitle.match(/[\u3040-\u309F\u30A0-\u30FF]{3,}/g);
          if (japaneseMatches && japaneseMatches.length > 0) {
            const validJapanese = japaneseMatches.find(match => !isNoiseText(match));
            if (validJapanese) {
              backupTerm = validJapanese.substring(0, 5);
              console.log('🔤 备用搜索使用日文片段:', backupTerm);
            } else {
              console.log('❌ 所有日文片段都被判定为噪音,跳过备用搜索');
            }
          }
        }

        if (backupTerm) {
          try {
            console.log('🔍 执行备用搜索:', backupTerm);

            // 等待10秒
            await new Promise(resolve => setTimeout(resolve, 10000));

            const formData = new FormData();
            formData.append('formhash', getFormHash() || '');
            formData.append('srchtxt', backupTerm);
            formData.append('srchfid[]', '30');
            formData.append('orderby', 'dateline');
            formData.append('ascdesc', 'desc');
            formData.append('searchsubmit', 'yes');

            const response = await fetch('https://bbs.yamibo.com/search.php?mod=forum', {
              method: 'POST',
              credentials: 'include',
              headers: {
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
                'Referer': 'https://bbs.yamibo.com/search.php',
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
              },
              body: formData
            });

            if (response.ok) {
              const html = await response.text();
              const parser = new DOMParser();
              const doc = parser.parseFromString(html, 'text/html');

              const backupPageResults = [];
              const threadList = doc.querySelectorAll('#threadlist li.pbw h3.xs3 a');
              if (threadList.length > 0) {
                backupPageResults.push(...Array.from(threadList));
              } else {
                backupPageResults.push(...Array.from(doc.querySelectorAll('a.xst, a[href*="thread-"]')));
              }

              const backupResults = backupPageResults.map(link => {
                const title = link.textContent.trim();
                let url = link.href;

                if (!title || title === '快速') return null;

                if (url.startsWith('/')) {
                  url = `https://bbs.yamibo.com${url}`;
                } else if (url.startsWith('forum.php')) {
                  url = `https://bbs.yamibo.com/${url}`;
                }

                const threadId = url.match(/tid=(\d+)/)?.[1] || url.match(/thread-(\d+)-/)?.[1];
                if (!threadId) return null;

                const similarity = calculateSimilarity(seriesTitle, title);

                return {
                  threadId,
                  title,
                  originalTitle: title,
                  url,
                  normalizedTitle: normalizeSeriesTitle(title),
                  similarity,
                  searchTerm: `backup:${backupTerm}`
                };
              }).filter(Boolean);

              backupResults.forEach(item => {
                const existing = searchResults.get(item.threadId);
                if (!existing || item.similarity > existing.similarity) {
                  searchResults.set(item.threadId, item);
                }
              });

              console.log(`✅ 备用搜索找到 ${backupResults.length} 个结果`);

              directory = Array.from(searchResults.values())
                .filter(item => item.similarity > 0.05);

            } else {
              console.log(`❌ 备用搜索失败: ${response.status}`);
            }

          } catch (error) {
            console.warn('❌ 备用搜索失败:', error.message);
          }
        }
      }

      // 如果搜索完全失败
      if (directory.length === 0) {
        console.log('❌ 所有搜索词都未找到相关章节');
      }

      // 最后兜底:提供当前章节作为基础目录
      const currentThreadId = getCurrentThreadId();
      if (directory.length === 0 && currentThreadId) {
        console.log('🔧 提供当前章节作为基础目录');
        return [{
          threadId: currentThreadId,
          title: seriesTitle,
          originalTitle: seriesTitle,
          url: window.location.href,
          normalizedTitle: normalizeSeriesTitle(seriesTitle),
          similarity: 1.0,
          searchTerm: 'current'
        }];
      }

      return directory;

    } catch (error) {
      console.error('❌ 搜索目录失败:', error);
      console.error('错误堆栈:', error.stack);
      return [];
    }
  }



  // 提取章节号(支持小数格式和复杂排序)
function extractChapterNumber(title) {
  if (!title || typeof title !== 'string') return 0;
  // 特殊章节(番外/彩页等)放到最后
  const specialPatterns = [
    /番外|外传|特别篇|SP|特典|彩页|后记|前记|预告|PV|CM/i,
    /extra|special|omake|bonus/i
  ];
  for (const p of specialPatterns) {
    if (p.test(title)) return 99999;
  }

  const patterns = [
    { regex: /(?:第\s*)?(\d+\.\d+)\s*[话話章节節回卷篇期]?/i, priority: 1, type: 'decimal' },
    { regex: /第\s*(\d+)\s*[话話章节節回卷篇]/i, priority: 2, type: 'standard' },
    { regex: /(\d+)\s*[话話章节節]/i, priority: 3, type: 'numbered' },
    { regex: /[\[\((](\d+(?:\.\d+)?)[\]\))]/, priority: 4, type: 'bracketed' },
    { regex: /(?:第\s*)?(\d+)\s*卷/i, priority: 5, type: 'volume' },
    { regex: /\s(\d+(?:\.\d+)?)$/, priority: 6, type: 'trailing' },
    { regex: /^(\d+(?:\.\d+)?)\s/, priority: 7, type: 'leading' },
    { regex: /(\d+(?:\.\d+)?)/, priority: 8, type: 'any' }
  ];

  let bestMatch = null;
  let bestPriority = Infinity;
  for (const p of patterns) {
    const m = title.match(p.regex);
    if (m && p.priority < bestPriority) {
      bestMatch = m[1];
      bestPriority = p.priority;
    }
  }

  if (bestMatch != null) {
    if (bestMatch.includes('.')) {
      // 小数章如 9.5 -> 9500(保持整数比较)
      const v = parseFloat(bestMatch);
      if (!isNaN(v)) return Math.round(v * 1000);
    } else {
      const v = parseInt(bestMatch, 10);
      if (!isNaN(v)) return v;
    }
  }

  // 简单中文数字映射(可扩展)
  const chineseNumbers = {
    '零': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
    '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
    '十一': 11, '十二': 12, '十三': 13, '十四': 14, '十五': 15,
    '十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20
  };
  for (const [ch, num] of Object.entries(chineseNumbers)) {
    if (title.includes(`第${ch}`) || title.includes(`${ch}话`) || title.includes(`${ch}章`)) {
      return num;
    }
  }

  return 0;
}

function getFormHash() {
  if (isReading) {
    console.log('🔑 FormHash: 阅读模式下跳过FormHash');
    return '';
  }
  const formHashInput = document.querySelector('input[name="formhash"]');
  return formHashInput ? formHashInput.value : '';
}

  // 从URL识别帖子所属版块
  function detectForumId(url = window.location.href) {
    // 方式1: 从URL参数中提取 fid
    const fidMatch = url.match(/[?&]fid=(\d+)/);
    if (fidMatch) {
      const fid = fidMatch[1];
      console.log(`📍 从URL参数识别版块: fid=${fid}`);
      return fid;
    }

    // 方式2: 从forum.php路径中提取
    const forumMatch = url.match(/forum-(\d+)-/);
    if (forumMatch) {
      const fid = forumMatch[1];
      console.log(`📍 从forum路径识别版块: fid=${fid}`);
      return fid;
    }

    // 方式3: 从thread URL中推断(访问页面时检查页面元素)
    try {
      const breadcrumbs = document.querySelectorAll('.z a[href*="forum"]');
      const forumIds = [];

      for (const breadcrumb of breadcrumbs) {
        // 尝试从 forum-数字- 格式提取
        const breadcrumbMatch1 = breadcrumb.href.match(/forum-(\d+)-/);
        if (breadcrumbMatch1) {
          const fid = breadcrumbMatch1[1];
          const name = breadcrumb.textContent.trim();
          forumIds.push({ fid, name, element: breadcrumb });
          continue;
        }
        // 尝试从 fid= 参数提取
        const breadcrumbMatch2 = breadcrumb.href.match(/fid=(\d+)/);
        if (breadcrumbMatch2) {
          const fid = breadcrumbMatch2[1];
          const name = breadcrumb.textContent.trim();
          forumIds.push({ fid, name, element: breadcrumb });
        }
      }

      // ✨ 选择最后一个版块(最精确的子版块)
      if (forumIds.length > 0) {
        const lastForum = forumIds[forumIds.length - 1];
        console.log(`📍 从面包屑导航识别版块: fid=${lastForum.fid} (${lastForum.name})`);
        if (forumIds.length > 1) {
          console.log(`   💡 跳过了 ${forumIds.length - 1} 个父级版块,选择最精确的子版块`);
        }
        return lastForum.fid;
      }
    } catch (e) {
      console.log('⚠️ 无法从面包屑导航识别版块:', e.message);
    }

    // 方式4: 从当前页面的返回链接中提取
    try {
      const backLinks = document.querySelectorAll('a[href*="forum"]');
      for (const backLink of backLinks) {
        const linkMatch = backLink.href.match(/fid=(\d+)/);
        if (linkMatch) {
          const fid = linkMatch[1];
          console.log(`📍 从返回链接识别版块: fid=${fid}`);
          return fid;
        }
      }
    } catch (e) {
      // ignore
    }

    // 默认返回中文百合漫画区 (fid=30)
    console.log('📍 无法识别版块,默认使用中文百合漫画区 (fid=30)');
    return '30';
  }

  // 获取版块名称(用于日志显示)
  function getForumName(fid) {
    const forumNames = {
      '30': '中文百合漫画区',
      '37': '百合漫画图源区'
    };
    return forumNames[fid] || `版块${fid}`;
  }

  // 检查两个标题是否包含相似词汇
  function containsSimilarWords(title1, title2) {
    const words1 = title1.toLowerCase().split(/[\s\-_]+/).filter(w => w.length > 1);
    const words2 = title2.toLowerCase().split(/[\s\-_]+/).filter(w => w.length > 1);

    console.log(`🔍 词汇比较: "${title1}" vs "${title2}"`);
    console.log(`   词汇1: [${words1.join(', ')}]`);
    console.log(`   词汇2: [${words2.join(', ')}]`);

    let commonWords = 0;
    const matchedPairs = [];

    for (const word1 of words1) {
      for (const word2 of words2) {
        if (word1.includes(word2) || word2.includes(word1)) {
          commonWords++;
          matchedPairs.push(`"${word1}" ⟷ "${word2}"`);
          break;
        }
      }
    }

    const threshold = Math.min(2, Math.min(words1.length, words2.length));
    const isMatch = commonWords >= threshold;

    console.log(`   匹配词汇: ${matchedPairs.join(', ')}`);
    console.log(`   匹配数量: ${commonWords}/${threshold} (需要: ${threshold})`);
    console.log(`   结果: ${isMatch ? '✅ 匹配' : '❌ 不匹配'}`);

    return isMatch;
  }

  /*** 悬浮按钮 ***/
  const ioniconsModule = document.createElement('script');
  ioniconsModule.type = 'module';
  ioniconsModule.src = 'https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js';
  document.head.appendChild(ioniconsModule);

  const ioniconsNomodule = document.createElement('script');
  ioniconsNomodule.noModule = true;
  ioniconsNomodule.src = 'https://unpkg.com/[email protected]/dist/ionicons/ionicons.js';
  document.head.appendChild(ioniconsNomodule);

  const fontLink = document.createElement('link');
  fontLink.href = 'https://fonts.googleapis.com/css2?family=Poppins&display=swap';
  fontLink.rel = 'stylesheet';
  document.head.appendChild(fontLink);

  const button = document.createElement('button');
  button.id = 'reader-toggle';
  button.innerHTML = `
    <span class="icon"><ion-icon name="book-outline"></ion-icon></span>
    <span class="title">进入阅读模式</span>
  `;

  document.body.appendChild(button);

  GM_addStyle(`
    #reader-toggle {
      position: fixed;
      top: 20%;
      right: 0px;
      transform: translateY(-50%);
      z-index: 99999;
      list-style: none;
      width: 60px;
      height: 60px;
      background: white;
      border-radius: 60px;
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
      cursor: pointer;
      display: flex;
      justify-content: center;
      align-items: center;
      transition: 0.5s;
      border: none;
      font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Source Han Sans SC", sans-serif;
    }

    #reader-toggle::before {
      content: "";
      position: absolute;
      inset: 0;
      border-radius: 40px;
      background: linear-gradient(45deg, #56CCF2, #2F80ED);
      opacity: 0;
      transition: 0.5s;
    }

    #reader-toggle::after {
      content: "";
      position: absolute;
      top: 10px;
      width: 100%;
      height: 100%;
      border-radius: 40px;
      background: linear-gradient(45deg, #56CCF2, #2F80ED);
      filter: blur(15px);
      z-index: -1;
      opacity: 0;
      transition: 0.5s;
    }

    #reader-toggle:hover {
      width: 180px;
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0);
    }

    #reader-toggle:hover::before {
      opacity: 1;
    }

    #reader-toggle:hover::after {
      opacity: 0.5;
    }

    #reader-toggle .icon {
      position: absolute;
      left: 0;
      top: 0;
      width: 60px;
      height: 60px;
      color: #777;
      font-size: 2em;
      transition: 0.5s;
      transition-delay: 0.25s;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    #reader-toggle .icon ion-icon {
      width: 24px;
      height: 24px;
    }

    #reader-toggle:hover .icon {
      transform: scale(0);
      color: #fff;
      transition-delay: 0s;
    }

    #reader-toggle .title {
      position: absolute;
      color: #fff;
      font-size: 1.4em;
      text-transform: uppercase;
      letter-spacing: 0.1em;
      transform: scale(0);
      transition: 0.5s;
      transition-delay: 0s;
    }

    #reader-toggle:hover .title {
      transform: scale(1);
      transition-delay: 0.25s;
    }

    #directory-sidebar {
      position: fixed;
      top: 0;
      right: -400px;
      width: 400px;
      height: 100%;
      background: linear-gradient(145deg, #1e2837 0%, #2a3441 25%, #1f2937 50%, #374151 75%, #1e293b 100%);
      z-index: 2147483647;
      transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      box-shadow: -15px 0 40px rgba(0, 0, 0, 0.4);
      display: flex;
      flex-direction: column;
      color: white;
      backdrop-filter: blur(20px);
      border: none;
    }

    #directory-sidebar.open {
      right: 0;
    }

    /* 遮罩层 */
    #directory-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.6);
      z-index: 2147483646;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.3s ease, visibility 0.3s ease;
      backdrop-filter: blur(2px);
    }

    #directory-overlay.show {
      opacity: 1;
      visibility: visible;
    }

    #directory-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 10px 20px;
      background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(99, 102, 241, 0.1) 100%);
      backdrop-filter: blur(15px);
      border: none;
      position: relative;
    }

    #directory-header::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      height: 1px;
      background: linear-gradient(90deg, transparent 0%, rgba(59, 130, 246, 0.4) 50%, transparent 100%);
    }

    #directory-title {
      font-size: 16px;
      font-weight: 600;
      color: white;
      letter-spacing: 0.5px;
    }

    #directory-close {
      background: rgba(255, 255, 255, 0.1);
      border: none;
      font-size: 18px;
      cursor: pointer;
      color: white;
      padding: 8px;
      border-radius: 8px;
      transition: all 0.2s ease;
      width: 36px;
      height: 36px;
      display: flex;
      align-items: center;
      justify-content: center;
      backdrop-filter: blur(10px);
    }

    #directory-close:hover {
      background: rgba(255, 255, 255, 0.2);
      transform: scale(1.05);
    }

    #directory-close:active {
      transform: scale(0.95);
    }

    #directory-content {
      flex: 1;
      overflow-y: auto;
      padding: 16px 0;
      scrollbar-width: thin;
      scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
    }

    #directory-content::-webkit-scrollbar {
      width: 6px;
    }

    #directory-content::-webkit-scrollbar-track {
      background: transparent;
    }

    #directory-content::-webkit-scrollbar-thumb {
      background: rgba(255, 255, 255, 0.3);
      border-radius: 3px;
    }

    #directory-content::-webkit-scrollbar-thumb:hover {
      background: rgba(255, 255, 255, 0.5);
    }

    #directory-list {
      list-style: none;
      padding: 0 16px;
      margin: 0;
    }

    .directory-item {
      display: block;
      padding: 14px 16px;
      margin: 0 0 8px 0;
      background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%);
      border: none;
      border-radius: 12px;
      text-decoration: none !important;
      color: rgba(255, 255, 255, 0.9);
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      cursor: pointer;
      font-size: 14px;
      font-weight: 500;
      backdrop-filter: blur(10px);
      position: relative;
      overflow: hidden;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    }

    .directory-item::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
      transform: translateX(-100%);
      transition: transform 0.6s;
    }

    .directory-item:hover::before {
      transform: translateX(100%);
    }

    .directory-item:hover {
      background: rgba(255, 255, 255, 0.15);
      transform: translateX(6px);
      color: white;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    }

    .directory-item.current {
      background: linear-gradient(135deg, #FF6B6B, #FF8E53);
      color: white;
      box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
      font-weight: 600;
    }

    .directory-item.current:hover {
      transform: translateX(6px) scale(1.02);
      box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
    }

    .directory-item.mainpost {
      background: linear-gradient(135deg, #4CAF50, #45a049);
      color: white;
    }

    .directory-item.mainpost:hover {
      background: linear-gradient(135deg, #45a049, #3d8b40);
    }

    #directory-loading {
      text-align: center;
      padding: 60px 20px;
      color: rgba(255, 255, 255, 0.7);
      font-size: 16px;
      font-weight: 500;
    }

    #directory-loading::before {
      content: '📚';
      display: block;
      font-size: 32px;
      margin-bottom: 16px;
      animation: pulse 2s infinite;
    }

    @keyframes pulse {
      0%, 100% { opacity: 0.7; }
      50% { opacity: 1; }
    }

    #directory-empty {
      text-align: center;
      padding: 60px 20px;
      color: rgba(255, 255, 255, 0.6);
      font-size: 16px;
    }

    #directory-empty::before {
      content: '📖';
      display: block;
      font-size: 32px;
      margin-bottom: 16px;
      opacity: 0.6;
    }

    /* 目录侧边栏的tooltip样式 */
    #directory-sidebar [data-tooltip]:hover::after {
      content: attr(data-tooltip);
      position: absolute;
      bottom: 100%;
      left: 50%;
      transform: translateX(-50%);
      background: rgba(0, 0, 0, 0.6);
      color: white;
      padding: 8px 12px;
      border-radius: 6px;
      font-size: 12px;
      white-space: nowrap;
      z-index: 2147483650;
      pointer-events: none;
      margin-bottom: 8px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
      opacity: 0;
      animation: tooltipFadeIn 0.2s ease forwards;
    }

    #directory-sidebar [data-tooltip]:hover::before {
      content: '';
      position: absolute;
      bottom: 100%;
      left: 50%;
      transform: translateX(-50%);
      border: 6px solid transparent;
      border-top-color: rgba(0, 0, 0, 0.6);
      z-index: 2147483650;
      pointer-events: none;
      margin-bottom: 2px;
      opacity: 0;
      animation: tooltipFadeIn 0.2s ease forwards;
    }
  `);

  // 添加快捷键:Ctrl+Shift+R 切换阅读模式
  document.addEventListener('keydown', e => {
    if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'r') {
      e.preventDefault();
      if (!isReading) enterReader();
      else exitReader();
    }
  });

  button.addEventListener('click', () => {
    if (!isReading) {
      enterReader();
      setAutoReaderStatus(true); // 开启全局阅读器模式
    } else {
      exitReader();
    }
  });





  /*** 目录功能 ***/
  async function showDirectoryModal() {
    // 创建遮罩层
    const overlay = document.createElement('div');
    overlay.id = 'directory-overlay';

    // 创建侧边栏
    const sidebar = document.createElement('div');
    sidebar.id = 'directory-sidebar';
    sidebar.innerHTML = `
      <div id="directory-header">
        <div id="directory-title">目录</div>
        <button id="directory-close">×</button>
      </div>
      <div id="directory-content">
        <div id="directory-loading">正在搜索相关章节...</div>
        <ul id="directory-list" style="display: none;"></ul>
        <div id="directory-empty" style="display: none;">未找到相关章节</div>
      </div>
    `;

    document.body.appendChild(overlay);
    document.body.appendChild(sidebar);

    // 显示遮罩和侧边栏
    setTimeout(() => {
      overlay.classList.add('show');
      sidebar.classList.add('open');
    }, 10);

    // 关闭按钮事件
    const closeBtn = sidebar.querySelector('#directory-close');
    closeBtn.addEventListener('click', closeSidebar);

    // 点击遮罩关闭
    overlay.addEventListener('click', closeSidebar);

    function closeSidebar() {
      overlay.classList.remove('show');
      sidebar.classList.remove('open');

      setTimeout(() => {
        overlay.remove();
        sidebar.remove();
      }, 300);
    }

    // 开始搜索目录
    loadSeriesDirectory(sidebar);
  }

  async function loadSeriesDirectory(modal) {
    console.log('📖 开始加载系列目录...');

    // 如果已有预加载的目录,直接使用
    if (seriesDirectory.length > 0) {
      console.log('✅ 使用预加载的目录:', seriesDirectory.length, '个章节');
      displayDirectory(modal, seriesDirectory);
      return;
    }

    // 如果预加载目录为空,先检查持久化缓存
    if (originalSeriesTitle || savedThreadTitle) {
      const titleToCheck = originalSeriesTitle || savedThreadTitle;
      const seriesKey = generateSeriesKey(titleToCheck);
      const cachedDirectory = getCachedDirectory(seriesKey);
      if (cachedDirectory && cachedDirectory.length > 0) {
        console.log('✅ 从持久化缓存加载目录:', cachedDirectory.length, '个章节');
        seriesDirectory = cachedDirectory;
        displayDirectory(modal, seriesDirectory);
        return;
      }
    }

    // 尝试多种方式获取页面标题
    let titleElement = null;
    let threadTitle = '';

    console.log('🔍 尝试查找页面标题...');

    // 简化标题提取逻辑
    const h1Element = document.querySelector('h1.ts');
    if (h1Element) {
      const subjectSpan = h1Element.querySelector('#thread_subject, span[id^="thread_subject"]');
      if (subjectSpan) {
        threadTitle = subjectSpan.textContent.trim();
        console.log(`✅ 找到标题: "${threadTitle}"`);
      } else {
        threadTitle = h1Element.textContent.trim();
        console.log(`✅ 找到h1文本: "${threadTitle}"`);
      }
    } else {
      // 兜底:使用原有逻辑
      const titleSelectors = [
        '#thread_subject',
        'span[id^="thread_subject"]',
        '.ts a',
        'h1.ts',
        '.ntn a',
        '.ntn',
        '.bm .mbm h1',
        '.ts'
      ];

      for (const selector of titleSelectors) {
        titleElement = document.querySelector(selector);
        if (titleElement) {
          threadTitle = titleElement.textContent.trim();
          console.log(`✅ 找到标题元素: "${selector}" -> "${threadTitle}"`);
          break;
        } else {
          console.log(`❌ 未找到: "${selector}"`);
        }
      }
    }

    // 在阅读模式下,优先使用原始系列标题进行目录搜索(避免使用错误的标题)
    if (isReading) {
      if (originalSeriesTitle) {
        threadTitle = originalSeriesTitle;
        console.log('🎯 阅读模式下使用原始系列标题:', threadTitle);
      } else if (savedThreadTitle) {
        threadTitle = savedThreadTitle;
        console.log('📦 阅读模式下使用预存标题:', threadTitle);
      }
    } else if (!threadTitle && originalSeriesTitle) {
      threadTitle = originalSeriesTitle;
      console.log('🎯 使用原始系列标题:', threadTitle);
    } else if (!threadTitle && savedThreadTitle) {
      threadTitle = savedThreadTitle;
      console.log('📦 使用预存标题:', threadTitle);
    }
    // 如果还是没找到,尝试从页面title获取
    if (!threadTitle) {
      const pageTitle = document.title;
      console.log('🔍 尝试从页面title获取:', pageTitle);

      // 移除论坛名称等后缀,只保留漫画标题部分
      if (pageTitle.includes(' - ')) {
        threadTitle = pageTitle.split(' - ')[0].trim();
        console.log('📝 从页面title提取:', threadTitle);
      } else {
        threadTitle = pageTitle.replace(/\s*-\s*百合会.*$/, '').trim();
        console.log('📝 清理后的标题:', threadTitle);
      }
    }

    const currentThreadId = getCurrentThreadId();

    console.log('📋 当前页面信息:');
    console.log('   标题元素:', titleElement ? '✅ 找到' : '❌ 未找到');
    console.log('   页面标题:', threadTitle);
    console.log('   线程ID:', currentThreadId);

    // 调试:打印页面中所有可能的标题元素
    if (!titleElement) {
      console.log('🔍 调试:查找所有可能的标题元素...');

      const allH1 = document.querySelectorAll('h1');
      const allSpans = document.querySelectorAll('span[id*="thread"], span[id*="subject"]');
      const allTs = document.querySelectorAll('.ts, .ntn');

      console.log('  - h1元素数量:', allH1.length);
      allH1.forEach((h1, index) => {
        if (index < 5) {
          console.log(`    h1[${index}]: "${h1.textContent.trim().substring(0, 50)}" (class: ${h1.className})`);
        }
      });

      console.log('  - 包含thread/subject的span数量:', allSpans.length);
      allSpans.forEach((span, index) => {
        if (index < 5) {
          console.log(`    span[${index}]: id="${span.id}", text="${span.textContent.trim().substring(0, 50)}"`);
        }
      });

      console.log('  - .ts/.ntn元素数量:', allTs.length);
      allTs.forEach((ts, index) => {
        if (index < 5) {
          console.log(`    ts[${index}]: class="${ts.className}", text="${ts.textContent.trim().substring(0, 50)}"`);
        }
      });
    }

    if (!threadTitle) {
      console.log('❌ 页面标题为空,显示空状态');
      showDirectoryEmpty(modal);
      return;
    }

    try {
      console.log('🔍 开始搜索目录...');
      const directory = await searchSeriesDirectory(threadTitle);

      console.log('📊 搜索结果:', directory.length, '个章节');

      if (directory.length === 0) {
        console.log('❌ 未找到章节,显示空状态');
        showDirectoryEmpty(modal);
        return;
      }

      console.log('✅ 找到章节,开始显示目录界面');
      displayDirectory(modal, directory, threadTitle);

    } catch (error) {
      console.error('❌ 加载目录失败:', error);
      console.error('错误详情:', error.message);
      console.error('错误堆栈:', error.stack);
      showDirectoryEmpty(modal, '加载失败,请稍后重试');
    }
  }

  function displayDirectory(modal, directory, threadTitle = '') {
    // 显示目录
    const loading = modal.querySelector('#directory-loading');
    const list = modal.querySelector('#directory-list');
    const title = modal.querySelector('#directory-title');

    loading.style.display = 'none';
    list.style.display = 'block';

    // 获取页面标题(如果没有提供)
    if (!threadTitle) {
     if (savedThreadTitle) {
       threadTitle = savedThreadTitle;
     } else {
        threadTitle = document.title.split(' - ')[0] || '漫画目录';
     }
    }

    // 简化标题,只显示"目录"
    title.textContent = '目录';

    console.log('📝 目录标题: 目录');

    list.innerHTML = '';

    const currentThreadId = getCurrentThreadId();

    directory.forEach((item, index) => {
      const li = document.createElement('li');
      const link = document.createElement('a');
      link.className = 'directory-item';
      if (item.source === 'mainpost') {
        link.classList.add('mainpost');
      }

      link.href = item.url;

      // 为主楼提取的目录优化显示格式
      if (item.source === 'mainpost') {
        let displayText = item.title;

        // 识别不同的章节格式并优化显示
        if (item.title.match(/^\d{1,2}$/)) {
          displayText = `第${String(item.title).padStart(2, '0')}话`;
        } else if (item.title.match(/^\d+\.\d+$/)) {
          displayText = `第${item.title}话`;
        } else if (item.title.match(/^\d+[话話章节節回卷篇]/i)) {
          displayText = item.title;
        } else if (item.title.match(/卷|番外|彩页|特典/i)) {
          displayText = item.title;
        } else if (item.title.match(/^0\d+$/)) {
          displayText = `第${item.title}话`;
        }

        link.textContent = displayText;
      } else {
        link.textContent = item.title;
      }

      // 添加点击事件处理,无缝加载新章节内容
      link.addEventListener('click', async (e) => {
        e.preventDefault();
        console.log('🔄 正在加载章节:', item.title);

        // 关闭侧边栏
        const overlay = document.getElementById('directory-overlay');
        const sidebar = document.getElementById('directory-sidebar');
        if (overlay && sidebar) {
          overlay.classList.remove('show');
          sidebar.classList.remove('open');
          setTimeout(() => {
            overlay.remove();
            sidebar.remove();
          }, 300);
        }

        // 无缝加载新章节内容
        await loadNewChapter(item.url, item.title);
      });

      // 高亮当前章节
      if (item.threadId === currentThreadId) {
        link.classList.add('current');
        console.log(`⭐ 当前章节: ${item.title}`);
      }

      li.appendChild(link);
      list.appendChild(li);

      console.log(`📄 章节 ${index + 1}: ${item.title}`);
    });

    console.log('✅ 目录界面显示完成');
  }

  function showDirectoryEmpty(modal, message = '未找到相关章节') {
    const loading = modal.querySelector('#directory-loading');
    const empty = modal.querySelector('#directory-empty');

    loading.style.display = 'none';
    empty.style.display = 'block';
    empty.textContent = message;
  }

  function getCurrentThreadId() {
    const href = window.location.href;
    let match = href.match(/thread-(\d+)-/);
    if (match) {
      return match[1];
    }

    try {
      const url = new URL(href);
      const tidParam = url.searchParams.get('tid');
      if (tidParam) {
        return tidParam;
      }
    } catch (e) {
      // ignore
    }

    match = href.match(/[?&]tid=(\d+)/);
    return match ? match[1] : null;
  }

  // 清理所有缓存,并清空内存缓存
  function clearAllCache() {
    console.log('🧹 清理所有目录缓存 (session + GM) ...');
    let cleanedCount = 0;

    // 1) 清理 sessionStorage 前缀条目
    try {
      const sKeys = Object.keys(sessionStorage).filter(k => k.startsWith(CACHE_PREFIX));
      sKeys.forEach(k => {
        try { sessionStorage.removeItem(k); cleanedCount++; } catch (e) {}
      });
    } catch (e) {
    }

    // 2) 清理 GM_* 条目(通过索引枚举)
    try {
      if (typeof GM_getValue === 'function' && typeof GM_setValue === 'function') {
        let idx = GM_getValue(GM_INDEX_KEY, undefined);
        if (typeof idx === 'string') {
          try { idx = JSON.parse(idx); } catch (e) { idx = []; }
        }
        if (!Array.isArray(idx)) idx = [];

        for (const key of idx) {
          try { storageRemove(key); cleanedCount++; } catch (e) {}
        }

        try { GM_setValue(GM_INDEX_KEY, []); } catch (e) {}
      }
    } catch (e) {
    }

    // 3) 清理内存缓存
    try { if (searchCache && searchCache.clear) searchCache.clear(); } catch (e) {}
    try { if (directoryMemoryCache && directoryMemoryCache.clear) directoryMemoryCache.clear(); } catch (e) {}

    console.log(`✅ 已清理 ${cleanedCount} 个持久化缓存条目,且已清空内存缓存`);
    return cleanedCount;
  }

  // 查看缓存状态(session + GM 索引 + 内存统计)
  function viewCacheStatus() {
    console.log('📊 缓存状态统计:');

    // sessionStorage 条目
    let sessionList = [];
    try {
      sessionList = Object.keys(sessionStorage).filter(k => k.startsWith(CACHE_PREFIX));
    } catch (e) { sessionList = []; }

    // GM 索引条目
    let gmList = [];
    try {
      if (typeof GM_getValue === 'function') {
        let idx = GM_getValue(GM_INDEX_KEY, undefined);
        if (typeof idx === 'string') {
          try { idx = JSON.parse(idx); } catch (e) { idx = []; }
        }
        if (Array.isArray(idx)) gmList = idx.slice();
      }
    } catch (e) { gmList = []; }

    console.log(`📦 sessionStorage 缓存数量: ${sessionList.length}`);
    console.log(`📦 GM_* 缓存数量 (index): ${gmList.length}`);
    console.log(`🧠 内存缓存 (searchCache): ${typeof searchCache !== 'undefined' && searchCache.size !== undefined ? searchCache.size : '未知'}`);
    console.log(`🧠 内存目录缓存 (directoryMemoryCache): ${typeof directoryMemoryCache !== 'undefined' && directoryMemoryCache.size !== undefined ? directoryMemoryCache.size : '未知'}`);

    if (sessionList.length > 0) {
      console.log('\n📋 sessionStorage 详情:');
      sessionList.forEach((k, i) => {
        try {
          const obj = JSON.parse(sessionStorage.getItem(k));
          const age = Math.round((Date.now() - (obj.ts || obj.timestamp || 0)) / (1000*60*60));
          const count = (obj.d || obj.data) ? (obj.d || obj.data).length : '未知';
          console.log(`   ${i+1}. ${k.replace(CACHE_PREFIX+'session-','')}: ${count}章, ${age}小时前`);
        } catch (e) {
          console.log(`   ${i+1}. ${k} (无法解析)`);
        }
      });
    }

    if (gmList.length > 0) {
      console.log('\n📋 GM_* 详情 (由索引列出):');
      gmList.forEach((k, i) => {
        try {
          const obj = storageGet(k, null);
          const age = obj ? Math.round((Date.now() - (obj.ts || obj.timestamp || 0)) / (1000*60*60)) : '未知';
          const count = obj ? ((obj.d || obj.data) ? (obj.d || obj.data).length : '未知') : '未知';
          console.log(`   ${i+1}. ${k.replace(CACHE_PREFIX,'')}: ${count}章, ${age}小时前`);
        } catch (e) {
          console.log(`   ${i+1}. ${k} (无法读取)`);
        }
      });
    }

    return {
      sessionCount: sessionList.length,
      gmCount: gmList.length,
      memorySearchCount: typeof searchCache !== 'undefined' && searchCache.size !== undefined ? searchCache.size : null,
      memoryDirCount: typeof directoryMemoryCache !== 'undefined' && directoryMemoryCache.size !== undefined ? directoryMemoryCache.size : null
    };
  }

// 必须识别作者 UID,按楼层顺序收集该作者的图片;识别失败直接返回空数组,避免全页搜索
function collectImagesFromDocument(doc = document) {
  try {
    const normalizeUrl = u => {
      if (!u) return null;
      u = String(u).trim();
      if (!u) return null;
      if (u.startsWith('//')) u = (location.protocol || 'https:') + u;
      if (!/^https?:\/\//i.test(u)) u = 'https://bbs.yamibo.com/' + u.replace(/^\/+/, '');
      u = u.replace(/^https?:\/\/https?:\/\//, 'https://');
      return u;
    };

    const isPlaceholder = (img, src) => {
      if (!src) return true;
      if (src.includes('static/image/common/none.gif')) {
        if (img.getAttribute('file') || img.getAttribute('zoomfile') || img.getAttribute('aid')) return false;
        return true;
      }
      return false;
    };

    // 识别并忽略头像/评分用户小图等噪音图片
    const isIgnoredImage = (img, src) => {
      try {
        if (!src && !img) return true;
        const s = String(src || '').toLowerCase();

        // 明确的头像路径(示例:/uc_server/data/avatar/...)
        if (/\/uc_server\/data\/avatar\//i.test(s)) return true;

        // 常见 avatar 文件名 / avatar_small
        if (/avatar(?:_small|_middle)?\.(jpg|jpeg|png|gif|webp)$/i.test(s)) return true;
        if (s.includes('/avatar/') || s.includes('/avatars/')) return true;

        // class 名称包含 avatar 或 user_avatar 等
        const cls = (img && img.className) ? String(img.className).toLowerCase() : '';
        if (cls.includes('user_avatar') || cls.includes('avatar') || cls.includes('userpic') || cls.includes('authoravatar')) return true;

        // 如果图片位于明显的评分/头像容器,忽略
        if (img && (img.closest('.postrate') || img.closest('.post-ratings') || img.closest('.postratedby') || img.closest('.poster') || img.closest('.author') || img.closest('.avatar') || img.closest('.user')) ) {
          return true;
        }

        // 若图片尺寸非常小(通常为头像),忽略阈值可调整
        const wAttr = img && (img.getAttribute('width') || img.width);
        const hAttr = img && (img.getAttribute('height') || img.height);
        const w = wAttr ? parseInt(wAttr, 10) : 0;
        const h = hAttr ? parseInt(hAttr, 10) : 0;
        if ((w > 0 && w < 80) || (h > 0 && h < 80)) return true;

        // 若 src 含有明显的评分/头像关键词
        if (s.includes('rating') || s.includes('score') || s.includes('grade') || s.includes('usericon')) return true;

        return false;
      } catch (e) {
        return false;
      }
    };

    const preferredSelectors = [
      'ignore_js_op img',
      '.savephotop img',
      'div.t_f img',
      'td.t_f img',
      '.pcb img',
      'img[zoomfile]',
      'img[file]',
      'img[aid]',
      'img.zoom',
      'img'
    ];

    // 定位主楼容器,尝试从中识别作者 UID
    let mainPost = null;
    try {
      const posts = doc.querySelectorAll('[id^="post_"], .post');
      if (posts && posts.length) {
        for (const p of posts) {
          if (p.id && /^post_\d+/.test(p.id)) { mainPost = p; break; }
        }
        if (!mainPost) mainPost = posts[0];
      }
    } catch (e) { mainPost = null; }

    // 必须识别作者 UID,否则直接返回空数组(避免全页抓取)
    let authorUid = null;
    if (mainPost) {
      try {
        const a = mainPost.querySelector('a[href*="space-uid-"], a[href*="uid="], a[href*="space.php?uid="]');
        if (a && a.getAttribute('href')) {
          const href = a.getAttribute('href');
          const m1 = href.match(/space-uid-(\d+)\.html/);
          const m2 = href.match(/uid=(\d+)/);
          if (m1) authorUid = m1[1];
          else if (m2) authorUid = m2[1];
        }
      } catch (e) { authorUid = null; }
    }

    if (!authorUid) {
      console.log('⚠️ collectImagesFromDocument: 无法识别作者 UID,跳过图片收集');
      return [];
    }

    const seen = new Set();
    const results = [];

    // 遍历所有 post_* 楼层,按文档顺序只收集该作者的图片
    const postsAll = Array.from(doc.querySelectorAll('[id^="post_"]'));
    for (const p of postsAll) {
      try {
        const authorLink = p.querySelector(`a[href*="space-uid-${authorUid}"], a[href*="uid=${authorUid}"], a[href*="space.php?uid=${authorUid}"]`);
        if (!authorLink) continue; // 非楼主发言,跳过

for (const sel of preferredSelectors) {
  const imgs = p.querySelectorAll(sel);
  for (const img of imgs) {
    const u = normalizeUrl(
      img.getAttribute('zoomfile') ||
      img.getAttribute('file') ||
      img.getAttribute('src') ||
      img.getAttribute('data-src')
    );
    if (!u) continue;
    if (isIgnoredImage(img, u)) continue;
    if (!seen.has(u)) { seen.add(u); results.push(u); }
  }
}
      } catch (e) {
        continue;
      }
    }

    return results;
  } catch (e) {
    console.warn('collectImagesFromDocument error', e);
    return [];
  }
}


  // 初始化时检查全局阅读器状态
  function initAutoReader() {
    autoReaderEnabled = checkAutoReaderStatus();
    console.log('🚀 初始化全局阅读器状态:', autoReaderEnabled);

    // 清理过期缓存
    cleanExpiredCache();

    // 检查是否有漫画内容,优先检测主楼
    const comicImages = collectImagesFromDocument(document);
    if (comicImages && comicImages.length > 0) {
      console.log('🎯 检测到漫画内容,立即开始预取目录...');

      // 立即开始预取目录信息,不管是否进入阅读模式
      preloadDirectoryInfo();

      // 如果全局阅读器开启,自动进入阅读模式
      if (autoReaderEnabled && !isReading) {
        console.log('🎯 自动进入阅读模式');
        setTimeout(() => {
          if (button && button.parentNode) {
            button.remove();
          }
          enterReader();
        }, 1000);
      }
    } else {
      console.log('🔍 尝试预取目录信息...');
      preloadDirectoryInfo();
    }
  }
  // 预加载目录信息
  async function preloadDirectoryInfo() {
    if (seriesDirectory.length > 0) {
      console.log('� 目录已预加载,跳过重复加载');
      return;
    }

    try {
      console.log('�📚 开始预加载目录信息...');

      // 获取当前页面标题
      let threadTitle = '';
      const h1Element = document.querySelector('h1.ts');
      if (h1Element) {
        const subjectSpan = h1Element.querySelector('#thread_subject, span[id^="thread_subject"]');
        if (subjectSpan) {
          threadTitle = subjectSpan.textContent.trim();
        } else {
          threadTitle = h1Element.textContent.trim();
        }
      } else {
        const titleElement = document.querySelector('#thread_subject, span[id^="thread_subject"]');
        if (titleElement) {
          threadTitle = titleElement.textContent.trim();
        } else {
          const pageTitle = document.title;
          threadTitle = pageTitle.includes(' - ') ? pageTitle.split(' - ')[0].trim() : pageTitle.replace(/\s*-\s*百合会.*$/, '').trim();
        }
      }

      if (threadTitle) {
        // 保存标题信息
        savedThreadTitle = threadTitle;
        originalSeriesTitle = threadTitle;

        console.log('📖 开始预取目录,使用标题:', threadTitle);

        // 先检查持久化缓存
        const seriesKey = generateSeriesKey(threadTitle);
        const cachedDirectory = getCachedDirectory(seriesKey);
        if (cachedDirectory && cachedDirectory.length > 0) {
          console.log('✅ 从持久化缓存预加载完成:', cachedDirectory.length, '个章节');
          seriesDirectory = cachedDirectory;
          return;
        }

        // 如果没有缓存,立即开始异步搜索目录
        searchSeriesDirectory(threadTitle).then(directory => {
          if (directory && directory.length > 0) {
            seriesDirectory = directory;
            console.log('✅ 预加载完成,获得', directory.length, '个章节');
          } else {
            console.log('⚠️ 预加载未找到目录');
          }
        }).catch(error => {
          console.warn('⚠️ 预加载目录失败:', error.message);
        });

        console.log('🚀 预加载任务已启动,后台进行中...');
      } else {
        console.log('⚠️ 无法获取页面标题,跳过预加载');
      }
    } catch (error) {
      console.warn('⚠️ 预加载目录失败:', error.message);
    }
  }

  // 页面加载完成后初始化
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
      setTimeout(initAutoReader, 1000); // 确保页面元素完全加载
    });
  } else {
    setTimeout(initAutoReader, 1000); // 给页面充足时间渲染
  }

  /*** 进入阅读模式 ***/
  function enterReader() {
    isReading = true;

    // 保存原始页面HTML和标题
    originalPageHTML = document.body.innerHTML;
    originalPageTitle = document.title;
    console.log('💾 已保存原始页面内容');

    button.remove();

    // 在替换页面内容之前,先提取并保存标题
    console.log('🔍 进入阅读模式,保存页面标题...');

    // 简化标题提取逻辑
    const h1Element = document.querySelector('h1.ts');
    if (h1Element) {
      const subjectSpan = h1Element.querySelector('#thread_subject, span[id^="thread_subject"]');
      if (subjectSpan) {
        savedThreadTitle = subjectSpan.textContent.trim();
        console.log('💾 保存标题:', savedThreadTitle);
      } else {
        savedThreadTitle = h1Element.textContent.trim();
        console.log('💾 保存h1文本:', savedThreadTitle);
      }
    } else {
      // 兜底:尝试其他方式
      const threadTitleElement = document.querySelector('#thread_subject, span[id^="thread_subject"]');
      if (threadTitleElement) {
        savedThreadTitle = threadTitleElement.textContent.trim();
        console.log('💾 保存标题元素:', savedThreadTitle);
      } else {
        // 最后兜底用<title>
        const pageTitle = document.title;
        savedThreadTitle = pageTitle.includes(' - ')
          ? pageTitle.split(' - ')[0].trim()
          : pageTitle.replace(/\s*-\s*百合会.*$/, '').trim();
        console.log('💾 从页面title保存:', savedThreadTitle);
      }
    }

    // 保存原始系列标题,用于后续目录搜索
    originalSeriesTitle = savedThreadTitle;
    console.log('🎯 保存原始系列标题:', originalSeriesTitle);

    // 检查是否已有预加载的目录数据
    if (seriesDirectory.length > 0) {
      console.log('✅ 使用预加载的目录数据:', seriesDirectory.length, '个章节');
    } else {
      // 检查持久化缓存
      const seriesKey = generateSeriesKey(savedThreadTitle);
      const cachedDirectory = getCachedDirectory(seriesKey);
      if (cachedDirectory && cachedDirectory.length > 0) {
        console.log('📦 从持久化缓存加载目录:', cachedDirectory.length, '个章节');
        seriesDirectory = cachedDirectory;
      } else {
        // 缓存也没有,异步搜索
        console.log('🔍 开始异步搜索目录...');
        searchSeriesDirectory(savedThreadTitle).then(directory => {
          seriesDirectory = directory;
          console.log('📋 异步获取到目录:', seriesDirectory.length, '个章节');
        }).catch(error => {
          console.error('❌ 异步获取目录失败:', error);
        });
      }
    }

    // 提取漫画图片
    images = collectImagesFromDocument(document)
      .map(u => {
        if (!u) return null;
        let url = String(u);
        if (url.startsWith('//')) url = 'https:' + url;
        if (!/^https?:\/\//i.test(url)) url = 'https://bbs.yamibo.com/' + url.replace(/^\/+/, '');
        if (url.startsWith('http://')) url = url.replace(/^http:\/\//, 'https://');
        url = url.replace(/^https?:\/\/https?:\/\//, 'https://');
        return url;
      })
      .filter(Boolean);

    console.log('📸 提取到', images.length, '张图片,URL已处理为HTTPS');

    document.body.innerHTML = `
      <button id="cw-exit" data-tooltip="退出阅读模式">⏻</button>
      <div id="cw-toolbar">
        <button id="cw-directory" data-tooltip="查看目录 (M)">☰</button>
        <button id="cw-full" data-tooltip="全屏模式 (F)">⛶</button>
        <button id="cw-bg" data-tooltip="切换背景色 (B)">◐</button>
        <div id="cw-zoom">
          <button id="cw-zoom-in" data-tooltip="放大图片 (+)">﹢</button>
          <button id="cw-zoom-out" data-tooltip="缩小图片 (-)">﹣</button>
        </div>
      </div>
      <div id="cw-container"></div>
      <div id="cw-bottom-bar">
        <span id="cw-page-info">1/${images.length}</span>
        <button id="cw-to-top" data-tooltip="返回顶部 (T)">⬆</button>
      </div>
    `;

    GM_addStyle(`
      html, body {
        margin: 0;
        padding: 0;
        background: #616161;
        overflow-y: auto;
        color: #fff;
        font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Source Han Sans SC", sans-serif !important;
      }

      /* 容器样式:负责整体布局 */
      #cw-container {
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: flex-start !important;
        padding: 70px 0 60px !important;
        gap: 20px !important;
        margin: 0 auto !important;
      }

      /* 图片基础样式 */
      #cw-container img {
        display: block !important;
        width: var(--img-width, 35vw) !important; /* 使用CSS变量控制宽度 */
        height: auto !important;
        border-radius: 0 !important;
        user-select: none !important;
        transition: all 0.3s ease !important;
        margin: 0 auto !important;
      }

      /* 小图保持原始大小 */
      #cw-container img[data-original-size="small"] {
        width: auto !important;
        max-width: var(--img-width, 35vw) !important;
      }

      #cw-exit {
        position: fixed;
        top: 15px;
        left: 20px;
        z-index: 2147483647;
        background: rgba(0,0,0,0.6);
        color: #fff;
        border: none;
        padding: 6px 10px;
        border-radius: 5px;
        cursor: pointer;
        font-size: 14px;
      }

      #cw-toolbar {
        position: fixed;
        top: 15px;
        right: 20px;
        display: flex;
        flex-direction: column;
        gap: 10px;
        z-index: 2147483647;
        pointer-events: auto;
      }

      #cw-toolbar button {
        background: rgba(0,0,0,0.6);
        color: #fff;
        border: none;
        padding: 6px;
        width: 32px;
        height: 32px;
        border-radius: 5px;
        cursor: pointer;
        font-size: 14px;
        transition: background-color 0.2s;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      #cw-toolbar button:hover {
        background: rgba(0,0,0,0.8);
        transform: scale(1.05);
      }

      #cw-toolbar button:active {
        transform: scale(0.95);
      }

      /* 放大缩小按钮的特殊样式 */
      #cw-zoom {
        display: flex;
        flex-direction: column;
        gap: 5px;
      }

      #cw-zoom-in,
      #cw-zoom-out {
        color: white !important;
        font-size: 20px !important;
        font-weight: bold !important;
      }

      /* ESC按钮的特殊样式 */
      #cw-full[data-fullscreen="true"] {
        font-size: 24px !important;
        line-height: 16px !important;
        padding: 6px 10px !important;
      }

      /* 底部工具栏 */
      #cw-bottom-bar {
        position: fixed;
        bottom: 15px;
        right: 20px;
        display: flex;
        align-items: center;
        gap: 15px;
        z-index: 2147483647;
        pointer-events: auto;
        background: rgba(0,0,0,0.6);
        padding: 8px 12px;
        border-radius: 5px;
        font-size: 14px;
      }

      #cw-bottom-bar button {
        background: rgba(255,255,255,0.2);
        color: #fff;
        border: none;
        padding: 4px 8px;
        border-radius: 3px;
        cursor: pointer;
        font-size: 12px;
      }
      #cw-bottom-bar button:hover {
        background: rgba(255,255,255,0.3);
      }

      #cw-page-info {
        color: #fff;
        font-weight: bold;
      }

      /* 全屏状态下的基础样式 */
      :fullscreen #cw-toolbar,
      :-webkit-full-screen #cw-toolbar,
      :fullscreen #cw-bottom-bar,
      :-webkit-full-screen #cw-bottom-bar,
      :fullscreen #cw-exit,
      :-webkit-full-screen #cw-exit {
        display: flex !important;
        position: fixed !important;
        z-index: 2147483647 !important;
        transition: all 0.3s ease !important;
        opacity: 0 !important;
        pointer-events: none !important;
      }

      /* 鼠标移动时显示工具栏 */
      :fullscreen.tools-visible #cw-toolbar,
      :-webkit-full-screen.tools-visible #cw-toolbar,
      :fullscreen.tools-visible #cw-bottom-bar,
      :-webkit-full-screen.tools-visible #cw-bottom-bar,
      :fullscreen.tools-visible #cw-exit,
      :-webkit-full-screen.tools-visible #cw-exit {
        opacity: 1 !important;
        pointer-events: auto !important;
      }

      :fullscreen #cw-toolbar,
      :-webkit-full-screen #cw-toolbar {
        top: 15px !important;
        right: 20px !important;
      }

      :fullscreen #cw-bottom-bar,
      :-webkit-full-screen #cw-bottom-bar {
        bottom: 15px !important;
        right: 20px !important;
      }

      /* 确保全屏状态下按钮样式保持一致 */
      :fullscreen #cw-toolbar button:not(#cw-zoom-in):not(#cw-zoom-out),
      :-webkit-full-screen #cw-toolbar button:not(#cw-zoom-in):not(#cw-zoom-out) {
        padding: 6px !important;
        width: 32px !important;
        height: 32px !important;
        font-size: 14px !important;
        line-height: 16px !important;
        display: flex !important;
        align-items: center !important;
        justify-content: center !important;
      }

      /* 全屏状态下的缩放按钮样式 */
      :fullscreen #cw-zoom-in,
      :fullscreen #cw-zoom-out,
      :-webkit-full-screen #cw-zoom-in,
      :-webkit-full-screen #cw-zoom-out {
        padding: 6px !important;
        width: 32px !important;
        height: 32px !important;
        font-size: 18px !important;
        line-height: 16px !important;
        color: #ffffff !important;
      }

      /* ESC 符号单独设置大小 */
      :fullscreen #cw-full[data-fullscreen="true"],
      :-webkit-full-screen #cw-full[data-fullscreen="true"] {
        font-size: 24px !important;
      }

      /* 自定义tooltip样式,禁用原生tooltip */
      [data-tooltip] {
        position: relative;
      }

      [data-tooltip]:hover::after {
        content: attr(data-tooltip);
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        background: rgba(0, 0, 0, 0.9);
        color: white;
        padding: 8px 12px;
        border-radius: 6px;
        font-size: 12px;
        white-space: nowrap;
        z-index: 2147483648;
        pointer-events: none;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
        opacity: 0;
        animation: tooltipFadeIn 0.2s ease forwards;
      }

      [data-tooltip]:hover::before {
        content: '';
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        border: 6px solid transparent;
        border-top-color: rgba(0, 0, 0, 0.9);
        z-index: 2147483648;
        pointer-events: none;
        opacity: 0;
        animation: tooltipFadeIn 0.2s ease forwards;
      }

      /* 顶部工具栏按钮 - tooltip显示在下方 */
      #cw-toolbar [data-tooltip]:hover::after,
      #cw-exit[data-tooltip]:hover::after {
        top: 100%;
        margin-top: 8px;
      }

      #cw-toolbar [data-tooltip]:hover::before,
      #cw-exit[data-tooltip]:hover::before {
        top: 100%;
        border-bottom-color: rgba(0, 0, 0, 0.9);
        margin-top: 2px;
      }

      /* 底部按钮 - tooltip显示在上方 */
      #cw-bottom-bar [data-tooltip]:hover::after {
        bottom: 100%;
        margin-bottom: 8px;
      }

      #cw-bottom-bar [data-tooltip]:hover::before {
        bottom: 100%;
        border-top-color: rgba(0, 0, 0, 0.9);
        margin-bottom: 2px;
      }

      /* 右侧按钮tooltip防止超出屏幕 */
      #cw-toolbar [data-tooltip]:hover::after {
        right: 0;
        left: auto;
        transform: translateX(-20px);
      }

      #cw-toolbar [data-tooltip]:hover::before {
        right: 20px;
        left: auto;
        transform: none;
      }

      /* 左侧退出按钮tooltip */
      #cw-exit[data-tooltip]:hover::after {
        left: 0;
        transform: translateX(20px);
      }

      #cw-exit[data-tooltip]:hover::before {
        left: 20px;
        transform: none;
      }

      /* 确保tooltip在全屏状态下也能正常显示 */
      :fullscreen [data-tooltip]:hover::after,
      :-webkit-full-screen [data-tooltip]:hover::after,
      :fullscreen [data-tooltip]:hover::before,
      :-webkit-full-screen [data-tooltip]:hover::before {
        z-index: 2147483649 !important;
      }
    `);

    // ====== 初始化阅读器UI(渲染图片并绑定按钮) ======
    function initReaderUI() {
      const container = document.getElementById('cw-container');
      if (!container) {
        console.warn('⚠️ cw-container 未找到,无法渲染图片');
        return;
      }

      // 设置初始缩放(使用 CSS 变量控制宽度)
      const applyZoom = () => {
        const vw = Math.max(10, Math.min(90, zoomLevel));
        document.documentElement.style.setProperty('--img-width', `${vw}vw`);
      };
      applyZoom();

      // 渲染图片列表
      container.innerHTML = '';
      images.forEach((url, idx) => {
        const img = document.createElement('img');
        img.src = url;
        img.loading = 'lazy';
        img.alt = `page-${idx + 1}`;
        img.dataset.index = String(idx + 1);
        container.appendChild(img);
      });

      // 更新页码显示(靠近视口中心的图片)
      const pageInfo = document.getElementById('cw-page-info');
      function updateCurrentPageInfo() {
        const imgs = container.querySelectorAll('img');
        if (!imgs.length) return;
        const viewportMid = window.scrollY + window.innerHeight / 2;
        let current = 1;
        let minDist = Infinity;
        imgs.forEach((img, i) => {
          const rect = img.getBoundingClientRect();
          const imgMid = window.scrollY + rect.top + rect.height / 2;
          const dist = Math.abs(imgMid - viewportMid);
          if (dist < minDist) { minDist = dist; current = i + 1; }
        });
        if (pageInfo) pageInfo.textContent = `${current}/${imgs.length}`;
      }

      // 退出、目录、全屏、背景、缩放、回顶部
      readerEventHandlers.onExitClick = () => exitReader();
      readerEventHandlers.onDirectoryClick = () => showDirectoryModal();
      readerEventHandlers.onFullClick = async () => {
        try {
          if (!document.fullscreenElement) await document.documentElement.requestFullscreen();
          else await document.exitFullscreen();
        } catch (e) { console.warn('全屏切换失败:', e); }
      };
      readerEventHandlers.onBgClick = () => {
        bgIsBlack = !bgIsBlack;
        document.body.style.background = bgIsBlack ? '#616161' : '#f5f5f5';
        document.body.style.color = bgIsBlack ? '#fff' : '#000';
      };
      readerEventHandlers.onZoomIn = () => { zoomLevel = Math.min(90, zoomLevel + 5); applyZoom(); };
      readerEventHandlers.onZoomOut = () => { zoomLevel = Math.max(10, zoomLevel - 5); applyZoom(); };
      readerEventHandlers.onToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' });

      const throttle = (fn, wait = 100) => {
        let t = null;
        return (...args) => {
          if (t) return;
          t = setTimeout(() => { fn(...args); t = null; }, wait);
        };
      };

      readerEventHandlers.updateCurrentPageInfo = throttle(updateCurrentPageInfo, 120);

      // 工具栏显示控制
      readerEventHandlers.showTools = function() {
        try {
          document.documentElement.classList.add('tools-visible');
          if (readerToolsTimer) clearTimeout(readerToolsTimer);
          readerToolsTimer = setTimeout(() => {
            document.documentElement.classList.remove('tools-visible');
            readerToolsTimer = null;
          }, 3000);
        } catch (e) {
          // ignore
        }
      };

      readerEventHandlers.onMousemove = readerEventHandlers.showTools;
      readerEventHandlers.onKeydown = readerEventHandlers.showTools;

      readerEventHandlers.onFullscreen = function() {
        if (document.fullscreenElement) {
          readerEventHandlers.showTools();
        } else {
          document.documentElement.classList.remove('tools-visible');
          if (readerToolsTimer) { clearTimeout(readerToolsTimer); readerToolsTimer = null; }
        }
      };

      // 绑定 DOM 事件
      const exitBtn = document.getElementById('cw-exit');
      if (exitBtn) exitBtn.addEventListener('click', readerEventHandlers.onExitClick);

      const dirBtn = document.getElementById('cw-directory');
      if (dirBtn) dirBtn.addEventListener('click', readerEventHandlers.onDirectoryClick);

      const fullBtn = document.getElementById('cw-full');
      if (fullBtn) fullBtn.addEventListener('click', readerEventHandlers.onFullClick);

      const bgBtn = document.getElementById('cw-bg');
      if (bgBtn) bgBtn.addEventListener('click', readerEventHandlers.onBgClick);

      const zoomIn = document.getElementById('cw-zoom-in');
      const zoomOut = document.getElementById('cw-zoom-out');
      if (zoomIn) zoomIn.addEventListener('click', readerEventHandlers.onZoomIn);
      if (zoomOut) zoomOut.addEventListener('click', readerEventHandlers.onZoomOut);

      const toTop = document.getElementById('cw-to-top');
      if (toTop) toTop.addEventListener('click', readerEventHandlers.onToTop);

      // 全局事件绑定
      if (!readerEventsBound) {
        window.addEventListener('scroll', readerEventHandlers.updateCurrentPageInfo, { passive: true });
        window.addEventListener('mousemove', readerEventHandlers.onMousemove, { passive: true });
        window.addEventListener('keydown', readerEventHandlers.onKeydown, { passive: true });
        document.addEventListener('fullscreenchange', readerEventHandlers.onFullscreen);
        readerEventsBound = true;
      }

      // 立即更新一次页码并短暂显示工具栏
      setTimeout(updateCurrentPageInfo, 200);
      readerEventHandlers.showTools();

      // 提供解绑函数,供 exitReader 调用
      unbindReaderEvents = function() {
        try {
          if (!readerEventsBound) return;

          // 移除 DOM 绑定
          try { if (exitBtn) exitBtn.removeEventListener('click', readerEventHandlers.onExitClick); } catch (e) {}
          try { if (dirBtn) dirBtn.removeEventListener('click', readerEventHandlers.onDirectoryClick); } catch (e) {}
          try { if (fullBtn) fullBtn.removeEventListener('click', readerEventHandlers.onFullClick); } catch (e) {}
          try { if (bgBtn) bgBtn.removeEventListener('click', readerEventHandlers.onBgClick); } catch (e) {}
          try { if (zoomIn) zoomIn.removeEventListener('click', readerEventHandlers.onZoomIn); } catch (e) {}
          try { if (zoomOut) zoomOut.removeEventListener('click', readerEventHandlers.onZoomOut); } catch (e) {}
          try { if (toTop) toTop.removeEventListener('click', readerEventHandlers.onToTop); } catch (e) {}



          // 移除全局事件
          try { window.removeEventListener('scroll', readerEventHandlers.updateCurrentPageInfo, { passive: true }); } catch (e) {}
          try { window.removeEventListener('mousemove', readerEventHandlers.onMousemove, { passive: true }); } catch (e) {}
          try { window.removeEventListener('keydown', readerEventHandlers.onKeydown, { passive: true }); } catch (e) {}
          try { document.removeEventListener('fullscreenchange', readerEventHandlers.onFullscreen); } catch (e) {}
        } catch (e) {
          // ignore
        }
        // 清理定时器与状态
        if (readerToolsTimer) { clearTimeout(readerToolsTimer); readerToolsTimer = null; }
        readerEventHandlers = {};
               readerEventsBound = false;
        unbindReaderEvents = null;
      };
    }

    // 立即初始化阅读器 UI
    initReaderUI();
  }

  /*** 退出阅读模式 ***/
  function exitReader() {
    isReading = false;
    setAutoReaderStatus(false); // 关闭全局阅读器模式

    // 解绑阅读器事件,避免残留监听
    try {
      if (typeof unbindReaderEvents === 'function') unbindReaderEvents();
    } catch (e) {
      // ignore
    }

    // 先退出全屏
    if (document.fullscreenElement) {
      document.exitFullscreen().catch(err => {
        console.warn('退出全屏失败:', err);
      });
    }

    // 如果有保存的原始页面内容,直接恢复
    if (originalPageHTML) {
      console.log('🔄 恢复原始页面内容...');

      // 移除阅读模式添加的样式
      const readerStyles = document.querySelectorAll('style');
      readerStyles.forEach(style => {
        // 移除包含阅读模式特定选择器的样式
        if (style.textContent.includes('#cw-container') ||
            style.textContent.includes('#cw-toolbar') ||
            style.textContent.includes('#chapter-loader')) {
          style.remove();
          console.log('🧹 移除阅读模式样式');
        }
      });

      document.body.innerHTML = originalPageHTML;
      document.title = originalPageTitle;

      // 重新绑定按钮事件
      const restoredButton = document.getElementById('reader-toggle');
      if (restoredButton) {
        restoredButton.addEventListener('click', () => {
          if (!isReading) {
            enterReader();
            setAutoReaderStatus(true);
          } else {
            exitReader();
          }
        });
      }

      console.log('✅ 页面内容已恢复,目录缓存保留');
    } else {
      // 如果没有保存的内容,刷新页面
      console.log('⚠️ 没有保存的页面内容,刷新页面');
      location.reload();
    }
  }

  // 无缝章节加载:加载提示与错误显示
  function showLoadingIndicator(message = '正在加载章节...') {
    try {
      // 移除已存在的加载提示
      const existing = document.getElementById('chapter-loader');
      if (existing) existing.remove();

      const loader = document.createElement('div');
      loader.id = 'chapter-loader';
      loader.innerHTML = `
        <div class="loader-content">
          <div class="loader-spinner"></div>
          <div class="loader-text">${message}</div>
        </div>
      `;

      const style = document.createElement('style');
      style.id = 'chapter-loader-style';
      style.textContent = `
        #chapter-loader { position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.6); display:flex; align-items:center; justify-content:center; z-index:2147483648; }
        #chapter-loader .loader-content { color:#fff; padding:18px 24px; border-radius:10px; background: rgba(0,0,0,0.45); text-align:center; }
        #chapter-loader .loader-spinner { width:30px; height:30px; border:3px solid rgba(255,255,255,0.25); border-top:3px solid #fff; border-radius:50%; animation: yamibo-spin 1s linear infinite; margin:0 auto 8px }
        @keyframes yamibo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        #chapter-loader .loader-text { font-size:14px; }
      `;

      document.head.appendChild(style);
      document.body.appendChild(loader);
    } catch (e) {
      console.warn('showLoadingIndicator error', e);
    }
  }

  function hideLoadingIndicator() {
    try {
      const loader = document.getElementById('chapter-loader');
      if (loader) {
        loader.remove();
      }
      const style = document.getElementById('chapter-loader-style');
      if (style) style.remove();
    } catch (e) {
      // ignore
    }
  }

  function showErrorMessage(message) {
    try {
      const id = 'chapter-error-msg';
      const existing = document.getElementById(id);
      if (existing) existing.remove();

      const el = document.createElement('div');
      el.id = id;
      el.style.cssText = 'position:fixed;left:50%;top:40%;transform:translate(-50%,-50%);background:#ff6b6b;color:#fff;padding:12px  18px;border-radius:8px;z-index:2147483649;font-size:14px;box-shadow:0 8px 30px rgba(0,0,0,0.4)';
      el.textContent = message;
      document.body.appendChild(el);
      setTimeout(() => { try { el.style.opacity = '0'; setTimeout(() => el.remove(), 250); } catch (e) {} }, 2500);
    } catch (e) {
      console.warn('showErrorMessage error', e);
    }
  }

  // 无缝章节切换
  async function loadNewChapter(chapterUrl, chapterTitle) {
    try {
      console.log('📖 开始无缝加载章节:', chapterTitle);
      showLoadingIndicator('正在加载章节...');

      const response = await fetch(chapterUrl, {
        method: 'GET',
        credentials: 'include',
        headers: {
          'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
          'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
          'Referer': window.location.href,
          'User-Agent': navigator.userAgent
        }
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const html = await response.text();
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, 'text/html');

      // 提取图片 URL
      const newImages = collectImagesFromDocument(doc)
        .map(u => {
          if (!u) return null;
          let url = String(u);
          if (url.startsWith('//')) url = 'https:' + url;
          if (!/^https?:\/\//i.test(url)) url = 'https://bbs.yamibo.com/' + url.replace(/^\/+/, '');
          if (url.startsWith('http://')) url = url.replace(/^http:\/\//, 'https://');
          url = url.replace(/^https?:\/\/https?:\/\//, 'https://');
          return url;
        })
        .filter(Boolean);

      if (!newImages || newImages.length === 0) {
        hideLoadingIndicator();
        showErrorMessage('未找到漫画图片,已跳转到原帖');
        location.href = chapterUrl;
        return;
      }

      // 更新全局图片数组
      images = newImages;

      // 尝试提取并保存新章节标题
      const newH1 = doc.querySelector('h1.ts');
      if (newH1) {
        const subjectSpan = newH1.querySelector('#thread_subject, span[id^="thread_subject"]');
        savedThreadTitle = subjectSpan ? subjectSpan.textContent.trim() : newH1.textContent.trim();
      } else {
        const newTitleEl = doc.querySelector('#thread_subject, span[id^="thread_subject"]');
        if (newTitleEl) savedThreadTitle = newTitleEl.textContent.trim();
      }

      // 更新浏览器地址栏(不刷新页面)
      try { window.history.pushState({}, '', chapterUrl); } catch (e) { /* ignore */ }

      // 只更新图片容器与页码
      const container = document.getElementById('cw-container');
      if (container) {
        container.innerHTML = '';
        images.forEach((url, idx) => {
          const img = document.createElement('img');
          img.src = url;
          img.loading = 'lazy';
          img.alt = `page-${idx + 1}`;
          img.dataset.index = String(idx + 1);

          img.onload = function() {
            try {
              const naturalWidth = this.naturalWidth || 0;
              const viewportWidth = window.innerWidth || 1024;
              const oneThirdViewport = viewportWidth / 3;
              if (naturalWidth > 0 && naturalWidth < oneThirdViewport) this.setAttribute('data-original-size', 'small');
              else this.removeAttribute('data-original-size');
            } catch (e) {
              // ignore
            }
          };

          container.appendChild(img);
        });

        const pageInfo = document.getElementById('cw-page-info');
        if (pageInfo) pageInfo.textContent = `1/${images.length}`;
      }

      // 滚动到顶部
      window.scrollTo({ top: 0, behavior: 'smooth' });

      hideLoadingIndicator();
      console.log('🎉 章节加载完成:', chapterTitle);

    } catch (error) {
      console.error('❌ 加载章节失败:', error);
      hideLoadingIndicator();
      showErrorMessage('加载章节失败: ' + (error && error.message ? error.message : String(error)));
      // 若失败,1s 后回退到原帖(避免卡死在阅读器)
      setTimeout(() => { try { location.href = chapterUrl; } catch (e) {} }, 1000);
    }
  }
})();