JavDB Power Tools

JavDB/MissAV 双站增强工具箱:收藏管理、状态标签、屏蔽过滤、跨站同步、热播/Top250榜单、新作品检测、预告片、字幕搜索、WebDav/Git云备份、演员信息、清单管理、截图墙、磁链聚合、以图识图等功能插件

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         JavDB Power Tools
// @namespace    zerobiubiu.top
// @version      1.46.4
// @author       zerobiubiu
// @description  JavDB/MissAV 双站增强工具箱:收藏管理、状态标签、屏蔽过滤、跨站同步、热播/Top250榜单、新作品检测、预告片、字幕搜索、WebDav/Git云备份、演员信息、清单管理、截图墙、磁链聚合、以图识图等功能插件
// @license      MIT
// @copyright    2024-2026 zerobiubiu (https://github.com/zerobiubiu) — 基于 JAV-JHS 3.3.6 (MIT) 二次开发
// @icon         https://cdn.jsdelivr.net/gh/zerobiubiu/Figure-bed/b507c13a-9f4e-41b5-bbbb-129d0a21f97d.png
// @homepageURL  https://github.com/zerobiubiu/monkey-jhs-disassemble
// @match        https://javdb.com/*
// @match        https://missav.ws/*
// @match        https://missav.live/*
// @match        https://missav.ai/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/js/tabulator.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/js/md5.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/viewer.min.js
// @connect      xunlei.com
// @connect      missav.live
// @connect      javdb.com
// @connect      supjav.com
// @connect      127.0.0.1
// @connect      javstore.net
// @connect      u9a9.com
// @connect      u3c3.com
// @connect      sukebei.nyaa.si
// @connect      btsow.lol
// @connect      imgur.com
// @connect      api.imgur.com
// @connect      lens.google.com
// @connect      yandex.ru
// @connect      translate-pa.googleapis.com
// @connect      123av.com
// @connect      fc2ppvdb.com
// @connect      adult.contents.fc2.com
// @connect      *
// @grant        GM_addStyle
// @grant        GM_addValueChangeListener
// @grant        GM_getValue
// @grant        GM_info
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        window.close
// @run-at       document-idle
// ==/UserScript==

(function(jquery, layui_layer, tabulator_tables, toastify_js, localforage$1, viewerjs, blueimp_md5) {
	"use strict";
	var __create = Object.create;
	var __defProp = Object.defineProperty;
	var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
	var __getOwnPropNames = Object.getOwnPropertyNames;
	var __getProtoOf = Object.getPrototypeOf;
	var __hasOwnProp = Object.prototype.hasOwnProperty;
	var __copyProps = (to, from, except, desc) => {
		if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
			key = keys[i];
			if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
				get: ((k) => from[k]).bind(null, key),
				enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
			});
		}
		return to;
	};
	var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
		value: mod,
		enumerable: true
	}) : target, mod));
	jquery = __toESM(jquery);
	layui_layer = __toESM(layui_layer);
	tabulator_tables = __toESM(tabulator_tables);
	toastify_js = __toESM(toastify_js);
	localforage$1 = __toESM(localforage$1);
	viewerjs = __toESM(viewerjs);
	blueimp_md5 = __toESM(blueimp_md5);
	var s = new Set();
	var _css = async (t) => {
		if (s.has(t)) return;
		s.add(t);
		((c) => {
			if (typeof GM_addStyle === "function") GM_addStyle(c);
			else (document.head || document.documentElement).appendChild(document.createElement("style")).append(c);
		})(t);
	};
	var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
	window.jQuery = jquery.default;
	window.$ = jquery.default;
	_css("html #layuicss-skinlayercss{width:1989px;display:none;position:absolute}.layui-layer-shade,.layui-layer{_position:absolute;pointer-events:auto;position:fixed}.layui-layer-shade{width:100%;height:100%;top:0;left:0}.layui-layer{-webkit-overflow-scrolling:touch;background-color:#fff;-webkit-background-clip:content;margin:0;padding:0;top:150px;left:0;box-shadow:1px 1px 50px #0000004d}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #0000001a;box-shadow:1px 1px 5px #0003}.layui-layer-load{background:#eee url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) 50% no-repeat}.layui-layer-ico{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANIAAAA9CAYAAADYizcVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAACAASURBVHic7Z15fFvVmfe/92qzJMtrvCQ2jp09TuwsQCAkISwJDVAoW0hombaQhgKlnU5pmc60A+3M0FK6dxhoGghLC0PCEjJAIW8gISQhwWSzHcfO4tix41heZNmSLFnS1T3vH7IUy1qdmPfTd8a/z8cfS/ee5/zuvbq/e855znOeKwkhBEkg3FYC1j0Eug6i2upQHU0ITycAkjEfOaMMOXcWmrz5aAoXIZkKk1WZEs563ezstVLV10WNy8bJfgedPg8A+XojU8wZVKbnsiAzj6VZhUwwmEaFV7Xb8NXV4D95DKWliYC1DbW3FwA5KwtNYRHakjJ0U6ajn1WJnJ07KrzCd5aAYyeqqwrVXYM6cBLhH7zOunzktCnIpkrk9AVoMpYi6SeMCq/D4aCpqYmWlhasVis2mw2n0wmAxWIhNzeXwsJCSkpKKCsrIyMjY1R41Z5ufLWH8R8/ir+5kUB7G2pvDwByVg6a8UXoSiejm1aOvmIucs64UeG1+vvY42rkQH8zRz1nOeXtplMJnm++1sIkwzjKjRO42FzKovTJFOoyk9YpJRKSam9AaXoLpfld1J6jKR2knFOOtvRGtGW3IGfPSPHUIlHXb2dTRxObO5updfakZFNhyeHW/FLuLChjljn7vHiV1tMM7N2Jt2oPyummlGy0E8swLFhE2sKlaC+aeF68qrsOxbaJQM9m1P7alGxkcwWanFvR5t6JbJp1XrwdHR3U1NRQV1dHe3t7Sjbjx49n1qxZVFZWUlBQcF68SksTA7u2M7D3Y5TmxpRstKWTSVt4JWlLrkFbUnZevA0DVt6yH+Sd3hqOes6mZFNunMAXsyq5JXs+M9LiNxBxhCTwNzyPv/4lVFstctZUNIWLkPPno8kuR7KUIBmyEV47wtlCwH4UtfMgAese1N4TAMi5FehmfhXdjHsAKaWDFsAfzzSwvq2eQw4bM8xZLM0pZEFGPhXp2ZQZLeRoDfQoXpo8TmpddqocnezssdLQH2wx5mXksrZoJvcXz0iRFRACz7Z38XzwV/xNJ9EWlaArr0A3ZQbaiWVo8guQ0zNQXQ4CnR0op5vwn2zAf7QWpa0FAF3ZFIzLbsC4/EaQUmUWKB1/xN+xHtV1CNk0AzljKZr0BcimCqS0MiRtDkLpQQw0obprCbiqUB07Ud0NweucPg9dwVq0Bfenfp2FoKqqik8//ZS2tjby8/MpKyujpKSEwsJCsrOzMZlMuN1u7HY7VquVlpYWmpqa6OwMtpBFRUVcdtllLFiwACnV8xUC93tv4dn6Nv7G42gvmoh+9jx002aiLZ2EpmACssWC6nQS6DiL0nwK//F6fEcOobSeDl7nydMwfuEmTNffkvJ1Fgie797DS92fUOM+w9S0AhalT2G+uZRZxvGU6HPJ1pqwK25afDbqPO0c7G9mj+skJwY6AKg0FfPVcVdwz7hFSDGuc5SQhKcTX+1/4K95GjlvLrpJt6It+xJS+kXJD9jTiXJyE/5Tm1G7DgdPvPJB9BXfRjLmJ7S1+jz88nQtv2mu4ZLMPFYVTGJlQRkT09KT8lp9Hv7SfpKNHafY39cFwPdKK/nBxAoK9caEtmqvnf7/3oT77TfQTZ5G2hVXYVh4JZq8xMcbsh34+EMGPvkIf+NxAEw33Y755juRsxK3isJvxX/2l/jbfoNsuQRt7iq0uSuRDMlbNeG3onT9BcW2EdW5HwBd0ffQTfgBki5xt9rpdLJz50527dpFcXExc+bMoaKiguzs5K240+nk0KFDVFdXc+bMGQCWLFnC0qVLsVgsCW1Vew/9b75C/+ZX0U2dSdqSa0hbfDWa/OTDANXeg2fHVgZ2bcd/oh4A862rMd/2ZeTsnIS2nX4Hf+j8kKc7djDXVMJt2fP5UvZcLtIntgvZburZz5v2gxx2Bx+YDxZczXfyryVfF9m91fzkJz/5SeiL8HTiO/AE/rpn0c28F8MlP0I78UYkffI+IoCkM6MpWICm6CpQ/ajdh1A7PgO/C03ePCSdOaad1efhscYDPNVSxwMXzeTxyZdwS/5EsrT6lHjTNTquyCpgeU4RPqGy39HN3t4OnAE/l2bmka7RxbRTe+24Nr2E570tmL5wE+l33YNhwRXI5tjHGXW+aUZ008vRV86HgIK/8Tj+4/WIAQ+6ydOR0mKLWPit+FofQ2l/Ct34B9Bf9DjanFuQtFmp8WrS0ViuQJu1HPChuvajOvciVCea9EuRNLEfPk6nk23btvHJJ5+wcOFCVqxYwaxZszAaEz9sQjAYDEycOJGpU6eiqipnzpyhpaUFr9dLcXExBoMhpp1q78H18nO433kD0w23YvnqWtIWXolsTv6QBJCMRvQzKzDMuxQUBf+JBvwNRxAeN7qpM5HiHH+n38HP29/j2a6PuTdvMT+e8EVuzKokU5Pa+Zo1Bhakl3FVxnT8IsAhdwuf9TfjVL3MN5Vg1pw73yFCEvgO/QLl+CvoL34Ew6WPIhnzUiKMOnFDFtqS5aDRoXYdINC5HySBtuhqhnc/BPCTU4d4oe04j06+mCemXkpBklYkHrJ1Bm4YV4JO1lDV18Xevk6EJHFdblF0YywErtdeYmDH/yF95d2kf3kNcub5ja1kczqGeQuQtFr8J4/hP34UEBgqL47R/RD4z/yEQNcL6C96FH3JE0i68xtrSNpsNNk3IMk61P4qVMdekASarOuIus5CsG3bNvbv38+yZctYsWIF6emp3cjDYTQamT59OhqNhtbWVlpaWhBCMHXq1OhunhC4XtmA54O/YrnrXixfvx85K3lrEAtyugXDJQuRtLqgg6LhCCCCApOG31eCJ6zv8YrtUx4Zfz2PFd1Eni5xqxkPWRoTyzPL0UlaDvSfZn9/E6okuDpjRribFxaSv+F5fJ/9DP3Fj6Cf8z1S7W/Hh4SmcCFIEDi7C7XjMyRTHppx8yJK/fFMAz8++RmPTr6YH5XNGQVWuDK7EIHE9p6z7O3toMBg4tKMSI+PZ9u7uP7rBdJX3o35ti+PYFwTj1hCP7MCEPiOHMZ/vB5NVg66ydMiiikdf8R3+sfoL3oUXdGPGJXrnHElIAj0bUd17kXWFyCnXxpRqqqqiq1bt7Js2TKuueaa1Mc18VglibKy4KC/sbGRlpYWMjIyKC4ujijnfu8tXH9ej+WuezGv+uroXOfZc5AE+GoO4G84gpydg27qzIhiz3fv4fGz7/LI+Ot5uPC6mOOaEdEicUX6ZASwy3mcz/qbyddlMM9UAgwKSbU34Nv/ONrSL2K49FGS/biuZ3PxHXwS38En0U6+HSkt/hNGU3g5wtON2n0I0W9FU3g5kjF4U9f12/lx435uzS/liamXXvAtNRRLsgvp9HvY7+jmrK+fxdmF5A+2dErrafpffYG0yxaR/uU1ET+u0taC0tqcUt8dVcV7sArZaAp3L/QzZiMcffgbj6PabehmzEbODHbZVHcdvtYfo829FX3JEyS6zsJ3FqVzA0rX86h9HyB87UhpU5Dk2N0nTcYSUDpRXfsR/rNoLIuRdMFxXkdHB1u3bmX27NmsWLEioYjcbjdbtmzh/fffp6GhAbPZTG5ufPd+aWkp/f39nDlzBofDQWlpabilU1qacP15PWkLr8Ty9fsvXERDoJ9VidrXi/9EA2qPLTgNMdijaBiw8vjZd7gxq5LHim66YBENxcL0SXQrLg65W7D6+7g8fTLjtOnIAErTWyBr0JXfy4U/IYdDQlf5EHLeXFRbbZBrEJs6mtBIMg8Wl48Ka31/L58OOhsk4AcTK7kkM49DDhubOs65swf27gRZxviFmyJ+XLXXjvvtN+h/4xUC7WeS8nmrD9D/2p/xbH+fQHfn4OlKmG5eiW7yNPxNJ4Ncg1Bsm0DSoC14kETXOdCzGc/hcnxN30Wx/gn/2T/gPbkGz+FyAn3b41hJ6Cb8ANlyCarrUJBrEDU1NUiSxMKFCxOKqKWlhccff5yOjg5mzJiBTqfjxRdf5NVXX41rI0kSV155JcXFxbS1tVFTUxPeN7BrO8gaTDfeGlNEgY52+t94hYGd2xCKP2q/6ujD/e5m3O9uRnU5hxNjvv3L6KbOxN94PMg1iLfsB5GRWTNuyaiKCIIt07cLrmWuqYQa9xnesh8EQBZuK0rzu+gm3YqcPTNJNecH2TIR3aRbAVCa30W4rZz1utnc2cyqgknMTj+/sclQ1Lrs3Fb9AbdWb2NfX/CmLjNaWFUwCYDNnc2c9bpR7Ta8VXtIu+IqtBeVRtThqz2E58P38NUewvHc0wQ6rXH5fEcO43pxHf7G4/Rv/i8Gtm8N79PkF5J2xVUAeKv2oNptwcnWns1oc1chm2bHrVftP8xAw+0IpS+4QdaDHHSWCO8ZBupXoHrqY9pKhjK0uauAoBiF7ywOh4O6ujrmzJmTdN5nw4YNXHXVVXzrW9/ihhtu4O677+ZHP/oRtbW11NfH5gTIyclhzpw5ANTV1eFwOFB7uhnY+3Fw3mfipBgnqjKwcxvu/36N/rc24a8/ElXEe2Af/Vs24v7v1/Dt3xu1X1MwnrQl1wAwsPdj1J5urP4+3umt4bbs+cw0jo+y2Ww/xEXVPyDn4HeYWP2PbO2ri9ivCJVvn36FcQf/npyD32H5sd/QrbgiykzU53Jb9nwA3umtwervQw5Y94DqR1v2pbgXajSgnXInctZU1J6jBKx72Nlrxa+qrCw4v8m1oTjstLGy5kMa+ntp97r59rFzF/3u8VOYYc6i1tnDzl4rvroaUAIYFl4ZfYxlUzBcthgAX/V+nM8+FVNMvrpqnM89hdLWgqTTY7zmevRzL4kok3bltWiLSlBON+GrqyHg2AnCjzZ3ZcJz8bX+M0EXjIy+9BeY5jdhmn8KXfEjgASqH3/rv8S11+bdjWyagdpfS8Cxk6amJgKBABUVFQl5jx8/TiAQYPny5RHbTSYTc+bMYdu2bQnt582bR35+Pu3t7TQ1NeGrPQwBhbTFV8c+z5oDuP7reQK2Lvwn6nG99CfU/nM3rNLajOvP6wmcPYPS1oLzz+tRWpuj6jFe/QW0F01EaW7EV3uYPa5G/CLAl7LnRnOKAOu6dtIf8ALgDHh4qvPDiDINA+28bNuHSnBW6EB/M+/1RYv8zpxLmJpWwFHPWfa4GpEDXQeDYT0pzBNdCCRjPprCRQAEug5S1dfF0pzClOaJEuGw08ZdtTs4Njgha9Ho+M5F52b6C/VGluYExztVfV34Tx5DV14Rc55IW1xCxpqHSBsUmfdQVVBMHedm/X1HDuN87j9RzrQg6Q2YbriF9LvXoJsW2ZrLWdnoyoM3r//kMVRXFXLG0sTzRKoX1fUZAJrsZegmPIKkn4CkL0Zf8gtkY/ChE3Dui1uFpCtEzlgarM5VRUtLC2VlZUnnibq7uzHHcfsXFxfjdrsT2lsslrDzoaWlBf/xo+hnz4s71gx0dyH8/iHfOxFOR/i72tdHoKvz3Hd7D6o9OspFzs5BPzvowPIfP8qB/mYWpU+JOU8kI6GXNBHb9JI24rt22H4AXYxt+boMFqVPAYJik1VbHXL+/JgnO9oI8ai2OmpcNhZkxJ70PODo5pi7L2l9Na4e7q77KBzVkK7R8Uz5Yv5u/JSIciGeGpcNpaUJ3ZT4oUtydg6Wex4g7fIlwKCYnn8Gpa0V39EanBueRmltRtLpMd14G+Y7/w5JH9sBEOJRWppQ3TVo0hckPB+h9CB83QBRghNKD0KxJ7QPIcSjumuwWq2UlJSkZHehCPFYrVb8zY1RD5eh0M+ei27S1PB3w+VL0OSd63pqJ5aStnDJuf0XX4Z2mAc0hBCPv7mRo56zzDeXxiynlWT+vmA5BYOxc0X6bB4u/EJEmelpBfx94bKweJZnzmJFZuwQrBDPUc9ZtKqjCU12edwTHk2EeFRHEyctDiqGjY0EsNNu5a7a7YzTG3ijchnTTLEng2tcPdx95CPqnMGby6LRsW7WEu4qiO6Ph3hO9jsIWNvQTkzcnZSzc7Hc8wBCCLyf7sZ7YB+qsw/hHQiKSKvFdMMtmG+/K66IgDBPwNqGOnAS2ZS4eyVpMzFMeRZEANk8J7xddVXhbbwX4Q+eqyZzWeLjH+RRB05is9koLBydIOJkCPHYbDYC7W1oS2OMjQahKZxA1g//De+hKiSTmbTLFoPm3JNftmSS8cDDGC6+HKGqpC1cEncCN8QTaG/jlDebWTHGRiFcmzGD96d/l5MDXcw0FjJBFzkJLiHx2ISbuSlzDv2qnwXmiRjk2BP6IZ5T3m60wtOJZPl/88QK8QhPJ50+D2XGyAkyu9/LQ8f2YPW6sXrdrKrdzqsV1zB9mJhqXD3cVbODo/3BGytDq+NP5VeyKs54K8TT6fOg9vaiyU8+ASrnjCPj3gdxpRnx7NyG/3hwsC2nWzCuuBnzzSuRDGkJ6wjxqL29CH8nUlqS8aBsQpu/JmKTv/23+JoehsE+u2ycgn7ikwmrCfEIfydOpzNpt+73v/89TqcTt9vNr3/966j9Ho8nvO+aa65h3rx5MWohzON0OlF7e9AUJI5O10woxjShOO5+OTsn6FlNghCP2ttDp+KkRJ84Gn+iPpeJScrMNycP1QrxdCpOtACS4cK9ZqlgOE+ONvJpbtJoebC4nEeOf0p/QOGww8aq2u28Mvtqys3BJ8dhp41VNds5Ptj1y9TqWT9rCSvz49+kw3nk9NSWAcg54zBcuhDP7u0QCATPwZxO2uVLkIzJl2wM55G0I5vRVz3H8DV9L/xdm/dl9GVPIWkT/17DeUymxMfa3t7OqlWrkh7Pzp07qa+vjyuk4Txykvi70cJwnmzt6CynSYahPLJkzEd4U+t7XyhCPJIxn3y9kR7FG7E/TdbwYPFMfjfjCkya4CCw2mHj7iM7OOVxUt/fy+raHWERZWh1rCtPLCIgzJOvNyJnZaG6HAnLh+Crq6Z/86thEQGoPTZcr/2ZgDV5GH6IR87KQtLlI5TUloSEILzN4c/agq9jmPpyUhEBYR5Jl4/FYknqKJAkiXnz5oX/AoEAW7Zs4eDBgxHbkwWmhngsFgtyVg6q05mw/GghxCNn5ZCvtWBXEp/vaCHEk6+1oJUzyhDOFqS00VmclgjCGYyglTPKmGLOoMnjZJwuunv0jQnTCAiVf2jYi0cNcMhh47aaD3ArCicGRWTR6Hhm5uK43bmhaPIEL/QUcwaawiICnR3IlsSBuL4jh3E+O+ji1hswLrsB1dnHwK7teD/dA34/lnu/haYgfn880BkMwdcUFiGnTUEMNCGlj2BxmiSHP2qyvpCgYCTEQHDyWU6bQm5uLna7Pa5HLhb27dtHTk4OJ06cSP1YAbs9+KDMzc1FM76IQMdZ5IzUAp4h2DVTXU5QFNDpkDMyk/5OAIGO4ENNM76ISYZxtPhs5GpTP9/zRYvPBsAkwzi0cu4sAvajyHmxm+tYSP+G7byIA/bg4kA5dxaV6bnUuuxcmhE7MPabRTOQkfjusb24AwrVjnOcGVodfyyP7ViIhVpX8AeuTM9FW1KGcropKgZuKHx11Tif/Q+UtlaQ5aBj4bYvIwY84PczsG8X3kOfIZ79DzK+8e24YgotDtSWlCGbfKju2qgYuIQQyrnPgdSf7qo7uDhQNlVSWFiI1WqNioFLhOuuu44NGzYwffr0lG0g6K2DoNNBhwel+VRUDFzUsTr7GNizE1/1AZRTJ1B7exBCIMky8ri84JKLhUvQz78MSRt70K80nwJAVzqZcuME6jzt4Ri4oWgYsPKh4yhr85ZGucHjoS/g4Xcd21honsJ1mZFOuTpPcFqk3DgBWZM3H7XzYEqVXihCPJq8+SzIzKPK0Zmw/Nqi6fx6+uVR29eNQERAmGdBZh66KdPxn2yIW9bfcATn+kERaTSYb7oD821fRjIaw65xw2WLQAh81QdwrP9DxDxTRF2DPLop05HTFxBwVaV8zACy+WLSyt8nbdZWNJmpt0ghHjl9ASUlJbS0tIyId9q0aVRWVnLbbbeNyC7EU1JSgm5aedhBEwuqy4l7yyZs3/0GjqeeZGDXhwRsXaDXB1sxnY6A9Syebe9i/9cfYv/JD/DVHopZV4hHN62ci82lHOxvjlnursZ1/MuZt7jz5B855E58TVwBL6/2fMaKY7/l99YP+H7rJmxKf0SZEM/F5lK0msJFKI2bEa7WlCdlXc+e6waaVn6KnDklQekghKuVgHUPck45msJFLNVksNHayOkBV8JJ2fuLZmD1evhp4wEA/lS+hNUjENHpARc7e6xUWHJYmlWIflYmA5/sJNDVGTUp66urxrHu98E4O40W8xdvw7zy7gjvnJwzjow1D+GUNQzs/RhfzUEcT/8a47UrSLvynFs60NWJ/2gt2oll6GdVIpmno9g2IrynU1q8B8ExjvA2IwJONJnLkxsAwnsa1bEzuBQ9Yyll+nSqq6ux2+1xvXdCiKh4OlmWee+99yK2Wa3W8KTrcNjtdpqamhg/fjxlZWXolaKgODqtUZOy/lMncP7p9/iODC7+nDkbw/zL0JdXIueOQzKkIbwDBLo68NUewrt3F77D+7E3HCH97rWYbznnGAl0WvEdOYS2dDL6irksStex2X6AVl9P1KTsV3Ov4FfWrXzsPMbyYye4KWsOV1umM8M4nkyNEa+qcNrXQ427lb/21VI/uBx9nNbCA/lXYdGcuw9afT3scZ2k3DiBRemT0UqmQuTc2ShNW9BVPJToN7ogKE1bUHtPoJ//AyRTIROAOZZcXuto4vsTE8+v/GTSPNJkDTk6A2uLRtbdeK2jiYb+Xh6dPD+YHMVgQls6Ce/ejzHdfEdEWeHpR+3uDM4T3bwS8x1fQdJFLy6Us3Ox3PstkKSgKDvOojoiHRjevR+jtLVgXnl3ODmKbJqDYnsN3YTvp3Tsgb4P8DbeD4Cky0Ob97WkNortNVR3A7qLHkXSTyBDH8yzUFtby5VXRodFAVx77bXYbMm765MnT+b666+Pua+2tpbOzk6WLVsWTo6iLZvCwO4dmG+7K6KscDrw1deiGV9E+sq/w3DZ4nCE/FBoiydimLcA0/Iv0v/GK7i3vYPa0x1RZmD3DpTW06TfdQ9yzjgKgdnGIrbYD/NQwTURZf+hcDmLLVN5sv19PnQcZYv9EFvsh9BIMmmyDkWoeNVz0RZZWhM3Z83lOwXLmGSIHNtusR/mxEAHj4xfQaEuM+j+1pbdgq/6d2js9Z9L4Kpqr8d/ajNybgXaslvC2+8sKOOJ5mqOuOxJA1d/WFo5Yt4jLjsbO04xLyOXO4c4JdIWLqX/rVdRWpsjAlf1FRdjWfttVFs3puu/FFNEIchZ2aR/ZQ1ybh6GOfPRzTgXiKq0NjPwyUfoyqaQtnBpeLs29078Z59AdR9JGLga5kibipRWAoF+ZFN07NhwqO4jKLaNyOnz0ObeGd5eWVnJRx99REdHR8zA1eHxdSNFR0cH1dXVFBUVUVl57ndKW3IN/a/9BeX0qYjAVf2sOeT89FfIWTloSycnrV8zvgjLfd/BuOz6iHqU06cY2LU9mCJgyTnR3JI9n99Zt1HvaY8KXL3UXMprU+5nj6uRrX1H2O6op83Xi08oaCWZLF0ms4wTuDZjJsszypmSFh19U+9p5037QSpNxdwyGLyKGISv/jkxsPv7QghVjC5UMbD7+8K5Pkf46p+L2vt0a714oH7358AqxAP1uwX/Z714urU+ar9769vCsf4PQqijzKyqwrH+D8J6x3Lh3vp21G6/9WnhPfWASPk6B9xCVfpSIRbeUw8I1x6E3/p01N59+/aJzZs3C3WUz1dVVbF582bxyCOPiH379kXt73/3TdH3n7/6XK5z33/+SrTfuEj0v/tm1O7nunaJh1s2CjXJdVaFKuxKv2ga6BZnvHYxoPqTln+4ZaPIPvBt8VzXrvD2sH9VN+Me0KUR6BjZgDgZfNW/xV+/AV3lg4MZhSJxf/EMjBode3o7RpX3Z03VPNNaz/dKK7m/ODq2zrj8RtDrB5eFjx76N/8X7q1vY7rp9iDHMGgL7gfZSMC5J7UKZSOSJvkEsr/tZ/jbn0FX9L3BjEKRWLBgAVqtdsSOh2TYsWMHe/fuZcmSJSxYEB1LaLr+FiSDAV9DdAT1haB/059x/3Uz5ltXBzMKDcM94xaRJuuociVOqyYhkaUxUWrIpUifhWFYEOtw/Ma6jQ1du3mw4GruGbfo3I4ItXl7hepzjeS5kFC73sO/Fq4XisXArn8Qqrsjbkm73yucim+UWIX491OHRfqHL4j7ju4S7V53/LIup1A98fePjFgVrjdeFh133yz61v1OBOw98Yv67UJVnKPDK1ThO/Pvov/TdDHQeJ9Qfe1xS7rdbuH1ekeHVVXFhx9+KH784x+LN954QzgcjrhlA6N9nV99UVhvXyb6/uNJEeixxS3aq7iFKzAwOrRCFb9q3yqKDn1ffPf0q6LDF9lLSJggkoAXNPGDMuNBtdfjP7oh3BKlko5rKLxqAIOcmp9/KI647Dx95mi4JUolHddQCL8fSRd7riIRlNZmPFvfDrdEqaTjioDqhThLyBOauY+gdDwdbolSSccVcdyKglab+AkcCx0dHezduzfcEqWSjmsozvs6nz4VXDE72BKlko5rKLxCSdrixEK9p53nuneFW6JY6bjiC0lV8DdsgIAv9bx2rlaUpi34T20GNTDiBJEQXKH49JkGfGog5bx2pwdcvNbRxMaOUwSEOvIEkQCBAO6tb4OipJzXLtDViXfvxwx88hGo6nkkiASEgr/jaVB9qee1855Gsb2GYtsIIjDiBJEAqqqyd+/e8KK/VPLa2e12amtrqa6uRggx8gSRELzO725GKP6U89oFOq0M7N4RXE6uBkacIBKC99Vz3bvwq4GU89q1+nrYYj/Mm/aDqKgjSxA5FKGUxartCJKxIKVMq8i6UUtZXO20UWgwppRpVSfLo5ayWGk+FVyYl0KmVbSaUUtZrLqrFPi7jAAADFNJREFUkXSFKWVaRdKNWsri9vZ20tPTU8q0qtFoRi1lsdJ0Ejk7N6VMq2i0o5ay+IinjXxtRkqZVnWS5kJSFkdiLIn+WBL9sST6iZGSkMYwhjEkxshHXmMYQ2zEHLAkGz+pqroUWAyUAIVAAOiSJKkJ2C1J0u5RPs7PhVsaa5DG8HkilpCEEHOFEA8At0qSlDAvthCiDdgMPCPL8gVP+n1e3GNCGsOoI14rJIQoEUI8IEnSD8+nXiHEYwRv6q7zsP1cuceENIZRR5xWaJkQ4jVJkiKiU7u6uqitraW3txeXy4UkSZjNZnJycqisrCQnJ2d4Pe3AalmWP071eOJxd/R5+bRFwTYgc6ZPBWDaOJk8k+DyEh2mNN3weuJyjwlpDKOO4UJSVfVrkiS9MHRbTU0NH3zwQfjFZfEwYcIErrvuOmbMiJxKEULcLMvy28mOJRb3p80DPLvfT1VLdJrkEEw6iWum6vnGJTrKxkUGL8fiHhPSGEYdQ4WkquoXJEl6P/Td6XTy8ssv09zcPKI6p06dyl133RWRYEUIsTRRyzScu6df4dFtHnadii+g4dBp4OuXGLn/cgNazbml/8O5x4Q0hlFHSEiqqpYBtZIkmQHa2tp48cUXcThSSz4zHLm5uaxZsybc3RNCnJYkaY4kSVHZRAe5qyVJsgA0dfv4zn97aOkNDC+aEhaV6vj1F00Y9ZqY3HJC6zGM4cLwQEhEDofjgkQEwcSTzz33XDhbkSRJE4EHEnBbAGz9Ab61xX3eIgLY0+znh+95CKgiJveYkMbwuUAIcbEkST8Iff/LX/5yQSIKwWazsWnTpqGbfq6qakSug+HcP3zPTdugM+FC8FGjj2erBmJyX/CE7B0b2/5ZkliSvOQ5CMGuN1YX/wxg2pRL/lkgjcheQuw6duKzn4W/D+mT37HxTOwMGTHw+qriqNRJ06ZekrL9seOfpZ566X8ZBl3NABw+fDjmOqgJEyZw9mz8/IB6vR6TyUTvYHhWCA0NDRw/fpxp08KZoB4AHo7Fve24L6ZTYXqehmNd8Vsok04iM02i3RkpwOeqBrh1toH89HAb9ADwsLayYrEEPFJTu/sXcWsFKisW/yPwZE3t7ohBlSSxUpZ4WBWklKNLlshV4ddAUAgSK0E8rKoiNXtZymWo/TBIEsnXZCeEdIH2YwCQJCmce/nDDz+M2j937lxWr15NY2MjL7zwAn5/5M1eWFjImjVrMJlM/PSnP8Xn80Xs37ZtW1hIg1xhIQ3lfmqPJ4r7hhl6fn5DOlUtfh56y4VXifQTTB2n4Y+3W8hMk7nyaTtu/7n9XkWwbp+Hf1lmjuDWAo8Aj1dWLJZranf/PNZFqaxY/E/Avw1+jRKcKvx1r68qi1rieturZ8fJcqA2sqwyFyL98xqNqDtx4kCUfVlZ5TitVl87rOxcNTDabxUcw2hCVdVwEojm5ma6uqLnMBsbG3E4HEyePJmvf/3rEWLKz89n7dq1mM1mTpw4ESUigNbWVqxWayhxf6aqqgtkWa4ayl3drtBsj251DrUpdPerLCjR8dQt6RFimpSrYf3KDLKNEntP+yNEFML7x3w8cpUJg1Y6x01QGP8C/KyyYvFPhxsNbvvZYJmErdZwvLl6Qvfrd140XlY0V0tC6peEFJ0qJgGammq6T5zYP14I+WoJqV8iub0QzE30l8w+oKpzE/2N5Pj/F2Nx6EO8bK1Op5N169ZFiEmn05Gfn883v/lNzGYzp06d4qWXXopLMqzuxcP+s+90bDd3u1Nl7evOCDEZtBKTcjVsuDMoov1nFP5+iyumvcsrONoRIdDF2pra3QA/r6xYnAY8WlmxmJra3Y9BWESPAv8ar7VKhttfPTtTyIF3VJVvSDKvMEJv+6RJl86UUd8RSN8A8Uqy8q+vKq5OtH/lpsTvhm1sPJDQftrUEWRK/d+LcM60WK1RCDabjXXr1vHNb36TyZMns2bNGvLy8sIiev7556O6fEMxrO7pw/7TaIs/BjplC7D2dSfr77CwoETHutstlOZowiJ68E1nVJdvKBq6FOYVhV0M08Ofamp3P1ZZsRiCYgqllAyJ6LG4NSbAqk2nywME3lFV1ryx+qIdd2xqHZH99EmXlKuyeEdI0poTJz7bMXXqJTHLpeJgiOVYCCEVB8PxE/vHHAspQpKk8II0lyv2Uz0Em83G+vXrWbt2LaWlpQApiShG3YXDuW39iT11Q8UUEkUqIgKwuyP2F0Z47QbF1M+5Ltw/1tTuTvwynjhY+XrLrIAqvS0GRQQgVLE41dXB06dfPEsN8DaDIgLQKPJiNNFPmTEHw//fkCQpwvMqy6MzKxM4j1gDzXkOvy/4iAf0hiWv31ka5SgQqrx0qIgA3lhd0hhVDu2ShoZoR4OqykuHigigoakqyn4Mf3sQQoTfYJ2enjjnRl5eHmvXrsVisdDc3IzT6aS0tJQ1a9agS5IgZVjd1uHc48yJb+/SbA3r77AwziyHHRDzirSsu90SciTERbYpYr81okUaMib6x8FNv6isWGxO1LUzeL33r3ytrYghYw9VVatev7P46YRHMghJBO6fOuWSoshtVB0/8VlK9mP4m8Sx0Ie8vPhLfvLy8rjvvvuwWCzh7lxWVhb33XdfWEwbNmyI6bWLUfexYf+ZlBM/E1Vptobn7gyKKNSdK8qUw928dbdbePBNZ0yvXch+KHdYssMcC08Odun+leCYKcqbF4Ik8RWEqEewD8E+wCNL0nVxzyCqAvEVCaleIO8TyPskJI+QR2A/hr9FhFeWTp06NWaBkGCGisjv99PV1cWf/vSncMv0ta99LW5Xb1jdu4f9Z+HE2C3aeIscJSKvIsJjplDL9PsvpSPHaJgMWon5RRFt0G65smJxaJ4oyrEw+Dkkpn8adEZEQaBseW1V8cbXVhVvVFV1Z8xCCSBr1S0nT1ZtPHmyaqOKGLH9GP62IEnS9tDnkpKSqDVFAEVFRVgsFhobG6McCyExORwOSktLY+beKyoqGvqS6T5ZlquGc8+ZoKUoM1qEMwu0jDPLVLX4oxwLITF1ulTmFelIi9HFu2GGfmjXr0+W5SotwW7cvwH/HMvFPeiAGBgsozLCuaS/NaQyl5QIY3NJySFJUkBV1b2SJC2UZZlly5YNj4+jrq6O3/zmN3HXI3V1dfHLX/4So9EYs2s3NPG/EOKFmNwSPLTIxD/9NdJzuP2kj1tf7ONUHPf4KVuAL27oIzNNiura6TRw74Jzr3cJcWuBJwEShQjV1O7+eWXFYjVUdjgCssHwdxuaDAADGlnHYIRsaNtQuGSDQaMOd0tqDUVFiw0AsvDoxKAHJ7QtEn5DMEfF+SHZPFMyJJtnGkMYDwOfQDAcaM+ePbS1tUUUSLaoz+/3x3SBT5o0afhCv2fica+Yruflg1qOWJWIAvFEFIJXEXS6osdHX5mXRklWxPjoGQDtYOxc0lYmgdDOatXAzgHzYPOrCoTE6wADZm3Uq/G0agAkaWt4g5DOBpTATpMxeGIimIzmdQCTcSDKPqAAEluHbx/D3xZkWd47tFVavXo1zzzzTNIXQydDRkYGq1evDn8XQjwuy/KxoWUiuCX47c3p3PWyg+4k80rJsKBEx0OLIhYWhrn/RyzsW7mpbU6yMolaosmTL05qH6sl+p9w7T4PDFnYNweokiRJD8GFfS+88EI4+eRIkZWVxb333kt+fjCdtBDCBsyRZblteNlB7k8lSTIAHO1Q+PZbrvMW07wiLb+92UK2UYrJ/T9CSGP428Kwpea3S5L0euh7b28vL730UsLlE7FQWlrKV77ylYhk/UKIFbIsx+2dDOfu7lf5/jsuDrUp8Uxi4o5KAz+82oxuSI9uOPeYkMYw6kgl+clnn33Gjh076OnpSVhXXl4ey5cvj3gTIIAQYpUsy5vimCXk3n7Sx/pPBzjakVhQV03W8+AVRqbnRc5HxeIeE9IYRh2x0nGpqnoDsEWSIt+r0tbWxtGjR3E6nXR2diLLMuPGjSMzM5NZs2YNdXEDIITolSRppSRJH6R6PPG4z/YpfHI6QH3nOUEZdRKzCzRcVqIl2xQloLjcY0Iaw6gjXoJIVVXLCeZSOK+3fgshnpAk6RlJkkb82sHPm3tMSGMYdaSQ73shwSXad0iSlOxNcH1CiNcHb+IDF3psnxf3mJDGMOoYyYvHBle0Lia4DKJckqSAEOIY0AbslmV5e8IKLgCjyf1/AfzC3EiJBPQWAAAAAElFTkSuQmCC) no-repeat}.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a,.layui-layer-btn a{vertical-align:top}.layui-layer-move{cursor:move;opacity:0;width:100%;height:100%;filter:alpha(opacity=0);z-index:2147483647;background-color:#fff;top:0;left:0}.layui-layer-resize{cursor:se-resize;width:15px;height:15px;position:absolute;bottom:0;right:0}.layui-layer{border-radius:2px;animation-duration:.3s;animation-fill-mode:both}@keyframes bounceIn{0%{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.layer-anim{animation-name:bounceIn}@keyframes zoomInDown{0%{opacity:0;animation-timing-function:ease-in-out;transform:scale(.1)translateY(-2000px)}60%{opacity:1;animation-timing-function:ease-out;transform:scale(.475)translateY(60px)}}.layer-anim-01{animation-name:zoomInDown}@keyframes fadeInUpBig{0%{opacity:0;transform:translateY(2000px)}to{opacity:1;transform:translateY(0)}}.layer-anim-02{animation-name:fadeInUpBig}@keyframes zoomInLeft{0%{opacity:0;animation-timing-function:ease-in-out;transform:scale(.1)translate(-2000px)}60%{opacity:1;animation-timing-function:ease-out;transform:scale(.475)translate(48px)}}.layer-anim-03{animation-name:zoomInLeft}@keyframes rollIn{0%{opacity:0;transform:translate(-100%)rotate(-120deg)}to{opacity:1;transform:translate(0)rotate(0)}}.layer-anim-04{animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.layer-anim-05{animation-name:fadeIn}@keyframes shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-10px)}20%,40%,60%,80%{transform:translate(10px)}}.layer-anim-06{animation-name:shake}@keyframes bounceOut{to{opacity:0;transform:scale(.7)}30%{transform:scale(1.05)}0%{transform:scale(1)}}.layer-anim-close{animation-name:bounceOut;animation-duration:.2s}.layui-layer-title{color:#333;text-overflow:ellipsis;white-space:nowrap;background-color:#f8f8f8;border-bottom:1px solid #eee;border-radius:2px 2px 0 0;height:42px;padding:0 80px 0 20px;font-size:14px;line-height:42px;overflow:hidden}.layui-layer-setwin{font-size:0;line-height:initial;top:15px}.layui-layer-setwin a{_overflow:hidden;width:16px;height:16px;margin-left:10px;font-size:12px;position:relative}.layui-layer-setwin .layui-layer-min cite{cursor:pointer;_overflow:hidden;background-color:#2e2d3c;width:14px;height:2px;margin-top:-1px;position:absolute;top:50%;left:0}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{cursor:pointer;background-position:0 -40px}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;pointer-events:auto;-webkit-user-select:none;user-select:none;padding:0 10px 12px}.layui-layer-btn a{color:#333;cursor:pointer;background-color:#f1f1f1;border:1px solid #dedede;border-radius:2px;height:28px;margin:0 6px;padding:0 15px;font-weight:400;line-height:28px;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{color:#fff;background-color:#2e8ded;border-color:#4898d5}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{word-break:break-all;padding:20px;font-size:14px;line-height:24px;position:relative;overflow:hidden auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{width:30px;height:30px;position:absolute;top:16px;left:15px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{box-shadow:none;border:6px solid #0000004d;border-radius:5px}.layui-layer-msg{min-width:180px;box-shadow:none;border:1px solid #d3d4d3}.layui-layer-hui{min-width:100px;filter:alpha(opacity=60);color:#fff;background-color:#0009;border:none}.layui-layer-hui .layui-layer-content{text-align:center;padding:12px 25px}.layui-layer-dialog .layui-layer-padding{text-align:left;padding:20px 20px 20px 55px}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-page .layui-layer-btn,.layui-layer-iframe .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{width:100%;display:block}.layui-layer-loading{box-shadow:none;background:0 0;border:none;border-radius:100%}.layui-layer-loading .layui-layer-content{background:url(data:image/gif;base64,R0lGODlhPAAYALMPAPPJp/HYxfSwkf50Df5TD/+HAPe5bvqHR/twOviZavyrMu/m3f9EAP9mAP+ZAO7u7iH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6QUNDRERBRjMxRTIwNjgxMTg4QzZERjVGQ0I0MDI1NDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTlDMEI5MzU5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTlDMEI5MzQ5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpENzU2N0YwMTMyMjA2ODExODhDNkRGNUZDQjQwMjU0OSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0NEREFGMzFFMjA2ODExODhDNkRGNUZDQjQwMjU0OSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAkFAA8ALAAAAAA8ABgAAATy8MlJq7046827/2AWCEdpCkGodkHSvHCcLGttLUOsw0NqW4EDYog4+CyInbJxwAQMiqjCcMQkGNgsNmFxLZXcisFBLpMNGYF2zRBQAt9vFWCuOwCXAJt9FMSXbhILdnY0FQd7a00SB39Kiw9jhGZoFQSJWgQTjksTCpNmChaYa5ucOxOgdaOkWaanMamqZRaXrZoSObAvA56zDqKHrQyQjbtME5KqlW/DRwbHDcwBv1UTV5hhEnDHVcqEzF2J2te75BLflBsCCFoIgRU4pwOGFQCfZQp4PxUBSX8IrPELscDLkhkDB7bQxQthwocQI0rMEAEAIfkECQUADwAsAAAAADwAGAAABOjwyUmrvTjrzbv/ILcAiWCUQKh+y9G88DsIa40JcQ4ji30JCIZQeAhcAgOdMvEYKZ5PQA8UHFoZtEpSuVQ4vmCHwuhJXK+EqcTA5YbfYrUmcD4jKNt2rAB/GzoHdWdTAXo6fXByGASCV1k4hjADiG8pG41XTA9skS98lGB/l5hDmgmdnqChHIykWBKQnZOqDpYaVaRkAKgvtA6KFwCudxO8DZ+UCh64grYPp7yqwBgLzFYEWRNIvAlecGMhAq1DCGQVu7JNBn7THQDvAObBeVwJ7T4gLW2i+P0PAQkOCBQoxZ/BgwgTYogAACH5BAkFAA8ALAAAAAA8ABgAAATV8MlJq7046827/2AojmMgHE2aIslCckAcb8Kg3umQvFhCMEAgYXcR4HCFpEJhCPAkiKAUiLAEbMdGwcHtcg28w3R8qKCyA6/aASABxvC2JJBNrdcuURQ+rUqMWVt3XmAhC3xweWdHg2oKIm+IU3KLOI1qkJKTEpU3l14iAZpSTg+dKp9cjyKjQRMGdYKXhSFio2VzdQ2ppSEBP5IEvQ9YR7J3tCICmgIVALrHhE+/cMIXCboDxwpy0z5BQ8POxUcD3U88CwnkKQni6E8yMXnw9fb3+BURACH5BAkFAA8ALAAAAAA8ABgAAATe8MlJq7046827/2AojqQUCEeaJkC5CUgcJ0G2JE2u60jrVgICY0gcIhaXQ25QaDp1vt8jUKwaLbhGwcHtcgeNQe23EFqrAkogt/W6cwlp4mwlICW4gXvvyI1LZnRFaRJgfHtghCRUglUIJjmHbgUNBy4AjY4SAGGSXpSWJZiZRI8PnHqeXKAujKQMpmsNqquVP4GZioa0iT8Hrwx/Sqmefj8BuHRxE5xanmChPwKZRxVKznxgDX8/AAh00RQL18S1YYpSDzffpVEXAjvxB9zpiwnaOQfo9fz9/v8ApUQAACH5BAkFAA8ALAAAAAA8ABgAAATO8MlJq7046827/2AojmS1AIkhJEBQasFBMDRDHC62JE3v97iXJVEr1hKThUHBdBQGv99gIZwIjFiG4AFweL/eQtSHqD4Cs2yR0AW7xeOGoXpQYwvuPHQ8FabtNA15g3ENWyUBgEUDg3mFBy8AijV4jWB7UZAlkpMMlZZemD+aJImdjKBej0KdDIKpDoWHJXWdn42iPzmIrQagcGOkL0SKSG15wFF9ZgJ/WLMBvpeFCLtmMVhBOjxxB1RmJgDiAN8bAQkH6Qcs4O3u7/Dx4BEAIfkECQUADwAsAAAAADwAGAAABMrwyUmrvTjrzbv/YCiOV5AgKCosZIsFCCPPc5KZQ6M3QxK4lgWBRuMVFEgDS5LYOXc24CRGlBUc2CwW8BA8vw2BtFuVDbRoByAHdg6WrmG1kU63v9EWoMy41rVsdzoDQAllBH9oBYJOPy2GVYiJWYuMOlyPh5OUlpcuApqbDpWWjiQBfH6TgYxSVER0m50NeS2gZap1BrOmLpBVuVpcTYy1QLdzuQamAqxuYmMTMGUJcBYmeL3REgAJ3gkC2hgLAOUA1tvp6uvs7R8RACH5BAkFAA8ALAAAAAA8ABgAAATs8MlJq7046827/2AojpmQICgqBGSLCQgjz3OyZEFy7LvgXoDZoEAsDBoyxGWRaDifzgHgNwkQGA2HdqstyBKWA3TsZFEPDAJ37RjIzBIBmTy4uQKyAnuNPEwWR3NjPi4CWHtrXgQTcoJjAz8xA4hrbxJijmNwIzF6lFtXUw+YmU+iJJ2foAyipKUNp5wMk6oOV2aupZsihlmqioyvUT94DJ6USEoSAcINYJGHlF6sFE2lA7sjxQ3HXNN+FAu5ZIRUAlcEtF1IDAh2FQaC2FQUATE0NOVACYENAyv0LMCgQeBAtoAIEypcyLDhhAgAIfkECQUADwAsAAAAADwAGAAABODwyUmrvTjrzbv/YCiOWiAgTJoiSUC+V0KodEokZWIIugtPAYNCUSgMZjWV4BI4NJ7QxsH3Mjiu2OsgqaJKFoOoeLB4AbJoR4PLQFQQ4rh7tEin2QyqIM5fiqx2WVtcfg9hfGMjCoFZBWxzC4h8Xh6MaI8SAJJxACKWWZgPmptRnSGLn45ccwGkUWUhgJaDSTgSh6QDIwGfDkhJeq4Ntn+zbAQUYLmwI7Joa2ymQLhxA5QiAKhXBdBcBxcLcHEHzD+Zv0kEhRcACQfvCdLmFAEytdfzZgD7AOX5/wADCrwQAQAh+QQJBQAPACwAAAAAPAAYAAAE6fDJSau9OOvNu/8gFiQkKSxh+gkE474uEqj0tSBw/gq1twCKYBCAOuiOhJllkRg0no0BDxRQOK5YhyJx7BIsAKj4ifgsrNlsodtNUBbOsdjgMaTvA7buOznI5UoaC3eELXowSgF/cm4bAIR3DYcwUwKLYwMcdpBZkpMujQaXY5qcnZ+gEgmjYhyPpleen42WrFEcg7AOqC4AEoq2Bx1onGu8FAi2vriwvAzCE2Gj0B1VhApGqEkVAnFyByhmm1gGKDifUxULfmN0PerZbATLGD8A9+8ZLF0y+f4PAhAIFJgg0L+DCBMqrBABACH5BAkFAA8ALAAAAAA8ABgAAATz8MlJq7046827/2AWCEhpCkGodkHCvHCcpGtdBUSswwRt3waFUGHwVRC7JAOBaR2eh9nG4KhaqwaLS5lMaBvgMNiLAVzPDgAlwOUaBeJ4Q3BZoNGLiaCtpEsWcnJGElR3V1kSSHw7TBIJgXFkFAqGVwoTi0oTA5BiAxaVZ5iZOxOdcaChVqOkMaanYRaUqpcSOa0vBJuwDZ8VhaGID4q4jQ+PsJITAaoOPlu4kgG8g4TBa7gvRsiQyr+GwhMHuAcWBpDhFwCzVQpqFgu3iwR5FgAHYgfvP2vEXAj7+K1YMI7LgXoCf7SQx4CAlIQQI0qcmCECACH5BAkFAA8ALAAAAAA8ABgAAATe8MlJq7046827/2AXACQZnuCSEEzrEkmAzpjAunhLCDRmKI5g0LC4BHJIl+wRSByeh1hKSBUWKYtbMkdQNb7gb+IDrFIVlcR2iwi7GzwOwGwGTI7r5Pu91NDpEwJ5SAR7bmMaC39mVweDOYZuAxsBi1VLCI84kW6UllSYmi6cYZ6fQaGiDKRfkxunQRNqqoWkiBoGpwaBqi2sfRmKn1cPWb21hreUZWYKwA+zogkGhrsguVVEF5maCBIAB2EHdicLAecBxBYL3HkI6j0zgmvK8T1NWgwwz/b9/v8AMUQAACH5BAkFAA8ALAAAAAA8ABgAAATY8MlJq7046827/+AXACQQhCi4CATjvoSwpFww2nMWtG/vEieaZQFwGI8ORbCy4Pl6hMZhmlimFMiswypBPKGNsDicoBm02pxE8IWN3w0BaoHWGijOLxzO5RTrWRMBbT97b2UgWIBIQQmEDIZvAyGKi0aNj5FvlJZHmISaYyFnnVtrmaENkyCknRMAj1GhiB90nXcTeU+pfRytgGoPjoSyhrQqlWgATLo+xW+4NH9ZCsGCzT5vB8tCEkQG4Aa9EwtehAjW3TQLCdgJ6erdAgn0CXLx+Pn6+xwRACH5BAkFAA8ALAAAAAA8ABgAAATM8MlJq7046827/2AojuSzCAeiqsJSaoChzIoBaALB7DyfvJaAwkEsEhWBS6LHIDQaTt0PKFkMjViFiwLoOZ/gJ0FAfVyx2Ypu9w2HB9QAeu5ISgQ9t75BfhnoaAYTCDxte08HQIBzE2sMh25wL4toE3mQYYqURpY8mJmTm0WdO59gQH+ighKEbKaJLwCiDjd3l5h9L2eAChQBjoZ7di9ylMMSS4WHBFNUVnRaSkx6DM1lDwC7CgBbFwCOTAy51yQLCeDM3eTr7O3u7yMRACH5BAkFAA8ALAAAAAA8ABgAAATO8MlJq7046827/2AojiQVCEmaCEGpLYbizI5iLFlwMHzPHy1XJSCjGRVBU6JBYDZ8PEJSuCgaj7jJssHtMqGIrKt6LdeyAq/660sIHwaz2fBYrNdQxnRkldMUD1t3XQRQAi4BfmYBA4NqYIiKZQGOjz4IkZJGlJVdkCULmkYLjZ1Pl0KiNIGmhT6HLnGidJyVeXsioaJZgneuPZhviZJicIO/UcVjfVgWAAeWl7hjskY3GQsIeTxhbxcLAeEByhjPCOcIBwDe7O3u7/DxEhEAIfkECQUADwAsAAAAADwAGAAABMHwyUmrvTjrzbv/YCiOWGACgbcALEpygeHMtGNoAsLsPCG8GEBt6FAAEofkIRE48J68A7ASIBILjay2QYB6f9PHQmElDrbarveZmsrKRLR2/USE4dazXE1ntElVeENYcg19O2CAgoOFhocJQIGLM4RyhwyQL5KTlWiXiSSTNHpofHR/JG+TjZcEYQuinWmfYQ9CgrJZpmtStQ+bRAYAB1sHAAKHoLUxVjcZOV4+vhYxBtYACx0CCdwJytPg4eLj5B8RACH5BAkFAA8ALAAAAAA8ABgAAATI8MlJq7046827/2AojtgCGKgBLGSbLYojz7LClgnC7AyS3C7KgkacJQKWAIHHZBCQQQmsSG0khMsmkwB0GajUQkMg1WmbiO5oCKYOGsjE+Xz1tt3WR3a+DX7vRGIDAXxnUCQxgDRiDQCFWgAuiYoyjI6PTJEtk5SChJg8hyMAlDNvV3uPBEFspQ1wD3KgdZKlYnULqXNcUa2ADQYUn3xPURIBnFQHmkJmWmnGE1NUNi+yTD/RFQEGCt4KBqIvAOQr2ufo6errIhEAIfkECQUADwAsAAAAADwAGAAABLTwyUmrvTjrzbv/YCiO2QIYSqoYxkK+GODMdG1owoHsyCHAF0NtSLtZAgiGcqlEBIATGXEKqCwIzCyD4IIqplNFJanNIqBSMLEqAZTL7NdXTRRLDm/tAUgHT7B5TAR8fUQTgVqEhTWHiEyKizN/jkqDMHORdg9kjmcwaYtxApQMPzALkTNdEpx5nkBCi0YTAYgEcbCFsxQLrUwIq1APAXS4Fjm/AsHCDwsoQy3M0tPU1dbXGBEAIfkECQUADwAsAAAAADwAGAAABNbwyUmrvTjrzbv/4BWMZGiCC+CsrKMYyyljQWuvSqAFQA/os0kgcSjejoqYRYBgOJ8EQDDRqDYKR6Ql8ew+EzKBtTHIZqUTgHfNQKPGDXNWQWmyuwgTdSzPAgN3a0AfA2NYfTcGEgKBXgIhcIeILYoPXI1fkIaTNpWXmAxgIIVWZZwsaIygDI8ge1Wmpw5/qwyDHgFwsisUBKAEJ69XspUSapitJgabk0kVB40HQQAHsJy3ab5eUUEWCwpyzhkCCeUJbt0VCwZZMOnvxgbyBujw9vf4+RcRACH5BAUFAA8ALAAAAAA8ABgAAATi8MlJq7046827/2AWjGRogovirKyjAMspY0Zrr0owe0Fy/IeE7gG4GRWxS4LAaDIIiaQn0ahaq0KjVmEJMJ1gwpAjuJob2jSgggC7GYjO4nwdpLcUwfst4FDpVQV3WlJfe04EHAOAVYNaaw8Bh29jGIyNjjcGEgCTbpAZl2iZNptEnmCgGIuMpKUSkqhNlUqXgq4roIaTiRsBtrgrUgmyCR1/dHa4XBMLu28EUhsGgAYqrrReh2IgAAdXB2sLrqYWS24H0juR13flFwsA8jDr8DVq9fkB9ywKBur5AgocSPBCBAA7) no-repeat;width:60px;height:24px}.layui-layer-loading .layui-layer-loading1{background:url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) no-repeat;width:37px;height:37px}.layui-layer-loading .layui-layer-loading2,.layui-layer-ico16{background:url(data:image/gif;base64,R0lGODlhIAAgALMAAP///7Ozs/v7+9bW1uHh4fLy8rq6uoGBgTQ0NAEBARsbG8TExJeXl/39/VRUVAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAAACwAAAAAIAAgAAAE5xDISSlLrOrNp0pKNRCdFhxVolJLEJQUoSgOpSYT4RowNSsvyW1icA16k8MMMRkCBjskBTFDAZyuAEkqCfxIQ2hgQRFvAQEEIjNxVDW6XNE4YagRjuBCwe60smQUDnd4Rz1ZAQZnFAGDd0hihh12CEE9kjAEVlycXIg7BAsMB6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YEvpJivxNaGmLHT0VnOgGYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHQjYKhKP1oZmADdEAAAh+QQFBQAAACwAAAAAGAAXAAAEchDISasKNeuJFKoHs4mUYlJIkmjIV54Soypsa0wmLSnqoTEtBw52mG0AjhYpBxioEqRNy8V0qFzNw+GGwlJki4lBqx1IBgjMkRIghwjrzcDti2/Gh7D9qN774wQGAYOEfwCChIV/gYmDho+QkZKTR3p7EQAh+QQFBQAAACwBAAAAHQAOAAAEchDISWdANesNHHJZwE2DUSEo5SjKKB2HOKGYFLD1CB/DnEoIlkti2PlyuKGEATMBaAACSyGbEDYD4zN1YIEmh0SCQQgYehNmTNNaKsQJXmBuuEYPi9ECAU/UFnNzeUp9VBQEBoFOLmFxWHNoQw6RWEocEQAh+QQFBQAAACwHAAAAGQARAAAEaRDICdZZNOvNDsvfBhBDdpwZgohBgE3nQaki0AYEjEqOGmqDlkEnAzBUjhrA0CoBYhLVSkm4SaAAWkahCFAWTU0A4RxzFWJnzXFWJJWb9pTihRu5dvghl+/7NQmBggo/fYKHCX8AiAmEEQAh+QQFBQAAACwOAAAAEgAYAAAEZXCwAaq9ODAMDOUAI17McYDhWA3mCYpb1RooXBktmsbt944BU6zCQCBQiwPB4jAihiCK86irTB20qvWp7Xq/FYV4TNWNz4oqWoEIgL0HX/eQSLi69boCikTkE2VVDAp5d1p0CW4RACH5BAUFAAAALA4AAAASAB4AAASAkBgCqr3YBIMXvkEIMsxXhcFFpiZqBaTXisBClibgAnd+ijYGq2I4HAamwXBgNHJ8BEbzgPNNjz7LwpnFDLvgLGJMdnw/5DRCrHaE3xbKm6FQwOt1xDnpwCvcJgcJMgEIeCYOCQlrF4YmBIoJVV2CCXZvCooHbwGRcAiKcmFUJhEAIfkEBQUAAAAsDwABABEAHwAABHsQyAkGoRivELInnOFlBjeM1BCiFBdcbMUtKQdTN0CUJru5NJQrYMh5VIFTTKJcOj2HqJQRhEqvqGuU+uw6AwgEwxkOO55lxIihoDjKY8pBoThPxmpAYi+hKzoeewkTdHkZghMIdCOIhIuHfBMOjxiNLR4KCW1ODAlxSxEAIfkEBQUAAAAsCAAOABgAEgAABGwQyEkrCDgbYvvMoOF5ILaNaIoGKroch9hacD3MFMHUBzMHiBtgwJMBFolDB4GoGGBCACKRcAAUWAmzOWJQExysQsJgWj0KqvKalTiYPhp1LBFTtp10Is6mT5gdVFx1bRN8FTsVCAqDOB9+KhEAIfkEBQUAAAAsAgASAB0ADgAABHgQyEmrBePS4bQdQZBdR5IcHmWEgUFQgWKaKbWwwSIhc4LonsXhBSCsQoOSScGQDJiWwOHQnAxWBIYJNXEoFCiEWDI9jCzESey7GwMM5doEwW4jJoypQQ743u1WcTV0CgFzbhJ5XClfHYd/EwZnHoYVDgiOfHKQNREAIfkEBQUAAAAsAAAPABkAEQAABGeQqUQruDjrW3vaYCZ5X2ie6EkcKaooTAsi7ytnTq046BBsNcTvItz4AotMwKZBIC6H6CVAJaCcT0CUBTgaTg5nTCu9GKiDEMPJg5YBBOpwlnVzLwtqyKnZagZWahoMB2M3GgsHSRsRACH5BAUFAAAALAEACAARABgAAARcMKR0gL34npkUyyCAcAmyhBijkGi2UW02VHFt33iu7yiDIDaD4/erEYGDlu/nuBAOJ9Dvc2EcDgFAYIuaXS3bbOh6MIC5IAP5Eh5fk2exC4tpgwZyiyFgvhEMBBEAIfkEBQUAAAAsAAACAA4AHQAABHMQyAnYoViSlFDGXBJ808Ep5KRwV8qEg+pRCOeoioKMwJK0Ekcu54h9AoghKgXIMZgAApQZcCCu2Ax2O6NUud2pmJcyHA4L0uDM/ljYDCnGfGakJQE5YH0wUBYBAUYfBIFkHwaBgxkDgX5lgXpHAXcpBIsRADs=) no-repeat;width:32px;height:32px}.layui-layer-tips{box-shadow:none;background:0 0;border:none}.layui-layer-tips .layui-layer-content{_float:left;color:#fff;background-color:#000;border-radius:2px;min-width:12px;padding:5px 10px;font-size:12px;line-height:22px;position:relative;box-shadow:1px 1px 3px #0003}.layui-layer-tips .layui-layer-close{top:-1px;right:-2px}.layui-layer-tips i.layui-layer-TipsT,.layui-layer-tips i.layui-layer-TipsB{border-right-style:solid;border-right-color:#000;left:5px}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsR,.layui-layer-tips i.layui-layer-TipsL{border-bottom-style:solid;border-bottom-color:#000;top:1px}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{color:#fff;background:#4476a7;border:none}.layui-layer-lan .layui-layer-btn{text-align:right;border-top:1px solid #e9e7e7;padding:10px}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{color:#fff;background:#009f95;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAABkCAYAAAAv8xodAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNS8xMS8xNNdPc0oAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAWb0lEQVR4nO2de2wU172Av5l92t4Hti/YQHmVmFcSE7+WR0h0uRRVVC2pKldpCFJQEJAiFwg4diikgLEghEhpVLVQNblBSFEUVYoi+hc0FQoE87B52JQAiQnBJDY2wfix633O7P1jdwbbxe9d7yx3PgnJc87iM2M+js+cx+8nhMNhdHQeFcRE34COTizRhdZ5pNCF1nmk0IXWeaQwPqxQEIRYtpEKOABL9NoLtAKhWDbSDRF4EsgAbEBHtL3LAIN9CV6+fHlSvS0fOXLkof9oj8pzDJZ499A2YCxgWbJkiSUnJ8cApADZgCEO7ZmAImAyYFuxYsV4Iv+ZpgIFcWhPR2PEU2gbkAlQU1Mz67PPPvvvq1ev/s/f/va3LCIyp8e4PROQD4wrLi7Oam9vP/Dhhx/+9f79+3/Kz8+3AxPi0KaOxoiX0KrM169ff6KgoGAKgMFgEF944YWZ0c9Y+vrLw8BEpAceV1xcnHX48OE3HQ7HRIAxY8ZM2bdv39Lo5zJi2KaOBomH0D1knjFjxsTulX6/Pxj9Uo5Re4rMY1esWDH+8OHDb6akpGR2/4Db7fZEv4zXuF1HI8Ra6H5lDgQC0ttvv/1l9NIfg/Z6yPzee+/t6S1zY2PjpdWrV/8retkagzZ1NEwshR5Q5j179pzfu3dvOxBg5HINKHNTU1Ody+WqbG1tDQENQOcI29TROA+dthsGGYAd+pd5165d94nIfHeE7Q1K5qKioorvv/8+ANwGakfYZlwIh8MEg0F1OtFoNGIwxGMCKL4Eg0G6urqQJAlRFElNTcVsNo/6fcSih84A7BaLRRiCzCMZy5qAQoYm86URtBc3FJnNZjMzZswgKyuLQCCAJEmJvrUhEQwGcbvdZGdn88ILL5CTk0NnZ2dCnmOkPbQqc11d3eMPk3n37t01lZWVbcRW5v966aWXJh44cKAyWWWWZZlgMIjD4aCoqIj09HRkWeb48eO0tbUlTS+tyDxz5kz2799Peno6LS0trFq1Cr/fT2pq6qjez0iE1mUeJorM6enpFBUVkZaWRigUIhgMEgwGB/4GGiEQCOB2u5kzZw779u0jPT0yze/xeJAkKdYrzoNiuELrMg8TSZIIBoNkZWVRWFiI2WwmHA7j8/k4d+4cnZ2dCRl7DhWlZ37iiSd46623cDgcANy5c4c33ngDj8eD0+kc9fsajtC6zMMkGAwiSRKTJk0iLy9PHVZ4PB7Onj1LR0cHZrMZUdT2njGlZ87Ly2P//v1YrVYAmpqaKC0tpaGhAbvdnpBh01CFHm2ZzURmMx4ZmadNm0Zubi6iKCIIAm1tbZw5cwav14vJZNK8zH6/H7fbTWFhIXv27Okh85YtW7h9+zY2mw2TyZSQ+xuK0KrMly9ffiInJ2dC98pEyNzY2HjJ5XJVJoPMoVCI2bNnM2vWLCCyo/Hu3bucPXuWUCiE2WxOyJhzKCgyL1y4kMrKSiyWyO6Fb7/9li1bttDS0pJQmWHwQidU5oMHD+6xWq09NhYlg8zKtJwsy+Tm5vLYY48RDocRRZGmpiaqq6uRZRmTyaR5mX0+Hx6Ph2eeeYbdu3er0jY0NFBaWqoJmWFwQg8kc2j37t3ndZl70l3mvLw8pk6dSjgcxmAw8O2333LhwgVEUUyKF0C/36/KXFFRoUpbX19PaWkp9+7dw263YzTGap1u+Ax0B7rMwyAcDhMIBDAajbhcLsaPH48sy4iiyNdff01tbS1GozHhvdlgUHrmpUuXsmPHDvU3ybVr1ygtLaW9vV0zMkP/QusyDwNljtlsNlNUVMS4ceNUma9evcrVq1eTSma3282yZcvYvn27KvP169cpKyujo6NDUzJD30Kn8UDmJ3NycsZ3r4yDzABzeURkTklJweVykZGRQTgcRhAEamtr+eabbzCZTJoSoC+UnvnnP/85W7duVWWuq6ujtLQUn8+XsKm5/ujrJ2sDuHDhwpxRktkJZC9evDg92WV2OBy4XC5sNhuyLCPLMpcuXaKhoQGj0ZgUMnu9XtxuN7/61a947bXX1PLa2lrKy8s1KzP0LbR18uTJ4uzZs3vMM0uSJMdBZohuOy0rK1vUW+bm5uZ/a11mgFAohM1mY8GCBaSmpiJJEj6fj9raWpqbmzGZTJoUoDfBYBCPx/MfMl+8eJGtW7fS1dWlWZmhn912zc3N4WCvjQUGg0H85S9/mR29NEf/xAIvQGNj43/skU5PT//xypUrp0YvHUQO2WoOWZZxOp04HA4kScJgMNDS0sKdO3cQRVGzAvQmGAwyZswY1q5dq5bduHGD9evX4/F4cDgcmn6WvoTu8vv94YMHD16WJKnHUamCgoIpNTU1s6KXY4mEKRgprQAlJSWn6+vrP+9eYTabUysqKirKy8tnEBmaFKFBqUVR5M6dO9y+fRuj0YgkSUyePJk5c+aoG4+SAYPBgMfj4cqVK2rZhAkT+NnPfoYsywQCgQTe3cD0JXQrENq4ceMPf/7zny+OgtR+4JbX65Vzc3PfSUapTSYTsixz7tw5Ghoa1F5s5syZ5Ofnq2NsraM8x86dO6mpqQEgJSWF7du3s3z5ctxuN16vN8F32Td9CS0BzYyu1HUksdSCIKiLJDU1Ndy4cQODwUA4HGbatGkUFhYC9DidokVEUcThcODz+Xj99deprq4GIs9XXl5OcXExbrebrq6uBN/pw+lvJ0wIXeoho2wwunTpEteuXUMURWRZZtKkSbhcLkRR1LzUBoMBu91OIBDg97//PefOnVPrNm/ezPPPP4/H49Gk1ANt7dKlHgbKXPOVK1e4fPkygiAgyzLZ2dksXLgQq9WaFFI7HA78fj9btmzhxIkTat2mTZt48cUXNSn1YPYq6lIPA5PJhMlk4uuvv+bChQuqvJmZmcyfP5/U1FQCgQCyHKvwJLFHGX4IgsCOHTt6SF1SUsKqVavweDx4PJ5+vsvoMtjNt7rUw0BZ4r516xbV1dWEQiHC4TBOp5OFCxfidDrVDUxaRZFalmXeeOMNjh8/rtatW7eOl19+ma6uLs1IPZTd5LrUw8BgMGAymWhsbOTs2bP4/ZH4OmlpaSxcuJCxY8eqm/+1iiiK2O12ALZv386xY8fUujVr1rBu3To8Hg9utztRt6gy1OMRutTDwGAwYDabaWlpoaqqSh13WiwWXC4X2dnZSSO10Whk9+7dHD16VK1btWoVJSUlmuiph3PeR5d6GCh7n9vb2zl16hSdnZ0IgoDRaKSoqIgpU6aoJ1u0ijL8EEWRHTt2cOTIEbVu5cqVbNiwIeE99XAPsOlSDwNRFDGZTHg8Hqqqqrh37x6CIGAwGMjPz2f69OlIkqRpqQVBwG63Y7Va2bt3L59++qlat2LFCkpLS+nq6kqY1CM5kalLPQyUntrn81FVVUVzc7N6MDY3N1cNOKNllOGHxWJh3759fPLJJ2pdcXExr776Kj6fLyHL5CM9YqxLPQwEQVCXmM+cOUNDQwMQ2bap5d65O0pPrUj90UcfqXW/+MUvyMjISMizxOLMvC71MFCkFgSB6upqTp06xcmTJ+no6EiKPdPwQOqUlBTeffddDh48yHfffcfHH39MW1tbQk7lxCoIxHCkHmkE/6FKnTvC9uKCsgBz7949fD5fUgSa6Y4gCDgcDiwWC4cOHWLdunUcOHAAs9mc1ELDIKU+fvz4tOilPQZtDkrq4uLiLGAcGk1JocxVJ0Ogmb5wOp2MGzcOg8FAdnY2NpstIfcR65/egFIXFRVNjn4ZqxwrA0q9fv36Z6KXmhRaJ3bEozvoV+qOjg5l5j2Wm4P7lbq+vv776JeJX8rSiSvxevtQpM7auHHjD62trTWrV6+e5vP5gqWlpV9HPxOLHCvdqQPwer1TcnNz3zly5MjN3Nxc16VLl86sXbv2dLQ9PcfKI47wsC2MMQxLZSTyAtj77KEXaIlVI73IBaY8pPwk0KZnktU2I80kG+/5oRDQRORwq5VIKjcf8f3VX0fkdHg2kTFza/Qe2uLYpo5GiHcPrTm0vKleZ+T010ObiaQStkavfUR6uViPfbujJJ13EOnFlaTzo74W/Kj8qn5UnmOw9DXLYQbGA9a8vDxTXl6eiYjY2TwQPNYUEEky74gmnbcRSUJfRCSKv47OgPQl9BiAo0ePTjl//vzi8+fPL/7nP/+pLIhkEXup04EJ+fn59vv37//pww8//Gt7e/uBbgsi+ehS6wyCvoROcTqdwpIlS2YIUX7yk5/M+Pzzz6dH62MtdQbAvn37lo4ZM2YKgMPhmHj48OE3dal1hkKfCys+nw9Jknpsl3r22Wcfi5PUIeiRZB6AlJSUzF5SF6BLrdMP/YYC++CDD/4d7jUt8Oyzzz526tSpx6KXsZK6FWD16tX/amxs7BGMUZE6Oq4eiy61Tj/0FwpMeuWVV+6+//77tb2lXrhw4fQYS90JNLS2toZcLldlU1NTXffKlJSUzPfee2+PLrXOQAwUCkxas2ZN8/vvv1/be5NRHKSuBW5///33gaKioopklFqSJPx+P36/n0AgkLRz3oFAgLa2NlpbW2lvb0+KmHwK/W1OCtJN6g8++KBuEFKPdBP9JZJUakmSCAQCZGVlkZOTg8lk0nx0pIchSRJut5ucnBx+85vfkJWVhdvtThqpB9ptFwTuMHipxzH6UheiAaklSSIzM5Onn36auXPnsmjRIjU+nNbPCHbH7/fjdDrZu3cvJSUlHDhwgJycnKSRerChwAaUuqqqKid6OWpSv/TSSxOB/0IjUgeDQfx+P6FQCKfTyfz580lPT9d8dKTuCIKAJElq7JDMzEz279/PrFmz6OzsTNr40L0ZUOoFCxb8eLSlPnDgQKVWpDYajbjdbk6fPo3f7yccDpOamsqCBQsYN24cgUBA04FkFMxmM263mz/84Q+0tEQ2RGZmZvL222/z+OOPa76nHmooMF3qPlBibrS2tlJVVaVGEDKbzcybN48f/ehHBINBTcsAD0Lp3rx5k82bN9Pc3AxEjli9++67PPXUU5ruqYcTCiyhUj9snlprUnd0dPDFF1/Q0dGhBpIpKChg6tSpSJKkealNJhM2m41bt26xefNm7ty5A4DVauXNN98kPz8ft9utxunTEsMNBZYwqV0uV+UgpU5IzmFFap/PxxdffMEPP/yAIAgIgsBTTz3FzJkzkyLniiJ1Q0MDJSUl3Lp1CwCbzcZbb73FvHnz8Hg8mpN6JKHAtC51AQmSWom5EQwGOX36tJoJC2DOnDk8+eSThEIhzc9VK1I3Nzfz2muvcfv2bQA1DNjTTz+N2+3G5/Ml+E4fMNJQYANKfe7cuVGV+uDBg3u0IrXZbEaWZU6fPq0mEgqHw+Tk5JCXl6cmEtK61Ha7naamJjZu3Mg333wDRN4NKioqWLRokaZ66liEAutX6qKiolGV2mq1pmtFaniQc6Wmpob6+no1PcW0adOYN28eoihqvqc2Go3Y7Xbu3r3Lpk2buH79OhAJB7x3716WLFmimZ46VqHAdKn7Qcki2z2RUDgcZuLEicybNw+z2az5BRhF6ra2NsrKyvjqq6+AyDvDrl27+OlPf4rH40m41LEMBTZUqUcaaGaoUs8dYXsjQomMdPXqVerq6hAEgXA4TFZWFvPnz1cTCSWD1Pfv3+d3v/sdly9fBiLDq+3bt7Ns2bKESx3rUGADSt0tFJgjBm32kPq777670L1SkXrx4sXpRI6POWPQ5rBRktffuHGDCxcuIEkSsiyTkZHBokWLcDgcmpdamaf2er2UlZVRVxdZGhBFkW3btiU8OWc8QoH1kLr3B+bNm6fEzIhVKDBV6vnz5+9pbm7+d/dKq9WaXlZWtih6mRmjNoeNkkiooaGB6upqvF4vsixjt9uZP38+NptN8yF1Fak9Hg/l5eXU1tYCkZ66rKyM5557Do/Hk5CpyXiEAnMABovFIixZsiS7d6XX61Veh2O1DpwSbZOVK1dOTU9P/3HvDzQ2NioRkzSR09dgMCCKIk1NTbS0tGAwGJAkCafTidPp1HQPraDkMXS73fz2t7/lxo0bQETqdevWqRm+RptYC50B2C0Wi3D16tXcadOm9RA6EAgEKysrlazosRhopRA5Fe4sLy+fUVFRUWE2m3vEnq6vr/+8pKTkdPRSE6HAlFwqc+bMYfLkyUiShNFo5Pbt2zQ1NSVNBFLlRXbZsmVMmDBBLf/yyy/p6upS852PJrGMnDSgzK+//nrNO++800Ektsf9EbY3KJlzc3Pf8Xq9MnCL+MYUGRTKGDk/P5+pU6ciyzIGg4GGhgY1QWci4ioPFa/Xi8fj4bnnnqOsrEwNTlRTU8POnTuRZTkhzxEroYcq890Rtjccmf9jPD+ahMNhdWxcWFjIpEmTVJlv3LhBXV2dmn9F6yjp237961+zefNmtby6upqtW7cSCATUbFmjTSyEzgRsQ5R5JOPnpJQ5GAxiMBgoLCxk/PjxyLKMKIpcu3aNK1euqC+LWkeR+fnnn2fTpk1q+blz59i2bRuBQAC73Z6Q4QaMXOhMwOZ0OoWLFy/qMj8ERWar1UpRUREZGRnIsowgCFy+fJmvvvoq6WR+8cUXKSkpUctPnDjBtm3b1PQUiXwHGInQuswDoOzVSEtLw+VyMWbMGMLhMOFwmIsXL3Lr1i1MJlNSJAnyeDx0dXWxatUq1q1bp5afOHGCHTt2aEJmGL7QuswDoMjsdDpxuVykpaWp4+jz58/T2NioLolrHUXml19+mTVr1qjlx48fZ+fOnQCakBmGJ7Qu8wAo2WDHjh1LYWEhFktkDcnv91NTU0NLS0vSyOx2u+nq6uKVV15h1apVavmxY8fYtWuXuhyuBZlh6ELrMg+AciJl/PjxFBQUqGPjrq4uzp49S1tbW9KkblN65pKSElauXKmWHz16lMrKSs3JDEMTWpd5AEKhEKFQiClTpjB37lxEUUQQBDo6Ojhz5gwejydpZFZ65g0bNrBixQq1/MiRI+zZsweLxYLD4dBccPzBCq3LPAChUAhJkpg+fTq5uQ9yfN67d0/ds5EseQgVmUtLSykuLlbLP/30U/bt24fVasVms2lOZhic0LrMg0CWZdLT01WZlb0a1dXVSJKE2WzWpAC9CQQC+P1+Xn311R4yf/LJJ+zfvx+LxYLdbtfsswwktC7zEAiFQni9XlJSUmhoaOD8+fM9cnonA6FQiIyMDJYvX66WffTRR/zxj38kJSVF0zJD/0LrMg8Bo9FIZ2cnJ0+eJC0tTd1FlwwLJt0xmUy0tbXx8ccfs3TpUv7xj39w6NChpJAZ+hY6jdGVGSL5BZNSZugZvsDn8yXNtFxvlJM1f/nLX/j73/9Oa2ur+gKYDPQltA2gqqpqzijJnAGMKy4uzkpGmRVEUUyKl76BsNlsavL5sWPHJvhuhkZfP32r0+kUZs+ePbF7YZxkhmiOlfXr1z+TrDLraIO+hA62t7eHOzo6upSCOMoM0cyy3ZLME73WZdYZEn0J7QcoLy+/ePPmzeabN2/e2bBhw9k4yQyRkyT+tWvXnj527Nj/trS0fPnZZ58d0mXWGSp9pUYWiIQa6J1mwgf8QGxlVhgDPPOQ8pjKrCev1zbxSl4fJpKOwsGD09l+oGMkjQ1AG3CSSAZbJen8HUZ+VEvn/xEP7aF1dJKV5J9j0tHphi60ziOFLrTOI4UutM4jhS60ziPF/wHgvbfirVNMHAAAAABJRU5ErkJggg==) no-repeat}.layui-layer-prompt .layui-layer-input{color:#333;border:1px solid #ccc;width:220px;height:30px;margin:0 auto;padding:0 5px;line-height:30px;display:block;box-shadow:inset 1px 1px 5px #0000001a}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px #0006}.layui-layer-tab .layui-layer-title{background-color:#eee;border-bottom:1px solid #ccc;padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{float:left;text-align:center;cursor:default;text-overflow:ellipsis;white-space:nowrap;min-width:80px;max-width:260px;padding:0 20px;position:relative;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{z-index:10;background-color:#fff;border-left:1px solid #ccc;border-right:1px solid #ccc;height:43px}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{clear:both;line-height:24px}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{cursor:pointer;position:absolute;top:5px;right:10px}.layui-layer-photos{animation-duration:1s}.layui-layer-photos .layui-layer-content{text-align:center;overflow:hidden}.layui-layer-photos .layui-layer-phimg img{vertical-align:top}.layui-layer-imguide,.layui-layer-imgbar{display:none}.layui-layer-imgprev,.layui-layer-imgnext{_width:44px;blr:expression(this.onFocus=this.blur());outline:none;width:27px;height:44px;margin-top:-22px;position:absolute;top:50%}.layui-layer-imgprev{background-position:-5px -5px;left:10px}.layui-layer-imgprev:hover{background-position:-33px -5px}.layui-layer-imgnext{background-position:-5px -50px;right:10px}.layui-layer-imgnext:hover{background-position:-33px -50px}.layui-layer-imgbar{background-color:#000c;background-color:#000 \\9 ;width:100%;height:32px;filter:Alpha(opacity=80);color:#fff;text-overflow:ellipsis;white-space:nowrap;font-size:0;line-height:32px;position:absolute;bottom:0;left:0;overflow:hidden}.layui-layer-imgtit *{vertical-align:top;font-size:12px}.layui-layer-imgtit a{text-overflow:ellipsis;white-space:nowrap;color:#fff;max-width:65%;overflow:hidden}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (width<=1100px){.layui-layer-iframe{-webkit-overflow-scrolling:touch;overflow-y:auto}}");
	_css("/*!\n * Toastify js 1.12.0\n * https://github.com/apvarun/toastify-js\n * @license MIT licensed\n *\n * Copyright (C) 2018 Varun A P\n */\n.toastify{color:#fff;opacity:0;cursor:pointer;z-index:2147483647;background:linear-gradient(135deg,#73a5ff,#5477f5);border-radius:2px;max-width:calc(50% - 20px);padding:12px 20px;text-decoration:none;transition:all .4s cubic-bezier(.215,.61,.355,1);display:inline-block;position:fixed;box-shadow:0 3px 6px -1px #0000001f,0 10px 36px -4px #4d60e84d}.toastify.on{opacity:1}.toast-close{color:#fff;cursor:pointer;opacity:.4;background:0 0;border:0;padding:0 5px;font-family:inherit;font-size:1em}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{border-radius:2px;width:1.5em;height:1.5em;margin:-7px 5px}.toastify-center{max-width:-moz-fit-content;margin-left:auto;margin-right:auto;left:0;right:0}@media only screen and (width<=360px){.toastify-right,.toastify-left{max-width:fit-content;margin-left:auto;margin-right:auto;left:0;right:0}}");
	_css("/*!\n * Viewer.js v1.11.7\n * https://fengyuanchen.github.io/viewerjs\n *\n * Copyright 2015-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2024-11-24T04:32:14.526Z\n */\n.viewer-close:before,.viewer-flip-horizontal:before,.viewer-flip-vertical:before,.viewer-fullscreen-exit:before,.viewer-fullscreen:before,.viewer-next:before,.viewer-one-to-one:before,.viewer-play:before,.viewer-prev:before,.viewer-reset:before,.viewer-rotate-left:before,.viewer-rotate-right:before,.viewer-zoom-in:before,.viewer-zoom-out:before{color:#0000;background-image:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 560 40'%3E%3Cpath fill='%23fff' d='M49.6 17.9h20.2v3.9H49.6zm123.1 2 10.9-11 2.7 2.8-8.2 8.2 8.2 8.2-2.7 2.7-10.9-10.9zm94 0-10.8-11-2.7 2.8 8.1 8.2-8.1 8.2 2.7 2.7 10.8-10.9zM212 9.3l20.1 10.6L212 30.5V9.3zm161.5 4.6-7.2 6 7.2 5.9v-4h12.4v4l7.3-5.9-7.3-6v4h-12.4v-4zm40.2 12.3 5.9 7.2 5.9-7.2h-4V13.6h4l-5.9-7.3-5.9 7.3h4v12.6h-4zm35.9-16.5h6.3v2h-4.3V16h-2V9.7Zm14 0h6.2V16h-2v-4.3h-4.2v-2Zm6.2 14V30h-6.2v-2h4.2v-4.3h2Zm-14 6.3h-6.2v-6.3h2v4.4h4.3v2Zm-438 .1v-8.3H9.6v-3.9h8.2V9.7h3.9v8.2h8.1v3.9h-8.1v8.3h-3.9zM93.6 9.7h-5.8v3.9h2V30h3.8V9.7zm16.1 0h-5.8v3.9h1.9V30h3.9V9.7zm-11.9 4.1h3.9v3.9h-3.9zm0 8.2h3.9v3.9h-3.9zm244.6-11.7 7.2 5.9-7.2 6v-3.6c-5.4-.4-7.8.8-8.7 2.8-.8 1.7-1.8 4.9 2.8 8.2-6.3-2-7.5-6.9-6-11.3 1.6-4.4 8-5 11.9-4.9v-3.1Zm147.2 13.4h6.3V30h-2v-4.3h-4.3v-2zm14 6.3v-6.3h6.2v2h-4.3V30h-1.9zm6.2-14h-6.2V9.7h1.9V14h4.3v2zm-13.9 0h-6.3v-2h4.3V9.7h2V16zm33.3 12.5 8.6-8.6-8.6-8.7 1.9-1.9 8.6 8.7 8.6-8.7 1.9 1.9-8.6 8.7 8.6 8.6-1.9 2-8.6-8.7-8.6 8.7-1.9-2zM297 10.3l-7.1 5.9 7.2 6v-3.6c5.3-.4 7.7.8 8.7 2.8.8 1.7 1.7 4.9-2.9 8.2 6.3-2 7.5-6.9 6-11.3-1.6-4.4-7.9-5-11.8-4.9v-3.1Zm-157.3-.6c2.3 0 4.4.7 6 2l2.5-3 1.9 9.2h-9.3l2.6-3.1a6.2 6.2 0 0 0-9.9 5.1c0 3.4 2.8 6.3 6.2 6.3 2.8 0 5.1-1.9 6-4.4h4c-1 4.7-5 8.3-10 8.3a10 10 0 0 1-10-10.2 10 10 0 0 1 10-10.2Z'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-size:280px;width:20px;height:20px;font-size:0;line-height:0;display:block}.viewer-zoom-in:before{content:\"Zoom In\";background-position:0 0}.viewer-zoom-out:before{content:\"Zoom Out\";background-position:-20px 0}.viewer-one-to-one:before{content:\"One to One\";background-position:-40px 0}.viewer-reset:before{content:\"Reset\";background-position:-60px 0}.viewer-prev:before{content:\"Previous\";background-position:-80px 0}.viewer-play:before{content:\"Play\";background-position:-100px 0}.viewer-next:before{content:\"Next\";background-position:-120px 0}.viewer-rotate-left:before{content:\"Rotate Left\";background-position:-140px 0}.viewer-rotate-right:before{content:\"Rotate Right\";background-position:-160px 0}.viewer-flip-horizontal:before{content:\"Flip Horizontal\";background-position:-180px 0}.viewer-flip-vertical:before{content:\"Flip Vertical\";background-position:-200px 0}.viewer-fullscreen:before{content:\"Enter Full Screen\";background-position:-220px 0}.viewer-fullscreen-exit:before{content:\"Exit Full Screen\";background-position:-240px 0}.viewer-close:before{content:\"Close\";background-position:-260px 0}.viewer-container{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;user-select:none;direction:ltr;font-size:0;line-height:0;position:absolute;inset:0;overflow:hidden}.viewer-container ::-moz-selection{background-color:#0000}.viewer-container::-moz-selection{background-color:#0000}.viewer-container ::selection{background-color:#0000}.viewer-container::selection{background-color:#0000}.viewer-container:focus{outline:0}.viewer-container img{width:100%;height:auto;display:block;min-width:0!important;max-width:none!important;min-height:0!important;max-height:none!important}.viewer-canvas{position:absolute;inset:0;overflow:hidden}.viewer-canvas>img{width:auto;height:auto;margin:15px auto;max-width:90%!important}.viewer-footer{text-align:center;position:absolute;bottom:0;left:0;right:0;overflow:hidden}.viewer-navbar{background-color:#00000080;overflow:hidden}.viewer-list{box-sizing:content-box;height:50px;margin:0;padding:1px 0;overflow:hidden}.viewer-list>li{color:#0000;cursor:pointer;float:left;opacity:.5;width:30px;height:50px;font-size:0;line-height:0;transition:opacity .15s;overflow:hidden}.viewer-list>li:focus,.viewer-list>li:hover{opacity:.75}.viewer-list>li:focus{outline:0}.viewer-list>li+li{margin-left:1px}.viewer-list>.viewer-loading{position:relative}.viewer-list>.viewer-loading:after{border-width:2px;width:20px;height:20px;margin-top:-10px;margin-left:-10px}.viewer-list>.viewer-active,.viewer-list>.viewer-active:focus,.viewer-list>.viewer-active:hover{opacity:1}.viewer-player{cursor:none;z-index:1;background-color:#000;display:none;bottom:0;right:0}.viewer-player,.viewer-player>img{position:absolute;top:0;left:0}.viewer-toolbar>ul{margin:0 auto 5px;padding:6px 3px;display:inline-block;overflow:hidden}.viewer-toolbar>ul>li{cursor:pointer;float:left;background-color:#00000080;border-radius:50%;width:24px;height:24px;transition:background-color .15s;overflow:hidden}.viewer-toolbar>ul>li:focus,.viewer-toolbar>ul>li:hover{background-color:#000c}.viewer-toolbar>ul>li:focus{z-index:1;outline:0;position:relative;box-shadow:0 0 3px #fff}.viewer-toolbar>ul>li:before{margin:2px}.viewer-toolbar>ul>li+li{margin-left:1px}.viewer-toolbar>ul>.viewer-small{width:18px;height:18px;margin-top:3px;margin-bottom:3px}.viewer-toolbar>ul>.viewer-small:before{margin:-1px}.viewer-toolbar>ul>.viewer-large{width:30px;height:30px;margin-top:-3px;margin-bottom:-3px}.viewer-toolbar>ul>.viewer-large:before{margin:5px}.viewer-tooltip{color:#fff;text-align:center;background-color:#000c;border-radius:10px;width:50px;height:20px;margin-top:-10px;margin-left:-25px;font-size:12px;line-height:20px;display:none;position:absolute;top:50%;left:50%}.viewer-title{color:#ccc;opacity:.8;text-overflow:ellipsis;white-space:nowrap;max-width:90%;min-height:14px;margin:5px 5%;font-size:12px;line-height:1.2;transition:opacity .15s;display:inline-block;overflow:hidden}.viewer-title:hover{opacity:1}.viewer-button{-webkit-app-region:no-drag;cursor:pointer;background-color:#00000080;border-radius:50%;width:80px;height:80px;transition:background-color .15s;position:absolute;top:-40px;right:-40px;overflow:hidden}.viewer-button:focus,.viewer-button:hover{background-color:#000c}.viewer-button:focus{outline:0;box-shadow:0 0 3px #fff}.viewer-button:before{position:absolute;bottom:15px;left:15px}.viewer-fixed{position:fixed}.viewer-open{overflow:hidden}.viewer-show{display:block}.viewer-hide{display:none}.viewer-backdrop{background-color:#00000080}.viewer-invisible{visibility:hidden}.viewer-move{cursor:move;cursor:grab}.viewer-fade{opacity:0}.viewer-in{opacity:1}.viewer-transition{transition:all .3s}@keyframes viewer-spinner{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.viewer-loading:after{content:\"\";z-index:1;border:4px solid #ffffff1a;border-left-color:#ffffff80;border-radius:50%;width:40px;height:40px;margin-top:-20px;margin-left:-20px;animation:1s linear infinite viewer-spinner;display:inline-block;position:absolute;top:50%;left:50%}@media (width<=767px){.viewer-hide-xs-down{display:none}}@media (width<=991px){.viewer-hide-sm-down{display:none}}@media (width<=1199px){.viewer-hide-md-down{display:none}}");
	_css(".tabulator{text-align:left;background-color:#fff;border:1px solid #999;font-size:14px;position:relative;overflow:hidden;transform:translateZ(0)}.tabulator[tabulator-layout=fitDataFill] .tabulator-tableholder .tabulator-table{min-width:100%}.tabulator[tabulator-layout=fitDataTable]{display:inline-block}.tabulator.tabulator-block-select,.tabulator.tabulator-ranges .tabulator-cell:not(.tabulator-editing){-webkit-user-select:none;user-select:none}.tabulator .tabulator-header{box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;white-space:nowrap;border-bottom:1px solid #999;outline:none;width:100%;position:relative;overflow:hidden}.tabulator .tabulator-header.tabulator-header-hidden{display:none}.tabulator .tabulator-header .tabulator-header-contents{position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers{display:inline-block}.tabulator .tabulator-header .tabulator-col{box-sizing:border-box;text-align:left;vertical-align:bottom;background:#f9fafb;border-right:1px solid #ddd;flex-direction:column;justify-content:flex-start;display:inline-flex;position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-col.tabulator-moving{pointer-events:none;background:#dae1e7;border:1px solid #999;position:absolute}.tabulator .tabulator-header .tabulator-col.tabulator-range-highlight{color:#000;background-color:#d6d6d6}.tabulator .tabulator-header .tabulator-col.tabulator-range-selected{color:#fff;background-color:#3876ca}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{box-sizing:border-box;padding:4px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button{padding:0 8px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button:hover{cursor:pointer;opacity:.6}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title-holder{position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title{box-sizing:border-box;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;width:100%;overflow:hidden}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title.tabulator-col-title-wrap{text-overflow:clip;white-space:normal}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor{box-sizing:border-box;background:#fff;border:1px solid #999;width:100%;padding:1px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-header-popup-button+.tabulator-title-editor{width:calc(100% - 22px)}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{align-items:center;display:flex;position:absolute;top:0;bottom:0;right:4px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-left:6px solid #0000;border-right:6px solid #0000;width:0;height:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{border-top:1px solid #ddd;margin-right:-1px;display:flex;position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter{box-sizing:border-box;text-align:center;width:100%;margin-top:2px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea{height:auto!important}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg{margin-top:3px}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear{width:0;height:0}.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-right:25px}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover{cursor:pointer;background-color:#dae1e7}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter{color:#bbb}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-bottom:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-top:none;border-bottom:6px solid #bbb}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-bottom:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-top:none;border-bottom:6px solid #666}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-top:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{color:#666;border-top:6px solid #666;border-bottom:none}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title{text-orientation:mixed;writing-mode:vertical-rl;justify-content:center;align-items:center;display:flex}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title{transform:rotate(180deg)}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title{padding-top:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title{padding-bottom:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-sorter{justify-content:center;inset:4px 0 auto}.tabulator .tabulator-header .tabulator-frozen{z-index:11;position:sticky;left:0}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left{border-right:2px solid #ddd}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right{border-left:2px solid #ddd}.tabulator .tabulator-header .tabulator-calcs-holder{box-sizing:border-box;border-top:1px solid #ddd;border-bottom:1px solid #ddd;display:inline-block;background:#fff!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row{background:#fff!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-header .tabulator-frozen-rows-holder{padding-top:1em;display:inline-block}.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty{display:none}.tabulator .tabulator-tableholder{-webkit-overflow-scrolling:touch;white-space:nowrap;width:100%;position:relative;overflow:auto}.tabulator .tabulator-tableholder:focus{outline:none}.tabulator .tabulator-tableholder .tabulator-placeholder{box-sizing:border-box;justify-content:center;align-items:center;width:100%;min-width:100%;display:flex}.tabulator .tabulator-tableholder .tabulator-placeholder[tabulator-render-mode=virtual]{min-height:100%}.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents{color:#ccc;text-align:center;white-space:normal;padding:10px;font-size:20px;font-weight:700;display:inline-block}.tabulator .tabulator-tableholder .tabulator-table{color:#333;white-space:nowrap;background-color:#fff;display:inline-block;position:relative;overflow:visible}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{font-weight:700;background:#e2e2e2!important}.tabulator .tabulator-tableholder .tabulator-range-overlay{pointer-events:none;z-index:10;position:absolute;inset:0}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range{box-sizing:border-box;border:1px solid #2975dd;position:absolute}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{content:\"\";background-color:#2975dd;border-radius:999px;width:6px;height:6px;position:absolute;bottom:-3px;right:-3px}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range-cell-active{box-sizing:border-box;border:2px solid #2975dd;position:absolute}.tabulator .tabulator-footer{color:#555;-webkit-user-select:none;user-select:none;-khtml-user-select:none;-o-user-select:none;white-space:nowrap;background-color:#fff;border-top:1px solid #999;font-weight:700}.tabulator .tabulator-footer .tabulator-footer-contents{flex-direction:row;justify-content:space-between;align-items:center;padding:5px 10px;display:flex}.tabulator .tabulator-footer .tabulator-footer-contents:empty{display:none}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:-5px;overflow-x:auto}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab{border:1px solid #999;border-top:none;border-bottom-right-radius:5px;border-bottom-left-radius:5px;padding:5px;font-size:.9em;display:inline-block}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab:hover{cursor:pointer;opacity:.7}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{background:#fff}.tabulator .tabulator-footer .tabulator-calcs-holder{box-sizing:border-box;text-align:left;border-top:1px solid #ddd;border-bottom:1px solid #ddd;width:100%;overflow:hidden}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{display:inline-block}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{margin-bottom:-5px}.tabulator .tabulator-footer>*+.tabulator-page-counter{margin-left:10px}.tabulator .tabulator-footer .tabulator-page-counter{font-weight:400}.tabulator .tabulator-footer .tabulator-paginator{color:#555;font-family:inherit;font-size:inherit;font-weight:inherit;text-align:right;flex:1}.tabulator .tabulator-footer .tabulator-page-size{border:1px solid #aaa;border-radius:3px;margin:0 5px;padding:2px 5px;display:inline-block}.tabulator .tabulator-footer .tabulator-pages{margin:0 7px}.tabulator .tabulator-footer .tabulator-page{background:#fff3;border:1px solid #aaa;border-radius:3px;margin:0 2px;padding:2px 5px;display:inline-block}.tabulator .tabulator-footer .tabulator-page.active{color:#d00}.tabulator .tabulator-footer .tabulator-page:disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-footer .tabulator-page:not(disabled):hover{color:#fff;cursor:pointer;background:#0003}}.tabulator .tabulator-col-resize-handle{vertical-align:middle;z-index:11;width:6px;margin-left:-3px;margin-right:-3px;display:inline-block;position:relative}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-col-resize-handle:hover{cursor:ew-resize}}.tabulator .tabulator-col-resize-handle:last-of-type{width:3px;margin-right:0}.tabulator .tabulator-col-resize-guide{opacity:.5;background-color:#999;width:4px;height:100%;margin-left:-.5px;position:absolute;top:0}.tabulator .tabulator-row-resize-guide{opacity:.5;background-color:#999;width:100%;height:4px;margin-top:-.5px;position:absolute;left:0}.tabulator .tabulator-alert{text-align:center;z-index:100;background:#0006;align-items:center;width:100%;height:100%;display:flex;position:absolute;top:0;left:0}.tabulator .tabulator-alert .tabulator-alert-msg{background:#fff;border-radius:10px;margin:0 auto;padding:10px 20px;font-size:16px;font-weight:700;display:inline-block}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg{color:#000;border:4px solid #333}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error{color:#590000;border:4px solid #d00}.tabulator-row{box-sizing:border-box;background-color:#fff;min-height:22px;position:relative}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{cursor:pointer;background-color:#bbb}}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{cursor:pointer;background-color:#769bcc}}.tabulator-row.tabulator-row-moving{background:#fff;border:1px solid #000}.tabulator-row.tabulator-moving{pointer-events:none;z-index:15;border-top:1px solid #ddd;border-bottom:1px solid #ddd;position:absolute}.tabulator-row.tabulator-range-highlight .tabulator-cell.tabulator-range-row-header{color:#000;background-color:#d6d6d6}.tabulator-row.tabulator-range-highlight.tabulator-range-selected .tabulator-cell.tabulator-range-row-header,.tabulator-row.tabulator-range-selected .tabulator-cell.tabulator-range-row-header{color:#fff;background-color:#3876ca}.tabulator-row .tabulator-row-resize-handle{height:5px;position:absolute;bottom:0;left:0;right:0}.tabulator-row .tabulator-row-resize-handle.prev{top:0;bottom:auto}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-row-resize-handle:hover{cursor:ns-resize}}.tabulator-row .tabulator-responsive-collapse{box-sizing:border-box;border-top:1px solid #ddd;border-bottom:1px solid #ddd;padding:5px}.tabulator-row .tabulator-responsive-collapse:empty{display:none}.tabulator-row .tabulator-responsive-collapse table{font-size:14px}.tabulator-row .tabulator-responsive-collapse table tr td{position:relative}.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type{padding-right:10px}.tabulator-row .tabulator-cell{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;border-right:1px solid #ddd;outline:none;padding:4px;display:inline-block;position:relative;overflow:hidden}.tabulator-row .tabulator-cell .tabulator-tick{fill:#2dc214}.tabulator-row .tabulator-cell .tabulator-cross{fill:#ce1515}.tabulator-row .tabulator-cell.tabulator-row-header{background:#f9fafb;border-bottom:1px solid #ddd;border-right:1px solid #999}.tabulator-row .tabulator-cell.tabulator-frozen{background-color:inherit;z-index:11;display:inline-block;position:sticky;left:0}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-right:2px solid #ddd}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-left:2px solid #ddd}.tabulator-row .tabulator-cell.tabulator-editing{border:1px solid #1d68cd;outline:none;padding:0}.tabulator-row .tabulator-cell.tabulator-editing input,.tabulator-row .tabulator-cell.tabulator-editing select{background:0 0;border:1px;outline:none}.tabulator-row .tabulator-cell.tabulator-validation-fail{border:1px solid #db2828}.tabulator-row .tabulator-cell.tabulator-validation-fail input,.tabulator-row .tabulator-cell.tabulator-validation-fail select{color:#db2828;background:0 0;border:1px}.tabulator-row .tabulator-cell.tabulator-row-handle{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;justify-content:center;align-items:center;display:inline-flex}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box{width:80%}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar{background:#666;width:100%;height:3px;margin-top:2px}.tabulator-row .tabulator-cell.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(.tabulator-range-row-header){background-color:#9abcea}.tabulator-row .tabulator-cell .tabulator-data-tree-branch-empty{width:7px;display:inline-block}.tabulator-row .tabulator-cell .tabulator-data-tree-branch{vertical-align:middle;border-bottom:2px solid #ddd;border-left:2px solid #ddd;border-bottom-left-radius:1px;width:7px;height:9px;margin-top:-9px;margin-right:5px;display:inline-block}.tabulator-row .tabulator-cell .tabulator-data-tree-control{vertical-align:middle;background:#0000001a;border:1px solid #333;border-radius:2px;justify-content:center;align-items:center;width:11px;height:11px;margin-right:5px;display:inline-flex;overflow:hidden}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover{cursor:pointer;background:#0003}}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:0 0;width:1px;height:7px;display:inline-block;position:relative}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;width:1px;height:7px;display:inline-block;position:relative}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;background:#666;border-radius:20px;justify-content:center;align-items:center;width:15px;height:15px;font-size:1.1em;font-weight:700;display:inline-flex}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover{cursor:pointer;opacity:.7}}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close{display:initial}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open{display:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg{stroke:#fff}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close{display:none}.tabulator-row .tabulator-cell .tabulator-traffic-light{border-radius:14px;width:14px;height:14px;display:inline-block}.tabulator-row.tabulator-group{box-sizing:border-box;background:#ccc;border-top:1px solid #999;border-bottom:1px solid #999;border-right:1px solid #ddd;min-width:100%;padding:5px 5px 5px 10px;font-weight:700}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-group:hover{cursor:pointer;background-color:#0000001a}}.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow{border:6px solid #0000;border-top-color:#666;border-bottom:0;margin-right:10px}.tabulator-row.tabulator-group.tabulator-group-level-1{padding-left:30px}.tabulator-row.tabulator-group.tabulator-group-level-2{padding-left:50px}.tabulator-row.tabulator-group.tabulator-group-level-3{padding-left:70px}.tabulator-row.tabulator-group.tabulator-group-level-4{padding-left:90px}.tabulator-row.tabulator-group.tabulator-group-level-5{padding-left:110px}.tabulator-row.tabulator-group .tabulator-group-toggle{display:inline-block}.tabulator-row.tabulator-group .tabulator-arrow{vertical-align:middle;border:6px solid #0000;border-left-color:#666;border-right:0;width:0;height:0;margin-right:16px;display:inline-block}.tabulator-row.tabulator-group span{color:#d00;margin-left:10px}.tabulator-toggle{box-sizing:border-box;background:#dcdcdc;border:1px solid #ccc;flex-direction:row;display:flex}.tabulator-toggle.tabulator-toggle-on{background:#1c6cc2}.tabulator-toggle .tabulator-toggle-switch{box-sizing:border-box;background:#fff;border:1px solid #ccc}.tabulator-popup-container{-webkit-overflow-scrolling:touch;box-sizing:border-box;z-index:10000;background:#fff;border:1px solid #ddd;font-size:14px;display:inline-block;position:absolute;overflow-y:auto;box-shadow:0 0 5px #0003}.tabulator-popup{border-radius:3px;padding:5px}.tabulator-tooltip{box-shadow:none;pointer-events:none;border-radius:2px;max-width:min(500px,100%);padding:3px 5px;font-size:12px}.tabulator-menu .tabulator-menu-item{box-sizing:border-box;-webkit-user-select:none;user-select:none;padding:5px 10px;position:relative}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{cursor:pointer;background:#efefef}}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu{padding-right:25px}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after{content:\"\";vertical-align:top;border:1px solid #ddd;border-width:1px 1px 0 0;width:7px;height:7px;display:inline-block;position:absolute;top:calc(5px + .4em);right:10px;transform:rotate(45deg)}.tabulator-menu .tabulator-menu-separator{border-top:1px solid #ddd}.tabulator-edit-list{-webkit-overflow-scrolling:touch;max-height:200px;font-size:14px;overflow-y:auto}.tabulator-edit-list .tabulator-edit-list-item{color:#333;outline:none;padding:4px}.tabulator-edit-list .tabulator-edit-list-item.active{color:#fff;background:#1d68cd}.tabulator-edit-list .tabulator-edit-list-item.active.focused{outline:1px solid #ffffff80}.tabulator-edit-list .tabulator-edit-list-item.focused{outline:1px solid #1d68cd}@media (hover:hover) and (pointer:fine){.tabulator-edit-list .tabulator-edit-list-item:hover{color:#fff;cursor:pointer;background:#1d68cd}}.tabulator-edit-list .tabulator-edit-list-placeholder{color:#333;text-align:center;padding:4px}.tabulator-edit-list .tabulator-edit-list-group{color:#333;border-bottom:1px solid #ddd;padding:6px 4px 4px;font-weight:700}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-2,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-2{padding-left:12px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-3,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-3{padding-left:20px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-4,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-4{padding-left:28px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-5,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-5{padding-left:36px}.tabulator.tabulator-ltr{direction:ltr}.tabulator.tabulator-rtl{text-align:initial;direction:rtl}.tabulator.tabulator-rtl .tabulator-header .tabulator-col{border-left:1px solid #ddd;border-right:initial;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{margin-left:-1px;margin-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-left:25px;padding-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{left:8px;right:auto}.tabulator.tabulator-rtl .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{content:\"\";background-color:#2975dd;border-radius:999px;width:6px;height:6px;position:absolute;bottom:-3px;left:-3px;right:auto}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell{border-left:1px solid #ddd;border-right:initial}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-left:initial;border-right:2px solid #ddd;border-bottom-right-radius:1px;border-bottom-left-radius:0;margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-control{margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-left:2px solid #ddd}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-right:2px solid #ddd}.tabulator.tabulator-rtl .tabulator-row .tabulator-col-resize-handle:last-of-type{width:3px;margin-left:0;margin-right:-3px}.tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder{text-align:initial}.tabulator-print-fullscreen{z-index:10000;position:absolute;inset:0}body.tabulator-print-fullscreen-hide>:not(.tabulator-print-fullscreen){display:none!important}.tabulator-print-table{border-collapse:collapse}.tabulator-print-table .tabulator-data-tree-branch{vertical-align:middle;border-bottom:2px solid #ddd;border-left:2px solid #ddd;border-bottom-left-radius:1px;width:7px;height:9px;margin-top:-9px;margin-right:5px;display:inline-block}.tabulator-print-table .tabulator-print-table-group{box-sizing:border-box;background:#ccc;border-top:1px solid #999;border-bottom:1px solid #999;border-right:1px solid #ddd;min-width:100%;padding:5px 5px 5px 10px;font-weight:700}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-print-table-group:hover{cursor:pointer;background-color:#0000001a}}.tabulator-print-table .tabulator-print-table-group.tabulator-group-visible .tabulator-arrow{border:6px solid #0000;border-top-color:#666;border-bottom:0;margin-right:10px}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td{padding-left:30px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td{padding-left:50px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td{padding-left:70px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td{padding-left:90px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td{padding-left:110px!important}.tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle{display:inline-block}.tabulator-print-table .tabulator-print-table-group .tabulator-arrow{vertical-align:middle;border:6px solid #0000;border-left-color:#666;border-right:0;width:0;height:0;margin-right:16px;display:inline-block}.tabulator-print-table .tabulator-print-table-group span{color:#d00;margin-left:10px}.tabulator-print-table .tabulator-data-tree-control{vertical-align:middle;background:#0000001a;border:1px solid #333;border-radius:2px;justify-content:center;align-items:center;width:11px;height:11px;margin-right:5px;display:inline-flex;overflow:hidden}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-data-tree-control:hover{cursor:pointer;background:#0003}}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:0 0;width:1px;height:7px;display:inline-block;position:relative}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;width:1px;height:7px;display:inline-block;position:relative}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator{box-shadow:none;color:#000000de;border:1px solid #22242626;border-radius:.285714rem;width:100%;margin:1em 0}.tabulator .tabulator-header{box-shadow:none;color:#000000de;font-style:none;text-transform:none;border-bottom:1px solid #2224261a;font-weight:700}.tabulator .tabulator-header,.tabulator .tabulator-header .tabulator-col{background-color:#f9fafb;border-right:none}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{padding:.928571em .785714em}.tabulator .tabulator-tableholder .tabulator-table{background-color:#0000}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{background:#f2f2f2!important}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top{border-bottom:2px solid #ddd}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom{border-top:2px solid #ddd}.tabulator .tabulator-footer{box-shadow:none;color:#000000de;text-align:right;text-transform:none;background:#f9fafb;border-top:1px solid #22242626;padding:.785714em;font-style:normal;font-weight:400}.tabulator .tabulator-footer .tabulator-calcs-holder{margin:-.785714em -.785714em .785714em;background:#fff!important}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{background:#fff!important}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{border-bottom:none;margin-bottom:-.785714em}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:calc(-.78571em - 5px)}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{color:#d00}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.positive,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.positive{box-shadow:inset 0 0 #a3c293;color:#21ba45!important;background:#fcfff5!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.positive:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.positive:hover{color:#13ae38!important;background:#f7ffe6!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.negative,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.negative{box-shadow:inset 0 0 #e0b4b4;color:#db2828!important;background:#fff6f6!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.negative:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.negative:hover{color:#d41616!important;background:#ffe7e7!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.error,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.error{box-shadow:inset 0 0 #e0b4b4;color:#db2828!important;background:#fff6f6!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.error:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.error:hover{color:#d12323!important;background:#ffe7e7!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.warning,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.warning{box-shadow:inset 0 0 #c9ba9b;color:#f2c037!important;background:#fffaf3!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.warning:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.warning:hover{color:#f1bb29!important;background:#fff4e4!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active{box-shadow:inset 0 0 #000000de;color:#000000de!important;background:#e0e0e0!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active:hover{color:#13ae38!important;background:#f7ffe6!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active{color:#0003;pointer-events:none}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.disabled:hover{color:#0003;pointer-events:none}}.tabulator.inverted{color:#ffffffe6;background:#333;border:none}.tabulator.inverted .tabulator-header{color:#ffffffe6;background-color:#00000026}.tabulator.inverted .tabulator-header,.tabulator.inverted .tabulator-header .tabulator-col{border-color:#ffffff1a!important}.tabulator.inverted .tabulator-tableholder .tabulator-table .tabulator-row{color:#ffffffe6;border:none}.tabulator.inverted .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-color:#ffffff1a!important}.tabulator.inverted .tabulator-footer{background:#fff}.tabulator.striped .tabulator-row:nth-child(2n){background-color:#f2f2f2}.tabulator.celled{border:1px solid #22242626}.tabulator.celled .tabulator-header .tabulator-col,.tabulator.celled .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-right:1px solid #2224261a}.tabulator[class*=single\\ line] .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-right:none}.tabulator.red{border-top:.2em solid #db2828}.tabulator.inverted.red{color:#fff!important;background-color:#db2828!important}.tabulator.orange{border-top:.2em solid #f2711c}.tabulator.inverted.orange{color:#fff!important;background-color:#f2711c!important}.tabulator.yellow{border-top:.2em solid #fbbd08}.tabulator.inverted.yellow{color:#fff!important;background-color:#fbbd08!important}.tabulator.olive{border-top:.2em solid #b5cc18}.tabulator.inverted.olive{color:#fff!important;background-color:#b5cc18!important}.tabulator.green{border-top:.2em solid #21ba45}.tabulator.inverted.green{color:#fff!important;background-color:#21ba45!important}.tabulator.teal{border-top:.2em solid #00b5ad}.tabulator.inverted.teal{color:#fff!important;background-color:#00b5ad!important}.tabulator.blue{border-top:.2em solid #2185d0}.tabulator.inverted.blue{color:#fff!important;background-color:#2185d0!important}.tabulator.violet{border-top:.2em solid #6435c9}.tabulator.inverted.violet{color:#fff!important;background-color:#6435c9!important}.tabulator.purple{border-top:.2em solid #a333c8}.tabulator.inverted.purple{color:#fff!important;background-color:#a333c8!important}.tabulator.pink{border-top:.2em solid #e03997}.tabulator.inverted.pink{color:#fff!important;background-color:#e03997!important}.tabulator.brown{border-top:.2em solid #a5673f}.tabulator.inverted.brown{color:#fff!important;background-color:#a5673f!important}.tabulator.grey{border-top:.2em solid #767676}.tabulator.inverted.grey{color:#fff!important;background-color:#767676!important}.tabulator.black{border-top:.2em solid #1b1c1d}.tabulator.inverted.black{color:#fff!important;background-color:#1b1c1d!important}.tabulator.padded .tabulator-header .tabulator-col .tabulator-col-content{padding:1em}.tabulator.padded .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:20px}.tabulator.padded .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:1em}.tabulator.padded.very .tabulator-header .tabulator-col .tabulator-col-content{padding:1.5em}.tabulator.padded.very .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:26px}.tabulator.padded.very .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:1.5em}.tabulator.compact .tabulator-header .tabulator-col .tabulator-col-content{padding:.5em .7em}.tabulator.compact .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:12px}.tabulator.compact .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:.5em .7em}.tabulator.compact.very .tabulator-header .tabulator-col .tabulator-col-content{padding:.4em .6em}.tabulator.compact.very .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:10px}.tabulator.compact.very .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:.4em .6em}.tabulator-row{border-bottom:1px solid #2224261a}.tabulator-row.tabulator-row-even{background-color:#fff}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{box-shadow:inset 0 0 #000000de;color:#000000de!important;background:#e0e0e0!important}}.tabulator-row.tabulator-selected{background-color:#9abcea!important}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{cursor:pointer;background-color:#769bcc!important}}.tabulator-row.tabulator-moving{pointer-events:none!important}.tabulator-row .tabulator-cell{vertical-align:middle;border-right:none;padding:.785714em}.tabulator-row .tabulator-cell:last-of-type{border-right:none}.tabulator-row .tabulator-cell.tabulator-row-header{border-bottom:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{color:#fff}.tabulator-row.tabulator-group{background:#fafafa}.tabulator-row.tabulator-group span{color:#666}.tabulator-menu{background:#fff}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{background:#f9fafb}}.tabulator-edit-select-list{background:#fff}.tabulator-edit-select-list .tabulator-edit-select-list-item.active{color:#fff}.tabulator-edit-select-list .tabulator-edit-select-list-item.active.focused{outline:1px solid #ffffff80}@media (hover:hover) and (pointer:fine){.tabulator-edit-select-list .tabulator-edit-select-list-item:hover{color:#fff}}.tabulator-edit-select-list .tabulator-edit-select-list-notice{color:inherit}.tabulator-print-table .tabulator-print-table-group{background:#fafafa}.tabulator-print-table .tabulator-print-table-group span{color:#666}");
	window.layer = layui_layer.default;
	window.Tabulator = tabulator_tables.default;
	window.Toastify = toastify_js.default;
	window.localforage = localforage$1.default;
	window.Viewer = viewerjs.default;
	window.md5 = blueimp_md5.default;
	var currentHref = window.location.href;
	var isJavdbSite = currentHref.includes("javdb");
	var isMissavSite = /missav\.(ws|live|com|ai)/.test(currentHref);
	var isSearchOrUserPage = currentHref.includes("/search?q") || currentHref.includes("/search/") || currentHref.includes("/users/");
	var JAVDB = "javdb";
	var ACTOR = "actor";
	var ACTRESS = "actress";
	var CENSORED = "censored";
	var UNCENSORED = "uncensored";
	function injectCss(css) {
		if (css) {
			if (css.includes("<style>")) document.head.insertAdjacentHTML("beforeend", css);
			else {
				const el = document.createElement("style");
				el.textContent = css;
				document.head.appendChild(el);
			}
		}
	}
	var GFRIENDS_SOURCES = [{
		name: "jsDelivr (全球CDN)",
		json: "https://cdn.jsdelivr.net/gh/gfriends/gfriends/Filetree.json",
		base: "https://cdn.jsdelivr.net/gh/gfriends/gfriends/Content/"
	}, {
		name: "GitHub Raw (备用)",
		json: "https://raw.githubusercontent.com/gfriends/gfriends/master/Filetree.json",
		base: "https://raw.githubusercontent.com/gfriends/gfriends/master/Content/"
	}];
	var GFRIENDS_CDN_INDEX_KEY = "jhs_img_cdn_index";
	var GFRIENDS_DB_NAME = "GfriendsAvatarDB";
	var FILETREE_STORE = "filetreeStore";
	var FILETREE_DATA_KEY = "filetree_data";
	async function withLoading(fn) {
		const loadingHandle = loading();
		try {
			return await fn();
		} finally {
			loadingHandle.close();
		}
	}
	var FiletreeDb = class {
		db = null;
		async open() {
			if (this.db) return this.db;
			return new Promise((resolve, reject) => {
				const request = indexedDB.open(GFRIENDS_DB_NAME, 1);
				request.onupgradeneeded = () => {
					const database = request.result;
					this.db = database;
					if (!database.objectStoreNames.contains("filetreeStore")) database.createObjectStore(FILETREE_STORE);
				};
				request.onsuccess = () => {
					const database = request.result;
					this.db = database;
					resolve(database);
				};
				request.onerror = () => {
					console.error("IndexedDB open error:", request.error);
					reject(new Error("Failed to open IndexedDB"));
				};
			});
		}
		async get(key) {
			const db = await this.open();
			return new Promise((resolve) => {
				const request = db.transaction([FILETREE_STORE], "readonly").objectStore(FILETREE_STORE).get(key);
				request.onsuccess = () => resolve(request.result ?? null);
				request.onerror = () => resolve(null);
			});
		}
		async set(key, value) {
			const db = await this.open();
			return new Promise((resolve, reject) => {
				const request = db.transaction([FILETREE_STORE], "readwrite").objectStore(FILETREE_STORE).put(value, key);
				request.onsuccess = () => resolve();
				request.onerror = () => {
					console.error("IndexedDB set error:", request.error);
					reject(new Error("Failed to write to IndexedDB"));
				};
			});
		}
	};
	var filetreeDb = new FiletreeDb();
	var cachedFiletreeRaw = null;
	var cachedFiletreeParsed = null;
	function clearCache() {
		cachedFiletreeRaw = null;
		cachedFiletreeParsed = null;
	}
	function getCurrentCdnSource() {
		const rawIndex = parseInt(localStorage.getItem("jhs_img_cdn_index") || "0", 10);
		const index = rawIndex >= 0 && rawIndex < GFRIENDS_SOURCES.length ? rawIndex : 0;
		const source = GFRIENDS_SOURCES[index];
		return {
			json: source.json,
			base: source.base,
			index
		};
	}
	function parseFiletree(data) {
		if (!data || !data.Content) return null;
		const result = {};
		const content = data.Content;
		const baseUrl = getCurrentCdnSource().base;
		for (const folderKey in content) {
			const encodedFolder = encodeURIComponent(folderKey);
			const folder = content[folderKey];
			for (const fileName in folder) {
				let namePart = fileName.replace(/\.jpg$/i, "").split("-")[0];
				if (namePart.startsWith("AI-Fix-")) namePart = namePart.substring(7);
				const normalizedName = namePart.toLowerCase().trim();
				if (normalizedName.length > 0) {
					const urlPath = folder[fileName];
					const queryIndex = urlPath.indexOf("?");
					let encodedPath;
					let queryString = "";
					if (queryIndex > -1) {
						encodedPath = encodeURIComponent(urlPath.substring(0, queryIndex));
						queryString = urlPath.substring(queryIndex);
					} else encodedPath = encodeURIComponent(urlPath);
					const fullUrl = `${baseUrl}${encodedFolder}/${encodedPath}${queryString}`;
					if (!result[normalizedName]) result[normalizedName] = [];
					if (!result[normalizedName].includes(fullUrl)) result[normalizedName].push(fullUrl);
				}
			}
		}
		return result;
	}
	async function ensureFiletreeLoaded() {
		if (cachedFiletreeRaw && cachedFiletreeParsed) return;
		let idbData = null;
		try {
			idbData = await filetreeDb.get(FILETREE_DATA_KEY);
		} catch (error) {
			console.error("读取 IndexedDB 失败:", error);
		}
		if (idbData && idbData.Content) {
			cachedFiletreeRaw = idbData;
			cachedFiletreeParsed = parseFiletree(idbData);
			if (cachedFiletreeParsed) return;
		}
		show.info("正在载入头像数据源...");
		const response = await fetch(getCurrentCdnSource().json);
		if (!response.ok) throw new Error(`请求头像源失败: ${response.status}`);
		const filetree = await response.json();
		if (filetree && filetree.Content) {
			cachedFiletreeRaw = filetree;
			cachedFiletreeParsed = parseFiletree(filetree);
			try {
				await filetreeDb.set(FILETREE_DATA_KEY, filetree);
				clog.debug("载入头像数据源并写入缓存成功!");
			} catch (error) {
				clog.error(error);
				show.error("头像数据源写入缓存失败,可能磁盘已满或其他权限问题。");
			}
			return;
		}
		console.log(filetree);
		throw new Error("解析头像数据源失败");
	}
	async function loadGfriends(actressNames) {
		try {
			await withLoading(() => ensureFiletreeLoaded());
		} catch (error) {
			show.error(error);
			return [];
		}
		const filetreeIndex = cachedFiletreeParsed;
		if (!filetreeIndex) return [];
		const urlSet = new Set();
		const normalizedNames = actressNames.map((name) => name.toLowerCase().trim()).filter((name) => name.length > 0);
		if (normalizedNames.length === 0) return [];
		for (const name of normalizedNames) {
			const urls = filetreeIndex[name];
			if (urls) urls.forEach((url) => urlSet.add(url));
		}
		return Array.from(urlSet);
	}
	var featureFlags = {
		upgradeSignature300s: true,
		caseInsensitiveCarNum: true,
		autoPageReplaceState: true,
		wantWatchBatchImport: true,
		movieShowTypeVisibility: true,
		storageCacheDeepCopy: true,
		webdavIdempotentMkdir: true,
		westernCarFormat: true,
		actressUserSelectAll: true,
		navBarNoPaste: false,
		translatePlugin: true,
		screenShotPlugin: true,
		javDbApiAggregate: true,
		magnetHubPlugin: true,
		imageRecognitionPlugin: true,
		fc2By123AvPlugin: true
	};
	(() => {
		try {
			const stored = JSON.parse(localStorage.getItem("jhs_upgrade_flags") || "{}");
			Object.assign(featureFlags, stored);
		} catch {}
	})();
	var WebDavClient = class {
		davUrl;
		username;
		password;
		folderName;
		constructor(davUrl, username, password) {
			this.davUrl = davUrl.endsWith("/") ? davUrl : davUrl + "/";
			this.username = username;
			this.password = password;
			this.folderName = null;
		}
		_getAuthHeaders() {
			return {
				Authorization: `Basic ${btoa(`${this.username}:${this.password}`)}`,
				Depth: "1"
			};
		}
		_sendRequest(method, path, headers = {}, body) {
			return new Promise((resolve, reject) => {
				const url = this.davUrl + path;
				const requestHeaders = {
					...this._getAuthHeaders(),
					...headers
				};
				GM_xmlhttpRequest({
					method,
					url,
					headers: requestHeaders,
					data: body,
					onload: (response) => {
						if (response.status >= 200 && response.status < 300) resolve(response);
						else {
							console.error(response);
							reject(new Error(`请求失败 ${response.status}: ${response.statusText}`));
						}
					},
					onerror: (error) => {
						console.error("请求WebDav发生错误:", error);
						reject(new Error("请求WebDav失败, 请检查服务是否启动, 凭证是否正确"));
					}
				});
			});
		}
		async checkFolderExists(path) {
			try {
				await this._sendRequest("PROPFIND", path, { Depth: "0" });
				return true;
			} catch (error) {
				const statusMatch = String(error?.message || "").match(/请求失败 (\d+):/);
				if ((statusMatch ? parseInt(statusMatch[1], 10) : 0) === 404) return false;
				throw error;
			}
		}
		async createFolder(folderName) {
			if (!featureFlags.webdavIdempotentMkdir) {
				await this._sendRequest("MKCOL", folderName);
				return;
			}
			try {
				if (!await this.checkFolderExists(folderName)) {
					clog.log(`目录 ${folderName} 不存在,正在创建...`);
					await this._sendRequest("MKCOL", folderName, { Depth: "0" });
					clog.log(`目录 ${folderName} 创建成功。`);
				}
			} catch (error) {
				clog.error(`创建目录 ${folderName} 时发生错误:`, error);
				throw error;
			}
		}
		async backup(folder, filename, content) {
			await this.createFolder(folder);
			const path = folder + "/" + filename;
			await this._sendRequest("PUT", path, { "Content-Type": "text/plain" }, content);
		}
		async getFileList(folder) {
			const responseText = (await this._sendRequest("PROPFIND", folder, { "Content-Type": "application/xml" }, "<?xml version=\"1.0\"?>\n                <d:propfind xmlns:d=\"DAV:\">\n                    <d:prop>\n                        <d:displayname />\n                        <d:getcontentlength />\n                        <d:creationdate />\n                        <d:getlastmodified />\n                        <d:iscollection />\n                    </d:prop>\n                </d:propfind>\n            ")).responseText;
			const responses = new DOMParser().parseFromString(responseText, "text/xml").getElementsByTagNameNS("DAV:", "response");
			const items = [];
			for (let index = 0; index < responses.length; index++) {
				if (index === 0) continue;
				const responseElement = responses[index];
				console.log(responseElement);
				const displayName = responseElement.getElementsByTagNameNS("DAV:", "displayname")[0]?.textContent || "";
				const contentLength = responseElement.getElementsByTagNameNS("DAV:", "getcontentlength")[0]?.textContent || "0";
				const createTime = responseElement.getElementsByTagNameNS("DAV:", "creationdate")[0]?.textContent || responseElement.getElementsByTagNameNS("DAV:", "getlastmodified")[0]?.textContent || "";
				if (contentLength !== "0") items.push({
					fileId: displayName,
					name: displayName,
					size: Number(contentLength),
					createTime
				});
			}
			items.reverse();
			return items;
		}
		async deleteFile(filename) {
			const path = this.folderName + "/" + encodeURI(filename);
			await this._sendRequest("DELETE", path, { "Cache-Control": "no-cache" });
		}
		async getBackupList(folder) {
			this.folderName = folder;
			await this.createFolder(folder);
			return this.getFileList(folder);
		}
		async getFileContent(filename) {
			const path = this.folderName + "/" + filename;
			return (await this._sendRequest("GET", path, { Accept: "application/octet-stream" })).responseText;
		}
	};
	function createLoading() {
		const container = document.createElement("div");
		container.className = "loading-container";
		const animation = document.createElement("div");
		animation.className = "loading-animation";
		container.appendChild(animation);
		document.body.appendChild(container);
		return { close: () => {
			if (container && container.parentNode) container.parentNode.removeChild(container);
		} };
	}
	var createToast = (message, type, position, duration, onClick) => {
		let userOptions;
		if (typeof position == "object") userOptions = position;
		else {
			userOptions = typeof duration == "object" ? duration : onClick || {};
			userOptions.gravity = position || "top";
			userOptions.position = typeof duration == "string" ? duration : "center";
		}
		if (!userOptions.gravity || userOptions.gravity === "center") userOptions.offset = { y: "calc(50vh - 150px)" };
		const infoFrom = "#60A5FA";
		const infoTo = "#93C5FD";
		const successFrom = "#10B981";
		const successTo = "#6EE7B7";
		const errorFrom = "#EF4444";
		const errorTo = "#FCA5A5";
		const baseStyle = {
			borderRadius: "12px",
			color: "white",
			padding: "12px 16px",
			boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
			minWidth: "150px",
			textAlign: "center",
			zIndex: 999999999
		};
		const toastOptions = {
			text: message,
			duration: 1e3,
			close: false,
			gravity: "top",
			position: "center",
			style: {
				info: {
					...baseStyle,
					background: `linear-gradient(to right, ${infoFrom}, ${infoTo})`
				},
				success: {
					...baseStyle,
					background: `linear-gradient(to right, ${successFrom}, ${successTo})`
				},
				error: {
					...baseStyle,
					background: `linear-gradient(to right, ${errorFrom}, ${errorTo})`
				}
			}[type],
			stopOnFocus: true,
			oldestFirst: false,
			...userOptions
		};
		if (toastOptions.duration === -1) toastOptions.close = true;
		const toast = Toastify(toastOptions);
		toast.showToast();
		toast.closeShow = () => {
			toast.toastElement.remove();
		};
		return toast;
	};
	var show$1 = {
		ok: (message, position = "center", duration, onClick) => createToast(message, "success", position, duration, onClick),
		error: (message, position = "center", duration, onClick) => createToast(message, "error", position, duration, onClick),
		info: (message, position = "center", duration, onClick) => createToast(message, "info", position, duration, onClick)
	};
	function failWithToast(msg) {
		show$1.error(msg);
		throw new Error(msg);
	}
	var VIEWER_CONFIG = (ctx) => ({
		zIndex: 999999990,
		navbar: false,
		zoomOnWheel: false,
		zoomRatio: .1,
		toggleOnDblclick: false,
		toolbar: {
			zoomIn: 1,
			zoomOut: 1,
			reset: 1,
			rotateLeft: 0,
			rotateRight: 0,
			flipHorizontal: 0,
			flipVertical: 0
		},
		title: false,
		keyboard: false,
		viewed() {
			const viewer = ctx.viewerRef.current;
			viewer.zoomTo(1.4);
			const offsetX = (viewer.viewerData.width - viewer.imageData.width) / 2;
			viewer.moveTo(offsetX, 0);
		},
		shown() {
			const viewer = ctx.viewerRef.current;
			if (ctx.isTemporary) ctx.container.remove();
			document.documentElement.style.overflow = "hidden";
			document.body.style.overflow = "hidden";
			viewer.handleKeydown = function(event) {
				if (event.key === "Escape" || event.key === " ") {
					event.preventDefault();
					event.stopPropagation();
					viewer.destroy();
					document.removeEventListener("keydown", viewer.handleKeydown);
					document.documentElement.style.overflow = "";
					document.body.style.overflow = "";
					ctx.resetOverflow();
				}
			};
			document.addEventListener("keydown", viewer.handleKeydown);
		},
		hidden() {
			const viewer = ctx.viewerRef.current;
			if (viewer && viewer.handleKeydown) document.removeEventListener("keydown", viewer.handleKeydown);
			viewer.destroy();
			document.documentElement.style.overflow = "";
			document.body.style.overflow = "";
			ctx.resetOverflow();
		}
	});
	var VOID_TAGS = new Set([
		"img",
		"input",
		"br",
		"hr",
		"meta",
		"link",
		"area",
		"base",
		"col",
		"embed",
		"param",
		"source",
		"track",
		"wbr"
	]);
	var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
	function isReactElement(node) {
		return node != null && typeof node === "object" && "type" in node && "props" in node && typeof node.props === "object" && node.props !== null;
	}
	function escapeText(s) {
		return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
	}
	function escapeAttr(s) {
		return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
	}
	function sanitizeUrl(url) {
		const trimmed = url.trim().toLowerCase();
		if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:") || trimmed.startsWith("vbscript:")) return "#";
		return url;
	}
	function camelToKebab(s) {
		return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
	}
	function styleToCss(style) {
		return Object.entries(style).filter(([, v]) => v != null && v !== false).map(([k, v]) => `${camelToKebab(k)}:${v}`).join(";");
	}
	function renderAttrs(props) {
		const parts = [];
		for (const [key, value] of Object.entries(props)) {
			if (value == null || value === false) continue;
			if (key === "children") continue;
			if (key === "dangerouslySetInnerHTML") continue;
			if (key.startsWith("on")) continue;
			if (key === "className") {
				if (value !== "") parts.push(`class="${escapeAttr(String(value))}"`);
				continue;
			}
			if (key === "htmlFor") {
				parts.push(`for="${escapeAttr(String(value))}"`);
				continue;
			}
			if (key === "style") {
				if (typeof value === "string") parts.push(`style="${escapeAttr(value)}"`);
				else if (typeof value === "object" && value !== null) {
					const css = styleToCss(value);
					if (css) parts.push(`style="${escapeAttr(css)}"`);
				}
				continue;
			}
			if (value === true) {
				parts.push(key);
				continue;
			}
			const attrValue = key === "href" || key === "src" ? sanitizeUrl(String(value)) : String(value);
			parts.push(`${key}="${escapeAttr(attrValue)}"`);
		}
		return parts.length ? " " + parts.join(" ") : "";
	}
	function renderChildren(children) {
		if (children == null || typeof children === "boolean") return "";
		if (typeof children === "string") return escapeText(children);
		if (typeof children === "number" || typeof children === "bigint") return String(children);
		if (Array.isArray(children)) return children.map((c) => renderChildren(c)).join("");
		if (isReactElement(children)) return jsxToString(children);
		return "";
	}
	function jsxToString(node) {
		if (node == null || typeof node === "boolean") return "";
		if (typeof node === "string") return escapeText(node);
		if (typeof node === "number" || typeof node === "bigint") return String(node);
		if (Array.isArray(node)) return node.map(jsxToString).join("");
		if (!isReactElement(node)) return "";
		const { type, props } = node;
		if (type === REACT_FRAGMENT_TYPE) return renderChildren(props.children);
		if (typeof type === "function") return jsxToString(type(props));
		if (typeof type === "string") {
			const tag = type;
			const attrs = renderAttrs(props);
			if (VOID_TAGS.has(tag)) return `<${tag}${attrs} />`;
			const dsi = props.dangerouslySetInnerHTML;
			if (dsi && typeof dsi.__html === "string") return `<${tag}${attrs}>${dsi.__html}</${tag}>`;
			return `<${tag}${attrs}>${renderChildren(props.children)}</${tag}>`;
		}
		return "";
	}
	var require_react_jsx_runtime_production = __commonJSMin(((exports) => {
		var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
		var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
		function jsxProd(type, config, maybeKey) {
			var key = null;
			void 0 !== maybeKey && (key = "" + maybeKey);
			void 0 !== config.key && (key = "" + config.key);
			if ("key" in config) {
				maybeKey = {};
				for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]);
			} else maybeKey = config;
			config = maybeKey.ref;
			return {
				$$typeof: REACT_ELEMENT_TYPE,
				type,
				key,
				ref: void 0 !== config ? config : null,
				props: maybeKey
			};
		}
		exports.Fragment = REACT_FRAGMENT_TYPE;
		exports.jsx = jsxProd;
		exports.jsxs = jsxProd;
	}));
	var import_jsx_runtime = __commonJSMin(((exports, module) => {
		module.exports = require_react_jsx_runtime_production();
	}))();
	var INDENT_BEFORE = "\n                ";
	var INDENT_BETWEEN = "\n                ";
	var INDENT_AFTER = "\n            ";
	var URL_PATTERN = /(?:https?|ftp):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|]/gi;
	function linkifySafe(text) {
		const nodes = [];
		let lastIndex = 0;
		let match;
		URL_PATTERN.lastIndex = 0;
		while ((match = URL_PATTERN.exec(text)) !== null) {
			if (match.index > lastIndex) nodes.push(text.slice(lastIndex, match.index));
			const url = match[0];
			nodes.push((0, import_jsx_runtime.jsx)("a", {
				href: sanitizeUrl(url),
				target: "_blank",
				rel: "noopener noreferrer",
				children: url
			}, match.index));
			lastIndex = match.index + url.length;
		}
		if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
		return nodes;
	}
	function LoggerLogEntry({ timeStr, messageType, message }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			INDENT_BEFORE,
			(0, import_jsx_runtime.jsxs)("span", {
				className: "console-logger-timestamp",
				children: [
					"[",
					timeStr,
					"]"
				]
			}),
			INDENT_BETWEEN,
			(0, import_jsx_runtime.jsx)("span", {
				className: "console-logger-message",
				"data-type": messageType,
				children: linkifySafe(message)
			}),
			INDENT_AFTER
		] });
	}
	var LOG_TYPE_STYLES = {
		base: {
			label: "信息",
			background: "#e8f4fd",
			borderLeftColor: "#3498db"
		},
		warn: {
			label: "警告",
			background: "#fef9e7",
			borderLeftColor: "#f39c12"
		},
		error: {
			label: "错误",
			background: "#fdedec",
			borderLeftColor: "#e74c3c"
		},
		debug: {
			label: "调试",
			background: "#f4f6f6",
			borderLeftColor: "#95a5a6"
		}
	};
	var FILTER_TYPE_MAP = {
		base: [
			"base",
			"warn",
			"error"
		],
		warn: ["warn"],
		error: ["error"],
		debug: [
			"base",
			"warn",
			"error",
			"debug"
		]
	};
	var MAXIMIZE_KEY = "jhs_clog_maximize";
	var EXPAND_KEY = "jhs_clog_expand";
	var FILTER_KEY = "jhs_clog_filter";
	function createContainer(logger) {
		logger.container = document.createElement("div");
		logger.container.className = "console-logger-container";
		logger.container.style.display = "none";
		logger.toggleBtn = document.createElement("div");
		logger.toggleBtn.className = "console-logger-toggle collapsed";
		logger.container.appendChild(logger.toggleBtn);
		logger.window = document.createElement("div");
		logger.window.className = "console-logger-window collapsed";
		const header = document.createElement("div");
		header.className = "console-logger-header";
		const title = document.createElement("div");
		title.className = "console-logger-title";
		title.textContent = "JHS V3.3.2";
		const controls = document.createElement("div");
		controls.className = "console-logger-controls";
		logger.maximizeBtn = document.createElement("button");
		logger.maximizeBtn.textContent = "";
		logger.maximizeBtn.classList.add("console-logger-maximize-toggle");
		controls.appendChild(logger.maximizeBtn);
		const clearBtn = document.createElement("button");
		clearBtn.textContent = "清空";
		clearBtn.addEventListener("click", () => logger.clear());
		controls.appendChild(clearBtn);
		header.appendChild(title);
		header.appendChild(controls);
		logger.filtersContainer = document.createElement("div");
		logger.filtersContainer.className = "console-logger-filters";
		logger.filterButtonGroup = document.createElement("div");
		logger.filterButtonGroup.className = "console-logger-filter-group";
		logger.filtersContainer.appendChild(logger.filterButtonGroup);
		logger.scrollToBottomBtn = document.createElement("button");
		logger.scrollToBottomBtn.className = "console-logger-scroll-to-bottom";
		logger.scrollToBottomBtn.textContent = "到底部";
		logger.filtersContainer.appendChild(logger.scrollToBottomBtn);
		logger.content = document.createElement("div");
		logger.content.className = "console-logger-content jhs-scrollbar";
		logger.window.appendChild(header);
		logger.window.appendChild(logger.filtersContainer);
		logger.window.appendChild(logger.content);
		logger.container.appendChild(logger.window);
		document.body.appendChild(logger.container);
		Object.keys(LOG_TYPE_STYLES).forEach((typeName) => {
			const filterBtn = document.createElement("div");
			filterBtn.className = "console-logger-filter";
			if (typeName === logger.currentFilter) filterBtn.classList.add("active");
			filterBtn.textContent = LOG_TYPE_STYLES[typeName].label;
			filterBtn.dataset.type = typeName;
			filterBtn.addEventListener("click", () => logger.setFilter(typeName));
			logger.filterButtonGroup.appendChild(filterBtn);
		});
	}
	function bindEvents(logger) {
		logger.toggleBtn.addEventListener("click", () => {
			toggleExpandCollapsed(logger);
		});
		logger.maximizeBtn.addEventListener("click", () => toggleMaximize(logger));
		logger.scrollToBottomBtn.addEventListener("click", () => {
			logger.content.scrollTop = logger.content.scrollHeight;
			logger.userScrolledUp = false;
		});
		logger.content.addEventListener("scroll", () => {
			logger.userScrolledUp = !(logger.content.scrollHeight - logger.content.clientHeight <= logger.content.scrollTop + 5);
		});
		logger.content.addEventListener("wheel", (evt) => {
			const atTop = logger.content.scrollTop === 0;
			const atBottom = logger.content.scrollHeight - logger.content.clientHeight <= logger.content.scrollTop + 1;
			if (atTop && evt.deltaY < 0 || atBottom && evt.deltaY > 0) {
				evt.preventDefault();
				evt.stopPropagation();
			}
		}, { passive: false });
	}
	function toggleExpandCollapsed(logger) {
		const isCollapsed = logger.window.classList.toggle("collapsed");
		logger.toggleBtn.classList.toggle("collapsed");
		if (isCollapsed) localStorage.setItem(EXPAND_KEY, "no");
		else {
			localStorage.setItem(EXPAND_KEY, "yes");
			reRenderAllLogs(logger);
		}
	}
	function checkInitialCollapseState(logger) {
		const stored = localStorage.getItem(EXPAND_KEY);
		if (stored && stored !== "no") {
			logger.window.classList.toggle("collapsed");
			logger.toggleBtn.classList.toggle("collapsed");
			setTimeout(() => {
				logger.content.scrollTop = logger.content.scrollHeight;
			}, 0);
		} else {
			logger.window.classList.add("collapsed");
			logger.toggleBtn.classList.add("collapsed");
		}
	}
	function checkInitialMaximizeState(logger) {
		if (localStorage.getItem(MAXIMIZE_KEY) === "maximized") {
			logger.window.classList.add("maximized");
			logger.maximizeBtn.classList.add("active");
		}
	}
	function toggleMaximize(logger) {
		const isMaximized = logger.window.classList.toggle("maximized");
		logger.maximizeBtn.classList.toggle("active", isMaximized);
		if (isMaximized) localStorage.setItem(MAXIMIZE_KEY, "maximized");
		else localStorage.setItem(MAXIMIZE_KEY, "minimized");
		if (!logger.window.classList.contains("collapsed")) logger.content.scrollTop = logger.content.scrollHeight;
	}
	function renderLog(logger, entry) {
		if (logger.container.style.display === "none") return;
		if (logger.window.classList.contains("collapsed")) return;
		if (!(FILTER_TYPE_MAP[logger.currentFilter] || []).includes(entry.type)) return;
		const el = _createLogElement(entry);
		logger.content.appendChild(el);
		if (!logger.window.classList.contains("collapsed") && !logger.userScrolledUp) logger.content.scrollTop = logger.content.scrollHeight;
	}
	function reRenderAllLogs(logger) {
		if (logger.container.style.display !== "none") {
			if (!logger.window.classList.contains("collapsed")) setTimeout(() => {
				logger.content.innerHTML = "";
				if (logger.logs.length === 0) return;
				const filterTypes = FILTER_TYPE_MAP[logger.currentFilter] || [];
				const fragment = document.createDocumentFragment();
				logger.logs.forEach((entry) => {
					if (filterTypes.includes(entry.type)) {
						const el = _createLogElement(entry);
						fragment.appendChild(el);
					}
				});
				logger.content.appendChild(fragment);
				logger.content.scrollTop = logger.content.scrollHeight;
			}, 0);
		}
	}
	function _createLogElement(entry) {
		const el = document.createElement("div");
		el.className = "console-logger-entry";
		el.dataset.type = entry.type;
		el.dataset.id = String(entry.id);
		const style = LOG_TYPE_STYLES[entry.type] || LOG_TYPE_STYLES.base;
		el.style.borderLeft = "3px solid " + style.borderLeftColor;
		el.style.background = style.background;
		const timeStr = (entry.timestamp instanceof Date ? entry.timestamp : new Date(entry.timestamp)).toTimeString().split(" ")[0];
		el.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(LoggerLogEntry, {
			timeStr,
			messageType: entry.messageType,
			message: entry.message
		}));
		return el;
	}
	function setFilter(logger, filterType) {
		if (logger.currentFilter === filterType) return;
		logger.currentFilter = filterType;
		localStorage.setItem(FILTER_KEY, filterType);
		logger.filterButtonGroup.querySelectorAll(".console-logger-filter").forEach((btn) => {
			if (btn.dataset.type === filterType) btn.classList.add("active");
			else btn.classList.remove("active");
		});
		reRenderAllLogs(logger);
	}
	function clear(logger) {
		logger.logs = [];
		logger.content.innerHTML = "";
	}
	var Logger = class {
		currentFilter;
		logs = [];
		isInitialized = false;
		userScrolledUp = false;
		container;
		toggleBtn;
		window;
		maximizeBtn;
		filtersContainer;
		filterButtonGroup;
		scrollToBottomBtn;
		content;
		maxLogCount = 2e3;
		maxLogCountInitialized = false;
		constructor() {
			const stored = localStorage.getItem(FILTER_KEY);
			this.currentFilter = stored && LOG_TYPE_STYLES[stored] ? stored : "base";
		}
		tryInitialize() {
			return document.readyState !== "loading" && (this.isInitialized || (this.init(), this.isInitialized = true, true));
		}
		init() {
			createContainer(this);
			bindEvents(this);
			checkInitialMaximizeState(this);
			checkInitialCollapseState(this);
		}
		addLog(message, typeArg = "base", ...extraArgs) {
			if (!this.maxLogCountInitialized) {
				this.maxLogCountInitialized = true;
				storageManager.getSetting("clogMsgCount", 2e3).then((v) => {
					this.maxLogCount = v;
				}).catch(() => {});
			}
			const initialized = this.tryInitialize();
			let resolvedType;
			let resolvedExtra;
			if (LOG_TYPE_STYLES[typeArg]) {
				resolvedType = typeArg;
				resolvedExtra = extraArgs;
			} else {
				resolvedType = "base";
				resolvedExtra = [typeArg, ...extraArgs];
			}
			resolvedType = LOG_TYPE_STYLES[resolvedType] ? resolvedType : "base";
			const allParts = [message, ...resolvedExtra];
			let messageType = "msg";
			const parts = [];
			allParts.forEach((part) => {
				if (Object.prototype.toString.call(part) === "[object Error]") parts.push(String(part));
				else if (typeof part === "object" && part !== null) try {
					parts.push("\n" + JSON.stringify(part, null, 2));
					messageType = "json";
				} catch {
					parts.push(String(part));
					messageType = "msg";
				}
				else parts.push(String(part));
			});
			const entry = {
				message: parts.join("  "),
				messageType,
				type: resolvedType,
				timestamp: new Date(),
				id: Date.now() + Math.random()
			};
			this.logs.push(entry);
			if (this.logs.length > this.maxLogCount) {
				const firstEntry = this.logs[0];
				if (initialized) {
					const oldEl = this.content.querySelector(`.console-logger-entry[data-id="${firstEntry.id}"]`);
					if (oldEl) {
						this.logs.shift();
						this.content.removeChild(oldEl);
					}
				}
			}
			if (initialized) renderLog(this, entry);
		}
		log(...args) {
			const [first, ...rest] = args;
			setTimeout(() => {
				this.addLog(first, "base", ...rest);
			}, 0);
		}
		error(...args) {
			const [first, ...rest] = args;
			console.error(...args);
			setTimeout(() => {
				this.addLog(first, "error", ...rest);
			}, 0);
		}
		warn(...args) {
			const [first, ...rest] = args;
			setTimeout(() => {
				this.addLog(first, "warn", ...rest);
			}, 0);
		}
		debug(...args) {
			const [first, ...rest] = args;
			setTimeout(() => {
				this.addLog(first, "debug", ...rest);
			}, 0);
		}
		reRenderAllLogs() {
			reRenderAllLogs(this);
		}
		setFilter(filterType) {
			setFilter(this, filterType);
		}
		clear() {
			clear(this);
		}
		show() {
			if (this.isInitialized && this.container || this.tryInitialize() && this.container) {
				this.container.style.display = "";
				this.reRenderAllLogs();
			}
		}
		hide() {
			if (this.isInitialized && this.container) this.container.style.display = "none";
		}
		lowZIndex() {
			if (this.isInitialized && this.container) this.container.style.zIndex = "12345678";
		}
		highZIndex() {
			if (this.isInitialized && this.container) this.container.style.zIndex = "999999999";
		}
	};
	var REVISION_CHANNEL = "jhs-storage-revision";
	var StorageRevision = class {
		revision = 0;
		channel = null;
		senderId = Math.random().toString(36).slice(2, 10);
		callbacks = [];
		get current() {
			return this.revision;
		}
		init() {
			if (this.channel) return;
			try {
				this.channel = new BroadcastChannel(REVISION_CHANNEL);
				this.channel.addEventListener("message", (event) => {
					const msg = event.data;
					if (msg && typeof msg.revision === "number" && msg.senderId !== this.senderId) {
						if (msg.revision > this.revision) this.revision = msg.revision;
						for (const cb of this.callbacks) try {
							cb(msg.revision);
						} catch (err) {
							console.error("[StorageRevision] 回调执行失败", err);
						}
					}
				});
			} catch {}
		}
		increment() {
			this.revision++;
			try {
				this.channel?.postMessage({
					revision: this.revision,
					senderId: this.senderId
				});
			} catch {}
			return this.revision;
		}
		onRemoteChange(callback) {
			this.callbacks.push(callback);
		}
		destroy() {
			this.channel?.close();
			this.channel = null;
			this.callbacks.length = 0;
		}
	};
	var storageRevision = new StorageRevision();
	var FILTER_ACTION = "filter";
	var FAVORITE_ACTION = "favorite";
	var HAS_WATCH_ACTION = "hasWatch";
	var HAS_DOWN_ACTION = "hasDown";
	var BLOCK_TEXT = "🚫 屏蔽";
	var BLOCKED_TEXT = "🚫 已屏蔽";
	var BLOCK_COLOR = "#de3333";
	var FAVORITE_TEXT = "⭐ 收藏";
	var FAVORITED_TEXT = "⭐ 已收藏";
	var FAVORITE_COLOR = "#25b1dc";
	var WATCHED_TEXT = "🔍 已观看";
	var WATCHED_COLOR = "#d7a80c";
	var HAS_DOWN_TEXT$1 = "📥️ 已下载";
	var HAS_DOWN_COLOR = "#7bc73b";
	var HAS_DOWN_STATUS$1 = "hasDown";
	function saveSingleCar(input, list) {
		const { carNum, actionType, publishTime, starId, score } = input;
		let { url, names } = input;
		if (!carNum) failWithToast("番号为空!");
		if (!url) failWithToast("url为空!");
		if (!url.includes("http")) url = window.location.origin + url;
		if (names) names = names.trim();
		let car = featureFlags.caseInsensitiveCarNum ? list.find((item) => item.carNum.toLowerCase() === carNum.toLowerCase()) : list.find((item) => item.carNum === carNum);
		if (car) {
			if (names) car.names = names;
			if (url) car.url = url;
			if (publishTime) car.publishTime = publishTime;
			if (score !== void 0) car.score = score;
			car.updateDate = utils.getNowStr();
		} else {
			const now = utils.getNowStr();
			car = {
				carNum,
				url,
				names,
				status: "",
				createDate: now,
				updateDate: now,
				publishTime
			};
			if (starId) car.starId = starId;
			if (score !== void 0) car.score = score;
			list.push(car);
		}
		switch (actionType) {
			case FILTER_ACTION:
				if (car.status === "filter") failWithToast(`${carNum} 已在屏蔽列表中`);
				car.status = FILTER_ACTION;
				break;
			case FAVORITE_ACTION:
				if (car.status === "favorite") failWithToast(`${carNum} 已在收藏列表中`);
				car.status = FAVORITE_ACTION;
				break;
			case HAS_DOWN_STATUS$1:
				car.status = HAS_DOWN_STATUS$1;
				break;
			case HAS_WATCH_ACTION:
				car.status = HAS_WATCH_ACTION;
				break;
			default: failWithToast("actionType错误, 请联系作者更正: " + actionType);
		}
	}
	async function getCarList(sm) {
		if (featureFlags.storageCacheDeepCopy) {
			if (!sm.cacheCarList) sm.cacheCarList = await sm.forage.getItem(sm.car_list_key) || [];
			return utils.copyObj(sm.cacheCarList ?? []);
		}
		return await sm.forage.getItem(sm.car_list_key) || [];
	}
	async function getCar(sm, carNum) {
		const list = await getCarList(sm);
		if (featureFlags.caseInsensitiveCarNum) {
			const lower = carNum.toLowerCase();
			return list.find((car) => car.carNum.toLowerCase() === lower);
		}
		return list.find((car) => car.carNum === carNum);
	}
	async function saveCar(sm, input) {
		const list = await sm.forage.getItem(sm.car_list_key) || [];
		saveSingleCar(input, list);
		await sm.forage.setItem(sm.car_list_key, list);
		storageRevision.increment();
		sm.cacheCarList = list;
		await sm.removeNewVideoList([input.carNum]);
		const car = featureFlags.caseInsensitiveCarNum ? list.find((c) => c.carNum.toLowerCase() === input.carNum.toLowerCase()) : list.find((c) => c.carNum === input.carNum);
		if (car) sm.carListChangeCallback?.({
			action: "upsert",
			upserts: [car]
		});
	}
	async function updateCarInfo(sm, input) {
		const { carNum, url, actionType, remark } = input;
		let { names } = input;
		if (!carNum) failWithToast("番号为空!");
		if (!url) failWithToast("url为空!");
		if (names) names = names.trim();
		const list = await sm.forage.getItem(sm.car_list_key) || [];
		const car = list.find((item) => item.carNum === carNum);
		if (!car) failWithToast("数据不存在: " + carNum);
		car.names = names;
		car.url = url;
		car.remark = remark;
		car.updateDate = utils.getNowStr();
		switch (actionType) {
			case FAVORITE_ACTION:
				car.status = FAVORITE_ACTION;
				break;
			case HAS_WATCH_ACTION:
				car.status = HAS_WATCH_ACTION;
				break;
			default: failWithToast("actionType错误, 请联系作者更正: " + actionType);
		}
		await sm.forage.setItem(sm.car_list_key, list);
		storageRevision.increment();
		sm.cacheCarList = list;
		sm.carListChangeCallback?.({
			action: "upsert",
			upserts: [car]
		});
	}
	async function saveCarList(sm, inputs) {
		if (!inputs || !Array.isArray(inputs) || inputs.length === 0) failWithToast("记录列表为空!");
		const list = await sm.forage.getItem(sm.car_list_key) || [];
		for (const item of inputs) saveSingleCar(item, list);
		await sm.forage.setItem(sm.car_list_key, list);
		storageRevision.increment();
		sm.cacheCarList = list;
		const numSet = featureFlags.caseInsensitiveCarNum ? new Set(inputs.map((i) => i.carNum.toLowerCase())) : new Set(inputs.map((i) => i.carNum));
		const upserts = featureFlags.caseInsensitiveCarNum ? list.filter((c) => numSet.has(c.carNum.toLowerCase())) : list.filter((c) => numSet.has(c.carNum));
		if (upserts.length > 0) sm.carListChangeCallback?.({
			action: "upsert",
			upserts
		});
	}
	async function removeCar(sm, carNum) {
		const list = await getCarList(sm);
		const beforeLen = list.length;
		const filtered = list.filter((item) => item.carNum !== carNum);
		if (filtered.length === beforeLen) {
			show.error(`${carNum} 不存在`);
			return false;
		} else {
			await sm.forage.setItem(sm.car_list_key, filtered);
			storageRevision.increment();
			sm.cacheCarList = filtered;
			sm.carListChangeCallback?.({
				action: "delete",
				deletes: [carNum]
			});
			return true;
		}
	}
	async function batchRemoveCars(sm, carNums) {
		const list = await getCarList(sm);
		const beforeLen = list.length;
		const numSet = new Set(carNums);
		const filtered = list.filter((item) => !numSet.has(item.carNum));
		const removed = beforeLen - filtered.length;
		if (removed === 0) return false;
		await sm.forage.setItem(sm.car_list_key, filtered);
		storageRevision.increment();
		sm.cacheCarList = filtered;
		sm.carListChangeCallback?.({
			action: "delete",
			deletes: carNums
		});
		return removed;
	}
	async function getBlacklist(sm) {
		return await sm.forage.getItem(sm.blacklist_key) || [];
	}
	async function addBlacklistItem(sm, item) {
		const { starId, name, allName, role, movieType, url } = item;
		if (!starId) throw new Error("缺失starId");
		if (!name) throw new Error("缺失name");
		if (!role) throw new Error("缺失role");
		const list = await getBlacklist(sm);
		const existing = list.find((entry) => entry.starId === starId);
		if (existing) {
			existing.url = url;
			existing.role = role;
			existing.movieType = movieType;
			clog.log("更新黑名单演员信息", existing);
		} else {
			const created = {
				starId,
				name,
				allName: allName || [name],
				createTime: utils.getNowStr(),
				role,
				movieType,
				url
			};
			list.push(created);
			clog.log("增加黑名单演员信息", created);
		}
		await sm.forage.setItem(sm.blacklist_key, list);
		storageRevision.increment();
	}
	async function updateBlacklistItem(sm, item) {
		if (!item || !item.starId) throw new Error("参数不全");
		const list = await getBlacklist(sm);
		const existing = list.find((entry) => entry.starId === item.starId);
		if (!existing) throw new Error(`未找到黑名单演员信息:${item.name} ${item.starId}`);
		if (item.checkTime) existing.checkTime = item.checkTime;
		if (item.lastPublishTime) existing.lastPublishTime = item.lastPublishTime;
		await sm.forage.setItem(sm.blacklist_key, list);
		storageRevision.increment();
	}
	async function deleteBlacklistItem(sm, starId) {
		const list = await getBlacklist(sm);
		const filtered = list.filter((entry) => entry.starId !== starId);
		if (list.length !== filtered.length) {
			await sm.forage.setItem(sm.blacklist_key, filtered);
			storageRevision.increment();
		}
	}
	function ColoredTextCell({ text, color }) {
		return (0, import_jsx_runtime.jsx)("span", {
			style: { color },
			children: text
		});
	}
	function logColoredHtml(text) {
		return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
			text,
			color: "#f40"
		}));
	}
	async function getBlacklistCarList(sm) {
		const cached = sm.cache_filter_actor_actress_car_list;
		if (cached && cached.length > 0) {
			if (featureFlags.storageCacheDeepCopy) return utils.deepFreeze(utils.copyObj(cached));
			return cached;
		}
		const fresh = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
		sm.cache_filter_actor_actress_car_list = fresh;
		if (featureFlags.storageCacheDeepCopy) return utils.deepFreeze(utils.copyObj(fresh));
		return fresh;
	}
	async function batchSaveBlacklistCarList(sm, inputs) {
		const list = await getBlacklistCarList(sm);
		const cloned = JSON.parse(JSON.stringify(list));
		let changed = false;
		const newCarNums = [];
		for (const item of inputs) if (!cloned.find((entry) => entry.carNum === item.carNum)) {
			saveSingleCar(item, cloned);
			clog.log("屏蔽演员番号: " + logColoredHtml(`${item.names} ${item.carNum}`));
			changed = true;
			newCarNums.push(item.carNum);
		}
		if (changed) {
			await sm.forage.setItem(sm.blacklist_car_list_key, cloned);
			storageRevision.increment();
			await sm.removeNewVideoList(newCarNums);
			window.cleanCache_filter_actor_actress_car_list();
		}
	}
	async function removeBlacklistCarList(sm, starId) {
		const list = await getBlacklistCarList(sm);
		const filtered = list.filter((item) => item.starId !== starId);
		if (filtered.length !== list.length) {
			await sm.forage.setItem(sm.blacklist_car_list_key, filtered);
			storageRevision.increment();
			window.cleanCache_filter_actor_actress_car_list();
		}
	}
	var UNCENSORED_TAG = "(無碼)";
	async function getFavoriteActressList(sm) {
		if (featureFlags.storageCacheDeepCopy) {
			if (!sm.cacheFavoriteActressList) sm.cacheFavoriteActressList = await sm.forage.getItem(sm.favorite_actresses_key) || [];
			return utils.copyObj(sm.cacheFavoriteActressList ?? []);
		}
		return await sm.forage.getItem(sm.favorite_actresses_key) || [];
	}
	async function addFavoriteActressList(sm, inputs) {
		const list = await getFavoriteActressList(sm);
		let count = 0;
		for (const item of inputs) {
			const { starId, avatar, lastCheckTime, lastPublishTime } = item;
			let { name, allName, actressType } = item;
			if (!starId) throw new Error("缺失starId");
			if (!name) throw new Error("缺失name");
			if (!allName) allName = [name];
			if (!actressType) actressType = name.includes(UNCENSORED_TAG) || allName.some((n) => n.includes(UNCENSORED_TAG)) ? UNCENSORED : CENSORED;
			name = name.replace(UNCENSORED_TAG, "");
			allName = allName.map((n) => n.replace(UNCENSORED_TAG, ""));
			const existing = list.find((entry) => entry.starId === starId);
			if (existing) {
				if (!existing.avatar || !existing.avatar.includes("https")) {
					if (avatar) {
						clog.log(avatar);
						existing.avatar = avatar;
						clog.log(logColoredHtml(`补全女优头像: ${name}`));
						count++;
					}
				}
				if (!existing.actressType && actressType) {
					existing.actressType = actressType;
					clog.log(logColoredHtml(`补全女优类别: ${name} ${actressType}`));
					count++;
				}
				if (existing.name.includes(UNCENSORED_TAG)) {
					existing.name = name;
					existing.allName = allName;
					clog.log(logColoredHtml(`更正女优名字: ${name} ${allName}`));
					count++;
				}
				continue;
			}
			const now = utils.getNowStr();
			list.push({
				starId,
				name,
				allName,
				avatar,
				lastCheckTime,
				lastPublishTime,
				createDate: now,
				updateDate: now,
				actressType
			});
			clog.log(logColoredHtml(`同步JavDB已收藏的演员: ${name}`));
			count++;
		}
		if (count > 0) {
			await sm.forage.setItem(sm.favorite_actresses_key, list);
			storageRevision.increment();
		} else clog.log("信息已记录, 无需要进行同步收藏的演员");
		return count;
	}
	async function removeFavoriteActress(sm, starId) {
		const list = await getFavoriteActressList(sm);
		const beforeLen = list.length;
		const filtered = list.filter((entry) => entry.starId !== starId);
		if (filtered.length === beforeLen) {
			clog.error(`移除演员失败, ${starId} 不存在`);
			return false;
		} else {
			await sm.forage.setItem(sm.favorite_actresses_key, filtered);
			storageRevision.increment();
			sm.cacheFavoriteActressList = filtered;
			return true;
		}
	}
	async function updateFavoriteActress(sm, item) {
		const list = await getFavoriteActressList(sm);
		const { starId, name, allName, avatar, lastCheckTime, newVideoList, lastPublishTime, actressType, remark } = item;
		if (!starId) throw new Error("缺失starId");
		const existing = list.find((entry) => entry.starId === starId);
		if (!existing) {
			clog.error("未找到演员信息", starId, name);
			return false;
		}
		if (name) existing.name = name;
		if (allName) existing.allName = allName;
		if (avatar) existing.avatar = avatar;
		if (actressType != null) existing.actressType = actressType;
		if (lastCheckTime) existing.lastCheckTime = lastCheckTime;
		if (newVideoList) existing.newVideoList = newVideoList;
		if (lastPublishTime) existing.lastPublishTime = lastPublishTime;
		if (remark) existing.remark = remark;
		existing.updateDate = utils.getNowStr();
		await sm.forage.setItem(sm.favorite_actresses_key, list);
		storageRevision.increment();
	}
	async function removeNewVideoList(sm, carNums) {
		const actressList = await getFavoriteActressList(sm);
		let changed = false;
		const updated = actressList.map((actress) => {
			if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress;
			const filtered = actress.newVideoList.filter((carNum) => {
				const matched = carNums.includes(carNum);
				if (matched) {
					clog.log("移除关联女优新作品", actress.name, carNum);
					changed = true;
				}
				return !matched;
			});
			return {
				...actress,
				newVideoList: filtered
			};
		});
		if (changed) await sm.forage.setItem(sm.favorite_actresses_key, updated);
	}
	async function saveKeyword(sm, keyword, key, label) {
		let list;
		if (Array.isArray(keyword)) list = [...keyword];
		else {
			list = await sm.forage.getItem(key) || [];
			if (list.includes(keyword)) failWithToast(`${keyword} ${label}已存在`);
			list.push(keyword);
		}
		await sm.forage.setItem(key, list);
		return list;
	}
	async function saveTitleFilterKeyword(sm, keyword) {
		await saveKeyword(sm, keyword, sm.filter_keyword_title_key, "标题关键词");
		storageRevision.increment();
		if (Array.isArray(keyword)) return null;
		const actressList = await getFavoriteActressList(sm);
		let changed = false;
		const updated = actressList.map((actress) => {
			if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress;
			const filtered = actress.newVideoList.filter((carNum) => {
				const matched = carNum.startsWith(keyword);
				if (matched) {
					clog.log("移除关联女优新作品", actress.name, carNum);
					changed = true;
				}
				return !matched;
			});
			return {
				...actress,
				newVideoList: filtered
			};
		});
		if (changed) await sm.forage.setItem(sm.favorite_actresses_key, updated);
	}
	async function saveReviewFilterKeyword(sm, keyword) {
		return saveKeyword(sm, keyword, sm.filter_keyword_review_key, "评论关键词");
	}
	async function getTitleFilterKeyword(sm) {
		return await sm.forage.getItem(sm.filter_keyword_title_key) || [];
	}
	async function getReviewFilterKeywordList(sm) {
		return await sm.forage.getItem(sm.filter_keyword_review_key) || [];
	}
	var SCHEMA_VERSION_KEY = "__jhs_schema_version";
	async function runMigrations(sm) {
		const run = async () => {
			let version = await sm.forage.getItem("__jhs_schema_version") ?? 0;
			if (version >= 6) return;
			const steps = [
				{
					target: 1,
					fn: () => merge_table_name(sm)
				},
				{
					target: 2,
					fn: () => clean_no_url_blacklist(sm)
				},
				{
					target: 3,
					fn: () => async_merge_other(sm)
				},
				{
					target: 4,
					fn: () => merge_blacklist(sm)
				},
				{
					target: 5,
					fn: () => merge_favoriteActress(sm)
				},
				{
					target: 6,
					fn: () => merge_tow_car_list_table(sm)
				}
			];
			for (const step of steps) {
				if (version >= step.target) continue;
				try {
					await step.fn();
					await sm.forage.setItem(SCHEMA_VERSION_KEY, step.target);
					version = step.target;
				} catch (err) {
					clog.error(`数据迁移 ${version}→${step.target} 失败,下次启动重试`, err);
					break;
				}
			}
			sm.cacheCarList = null;
			sm.cacheFavoriteActressList = null;
			sm.cache_filter_actor_actress_car_list = null;
			sm.cacheSettingObj = null;
		};
		if (typeof navigator !== "undefined" && navigator.locks) await navigator.locks.request("jhs-storage-migration", { mode: "exclusive" }, run);
		else await run();
	}
	async function merge_table_name(sm) {
		let oldKey = "filter_actor_actress_info_list";
		let items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.blacklist_key, items);
		}
		await sm.forage.removeItem(oldKey);
		oldKey = "favorite_actresses_info_list";
		items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.favorite_actresses_key, items);
		}
		await sm.forage.removeItem(oldKey);
		oldKey = "car_list_filter_actor_actress";
		items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.blacklist_car_list_key, items);
		}
		await sm.forage.removeItem(oldKey);
		oldKey = "title_filter_keyword";
		items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.filter_keyword_title_key, items);
		}
		await sm.forage.removeItem(oldKey);
		oldKey = "review_filter_keyword";
		items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.filter_keyword_review_key, items);
		}
		await sm.forage.removeItem(oldKey);
		oldKey = "highlightedTags";
		items = await sm.forage.getItem(oldKey) || [];
		if (items && items.length > 0) {
			clog.debug("更正", oldKey);
			await sm.forage.setItem(sm.highlighted_tags_key, items);
		}
		await sm.forage.removeItem(oldKey);
	}
	async function clean_no_url_blacklist(sm) {
		const [blacklistCars, blacklist] = await Promise.all([sm.forage.getItem(sm.blacklist_car_list_key) || [], sm.forage.getItem(sm.blacklist_key) || []]);
		if (!blacklistCars.length && !blacklist.length) return;
		const nameSet = new Set(blacklist.map((item) => item.name));
		const filteredCars = blacklistCars.filter((item) => !item.actress || nameSet.has(item.actress));
		if (blacklistCars.length !== filteredCars.length) {
			clog.debug("清理 blacklistCarList 前", blacklistCars.length);
			clog.debug("清理 blacklistCarList 后", filteredCars.length);
			await sm.forage.setItem(sm.blacklist_car_list_key, filteredCars);
			sm.cache_filter_actor_actress_car_list = null;
		}
		const actressSet = new Set(filteredCars.map((item) => item.actress));
		let cleanedBlacklist = blacklist.filter((item) => actressSet.has(item.name));
		cleanedBlacklist = cleanedBlacklist.map((item) => {
			const cleaned = { ...item };
			delete cleaned.key;
			delete cleaned.recordTime;
			if (item.recordTime !== void 0) cleaned.createTime = item.recordTime;
			return cleaned;
		});
		if (blacklist.length !== cleanedBlacklist.length || blacklist.some((item) => "key" in item || "recordTime" in item)) {
			clog.debug("清理 Blacklist 前", blacklist.length);
			clog.debug("清理 Blacklist 后", cleanedBlacklist.length);
			await sm.forage.setItem(sm.blacklist_key, cleanedBlacklist);
		}
	}
	async function async_merge_other(sm) {
		const setting = await sm.getSetting();
		let changed = false;
		for (const key of [
			"enableCheckFilterActorActress",
			"checkIntervalTime_filterActorActress",
			"checkIntervalTime_ruleTime",
			"checkIntervalTime_newVideo",
			"checkIntervalTime_favoriteActress",
			"checkFilterTime",
			"checkFilterConcurrencyCount",
			"checkFilterSleep",
			"enableCheckBlacklist",
			"checkBlacklist_intervalTime",
			"checkBlacklist_ruleTime",
			"enableCheckFavoriteActress",
			"checkFavoriteActress_IntervalTime",
			"enableCheckNewVideo",
			"checkNewVideo_intervalTime",
			"checkNewVideo_ruleTime",
			"checkConcurrencyCount",
			"checkRequestSleep"
		]) if (Object.prototype.hasOwnProperty.call(setting, key)) {
			delete setting[key];
			changed = true;
		}
		if (setting.downPath115) {
			delete setting.downPath115;
			changed = true;
		}
		if (changed) {
			await sm.saveSetting(setting);
			clog.debug("配置数据已更正");
		}
	}
	async function merge_blacklist(sm) {
		const list = await sm.forage.getItem(sm.blacklist_key) || [];
		if (!list.length) return;
		let changed = false;
		const merged = list.map((item) => {
			let itemChanged = false;
			if (Object.prototype.hasOwnProperty.call(item, "isActor") && !item.role) {
				item.role = item.isActor ? ACTOR : ACTRESS;
				delete item.isActor;
				itemChanged = true;
			}
			if (!item.starId && item.url) try {
				const extracted = new URL(item.url).pathname.split("/").filter((seg) => seg.trim() !== "").pop();
				if (item.starId !== extracted) {
					item.starId = extracted;
					itemChanged = true;
				}
			} catch (err) {
				clog.error("提取url-starId发生错误", item.url, err);
			}
			if (!item.allName) {
				item.allName = item.name ? [item.name] : [];
				itemChanged = true;
			}
			if (!item.movieType) {
				item.movieType = CENSORED;
				itemChanged = true;
			}
			if (item.url && item.url.includes("sort_type")) try {
				const urlObj = new URL(item.url);
				urlObj.searchParams.delete("sort_type");
				item.url = urlObj.toString();
				itemChanged = true;
				clog.debug("去除黑名单地址sort_type参数");
			} catch (err) {
				clog.error("去除黑名单地址sort_type参数失败", item.url, err);
			}
			if (itemChanged) changed = true;
			return item;
		});
		if (changed) {
			clog.debug("更正 Blacklist 数据结构");
			await sm.forage.setItem(sm.blacklist_key, merged);
		}
		const carList = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
		changed = false;
		const mergedCars = carList.map((item) => {
			if (!item.starId) {
				const match = list.find((entry) => entry.name === item.actress);
				if (match) item.starId = match.starId;
				changed = true;
			}
			if (item.type) {
				delete item.type;
				changed = true;
			}
			return item;
		});
		if (changed) {
			clog.debug("更正 blacklistCarList 数据结构");
			await sm.forage.setItem(sm.blacklist_car_list_key, mergedCars);
			sm.cache_filter_actor_actress_car_list = null;
		}
	}
	async function merge_favoriteActress(sm) {
		const list = await sm.forage.getItem(sm.favorite_actresses_key) || [];
		if (!list.length) return;
		let changed = false;
		const merged = list.map((item) => {
			if (item.dbId) {
				item.starId = item.dbId;
				delete item.dbId;
				changed = true;
			}
			return item;
		});
		if (changed) {
			clog.debug("更正 favoriteActressesInfoList 数据结构");
			await sm.forage.setItem(sm.favorite_actresses_key, merged);
			sm.cacheFavoriteActressList = null;
		}
	}
	async function merge_tow_car_list_table(sm) {
		const blacklistCars = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
		const carList = await sm.forage.getItem(sm.car_list_key) || [];
		let changed = false;
		const mergedBlacklistCars = blacklistCars.map((item) => {
			if (item.actress !== void 0) {
				item.names = item.actress;
				delete item.actress;
				changed = true;
			}
			return item;
		});
		if (changed) {
			clog.debug("更正 blacklistCarList 数据结构 actress->names");
			await sm.forage.setItem(sm.blacklist_car_list_key, mergedBlacklistCars);
			sm.cache_filter_actor_actress_car_list = null;
		}
		changed = false;
		const mergedCars = carList.map((item) => {
			if (item.actress !== void 0) {
				item.names = item.actress;
				delete item.actress;
				changed = true;
			}
			return item;
		});
		if (changed) {
			clog.debug("更正 carList 数据结构 actress->names");
			await sm.forage.setItem(sm.car_list_key, mergedCars);
			sm.cacheCarList = null;
		}
	}
	var StorageManager = class StorageManager {
		static instance = null;
		car_list_key = "car_list";
		filter_keyword_title_key = "filter_keyword_title";
		filter_keyword_review_key = "filter_keyword_review";
		setting_key = "setting";
		blacklist_key = "blacklist";
		blacklist_car_list_key = "blacklist_car_list";
		favorite_actresses_key = "favorite_actresses";
		highlighted_tags_key = "highlighted_tags";
		forage;
		cacheCarList = null;
		cacheFavoriteActressList = null;
		cache_filter_actor_actress_car_list = null;
		cacheSettingObj = null;
		carListChangeCallback;
		constructor(forageInstance) {
			if (StorageManager.instance) throw new Error("StorageManager已被实例化过了!");
			StorageManager.instance = this;
			this.forage = forageInstance ?? localforage.createInstance({
				driver: localforage.INDEXEDDB,
				name: "JAV-JHS",
				version: 1,
				storeName: "appData"
			});
		}
		static __resetForTesting() {
			StorageManager.instance = null;
		}
		setCarListChangeCallback(cb) {
			this.carListChangeCallback = cb;
		}
		clearFilterActorActressCarListCache() {
			this.cache_filter_actor_actress_car_list = null;
		}
		clearSettingCache() {
			this.cacheSettingObj = null;
		}
		clearCarListCache() {
			this.cacheCarList = null;
		}
		clearFavoriteActressListCache() {
			this.cacheFavoriteActressList = null;
		}
		async getCarList() {
			return getCarList(this);
		}
		async getCar(carNum) {
			return getCar(this, carNum);
		}
		_saveSingleCar(input, list) {
			saveSingleCar(input, list);
		}
		async saveCar(input) {
			return saveCar(this, input);
		}
		async updateCarInfo(input) {
			return updateCarInfo(this, input);
		}
		async saveCarList(inputs) {
			return saveCarList(this, inputs);
		}
		async removeCar(carNum) {
			return removeCar(this, carNum);
		}
		async batchRemoveCars(carNums) {
			return batchRemoveCars(this, carNums);
		}
		async getBlacklist() {
			return getBlacklist(this);
		}
		async addBlacklistItem(item) {
			return addBlacklistItem(this, item);
		}
		async updateBlacklistItem(item) {
			return updateBlacklistItem(this, item);
		}
		async deleteBlacklistItem(starId) {
			return deleteBlacklistItem(this, starId);
		}
		async getBlacklistCarList() {
			return getBlacklistCarList(this);
		}
		async batchSaveBlacklistCarList(inputs) {
			return batchSaveBlacklistCarList(this, inputs);
		}
		async removeBlacklistCarList(starId) {
			return removeBlacklistCarList(this, starId);
		}
		async getFavoriteActressList() {
			return getFavoriteActressList(this);
		}
		async addFavoriteActressList(inputs) {
			return addFavoriteActressList(this, inputs);
		}
		async removeFavoriteActress(starId) {
			return removeFavoriteActress(this, starId);
		}
		async updateFavoriteActress(item) {
			return updateFavoriteActress(this, item);
		}
		async removeNewVideoList(carNums) {
			return removeNewVideoList(this, carNums);
		}
		async getHighlightedTags() {
			return await this.forage.getItem(this.highlighted_tags_key) || [];
		}
		async setHighlightedTags(tags) {
			return await this.forage.setItem(this.highlighted_tags_key, tags);
		}
		async saveTitleFilterKeyword(keyword) {
			return saveTitleFilterKeyword(this, keyword);
		}
		async saveReviewFilterKeyword(keyword) {
			return saveReviewFilterKeyword(this, keyword);
		}
		async getTitleFilterKeyword() {
			return getTitleFilterKeyword(this);
		}
		async getReviewFilterKeywordList() {
			return getReviewFilterKeywordList(this);
		}
		async getSetting(key = null, defaultValue) {
			if (!this.cacheSettingObj) this.cacheSettingObj = await this.forage.getItem(this.setting_key) || {};
			const setting = featureFlags.storageCacheDeepCopy ? utils.copyObj(this.cacheSettingObj) : this.cacheSettingObj;
			if (key === null) return setting;
			const val = setting[key];
			if (val) {
				if (val === "true" || val === "false") return val.toLowerCase() === "true";
				else if (typeof val != "string" || val.trim() === "" || isNaN(Number(val))) return val;
				else return Number(val);
			} else return defaultValue;
		}
		async saveSetting(setting) {
			if (setting) {
				await this.forage.setItem(this.setting_key, setting);
				storageRevision.increment();
				window.clean_cacheSettingObj();
			} else show.error("设置对象为空");
		}
		async saveSettingItem(key, value) {
			if (!key) {
				show.error("key 不能为空");
				return;
			}
			const setting = await this.getSetting();
			setting[key] = value;
			await this.saveSetting(setting);
			window.clean_cacheSettingObj();
		}
		async importData(data) {
			if (typeof data !== "object" || data === null || Array.isArray(data)) throw new Error("备份数据格式非法:必须是键值对对象");
			const tempInstance = localforage.createInstance({
				driver: localforage.INDEXEDDB,
				name: "JAV-JHS",
				storeName: "appData_import_staging"
			});
			try {
				const tasks = [];
				for (const key in data) tasks.push(tempInstance.setItem(key, data[key]));
				await Promise.all(tasks);
				await tempInstance.iterate((value, key) => {
					return this.forage.setItem(key, value);
				});
				storageRevision.increment();
				await tempInstance.clear();
				await tempInstance.dropInstance();
			} catch (err) {
				try {
					await tempInstance.clear();
					await tempInstance.dropInstance();
				} catch {}
				throw err;
			}
			this.cacheCarList = null;
			this.cacheFavoriteActressList = null;
			this.cache_filter_actor_actress_car_list = null;
			this.cacheSettingObj = null;
			await this.runMigrations();
		}
		async exportData() {
			const data = {};
			await this.forage.iterate((value, key) => {
				data[key] = value;
			});
			if (Object.keys(data).length === 0) throw new Error("没有可导出的数据");
			return data;
		}
		async runMigrations() {
			return runMigrations(this);
		}
		async clean_no_url_blacklist() {
			return clean_no_url_blacklist(this);
		}
		async async_merge_other() {
			return async_merge_other(this);
		}
	};
	var MIME_TYPES = {
		txt: "text/plain",
		html: "text/html",
		css: "text/css",
		csv: "text/csv",
		json: "application/json",
		xml: "application/xml",
		jpg: "image/jpeg",
		jpeg: "image/jpeg",
		png: "image/png",
		gif: "image/gif",
		webp: "image/webp",
		svg: "image/svg+xml",
		pdf: "application/pdf",
		doc: "application/msword",
		docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		xls: "application/vnd.ms-excel",
		xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
		ppt: "application/vnd.ms-powerpoint",
		pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
		zip: "application/zip",
		rar: "application/x-rar-compressed",
		"7z": "application/x-7z-compressed",
		mp3: "audio/mpeg",
		wav: "audio/wav",
		mp4: "video/mp4",
		webm: "video/webm",
		ogg: "audio/ogg"
	};
	function download(data, filename) {
		show.info("开始请求下载...");
		const mimeType = MIME_TYPES[filename.split(".").pop().toLowerCase()] || "application/octet-stream";
		let blob;
		if (data instanceof Blob) blob = data;
		else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) blob = new Blob([data], { type: mimeType });
		else if (typeof data === "string" && data.startsWith("data:")) {
			const raw = atob(data.split(",")[1]);
			const buf = new ArrayBuffer(raw.length);
			const bytes = new Uint8Array(buf);
			for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
			blob = new Blob([bytes], { type: mimeType });
		} else blob = new Blob([data], { type: mimeType });
		const url = URL.createObjectURL(blob);
		const anchor = document.createElement("a");
		anchor.href = url;
		anchor.download = filename;
		document.body.appendChild(anchor);
		anchor.click();
		setTimeout(() => {
			document.body.removeChild(anchor);
			URL.revokeObjectURL(url);
		}, 100);
	}
	function StyleBlock({ id, css }) {
		return (0, import_jsx_runtime.jsx)("style", {
			id,
			dangerouslySetInnerHTML: { __html: css }
		});
	}
	function styleBlockHtml(css, id) {
		return jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, {
			id,
			css
		}));
	}
	function insertStyle(css) {
		if (css) {
			if (css.indexOf("<style>") === -1) css = styleBlockHtml(css);
			$("head").append(css);
		}
	}
	function importResource(url) {
		let el;
		if (url.indexOf("css") >= 0) {
			const link = document.createElement("link");
			link.setAttribute("rel", "stylesheet");
			link.href = url;
			el = link;
		} else {
			const script = document.createElement("script");
			script.setAttribute("type", "text/javascript");
			script.src = url;
			el = script;
		}
		document.documentElement.appendChild(el);
	}
	function smoothScrollToTop(duration = 500) {
		return new Promise((resolve) => {
			const startTime = performance.now();
			const startY = window.pageYOffset;
			window.requestAnimationFrame(function step(now) {
				const elapsed = now - startTime;
				const progress = Math.min(elapsed / duration, 1);
				const eased = progress < .5 ? progress * 4 * progress * progress : 1 - Math.pow(progress * -2 + 2, 3) / 2;
				window.scrollTo(0, startY * (1 - eased));
				if (progress < 1) window.requestAnimationFrame(step);
				else resolve();
			});
		});
	}
	function loopDetector(container, checkFn, onSuccess, intervalMs = 20, timeoutMs = 1e4, callOnTimeout = true) {
		const token = String(Math.random());
		const startTime = new Date().getTime();
		const finish = (triggered) => {
			clearInterval(container[token]);
			if (triggered && onSuccess) onSuccess();
			delete container[token];
		};
		container[token] = setInterval(() => {
			const elapsed = new Date().getTime() - startTime;
			if (checkFn()) finish(true);
			else if (elapsed >= timeoutMs) finish(callOnTimeout);
		}, intervalMs);
	}
	function rightClick(container, targetSelector, handler) {
		let el = null;
		if (typeof container === "string") el = document.querySelector(container);
		else if (container instanceof HTMLElement) el = container;
		if (!el) {
			clog.warn("rightClick(), 容器无效或未提供,将使用 document.body 进行全局委托。");
			el = document.body;
		}
		if (typeof targetSelector === "string" && targetSelector.trim() !== "") el.addEventListener("contextmenu", (event) => {
			const target = event.target.closest(targetSelector);
			if (target) handler(event, target);
		});
		else clog.error("rightClick(), 必须提供有效的 targetSelector。");
	}
	function htmlTo$dom(html) {
		const parser = new DOMParser();
		return $(parser.parseFromString(html, "text/html"));
	}
	function isHidden(el) {
		const node = el.jquery ? el[0] : el;
		return !node || node.offsetWidth <= 0 && node.offsetHeight <= 0 || window.getComputedStyle(node).display === "none";
	}
	function getNowStr(dateSep = "-", timeSep = ":", timestamp = null) {
		const date = timestamp ? new Date(timestamp) : new Date();
		const year = date.getFullYear();
		const month = String(date.getMonth() + 1).padStart(2, "0");
		const day = String(date.getDate()).padStart(2, "0");
		const hours = String(date.getHours()).padStart(2, "0");
		const minutes = String(date.getMinutes()).padStart(2, "0");
		const seconds = String(date.getSeconds()).padStart(2, "0");
		return `${[
			year,
			month,
			day
		].join(dateSep)} ${[
			hours,
			minutes,
			seconds
		].join(timeSep)}`;
	}
	function formatDate(input, dateSep = "-", timeSep = ":") {
		let date;
		if (input instanceof Date) date = input;
		else {
			if (typeof input !== "string") throw new Error("Invalid date input: must be Date object or date string");
			date = new Date(input);
			if (isNaN(date.getTime())) throw new Error("Invalid date string");
		}
		const year = date.getFullYear();
		const month = String(date.getMonth() + 1).padStart(2, "0");
		const day = String(date.getDate()).padStart(2, "0");
		const hours = String(date.getHours()).padStart(2, "0");
		const minutes = String(date.getMinutes()).padStart(2, "0");
		const seconds = String(date.getSeconds()).padStart(2, "0");
		return `${[
			year,
			month,
			day
		].join(dateSep)} ${[
			hours,
			minutes,
			seconds
		].join(timeSep)}`;
	}
	function getHourDifference(start, end) {
		const startMs = start.getTime();
		const endMs = end.getTime();
		const hours = Math.abs(endMs - startMs) / 36e5;
		return Math.floor(hours);
	}
	function isUnnecessaryCheck(timeStr, checkIntervalTime) {
		if (!checkIntervalTime) throw new Error("未传入checkIntervalTime");
		const hours = parseInt(String(checkIntervalTime), 10);
		if (isNaN(hours)) return false;
		return getHourDifference(new Date(timeStr), new Date()) < hours;
	}
	function q(event, content, onConfirm, onCancel) {
		let left;
		let top;
		if (event) {
			left = event.clientX - 130;
			top = event.clientY - 120;
		} else {
			left = window.innerWidth / 2 - 120;
			top = window.innerHeight / 2 - 120;
		}
		const layerIdx = layer.confirm(content, {
			offset: [String(top), String(left)],
			title: "提示",
			btn: ["确定", "取消"],
			shade: 0,
			zIndex: 999999991
		}, function() {
			if (onConfirm) onConfirm();
			layer.close(layerIdx);
		}, function() {
			if (onCancel) onCancel();
		});
	}
	function getDefaultArea() {
		return ["85%", "90%"];
	}
	function getResponsiveArea(area) {
		const width = window.innerWidth;
		if (width >= 1200) return area || getDefaultArea();
		else if (width >= 768) return ["70%", "90%"];
		else return ["95%", "95%"];
	}
	function closePage() {
		storageManager.getSetting("needClosePage", "yes").then((setting) => {
			if (setting !== "yes") return;
			parent.document.documentElement.style.overflow = "auto";
			[
				".layui-layer-shade",
				".layui-layer-move",
				".layui-layer"
			].forEach((selector) => {
				const matches = parent.document.querySelectorAll(selector);
				if (matches.length > 0) {
					const el = matches.length > 1 ? matches[matches.length - 1] : matches[0];
					el.parentNode?.removeChild(el);
				}
			});
			window.close();
		}).catch((err) => clog.error("needClosePage 设置读取失败", err));
	}
	async function retry(fn, maxRetries = 3) {
		let attempt = 0;
		while (attempt < maxRetries) try {
			const result = await fn();
			if (attempt > 0) clog.debug(`[重试] 请求成功,共发起 ${attempt + 1} 次。`);
			return result;
		} catch (err) {
			const msg = String(err);
			if (msg.includes("Just a moment") || msg.includes("重定向") || msg.toLowerCase().includes("404 not found")) throw err;
			attempt++;
			if (attempt === maxRetries) {
				clog.debug(`[重试] 达到最大重试次数 (${maxRetries}),最终失败:`, err);
				throw err;
			}
			clog.debug(`[重试] 请求失败,准备第 ${attempt + 1} 次重试, 错误信息: ${msg}`);
		}
	}
	function copyToClipboard$1(label, text) {
		navigator.clipboard.writeText(text).then(() => show.info(`${label}已复制到剪切板, ${text}`)).catch((err) => clog.error("复制失败: ", err));
	}
	function addCookie(cookieStr, options = {}) {
		const { maxAge = 604800, path = "/", domain = "", secure = false, sameSite = "Lax" } = options;
		cookieStr.split(";").forEach((pair) => {
			const trimmed = pair.trim();
			if (trimmed) {
				const parts = trimmed.split("=");
				if (parts.length >= 2 && parts[0].trim()) {
					const segments = [`${parts[0].trim()}=${parts.slice(1).join("=")}`];
					if (maxAge > 0) segments.push(`max-age=${maxAge}`);
					segments.push(`path=${path}`);
					if (domain) segments.push(`domain=${domain}`);
					if (secure) segments.push("Secure");
					if (sameSite) segments.push(`SameSite=${sameSite}`);
					document.cookie = segments.join("; ");
				}
			}
		});
	}
	function genericSort(data, sortConfigs, nullsFirst = true) {
		if (!Array.isArray(data) || data.length === 0) return [];
		if (!Array.isArray(sortConfigs) || sortConfigs.length === 0) return [...data];
		const items = [...data];
		const coerceDate = (value) => {
			if (value instanceof Date) return value;
			if (typeof value === "string") {
				const parsed = new Date(value);
				if (!isNaN(parsed.getTime())) return parsed;
			}
			return value;
		};
		return items.sort((itemA, itemB) => {
			for (const config of sortConfigs) {
				const { key, order = "asc" } = config;
				let valA = itemA;
				let valB = itemB;
				if (key != null) {
					if (typeof key === "function") {
						valA = key(itemA);
						valB = key(itemB);
					} else {
						valA = itemA && typeof itemA === "object" ? itemA[key] : void 0;
						valB = itemB && typeof itemB === "object" ? itemB[key] : void 0;
					}
				}
				const coercedA = coerceDate(valA);
				const coercedB = coerceDate(valB);
				let cmp;
				const aIsNull = valA == null;
				const bIsNull = valB == null;
				if (aIsNull && bIsNull) return 0;
				if (aIsNull) {
					if (nullsFirst) return 1;
					else return -1;
				}
				if (bIsNull) {
					if (nullsFirst) return 1;
					else return -1;
				}
				cmp = coercedA instanceof Date && coercedB instanceof Date ? coercedA.getTime() - coercedB.getTime() : typeof valA === "number" && typeof valB === "number" ? valA - valB : typeof valA === "string" && typeof valB === "string" ? valA.localeCompare(valB) : String(valA).localeCompare(String(valB));
				if (order === "desc") cmp *= -1;
				if (cmp !== 0) return cmp;
			}
			return 0;
		});
	}
	function isUrl(str) {
		try {
			new URL(str);
			return true;
		} catch {
			return false;
		}
	}
	function setHrefParam(key, value) {
		const url = new URL(window.location.href);
		url.searchParams.set(key, value);
		window.history.pushState({}, "", url.toString());
	}
	function getUrlParam(url, param) {
		const query = url.split("?")[1];
		if (!query) return null;
		const regex = new RegExp(`(?:^|&)${param}=([^&]*)`);
		const match = query.match(regex);
		let value = "";
		if (match && match[1]) value = decodeURIComponent(match[1].replace(/\+/g, " "));
		if (value) {
			if (value === "true" || value === "false") return value.toLowerCase() === "true";
			else if (typeof value !== "string" || value.trim() === "" || isNaN(Number(value))) return value;
			else return Number(value);
		} else return value;
	}
	function isMobile() {
		const ua = navigator.userAgent.toLowerCase();
		return [
			"iphone",
			"ipod",
			"ipad",
			"android",
			"blackberry",
			"windows phone",
			"nokia",
			"webos",
			"opera mini",
			"mobile",
			"mobi",
			"tablet"
		].some((keyword) => ua.includes(keyword));
	}
	function simpleId() {
		return crypto.randomUUID().replace("-", "");
	}
	function sleep$1(ms = 1e3) {
		return new Promise((resolve) => setTimeout(resolve, ms));
	}
	function copyObj(data) {
		return JSON.parse(JSON.stringify(data));
	}
	function deepFreeze(obj) {
		if (obj === null || typeof obj !== "object" || Object.isFrozen(obj)) return obj;
		const propNames = Object.getOwnPropertyNames(obj);
		for (const name of propNames) {
			const value = obj[name];
			if (value && typeof value === "object") deepFreeze(value);
		}
		return Object.freeze(obj);
	}
	function reBuildSignature$1() {
		const tsKey = "jhs_review_ts";
		const signKey = "jhs_review_sign";
		const nowSec = Math.floor(Date.now() / 1e3);
		if (nowSec - (Number(localStorage.getItem(tsKey)) || 0) <= 20) return localStorage.getItem(signKey);
		const sign = `${nowSec}.lpw6vgqzsp.${md5(`${nowSec}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`;
		localStorage.setItem(tsKey, String(nowSec));
		localStorage.setItem(signKey, sign);
		return sign;
	}
	function time(timers, label = "default", unit = "s", precision = 2) {
		if (timers.has(label)) {
			const entry = timers.get(label);
			const elapsed = performance.now() - entry.startTime;
			let formatted;
			let unitLabel;
			if (entry.unit === "s") {
				formatted = (elapsed / 1e3).toFixed(entry.precision);
				unitLabel = "秒";
			} else {
				formatted = elapsed.toFixed(entry.precision);
				unitLabel = "毫秒";
			}
			timers.delete(label);
			return `${label}: ${formatted}${unitLabel}`;
		}
		timers.set(label, {
			startTime: performance.now(),
			unit,
			precision
		});
	}
	var escLayerGate = (() => {
		let w;
		try {
			w = window.top;
			w && w.location;
		} catch {
			w = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
		}
		if (!w.__jhsEscGate) w.__jhsEscGate = {
			locked: false,
			tryEnter() {
				if (this.locked) return false;
				this.locked = true;
				return true;
			},
			release() {
				this.locked = false;
			}
		};
		return w.__jhsEscGate;
	})();
	function getTopLayerEl() {
		const nodes = document.querySelectorAll(".layui-layer[times]");
		let topEl = null;
		let topZ = -Infinity;
		for (let i = 0; i < nodes.length; i++) {
			const el = nodes[i];
			const type = el.getAttribute("type") || "";
			if (type === "loading" || type === "tips") continue;
			if (el.getAttribute("data-jhs-esc-closing") === "1") continue;
			const cs = window.getComputedStyle(el);
			if (cs.display === "none" || cs.visibility === "hidden") continue;
			const z = parseInt(cs.zIndex, 10);
			const zVal = Number.isFinite(z) ? z : i;
			const times = Number(el.getAttribute("times"));
			if (!Number.isFinite(times)) continue;
			if (zVal >= topZ) {
				topZ = zVal;
				topEl = el;
			}
		}
		return topEl;
	}
	function syncLayerIndexStack(layerIndexStack) {
		return layerIndexStack.filter((idx) => document.getElementById(`layui-layer${idx}`) != null);
	}
	function _handleGlobalEscKey(event) {
		if (event.key !== "Escape" && event.keyCode !== 27) return;
		if ((event.originalEvent ?? event).repeat) {
			event.preventDefault();
			event.stopImmediatePropagation();
			return;
		}
		if (!escLayerGate.tryEnter()) {
			event.preventDefault();
			event.stopImmediatePropagation();
			return;
		}
		const topEl = getTopLayerEl();
		if (!topEl) {
			escLayerGate.release();
			return;
		}
		const layerIdx = Number(topEl.getAttribute("times"));
		if (!Number.isFinite(layerIdx)) {
			escLayerGate.release();
			return;
		}
		let hasViewer = $(topEl).find(".viewer-container").length > 0;
		let hasInnerLayer = false;
		if (!hasViewer) {
			const iframeEl = topEl.querySelector(`#layui-layer-iframe${layerIdx}`);
			if (iframeEl?.contentDocument) try {
				if (iframeEl.contentDocument.querySelector(".viewer-container")) hasViewer = true;
				if (!hasViewer) {
					const iframeLayers = iframeEl.contentDocument.querySelectorAll(".layui-layer[times]");
					for (const il of Array.from(iframeLayers)) {
						const ilType = il.getAttribute("type") || "";
						if (ilType === "loading" || ilType === "tips") continue;
						if (il.getAttribute("data-jhs-esc-closing") === "1") continue;
						const ilCs = window.getComputedStyle(il);
						if (ilCs.display === "none" || ilCs.visibility === "hidden") continue;
						hasInnerLayer = true;
						break;
					}
				}
			} catch {
				clog.warn("无法检查跨域 iframe 内的 .viewer-container");
			}
		}
		if (hasViewer) {
			escLayerGate.release();
			return;
		}
		if (hasInnerLayer) {
			const iframeEl = topEl.querySelector(`#layui-layer-iframe${layerIdx}`);
			if (iframeEl?.contentDocument) try {
				escLayerGate.release();
				const innerEvt = new KeyboardEvent("keydown", {
					key: "Escape",
					keyCode: 27,
					which: 27,
					bubbles: true,
					cancelable: true,
					composed: true
				});
				iframeEl.contentDocument.dispatchEvent(innerEvt);
				window.setTimeout(() => escLayerGate.release(), 100);
				return;
			} catch {
				escLayerGate.release();
				return;
			}
			escLayerGate.release();
			return;
		}
		event.preventDefault();
		event.stopImmediatePropagation();
		topEl.setAttribute("data-jhs-esc-closing", "1");
		topEl.style.display = "none";
		const shade = document.getElementById(`layui-layer-shade${layerIdx}`);
		if (shade) {
			shade.setAttribute("data-jhs-esc-closing", "1");
			shade.style.display = "none";
		}
		layer.close(layerIdx);
		window.setTimeout(() => escLayerGate.release(), 800);
	}
	function _handleGlobalEscKeyUp(event) {
		if (event.key === "Escape" || event.keyCode === 27) escLayerGate.release();
	}
	function setupEscClose(util, layerIdx) {
		if (!util._boundHandler) {
			util._boundHandler = util._handleGlobalEscKey.bind(util);
			util._boundEscKeyupHandler = util._handleGlobalEscKeyUp.bind(util);
			$(document).off("keydown.globalLayerEsc keyup.globalLayerEsc");
			document.removeEventListener("keydown", util._boundHandler, true);
			document.removeEventListener("keyup", util._boundEscKeyupHandler, true);
			document.addEventListener("keydown", util._boundHandler, true);
			document.addEventListener("keyup", util._boundEscKeyupHandler, true);
		}
		const numIdx = Number(layerIdx);
		if (Number.isFinite(numIdx) && util.layerIndexStack.indexOf(numIdx) === -1) util.layerIndexStack.push(numIdx);
		const iframeEl = document.getElementById(`layui-layer-iframe${layerIdx}`);
		if (iframeEl && iframeEl.getAttribute("data-esc-bound") !== "yes") {
			const bindIframe = () => {
				try {
					const doc = iframeEl.contentDocument;
					if (!doc || !util._boundHandler || !util._boundEscKeyupHandler) return;
					doc.removeEventListener("keydown", util._boundHandler, true);
					doc.removeEventListener("keyup", util._boundEscKeyupHandler, true);
					doc.addEventListener("keydown", util._boundHandler, true);
					doc.addEventListener("keyup", util._boundEscKeyupHandler, true);
					iframeEl.setAttribute("data-esc-bound", "yes");
				} catch (err) {
					clog.error("iframe监听失败 (跨域或未加载完毕):", err);
				}
			};
			if (iframeEl.contentDocument?.readyState === "complete") bindIframe();
			else iframeEl.addEventListener("load", bindIframe, { once: true });
		}
	}
	var CommonUtil = class CommonUtil {
		static instance;
		intervalContainer = {};
		mimeTypes = MIME_TYPES;
		timers = new Map();
		layerIndexStack = [];
		_boundHandler = null;
		_boundEscKeyupHandler = null;
		constructor() {
			CommonUtil.instance ||= this;
			return CommonUtil.instance;
		}
		insertStyle = (css) => insertStyle(css);
		importResource(url) {
			importResource(url);
		}
		smoothScrollToTop(duration = 500) {
			return smoothScrollToTop(duration);
		}
		loopDetector(checkFn, onSuccess, intervalMs = 20, timeoutMs = 1e4, callOnTimeout = true) {
			loopDetector(this.intervalContainer, checkFn, onSuccess, intervalMs, timeoutMs, callOnTimeout);
		}
		rightClick(container, targetSelector, handler) {
			rightClick(container, targetSelector, handler);
		}
		htmlTo$dom(html) {
			return htmlTo$dom(html);
		}
		isHidden(el) {
			return isHidden(el);
		}
		q(event, content, onConfirm, onCancel) {
			q(event, content, onConfirm, onCancel);
		}
		getDefaultArea() {
			return getDefaultArea();
		}
		getResponsiveArea(area) {
			return getResponsiveArea(area);
		}
		closePage() {
			closePage();
		}
		openPage(url, title, shadeClose, event) {
			shadeClose = shadeClose ?? true;
			if (event && (event.ctrlKey || event.metaKey)) {
				GM_openInTab(url.includes("http") ? url : window.location.origin + url, { insert: false });
				return;
			}
			let content = url;
			if (!url.includes("/actors/") && !url.includes("/star/")) content = url.includes("?") ? `${url}&hideNav=1` : `${url}?hideNav=1`;
			layer.open({
				type: 2,
				title,
				content,
				scrollbar: false,
				shadeClose,
				area: getResponsiveArea(["85%", "90%"]),
				isOutAnim: false,
				anim: -1,
				success: (_layerEl, layerIdx) => {
					this.setupEscClose(layerIdx);
				}
			});
		}
		getTopLayerEl() {
			return getTopLayerEl();
		}
		syncLayerIndexStack() {
			this.layerIndexStack = syncLayerIndexStack(this.layerIndexStack);
		}
		_handleGlobalEscKey(event) {
			_handleGlobalEscKey(event);
		}
		_handleGlobalEscKeyUp(event) {
			_handleGlobalEscKeyUp(event);
		}
		setupEscClose(layerIdx) {
			setupEscClose(this, layerIdx);
		}
		getNowStr(dateSep = "-", timeSep = ":", timestamp = null) {
			return getNowStr(dateSep, timeSep, timestamp);
		}
		formatDate(input, dateSep = "-", timeSep = ":") {
			return formatDate(input, dateSep, timeSep);
		}
		getHourDifference(start, end) {
			return getHourDifference(start, end);
		}
		isUnnecessaryCheck(timeStr, checkIntervalTime) {
			return isUnnecessaryCheck(timeStr, checkIntervalTime);
		}
		download(data, filename) {
			download(data, filename);
		}
		async retry(fn, maxRetries = 3) {
			return retry(fn, maxRetries);
		}
		copyToClipboard(label, text) {
			copyToClipboard$1(label, text);
		}
		addCookie(cookieStr, options = {}) {
			addCookie(cookieStr, options);
		}
		genericSort(data, sortConfigs, nullsFirst = true) {
			return genericSort(data, sortConfigs, nullsFirst);
		}
		isUrl(str) {
			return isUrl(str);
		}
		setHrefParam(key, value) {
			setHrefParam(key, value);
		}
		getUrlParam(url, param) {
			return getUrlParam(url, param);
		}
		isMobile() {
			return isMobile();
		}
		simpleId() {
			return simpleId();
		}
		sleep(ms = 1e3) {
			return sleep$1(ms);
		}
		copyObj(data) {
			return copyObj(data);
		}
		deepFreeze(obj) {
			return deepFreeze(obj);
		}
		reBuildSignature() {
			return reBuildSignature$1();
		}
		time(label = "default", unit = "s", precision = 2) {
			return time(this.timers, label, unit, precision);
		}
	};
	var GmHttp = class {
		async get(url, queryParams = {}, headers = {}, followRedirect) {
			return this.gmRequest("GET", url, null, queryParams, headers, followRedirect);
		}
		post(url, body = {}, headers = {}) {
			const requestHeaders = {
				"Content-Type": "application/json",
				...headers
			};
			const bodyString = JSON.stringify(body);
			return this.gmRequest("POST", url, bodyString, null, requestHeaders);
		}
		postForm(url, formData = {}, headers = {}) {
			const requestHeaders = headers || {};
			requestHeaders["Content-Type"] ||= "application/x-www-form-urlencoded";
			let bodyString = "";
			if (formData && Object.keys(formData).length > 0) bodyString = Object.entries(formData).map(([key, value]) => `${key}=${String(value)}`).join("&");
			return this.gmRequest("POST", url, bodyString, null, requestHeaders);
		}
		postFileFormData(url, formFields = {}, headers = {}) {
			const requestHeaders = headers || {};
			const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`;
			requestHeaders["Content-Type"] = `multipart/form-data; boundary=${boundary}`;
			let body = "";
			if (formFields && Object.keys(formFields).length > 0) body = Object.entries(formFields).map(([key, value]) => `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${String(value)}\r\n`).join("");
			body += `--${boundary}--`;
			return this.gmRequest("POST", url, body, null, requestHeaders);
		}
		async downloadFileInChunks(url, headers = {}, filename, transform) {
			if (!filename) throw new Error("请提供文件名 (filename) 用于保存。");
			const timeout = await storageManager.getSetting("httpTimeout", 5e3);
			const retryCount = await storageManager.getSetting("httpRetryCount", 3);
			let fileSize;
			let mimeType;
			clog.log(`[${filename}] 正在获取文件大小...`);
			try {
				const sizeResponse = await utils.retry(() => new Promise((resolve, reject) => {
					GM_xmlhttpRequest({
						method: "GET",
						url,
						headers: {
							...headers,
							Range: "bytes=0-0"
						},
						timeout,
						onload: resolve,
						onerror: () => reject(new Error("网络错误:无法获取文件大小")),
						ontimeout: () => reject(new Error("超时:获取文件大小"))
					});
				}), retryCount);
				if (sizeResponse.status !== 206 && sizeResponse.status !== 200) throw new Error(`请求文件大小失败,状态码: ${sizeResponse.status}`);
				{
					const contentRangeMatch = sizeResponse.responseHeaders.match(/content-range:\s*bytes\s*\d+-\d+\/(\d+)/i);
					const contentTypeMatch = sizeResponse.responseHeaders.match(/content-type:\s*([^\s;]+)/i);
					if (contentRangeMatch && contentRangeMatch[1]) fileSize = parseInt(contentRangeMatch[1], 10);
					else {
						const contentLengthMatch = sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i);
						if (!contentLengthMatch || sizeResponse.status !== 200) throw new Error("无法从响应头中获取文件总大小,服务器可能不支持 Range 请求。");
						fileSize = parseInt(contentLengthMatch[1], 10);
						clog.warn(`[${filename}] 服务器返回 200 状态码,可能不支持 Range 请求。将尝试完整下载。`);
					}
					if (contentTypeMatch && contentTypeMatch[1]) mimeType = contentTypeMatch[1];
					clog.log(`[${filename}] 文件总大小:${(fileSize / 1024 / 1024).toFixed(2)} MB, MIME 类型: ${mimeType || "未知"}`);
				}
			} catch (err) {
				clog.error(`[${filename}] 获取文件大小失败:`, err instanceof Error ? err.message : String(err));
				throw err;
			}
			if (!fileSize || fileSize <= 0) throw new Error("获取到的文件大小无效或服务器拒绝提供大小信息。");
			const chunkSize = 1048576;
			const chunkCount = Math.ceil(fileSize / chunkSize);
			const promises = [];
			const chunks = new Array(chunkCount);
			clog.log(`[${filename}] 文件将被分为 ${chunkCount} 块进行下载 (每块约 ${1 .toFixed(2)} MB)`);
			for (let index = 0; index < chunkCount; index++) {
				const start = index * chunkSize;
				const rangeHeader = `bytes=${start}-${Math.min(start + chunkSize - 1, fileSize - 1)}`;
				await utils.retry(() => new Promise((resolve, reject) => {
					const requestHeaders = {
						...headers,
						Range: rangeHeader,
						Accept: "application/octet-stream"
					};
					GM_xmlhttpRequest({
						method: "GET",
						url,
						headers: requestHeaders,
						timeout,
						responseType: "arraybuffer",
						onload: (response) => {
							if (response.status === 206 || response.status === 200) {
								const chunkData = response.response;
								if (chunkData instanceof ArrayBuffer) {
									chunks[index] = chunkData;
									clog.log(`[${filename}] 成功下载第 ${index + 1}/${chunkCount} 块 (${rangeHeader})`);
									resolve();
								} else reject(new Error(`第 ${index + 1} 块响应不是 ArrayBuffer。`));
							} else reject(new Error(`第 ${index + 1} 块请求失败,状态码: ${response.status}`));
						},
						onerror: (errorEvent) => reject(new Error(`第 ${index + 1} 块网络错误: ${errorEvent.error}`)),
						ontimeout: () => reject(new Error(`第 ${index + 1} 块超时。`))
					});
				}), retryCount);
				promises.push(Promise.resolve());
			}
			try {
				await Promise.all(promises);
				clog.log(`[${filename}] 所有分块下载完成,开始合并...`);
			} catch (err) {
				clog.error(`[${filename}] 分块下载过程中发生错误:`, err instanceof Error ? err.message : String(err));
				throw err;
			}
			const blob = new Blob(chunks);
			if (blob.size !== fileSize) clog.warn(`[${filename}] 警告:合并后的 Blob 大小 (${blob.size}) 与预期文件大小 (${fileSize}) 不匹配!`);
			const text = await blob.text();
			const content = transform ? transform(text) : text;
			utils.download(content, filename);
			clog.log(`[${filename}] 文件合并完成,已触发浏览器下载。`);
		}
		async gmRequest(method, url, data = {}, queryParams = {}, headers = {}, followRedirect = false) {
			let requestUrl = url;
			if (queryParams && Object.keys(queryParams).length) {
				const queryString = new URLSearchParams(queryParams).toString();
				requestUrl += (requestUrl.includes("?") ? "&" : "?") + queryString;
			}
			const timeout = await storageManager.getSetting("httpTimeout", 5e3);
			const retryCount = await storageManager.getSetting("httpRetryCount", 3);
			const requestData = data || void 0;
			return await utils.retry(() => new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method,
					url: requestUrl,
					headers,
					timeout,
					data: requestData,
					onload: (response) => {
						try {
							if (followRedirect && response.finalUrl && response.finalUrl !== requestUrl) {
								reject("请求被重定向了,URL是:" + response.finalUrl);
								return;
							}
							if (response.status >= 200 && response.status < 300) {
								if (response.responseText) try {
									resolve(JSON.parse(response.responseText));
								} catch {
									resolve(response.responseText);
								}
								else resolve(response.responseText || response);
							} else {
								clog.error("请求失败,状态码:", response.status, requestUrl);
								if (response.responseText) try {
									reject(JSON.parse(response.responseText));
								} catch {
									reject(new Error(response.responseText || `请求发生错误 ${response.status}`));
								}
								else reject(new Error(`请求发生错误 ${response.status}`));
							}
						} catch (err) {
							reject(err);
						}
					},
					onerror: (errorEvent) => {
						clog.error("网络错误:", requestUrl);
						reject(new Error(errorEvent.error || "网络错误"));
					},
					ontimeout: () => {
						reject(new Error("请求超时: " + requestUrl));
					}
				});
			}), retryCount);
		}
	};
	var layer_fix_default = "/**\n * 修复 layui-layer 关闭按钮不可见 + IE hack 被 lightningcss 误删的属性。\n *\n * 背景:layer.css 含 `*right` / `_display` 等 IE 前缀 hack,vite lightningcss\n * (errorRecovery)解析时连带丢掉相邻标准属性,导致:\n * - `.layui-layer-setwin` 丢失 `position:absolute; right:15px` → 关闭钮不在右上角\n * - `.layui-layer-close2` 几乎只剩无效 `_display:none` → 无标题层关闭钮失效\n *\n * 本文件用无 hack 的标准 CSS 覆盖还原。\n */\n\n.layui-layer-setwin {\n    position: absolute !important;\n    right: 15px !important;\n    top: 15px !important;\n    font-size: 0;\n    line-height: initial;\n    z-index: 10;\n}\n\n.layui-layer-setwin a {\n    position: relative;\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    margin-left: 10px;\n    font-size: 12px;\n    vertical-align: top;\n}\n\n.layui-layer-setwin .layui-layer-close1 {\n    cursor: pointer;\n    background-position: 0 -40px;\n}\n\n.layui-layer-setwin .layui-layer-close1:hover {\n    opacity: 0.7;\n}\n\n.layui-layer-setwin .layui-layer-close2 {\n    position: absolute !important;\n    right: -28px !important;\n    top: -28px !important;\n    width: 30px !important;\n    height: 30px !important;\n    margin-left: 0 !important;\n    background-position: -149px -31px !important;\n    display: inline-block !important;\n    cursor: pointer;\n}\n\n.layui-layer-setwin .layui-layer-close2:hover {\n    background-position: -180px -31px !important;\n}\n";
	function resetOverflowByShadeCount(delay = 10) {
		setTimeout(() => {
			const shadeCount = document.querySelectorAll(".layui-layer-shade").length;
			document.documentElement.style.overflow = shadeCount > 0 ? "hidden" : "";
		}, delay);
	}
	function removeFromEscStack(index) {
		try {
			const stack = window.utils?.layerIndexStack;
			if (!stack || index == null) return;
			const num = Number(index);
			for (let i = stack.length - 1; i >= 0; i--) if (stack[i] === num) stack.splice(i, 1);
		} catch {}
	}
	function setupLayerWrapper() {
		injectCss(layer_fix_default);
		const originalClose = layer.close;
		layer.close = function(index) {
			removeFromEscStack(index);
			const result = originalClose.call(this, index);
			resetOverflowByShadeCount();
			return result;
		};
		const originalCloseAll = layer.closeAll;
		if (typeof originalCloseAll === "function") layer.closeAll = function(type) {
			try {
				const stack = window.utils?.layerIndexStack;
				if (stack) stack.length = 0;
			} catch {}
			const result = originalCloseAll.call(this, type);
			resetOverflowByShadeCount();
			return result;
		};
		const originalOpen = layer.open;
		layer.open = function(options) {
			const opts = options || {};
			if (opts.closeBtn === void 0 || opts.closeBtn === null) opts.closeBtn = 1;
			if (opts.shadeClose === void 0 && (opts.type === 1 || opts.type === 2)) opts.shadeClose = true;
			const originalSuccess = opts.success;
			opts.success = function(el, index) {
				if (typeof originalSuccess == "function") originalSuccess.call(this, el, index);
				utils.setupEscClose(index);
			};
			return originalOpen.call(this, opts);
		};
	}
	var tooltip_default = ".js-tooltip {\n    /* 通用样式 */\n    position: fixed;\n    padding: 8px 12px;\n    border-radius: 6px;\n    white-space: normal;\n    max-width: 600px;\n    pointer-events: none;\n    font-size: 14px;\n    line-height: 1.5;\n    z-index: var(--jhs-z-top);\n    background: #f0fdf4;\n    color: #166534;\n    border: none;\n    box-shadow: 0 6px 20px rgb(0, 0, 0, 0.3);\n    display: none;\n}\n\n.js-tooltip.is-active {\n    display: block !important;\n}\n";
	var TOOLTIP_SELECTOR = "[data-tip-top], [data-tip-bottom], [data-tip-left], [data-tip-right], [data-tip]";
	function createTooltipElement(content) {
		const wrapper = document.createElement("div");
		wrapper.classList.add("js-tooltip");
		const inner = document.createElement("div");
		inner.textContent = content;
		wrapper.appendChild(inner);
		document.body.appendChild(wrapper);
		return wrapper;
	}
	function positionTooltip(element, content, direction) {
		const tooltip = createTooltipElement(content);
		tooltip.style.display = "block";
		const elementRect = element.getBoundingClientRect();
		const tooltipRect = tooltip.getBoundingClientRect();
		tooltip.style.display = "none";
		const viewportWidth = window.innerWidth;
		const viewportHeight = window.innerHeight;
		let left;
		let top;
		let actualDirection = direction;
		const fitsVertically = (y) => y >= 8 && y + tooltipRect.height <= viewportHeight - 8;
		const fitsHorizontally = (x) => x >= 8 && x + tooltipRect.width <= viewportWidth - 8;
		const centerX = elementRect.left + elementRect.width / 2 - tooltipRect.width / 2;
		const centerY = elementRect.top + elementRect.height / 2 - tooltipRect.height / 2;
		switch (direction) {
			case "top":
				top = elementRect.top - tooltipRect.height - 0;
				if (top < 8 && fitsVertically(elementRect.bottom + 0)) {
					top = elementRect.bottom + 0;
					actualDirection = "bottom";
				}
				break;
			case "bottom":
				top = elementRect.bottom + 0;
				if (top + tooltipRect.height > viewportHeight - 8 && fitsVertically(elementRect.top - tooltipRect.height - 0)) {
					top = elementRect.top - tooltipRect.height - 0;
					actualDirection = "top";
				}
				break;
			case "left":
				left = elementRect.left - tooltipRect.width - 0;
				if (left < 8 && fitsHorizontally(elementRect.right + 0)) {
					left = elementRect.right + 0;
					actualDirection = "right";
				}
				break;
			case "right":
				left = elementRect.right + 0;
				if (left + tooltipRect.width > viewportWidth - 8 && fitsHorizontally(elementRect.left - tooltipRect.width - 0)) {
					left = elementRect.left - tooltipRect.width - 0;
					actualDirection = "left";
				}
		}
		const isHorizontal = actualDirection === "left" || actualDirection === "right";
		if (actualDirection === "top" || actualDirection === "bottom") {
			left = centerX;
			if (left < 8) left = 8;
			else if (left + tooltipRect.width > viewportWidth - 8) left = viewportWidth - tooltipRect.width - 8;
		} else if (isHorizontal) {
			top = centerY;
			if (top < 8) top = 8;
			else if (top + tooltipRect.height > viewportHeight - 8) top = viewportHeight - tooltipRect.height - 8;
		}
		tooltip.style.left = `${left}px`;
		tooltip.style.top = `${top}px`;
		tooltip.classList.add("is-active");
		element.tooltipElement = tooltip;
	}
	function setupTooltip() {
		injectCss(tooltip_default);
		document.addEventListener("mouseover", (event) => {
			const target = event.target.closest(TOOLTIP_SELECTOR);
			if (target && !target.tooltipElement) {
				let content = "";
				let direction = "top";
				if (target.hasAttribute("data-tip-bottom")) {
					content = target.getAttribute("data-tip-bottom") ?? "";
					direction = "bottom";
				} else if (target.hasAttribute("data-tip-left")) {
					content = target.getAttribute("data-tip-left") ?? "";
					direction = "left";
				} else if (target.hasAttribute("data-tip-right")) {
					content = target.getAttribute("data-tip-right") ?? "";
					direction = "right";
				} else if (target.hasAttribute("data-tip-top")) {
					content = target.getAttribute("data-tip-top") ?? "";
					direction = "top";
				} else if (target.hasAttribute("data-tip")) {
					content = target.getAttribute("data-tip") ?? "";
					direction = "top";
				}
				if (!content) return;
				target.hoverTimeout = window.setTimeout(() => {
					if (target.matches(":hover") && !target.tooltipElement) positionTooltip(target, content, direction);
				}, 50);
			}
		});
		document.addEventListener("mouseout", (event) => {
			const target = event.target.closest(TOOLTIP_SELECTOR);
			if (!target) return;
			if (target.hoverTimeout) {
				clearTimeout(target.hoverTimeout);
				target.hoverTimeout = null;
			}
			if (!target.contains(event.relatedTarget)) {
				if (target.tooltipElement) {
					target.tooltipElement.remove();
					target.tooltipElement = null;
				}
			}
		});
	}
	var WEBDAV_SALT = "x7k9p3";
	function encryptCredential(value) {
		return (WEBDAV_SALT + value + WEBDAV_SALT).split("").map((ch) => {
			const code = ch.codePointAt(0);
			return String.fromCodePoint(code + 5);
		}).join("");
	}
	function decryptCredential(value) {
		return value.split("").map((ch) => {
			const code = ch.codePointAt(0);
			return String.fromCodePoint(code - 5);
		}).join("").slice(6, -6);
	}
	var BADGE_BASE = {
		display: "inline-block",
		padding: "1px 6px",
		borderRadius: "3px",
		fontSize: "11px",
		lineHeight: "16px",
		margin: "1px",
		whiteSpace: "nowrap"
	};
	var BADGE_COLORS = {
		ok: {
			background: "#dcfce7",
			color: "#166534"
		},
		fail: {
			background: "#fee2e2",
			color: "#991b1b"
		},
		skip: {
			background: "#f1f5f9",
			color: "#64748b"
		},
		page: {
			background: "#e0e7ff",
			color: "#3730a3"
		},
		site: {
			background: "#fef3c7",
			color: "#92400e"
		},
		flag: {
			background: "#fce7f3",
			color: "#9d174d"
		}
	};
	var TIME_COLORS = {
		fast: "#16a34a",
		medium: "#ca8a04",
		slow: "#dc2626"
	};
	function summarize(plugins) {
		return {
			total: plugins.length,
			cssOk: plugins.filter((p) => p.cssStatus === "fulfilled").length,
			handleOk: plugins.filter((p) => p.handleStatus === "fulfilled").length,
			failed: plugins.filter((p) => p.cssStatus === "rejected" || p.handleStatus === "rejected").length,
			skipped: plugins.filter((p) => p.cssStatus === "skipped" && p.handleStatus === "skipped").length,
			flagged: plugins.filter((p) => p.isFeatureFlagged).length,
			totalMs: plugins.reduce((sum, p) => sum + (p.totalDurationMs ?? 0), 0)
		};
	}
	function sortRows(plugins) {
		const isFailed = (p) => p.cssStatus === "rejected" || p.handleStatus === "rejected";
		return [...plugins].sort((a, b) => {
			const failDiff = Number(isFailed(b)) - Number(isFailed(a));
			if (failDiff !== 0) return failDiff;
			return (b.totalDurationMs ?? 0) - (a.totalDurationMs ?? 0);
		});
	}
	function StatusBadge({ status, durationMs }) {
		const tone = status === "fulfilled" ? "ok" : status === "rejected" ? "fail" : "skip";
		const icon = status === "fulfilled" ? "✅" : status === "rejected" ? "❌" : "⏭";
		return (0, import_jsx_runtime.jsxs)("span", {
			style: {
				...BADGE_BASE,
				...BADGE_COLORS[tone]
			},
			children: [
				icon,
				" ",
				durationMs != null ? `${durationMs}ms` : status === "skipped" ? "跳过" : "-"
			]
		});
	}
	function DurationCell({ ms }) {
		if (ms == null) return (0, import_jsx_runtime.jsx)("span", {
			style: {
				color: "#94a3b8",
				fontSize: "11px"
			},
			children: "-"
		});
		const color = ms < 100 ? TIME_COLORS.fast : ms <= 500 ? TIME_COLORS.medium : TIME_COLORS.slow;
		return (0, import_jsx_runtime.jsxs)("span", {
			style: {
				fontFamily: "monospace",
				fontSize: "11px",
				color
			},
			children: [ms, "ms"]
		});
	}
	function BadgeListCell({ values, tone }) {
		if (values.length === 0) return (0, import_jsx_runtime.jsx)("span", {
			style: {
				color: "#94a3b8",
				fontSize: "11px"
			},
			children: "全部"
		});
		return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: values.map((value) => (0, import_jsx_runtime.jsx)("span", {
			style: {
				...BADGE_BASE,
				...BADGE_COLORS[tone]
			},
			children: value
		}, value)) });
	}
	function DiagnosticsTable({ data }) {
		const summary = summarize(data);
		const rows = sortRows(data);
		const th = {
			padding: "8px 6px",
			textAlign: "left",
			fontWeight: 600,
			borderBottom: "2px solid #e2e8f0",
			background: "#f1f5f9",
			whiteSpace: "nowrap"
		};
		const td = {
			padding: "6px",
			borderBottom: "1px solid #f1f5f9",
			verticalAlign: "top"
		};
		return (0, import_jsx_runtime.jsxs)("div", {
			style: { fontSize: "13px" },
			children: [(0, import_jsx_runtime.jsxs)("div", {
				className: "jhs-diag-summary",
				style: {
					background: "#f8fafc",
					border: "1px solid #e2e8f0",
					borderRadius: "6px",
					padding: "10px 12px",
					marginBottom: "12px",
					lineHeight: 1.8
				},
				children: [
					(0, import_jsx_runtime.jsx)("div", {
						style: {
							fontWeight: 700,
							fontSize: "14px",
							marginBottom: "4px"
						},
						children: "📊 插件诊断报告"
					}),
					(0, import_jsx_runtime.jsxs)("div", { children: [
						"总计: ",
						(0, import_jsx_runtime.jsx)("b", { children: summary.total }),
						" 个插件 | ✅ CSS: ",
						summary.cssOk,
						"/",
						summary.total,
						" | ✅ Handle:",
						" ",
						summary.handleOk,
						"/",
						summary.total,
						" | ⏱ 总耗时: ",
						(0, import_jsx_runtime.jsxs)("b", { children: [summary.totalMs, "ms"] })
					] }),
					(0, import_jsx_runtime.jsxs)("div", { children: [
						(0, import_jsx_runtime.jsxs)("span", {
							style: { color: summary.failed > 0 ? "#dc2626" : "#64748b" },
							children: [
								"❌ 失败: ",
								summary.failed,
								" 个插件"
							]
						}),
						" ",
						"| ⏭ 跳过: ",
						summary.skipped,
						" 个插件 | 🚩 Feature Flag: ",
						summary.flagged,
						" 个"
					] })
				]
			}), (0, import_jsx_runtime.jsxs)("table", {
				className: "jhs-diag-table",
				style: {
					width: "100%",
					borderCollapse: "collapse",
					fontSize: "12px"
				},
				children: [(0, import_jsx_runtime.jsx)("thead", { children: (0, import_jsx_runtime.jsxs)("tr", { children: [
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "#"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "插件名"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "页面类型"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "站点"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "CSS"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "Handle"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "耗时"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "错误信息"
					}),
					(0, import_jsx_runtime.jsx)("th", {
						style: th,
						children: "Flag"
					})
				] }) }), (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((p, index) => {
					const failed = p.cssStatus === "rejected" || p.handleStatus === "rejected";
					const error = p.cssError ?? p.handleError;
					return (0, import_jsx_runtime.jsxs)("tr", {
						className: failed ? "jhs-diag-failed" : void 0,
						style: failed ? { background: "rgba(239,68,68,0.04)" } : void 0,
						children: [
							(0, import_jsx_runtime.jsx)("td", {
								style: {
									...td,
									color: "#94a3b8"
								},
								children: index + 1
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: {
									...td,
									fontWeight: failed ? 600 : 400
								},
								children: p.name
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: (0, import_jsx_runtime.jsx)(BadgeListCell, {
									values: p.pageTypes,
									tone: "page"
								})
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: (0, import_jsx_runtime.jsx)(BadgeListCell, {
									values: p.sites,
									tone: "site"
								})
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: (0, import_jsx_runtime.jsx)(StatusBadge, {
									status: p.cssStatus,
									durationMs: p.cssDurationMs
								})
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: (0, import_jsx_runtime.jsx)(StatusBadge, {
									status: p.handleStatus,
									durationMs: p.handleDurationMs
								})
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: (0, import_jsx_runtime.jsx)(DurationCell, { ms: p.totalDurationMs })
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: error ? (0, import_jsx_runtime.jsx)("span", {
									className: "jhs-diag-error",
									title: error,
									style: {
										color: "#dc2626",
										fontSize: "11px",
										display: "inline-block",
										maxWidth: "200px",
										overflow: "hidden",
										textOverflow: "ellipsis",
										whiteSpace: "nowrap",
										verticalAlign: "bottom"
									},
									children: error.length > 60 ? `${error.slice(0, 60)}…` : error
								}) : (0, import_jsx_runtime.jsx)("span", {
									style: {
										color: "#94a3b8",
										fontSize: "11px"
									},
									children: "-"
								})
							}),
							(0, import_jsx_runtime.jsx)("td", {
								style: td,
								children: p.isFeatureFlagged ? (0, import_jsx_runtime.jsxs)("span", {
									style: {
										...BADGE_BASE,
										...BADGE_COLORS.flag
									},
									children: ["🚩 ", p.featureFlagName]
								}) : null
							})
						]
					}, p.name);
				}) })]
			})]
		});
	}
	function DiagnosticsWrapper({ html }) {
		return (0, import_jsx_runtime.jsx)("div", {
			style: { padding: "15px" },
			dangerouslySetInnerHTML: { __html: html }
		});
	}
	function renderDiagnosticsHtml(pluginManager) {
		return jsxToString((0, import_jsx_runtime.jsx)(DiagnosticsTable, { data: pluginManager.getDiagnostics().plugins }));
	}
	function renderDiagnosticsText(pluginManager) {
		const diag = pluginManager.getDiagnostics();
		const plugins = diag.plugins;
		const cssOk = plugins.filter((p) => p.cssStatus === "fulfilled").length;
		const handleOk = plugins.filter((p) => p.handleStatus === "fulfilled").length;
		const failed = plugins.filter((p) => p.cssStatus === "rejected" || p.handleStatus === "rejected");
		const skipped = plugins.filter((p) => p.cssStatus === "skipped" && p.handleStatus === "skipped");
		const flagged = plugins.filter((p) => p.isFeatureFlagged);
		const totalMs = plugins.reduce((sum, p) => sum + (p.totalDurationMs ?? 0), 0);
		const lines = plugins.map((p) => {
			const parts = [
				`${p.cssStatus === "rejected" || p.handleStatus === "rejected" ? "❌" : p.cssStatus === "skipped" && p.handleStatus === "skipped" ? "⏭️" : "✅"} ${p.name}`,
				`CSS:${p.cssStatus}${p.cssDurationMs != null ? `(${p.cssDurationMs}ms)` : ""}`,
				`Handle:${p.handleStatus}${p.handleDurationMs != null ? `(${p.handleDurationMs}ms)` : ""}`,
				`总耗时:${p.totalDurationMs != null ? `${p.totalDurationMs}ms` : "-"}`
			];
			if (p.pageTypes.length) parts.push(`页面:[${p.pageTypes.join(",")}]`);
			if (p.sites.length) parts.push(`站点:[${p.sites.join(",")}]`);
			if (p.isFeatureFlagged) parts.push(`Flag:${p.featureFlagName}`);
			if (p.cssError) parts.push(`CSS错误:${p.cssError}`);
			if (p.handleError) parts.push(`Handle错误:${p.handleError}`);
			return parts.join(" | ");
		});
		return [
			"JavDB Power Tools 插件诊断报告",
			`总计 ${diag.total} 个插件 | CSS ${cssOk}/${diag.total} | Handle ${handleOk}/${diag.total} | 总耗时 ${totalMs}ms`,
			`失败 ${failed.length} 个 | 跳过 ${skipped.length} 个 | Feature Flag ${flagged.length} 个`,
			"",
			...lines
		].join("\n");
	}
	function registerDiagnosticsMenu(pluginManager) {
		GM_registerMenuCommand("📊 插件诊断", () => {
			const diag = pluginManager.getDiagnostics();
			const failed = diag.handle.filter((p) => p.status === "rejected");
			layer.open({
				type: 1,
				title: `插件诊断 — 共 ${diag.total} 个插件,${failed.length} 个失败`,
				area: ["600px", "70%"],
				content: jsxToString((0, import_jsx_runtime.jsx)(DiagnosticsWrapper, { html: renderDiagnosticsHtml(pluginManager) }))
			});
		});
	}
	function detectPageContext() {
		const href = window.location.href;
		const isDetailPage = href.split("?")[0].includes("/v/");
		const isListPage = typeof $ !== "undefined" && ($(".movie-list").length > 0 || href.includes("advanced_search"));
		const isFc2Page = href.includes("advanced_search?type=3") || href.includes("advanced_search?type=100");
		return {
			site: isJavdbSite ? "javdb" : isMissavSite ? "missav" : "unknown",
			pageType: isDetailPage ? "detail" : isFc2Page ? "fc2" : isListPage ? "list" : "unknown",
			isDetailPage,
			isListPage,
			isFc2Page,
			isJavdbSite,
			isMissavSite
		};
	}
	var pageContext = {
		site: "unknown",
		pageType: "unknown",
		isDetailPage: false,
		isListPage: false,
		isFc2Page: false,
		isJavdbSite: false,
		isMissavSite: false
	};
	function initPageContext() {
		pageContext = detectPageContext();
		return pageContext;
	}
	var PERSISTENT_DATA_DB_NAME = "JAV-JHS-PersistentData";
	var PERSISTENT_DATA_RECORD_STORE = "records";
	var PERSISTENT_DATA_META_STORE = "meta";
	var IndexedDbBackend = class {
		dbPromise = null;
		open() {
			if (this.dbPromise) return this.dbPromise;
			if (typeof indexedDB === "undefined") return Promise.reject(new Error("当前环境不支持 IndexedDB"));
			this.dbPromise = new Promise((resolve, reject) => {
				const request = indexedDB.open(PERSISTENT_DATA_DB_NAME, 1);
				request.onupgradeneeded = () => {
					const db = request.result;
					if (!db.objectStoreNames.contains("records")) db.createObjectStore(PERSISTENT_DATA_RECORD_STORE, { keyPath: "id" }).createIndex("namespace", "namespace", { unique: false });
					if (!db.objectStoreNames.contains("meta")) db.createObjectStore(PERSISTENT_DATA_META_STORE, { keyPath: "key" });
				};
				request.onsuccess = () => {
					const db = request.result;
					db.onversionchange = () => db.close();
					resolve(db);
				};
				request.onerror = () => reject(request.error ?? new Error("打开持久数据数据库失败"));
				request.onblocked = () => reject(new Error("持久数据数据库升级被其他标签页阻塞"));
			});
			return this.dbPromise;
		}
		async loadRecords() {
			const db = await this.open();
			return await new Promise((resolve, reject) => {
				const request = db.transaction(PERSISTENT_DATA_RECORD_STORE, "readonly").objectStore(PERSISTENT_DATA_RECORD_STORE).getAll();
				request.onsuccess = () => resolve(request.result);
				request.onerror = () => reject(request.error ?? new Error("读取持久数据失败"));
			});
		}
		async putRecords(records) {
			if (records.length === 0) return;
			const db = await this.open();
			await new Promise((resolve, reject) => {
				const tx = db.transaction(PERSISTENT_DATA_RECORD_STORE, "readwrite");
				const objectStore = tx.objectStore(PERSISTENT_DATA_RECORD_STORE);
				for (const record of records) objectStore.put(record);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error ?? new Error("写入持久数据失败"));
				tx.onabort = () => reject(tx.error ?? new Error("写入持久数据事务中止"));
			});
		}
		async putRecord(record) {
			await this.putRecords([record]);
		}
		async deleteRecord(id) {
			const db = await this.open();
			await new Promise((resolve, reject) => {
				const tx = db.transaction(PERSISTENT_DATA_RECORD_STORE, "readwrite");
				tx.objectStore(PERSISTENT_DATA_RECORD_STORE).delete(id);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error ?? new Error("删除持久数据失败"));
				tx.onabort = () => reject(tx.error ?? new Error("删除持久数据事务中止"));
			});
		}
		async loadMeta(key) {
			const db = await this.open();
			return await new Promise((resolve, reject) => {
				const request = db.transaction(PERSISTENT_DATA_META_STORE, "readonly").objectStore(PERSISTENT_DATA_META_STORE).get(key);
				request.onsuccess = () => resolve(request.result ?? null);
				request.onerror = () => reject(request.error ?? new Error("读取持久数据状态失败"));
			});
		}
		async saveMeta(meta) {
			const db = await this.open();
			await new Promise((resolve, reject) => {
				const tx = db.transaction(PERSISTENT_DATA_META_STORE, "readwrite");
				tx.objectStore(PERSISTENT_DATA_META_STORE).put(meta);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error ?? new Error("写入持久数据状态失败"));
				tx.onabort = () => reject(tx.error ?? new Error("写入持久数据状态事务中止"));
			});
		}
	};
	var PERSISTENT_DATA_NAMESPACES = [
		"translate",
		"dmmVideo",
		"otherSite",
		"otherSiteDmm",
		"screenShot",
		"actressInfo",
		"scoreInfo",
		"visitHistory",
		"syncSession",
		"vltPending",
		"carSyncIntent",
		"reflowJournal",
		"reflowRollback"
	];
	var LEGACY_KEY_NAMESPACES = {
		jhs_translate: "translate",
		jhs_dmm_video: "dmmVideo",
		jhs_other_site: "otherSite",
		jhs_other_site_dmm: "otherSiteDmm",
		jhs_screenShot: "screenShot",
		jhs_actress_info: "actressInfo",
		jhs_score_info: "scoreInfo",
		jhs_visit_history: "visitHistory"
	};
	function getPersistentNamespaceForLegacyKey(key) {
		return LEGACY_KEY_NAMESPACES[key];
	}
	function recordId(namespace, entryKey) {
		return `${namespace}\u0000${entryKey}`;
	}
	function isRecord(value) {
		return typeof value === "object" && value !== null && !Array.isArray(value);
	}
	function reportPersistenceError(error) {
		const logger = typeof window !== "undefined" && window.clog ? window.clog : null;
		if (logger) logger.error("持久数据写入 IndexedDB 失败", error);
		else console.error("持久数据写入 IndexedDB 失败", error);
	}
	var PersistentDataStore = class {
		backend;
		records = new Map();
		writeQueue = Promise.resolve();
		initialized = false;
		active = false;
		constructor(backend = new IndexedDbBackend()) {
			this.backend = backend;
		}
		async initialize() {
			this.records.clear();
			for (const record of await this.backend.loadRecords()) this.records.set(record.id, record);
			this.initialized = true;
			this.active = false;
		}
		get isInitialized() {
			return this.initialized;
		}
		get isActive() {
			return this.active;
		}
		activate() {
			this.assertInitialized();
			this.active = true;
		}
		deactivate() {
			this.active = false;
		}
		get(namespace, entryKey) {
			this.assertInitialized();
			return this.records.get(recordId(namespace, entryKey))?.value;
		}
		getNamespace(namespace) {
			this.assertInitialized();
			const result = Object.create(null);
			for (const record of this.records.values()) if (record.namespace === namespace) Object.defineProperty(result, record.entryKey, {
				configurable: true,
				enumerable: true,
				value: record.value,
				writable: true
			});
			return result;
		}
		async set(namespace, entryKey, value) {
			await this.setMany([{
				namespace,
				entryKey,
				value
			}]);
		}
		async setMany(entries) {
			this.assertInitialized();
			const records = entries.map((entry) => ({
				id: recordId(entry.namespace, entry.entryKey),
				namespace: entry.namespace,
				entryKey: entry.entryKey,
				value: entry.value
			}));
			for (const record of records) this.records.set(record.id, record);
			await this.enqueueWrite(() => this.backend.putRecords(records));
		}
		async delete(namespace, entryKey) {
			this.assertInitialized();
			const id = recordId(namespace, entryKey);
			this.records.delete(id);
			await this.enqueueWrite(() => this.backend.deleteRecord(id));
		}
		async replaceNamespace(namespace, values) {
			this.assertInitialized();
			const oldRecords = [...this.records.values()].filter((record) => record.namespace === namespace);
			const nextRecords = Object.entries(values).map(([entryKey, value]) => ({
				id: recordId(namespace, entryKey),
				namespace,
				entryKey,
				value
			}));
			for (const record of oldRecords) this.records.delete(record.id);
			for (const record of nextRecords) this.records.set(record.id, record);
			await this.enqueueWrite(async () => {
				for (const record of nextRecords) await this.backend.putRecord(record);
				for (const record of oldRecords) if (!this.records.has(record.id)) await this.backend.deleteRecord(record.id);
			});
		}
		async clearNamespace(namespace) {
			await this.replaceNamespace(namespace, {});
		}
		exportNamespaces(namespaces) {
			this.assertInitialized();
			const result = {};
			for (const namespace of namespaces) result[namespace] = this.getNamespace(namespace);
			return result;
		}
		async getMeta(key) {
			return await this.backend.loadMeta(key);
		}
		async setMeta(meta) {
			await this.backend.saveMeta(meta);
		}
		async flush() {
			await this.writeQueue;
		}
		enqueueWrite(task) {
			const operation = this.writeQueue.then(task);
			this.writeQueue = operation.catch(() => void 0);
			return operation;
		}
		assertInitialized() {
			if (!this.initialized) throw new Error("持久数据存储尚未初始化");
		}
	};
	var persistentDataStore = new PersistentDataStore();
	async function flushPersistentData() {
		if (persistentDataStore.isActive) await persistentDataStore.flush();
	}
	function readPersistentMap(namespace, legacyKey) {
		if (persistentDataStore.isActive) return persistentDataStore.getNamespace(namespace);
		try {
			const raw = localStorage.getItem(legacyKey);
			if (!raw) return {};
			const value = JSON.parse(raw);
			return isRecord(value) ? value : {};
		} catch {
			return {};
		}
	}
	function writePersistentEntry(namespace, legacyKey, entryKey, value) {
		if (persistentDataStore.isActive) {
			persistentDataStore.set(namespace, entryKey, value).catch(reportPersistenceError);
			return;
		}
		try {
			const cache = readPersistentMap(namespace, legacyKey);
			cache[entryKey] = value;
			localStorage.setItem(legacyKey, JSON.stringify(cache));
		} catch (error) {
			reportPersistenceError(error);
		}
	}
	function replacePersistentMap(namespace, legacyKey, values) {
		if (persistentDataStore.isActive) {
			persistentDataStore.replaceNamespace(namespace, values).catch(reportPersistenceError);
			return;
		}
		try {
			localStorage.setItem(legacyKey, JSON.stringify(values));
		} catch (error) {
			reportPersistenceError(error);
		}
	}
	function readPersistentValue(namespace, entryKey, legacyKey) {
		if (persistentDataStore.isActive) return persistentDataStore.get(namespace, entryKey) ?? null;
		try {
			const raw = localStorage.getItem(legacyKey);
			return raw ? JSON.parse(raw) : null;
		} catch {
			return null;
		}
	}
	function writePersistentValue(namespace, entryKey, legacyKey, value) {
		if (persistentDataStore.isActive) {
			persistentDataStore.set(namespace, entryKey, value).catch(reportPersistenceError);
			return;
		}
		try {
			localStorage.setItem(legacyKey, JSON.stringify(value));
		} catch (error) {
			reportPersistenceError(error);
		}
	}
	async function writePersistentValueAndWait(namespace, entryKey, legacyKey, value) {
		if (persistentDataStore.isActive) {
			await persistentDataStore.set(namespace, entryKey, value);
			return;
		}
		localStorage.setItem(legacyKey, JSON.stringify(value));
	}
	function removePersistentValue(namespace, entryKey, legacyKey) {
		if (persistentDataStore.isActive) {
			persistentDataStore.delete(namespace, entryKey).catch(reportPersistenceError);
			return;
		}
		try {
			localStorage.removeItem(legacyKey);
		} catch (error) {
			reportPersistenceError(error);
		}
	}
	function readPersistentPrefix(namespace, legacyPrefix) {
		if (persistentDataStore.isActive) return persistentDataStore.getNamespace(namespace);
		const result = {};
		try {
			for (let index = 0; index < localStorage.length; index++) {
				const key = localStorage.key(index);
				if (!key?.startsWith(legacyPrefix)) continue;
				try {
					const raw = localStorage.getItem(key);
					if (raw !== null && raw !== "") result[key.slice(legacyPrefix.length)] = JSON.parse(raw);
				} catch {}
			}
		} catch {}
		return result;
	}
	function clearPersistentNamespace(namespace, legacyKey) {
		if (persistentDataStore.isActive) persistentDataStore.clearNamespace(namespace).catch(reportPersistenceError);
		try {
			localStorage.removeItem(legacyKey);
		} catch (error) {
			reportPersistenceError(error);
		}
	}
	var MIGRATION_META_KEY = "persistent-data-v1";
	var SINGLE_ENTRY_KEY = "current";
	var MAP_SPECS = [
		{
			legacyKey: "jhs_translate",
			namespace: "translate"
		},
		{
			legacyKey: "jhs_dmm_video",
			namespace: "dmmVideo"
		},
		{
			legacyKey: "jhs_other_site",
			namespace: "otherSite"
		},
		{
			legacyKey: "jhs_other_site_dmm",
			namespace: "otherSiteDmm"
		},
		{
			legacyKey: "jhs_screenShot",
			namespace: "screenShot"
		},
		{
			legacyKey: "jhs_actress_info",
			namespace: "actressInfo"
		},
		{
			legacyKey: "jhs_score_info",
			namespace: "scoreInfo"
		},
		{
			legacyKey: "jhs_visit_history",
			namespace: "visitHistory"
		}
	];
	var SINGLE_SPECS = [{
		legacyKey: "jhs:sync_session_v1",
		namespace: "syncSession"
	}, {
		legacyKey: "jhs:reflow:journal",
		namespace: "reflowJournal"
	}];
	var PREFIX_SPECS = [
		{
			prefix: "jdb:vlt-pending-server-sync:",
			namespace: "vltPending"
		},
		{
			prefix: "jhs:car-sync-intent:",
			namespace: "carSyncIntent"
		},
		{
			prefix: "jhs:reflow:rollback:",
			namespace: "reflowRollback"
		}
	];
	function getStorageKeys(storage) {
		const keys = [];
		for (let index = 0; index < storage.length; index++) {
			const key = storage.key(index);
			if (key) keys.push(key);
		}
		return keys;
	}
	function parseJson(raw, sourceKey) {
		try {
			return JSON.parse(raw);
		} catch (error) {
			throw new Error(`无法解析旧版持久数据 ${sourceKey}: ${error instanceof Error ? error.message : String(error)}`);
		}
	}
	function isObject(value) {
		return typeof value === "object" && value !== null && !Array.isArray(value);
	}
	function stableValue(value) {
		if (Array.isArray(value)) return value.map(stableValue);
		if (!isObject(value)) return value;
		const sorted = {};
		for (const key of Object.keys(value).sort()) sorted[key] = stableValue(value[key]);
		return sorted;
	}
	function stableJson(value) {
		return JSON.stringify(stableValue(value));
	}
	function collectEntries(storage) {
		const entries = [];
		for (const spec of MAP_SPECS) {
			const raw = storage.getItem(spec.legacyKey);
			if (raw === null || raw === "") continue;
			const value = parseJson(raw, spec.legacyKey);
			if (!isObject(value)) throw new Error(`旧版持久数据 ${spec.legacyKey} 不是对象,已停止迁移`);
			for (const [entryKey, entryValue] of Object.entries(value)) entries.push({
				namespace: spec.namespace,
				entryKey,
				value: entryValue,
				sourceKey: spec.legacyKey
			});
		}
		for (const spec of SINGLE_SPECS) {
			const raw = storage.getItem(spec.legacyKey);
			if (raw === null || raw === "") continue;
			const value = parseJson(raw, spec.legacyKey);
			if (!isObject(value)) throw new Error(`旧版持久数据 ${spec.legacyKey} 不是对象,已停止迁移`);
			entries.push({
				namespace: spec.namespace,
				entryKey: SINGLE_ENTRY_KEY,
				value,
				sourceKey: spec.legacyKey
			});
		}
		const allKeys = getStorageKeys(storage);
		for (const spec of PREFIX_SPECS) for (const sourceKey of allKeys) {
			if (!sourceKey.startsWith(spec.prefix)) continue;
			const raw = storage.getItem(sourceKey);
			if (raw === null || raw === "") continue;
			const value = parseJson(raw, sourceKey);
			if (!(spec.namespace === "reflowRollback" ? Array.isArray(value) : isObject(value))) throw new Error(`旧版持久数据 ${sourceKey} 格式无效,已停止迁移`);
			entries.push({
				namespace: spec.namespace,
				entryKey: sourceKey.slice(spec.prefix.length),
				value,
				sourceKey
			});
		}
		return entries;
	}
	function sourceKeysFor(entries) {
		return [...new Set(entries.map((entry) => entry.sourceKey))].sort();
	}
	function verifyEntries(store, entries) {
		const expected = new Map();
		for (const entry of entries) expected.set(`${entry.namespace}\u0000${entry.entryKey}`, entry);
		const namespaces = new Set(entries.map((entry) => entry.namespace));
		for (const namespace of namespaces) {
			const actual = store.getNamespace(namespace);
			const expectedEntries = [...expected.values()].filter((entry) => entry.namespace === namespace);
			if (Object.keys(actual).length !== expectedEntries.length) throw new Error(`持久数据校验失败:${namespace} 记录数量不一致`);
			for (const entry of expectedEntries) if (stableJson(actual[entry.entryKey]) !== stableJson(entry.value)) throw new Error(`持久数据校验失败:${namespace}/${entry.entryKey}`);
		}
	}
	function buildState(phase, sourceKeys) {
		return {
			key: MIGRATION_META_KEY,
			phase,
			sourceKeys,
			updatedAt: Date.now()
		};
	}
	async function migratePersistentData(store, storage = localStorage) {
		const entries = collectEntries(storage);
		const sourceKeys = sourceKeysFor(entries);
		const previous = await store.getMeta(MIGRATION_META_KEY);
		if (entries.length === 0 && previous?.phase === "complete") {
			store.activate();
			return {
				phase: "complete",
				migratedKeys: [],
				removedKeys: [],
				retainedKeys: []
			};
		}
		await store.setMeta(buildState("copying", sourceKeys));
		await store.setMany(entries);
		verifyEntries(store, entries);
		await store.setMeta(buildState("verified", sourceKeys));
		store.activate();
		const removedKeys = [];
		const retainedKeys = [];
		for (const sourceKey of sourceKeys) try {
			storage.removeItem(sourceKey);
			removedKeys.push(sourceKey);
		} catch {
			retainedKeys.push(sourceKey);
		}
		const phase = retainedKeys.length === 0 ? "complete" : "cleanup-pending";
		await store.setMeta(buildState(phase, sourceKeys));
		return {
			phase,
			migratedKeys: sourceKeys,
			removedKeys,
			retainedKeys
		};
	}
	function TemporaryImageContainer({ src, alt = "" }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "temporary-container",
			style: { display: "none" },
			children: (0, import_jsx_runtime.jsx)("img", {
				src,
				alt
			})
		});
	}
	var PluginManager = class {
		plugins = new Map();
		registrationComplete = false;
		lastCssResults = [];
		lastHandleResults = [];
		featureFlags = new Map();
		matchesPage(plugin) {
			const types = plugin.pageTypes;
			if (!types || types.length === 0) return true;
			return types.includes(pageContext.pageType);
		}
		register(pluginClass, featureFlag) {
			if (typeof pluginClass !== "function") throw new Error("插件必须是一个类");
			const instance = new pluginClass();
			instance.pluginManager = this;
			const pluginName = instance.getName();
			if (this.plugins.has(pluginName)) throw new Error(`插件"${pluginName}"已注册`);
			if (featureFlag) this.featureFlags.set(pluginName, featureFlag);
			this.plugins.set(pluginName, instance);
		}
		getBean(name) {
			const plugin = this.plugins.get(name);
			if (!plugin && this.registrationComplete) clog.warn(`[PluginManager] getBean("${name}") 未找到已注册插件`);
			return plugin;
		}
		async processCss() {
			this.registrationComplete = true;
			const results = await Promise.all(Array.from(this.plugins).map(async ([name, plugin]) => {
				if (!this.matchesPage(plugin)) return {
					name,
					status: "skipped"
				};
				const start = performance.now();
				try {
					if (typeof plugin.initCss === "function") {
						const css = await plugin.initCss();
						if (css) utils.insertStyle(css);
						return {
							name,
							status: "fulfilled",
							durationMs: Math.round(performance.now() - start)
						};
					}
					return {
						name,
						status: "skipped"
					};
				} catch (err) {
					clog.error(`插件 ${name} 加载 CSS 失败`, err);
					return {
						name,
						status: "rejected",
						error: err,
						durationMs: Math.round(performance.now() - start)
					};
				}
			}));
			this.lastCssResults = results;
			const failed = results.filter((result) => result.status === "rejected");
			if (failed.length) clog.error("以下插件的 CSS 加载失败:", failed.map((result) => result.name));
			return results;
		}
		async processPlugins() {
			const results = await Promise.all(Array.from(this.plugins).map(async ([name, plugin]) => {
				if (!this.matchesPage(plugin)) return {
					name,
					status: "skipped"
				};
				const start = performance.now();
				try {
					if (typeof plugin.handle === "function") {
						await plugin.handle();
						return {
							name,
							status: "fulfilled",
							durationMs: Math.round(performance.now() - start)
						};
					}
					return {
						name,
						status: "skipped"
					};
				} catch (err) {
					clog.error(`插件 ${name} 执行失败`, err);
					return {
						name,
						status: "rejected",
						error: err,
						durationMs: Math.round(performance.now() - start)
					};
				}
			}));
			this.lastHandleResults = results;
			const failed = results.filter((result) => result.status === "rejected");
			if (failed.length) clog.error("以下插件执行失败:", failed.map((result) => result.name));
			return results;
		}
		getDiagnostics() {
			const cssByName = new Map(this.lastCssResults.map((result) => [result.name, result]));
			const handleByName = new Map(this.lastHandleResults.map((result) => [result.name, result]));
			const plugins = Array.from(this.plugins).map(([name, plugin]) => {
				const css = cssByName.get(name);
				const handle = handleByName.get(name);
				const cssDurationMs = css?.durationMs ?? null;
				const handleDurationMs = handle?.durationMs ?? null;
				const featureFlagName = this.featureFlags.get(name) ?? null;
				return {
					name,
					cssStatus: css?.status ?? "skipped",
					handleStatus: handle?.status ?? "skipped",
					cssDurationMs,
					handleDurationMs,
					totalDurationMs: cssDurationMs == null && handleDurationMs == null ? null : (cssDurationMs ?? 0) + (handleDurationMs ?? 0),
					cssError: css?.status === "rejected" ? css.error instanceof Error ? css.error.message : String(css.error) : null,
					handleError: handle?.status === "rejected" ? handle.error instanceof Error ? handle.error.message : String(handle.error) : null,
					pageTypes: [...plugin.pageTypes],
					sites: [...plugin.sites],
					isFeatureFlagged: featureFlagName != null,
					featureFlagName
				};
			});
			return {
				total: this.plugins.size,
				css: this.lastCssResults,
				handle: this.lastHandleResults,
				plugins
			};
		}
		destroyAll() {
			for (const [name, plugin] of this.plugins) try {
				plugin.destroy();
			} catch (err) {
				clog.error(`插件 ${name} 销毁失败`, err);
			}
		}
	};
	var EDIT_SVG = "<svg t=\"1760920692801\" class=\"jhs-icon icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"3545\" width=\"200\" height=\"200\"><path d=\"M1013.929675 128.26571a143.759824 143.759824 0 0 1 10.44409 53.858738 84.576649 84.576649 0 0 1-5.836403 30.308339 92.870485 92.870485 0 0 1-18.635533 29.284408 1314.726599 1314.726599 0 0 1-24.983901 24.574329c-7.372299 7.06512-13.82306 13.311095-19.249891 18.737926-6.143582 6.143582-12.082378 11.672806-17.406817 16.382886L720.266444 82.598415c9.317766-8.601015 20.478607-18.942712 33.277737-31.02509s23.448006-21.604931 31.946628-28.67005a102.085858 102.085858 0 0 1 68.193763-22.731255c11.263234 0.307179 22.116896 2.047861 32.560985 5.222045 10.546483 3.071791 19.659463 6.655547 27.441334 10.546483 16.280493 8.601015 34.301667 23.550399 54.063524 45.052936 19.864249 21.502538 35.120812 43.82422 46.076867 67.272226z m-907.20231 570.943576l32.560986-33.38013c17.099637-17.509209 38.397389-39.216533 64.098041-64.917186l84.986221-85.395793 94.303987-94.815953 250.350976-251.477299L850.817567 389.163169 600.46659 640.640468l-93.177663 94.815953c-31.02509 30.410732-58.978389 58.364031-83.859898 83.655111-24.779115 25.29108-45.360116 46.17926-61.743001 62.562146a504.797674 504.797674 0 0 1-55.804206 50.274981c-10.239304 7.884264-20.581 14.130239-31.537055 18.737926a507.152714 507.152714 0 0 1-47.715156 19.86425 1609.311367 1609.311367 0 0 1-131.063087 42.185931c-20.478607 5.426831-35.837563 8.908194-45.974474 10.546483-20.88818 2.35504-34.813633-0.819144-41.981145-9.42016-6.860333-8.601015-8.805801-22.93604-5.73401-43.312254a396.261054 396.261054 0 0 1 11.058448-47.305584c5.836403-20.683394 12.082378-42.185931 18.635532-64.40522 6.553154-22.219289 13.003916-42.697897 19.249891-61.435822 6.143582-18.635533 11.263234-31.537055 15.15417-38.602176 4.607687-10.853662 9.829732-20.785787 15.666135-29.796373a192.49891 192.49891 0 0 1 25.086294-29.796374z\" fill=\"#FF9500\" p-id=\"3546\"></path></svg>";
	var DELETE_SVG = "<svg t=\"1760921450746\" class=\"jhs-icon icon\" viewBox=\"0 0 1194 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4530\" width=\"200\" height=\"200\"><path d=\"M761.086847 36.028779s309.754321-147.538628 424.952209 231.50509c2.047962 6.570546 71.337359 253.862013-220.838618 415.139055-12.970429 7.167869-267.515096 145.746661-370.339877 341.327076 0 0-90.963666-205.649563-393.379455-351.566888-6.399883-3.071944-304.549083-156.583796-163.751664-487.2444 3.669266-8.533177 163.666333-336.20717 466.423449-99.411511l24.575549 27.391498L387.931021 324.279495l237.648977 159.570408-109.139333 145.746661L625.579998 849.069874l-30.719437-205.820227 166.226286-169.81022-216.486698-168.103585L761.086847 36.028779z\" fill=\"#F4382E\" p-id=\"4531\"></path></svg>";
	var REFRESH_SVG = "<svg t=\"1760926993643\" class=\"jhs-icon icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"5942\" width=\"200\" height=\"200\"><path d=\"M511.966722 0a511.966722 511.966722 0 1 0 179.828311 32.445891l-22.46254 59.964102A447.970882 447.970882 0 1 1 511.966722 63.99584a31.99792 31.99792 0 0 0 0-63.99584z\" fill=\"#333333\" p-id=\"5943\"></path><path d=\"M649.2378 9.151405A30.909991 30.909991 0 0 1 671.316364 0h193.267438a31.99792 31.99792 0 0 1 31.357962 31.99792c0 17.662852-13.759106 31.99792-31.357962 31.99792H703.954243v160.629559a31.99792 31.99792 0 0 1-31.99792 31.357962 31.485953 31.485953 0 0 1-31.99792-31.357962V31.357962c0-8.511447 3.647763-16.318939 9.343392-21.950573z\" fill=\"#333333\" p-id=\"5944\"></path></svg>";
	var BasePlugin = class {
		pluginManager;
		get sites() {
			return [];
		}
		get pageTypes() {
			return [];
		}
		destroy() {}
		editSvg = EDIT_SVG;
		deleteSvg = DELETE_SVG;
		refreshSvg = REFRESH_SVG;
		getName() {
			throw new Error(`${this.constructor.name} 未显示getName()`);
		}
		getBean(name) {
			return this.pluginManager.getBean(name);
		}
		async initCss() {
			return "";
		}
		async handle() {}
		getPageInfo() {
			let carNum = null;
			let url = null;
			let actress = null;
			let actors = null;
			let publishTime = null;
			const href = window.location.href;
			if (isJavdbSite) {
				carNum = $("a[title=\"複製番號\"]").attr("data-clipboard-text") ?? null;
				url = href.split("?")[0].split("#")[0];
				actress = $(".female").prev().map((_index, el) => $(el).text()).get().join(" ");
				actors = $(".male").prev().map((_index, el) => $(el).text()).get().join(" ");
				publishTime = $("strong:contains(\"日期:\")").parent(".panel-block").find(".value").text().trim();
			}
			return {
				carNum,
				url,
				actress,
				actors,
				publishTime
			};
		}
		getActressId() {
			const match = currentHref.match(/\/actors\/([^/?]+)/);
			if (match && match.length > 1) return match[1];
			else return null;
		}
		getActressPageInfo() {
			const href = window.location.href;
			if (!href.includes("/actors/") && !href.includes("/star/")) throw new Error("接口调用错误, 非演员详情页");
			const allName = [];
			const nameEl = $(".actor-section-name");
			if (nameEl.length) nameEl.text().trim().split(",").forEach((part) => {
				allName.push(part.trim());
			});
			const metaEl = $(".section-meta:not(:contains('影片'))");
			if (metaEl.length) metaEl.text().trim().split(",").forEach((part) => {
				allName.push(part.trim());
			});
			const role = $(".section-meta:contains('男優')").length > 0 ? ACTOR : ACTRESS;
			let movieType = CENSORED;
			if (allName.some((part) => part.includes("無碼"))) movieType = UNCENSORED;
			if (href.includes("uncensored")) movieType = UNCENSORED;
			let blacklistUrl = null;
			let starId = null;
			const urlObj = new URL(href);
			if (isJavdbSite) {
				starId = urlObj.pathname.split("/").filter((seg) => seg.trim() !== "").pop() ?? null;
				const searchParams = urlObj.searchParams;
				searchParams.delete("sort_type");
				searchParams.delete("page");
				blacklistUrl = urlObj.toString();
			}
			return {
				starId,
				name: allName[0],
				allName,
				role,
				movieType,
				blacklistUrl
			};
		}
		getSelector(siteType) {
			const type = siteType || "javdb";
			const selectors = { javdb: {
				boxSelector: ".movie-list",
				itemSelector: ".movie-list .item",
				coverImgSelector: ".cover img",
				requestDomItemSelector: ".movie-list .item",
				nextPageSelector: ".pagination-next"
			} };
			if (!type || !selectors[type]) throw new Error("类型错误: 无法确定选择器类型 (JavDb)");
			return selectors[type];
		}
		parseMovieId(url) {
			return url.split("/").pop().split(/[?#]/)[0];
		}
		getBoxCarInfo(boxEl) {
			let titleAttr;
			let imgTitleAttr;
			let dataTitleAttr;
			const linkEl = boxEl.find("a");
			const url = linkEl.attr("href");
			let carNum = null;
			let title = null;
			let publishTime = null;
			const videoTitleEl = boxEl.find(".video-title");
			if (videoTitleEl.length > 0) {
				const strongEl = videoTitleEl.find("strong");
				if (strongEl.length > 0) carNum = strongEl.text().trim();
				title = (titleAttr = linkEl.attr("title")) == null ? void 0 : titleAttr.trim();
				if (!title) {
					const text = videoTitleEl.text().trim();
					title = carNum && text.includes(carNum) ? text.replace(carNum, "").trim() : text;
				}
				publishTime = boxEl.find(".meta").text().trim();
			}
			if (!carNum) {
				const imgEl = boxEl.find("img");
				if (imgEl.length > 0) title = ((imgTitleAttr = imgEl.attr("title")) == null ? void 0 : imgTitleAttr.trim()) || ((dataTitleAttr = imgEl.attr("data-title")) == null ? void 0 : dataTitleAttr.trim());
				const dates = boxEl.find("date").map((_index, el) => $(el).text().trim()).get();
				const isDate = (val) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(val);
				publishTime = dates.find(isDate) || null;
				carNum = dates.find((val) => !isDate(val)) || null;
			}
			if (!carNum) {
				const msg = "提取番号信息失败: carNum 为空";
				clog.error("Error in getBoxCarInfo:", msg, "Box Element:", boxEl.get(0));
				failWithToast(msg);
			}
			if (featureFlags.westernCarFormat) carNum = this._formatWesternCar(carNum, publishTime || "");
			return {
				carNum,
				url: url || "",
				title: title || "",
				publishTime: publishTime || ""
			};
		}
		_formatWesternCar(carNum, rawDate) {
			if (!carNum || !rawDate) return carNum;
			if (!/^[a-zA-Z\s]+$/.test(carNum)) return carNum;
			const dateMatch = rawDate.match(/\d{2}(\d{2})-(\d{2})-(\d{2})/);
			if (dateMatch) return `${carNum.replace(/\s+/g, "")}.${dateMatch[1]}.${dateMatch[2]}.${dateMatch[3]}`;
			return carNum;
		}
		getBoxCarInfoList(boxEls = null) {
			boxEls ||= $(this.getSelector().itemSelector);
			if (boxEls.length === 0) {
				clog.error("获取当前列表页所有item的番号信息失败!");
				return [];
			}
			const list = [];
			boxEls.each((index, element) => {
				const itemEl = $(element);
				try {
					const info = this.getBoxCarInfo(itemEl);
					list.push(info);
				} catch (err) {
					clog.error("[getBoxCarInfoList] 提取单个 boxCar 信息失败:", err instanceof Error ? err.message : String(err), "元素索引:", index);
				}
			});
			return list;
		}
		checkDuplicateCarNumbers(list, other) {
			if (!list || list.length === 0 || !other || other.length === 0) return false;
			const existing = new Set(list.map((item) => item.carNum).filter(Boolean));
			if (existing.size === 0) return false;
			let duplicateCount = 0;
			for (const item of other) if (item && item.carNum && existing.has(item.carNum)) duplicateCount++;
			if (duplicateCount / other.length >= .5) {
				clog.warn("警告: 检测到大量番号信息重复, 该类别可能已被限制页码。");
				return true;
			}
			return false;
		}
	};
	var DetailPagePlugin = class extends BasePlugin {
		getName() {
			return "DetailPagePlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		constructor() {
			super();
		}
		async handle() {
			if (window.isDetailPage) {
				$(".video-meta-panel a").each((_index, element) => {
					const href = $(element).attr("href");
					if (href && (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("/"))) $(element).attr("target", "_blank");
				});
				this.handleFancyBox();
			}
		}
		handleFancyBox() {
			document.addEventListener("click", (event) => {
				if (event.target.closest(".fancybox-button--thumbs")) {
					const thumbsVisible = !$(".fancybox-thumbs").is(":hidden");
					localStorage.setItem("jhs_fancyboxThumbs", thumbsVisible.toString());
					unsafeWindow.$.fancybox.defaults.thumbs.autoStart = thumbsVisible;
				}
			});
			if (unsafeWindow.$.fancybox !== void 0) {
				const storedValue = localStorage.getItem("jhs_fancyboxThumbs");
				unsafeWindow.$.fancybox.defaults.thumbs.autoStart = storedValue === "true";
			}
		}
	};
	var HighlightMagnetPlugin = class extends BasePlugin {
		getName() {
			return "HighlightMagnetPlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		doFilterMagnet() {
			this.handleDb();
		}
		handleDb() {
			if (!isJavdbSite) return;
			const nameEls = $("#magnets-content .name");
			if (nameEls.length === 0) return;
			const highQualityMarkers = [
				"4k",
				"-c",
				"-u",
				"-uc"
			];
			let hasHighQuality = false;
			nameEls.each((_index, element) => {
				const nameEl = $(element);
				const nameText = nameEl.text().toLowerCase();
				const isHighQuality = highQualityMarkers.some((marker) => nameText.includes(marker));
				nameEl.parent().parent().parent().addClass("magnet-row");
				if (nameText.includes("4k")) nameEl.css("color", "#f40");
				if (isHighQuality) {
					hasHighQuality = true;
					nameEl.parent().parent().parent().addClass("high-quality");
				}
			});
			if (hasHighQuality) $("#magnets-content .magnet-row").not(".high-quality").hide();
			else $("#enable-magnets-filter").addClass("do-hide");
		}
		showAll() {
			if (isJavdbSite) $("#magnets-content .item").toArray().forEach((element) => $(element).show());
		}
	};
	function FoldCategorySectionButton() {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "foldCategoryBtn",
			children: (0, import_jsx_runtime.jsxs)("a", {
				className: "jhs-toolbar-menu-btn",
				style: {
					backgroundColor: "#d23e60 !important",
					marginLeft: "20px",
					borderBottom: "none !important",
					borderRadius: "3px"
				},
				children: [(0, import_jsx_runtime.jsx)("span", {}), (0, import_jsx_runtime.jsx)("i", { style: { marginLeft: "10px" } })]
			})
		});
	}
	function FoldCategoryToolbar({ selectedTagsText }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				display: "flex",
				alignItems: "center",
				flexGrow: 1,
				justifyContent: "flex-end"
			},
			children: [(0, import_jsx_runtime.jsxs)("div", { children: ["已选分类: ", (0, import_jsx_runtime.jsx)("span", {
				id: "jhs-check-tag",
				children: selectedTagsText
			})] }), (0, import_jsx_runtime.jsxs)("a", {
				className: "jhs-toolbar-menu-btn  main-tab-btn",
				id: "foldCategoryBtn",
				style: { backgroundColor: "#d23e60 !important" },
				children: [(0, import_jsx_runtime.jsx)("span", {}), (0, import_jsx_runtime.jsx)("i", { style: { marginLeft: "10px" } })]
			})]
		});
	}
	function HighlightButton() {
		return (0, import_jsx_runtime.jsx)("button", {
			className: "highlight-btn",
			title: "高亮显示",
			children: "★"
		});
	}
	var fold_category_plugin_default = "#tags a.tag,\n.tags a.tag {\n    position: relative;\n}\n\n.highlight-btn {\n    position: absolute;\n    top: -10px;\n    right: -10px;\n    background-color: #4caf50;\n    color: white;\n    border: none;\n    border-radius: 50%;\n    width: 24px;\n    height: 24px;\n    font-size: 14px;\n    line-height: 24px;\n    text-align: center;\n    cursor: pointer;\n    box-shadow: 0 2px 4px rgb(0, 0, 0, 0.2);\n    display: none;\n    z-index: 999;\n}\n\n/* 当父元素被高亮时,按钮变为其他颜色 */\n.highlighted .highlight-btn {\n    background-color: #ff5722;\n}\n\n/* 高亮状态下的标签样式 */\n.highlighted {\n    /* 浅黄色 */\n    border: __HIGHLIGHTED_TAG_NUMBER__px solid __HIGHLIGHTED_TAG_COLOR__;\n}\n";
	var FoldCategoryPlugin = class extends BasePlugin {
		getName() {
			return "FoldCategoryPlugin";
		}
		get pageTypes() {
			return ["list"];
		}
		async initCss() {
			const settings = await storageManager.getSetting();
			return fold_category_plugin_default.replace("__HIGHLIGHTED_TAG_NUMBER__", String(settings.highlightedTagNumber || 1)).replace("__HIGHLIGHTED_TAG_COLOR__", settings.highlightedTagColor || "#ce2222");
		}
		async handle() {
			if (window.isListPage) {
				if (!currentHref.includes("advanced_search")) {
					this.highlightTag();
					utils.loopDetector(() => currentHref.includes("/actors/") || $("#waitCheckBtn").length > 0, () => {
						this.createFoldBtn();
					}, 1, 1e4, true);
					$("#tags .tag-category .tag-expand").each((_index, expandEl) => {
						if ($(expandEl).parent().hasClass("collapse")) expandEl.click();
					});
				}
			}
		}
		highlightTag() {
			(async () => {
				const highlightedTags = await storageManager.getHighlightedTags();
				if (highlightedTags) highlightedTags.forEach((tagName) => {
					$(`#tags a.tag:contains(${tagName})`).addClass("highlighted");
					$(`.tags a.tag:contains(${tagName})`).addClass("highlighted");
				});
			})().then();
			$("#tags a.tag, .tags a.tag").hover(function() {
				const tagEl = $(this);
				const btnEl = $(jsxToString((0, import_jsx_runtime.jsx)(HighlightButton, {})));
				tagEl.append(btnEl);
				btnEl.fadeIn(0);
			}, function() {
				$(this).find(".highlight-btn").fadeOut(0, function() {
					$(this).remove();
				});
			});
			$(document).on("click", ".highlight-btn", async function(event) {
				event.stopPropagation();
				event.preventDefault();
				const tagEl = $(this).closest("a.tag");
				const cloneEl = tagEl.clone();
				cloneEl.find(".highlight-btn").remove();
				const tagName = cloneEl.text().trim().replace(/\s*\(\d+\)$/, "");
				let highlightedTags = await storageManager.getHighlightedTags();
				if (highlightedTags.includes(tagName)) {
					highlightedTags = highlightedTags.filter((item) => item !== tagName);
					tagEl.removeClass("highlighted");
				} else {
					highlightedTags.push(tagName);
					tagEl.addClass("highlighted");
				}
				await storageManager.setHighlightedTags(highlightedTags);
			});
		}
		async createFoldBtn() {
			let tagsEl = $("#tags");
			const selectedTagsText = $("#tags dl div.tag.is-info").map(function() {
				return $(this).text().replace(/\n/g, "").replace(/ /g, "");
			}).get().join(" ");
			if (!selectedTagsText) return;
			$(".tabs").append(jsxToString((0, import_jsx_runtime.jsx)(FoldCategoryToolbar, { selectedTagsText })));
			const sectionTitleEl = $("h2.section-title");
			if (sectionTitleEl.length > 0) {
				sectionTitleEl.append(jsxToString((0, import_jsx_runtime.jsx)(FoldCategorySectionButton, {})));
				tagsEl = $("section > div > div.box");
			}
			if (!tagsEl) return;
			const foldBtnEl = $("#foldCategoryBtn");
			let isFolded = localStorage.getItem("jhs_foldCategory") === "yes";
			const [label, iconClass] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
			foldBtnEl.find("span").text(label).end().find("i").attr("class", iconClass);
			if (!window.location.href.includes("noFold=1")) tagsEl[isFolded ? "hide" : "show"]();
			foldBtnEl.on("click", async (event) => {
				event.preventDefault();
				isFolded = !isFolded;
				localStorage.setItem("jhs_foldCategory", isFolded ? "yes" : "no");
				const [nextLabel, nextIconClass] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
				foldBtnEl.find("span").text(nextLabel).end().find("i").attr("class", nextIconClass);
				tagsEl[isFolded ? "hide" : "show"]();
			});
		}
	};
	function ActressInfoDetailSegment({ actressName, info, wikiApiUrl }) {
		const nameNode = (0, import_jsx_runtime.jsxs)("strong", {
			style: { marginRight: "5px" },
			children: [(0, import_jsx_runtime.jsx)("span", {
				style: { userSelect: "all" },
				children: actressName
			}), ":"]
		});
		if (info) return (0, import_jsx_runtime.jsxs)("div", {
			className: "panel-block actress-info",
			children: [nameNode, (0, import_jsx_runtime.jsxs)("a", {
				href: info.url,
				style: { marginLeft: "5px" },
				target: "_blank",
				children: [
					(0, import_jsx_runtime.jsxs)("span", {
						className: "info-tag",
						children: [
							info.birthday,
							" ",
							info.age
						]
					}),
					(0, import_jsx_runtime.jsxs)("span", {
						className: "info-tag",
						children: [
							info.height,
							" ",
							info.weight
						]
					}),
					(0, import_jsx_runtime.jsxs)("span", {
						className: "info-tag",
						children: [
							info.threeSizeText,
							" ",
							info.braSize
						]
					})
				]
			})]
		});
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "panel-block actress-info",
			children: [(0, import_jsx_runtime.jsx)("a", {
				href: wikiApiUrl + actressName,
				target: "_blank",
				children: nameNode
			}), (0, import_jsx_runtime.jsx)("span", {
				style: {
					marginLeft: "5px",
					color: "#999"
				},
				children: "暂无此演员信息"
			})]
		});
	}
	function ActressInfoStarPageHtml({ info }) {
		if (info) return (0, import_jsx_runtime.jsx)("a", {
			className: "actress-info",
			href: info.url,
			target: "_blank",
			children: (0, import_jsx_runtime.jsxs)("div", {
				style: {
					fontSize: "17px",
					fontWeight: "normal",
					marginTop: "5px"
				},
				children: [(0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						marginBottom: "10px"
					},
					children: [
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "300px" },
							children: ["出生日期: ", info.birthday]
						}),
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "200px" },
							children: ["年龄: ", info.age]
						}),
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "200px" },
							children: ["身高: ", info.height]
						})
					]
				}), (0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						marginBottom: "10px"
					},
					children: [
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "300px" },
							children: ["体重: ", info.weight]
						}),
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "200px" },
							children: ["三围: ", info.threeSizeText]
						}),
						(0, import_jsx_runtime.jsxs)("span", {
							style: { width: "200px" },
							children: ["罩杯: ", info.braSize]
						})
					]
				})]
			})
		});
		return (0, import_jsx_runtime.jsx)("div", {
			className: "actress-info",
			style: {
				fontSize: "17px",
				fontWeight: "normal",
				marginTop: "5px"
			},
			children: "无此相关演员信息"
		});
	}
	var actress_info_plugin_default = ".info-tag {\n    background-color: #ecf5ff;\n    display: inline-block;\n    height: 32px;\n    padding: 0 10px;\n    line-height: 30px;\n    font-size: 12px;\n    color: #409eff;\n    border: 1px solid #d9ecff;\n    border-radius: 4px;\n    box-sizing: border-box;\n    white-space: nowrap;\n}\n";
	var ActressInfoPlugin = class extends BasePlugin {
		apiUrl = "https://ja.wikipedia.org/wiki/";
		getName() {
			return "ActressInfoPlugin";
		}
		async handle() {
			if (await storageManager.getSetting("enableLoadActressInfo", "yes") === "yes") this.loadActressInfo();
		}
		loadActressInfo() {
			this.handleDetailPage().then();
			this.handleStarPage().then();
		}
		async initCss() {
			return actress_info_plugin_default;
		}
		async handleDetailPage() {
			if ($(".actress-info").length > 0) return;
			const actressNames = $(".female").prev().map((_index, element) => $(element).text().trim()).get();
			if (!actressNames.length) return;
			const storageKey = "jhs_actress_info";
			const cache = readPersistentMap("actressInfo", storageKey);
			let info;
			let html = "";
			for (const actressName of actressNames) {
				info = cache[actressName];
				if (!info) try {
					info = await this.searchInfo(actressName);
					if (info) cache[actressName] = info;
				} catch {
					clog.error("该名称查询失败,尝试其它名称");
				}
				const segment = jsxToString((0, import_jsx_runtime.jsx)(ActressInfoDetailSegment, {
					actressName,
					info,
					wikiApiUrl: this.apiUrl
				}));
				html += segment;
			}
			$("strong:contains(\"演員\")").parent().after(html);
			for (const [name, value] of Object.entries(cache)) if (value) writePersistentEntry("actressInfo", storageKey, name, value);
		}
		async handleStarPage() {
			if ($(".actress-info").length > 0) return;
			const names = [];
			const nameSectionEl = $(".actor-section-name");
			if (nameSectionEl.length) nameSectionEl.text().trim().split(",").forEach((name) => {
				names.push(name.trim());
			});
			const metaEl = $(".section-meta:not(:contains('影片'))");
			if (metaEl.length) metaEl.text().trim().split(",").forEach((name) => {
				names.push(name.trim());
			});
			if (!names.length) return;
			const storageKey = "jhs_actress_info";
			const cache = readPersistentMap("actressInfo", storageKey);
			let info = null;
			for (const actressName of names) {
				info = cache[actressName];
				if (info) break;
				try {
					info = await this.searchInfo(actressName);
				} catch {
					clog.error("该名称查询失败,尝试其它名称");
				}
				if (info) break;
			}
			if (info) names.forEach((actressName) => {
				cache[actressName] = info;
			});
			const html = jsxToString((0, import_jsx_runtime.jsx)(ActressInfoStarPageHtml, { info }));
			nameSectionEl.parent().append(html);
			for (const [name, value] of Object.entries(cache)) if (value) writePersistentEntry("actressInfo", storageKey, name, value);
		}
		async searchInfo(actressName) {
			if (actressName === "三上悠亞") actressName = "三上悠亜";
			const url = this.apiUrl + actressName;
			const responseHtml = String(await gmHttp.get(url));
			const domParser = new DOMParser();
			const $parsed = $(domParser.parseFromString(responseHtml, "text/html"));
			const birthday = $parsed.find("a[title=\"誕生日\"]").parent().parent().find("td").text().trim();
			const age = $parsed.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt($parsed.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "";
			const height = $parsed.find("tr:has(a[title=\"身長\"]) td").text().trim().split(" ")[0] + "cm";
			let weight = $parsed.find("tr:has(a[title=\"体重\"]) td").text().trim().split("/")[1].trim();
			if (weight === "― kg") weight = "";
			return {
				birthday,
				age,
				height,
				weight,
				threeSizeText: $parsed.find("a[title=\"スリーサイズ\"]").closest("tr").find("td").text().replace("cm", "").trim(),
				braSize: $parsed.find("th:contains(\"ブラサイズ\")").next("td").contents().first().text().trim(),
				url
			};
		}
	};
	var API_BASE = "https://jdforrepam.com/api";
	var SIGNATURE_TIME_KEY = "jhs_review_ts";
	var SIGNATURE_VALUE_KEY = "jhs_review_sign";
	var SIGNATURE_SINGLE_KEY = "jhs_jdsignature";
	var SIGNATURE_TTL_SECONDS = 20;
	var SIGNATURE_TTL_SECONDS_V2 = 300;
	var SIGNATURE_NAMESPACE = "lpw6vgqzsp";
	var SIGNATURE_SALT = "71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa";
	function reBuildSignature() {
		const now = Math.floor(Date.now() / 1e3);
		if (featureFlags.upgradeSignature300s) {
			const stored = localStorage.getItem(SIGNATURE_SINGLE_KEY);
			if (stored) {
				const parts = stored.split(".");
				if (parts.length === 3 && now - parseInt(parts[0], 10) <= SIGNATURE_TTL_SECONDS_V2) return stored;
			}
			const sig = `${now}.${SIGNATURE_NAMESPACE}.${md5(`${now}${SIGNATURE_SALT}`)}`;
			localStorage.setItem(SIGNATURE_SINGLE_KEY, sig);
			return sig;
		}
		if (now - Number(localStorage.getItem(SIGNATURE_TIME_KEY) || 0) <= SIGNATURE_TTL_SECONDS) return localStorage.getItem(SIGNATURE_VALUE_KEY);
		const sig = `${now}.${SIGNATURE_NAMESPACE}.${md5(`${now}${SIGNATURE_SALT}`)}`;
		localStorage.setItem(SIGNATURE_TIME_KEY, String(now));
		localStorage.setItem(SIGNATURE_VALUE_KEY, sig);
		return sig;
	}
	function _updateImgServer(str) {
		return str.replace(/https:\/\/.*?\/rhe951l4q/g, "https://c0.jdbstatic.com");
	}
	async function fetchMovieReviews(movieId, page = 1, limit = 20) {
		const url = `${API_BASE}/v1/movies/${encodeURIComponent(movieId)}/reviews`;
		const headers = { jdSignature: await reBuildSignature() };
		return (await gmHttp.get(url, {
			page,
			sort_by: "hotly",
			limit
		}, headers)).data.reviews;
	}
	async function fetchMovieDetail(movieId) {
		const url = `${API_BASE}/v4/movies/${encodeURIComponent(movieId)}`;
		const headers = { jdSignature: await reBuildSignature() };
		const resp = await gmHttp.get(url, void 0, headers);
		if (!resp.data) failWithToast("获取视频详情失败: " + resp.message);
		const movie = resp.data.movie;
		const imgList = [];
		movie.preview_images.forEach((img) => {
			imgList.push(_updateImgServer(img.large_url));
		});
		return {
			movieId: movie.id,
			actors: movie.actors,
			duration: movie.duration,
			title: movie.origin_title,
			carNum: movie.number,
			score: movie.score,
			releaseDate: movie.release_date,
			watchedCount: movie.watched_count,
			imgList
		};
	}
	async function fetchRelatedCollections(movieId, page = 1, limit = 20) {
		const url = `${API_BASE}/v1/lists/related?movie_id=${encodeURIComponent(movieId)}&page=${page}&limit=${limit}`;
		const headers = { jdSignature: await reBuildSignature() };
		const resp = await gmHttp.get(url, void 0, headers);
		const list = [];
		resp.data.lists.forEach((item) => {
			list.push({
				relatedId: item.id,
				name: item.name,
				movieCount: item.movies_count,
				collectionCount: item.collections_count,
				viewCount: item.views_count,
				createTime: utils.formatDate(item.created_at)
			});
		});
		return list;
	}
	async function fetchPlaybackRanking(period = "daily", filterBy = "high_score") {
		const url = `${API_BASE}/v1/rankings/playback?period=${period}&filter_by=${filterBy}`;
		const headers = { jdSignature: await reBuildSignature() };
		return (await gmHttp.get(url, void 0, headers)).data.movies;
	}
	var APP_AUTH_KEY$1 = "jhs_appAuthorization";
	async function DEFAULT_REQUEST_HEADERS() {
		return {
			"user-agent": "Dart/3.5 (dart:io)",
			"accept-language": "zh-TW",
			host: "jdforrepam.com",
			authorization: "Bearer " + (localStorage.getItem(APP_AUTH_KEY$1) || ""),
			jdsignature: await reBuildSignature()
		};
	}
	async function fetchTopMovies(type = "all", typeValue = "", page = 1, limit = 40) {
		const url = `${API_BASE}/v1/movies/top?start_rank=1&type=${type}&type_value=${typeValue}&ignore_watched=false&page=${page}&limit=${limit}`;
		const headers = await DEFAULT_REQUEST_HEADERS();
		return await gmHttp.get(url, void 0, headers);
	}
	function HitShowMovieItem({ movie }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "item",
			id: String(movie.id),
			children: (0, import_jsx_runtime.jsxs)("a", {
				href: `/v/${movie.id}`,
				className: "box",
				title: movie.origin_title,
				children: [
					(0, import_jsx_runtime.jsx)("div", {
						className: "cover ",
						children: (0, import_jsx_runtime.jsx)("img", {
							loading: "lazy",
							src: _updateImgServer(movie.cover_url || ""),
							alt: ""
						})
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						className: "video-title",
						children: [
							(0, import_jsx_runtime.jsx)("strong", { children: movie.number }),
							" ",
							movie.origin_title
						]
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: "score",
						id: `score_${movie.id}`
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: "meta",
						children: movie.release_date
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						className: "tags has-addons",
						children: [
							movie.has_cnsub ? (0, import_jsx_runtime.jsx)("span", {
								className: "tag is-warning",
								children: "含中字磁鏈"
							}) : movie.magnets_count > 0 ? (0, import_jsx_runtime.jsx)("span", {
								className: "tag is-success",
								children: "含磁鏈"
							}) : (0, import_jsx_runtime.jsx)("span", {
								className: "tag is-info",
								children: "无磁鏈"
							}),
							" ",
							movie.new_magnets ? (0, import_jsx_runtime.jsx)("span", {
								className: "tag is-info",
								children: "今日新種"
							}) : null
						]
					})
				]
			})
		});
	}
	function getStarRating(score) {
		const stars = [];
		const fullStars = Math.floor(score);
		for (let i = 0; i < fullStars; i++) stars.push((0, import_jsx_runtime.jsx)("i", { className: "icon-star" }));
		for (let i = 0; i < 5 - fullStars; i++) stars.push((0, import_jsx_runtime.jsx)("i", { className: "icon-star gray" }));
		return stars;
	}
	function HitShowScore({ score, watchedCount }) {
		return (0, import_jsx_runtime.jsxs)("span", {
			className: "value",
			children: [
				(0, import_jsx_runtime.jsx)("span", {
					className: "score-stars",
					children: getStarRating(score)
				}),
				" \xA0 ",
				score,
				"分,由",
				watchedCount,
				"人評價"
			]
		});
	}
	function HitShowToolBar({ period }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "button-group",
			style: { marginTop: "18px" },
			children: (0, import_jsx_runtime.jsxs)("div", {
				className: "buttons has-addons",
				id: "conditionBox",
				children: [
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${period === "daily" ? "is-info" : ""}`,
						href: "/advanced_search?handlePlayback=1&period=daily",
						children: "日榜"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${period === "weekly" ? "is-info" : ""}`,
						href: "/advanced_search?handlePlayback=1&period=weekly",
						children: "周榜"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${period === "monthly" ? "is-info" : ""}`,
						href: "/advanced_search?handlePlayback=1&period=monthly",
						children: "月榜"
					})
				]
			})
		});
	}
	function RankingContainers() {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
			className: "tool-box",
			style: { marginTop: "10px" }
		}), (0, import_jsx_runtime.jsx)("div", {
			className: "movie-list h cols-4 vcols-8",
			style: { marginTop: "10px" }
		})] });
	}
	var HitShowPlugin = class extends BasePlugin {
		contentBox = $(".section .container");
		getName() {
			return "HitShowPlugin";
		}
		async handle() {
			$("a[href*=\"rankings/playback\"]").on("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				window.location.href = "/advanced_search?handlePlayback=1&period=daily";
			});
			this.handlePlayback().then();
		}
		hookPage() {
			const titleEl = $("h2.section-title");
			titleEl.contents().first().replaceWith("热播");
			titleEl.css("marginBottom", "0");
			$(".empty-message").remove();
			$(".section .container .box").remove();
			$("#sort-toggle-btn").remove();
			this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(RankingContainers, {})));
		}
		async handlePlayback() {
			if (!window.location.href.includes("handlePlayback=1")) return;
			const period = new URLSearchParams(window.location.search).get("period");
			this.toolBar(period);
			this.hookPage();
			const movieListEl = $(".movie-list");
			movieListEl.html("");
			await withLoading(async () => {
				let success = false;
				for (let attempt = 1; attempt <= 3 && !success; attempt++) try {
					const movies = await fetchPlaybackRanking(period ?? "daily");
					const html = this.markDataListHtml(movies);
					movieListEl.html(html);
					this.loadScore(movies);
					success = true;
				} catch (error) {
					if (attempt < 3) {
						clog.error(`获取热播数据失败 (第 ${attempt} 次重试)`, error);
						await new Promise((resolve) => setTimeout(resolve, 1e3));
					} else clog.error("所有重试尝试均失败,无法获取数据。", error);
				}
			});
		}
		toolBar(period) {
			this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(HitShowToolBar, { period })));
		}
		loadScore(movies) {
			if (movies.length === 0) return;
			(async () => {
				const storageKey = "jhs_score_info";
				for (const movie of movies) try {
					const movieId = movie.id;
					if (!$(`#score_${movieId}`).length) return;
					if ($(`#${movieId}`).is(":hidden")) continue;
					const cache = readPersistentMap("scoreInfo", storageKey);
					const cachedHtml = cache[movieId];
					if (cachedHtml) {
						this.appendScoreHtml(movieId, cachedHtml);
						continue;
					}
					while (!document.hasFocus()) await new Promise((resolve) => setTimeout(resolve, 500));
					const detail = await fetchMovieDetail(movieId);
					const score = detail.score;
					const watchedCount = detail.watchedCount;
					const scoreHtml = jsxToString((0, import_jsx_runtime.jsx)(HitShowScore, {
						score,
						watchedCount
					}));
					this.appendScoreHtml(movieId, scoreHtml);
					cache[movieId] = scoreHtml;
					writePersistentEntry("scoreInfo", storageKey, String(movieId), scoreHtml);
					await new Promise((resolve) => setTimeout(resolve, 500));
				} catch (error) {
					clog.error(`🚨 解析评分数据失败 | 编号: ${movie.number}\n`, `错误详情: ${error instanceof Error ? error.message : String(error)}\n`, error instanceof Error && error.stack ? `调用栈:\n${error.stack}` : "");
				}
			})();
		}
		appendScoreHtml(movieId, html) {
			const scoreEl = $(`#score_${movieId}`);
			if (scoreEl.length && scoreEl.html().trim() === "") scoreEl.slideUp(0, function() {
				$(this).html(html).slideDown(500);
			});
		}
		markDataListHtml(movies) {
			let html = "";
			movies.forEach((movie) => {
				html += jsxToString((0, import_jsx_runtime.jsx)(HitShowMovieItem, { movie }));
			});
			return html;
		}
	};
	function LoginDialog() {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				padding: "30px",
				fontFamily: "'Helvetica Neue', Arial, sans-serif"
			},
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					style: { marginBottom: "25px" },
					children: (0, import_jsx_runtime.jsx)("input", {
						type: "text",
						id: "username",
						name: "username",
						style: {
							width: "100%",
							padding: "12px 15px",
							border: "1px solid #e0e0e0",
							borderRadius: "4px",
							boxSizing: "border-box",
							transition: "all 0.3s",
							fontSize: "14px",
							background: "#f9f9f9",
							color: "#333"
						},
						placeholder: "用户名 | 邮箱"
					})
				}),
				(0, import_jsx_runtime.jsx)("div", {
					style: { marginBottom: "15px" },
					children: (0, import_jsx_runtime.jsx)("input", {
						type: "password",
						id: "password",
						name: "password",
						style: {
							width: "100%",
							padding: "12px 15px",
							border: "1px solid #e0e0e0",
							borderRadius: "4px",
							boxSizing: "border-box",
							transition: "all 0.3s",
							fontSize: "14px",
							background: "#f9f9f9",
							color: "#333"
						},
						placeholder: "密码"
					})
				}),
				(0, import_jsx_runtime.jsx)("button", {
					id: "loginBtn",
					style: {
						width: "100%",
						padding: "12px",
						background: "#4a8bfc",
						color: "white",
						border: "none",
						borderRadius: "4px",
						fontSize: "15px",
						cursor: "pointer",
						transition: "background 0.3s"
					},
					children: "登录"
				})
			]
		});
	}
	function Top250ErrorMessage({ message }) {
		return (0, import_jsx_runtime.jsx)("h3", { children: message });
	}
	function Top250LoadError() {
		return (0, import_jsx_runtime.jsx)("h3", { children: "无法加载数据,请稍后再试。" });
	}
	function Top250NavLink() {
		return (0, import_jsx_runtime.jsx)("a", {
			href: "/rankings/top",
			children: (0, import_jsx_runtime.jsx)("span", { children: "Top250" })
		});
	}
	function Top250Pagination({ page }) {
		const hasMore = page >= 5;
		return (0, import_jsx_runtime.jsxs)("nav", {
			className: "pagination",
			children: [
				(0, import_jsx_runtime.jsx)("a", {
					className: `pagination-previous ${page <= 1 ? "do-hide" : ""}`,
					"data-page": page - 1,
					children: "上一頁"
				}),
				(0, import_jsx_runtime.jsx)("a", {
					className: `pagination-next ${hasMore ? "do-hide" : ""}`,
					"data-page": page + 1,
					children: "下一頁"
				}),
				(0, import_jsx_runtime.jsx)("ul", {
					className: "pagination-list",
					children: Array.from({ length: 5 }, (_, i) => i + 1).map((i) => (0, import_jsx_runtime.jsx)("li", { children: (0, import_jsx_runtime.jsx)("a", {
						className: `pagination-link ${page === i ? "is-current" : ""}`,
						"data-page": i,
						children: i
					}) }, i))
				})
			]
		});
	}
	function Top250ToolBar({ handleType, typeValue, hasCnsub, yearButtonsHtml }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "button-group",
			children: [(0, import_jsx_runtime.jsxs)("div", {
				className: "buttons has-addons",
				id: "conditionBox",
				style: { marginBottom: "0!important" },
				children: [
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${handleType === "all" ? "is-info" : ""}`,
						href: `/advanced_search?handleTop=1&handleType=all&type_value=&has_cnsub=${hasCnsub}`,
						children: "全部"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${typeValue === "0" ? "is-info" : ""}`,
						href: `/advanced_search?handleTop=1&handleType=video_type&type_value=0&has_cnsub=${hasCnsub}`,
						children: "有码"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${typeValue === "1" ? "is-info" : ""}`,
						href: `/advanced_search?handleTop=1&handleType=video_type&type_value=1&has_cnsub=${hasCnsub}`,
						children: "无码"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${typeValue === "2" ? "is-info" : ""}`,
						href: `/advanced_search?handleTop=1&handleType=video_type&type_value=2&has_cnsub=${hasCnsub}`,
						children: "欧美"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${typeValue === "3" ? "is-info" : ""}`,
						href: `/advanced_search?handleTop=1&handleType=video_type&type_value=3&has_cnsub=${hasCnsub}`,
						children: "Fc2"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: {
							padding: "18px 18px !important",
							marginLeft: "50px"
						},
						className: `button is-small ${hasCnsub === "1" ? "is-info" : ""}`,
						"data-cnsub-value": "1",
						children: "含中字磁鏈"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: `button is-small ${hasCnsub === "0" ? "is-info" : ""}`,
						"data-cnsub-value": "0",
						children: "无字幕"
					}),
					" ",
					(0, import_jsx_runtime.jsx)("a", {
						style: { padding: "18px 18px !important" },
						className: "button is-small",
						"data-cnsub-value": "",
						children: "重置"
					})
				]
			}), (0, import_jsx_runtime.jsx)("div", {
				className: "buttons has-addons",
				id: "conditionBox",
				dangerouslySetInnerHTML: { __html: yearButtonsHtml }
			})]
		});
	}
	function Top250YearButton({ year, typeValue, hasCnsub }) {
		return (0, import_jsx_runtime.jsx)("a", {
			style: { padding: "18px 18px !important" },
			className: `button is-small ${typeValue === year.toString() ? "is-info" : ""}`,
			href: `/advanced_search?handleTop=1&handleType=year&type_value=${year}&has_cnsub=${hasCnsub}`,
			children: year
		});
	}
	var APP_AUTH_KEY = "jhs_appAuthorization";
	var Top250Plugin = class extends BasePlugin {
		hasCnsub = "";
		contentBox = $(".section .container");
		movies = [];
		getName() {
			return "Top250Plugin";
		}
		async handle() {
			$(".main-tabs ul li:contains(\"猜你喜歡\")").html(jsxToString((0, import_jsx_runtime.jsx)(Top250NavLink, {})));
			$("a[href*=\"rankings/top\"]").on("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				const target = $(event.target);
				const href = (target.is("a") ? target : target.closest("a")).attr("href");
				const queryString = href?.includes("?") ? href.split("?")[1] : href ?? "";
				const params = new URLSearchParams(queryString);
				this.checkLogin(event, params);
			});
			this.handleTop().then();
		}
		hookPage() {
			$("h2.section-title").contents().first().replaceWith("Top250");
			$(".empty-message").remove();
			$(".section .container .box").remove();
			$("#sort-toggle-btn").remove();
			this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(RankingContainers, {})));
			this.renderPagination();
		}
		renderPagination() {
			const params = new URLSearchParams(window.location.search);
			const page = parseInt(params.get("page") ?? "") || 1;
			this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(Top250Pagination, { page })));
			this.contentBox.on("click", ".pagination-link, .pagination-previous, .pagination-next", (event) => {
				event.preventDefault();
				const pageNum = parseInt($(event.currentTarget).data("page"));
				if (!isNaN(pageNum) && pageNum > 0) ((targetPage) => {
					params.set("page", String(targetPage));
					window.history.pushState({}, "", "?" + params.toString());
					window.location.reload();
				})(pageNum);
			});
		}
		async handleTop() {
			if (!window.location.href.includes("handleTop=1")) return;
			const params = new URLSearchParams(window.location.search);
			const handleType = params.get("handleType") || "all";
			const typeValue = params.get("type_value") || "";
			this.hasCnsub = params.get("has_cnsub") || "";
			const page = parseInt(params.get("page") ?? "") || 1;
			this.toolBar(handleType, typeValue, page);
			this.hookPage();
			const movieListEl = $(".movie-list");
			movieListEl.html("");
			await withLoading(async () => {
				let success = false;
				for (let attempt = 1; attempt <= 3 && !success; attempt++) try {
					const response = await fetchTopMovies(handleType, typeValue, page, 50);
					const successFlag = response.success;
					const message = response.message;
					const action = response.action;
					if (successFlag === 1) {
						const movies = response.data.movies;
						if (movies.length === 0) {
							show.error("无数据");
							return;
						}
						this.movies = movies;
						const filteredMovies = movies.filter((movie) => this.hasCnsub === "1" ? movie.has_cnsub : this.hasCnsub !== "0" || !movie.has_cnsub);
						const hitShowPlugin = this.getBean("HitShowPlugin");
						const html = hitShowPlugin.markDataListHtml(filteredMovies);
						movieListEl.html(html);
						hitShowPlugin.loadScore(filteredMovies);
						success = true;
					} else {
						console.error(response);
						movieListEl.html(jsxToString((0, import_jsx_runtime.jsx)(Top250ErrorMessage, { message })));
						show.error(message);
						if (action === "JWTVerificationError") {
							await localStorage.removeItem(APP_AUTH_KEY);
							await this.checkLogin(null, new URLSearchParams(window.location.search));
						}
						success = true;
					}
				} catch (error) {
					if (attempt < 3) {
						clog.error(`获取Top数据失败 (第 ${attempt} 次重试):`, error);
						await new Promise((resolve) => setTimeout(resolve, 1e3));
					} else {
						clog.error("所有重试尝试均失败,无法获取Top数据。", error);
						movieListEl.html(jsxToString((0, import_jsx_runtime.jsx)(Top250LoadError, {})));
					}
				}
			});
		}
		toolBar(handleType, typeValue, page) {
			if (page.toString() === "5") $(".pagination-next").remove();
			$(".pagination-ellipsis").closest("li").remove();
			$(".pagination-list li a").each(function() {
				if (parseInt($(this).text()) > 5) $(this).closest("li").remove();
			});
			let yearButtonsHtml = "";
			for (let year = new Date().getFullYear(); year >= 2008; year--) yearButtonsHtml += jsxToString((0, import_jsx_runtime.jsx)(Top250YearButton, {
				year,
				typeValue,
				hasCnsub: this.hasCnsub
			}));
			this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(Top250ToolBar, {
				handleType,
				typeValue,
				hasCnsub: this.hasCnsub,
				yearButtonsHtml
			})));
			$("a[data-cnsub-value]").on("click", (event) => {
				const cnsubValue = $(event.currentTarget).data("cnsub-value");
				this.hasCnsub = cnsubValue.toString();
				$("a[data-cnsub-value]").removeClass("is-info");
				$(event.currentTarget).addClass("is-info");
				$(".toolbar a.button").not("[data-cnsub-value]").each((_index, el) => {
					const hrefEl = $(el);
					const url = new URL(hrefEl.attr("href") ?? "", window.location.origin);
					url.searchParams.set("has_cnsub", cnsubValue);
					hrefEl.attr("href", url.toString());
				});
				const filteredMovies = this.movies.filter((movie) => this.hasCnsub === "1" ? movie.has_cnsub : this.hasCnsub !== "0" || !movie.has_cnsub);
				const hitShowPlugin = this.getBean("HitShowPlugin");
				const listHtml = hitShowPlugin.markDataListHtml(filteredMovies);
				$(".movie-list").html(listHtml);
				hitShowPlugin.loadScore(filteredMovies);
			});
		}
		async checkLogin(event, params) {
			if (!localStorage.getItem(APP_AUTH_KEY)) {
				show.error("该类别依赖移动端接口,请先完成登录");
				this.openLoginDialog();
				return;
			}
			let handleType = "all";
			let typeValue = "";
			const tValue = params.get("t") || "";
			if (/^y\d+$/.test(tValue)) {
				handleType = "year";
				typeValue = tValue.substring(1);
			} else if (tValue !== "") {
				handleType = "video_type";
				typeValue = tValue;
			}
			const path = `/advanced_search?handleTop=1&handleType=${handleType}&type_value=${typeValue}`;
			if (event && (event.ctrlKey || event.metaKey)) GM_openInTab(window.location.origin + path, { insert: false });
			else window.location.href = path;
		}
		openLoginDialog() {
			layer.open({
				type: 1,
				title: "JavDB",
				closeBtn: 1,
				area: ["360px", "auto"],
				shadeClose: false,
				content: jsxToString((0, import_jsx_runtime.jsx)(LoginDialog, {})),
				success: (_layero, index) => {
					$("#username, #password").on("focus", function() {
						this.style.borderColor = "#4a8bfc";
						this.style.background = "#fff";
					}).on("blur", function() {
						this.style.borderColor = "#e0e0e0";
						this.style.background = "#f9f9f9";
					});
					$("#loginBtn").on("mouseover", function() {
						this.style.background = "#3a7be0";
					}).on("mouseout", function() {
						this.style.background = "#4a8bfc";
					});
					$("#loginBtn").click(function() {
						const username = $("#username").val();
						const password = $("#password").val();
						if (!username || !password) {
							show.error("请输入用户名和密码");
							return;
						}
						withLoading(async () => {
							const response = await (async (username, password) => {
								const url = `${API_BASE}/v1/sessions?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&device_uuid=04b9534d-5118-53de-9f87-2ddded77111e&device_name=iPhone&device_model=iPhone&platform=ios&system_version=17.4&app_version=official&app_version_number=1.9.29&app_channel=official`;
								const headers = {
									"user-agent": "Dart/3.5 (dart:io)",
									"accept-language": "zh-TW",
									"content-type": "multipart/form-data; boundary=--dio-boundary-2210433284",
									jdsignature: await reBuildSignature()
								};
								return await gmHttp.post(url, void 0, headers);
							})(username, password);
							const successFlag = response.success;
							if (successFlag === 0) show.error(response.message);
							else {
								if (successFlag !== 1) {
									clog.error("登录失败", response);
									throw new Error(response.message);
								}
								{
									const token = response.data.token;
									await localStorage.setItem(APP_AUTH_KEY, token);
									show.ok("登录成功");
									layer.close(index);
									window.location.href = "/advanced_search?handleTop=1&period=daily";
								}
							}
						}).catch((error) => {
							clog.error("登录异常:", error);
							show.error(error instanceof Error ? error.message : String(error));
						});
					});
				}
			});
		}
	};
	function NavOtherDropdown() {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "navbar-item has-dropdown is-hoverable",
			children: [(0, import_jsx_runtime.jsx)("a", {
				className: "navbar-link",
				children: "其它"
			}), (0, import_jsx_runtime.jsxs)("div", {
				className: "navbar-dropdown is-boxed",
				children: [(0, import_jsx_runtime.jsx)("a", {
					className: "navbar-item",
					href: "/feedbacks/new",
					target: "_blank",
					children: "反饍"
				}), (0, import_jsx_runtime.jsx)("a", {
					className: "navbar-item",
					rel: "nofollow noopener",
					target: "_blank",
					href: "https://theporndude.com/zh",
					children: "ThePornDude"
				})]
			})]
		});
	}
	function NavSearchBox() {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "navbar-menu",
			id: "search-box",
			children: (0, import_jsx_runtime.jsxs)("div", {
				className: "navbar-start",
				style: {
					display: "flex",
					alignItems: "center",
					gap: "5px"
				},
				children: [
					(0, import_jsx_runtime.jsxs)("select", {
						id: "search-type",
						style: {
							padding: "8px 12px",
							border: "1px solid #555",
							borderRadius: "4px",
							backgroundColor: "#333",
							color: "#eee",
							fontSize: "14px",
							outline: "none"
						},
						children: [
							(0, import_jsx_runtime.jsx)("option", {
								value: "all",
								children: "影片"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "actor",
								children: "演員"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "series",
								children: "系列"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "maker",
								children: "片商"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "director",
								children: "導演"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "code",
								children: "番號"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "list",
								children: "清單"
							})
						]
					}),
					(0, import_jsx_runtime.jsx)("input", {
						id: "search-keyword",
						type: "text",
						placeholder: "輸入影片番號,演員名等關鍵字進行檢索",
						style: {
							padding: "8px 12px",
							border: "1px solid #555",
							borderRadius: "4px",
							flexGrow: "1",
							fontSize: "14px",
							backgroundColor: "#333",
							color: "#eee",
							outline: "none"
						}
					}),
					(0, import_jsx_runtime.jsx)("a", {
						href: "/advanced_search?noFold=1",
						title: "進階檢索",
						style: {
							padding: "6px 12px",
							backgroundColor: "#444",
							borderRadius: "4px",
							textDecoration: "none",
							color: "#ddd",
							fontSize: "14px",
							border: "1px solid #555"
						},
						children: (0, import_jsx_runtime.jsx)("span", { children: "..." })
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "search-img-btn",
						title: "以图识图",
						style: {
							padding: "6px 16px",
							backgroundColor: "#444",
							color: "#fff",
							borderRadius: "4px",
							textDecoration: "none",
							fontWeight: "500",
							cursor: "pointer",
							border: "1px solid #555"
						},
						children: "识图"
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "search-btn",
						style: {
							padding: "6px 16px",
							backgroundColor: "#444",
							color: "#fff",
							borderRadius: "4px",
							textDecoration: "none",
							fontWeight: "500",
							cursor: "pointer",
							border: "1px solid #555"
						},
						children: "检索"
					})
				]
			})
		});
	}
	var nav_bar_plugin_default = ".highlight-red {\n    /* 核心要求:高亮红色文本 */\n    color: red !important;\n\n    /* 建议:增加字体加粗,效果更明显 */\n    font-weight: bold;\n\n    /* 建议:增加背景色,效果更突出 */\n\n    /* background-color: yellow; */\n}\n";
	var NavBarPlugin = class extends BasePlugin {
		getName() {
			return "NavBarPlugin";
		}
		async initCss() {
			return nav_bar_plugin_default;
		}
		async handle() {
			this.mergeNav();
			this.hookSearch();
			this.hookOldSearch();
			this.toggleOtherNavItem();
			$(window).resize(this.toggleOtherNavItem);
			if (window.location.href.includes("/search")) {
				const params = new URLSearchParams(window.location.search);
				const keyword = params.get("q");
				const searchType = params.get("f");
				$("#search-keyword").val(keyword ?? "");
				if (searchType) $("#search-type").val(searchType);
				if (keyword) this.highlightKeyword(keyword);
			}
		}
		highlightKeyword(keyword) {
			const trimmed = keyword.trim();
			if (!trimmed) return;
			const lower = trimmed.toLowerCase();
			$(".video-title strong, .actor-box strong").each(function() {
				const el = $(this);
				if (el.text().toLowerCase().includes(lower)) el.addClass("highlight-red");
			});
		}
		hookSearch() {
			$("#navbar-menu-hero").after(jsxToString((0, import_jsx_runtime.jsx)(NavSearchBox, {})));
			const $keyword = $("#search-keyword");
			if (!featureFlags.navBarNoPaste) $keyword.on("paste", (event) => {
				const items = event.originalEvent.clipboardData.items;
				for (let index = 0; index < items.length; index++) if (items[index].type.indexOf("image") !== -1) return;
				setTimeout(() => {
					$("#search-btn").click();
				}, 0);
			});
			$keyword.on("keypress", (event) => {
				if (event.key === "Enter") setTimeout(() => {
					$("#search-btn").click();
				}, 0);
			});
			$("#search-btn").on("click", (_event) => {
				const keyword = String($("#search-keyword").val() ?? "");
				const searchType = String($("#search-type option:selected").val() ?? "");
				if (keyword !== "") {
					if (window.location.href.includes("/search")) window.location.href = "/search?q=" + encodeURIComponent(keyword) + "&f=" + encodeURIComponent(searchType);
					else window.open("/search?q=" + encodeURIComponent(keyword) + "&f=" + encodeURIComponent(searchType), "_blank", "noopener,noreferrer");
				}
			});
			if (featureFlags.imageRecognitionPlugin) $("#search-img-btn").on("click", (e) => {
				e.preventDefault();
				this.getBean("ImageRecognitionPlugin")?.open?.();
			});
			else $("#search-img-btn").hide();
		}
		hookOldSearch() {
			const searchImageEl = document.querySelector(".search-image");
			if (!searchImageEl) return;
			const clonedEl = searchImageEl.cloneNode(true);
			searchImageEl.parentNode.replaceChild(clonedEl, searchImageEl);
			$("#button-search-image").attr("data-tooltip", "以图识图");
			if (featureFlags.imageRecognitionPlugin) $(".search-image").on("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				this.getBean("ImageRecognitionPlugin")?.open?.();
			});
		}
		mergeNav() {
			$("a[href*=\"/feedbacks/new\"]").remove();
			$("a[href*=\"theporndude.com\"]").remove();
			$("a.navbar-link[href=\"/makers\"]").parent().after(jsxToString((0, import_jsx_runtime.jsx)(NavOtherDropdown, {})));
		}
		toggleOtherNavItem() {
			const searchBox = $("#search-box");
			const searchBarContainer = $("#search-bar-container");
			if (($(window).width() ?? 0) > 1023) {
				searchBox.show();
				searchBarContainer.hide();
			}
		}
	};
	var AsyncTaskQueue = class {
		concurrency;
		running = 0;
		pending = [];
		idleWaiters = [];
		constructor(concurrency = 1) {
			this.concurrency = Math.max(1, Math.floor(Number(concurrency)) || 1);
		}
		setConcurrency(concurrency) {
			this.concurrency = Math.max(1, Math.floor(Number(concurrency)) || 1);
			this.pump();
		}
		getConcurrency() {
			return this.concurrency;
		}
		addTask(task) {
			this.pending.push(task);
			this.pump();
		}
		async waitAllFinished() {
			if (this.running === 0 && this.pending.length === 0) return;
			return new Promise((resolve) => {
				this.idleWaiters.push(resolve);
				this.pump();
			});
		}
		pump() {
			while (this.running < this.concurrency && this.pending.length > 0) {
				const task = this.pending.shift();
				this.running++;
				Promise.resolve().then(() => task()).catch((err) => {
					clog.error("执行异步队列任务失败:", err);
				}).finally(() => {
					this.running--;
					this.pump();
					this.notifyIdle();
				});
			}
			this.notifyIdle();
		}
		notifyIdle() {
			if (this.running !== 0 || this.pending.length !== 0) return;
			const waiters = this.idleWaiters.splice(0);
			for (const w of waiters) w();
		}
	};
	var TaskSupervisor = class {
		controller = new AbortController();
		entries = [];
		_aborted = false;
		get aborted() {
			return this._aborted;
		}
		get signal() {
			return this.controller.signal;
		}
		setTimeout(fn, ms) {
			if (this._aborted) return -1;
			const id = window.setTimeout(() => {
				this.removeEntry(id, "timer");
				fn();
			}, ms);
			this.entries.push({
				type: "timer",
				cleanup: () => window.clearTimeout(id)
			});
			return id;
		}
		setInterval(fn, ms) {
			if (this._aborted) return -1;
			const id = window.setInterval(fn, ms);
			this.entries.push({
				type: "interval",
				cleanup: () => window.clearInterval(id)
			});
			return id;
		}
		observe(target, callback, options) {
			const observer = new MutationObserver(callback);
			if (!this._aborted) observer.observe(target, options);
			this.entries.push({
				type: "observer",
				cleanup: () => observer.disconnect()
			});
			return observer;
		}
		addEventListener(target, type, listener, options) {
			if (this._aborted) return;
			const opts = {
				...options,
				signal: this.controller.signal
			};
			target.addEventListener(type, listener, opts);
			this.entries.push({
				type: "listener",
				cleanup: () => target.removeEventListener(type, listener, options)
			});
		}
		abort() {
			if (this._aborted) return;
			this._aborted = true;
			this.controller.abort();
			for (const entry of this.entries) try {
				entry.cleanup();
			} catch {}
			this.entries.length = 0;
		}
		removeEntry(id, type) {
			const index = this.entries.findIndex((e) => e.type === type && e.cleanup.toString().includes(String(id)));
			if (index !== -1) this.entries.splice(index, 1);
		}
	};
	var ENABLED_SITES_KEY = "jhs_enabled_sites";
	function getEnabledSites(plugin) {
		const stored = localStorage.getItem(ENABLED_SITES_KEY);
		if (stored) try {
			return JSON.parse(stored);
		} catch {}
		return plugin.siteConfigs.map((siteConfig) => siteConfig.id);
	}
	function saveEnabledSites(siteIds) {
		localStorage.setItem(ENABLED_SITES_KEY, JSON.stringify(siteIds));
	}
	var PRELOAD_CACHE_MAX = 5e3;
	function evictStaleCache(cache) {
		const keys = Object.keys(cache);
		if (keys.length > 5e3) {
			const tsOf = (k) => {
				return cache[k]?.ts ?? 0;
			};
			const sorted = keys.filter((k) => tsOf(k) !== 0).sort((a, b) => tsOf(a) - tsOf(b));
			const toRemove = [...keys.filter((k) => tsOf(k) === 0), ...sorted].slice(0, keys.length - PRELOAD_CACHE_MAX);
			for (const k of toRemove) delete cache[k];
		}
	}
	function isCacheEntryValid(entry, preloadCacheTTLDays) {
		if (!entry) return false;
		if (!preloadCacheTTLDays) return true;
		const ts = entry.ts;
		if (!ts) return true;
		return Date.now() - ts < preloadCacheTTLDays * 864e5;
	}
	function getItemPreloadStatus(item) {
		const badge = item.querySelector(".jhs-preload-status .jhs-ps-badge");
		if (!badge) return "none";
		for (const key of [
			"queued",
			"requesting",
			"success",
			"failed"
		]) if (badge.classList.contains(`jhs-ps-${key}`)) return key;
		return "none";
	}
	var PRELOAD_CHIPS = [
		{
			value: "queued",
			label: "排队中"
		},
		{
			value: "requesting",
			label: "请求中"
		},
		{
			value: "success",
			label: "成功匹配"
		},
		{
			value: "failed",
			label: "匹配失败"
		}
	];
	var PRELOAD_HIDDEN_ATTR = "data-preload-hidden";
	var OTHER_HIDDEN_ATTRS = [
		"data-hide",
		"data-lrs-hidden",
		"data-status-tag-hidden",
		"data-video-lists-tag-hidden"
	];
	function initFilterBar(plugin) {
		if (!plugin.preloadStatusEnabled) return;
		if (tryBuildFilterBar(plugin)) return;
		let retries = 0;
		plugin.filterBarObserver = plugin.supervisor.observe(document.body, () => {
			if (document.querySelector(".preload-filter-bar")) {
				plugin.filterBarObserver?.disconnect();
				plugin.filterBarObserver = null;
				return;
			}
			if (tryBuildFilterBar(plugin) || ++retries > 50) {
				plugin.filterBarObserver?.disconnect();
				plugin.filterBarObserver = null;
			}
		}, {
			childList: true,
			subtree: true
		});
		plugin.supervisor.setTimeout(() => {
			plugin.filterBarObserver?.disconnect();
			plugin.filterBarObserver = null;
		}, 1e4);
	}
	function tryBuildFilterBar(plugin) {
		if (document.querySelector(".preload-filter-bar")) return true;
		const target = findFilterMountTarget();
		if (!target) return false;
		buildFilterBar$1(plugin, target);
		return true;
	}
	function findFilterMountTarget() {
		const stf = document.querySelector(".status-tag-filter-bar");
		if (stf) return stf;
		const vlt = document.querySelector(".jhs-vlt-filter-bar");
		if (vlt) return vlt;
		if (/^\/actors\//.test(window.location.pathname)) {
			const actorTags = document.querySelector("body > section > div > div.actor-tags.tags");
			if (actorTags) return actorTags;
		} else {
			const tabsBoxed = document.querySelector("body > section > div > div.tabs.is-boxed");
			if (tabsBoxed) return tabsBoxed;
		}
		const section = document.querySelector("body > section > div > div");
		if (section && section.firstElementChild) {
			if (!section.firstElementChild.matches(".preload-filter-bar, .status-tag-filter-bar, .jhs-vlt-filter-bar")) return section.firstElementChild;
		}
		const container = document.querySelector("body > section > div");
		if (container && container.firstElementChild) {
			if (!container.firstElementChild.matches(".preload-filter-bar, .status-tag-filter-bar, .jhs-vlt-filter-bar")) return container.firstElementChild;
		}
		return null;
	}
	function buildFilterBar$1(_plugin, mountTarget) {
		const bar = document.createElement("div");
		bar.className = "preload-filter-bar";
		const label = document.createElement("span");
		label.className = "preload-filter-label";
		label.textContent = "预加载:";
		bar.appendChild(label);
		const chipsContainer = document.createElement("div");
		chipsContainer.className = "preload-filter-chips";
		bar.appendChild(chipsContainer);
		const refreshChips = () => {
			const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".preload-filter-chip.active")).map((c) => c.dataset.value || ""));
			chipsContainer.innerHTML = "";
			const counts = collectPreloadCounts();
			for (const chip of PRELOAD_CHIPS) {
				const el = document.createElement("span");
				el.className = "preload-filter-chip";
				el.dataset.value = chip.value;
				const dot = document.createElement("span");
				dot.className = "pf-dot";
				el.appendChild(dot);
				el.appendChild(document.createTextNode(`${chip.label} ${counts[chip.value] ?? 0}`));
				if (activeValues.has(chip.value)) el.classList.add("active");
				el.addEventListener("click", () => {
					el.classList.toggle("active");
					applyPreloadFilter();
				});
				chipsContainer.appendChild(el);
			}
		};
		refreshChips();
		if (mountTarget.matches(".status-tag-filter-bar, .jhs-vlt-filter-bar, .actor-tags.tags, .tabs.is-boxed")) mountTarget.insertAdjacentElement("afterend", bar);
		else mountTarget.insertAdjacentElement("beforebegin", bar);
		bar._refreshChips = refreshChips;
	}
	function collectPreloadCounts() {
		const counts = {
			queued: 0,
			requesting: 0,
			success: 0,
			failed: 0
		};
		document.querySelectorAll(".movie-list .item").forEach((item) => {
			if (OTHER_HIDDEN_ATTRS.some((a) => item.hasAttribute(a))) return;
			const st = getItemPreloadStatus(item);
			if (st in counts) counts[st]++;
		});
		return counts;
	}
	function applyPreloadFilter() {
		const activeChips = document.querySelectorAll(".preload-filter-chip.active");
		const selected = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
		const HIDDEN = PRELOAD_HIDDEN_ATTR;
		if (selected.size === 0) {
			document.querySelectorAll(`.item[${HIDDEN}]`).forEach((item) => {
				const el = item;
				if (OTHER_HIDDEN_ATTRS.some((a) => el.hasAttribute(a))) return;
				el.removeAttribute(HIDDEN);
				el.style.display = "";
			});
			return;
		}
		document.querySelectorAll(".movie-list .item").forEach((item) => {
			const el = item;
			if (OTHER_HIDDEN_ATTRS.some((a) => el.hasAttribute(a))) return;
			if (el.style.display === "none" && !el.hasAttribute(HIDDEN)) return;
			const st = getItemPreloadStatus(el);
			if (selected.has(st)) {
				el.removeAttribute(HIDDEN);
				el.style.display = "";
			} else {
				el.setAttribute(HIDDEN, "");
				el.style.display = "none";
			}
		});
	}
	function refreshFilterBar(plugin) {
		if (!plugin.preloadStatusEnabled) return;
		clearTimeout(plugin.filterRefreshDebounce);
		plugin.filterRefreshDebounce = plugin.supervisor.setTimeout(() => {
			const bar = document.querySelector(".preload-filter-bar");
			if (bar && bar._refreshChips) {
				bar._refreshChips();
				applyPreloadFilter();
			}
		}, 150);
	}
	function ensureFilterBar(plugin) {
		if (!plugin.preloadStatusEnabled) return;
		if (!document.querySelector(".preload-filter-bar")) tryBuildFilterBar(plugin);
		const bar = document.querySelector(".preload-filter-bar");
		if (!bar) return;
		const stf = document.querySelector(".status-tag-filter-bar");
		if (stf && bar.previousElementSibling !== stf) {
			stf.insertAdjacentElement("afterend", bar);
			refreshFilterBar(plugin);
			return;
		}
		const vlt = document.querySelector(".jhs-vlt-filter-bar");
		if (!stf && vlt && bar.previousElementSibling !== vlt) {
			vlt.insertAdjacentElement("afterend", bar);
			refreshFilterBar(plugin);
		}
	}
	var PRELOAD_STATUS_MAP = {
		queued: {
			cls: "jhs-ps-queued",
			text: "排队中"
		},
		requesting: {
			cls: "jhs-ps-requesting",
			text: "请求中"
		},
		success: {
			cls: "jhs-ps-success",
			text: "成功匹配"
		},
		failed: {
			cls: "jhs-ps-failed",
			text: "匹配失败"
		}
	};
	function PreloadStatusBar() {
		return (0, import_jsx_runtime.jsx)("div", { className: "jhs-preload-status-bar" });
	}
	function PreloadStatusBadge({ siteId, siteName, status }) {
		const { cls, text } = PRELOAD_STATUS_MAP[status];
		return (0, import_jsx_runtime.jsxs)("span", {
			className: "jhs-preload-status",
			"data-site-id": siteId,
			children: [(0, import_jsx_runtime.jsx)("span", {
				className: "jhs-ps-name",
				children: siteName
			}), (0, import_jsx_runtime.jsx)("span", {
				className: `jhs-ps-badge ${cls}`,
				children: text
			})]
		});
	}
	var PRELOAD_LOG = "[OtherSite 预加载]";
	async function preloadListPage(plugin) {
		if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") {
			console.log(`${PRELOAD_LOG} 已禁用,跳过预加载`);
			return;
		}
		const listPagePlugin = plugin.getBean("ListPagePlugin");
		if (!listPagePlugin) {
			console.log(`${PRELOAD_LOG} ListPagePlugin 未就绪,跳过`);
			return;
		}
		const enabledSiteIds = getEnabledSites(plugin);
		const $items = $(".movie-list .item");
		if ($items.length === 0) {
			console.log(`${PRELOAD_LOG} 无 .item,跳过`);
			return;
		}
		const cache = readPersistentMap("otherSite", "jhs_other_site");
		let cachedCount = 0;
		let skippedHiddenCount = 0;
		let preloadCount = 0;
		$items.each((_index, itemEl) => {
			const $item = $(itemEl);
			if ($item.attr("data-hide") === "yes") {
				skippedHiddenCount++;
				return;
			}
			let carNum;
			try {
				carNum = listPagePlugin.findCarNumAndHref($item).carNum;
			} catch {
				return;
			}
			if (!carNum) return;
			for (const siteConfig of plugin.siteConfigs) {
				if (!enabledSiteIds.includes(siteConfig.id)) continue;
				if (siteConfig.condition && siteConfig.condition(siteConfig.sourceCarNum) === false) continue;
				if (plugin.blockedSiteIds.has(siteConfig.id)) continue;
				if (siteConfig.initUrl) continue;
				const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
				if (isCacheEntryValid(cache[cacheKey], plugin.preloadCacheTTLDays)) {
					cachedCount++;
					continue;
				}
				updatePreloadStatus(plugin, $item, siteConfig.id, "queued");
				plugin.preloadQueue.addTask(async () => {
					if (plugin.blockedSiteIds.has(siteConfig.id)) {
						updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
						return;
					}
					updatePreloadStatus(plugin, $item, siteConfig.id, "requesting");
					await preloadSite(plugin, carNum, siteConfig, $item);
				});
				preloadCount++;
			}
		});
		console.log(`${PRELOAD_LOG} ${$items.length} 个 item(屏蔽 ${skippedHiddenCount})| 已缓存 ${cachedCount} | 入队 ${preloadCount} 个任务` + (plugin.blockedSiteIds.size > 0 ? ` | 被拦截站点: ${[...plugin.blockedSiteIds].map((id) => id.replace("Btn", "")).join(", ")}` : ""));
	}
	async function preloadSite(plugin, carNum, siteConfig, $item) {
		const dedupeKey = `${carNum}_${siteConfig.id}`;
		const existing = plugin.inflightPreloads.get(dedupeKey);
		if (existing) return existing;
		const promise = doPreloadSite(plugin, carNum, siteConfig, $item);
		plugin.inflightPreloads.set(dedupeKey, promise);
		try {
			await promise;
		} finally {
			plugin.inflightPreloads.delete(dedupeKey);
		}
	}
	async function doPreloadSite(plugin, carNum, siteConfig, $item) {
		try {
			const baseUrl = await siteConfig.getBaseUrl();
			const searchUrl = siteConfig.searchPath(baseUrl, carNum);
			const htmlContent = await gmHttp.get(searchUrl, void 0, siteConfig.headers, true);
			const $dom = utils.htmlTo$dom(String(htmlContent));
			const detailHrefs = [];
			$dom.find(siteConfig.itemSelector).each((_index, element) => {
				const itemEl = $(element);
				if (!siteConfig.findCarNumOrTitle(itemEl).toLowerCase().includes(carNum.toLowerCase())) return;
				let href = siteConfig.getDetailPageHref(itemEl, baseUrl, carNum);
				if (!href) throw new Error("解析href失败");
				if (!href.includes("http")) href = baseUrl + (href.startsWith("/") ? href : "/" + href);
				detailHrefs.push(href);
			});
			let resultData = null;
			if (detailHrefs.length === 1) resultData = {
				type: "single",
				url: detailHrefs[0]
			};
			else if (detailHrefs.length > 1) resultData = {
				type: "multiple",
				url: searchUrl
			};
			if (resultData) {
				updatePreloadStatus(plugin, $item, siteConfig.id, "success");
				const storageKey = "jhs_other_site";
				const cache = readPersistentMap("otherSite", storageKey);
				const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
				cache[cacheKey] = {
					...resultData,
					ts: Date.now()
				};
				evictStaleCache(cache);
				replacePersistentMap("otherSite", storageKey, cache);
				console.log(`${PRELOAD_LOG} ✓ ${carNum} ${siteConfig.id.replace("Btn", "")} ${resultData.type === "single" ? "命中" : "多结果"}`);
			} else {
				updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
				console.log(`${PRELOAD_LOG} ✗ ${carNum} ${siteConfig.id.replace("Btn", "")} 未命中`);
			}
		} catch (error) {
			const siteName = siteConfig.id.replace("Btn", "");
			const errorMsg = String(error);
			if (errorMsg.includes("Just a moment") || errorMsg.includes("403")) {
				plugin.blockedSiteIds.add(siteConfig.id);
				updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
				console.warn(`${PRELOAD_LOG} ⚠ ${siteName} 被 Cloudflare 拦截,跳过本轮剩余任务`);
				return;
			}
			if (errorMsg.includes("重定向")) {
				updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
				console.warn(`${PRELOAD_LOG} ⚠ ${siteName} 域名失效(被重定向): ${errorMsg.slice(0, 60)}`);
				return;
			}
			updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
			console.warn(`${PRELOAD_LOG} ✗ ${carNum} ${siteName} 失败: ${errorMsg.slice(0, 60)}`);
		}
	}
	function updatePreloadStatus(plugin, $item, siteId, status) {
		if (!plugin.preloadStatusEnabled) return;
		const $videoTitle = $item.find(".video-title").first();
		if ($videoTitle.length === 0) return;
		if ($item.find(".jhs-preload-status-bar").length === 0) $videoTitle.after(jsxToString((0, import_jsx_runtime.jsx)(PreloadStatusBar, {})));
		const $bar = $item.find(".jhs-preload-status-bar");
		const $badge = $bar.find(`[data-site-id="${siteId}"]`);
		if ($badge.length === 0) {
			$bar.append(jsxToString((0, import_jsx_runtime.jsx)(PreloadStatusBadge, {
				siteId,
				siteName: siteId.replace("Btn", ""),
				status
			})));
			return;
		}
		const { cls, text } = PRELOAD_STATUS_MAP[status];
		$badge.find(".jhs-ps-badge").attr("class", `jhs-ps-badge ${cls}`).text(text);
		refreshFilterBar(plugin);
	}
	function getPreloadableSites(plugin) {
		const enabled = getEnabledSites(plugin);
		return plugin.siteConfigs.filter((s) => enabled.includes(s.id) && !s.initUrl);
	}
	function syncAllBadges(plugin) {
		if (!plugin.preloadStatusEnabled) return;
		const listPagePlugin = plugin.getBean("ListPagePlugin");
		if (!listPagePlugin) return;
		const sites = getPreloadableSites(plugin);
		if (sites.length === 0) return;
		const cache = readPersistentMap("otherSite", "jhs_other_site");
		$(".movie-list .item").each((_index, el) => {
			const $item = $(el);
			if ($item.attr("data-hide") === "yes") return;
			let carNum;
			try {
				carNum = listPagePlugin.findCarNumAndHref($item).carNum;
			} catch {
				return;
			}
			if (!carNum) return;
			for (const site of sites) {
				const cacheKey = carNum + "_" + site.id.replace("Btn", "");
				if (isCacheEntryValid(cache[cacheKey], plugin.preloadCacheTTLDays)) {
					const $badge = $item.find(`[data-site-id="${site.id}"] .jhs-ps-badge`);
					if (!$badge.length || !$badge.hasClass("jhs-ps-success")) updatePreloadStatus(plugin, $item, site.id, "success");
				} else if (!$item.find(`[data-site-id="${site.id}"]`).length) updatePreloadStatus(plugin, $item, site.id, "queued");
			}
		});
	}
	function startPreloadObserver(plugin) {
		const containerEl = document.querySelector(".movie-list");
		if (!containerEl) return;
		plugin.supervisor.observe(containerEl, () => {
			syncAllBadges(plugin);
			ensureFilterBar(plugin);
			refreshFilterBar(plugin);
			clearTimeout(plugin.preloadDebounce);
			plugin.preloadDebounce = plugin.supervisor.setTimeout(() => {
				preloadListPage(plugin).then();
			}, plugin.preloadDebounceMs);
		}, { childList: true });
	}
	function SiteResultTag() {
		return (0, import_jsx_runtime.jsx)("span", {
			className: "site-tag",
			style: { top: "-15px" },
			children: "多结果"
		});
	}
	async function probeSite(plugin, carNum, siteConfig) {
		const buttonEl = $(`#${siteConfig.id}`);
		if (siteConfig.initUrl) {
			buttonEl.attr("href", await siteConfig.initUrl(carNum));
			buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
		}
		if (siteConfig.noHandle && siteConfig.noHandle === true) {
			const dmmCachedResult = readPersistentMap("otherSiteDmm", "jhs_other_site_dmm")[carNum];
			if (dmmCachedResult) {
				if (dmmCachedResult.type === "single") {
					buttonEl.attr("href", dmmCachedResult.url);
					buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				} else if (dmmCachedResult.type === "multiple") {
					buttonEl.attr("href", dmmCachedResult.url);
					buttonEl.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
					buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				}
			}
		} else try {
			if (buttonEl.attr("href")) return;
			if (utils.isHidden(buttonEl)) return;
			const storageKey = "jhs_other_site";
			const cache = readPersistentMap("otherSite", storageKey);
			const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
			const cachedResult = cache[cacheKey];
			if (isCacheEntryValid(cachedResult, plugin.preloadCacheTTLDays)) {
				if (cachedResult.type === "single") {
					buttonEl.attr("href", cachedResult.url);
					buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				} else if (cachedResult.type === "multiple") {
					buttonEl.attr("href", cachedResult.url);
					buttonEl.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
					buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				}
				return;
			}
			const baseUrl = await siteConfig.getBaseUrl();
			const searchUrl = siteConfig.searchPath(baseUrl, carNum);
			buttonEl.attr("href", searchUrl);
			const htmlContent = await gmHttp.get(searchUrl, void 0, siteConfig.headers, true);
			const $dom = utils.htmlTo$dom(String(htmlContent));
			const detailHrefs = [];
			$dom.find(siteConfig.itemSelector).each((_index, element) => {
				const itemEl = $(element);
				if (!siteConfig.findCarNumOrTitle(itemEl).toLowerCase().includes(carNum.toLowerCase())) return;
				let href = siteConfig.getDetailPageHref(itemEl, baseUrl, carNum);
				if (!href) throw new Error("解析href失败");
				if (!href.includes("http")) href = baseUrl + (href.startsWith("/") ? href : "/" + href);
				detailHrefs.push(href);
			});
			let tagHtml = "";
			let resultData = null;
			if (detailHrefs.length === 1) {
				const detailHref = detailHrefs[0];
				buttonEl.attr("href", detailHref);
				buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				resultData = {
					type: "single",
					url: detailHref
				};
			} else if (detailHrefs.length > 1) {
				buttonEl.attr("href", searchUrl);
				tagHtml += jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {}));
				buttonEl.css("backgroundColor", plugin.okBackgroundColor);
				resultData = {
					type: "multiple",
					url: searchUrl
				};
			} else {
				buttonEl.attr("href", searchUrl);
				buttonEl.attr("title", "未查询到, 点击前往搜索页");
				buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
			}
			if (resultData) new AsyncTaskQueue().addTask(() => {
				const cache = readPersistentMap("otherSite", storageKey);
				cache[cacheKey] = {
					...resultData,
					ts: Date.now()
				};
				evictStaleCache(cache);
				replacePersistentMap("otherSite", storageKey, cache);
			});
			if (tagHtml) buttonEl.append(tagHtml);
		} catch (error) {
			const errorMsg = String(error);
			const siteName = siteConfig.id.replace("Btn", "");
			if (errorMsg.includes("Just a moment")) {
				buttonEl.attr("title", "请求失败:Cloudflare 安全检查。");
				buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
				clog.warn(`检测第三方资源失败, ${siteName} 需Cloudflare安全检查`);
			} else if (errorMsg.includes("重定向")) {
				buttonEl.attr("title", "域名失效");
				buttonEl.css("backgroundColor", plugin.domainErrorBackgroundColor);
				clog.warn(`检测第三方资源失败, ${siteName} 域名被重定向`);
			} else if (errorMsg.includes("404 Page Not Found")) {
				buttonEl.attr("title", "未查询到, 点击前往搜索页");
				buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
			} else {
				clog.error(error);
				buttonEl.attr("title", "请求失败。");
				buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
				clog.warn(`检测第三方资源失败, ${siteName}`);
			}
		}
	}
	function OtherSiteBox({ siteButtonsHtml, isJavdbSite }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "otherSiteBox",
			className: "panel-block",
			style: {
				marginTop: isJavdbSite ? "8px" : "10px",
				fontSize: "13px",
				userSelect: "none"
			},
			children: (0, import_jsx_runtime.jsx)("div", {
				style: {
					display: "flex",
					gap: "5px",
					flexWrap: "wrap"
				},
				dangerouslySetInnerHTML: { __html: siteButtonsHtml }
			})
		});
	}
	function OtherSiteBtn({ id, enabled }) {
		return (0, import_jsx_runtime.jsx)("a", {
			target: "_blank",
			className: "site-btn",
			style: enabled ? void 0 : { display: "none" },
			id,
			children: (0, import_jsx_runtime.jsx)("span", { children: id.replace("Btn", "") })
		});
	}
	function OtherSiteCheckbox({ id, isEnabled, isJavdbSite }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				marginRight: "15px",
				display: "flex",
				alignItems: isJavdbSite ? "center" : "flex-start"
			},
			children: [(0, import_jsx_runtime.jsx)("input", {
				type: "checkbox",
				id: `checkbox-${id}`,
				"data-site-id": id,
				checked: isEnabled,
				style: {
					marginRight: "8px",
					cursor: "pointer"
				}
			}), (0, import_jsx_runtime.jsx)("label", {
				for: `checkbox-${id}`,
				style: {
					color: "#333",
					fontWeight: "500",
					cursor: "pointer"
				},
				children: id.replace("Btn", "")
			})]
		});
	}
	var other_site_plugin_default = ".site-btn {\n    position: relative !important;\n    min-width: 80px;\n    display: inline-block;\n    padding: 5px 10px;\n    color: white !important;\n    background-color: #938585;\n    text-decoration: none;\n    border-radius: 4px;\n    text-align: center;\n    margin-bottom: 5px;\n}\n\n.site-btn:hover {\n    color: white;\n    transform: translateY(-2px);\n    box-shadow: 0 2px 5px rgb(0, 0, 0, 0.1);\n}\n\n.site-tag {\n    position: absolute;\n    top: -15px;\n    right: 0;\n    background-color: #ffc107;\n    color: #333;\n    font-size: 12px;\n    padding: 2px 6px;\n    border-radius: 4px;\n}\n";
	var preload_status_badge_default = "/**\n * 预加载状态徽标 + 筛选栏样式(OtherSitePlugin 列表页预加载实时状态)。\n *\n * 两部分:\n * 1. .jhs-preload-status-bar / .jhs-preload-status —— .item 卡片内 .video-title\n *    下方每站点状态徽标(排队中/请求中/成功匹配/匹配失败)。\n * 2. .preload-filter-bar / .preload-filter-chip —— 筛选栏(与 .status-tag-filter-bar\n *    同行挂载),按状态过滤卡片,实时刷新计数。\n *\n * 样式风格与 .status-tag-filter-bar 对齐(药丸芯片、border、active 配色),\n * 确保多个筛选栏视觉统一。\n */\n\n/* ========== 卡片内状态徽标 ========== */\n.jhs-preload-status-bar {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 4px;\n    margin-top: 4px;\n    line-height: 1.2;\n}\n\n.jhs-preload-status {\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n    padding: 1px 7px;\n    border-radius: 9px;\n    background-color: rgb(0, 0, 0, 0.05);\n    font-size: 11px;\n    line-height: 1.5;\n    white-space: nowrap;\n}\n\n.jhs-ps-name {\n    font-weight: 600;\n    color: #9b9b9b;\n}\n\n.jhs-ps-badge {\n    display: inline-flex;\n    align-items: center;\n    gap: 3px;\n    font-weight: 600;\n}\n\n.jhs-ps-queued {\n    color: #8a8a8a;\n}\n\n.jhs-ps-requesting {\n    color: #d97706;\n}\n\n.jhs-ps-requesting::before {\n    content: '';\n    width: 6px;\n    height: 6px;\n    border-radius: 50%;\n    background-color: #d97706;\n    animation: jhs-ps-pulse 1s ease-in-out infinite;\n}\n\n.jhs-ps-success {\n    color: #198754;\n}\n\n.jhs-ps-success::before {\n    content: '\\2713';\n    font-weight: 700;\n}\n\n.jhs-ps-failed {\n    color: #dc2626;\n}\n\n.jhs-ps-failed::before {\n    content: '\\2715';\n    font-weight: 700;\n}\n\n@keyframes jhs-ps-pulse {\n    0%,\n    100% {\n        opacity: 1;\n    }\n\n    50% {\n        opacity: 0.2;\n    }\n}\n\n/* ========== 筛选栏 ========== */\n\n.preload-filter-chip {\n    display: inline-flex;\n    align-items: center;\n}\n\n.preload-filter-chip .pf-dot {\n    display: inline-block;\n    width: 7px;\n    height: 7px;\n    border-radius: 50%;\n    margin-right: 5px;\n}\n\n.preload-filter-chip[data-value='queued'] .pf-dot {\n    background-color: #8a8a8a;\n}\n\n.preload-filter-chip[data-value='requesting'] .pf-dot {\n    background-color: #d97706;\n}\n\n.preload-filter-chip[data-value='success'] .pf-dot {\n    background-color: #198754;\n}\n\n.preload-filter-chip[data-value='failed'] .pf-dot {\n    background-color: #dc2626;\n}\n\n.preload-filter-chip.active[data-value='queued'] {\n    background: #8a8a8a;\n    border-color: #8a8a8a;\n    color: #fff;\n}\n\n.preload-filter-chip.active[data-value='requesting'] {\n    background: #d97706;\n    border-color: #d97706;\n    color: #fff;\n}\n\n.preload-filter-chip.active[data-value='success'] {\n    background: #198754;\n    border-color: #198754;\n    color: #fff;\n}\n\n.preload-filter-chip.active[data-value='failed'] {\n    background: #dc2626;\n    border-color: #dc2626;\n    color: #fff;\n}\n";
	var OtherSitePlugin = class extends BasePlugin {
		okBackgroundColor = "#7bc73b";
		errorBackgroundColor = "#de3333";
		warnBackgroundColor = "#d7a80c";
		domainErrorBackgroundColor = "#d7780c";
		siteConfigs = [{
			id: "missAvBtn",
			getBaseUrl: async () => await this.getMissAvUrl(),
			itemSelector: ".text-secondary",
			searchPath: (baseUrl, carNum) => `${baseUrl}/search/${carNum}`,
			getDetailPageHref: (item) => item.attr("href"),
			findCarNumOrTitle: (item) => item.text()
		}, {
			id: "supJavBtn",
			getBaseUrl: async () => await this.getSupJavUrl(),
			itemSelector: ".posts post",
			searchPath: (baseUrl, carNum) => `${baseUrl}/?s=${carNum}`,
			getDetailPageHref: (item) => item.attr("href"),
			findCarNumOrTitle: (item) => item.attr("title"),
			initUrl: async (carNum) => `${await this.getSupJavUrl()}/?s=${carNum}`
		}];
		settingCache = null;
		lastFetchTime = 0;
		CACHE_DURATION = 1e4;
		preloadQueue = new AsyncTaskQueue();
		supervisor = new TaskSupervisor();
		preloadDebounce = 0;
		blockedSiteIds = new Set();
		filterBarObserver = null;
		filterRefreshDebounce = 0;
		preloadStatusEnabled = true;
		preloadDebounceMs = 300;
		preloadConcurrency = 1;
		preloadCacheTTLDays = 0;
		inflightPreloads = new Map();
		getName() {
			return "OtherSitePlugin";
		}
		get pageTypes() {
			return ["detail", "list"];
		}
		async initCss() {
			return `${other_site_plugin_default}\n${preload_status_badge_default}`;
		}
		async handle() {
			this.blockedSiteIds.clear();
			if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") return;
			if (window.isDetailPage) this.loadOtherSite().then();
			else if (document.querySelector(".movie-list")) {
				if (await storageManager.getSetting("enablePreload", "yes") !== "yes") return;
				this.preloadStatusEnabled = await storageManager.getSetting("enablePreloadStatus", "yes") === "yes";
				const debounceRaw = await storageManager.getSetting("preloadDebounce", 300);
				this.preloadDebounceMs = typeof debounceRaw === "number" ? debounceRaw : Number(debounceRaw);
				if (isNaN(this.preloadDebounceMs) || this.preloadDebounceMs < 0) this.preloadDebounceMs = 300;
				const concRaw = await storageManager.getSetting("preloadConcurrency", 1);
				let conc = typeof concRaw === "number" ? concRaw : Number(concRaw);
				if (isNaN(conc) || conc < 1) conc = 1;
				if (conc > 10) conc = 10;
				this.preloadConcurrency = Math.floor(conc);
				this.preloadQueue.setConcurrency(this.preloadConcurrency);
				this.preloadCacheTTLDays = Number(await storageManager.getSetting("preloadCacheTTL", 0)) || 0;
				syncAllBadges(this);
				preloadListPage(this).then();
				startPreloadObserver(this);
				initFilterBar(this);
			}
		}
		async loadOtherSite(carNum, sourceCarNum) {
			if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") return;
			carNum ||= this.getPageInfo().carNum ?? "";
			const enabledSiteIds = this.getEnabledSites();
			const siteButtonsHtml = this.siteConfigs.map((siteConfig) => {
				siteConfig.sourceCarNum = sourceCarNum;
				if (siteConfig.condition && siteConfig.condition(siteConfig.sourceCarNum) === false) return "";
				return jsxToString((0, import_jsx_runtime.jsx)(OtherSiteBtn, {
					id: siteConfig.id,
					enabled: enabledSiteIds.includes(siteConfig.id)
				}));
			}).join("");
			const boxHtml = jsxToString((0, import_jsx_runtime.jsx)(OtherSiteBox, {
				siteButtonsHtml,
				isJavdbSite
			}));
			$(".movie-panel-info").append(boxHtml);
			$(".container .info").append(boxHtml);
			await Promise.all(this.siteConfigs.map(async (siteConfig) => {
				if (!siteConfig.condition || siteConfig.condition(siteConfig.sourceCarNum) !== false) await this.handleSite(carNum, siteConfig);
			}));
			this.renderSettingsArea();
			this.setupEventListeners();
		}
		async handleSite(carNum, siteConfig) {
			return probeSite(this, carNum, siteConfig);
		}
		async getSettingCache() {
			const now = Date.now();
			if (!this.settingCache || now - this.lastFetchTime > this.CACHE_DURATION) {
				this.settingCache = await storageManager.getSetting();
				this.lastFetchTime = now;
			}
			return this.settingCache;
		}
		async getMissAvUrl() {
			return (await this.getSettingCache()).missAvUrl || "https://missav.live";
		}
		async getJavDbUrl() {
			return (await this.getSettingCache()).javDbUrl || "https://javdb.com";
		}
		async getSupJavUrl() {
			return (await this.getSettingCache()).supJavUrl || "https://supjav.com";
		}
		getEnabledSites() {
			return getEnabledSites(this);
		}
		saveEnabledSites(siteIds) {
			saveEnabledSites(siteIds);
		}
		renderSettingsArea() {
			const enabledSiteIds = this.getEnabledSites();
			const checkboxContainer = document.getElementById("siteCheckboxes");
			if (checkboxContainer) checkboxContainer.innerHTML = this.siteConfigs.map((siteConfig) => {
				const isEnabled = enabledSiteIds.includes(siteConfig.id);
				return jsxToString((0, import_jsx_runtime.jsx)(OtherSiteCheckbox, {
					id: siteConfig.id,
					isEnabled,
					isJavdbSite
				}));
			}).join("");
		}
		setupEventListeners() {
			const settingsArea = document.getElementById("settingsArea");
			if (settingsArea) this.supervisor.addEventListener(settingsArea, "change", (event) => {
				const target = event.target;
				if (target.type === "checkbox") {
					const siteId = target.getAttribute("data-site-id");
					if (target.checked) {
						$(`#${siteId}`).show();
						const carNum = this.getPageInfo().carNum ?? "";
						const siteConfig = this.siteConfigs.find((config) => config.id === siteId);
						this.handleSite(carNum, siteConfig).then();
					} else $(`#${siteId}`).hide();
					const checkedSiteIds = Array.from(settingsArea.querySelectorAll("input[type=\"checkbox\"]:checked")).map((checkbox) => checkbox.getAttribute("data-site-id"));
					this.saveEnabledSites(checkedSiteIds);
				}
			});
		}
		destroy() {
			this.supervisor.abort();
		}
	};
	var SAFE_INLINE_TAGS = {
		B: true,
		I: true,
		EM: true,
		STRONG: true,
		BR: true,
		P: true,
		SPAN: true
	};
	function parseInlineStyle(s) {
		if (!s) return void 0;
		try {
			const o = {};
			for (const part of s.split(";")) {
				const idx = part.indexOf(":");
				if (idx <= 0) continue;
				const k = part.slice(0, idx).trim();
				const v = part.slice(idx + 1).trim();
				if (!k || !v) continue;
				o[k.replace(/-([a-z])/g, (_m, c) => c.toUpperCase())] = v;
			}
			return Object.keys(o).length ? o : void 0;
		} catch {
			return;
		}
	}
	function safeReviewNodes(html) {
		const doc = new DOMParser().parseFromString(html, "text/html");
		const walk = (nodes, keyPrefix) => {
			const res = [];
			Array.from(nodes).forEach((node, i) => {
				const key = `${keyPrefix}${i}`;
				if (node.nodeType === Node.TEXT_NODE) {
					const t = node.textContent ?? "";
					if (t) res.push(t);
				} else if (node.nodeType === Node.ELEMENT_NODE) {
					const el = node;
					const tag = el.tagName.toUpperCase();
					const cls = el.getAttribute("class") || void 0;
					const stl = parseInlineStyle(el.getAttribute("style"));
					if (tag === "A") res.push((0, import_jsx_runtime.jsx)("a", {
						href: sanitizeUrl(el.getAttribute("href") || ""),
						target: "_blank",
						rel: "noopener noreferrer",
						className: cls,
						style: stl,
						children: walk(el.childNodes, key + "-")
					}, key));
					else if (tag === "BR") res.push((0, import_jsx_runtime.jsx)("br", {}, key));
					else if (SAFE_INLINE_TAGS[tag]) {
						const inner = walk(el.childNodes, key + "-");
						if (tag === "P") res.push((0, import_jsx_runtime.jsx)("p", {
							className: cls,
							style: stl,
							children: inner
						}, key));
						else if (tag === "B") res.push((0, import_jsx_runtime.jsx)("b", {
							className: cls,
							style: stl,
							children: inner
						}, key));
						else if (tag === "I") res.push((0, import_jsx_runtime.jsx)("i", {
							className: cls,
							style: stl,
							children: inner
						}, key));
						else if (tag === "EM") res.push((0, import_jsx_runtime.jsx)("em", {
							className: cls,
							style: stl,
							children: inner
						}, key));
						else if (tag === "STRONG") res.push((0, import_jsx_runtime.jsx)("strong", {
							className: cls,
							style: stl,
							children: inner
						}, key));
						else if (tag === "SPAN") res.push((0, import_jsx_runtime.jsx)("span", {
							className: cls,
							style: stl,
							children: inner
						}, key));
					} else res.push(...walk(el.childNodes, key + "-"));
				}
			});
			return res;
		};
		return walk(doc.body.childNodes, "");
	}
	function ReviewItem({ floor, username, stars, time, likesCount, content }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "item columns is-desktop",
			style: {
				display: "block",
				marginTop: "6px",
				backgroundColor: "#ffffff",
				padding: "10px",
				marginLeft: "-10px",
				wordBreak: "break-word",
				position: "relative"
			},
			children: [
				(0, import_jsx_runtime.jsxs)("span", {
					style: {
						position: "absolute",
						top: "5px",
						right: "10px",
						color: "#999",
						fontSize: "12px"
					},
					children: [
						"#",
						floor,
						"楼"
					]
				}),
				username,
				" \xA0\xA0",
				" ",
				(0, import_jsx_runtime.jsx)("span", {
					className: "score-stars",
					dangerouslySetInnerHTML: { __html: stars }
				}),
				" ",
				(0, import_jsx_runtime.jsx)("span", {
					className: "time",
					children: time
				}),
				" \xA0\xA0 点赞:",
				likesCount,
				(0, import_jsx_runtime.jsx)("p", {
					className: "review-content",
					style: { marginTop: "5px" },
					children: safeReviewNodes(content)
				})
			]
		});
	}
	function ReviewLinkContent({ match }) {
		if (match.startsWith("ed2k://")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			" ",
			(0, import_jsx_runtime.jsx)("span", {
				style: {
					wordBreak: "break-all",
					background: "#e0f2fe",
					color: "#0369a1"
				},
				children: match
			}),
			" "
		] });
		if (match.startsWith("magnet:")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				href: match,
				className: "a-primary",
				style: {
					padding: 0,
					wordBreak: "break-all",
					whiteSpace: "pre-wrap"
				},
				target: "_blank",
				rel: "noopener noreferrer",
				children: match
			}),
			" "
		] });
		if (match.startsWith("http://") || match.startsWith("https://")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				href: match,
				className: "a-primary",
				style: {
					padding: 0,
					wordBreak: "break-all",
					whiteSpace: "pre-wrap"
				},
				target: "_blank",
				rel: "noopener noreferrer",
				children: match
			}),
			" "
		] });
		return match;
	}
	function ReviewStarIcon() {
		return (0, import_jsx_runtime.jsx)("i", { className: "icon-star" });
	}
	function SectionContainers({ containerId, footerId }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", { id: containerId }), (0, import_jsx_runtime.jsx)("div", { id: footerId })] });
	}
	function SectionEndMessage({ text }) {
		return (0, import_jsx_runtime.jsx)("div", {
			style: {
				textAlign: "center",
				padding: "10px",
				color: "#666",
				marginTop: "10px"
			},
			children: text
		});
	}
	function SectionErrorMessage({ text, retryId, linkColor }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				marginTop: "15px",
				backgroundColor: "#ffffff",
				padding: "10px",
				marginLeft: "-10px"
			},
			children: [text, (0, import_jsx_runtime.jsx)("a", {
				id: retryId,
				style: {
					marginLeft: "10px",
					color: linkColor,
					textDecoration: "none",
					cursor: "pointer"
				},
				children: "重试"
			})]
		});
	}
	function SectionHeader({ title, foldId, linkColor, tooltip, foldText, iconText }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				display: "flex",
				alignItems: "center",
				margin: "16px 0",
				color: "#666",
				fontSize: "14px"
			},
			children: [
				(0, import_jsx_runtime.jsx)("span", { style: {
					flex: 1,
					height: "1px",
					background: "linear-gradient(to right, transparent, #999, transparent)"
				} }),
				(0, import_jsx_runtime.jsx)("span", {
					style: { padding: "0 10px" },
					"data-tip": tooltip,
					children: title
				}),
				(0, import_jsx_runtime.jsxs)("a", {
					id: foldId,
					style: {
						marginLeft: "8px",
						color: linkColor,
						textDecoration: "none",
						display: "flex",
						alignItems: "center"
					},
					children: [(0, import_jsx_runtime.jsx)("span", {
						className: "toggle-text",
						children: foldText
					}), (0, import_jsx_runtime.jsx)("span", {
						className: "toggle-icon",
						style: { marginLeft: "4px" },
						children: iconText
					})]
				}),
				(0, import_jsx_runtime.jsx)("span", { style: {
					flex: 1,
					height: "1px",
					background: "linear-gradient(to right, transparent, #999, transparent)"
				} })
			]
		});
	}
	function SectionLoadMore({ loadMoreId, endId, text, endText }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("button", {
			id: loadMoreId,
			style: {
				width: "100%",
				backgroundColor: "#e1f5fe",
				border: "none",
				padding: "10px",
				marginTop: "10px",
				cursor: "pointer",
				color: "#0277bd",
				fontWeight: "bold",
				borderRadius: "4px"
			},
			children: text
		}), (0, import_jsx_runtime.jsx)("div", {
			id: endId,
			style: {
				display: "none",
				textAlign: "center",
				padding: "10px",
				color: "#666",
				marginTop: "10px"
			},
			children: endText
		})] });
	}
	function SectionStatusMessage({ text, id }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id,
			style: {
				marginTop: "15px",
				backgroundColor: "#ffffff",
				padding: "10px",
				marginLeft: "-10px"
			},
			children: text
		});
	}
	var ReviewPlugin = class extends BasePlugin {
		floorIndex = 1;
		isInit = false;
		constructor() {
			super();
		}
		getName() {
			return "ReviewPlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		async handle() {
			if (window.isDetailPage) {
				if (isJavdbSite) {
					const movieId = this.parseMovieId(window.location.href);
					await this.showReview(movieId);
					await this.getBean("RelatedPlugin").showRelated($("#magnets-content"), movieId);
				}
			}
		}
		async showReview(movieId, container) {
			const isExpanded = await storageManager.getSetting("enableLoadReview", "yes");
			const target = container || $("#magnets-content");
			target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionHeader, {
				title: "❓ 评论区",
				foldId: "reviewsFold",
				linkColor: "#1890ff",
				tooltip: "想要发表评论? 滑上去, 点击上面的按钮-看过",
				foldText: isExpanded === "yes" ? "折叠" : "展开",
				iconText: isExpanded === "yes" ? "▲" : "▼"
			})));
			$("#reviewsFold").on("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				const toggleText = $("#reviewsFold .toggle-text");
				const toggleIcon = $("#reviewsFold .toggle-icon");
				const isExpand = toggleText.text() === "展开";
				toggleText.text(isExpand ? "折叠" : "展开");
				toggleIcon.text(isExpand ? "▲" : "▼");
				if (isExpand) {
					$("#reviewsContainer").show();
					$("#reviewsFooter").show();
					if (!this.isInit) {
						this.fetchAndDisplayReviews(movieId);
						this.isInit = true;
					}
					storageManager.saveSettingItem("enableLoadReview", "yes");
				} else {
					$("#reviewsContainer").hide();
					$("#reviewsFooter").hide();
					storageManager.saveSettingItem("enableLoadReview", "no");
				}
			});
			target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionContainers, {
				containerId: "reviewsContainer",
				footerId: "reviewsFooter"
			})));
			if (isExpanded === "yes") await this.fetchAndDisplayReviews(movieId);
		}
		async fetchAndDisplayReviews(movieId) {
			const container = $("#reviewsContainer");
			const footer = $("#reviewsFooter");
			container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, {
				text: "获取评论中...",
				id: "reviewsLoading"
			})));
			const limit = await storageManager.getSetting("reviewCount", 20);
			let reviews = null;
			try {
				reviews = await fetchMovieReviews(movieId, 1, limit);
			} catch (err) {
				if (String(err).includes("簽名已過期")) show.error("生成签名失败, 请检查系统时间及时区是否正确!");
				clog.error("获取评论失败:", err);
			} finally {
				$("#reviewsLoading").remove();
			}
			if (!reviews) {
				container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionErrorMessage, {
					text: "获取评论失败",
					retryId: "retryFetchReviews",
					linkColor: "#1890ff"
				})));
				$("#retryFetchReviews").on("click", async () => {
					$("#retryFetchReviews").parent().remove();
					await this.fetchAndDisplayReviews(movieId);
				});
				return;
			}
			if (reviews.length === 0) {
				container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, { text: "无评论" })));
				return;
			}
			this.displayReviews(reviews, container);
			if (reviews.length === limit) {
				footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionLoadMore, {
					loadMoreId: "loadMoreReviews",
					endId: "reviewsEnd",
					text: "加载更多评论",
					endText: "已加载全部评论"
				})));
				let page = 1;
				const loadMoreBtn = $("#loadMoreReviews");
				loadMoreBtn.on("click", async () => {
					let moreReviews;
					loadMoreBtn.text("加载中...").prop("disabled", true);
					page++;
					try {
						moreReviews = await fetchMovieReviews(movieId, page, limit);
					} catch (err) {
						clog.error("加载更多评论失败:", err);
					} finally {
						loadMoreBtn.text("加载失败, 请点击重试").prop("disabled", false);
					}
					if (moreReviews) {
						this.displayReviews(moreReviews, container);
						if (moreReviews.length < limit) {
							loadMoreBtn.remove();
							$("#reviewsEnd").show();
						} else loadMoreBtn.text("加载更多评论").prop("disabled", false);
					}
				});
			} else footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionEndMessage, { text: "已加载全部评论" })));
		}
		displayReviews(reviews, container) {
			if (reviews.length) reviews.forEach((review) => {
				const stars = Array(review.score).fill(jsxToString((0, import_jsx_runtime.jsx)(ReviewStarIcon, {}))).join("");
				const content = review.content.replace(/ed2k:\/\/\|file\|[^|]+\|\d+\|[a-fA-F0-9]{32}\|\/|magnet:\?[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+|https?:\/\/[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+/g, (match) => jsxToString((0, import_jsx_runtime.jsx)(ReviewLinkContent, { match })));
				const html = jsxToString((0, import_jsx_runtime.jsx)(ReviewItem, {
					floor: this.floorIndex++,
					username: review.username,
					stars,
					time: utils.formatDate(review.created_at),
					likesCount: review.likes_count,
					content
				}));
				container.append(html);
			});
		}
	};
	var BLACKLIST_DEFAULT_LABEL = "加入黑名单";
	var BLACKLIST_DEFAULT_COLOR = "#d22020";
	var BLACKLIST_TIP = "将演员加入黑名单, 后续有作品更新也会纳入屏蔽中";
	var SORT_DEFAULT_LABEL = "当前排序方式: 默认";
	function menuBtnStyle(backgroundColor) {
		return { backgroundColor: `${backgroundColor} !important` };
	}
	function MenuButtonBoxHtml({ blacklistLabel = BLACKLIST_DEFAULT_LABEL, blacklistColor = BLACKLIST_DEFAULT_COLOR, tagsPage = false, advancedSearch = false, searchOrUserPage = false, sortLabel = SORT_DEFAULT_LABEL, newVideoCount = 0 }) {
		const showSortToggle = !searchOrUserPage && !advancedSearch;
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsxs)("div", {
			style: {
				display: "flex",
				alignItems: "center",
				flexGrow: advancedSearch ? void 0 : 1
			},
			children: [
				(0, import_jsx_runtime.jsx)("a", {
					id: "waitCheckBtn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle("#56c938"),
					children: (0, import_jsx_runtime.jsx)("span", { children: "打开待鉴定" })
				}),
				(0, import_jsx_runtime.jsx)("a", {
					id: "waitDownBtn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle("#2caac0"),
					children: (0, import_jsx_runtime.jsx)("span", { children: "打开已收藏" })
				}),
				tagsPage && (0, import_jsx_runtime.jsx)("a", {
					id: "addBlacklistBtn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle(blacklistColor),
					"data-tip": BLACKLIST_TIP,
					children: (0, import_jsx_runtime.jsx)("span", { children: blacklistLabel })
				})
			]
		}), (0, import_jsx_runtime.jsxs)("div", {
			style: {
				display: "flex",
				alignItems: "center"
			},
			children: [
				(0, import_jsx_runtime.jsx)("a", {
					id: "newVideoBtn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle("#2c6cc0"),
					children: (0, import_jsx_runtime.jsxs)("span", { children: [
						"新作品检测 (",
						(0, import_jsx_runtime.jsx)("span", {
							id: "newVideoCount",
							children: newVideoCount
						}),
						")"
					] })
				}),
				(0, import_jsx_runtime.jsx)("a", {
					id: "blacklistBtn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle("#34393f"),
					children: (0, import_jsx_runtime.jsx)("span", { children: "演员黑名单" })
				}),
				showSortToggle && (0, import_jsx_runtime.jsx)("a", {
					id: "sort-toggle-btn",
					className: "jhs-toolbar-menu-btn main-tab-btn",
					style: menuBtnStyle("#8783ab"),
					children: ` ${sortLabel} `
				})
			]
		})] });
	}
	var HAS_DOWN_TEXT = "📥️ 已下载";
	var ListPageButtonPlugin = class extends BasePlugin {
		getName() {
			return "ListPageButtonPlugin";
		}
		get pageTypes() {
			return ["list"];
		}
		async handle() {
			if (!window.isListPage) return;
			await this.createMenuBtn();
			this.bindEvent();
		}
		async createMenuBtn() {
			if (isJavdbSite) {
				let containerEl = $(".main-tabs, .tabs");
				const blacklistLabel = "加入黑名单";
				const blacklistColor = "#d22020";
				let tagBlacklistEntry = null;
				if (currentHref.includes("/tags")) utils.loopDetector(() => $("#jhs-check-tag").text().trim() !== "", async () => {
					const addBlacklistBtnEl = $("#addBlacklistBtn");
					addBlacklistBtnEl.attr("data-tip", "将当前分类标签加入到黑名单, 后续有作品更新也会纳入屏蔽中");
					const tagName = $("#jhs-check-tag").text().trim();
					if (!tagName) return;
					const tagStarId = "no-" + tagName;
					tagBlacklistEntry = (await storageManager.getBlacklist()).find((entry) => entry.starId === tagStarId);
					if (tagBlacklistEntry) {
						addBlacklistBtnEl.css("backgroundColor", "#885d5d");
						$("#addBlacklistBtn span").text("已加入黑名单");
					}
				});
				const isAdvancedSearch = currentHref.includes("advanced_search");
				if (isAdvancedSearch) containerEl = $("h2.section-title");
				const sortMethod = localStorage.getItem("jhs_sortMethod");
				const sortLabel = "当前排序方式: " + (sortMethod === "rateCount" ? "评价人数" : sortMethod === "date" ? "时间" : "默认");
				containerEl.append(jsxToString((0, import_jsx_runtime.jsx)(MenuButtonBoxHtml, {
					blacklistLabel,
					blacklistColor,
					tagsPage: currentHref.includes("/tags"),
					advancedSearch: isAdvancedSearch,
					searchOrUserPage: isSearchOrUserPage,
					sortLabel
				})));
			}
		}
		bindEvent() {
			$("#waitCheckBtn").on("click", () => {
				this.openWaitCheck().then();
			});
			$("#waitDownBtn").on("click", () => {
				this.openFavorite().then();
			});
			$("#newVideoBtn").on("click", () => {
				this.getBean("NewVideoPlugin").openDialog();
			});
			$("#blacklistBtn").on("click", () => {
				this.getBean("BlacklistPlugin").openBlacklistDialog();
			});
			$("#sort-toggle-btn").on("click", (event) => {
				const currentMethod = localStorage.getItem("jhs_sortMethod");
				const nextMethod = currentMethod && currentMethod !== "default" ? currentMethod === "rateCount" ? "date" : "default" : "rateCount";
				$(event.target).text(`当前排序方式: ${{
					default: "默认",
					rateCount: "评价人数",
					date: "时间"
				}[nextMethod]}`);
				localStorage.setItem("jhs_sortMethod", nextMethod);
				this.sortItems().then();
			});
			const blacklistPlugin = this.getBean("BlacklistPlugin");
			$("#addBlacklistBtn").on("click", async (event) => {
				await blacklistPlugin.addBlacklist(event);
			});
		}
		async sortItems() {
			if (currentHref.includes("handle") || currentHref.includes("advanced_search")) return;
			if (isSearchOrUserPage) return;
			const sortMethod = localStorage.getItem("jhs_sortMethod");
			if (!sortMethod) return;
			$(".movie-list .item").each((index, element) => {
				if (!$(element).attr("data-original-index")) $(element).attr("data-original-index", index);
			});
			const listEl = $(".movie-list");
			const itemEls = $(".item", listEl);
			if (sortMethod === "default") itemEls.get().sort((itemA, itemB) => $(itemA).data("original-index") - $(itemB).data("original-index")).forEach((el) => listEl.append(el));
			else {
				const items = itemEls.get();
				items.sort((itemA, itemB) => {
					if (sortMethod === "rateCount") {
						const getRateCount = (el) => {
							const match = $(el).find(".score .value").text().match(/由(\d+)人/);
							if (match) return parseFloat(match[1]);
							else return 0;
						};
						return getRateCount(itemB) - getRateCount(itemA);
					} else {
						const getDate = (el) => {
							const dateStr = $(el).find(".meta").text().trim();
							return new Date(dateStr).getTime();
						};
						return getDate(itemB) - getDate(itemA);
					}
				});
				listEl.empty().append(items);
			}
		}
		async openWaitCheck() {
			const selectorConfig = this.getSelector();
			const maxCount = await storageManager.getSetting("waitCheckCount", 5);
			const skipTags = [
				BLOCKED_TEXT,
				FAVORITED_TEXT,
				HAS_DOWN_TEXT,
				WATCHED_TEXT
			];
			let openedCount = 0;
			$(`${selectorConfig.itemSelector}:visible`).each((_index, element) => {
				if (openedCount >= maxCount) return false;
				const itemEl = $(element);
				if (skipTags.some((tagText) => itemEl.find(`span.tag:contains('${tagText}')`).length > 0)) return;
				const { carNum, aHref } = this.getBean("ListPagePlugin").findCarNumAndHref(itemEl);
				if (carNum.includes("FC2-")) {
					const movieId = this.parseMovieId(aHref);
					this.getBean("Fc2Plugin")?.openFc2Page(movieId, carNum, aHref);
				} else {
					const url = aHref + (aHref.includes("?") ? "&autoPlay=1" : "?autoPlay=1");
					window.open(url, "_blank", "noopener,noreferrer");
				}
				openedCount++;
			});
			if (openedCount === 0) show.info("没有需鉴定的视频");
		}
		async openFavorite() {
			const maxCount = await storageManager.getSetting("waitCheckCount", 5);
			const favoriteList = (await storageManager.getCarList()).filter((item) => item.status === FAVORITE_ACTION).sort((a, b) => Number(b.createDate) - Number(a.createDate));
			for (let i = 0; i < maxCount; i++) {
				if (i >= favoriteList.length) return;
				const carInfo = favoriteList[i];
				const carNum = carInfo.carNum;
				const url = carInfo.url;
				if (carNum.includes("FC2-")) {
					const movieId = this.parseMovieId(url);
					await this.getBean("Fc2Plugin")?.openFc2Page(movieId, carNum, url);
				} else window.open(url, "_blank", "noopener,noreferrer");
				clog.debug("打开已收藏", carNum, url);
			}
		}
	};
	var PaginationStateMachine = class {
		_phase = "idle";
		_hasMore = false;
		get phase() {
			return this._phase;
		}
		get hasMore() {
			return this._hasMore;
		}
		get canLoad() {
			return this._phase === "idle" && this._hasMore;
		}
		get isLoading() {
			return this._phase === "loading";
		}
		init(hasMore) {
			this._hasMore = hasMore;
			this._phase = hasMore ? "idle" : "exhausted";
		}
		startLoading() {
			if (!this.canLoad) return false;
			this._phase = "loading";
			return true;
		}
		loadSuccess(hasMore) {
			this._hasMore = hasMore;
			this._phase = hasMore ? "idle" : "exhausted";
		}
		loadError() {
			this._phase = "error";
		}
		retry() {
			if (this._phase === "error") this._phase = "idle";
		}
		reset() {
			this._phase = "idle";
			this._hasMore = false;
		}
	};
	async function fetchJavdbPageHtml(url) {
		let response = null;
		for (let attempt = 0; attempt < 3; attempt++) {
			response = await fetch(url, {
				method: "GET",
				credentials: "include",
				cache: "no-store",
				headers: { Accept: "text/html,application/xhtml+xml" }
			});
			if (response.ok) break;
			if (![
				429,
				502,
				503,
				504
			].includes(response.status) || attempt === 2) throw new Error(`页面请求失败:HTTP ${response.status}`);
			await new Promise((resolve) => setTimeout(resolve, 400 * (attempt + 1)));
		}
		if (!response?.ok) throw new Error("页面请求失败");
		const html = await response.text();
		const title = (html.match(/<title[^>]*>([^<]*)<\/title>/i)?.[1] || "").replace(/\s+/g, " ").trim().toLowerCase();
		if (!html.includes("<body") || title.includes("cloudflare") || title.includes("just a moment") || /<form[^>]*action=["'][^"']*sign_in/i.test(html)) throw new Error("页面被登录页或人机验证页替代");
		return html;
	}
	async function loadNextPage$1(plugin) {
		if (!plugin.pagination.canLoad || !plugin.nextUrl) return;
		plugin.pagination.startLoading();
		plugin.setState("waterfall-loading", "加载中...");
		const selectorConfig = plugin.getSelector();
		try {
			const pageNum = utils.getUrlParam(plugin.nextUrl, "page");
			let maxPage = 60;
			if (currentHref.includes("c11")) maxPage = 30;
			if (isJavdbSite && Number(pageNum) > maxPage || currentHref.includes("month")) {
				const beyond60Plugin = plugin.getBean("Beyond60Plugin");
				if (beyond60Plugin) {
					const { html, nextUrl: beyondNextUrl, hasMore: beyondHasMore } = await beyond60Plugin.handleBeyond60(plugin.nextUrl);
					if (html) {
						const scrollHeight = plugin.container.scrollHeight;
						plugin.pageItems.push({
							page: plugin.currentPage + 1,
							top: scrollHeight,
							url: plugin.nextUrl
						});
						$(".movie-list").append(html);
					}
					plugin.pagination.loadSuccess(beyondHasMore);
					plugin.nextUrl = beyondNextUrl;
					const paginationHtml = beyond60Plugin.createPagination(Number(pageNum), beyondHasMore);
					$(".pagination").html(paginationHtml);
					plugin.setState("", "");
					if (!plugin.pagination.hasMore) plugin.setState("waterfall-no-more", "已经到底了");
					return;
				}
			}
			const responseHtml = await fetchJavdbPageHtml(plugin.nextUrl);
			clog.log("请求下一页内容:", plugin.nextUrl);
			const $dom = utils.htmlTo$dom(responseHtml);
			const items = $dom.find(plugin.getSelector().requestDomItemSelector);
			const existingList = plugin.getBoxCarInfoList();
			const newList = plugin.getBoxCarInfoList(items);
			if (plugin.checkDuplicateCarNumbers(existingList, newList)) {
				plugin.nextUrl = null;
				plugin.pagination.loadError();
				plugin.setState("waterfall-error", "翻页内容出现重复数据, 页码受JavDB限制, 已停止瀑布流");
				return;
			}
			const scrollHeight = plugin.container.scrollHeight;
			plugin.pageItems.push({
				page: plugin.currentPage + 1,
				top: scrollHeight,
				url: plugin.nextUrl
			});
			const listPagePlugin = plugin.getBean("ListPagePlugin");
			const coverImgs = $dom.find(plugin.getSelector().coverImgSelector);
			listPagePlugin.replaceHdImg(coverImgs);
			$(plugin.getSelector().boxSelector).append(items);
			const nextLinkEl = $dom.find(selectorConfig.nextPageSelector);
			plugin.nextUrl = nextLinkEl == null ? void 0 : nextLinkEl.attr("href");
			plugin.pagination.loadSuccess(!!plugin.nextUrl);
			const paginationEl = $dom.find(".pagination");
			$(".pagination").replaceWith(paginationEl);
			plugin.setState("", "");
			if (!plugin.pagination.hasMore) plugin.setState("waterfall-no-more", "已经到底了");
		} catch (err) {
			clog.error("加载失败:", err);
			plugin.pagination.loadError();
			plugin.setState("waterfall-error", "加载失败,点击重试");
		} finally {
			if (plugin.loadMode === "click" && plugin.pagination.hasMore && plugin.nextUrl) plugin.checkLoad();
		}
	}
	function checkScrollPosition(plugin) {
		const scrollY = window.scrollY;
		for (let i = plugin.pageItems.length - 1; i >= 0; i--) {
			const item = plugin.pageItems[i];
			if (scrollY >= item.top) {
				if (plugin.currentPage !== item.page) plugin.currentPage = item.page;
				break;
			}
		}
	}
	function checkLoad$1(plugin) {
		if (!plugin.loader) return;
		if (plugin.loader.getBoundingClientRect().top < window.innerHeight + plugin.preloadDistance) {
			if (plugin.loadMode === "click") {
				if (plugin.pagination.isLoading || !plugin.pagination.hasMore || !plugin.nextUrl || plugin.loader.classList.contains("waterfall-error") || plugin.loader.classList.contains("waterfall-loading") || plugin.loader.classList.contains("waterfall-no-more")) return;
				plugin.setState("waterfall-click", "点击加载下一页");
			} else plugin.loadNextPage().then();
		}
	}
	function setLoadMode(plugin, mode) {
		plugin.loadMode = mode === "click" ? "click" : "auto";
		if (plugin.loadMode === "auto") plugin.checkLoad();
		else if (plugin.loader && plugin.pagination.hasMore && plugin.nextUrl && !plugin.pagination.isLoading && !plugin.loader.classList.contains("waterfall-no-more")) {
			if (plugin.loader.getBoundingClientRect().top < window.innerHeight + plugin.preloadDistance) plugin.setState("waterfall-click", "点击加载下一页");
		}
	}
	function showLoadAllBtn(plugin) {
		if (plugin.loadAllBtn) return;
		if (!plugin.container || !plugin.pagination.hasMore) return;
		plugin.createLoadAllBtn();
	}
	function hideLoadAllBtn(plugin) {
		if (!plugin.loadAllBtn) return;
		plugin.loadAllBtn.remove();
		plugin.loadAllBtn = null;
	}
	function createLoadAllBtn(plugin) {
		const btn = document.createElement("div");
		btn.className = "jhs-load-all-btn";
		btn.textContent = "加载全部";
		plugin.supervisor.addEventListener(btn, "click", () => {
			if (!plugin.isLoadingAll) plugin.loadAllPages().then();
		});
		document.body.appendChild(btn);
		plugin.loadAllBtn = btn;
	}
	async function loadAllPages(plugin) {
		if (plugin.isLoadingAll || !plugin.pagination.hasMore) return;
		plugin.isLoadingAll = true;
		plugin.updateLoadAllBtn("加载中...", true);
		try {
			while (plugin.pagination.hasMore && plugin.nextUrl) {
				const before = plugin.pageItems.length;
				await plugin.loadNextPage();
				if (plugin.pageItems.length === before) break;
				plugin.updateLoadAllBtn(`加载中...(第 ${plugin.currentPage} 页)`, true);
			}
			if (plugin.loader?.classList.contains("waterfall-error")) {
				if ((plugin.loader?.textContent || "").includes("重复")) plugin.updateLoadAllBtn("已停止(页码受限)", false);
				else plugin.updateLoadAllBtn("加载失败,点击重试", false);
			} else {
				plugin.updateLoadAllBtn("✓ 已全部加载", false);
				plugin.supervisor.setTimeout(() => {
					plugin.loadAllBtn?.classList.add("jhs-load-all-fadeout");
					plugin.supervisor.setTimeout(() => {
						plugin.loadAllBtn?.remove();
						plugin.loadAllBtn = null;
					}, 500);
				}, 2e3);
			}
		} catch {
			plugin.updateLoadAllBtn("加载失败,点击重试", false);
		} finally {
			plugin.isLoadingAll = false;
		}
	}
	function updateLoadAllBtn(plugin, text, disabled) {
		if (!plugin.loadAllBtn) return;
		plugin.loadAllBtn.textContent = text;
		if (disabled) plugin.loadAllBtn.classList.add("jhs-load-all-disabled");
		else plugin.loadAllBtn.classList.remove("jhs-load-all-disabled");
	}
	function setState$1(plugin, stateClass, text) {
		plugin.loader.className = `jhs-scroll ${stateClass}`;
		plugin.loader.textContent = text;
	}
	var auto_page_plugin_default = ".jhs-scroll {\n    text-align: center;\n    padding-top: 20px;\n    font-size: 14px;\n}\n\n.jhs-scroll.waterfall-loading {\n    color: #000;\n}\n\n.jhs-scroll.waterfall-error {\n    color: #f44336;\n    cursor: pointer;\n}\n\n.jhs-scroll.waterfall-no-more {\n    color: #4caf50;\n}\n\n.jhs-scroll.waterfall-click {\n    color: #1976d2;\n    cursor: pointer;\n    font-weight: 600;\n}\n\n.jhs-scroll.waterfall-click:hover {\n    color: #0d47a1;\n    text-decoration: underline;\n}\n\n.jhs-load-all-btn {\n    position: fixed;\n    left: 20px;\n    bottom: 80px;\n    z-index: var(--jhs-z-page);\n    padding: 10px 20px;\n    background: linear-gradient(to right, #5d87c2, #4a90d9);\n    color: #fff;\n    border-radius: 20px;\n    cursor: pointer;\n    font-size: 14px;\n    box-shadow: 0 2px 8px rgb(0, 0, 0, 0.3);\n    transition: opacity 0.5s, transform 0.2s;\n    user-select: none;\n}\n\n.jhs-load-all-btn:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 4px 12px rgb(0, 0, 0, 0.4);\n}\n\n.jhs-load-all-btn.jhs-load-all-disabled {\n    opacity: 0.6;\n    cursor: wait;\n}\n\n.jhs-load-all-btn.jhs-load-all-fadeout {\n    opacity: 0;\n}\n";
	var AutoPagePlugin = class extends BasePlugin {
		preloadDistance = 500;
		currentPage = this.getInitialPageNumber();
		pageItems = [];
		container = null;
		loader = null;
		nextUrl = void 0;
		pagination = new PaginationStateMachine();
		loadAllBtn = null;
		isLoadingAll = false;
		loadMode = "auto";
		supervisor = new TaskSupervisor();
		getName() {
			return "AutoPagePlugin";
		}
		destroy() {
			this.supervisor.abort();
		}
		get pageTypes() {
			return ["list"];
		}
		async initCss() {
			return auto_page_plugin_default;
		}
		async handle() {
			this.waterfall().then();
		}
		getInitialPageNumber() {
			if (isJavdbSite) {
				const match = currentHref.match(/[?&]page=(\d+)/);
				if (match) return parseInt(match[1], 10);
				else return 1;
			}
			return 1;
		}
		async waterfall() {
			if (await this.shouldDisablePaging()) return;
			const selectorConfig = this.getSelector();
			this.container = document.querySelector(selectorConfig.boxSelector);
			if (!this.container) {
				clog.error("没有找到容器节点,停止瀑布流!");
				return;
			}
			const container = this.container;
			this.loader = document.createElement("div");
			const loader = this.loader;
			loader.className = "jhs-scroll";
			container.parentNode.insertBefore(loader, container.nextSibling);
			this.pageItems.push({
				page: this.currentPage,
				top: 0,
				url: window.location.href
			});
			this.supervisor.addEventListener(loader, "click", () => {
				if (loader.classList.contains("waterfall-error") || loader.classList.contains("waterfall-click")) {
					this.pagination.retry();
					this.loadNextPage().then();
				}
			});
			this.supervisor.addEventListener(window, "scroll", () => {
				this.checkLoad();
				this.checkScrollPosition();
			});
			const nextLinkEl = document.querySelector(selectorConfig.nextPageSelector);
			this.nextUrl = nextLinkEl == null ? void 0 : nextLinkEl.href;
			this.pagination.init(!!this.nextUrl);
			const mode = await storageManager.getSetting("autoPageLoadMode", "auto");
			this.loadMode = mode === "click" ? "click" : "auto";
			this.supervisor.setTimeout(() => {
				this.checkLoad();
			}, 1e3);
			if (!this.pagination.hasMore) this.setState("waterfall-no-more", "已经到底了");
			else this.showLoadAllBtn();
		}
		async loadNextPage() {
			return loadNextPage$1(this);
		}
		checkScrollPosition() {
			checkScrollPosition(this);
		}
		checkLoad() {
			checkLoad$1(this);
		}
		setLoadMode(mode) {
			setLoadMode(this, mode);
		}
		showLoadAllBtn() {
			showLoadAllBtn(this);
		}
		hideLoadAllBtn() {
			hideLoadAllBtn(this);
		}
		createLoadAllBtn() {
			createLoadAllBtn(this);
		}
		async loadAllPages() {
			return loadAllPages(this);
		}
		updateLoadAllBtn(text, disabled) {
			updateLoadAllBtn(this, text, disabled);
		}
		setState(stateClass, text) {
			setState$1(this, stateClass, text);
		}
		async shouldDisablePaging() {
			if (!window.isListPage) return true;
			return [
				"search?q",
				"handlePlayback=1",
				"handleTop=1",
				"/want_watch_videos",
				"/watched_videos",
				"/advanced_search?type=100"
			].some((pattern) => currentHref.includes(pattern));
		}
		updatePageUrl_old(_url) {}
		updatePageUrl(_url) {}
	};
	var TABULATOR_ZH_CN = { pagination: {
		first: "首页",
		first_title: "首页",
		last: "尾页",
		last_title: "尾页",
		prev: "上一页",
		prev_title: "上一页",
		next: "下一页",
		next_title: "下一页",
		all: "所有",
		page_size: "每页行数"
	} };
	function createTabulatorOptions(columns, data) {
		const options = {
			layout: "fitColumns",
			placeholder: "暂无数据",
			virtualDom: true,
			responsiveLayout: "collapse",
			responsiveLayoutCollapse: true,
			columnDefaults: {
				headerHozAlign: "center",
				hozAlign: "center"
			},
			columns,
			locale: "zh-cn",
			langs: { "zh-cn": TABULATOR_ZH_CN }
		};
		if (data) options.data = data;
		return options;
	}
	function BlacklistActionCell() {
		return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: (0, import_jsx_runtime.jsxs)("a", {
			className: "a-danger delete-btn",
			children: [
				" ",
				(0, import_jsx_runtime.jsx)("span", { children: "✂️ 删除" }),
				" "
			]
		}) });
	}
	function BlacklistNameCell({ url, name }) {
		return (0, import_jsx_runtime.jsx)("a", {
			className: "open-url",
			"data-url": url,
			href: url,
			"data-name": name,
			target: "_blank",
			children: name
		});
	}
	function BlacklistPaginationCounter({ actorCount, currentCarCount }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			"演员: ",
			actorCount,
			" \xA0\xA0\xA0番号总数: ",
			currentCarCount,
			"  ",
			(0, import_jsx_runtime.jsx)("span", {
				id: "checkBlacklistMsg",
				style: { marginLeft: "10px" }
			})
		] });
	}
	function BlacklistStatusCell({ tipText, statusText }) {
		return (0, import_jsx_runtime.jsx)("span", {
			"data-tip": tipText,
			style: { color: tipText ? "#cc4444" : void 0 },
			children: statusText
		});
	}
	function BlacklistUrlTypeCell({ hasTag }) {
		return (0, import_jsx_runtime.jsx)("span", {
			style: { color: hasTag ? "#cc4444" : void 0 },
			children: hasTag ? "按所选分类屏蔽" : "未筛选分类"
		});
	}
	function buildBlacklistTableOptions(plugin, data) {
		return {
			...createTabulatorOptions([
				{
					title: "演员",
					field: "name",
					sorter: "string",
					minWidth: 100,
					responsive: 0,
					headerSort: false,
					formatter: (cell, _formatterParams, _onRendered) => {
						const rowData = cell.getData();
						return jsxToString((0, import_jsx_runtime.jsx)(BlacklistNameCell, {
							url: rowData.url,
							name: rowData.name
						}));
					}
				},
				{
					title: "性别角色",
					field: "role",
					sorter: "string",
					width: 120,
					responsive: 5,
					formatter: (cell, _formatterParams, _onRendered) => {
						const role = cell.getData().role;
						let roleText = role;
						if (role === "actor") roleText = "男演员";
						else if (role === "actress") roleText = "女演员";
						return roleText;
					}
				},
				{
					title: "影视类别",
					field: "movieType",
					sorter: "string",
					width: 120,
					responsive: 5,
					formatter: (cell, _formatterParams, _onRendered) => {
						const movieType = cell.getData().movieType;
						let movieTypeText = movieType;
						if (movieType === "censored") movieTypeText = "有码";
						else if (movieType === "uncensored") movieTypeText = "无码";
						return movieTypeText;
					}
				},
				{
					title: "屏蔽类型",
					field: "url",
					sorter: "string",
					minWidth: 120,
					responsive: 4,
					visible: isJavdbSite,
					formatter: (cell, _formatterParams, _onRendered) => {
						const hasTag = cell.getData().url.includes("t=");
						return jsxToString((0, import_jsx_runtime.jsx)(BlacklistUrlTypeCell, { hasTag }));
					}
				},
				{
					title: "番号数量",
					field: "count",
					sorter: "number",
					width: 170,
					responsive: 1
				},
				{
					title: "创建时间",
					field: "createTime",
					sorter: "string",
					width: 170,
					responsive: 5
				},
				{
					title: "最后发行时间",
					field: "lastPublishTime",
					sorter: "string",
					width: 170,
					responsive: 1
				},
				{
					title: "状态",
					field: "isUnCheck",
					sorter: "string",
					width: 120,
					responsive: 1,
					formatter: (cell, _formatterParams, _onRendered) => {
						let tipText = "";
						let statusText = "正常检测";
						if (cell.getData().isUnCheck) {
							tipText = `停更${plugin.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`;
							statusText = "停止检测";
						}
						return jsxToString((0, import_jsx_runtime.jsx)(BlacklistStatusCell, {
							tipText,
							statusText
						}));
					}
				},
				{
					title: "操作",
					sorter: "string",
					cssClass: "action-cell-dropdown",
					minWidth: 150,
					responsive: 0,
					headerSort: false,
					formatter: (cell, _formatterParams, onRendered) => {
						const rowData = cell.getData();
						onRendered(() => {
							const deleteBtn = cell.getElement().querySelector(".delete-btn");
							if (deleteBtn != null) deleteBtn.addEventListener("click", (event) => {
								const name = rowData.name;
								const starId = rowData.starId;
								if (name) {
									if (starId) utils.q(event, `是否移除对 ${name} 的屏蔽?`, async () => {
										await storageManager.removeBlacklistCarList(starId);
										await storageManager.deleteBlacklistItem(starId);
										show.info("操作成功");
										plugin.reloadTable().then();
									});
									else show.error("获取starId失败");
								} else show.error("获取名称失败");
							});
							const keywordBtn = cell.getElement().querySelector(".keyword-btn");
							if (keywordBtn != null) keywordBtn.addEventListener("click", () => {
								const prefixCounts = rowData.carList.reduce((acc, carItem) => {
									const prefix = carItem.carNum.split("-")[0] + "-";
									acc[prefix] = (acc[prefix] || 0) + 1;
									return acc;
								}, {});
								const sortedPrefixes = Object.entries(prefixCounts).map(([prefix, count]) => ({
									prefix,
									count
								})).sort((a, b) => b.count - a.count);
								console.log(sortedPrefixes);
							});
						});
						return jsxToString((0, import_jsx_runtime.jsx)(BlacklistActionCell, {}));
					}
				}
			], data),
			pagination: true,
			paginationMode: "local",
			paginationSize: 20,
			paginationSizeSelector: [
				20,
				50,
				100,
				1e3,
				99999
			],
			paginationCounter: (_pageSize, _pageNo, _maxPage, actorCount, _pages) => jsxToString((0, import_jsx_runtime.jsx)(BlacklistPaginationCounter, {
				actorCount,
				currentCarCount: plugin.currentCarCount
			})),
			index: "starId",
			initialSort: [{
				column: "createTime",
				dir: "desc"
			}]
		};
	}
	function MovieListWrapper({ html, nextUrl }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
			className: "movie-list",
			dangerouslySetInnerHTML: { __html: html }
		}), nextUrl ? (0, import_jsx_runtime.jsx)("a", {
			className: "pagination-next",
			href: nextUrl
		}) : null] });
	}
	async function filterAllVideo(plugin, names, $dom) {
		let items;
		let nextPageHref;
		if ($dom) {
			items = $dom.find(plugin.getSelector().requestDomItemSelector);
			nextPageHref = $dom.find(plugin.getSelector().nextPageSelector).attr("href");
		} else {
			items = $(plugin.getSelector().itemSelector);
			nextPageHref = $(plugin.getSelector().nextPageSelector).attr("href");
		}
		if (nextPageHref && items.length === 0) failWithToast("解析列表失败");
		for (const itemEl of items) {
			const $item = $(itemEl);
			const { carNum, url, publishTime } = plugin.getBean("ListPagePlugin").findCarNumAndHref($item);
			if (url && carNum) try {
				if (await storageManager.getCar(carNum)) continue;
				await storageManager.saveCar({
					carNum,
					url,
					names,
					actionType: FILTER_ACTION,
					publishTime
				});
				clog.log("屏蔽演员番号", names, carNum);
			} catch (error) {
				clog.error(`保存失败 [${carNum}]:`, error);
			}
		}
		if (nextPageHref) {
			show.info("请不要关闭窗口, 正在解析下一页:" + nextPageHref);
			await new Promise((resolve) => setTimeout(resolve, 500));
			const pageHtml = await gmHttp.get(nextPageHref);
			const domParser = new DOMParser();
			await filterAllVideo(plugin, names, $(domParser.parseFromString(String(pageHtml), "text/html")));
		} else {
			show.ok("执行结束!");
			refresh();
		}
	}
	async function filterActorVideo(plugin, name, starId, $dom) {
		const { nextPageLink } = await parseAndSaveFilterInfo(plugin, $dom, name, starId);
		plugin.nextPageLink = nextPageLink;
		if (nextPageLink) {
			let nextDom;
			plugin.lastPageLink = nextPageLink;
			show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink);
			const pageNum = utils.getUrlParam(nextPageLink, "page") || 0;
			const beyond60Plugin = plugin.getBean("Beyond60Plugin");
			if (isJavdbSite && beyond60Plugin && Number(pageNum) > 60) {
				const { html, nextUrl, hasMore: _hasMore } = await beyond60Plugin.handleBeyond60(nextPageLink);
				const wrapperHtml = jsxToString((0, import_jsx_runtime.jsx)(MovieListWrapper, {
					html,
					nextUrl
				}));
				nextDom = utils.htmlTo$dom(wrapperHtml);
			} else {
				clog.log("正在请求下一页内容:", nextPageLink);
				const pageHtml = await gmHttp.get(nextPageLink);
				nextDom = utils.htmlTo$dom(String(pageHtml));
			}
			await filterActorVideo(plugin, name, starId, nextDom);
		} else {
			show.ok("执行结束!");
			refresh();
		}
	}
	async function parseAndSaveFilterInfo(plugin, $dom, names, starId) {
		let items;
		let nextPageHref;
		if ($dom) {
			const siteType = JAVDB;
			items = $dom.find(plugin.getSelector(siteType).requestDomItemSelector);
			nextPageHref = $dom.find(plugin.getSelector(siteType).nextPageSelector).attr("href");
		} else {
			items = $(plugin.getSelector().itemSelector);
			nextPageHref = $(plugin.getSelector().nextPageSelector).attr("href");
		}
		if (nextPageHref && items.length === 0) return {
			nextPageLink: void 0,
			lastPublishTime: null
		};
		const carList = [];
		let lastPublishTime = null;
		for (const itemEl of items) {
			const $item = $(itemEl);
			const { carNum, url, publishTime } = plugin.getBean("ListPagePlugin").findCarNumAndHref($item);
			lastPublishTime ||= publishTime;
			if (url && carNum) carList.push({
				carNum,
				url,
				names,
				actionType: FILTER_ACTION,
				starId: starId ?? void 0,
				publishTime
			});
		}
		try {
			await storageManager.batchSaveBlacklistCarList(carList);
		} catch (error) {
			clog.error("保存失败:", error);
		}
		return {
			nextPageLink: nextPageHref,
			lastPublishTime
		};
	}
	function BlacklistConfirmMessage({ tagName, name, isAlreadyBlacklisted, isActress, notFirstPageByQuery }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			isAlreadyBlacklisted ? isActress ? "演员 " : "分类 " : isActress ? "是否将该演员 " : "是否将分类 ",
			(0, import_jsx_runtime.jsx)("span", {
				style: { color: "#f40" },
				children: isActress ? name : tagName
			}),
			isAlreadyBlacklisted ? " 已在黑名单中, 是否从当前页开始追加屏蔽?" : " 加入到黑名单中?",
			notFirstPageByQuery && (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("br", {}), " 注意: 当前页面非第一页, 屏蔽数据将从此页面开始"] })
		] });
	}
	async function addBlacklist(plugin, event) {
		const position = {
			clientX: event.clientX,
			clientY: event.clientY + 80
		};
		const isAlreadyBlacklisted = $("#addBlacklistBtn span").text().includes("已加入");
		let blacklistInfo;
		let isActress;
		let tagName = "";
		if (currentHref.includes("/tags")) {
			const tagUrl = new URL(currentHref);
			tagUrl.searchParams.delete("page");
			tagName = $("#jhs-check-tag").text().trim();
			blacklistInfo = {
				starId: "no-" + tagName,
				name: "虚拟演员-" + tagName,
				allName: ["虚拟演员"],
				role: "虚拟演员",
				movieType: tagName,
				blacklistUrl: tagUrl.toString()
			};
			isActress = false;
		} else {
			blacklistInfo = plugin.getActressPageInfo();
			isActress = true;
		}
		const { starId, name, allName, role, movieType, blacklistUrl } = blacklistInfo;
		const notFirstPageByQuery = currentHref.includes("page") && !currentHref.includes("page=1");
		const confirmMessage = jsxToString((0, import_jsx_runtime.jsx)(BlacklistConfirmMessage, {
			tagName,
			name,
			isAlreadyBlacklisted,
			isActress,
			notFirstPageByQuery
		}));
		utils.q(position, confirmMessage, async () => {
			navigator.locks.request("checkNewActressActorFilterCar", { ifAvailable: true }, async (lock) => {
				clog.debug("获取锁", lock);
				if (lock) {
					plugin.loadObj = loading();
					try {
						await storageManager.addBlacklistItem({
							starId: starId ?? void 0,
							name,
							allName,
							role,
							movieType,
							url: blacklistUrl ?? void 0
						});
						await plugin.filterActorVideo(name, starId);
						const okShow = show.ok(`屏蔽结束,是否跳转到最后一页: ${plugin.lastPageLink}`, {
							duration: -1,
							close: true,
							onClick: () => {
								okShow.closeShow?.();
								window.location.href = plugin.lastPageLink;
							}
						});
					} catch (error) {
						clog.error(error);
						const errorShow = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", {
							duration: -1,
							close: true,
							onClick: () => {
								errorShow.closeShow?.();
								window.location.href = plugin.nextPageLink;
							}
						});
					} finally {
						plugin.loadObj.close();
					}
				} else show.error("当前有定时任务在后台执行中, 无法发起此操作");
			}).catch((lockError) => {
				clog.error("锁任务出现错误:", lockError);
			});
		});
	}
	function BlacklistDataTypeOptions({ totalCount, actorCount, actressCount }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsxs)("option", {
				value: "",
				children: [
					"所有 (",
					totalCount,
					")"
				]
			}),
			(0, import_jsx_runtime.jsxs)("option", {
				value: "actor",
				children: [
					"男演员 (",
					actorCount,
					")"
				]
			}),
			(0, import_jsx_runtime.jsxs)("option", {
				value: "actress",
				children: [
					"女演员 (",
					actressCount,
					")"
				]
			})
		] });
	}
	async function getTableData(plugin) {
		const blacklist = await storageManager.getBlacklist();
		const blacklistCars = await storageManager.getBlacklistCarList();
		const searchValue = $("#searchValue").val();
		const statusType = $("#statusType").val();
		const $dataTypeSelect = $("#dataType");
		const dataType = $dataTypeSelect.val();
		const urlType = $("#urlType").val();
		const totalCount = blacklist.length;
		let actorCount = 0;
		let actressCount = 0;
		const enrichedList = blacklist.map((item) => {
			if (item.role === "actor") actorCount++;
			else if (item.role === "actress") actressCount++;
			let isUnCheck = false;
			if (item.lastPublishTime) isUnCheck = !utils.isUnnecessaryCheck(item.lastPublishTime, plugin.checkBlacklist_ruleTime);
			return {
				...item,
				isUnCheck
			};
		}).filter((item) => (!searchValue || !!item.name.includes(searchValue)) && (statusType !== "normal" || !item.isUnCheck) && (statusType !== "stop" || !!item.isUnCheck) && (dataType ? item.role === dataType : (urlType !== "hasT" || !!item.url.includes("t=")) && (urlType !== "noT" || !item.url.includes("t="))));
		$dataTypeSelect.html(jsxToString((0, import_jsx_runtime.jsx)(BlacklistDataTypeOptions, {
			totalCount,
			actorCount,
			actressCount
		})));
		$dataTypeSelect.val(dataType);
		const carsByStarId = new Map();
		for (const carItem of blacklistCars) {
			const starId = carItem.starId;
			if (!carsByStarId.has(starId)) carsByStarId.set(starId, []);
			carsByStarId.get(starId).push(carItem);
		}
		const finalData = enrichedList.map((item) => {
			const starId = item.starId;
			const cars = carsByStarId.get(starId) || [];
			return {
				...item,
				carList: cars,
				count: cars.length
			};
		});
		plugin.currentCarCount = finalData.reduce((sum, row) => sum + (row.count || 0), 0);
		return finalData;
	}
	function BlacklistDialog({ showUrlType }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				padding: "10px 20px",
				height: "100%",
				overflow: "hidden"
			},
			children: [(0, import_jsx_runtime.jsxs)("div", {
				style: {
					display: "flex",
					justifyContent: "space-between"
				},
				children: [(0, import_jsx_runtime.jsx)("div", { style: {
					display: "flex",
					gap: "5px"
				} }), (0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						gap: "5px"
					},
					children: [
						(0, import_jsx_runtime.jsxs)("select", {
							id: "dataType",
							style: {
								textAlign: "center",
								minWidth: "150px"
							},
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "",
									selected: true,
									children: "所有"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "actor",
									children: "男演员"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "actress",
									children: "女演员"
								})
							]
						}),
						(0, import_jsx_runtime.jsxs)("select", {
							id: "statusType",
							style: {
								textAlign: "center",
								minWidth: "150px"
							},
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "",
									selected: true,
									children: "--检测状态--"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "normal",
									children: "正常检测"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "stop",
									children: "停止检测"
								})
							]
						}),
						(0, import_jsx_runtime.jsxs)("select", {
							id: "urlType",
							"data-tip": "在演员页屏蔽时,是否选择了分类",
							style: {
								textAlign: "center",
								minWidth: "150px",
								display: showUrlType ? void 0 : "none"
							},
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "",
									selected: true,
									children: "--屏蔽类型--"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "hasT",
									children: "按所选分类屏蔽"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "noT",
									children: "未筛选分类"
								})
							]
						}),
						(0, import_jsx_runtime.jsx)("input", {
							id: "searchValue",
							type: "text",
							placeholder: "搜索演员",
							style: { padding: "4px 5px" }
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "cleanQueryBtn",
							className: "a-info",
							style: { marginLeft: "0" },
							children: "重置"
						})
					]
				})]
			}), (0, import_jsx_runtime.jsx)("div", {
				id: "table-container",
				style: { height: "calc(100% - 50px)" }
			})]
		});
	}
	var BlacklistPlugin = class extends BasePlugin {
		loadObj = null;
		tableObj;
		nextPageLink;
		lastPageLink;
		checkBlacklist_ruleTime = 8760;
		currentCarCount = 0;
		getName() {
			return "BlacklistPlugin";
		}
		async addBlacklist(event) {
			return addBlacklist(this, event);
		}
		async resetBtnTip() {
			this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760);
		}
		async openBlacklistDialog() {
			const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(BlacklistDialog, { showUrlType: isJavdbSite }));
			layer.open({
				type: 1,
				title: "演员黑名单",
				content: dialogHtml,
				scrollbar: false,
				area: utils.getResponsiveArea(["80%", "90%"]),
				anim: -1,
				success: async (_layerEl) => {
					await this.loadTableData();
					$(".layui-layer-content").on("click", "#cleanQueryBtn", async (_event) => {
						$("#searchValue").val("");
						$("#dataType").val("");
						$("#statusType").val("");
						await this.reloadTable();
					}).on("focusout keydown", "#searchValue", async (event) => {
						if (event.type === "focusout" || event.key === "Enter") {
							if (event.key === "Enter") event.preventDefault();
							if (event.type === "keydown" && event.key !== "Enter") return;
							$("#dataType").val("");
							await this.reloadTable();
						}
					}).on("change", "#dataType", async () => {
						$("#searchValue").val("");
						await this.reloadTable();
					}).on("change", "#statusType", async () => {
						await this.reloadTable();
					}).on("change", "#urlType", async () => {
						await this.reloadTable();
					}).on("click", ".open-url", (event) => {
						event.preventDefault();
						const $link = $(event.currentTarget);
						const url = $link.attr("data-url");
						const name = $link.attr("data-name");
						utils.openPage(url ?? "", name ?? "", true, event);
					});
				},
				end: () => {
					if (this.tableObj) {
						this.tableObj.destroy();
						this.tableObj = null;
					}
					refresh();
				}
			});
		}
		async reloadTable() {
			if (!this.tableObj) return;
			const data = await this.getTableData();
			this.tableObj.setData(data);
		}
		async getTableData() {
			return getTableData(this);
		}
		async loadTableData() {
			this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime") || 8760;
			const data = await this.getTableData();
			this.tableObj = new Tabulator("#table-container", buildBlacklistTableOptions(this, data));
		}
		getCurrentStarUrl() {
			let url = window.location.href.replace(/([&?])sort_type=[^&]+(&|$)/, "$1");
			url = url.replace(/[&?]$/, "");
			url = url.replace(/\?&/, "?");
			let result = url;
			result = result.replace(/([&?])page=\d+(&|$)/, "$1");
			result = result.replace(/[&?]$/, "");
			result = result.replace(/\?&/, "?");
			result = result.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, (match, seg1, seg2, tail) => seg2 !== void 0 ? `/${seg1}${tail}` : match);
			return result;
		}
		parseUrlId(url) {
			if (!url) throw new Error("url未传入");
			return new URL(url).pathname.split("/").filter((seg) => seg.trim() !== "").pop();
		}
		async filterAllVideo(names, $dom) {
			return filterAllVideo(this, names, $dom);
		}
		async filterActorVideo(name, starId, $dom) {
			return filterActorVideo(this, name, starId, $dom);
		}
		async parseAndSaveFilterInfo($dom, names, starId) {
			return parseAndSaveFilterInfo(this, $dom, names, starId);
		}
	};
	function ConfirmWarn({ text }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("br", {}),
			" ",
			(0, import_jsx_runtime.jsx)(ColoredTextCell, {
				text,
				color: "#f40"
			})
		] });
	}
	function WantWatchedHintSpan({ variant }) {
		return (0, import_jsx_runtime.jsxs)("span", {
			style: {
				marginLeft: "8px",
				color: "#888",
				fontSize: "12px"
			},
			children: [
				"(JHS 现已在详情页自动同步\"",
				variant === "want" ? "想看" : "看過",
				"\",本页按钮仅用于初始补录)"
			]
		});
	}
	function WantWatchedImportButton({ variant }) {
		return (0, import_jsx_runtime.jsx)("a", {
			className: variant === "want" ? "a-primary" : "a-success",
			id: "wantWatchBtn",
			style: { padding: "10px" },
			children: "导入至 JHS"
		});
	}
	var WantAndWatchedVideosPlugin = class extends BasePlugin {
		type = null;
		currentPage = 1;
		getName() {
			return "WantAndWatchedVideosPlugin";
		}
		async handle() {
			if (window.location.href.includes("/want_watch_videos")) {
				$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedImportButton, { variant: "want" })));
				$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedHintSpan, { variant: "want" })));
				$("#wantWatchBtn").on("click", (clickEvent) => {
					this.type = FAVORITE_ACTION;
					this.importWantWatchVideos(clickEvent, "是否将 想看的影片 导入到 JHS-收藏?");
				});
			}
			if (window.location.href.includes("/watched_videos")) {
				$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedImportButton, { variant: "watched" })));
				$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedHintSpan, { variant: "watched" })));
				$("#wantWatchBtn").on("click", (clickEvent) => {
					this.type = HAS_WATCH_ACTION;
					this.importWantWatchVideos(clickEvent, "是否将 看过的影片 导入到 JHS-已观看?");
				});
			}
		}
		importWantWatchVideos(_clickEvent, confirmMessage) {
			utils.q(null, confirmMessage + " " + jsxToString((0, import_jsx_runtime.jsx)(ConfirmWarn, { text: "执行此功能前请记得备份数据" })), async () => {
				await withLoading(async () => {
					this.currentPage = 1;
					await this.parseMovieList();
				}).catch((error) => {
					clog.error(error);
				});
			});
		}
		async parseMovieList(rootEl) {
			if (featureFlags.wantWatchBatchImport) {
				await this.parseMovieListBatch(rootEl);
				return;
			}
			let itemElements;
			let nextPageHref;
			if (rootEl) {
				itemElements = rootEl.find(this.getSelector().itemSelector);
				nextPageHref = rootEl.find(".pagination-next").attr("href") ?? "";
			} else {
				itemElements = $(this.getSelector().itemSelector);
				nextPageHref = $(".pagination-next").attr("href") ?? "";
			}
			for (const rawItem of itemElements) {
				const $item = $(rawItem);
				const href = $item.find("a").attr("href") ?? "";
				const carNum = $item.find(".video-title strong").text().trim();
				const metaText = $item.find(".meta").text().trim();
				if (href && carNum) try {
					if (await storageManager.getCar(carNum)) {
						show.info(`${carNum} 已存在, 跳过`);
						continue;
					}
					await storageManager.saveCar({
						carNum,
						url: href,
						names: void 0,
						actionType: this.type,
						publishTime: metaText ?? void 0
					});
				} catch (error) {
					clog.error(`保存失败 [${carNum}]:`, error);
				}
			}
			if (nextPageHref) {
				show.info("发现下一页,正在解析:", nextPageHref);
				await new Promise((resolve) => setTimeout(resolve, 1e3));
				$.ajax({
					url: nextPageHref,
					method: "GET",
					success: (responseHtml) => {
						const domParser = new DOMParser();
						const $parsedDom = $(domParser.parseFromString(responseHtml, "text/html"));
						this.parseMovieList($parsedDom);
					},
					error: function(request) {
						clog.error(request);
						show.error("加载下一页失败:" + request.message);
					}
				});
			} else {
				show.ok("导入结束!");
				refresh();
			}
		}
		async parseMovieListBatch(rootEl) {
			const container = rootEl || $("body");
			const movieList = container.find(this.getSelector().itemSelector);
			const nextPageLink = container.find(".pagination-next").attr("href");
			if (movieList.length === 0) {
				show.ok("导入结束!");
				refresh();
				return;
			}
			show.info(`正在处理第 ${this.currentPage} 页...`);
			const allLocalCars = await storageManager.getCarList();
			const carNumCache = new Set(allLocalCars.map((c) => featureFlags.caseInsensitiveCarNum ? c.carNum.toLowerCase() : c.carNum));
			const currentPageRecords = [];
			movieList.each((_i, element) => {
				const item = $(element);
				const carNum2 = item.find(".video-title strong").text().trim();
				const href = item.find("a").attr("href");
				const publishTime = item.find(".meta").text().trim();
				const key = featureFlags.caseInsensitiveCarNum ? carNum2.toLowerCase() : carNum2;
				if (carNum2 && href && !carNumCache.has(key)) {
					currentPageRecords.push({
						carNum: carNum2,
						url: href,
						names: null,
						actionType: this.type,
						publishTime
					});
					carNumCache.add(key);
				}
			});
			if (currentPageRecords.length > 0) try {
				await storageManager.saveCarList(currentPageRecords);
				clog.log(`第 ${this.currentPage} 页:成功写入 ${currentPageRecords.length} 条`);
			} catch (e) {
				clog.error("批量保存失败:", e);
			}
			this.currentPage++;
			if (!nextPageLink) {
				show.ok("导入结束!");
				refresh();
				return;
			}
			await new Promise((resolve) => setTimeout(resolve, 200));
			try {
				const html = await gmHttp.get(nextPageLink);
				const next$dom = utils.htmlTo$dom(String(html));
				await this.parseMovieListBatch(next$dom);
			} catch (e) {
				clog.error("请求下一页失败", e);
				show.error("加载下一页失败");
			}
		}
	};
	function RelatedItem({ index, relatedId, name, movieCount, collectionCount, viewCount, createTime }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "item columns is-desktop",
			style: {
				display: "block",
				marginTop: "6px",
				backgroundColor: "#ffffff",
				padding: "10px",
				marginLeft: "-10px",
				wordBreak: "break-word",
				position: "relative"
			},
			children: [
				(0, import_jsx_runtime.jsxs)("span", {
					style: {
						position: "absolute",
						top: "5px",
						right: "10px",
						color: "#999",
						fontSize: "12px"
					},
					children: ["#", index]
				}),
				(0, import_jsx_runtime.jsxs)("span", {
					style: {
						position: "absolute",
						bottom: "5px",
						right: "10px",
						color: "#999",
						fontSize: "12px"
					},
					children: ["创建时间: ", createTime]
				}),
				(0, import_jsx_runtime.jsx)("p", { children: (0, import_jsx_runtime.jsx)("a", {
					href: `/lists/${relatedId}`,
					target: "_blank",
					style: { color: "#2e8abb" },
					children: name
				}) }),
				(0, import_jsx_runtime.jsxs)("p", {
					style: { marginTop: "5px" },
					children: ["视频个数: ", movieCount]
				}),
				(0, import_jsx_runtime.jsxs)("p", {
					style: { marginTop: "5px" },
					children: [
						"收藏次数: ",
						collectionCount,
						" 被查看次数: ",
						viewCount
					]
				})
			]
		});
	}
	var RelatedPlugin = class extends BasePlugin {
		floorIndex = 1;
		isInit = false;
		getName() {
			return "RelatedPlugin";
		}
		async showRelated(container, movieId) {
			const isExpanded = await storageManager.getSetting("enableLoadRelated", "no");
			const target = container || $("#magnets-content");
			target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionHeader, {
				title: "相关清单",
				foldId: "relatedFold",
				linkColor: "#1897ff",
				foldText: isExpanded === "yes" ? "折叠" : "展开",
				iconText: isExpanded === "yes" ? "▲" : "▼"
			})));
			$("#relatedFold").on("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				const toggleText = $("#relatedFold .toggle-text");
				const toggleIcon = $("#relatedFold .toggle-icon");
				const isExpand = toggleText.text() === "展开";
				toggleText.text(isExpand ? "折叠" : "展开");
				toggleIcon.text(isExpand ? "▲" : "▼");
				if (isExpand) {
					$("#relatedContainer").show();
					$("#relatedFooter").show();
					if (!this.isInit) {
						this.fetchAndDisplayRelateds(movieId);
						this.isInit = true;
					}
					storageManager.saveSettingItem("enableLoadRelated", "yes");
				} else {
					$("#relatedContainer").hide();
					$("#relatedFooter").hide();
					storageManager.saveSettingItem("enableLoadRelated", "no");
				}
			});
			target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionContainers, {
				containerId: "relatedContainer",
				footerId: "relatedFooter"
			})));
			if (isExpanded === "yes") await this.fetchAndDisplayRelateds(movieId);
		}
		async fetchAndDisplayRelateds(movieId) {
			const container = $("#relatedContainer");
			const footer = $("#relatedFooter");
			container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, {
				text: "获取清单中...",
				id: "relatedLoading"
			})));
			const limit = 20;
			let list = null;
			try {
				list = await fetchRelatedCollections(movieId, 1, limit);
			} catch (err) {
				if (String(err).includes("簽名已過期")) show.error("生成签名失败, 请检查系统时间及时区是否正确!");
				clog.error("获取清单失败:", err);
			} finally {
				$("#relatedLoading").remove();
			}
			if (!list) {
				container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionErrorMessage, {
					text: "获取清单失败",
					retryId: "retryFetchRelateds",
					linkColor: "#1897ff"
				})));
				$("#retryFetchRelateds").on("click", async () => {
					$("#retryFetchRelateds").parent().remove();
					await this.fetchAndDisplayRelateds(movieId);
				});
				return;
			}
			if (list.length === 0) {
				container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, { text: "无清单" })));
				return;
			}
			this.displayRelateds(list, container);
			if (list.length === limit) {
				footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionLoadMore, {
					loadMoreId: "loadMoreRelateds",
					endId: "relatedEnd",
					text: "加载更多清单",
					endText: "已加载全部清单"
				})));
				let page = 1;
				const loadMoreBtn = $("#loadMoreRelateds");
				loadMoreBtn.on("click", async () => {
					let moreList;
					loadMoreBtn.text("加载中...").prop("disabled", true);
					page++;
					try {
						moreList = await fetchRelatedCollections(movieId, page, limit);
					} catch (err) {
						clog.error("加载更多清单失败:", err);
					} finally {
						loadMoreBtn.text("加载失败, 请点击重试").prop("disabled", false);
					}
					if (moreList) {
						this.displayRelateds(moreList, container);
						if (moreList.length < limit) {
							loadMoreBtn.remove();
							$("#relatedEnd").show();
						} else loadMoreBtn.text("加载更多清单").prop("disabled", false);
					}
				});
			} else footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionEndMessage, { text: "已加载全部清单" })));
		}
		displayRelateds(list, container) {
			if (list.length) list.forEach((item) => {
				const html = jsxToString((0, import_jsx_runtime.jsx)(RelatedItem, {
					index: this.floorIndex++,
					relatedId: item.relatedId,
					name: item.name,
					movieCount: item.movieCount,
					collectionCount: item.collectionCount,
					viewCount: item.viewCount,
					createTime: item.createTime
				}));
				container.append(html);
			});
		}
	};
	function FavoriteActressAvatarColumn() {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "column actor-avatar",
			children: (0, import_jsx_runtime.jsx)("div", {
				className: "image",
				children: (0, import_jsx_runtime.jsx)("span", { className: "avatar" })
			})
		});
	}
	var FavoriteActressesPlugin = class extends BasePlugin {
		getName() {
			return "FavoriteActressesPlugin";
		}
		async initCss() {
			const settings = await storageManager.getSetting();
			return fold_category_plugin_default.replace("__HIGHLIGHTED_TAG_NUMBER__", String(settings.highlightedTagNumber || 1)).replace("__HIGHLIGHTED_TAG_COLOR__", settings.highlightedTagColor || "#ce2222");
		}
		async handle() {
			this.bindEvent();
			await this.highlightActress();
			this.replaceActressAvatar();
		}
		async highlightActress() {
			if (!window.isDetailPage) return;
			if (await storageManager.getSetting("enableFavoriteActresses", "yes") !== "yes") return;
			const favoriteActresses = await storageManager.getFavoriteActressList();
			if (!favoriteActresses || favoriteActresses.length === 0) return;
			const starIdSet = new Set();
			favoriteActresses.forEach((actress) => {
				if (actress.starId) starIdSet.add(String(actress.starId).trim());
			});
			if (starIdSet.size !== 0) $(".female").prev().each((_index, element) => {
				const $link = $(element);
				const href = $link.attr("href");
				let urlStarId = null;
				if (href) {
					const segments = (href.endsWith("/") ? href.slice(0, -1) : href).split("/");
					const lastSegment = segments[segments.length - 1];
					if (lastSegment) urlStarId = lastSegment.trim();
				}
				let isFavorite = false;
				if (urlStarId) isFavorite = starIdSet.has(urlStarId);
				if (isFavorite) {
					$link.addClass("highlighted");
					$link.attr("title", "高亮已收藏演员, 可在设置-基础配置中关闭");
				}
			});
		}
		async removeActorFromStorage(starId) {
			if (await storageManager.removeFavoriteActress(starId)) clog.log("移除演员成功");
		}
		bindEvent() {
			const actorIdRegex = /\/actors\/(\w+)\/(collect|uncollect)/;
			$(document).on("confirm:complete", "a[href*=\"/actors/\"][href*=\"/uncollect\"]", async (event) => {
				const [confirmResult] = event.detail;
				if (!confirmResult) return;
				const match = $(event.currentTarget).attr("href")?.match(actorIdRegex);
				const actorId = match ? match[1] : null;
				if (actorId) await this.removeActorFromStorage(actorId);
			});
			$("#button-collect-actor").click(async (_event) => {
				const match = $("#button-collect-actor").attr("href")?.match(actorIdRegex);
				const actorId = match ? match[1] : null;
				const actressNames = [];
				const $nameSection = $(".actor-section-name");
				if ($nameSection.length) $nameSection.text().trim().split(",").forEach((name) => {
					actressNames.push(name.trim());
				});
				const $metaSection = $(".section-meta:not(:contains('影片'))");
				if ($metaSection.length) $metaSection.text().trim().split(",").forEach((name) => {
					actressNames.push(name.trim());
				});
				if (!actressNames) {
					clog.error("获取演员名称失败");
					return;
				}
				const firstName = actressNames[0];
				if (!actorId) {
					clog.error("无法获取演员ID进行收藏操作。");
					return;
				}
				const actressData = {
					starId: actorId,
					name: firstName,
					allName: actressNames,
					avatar: ($(".avatar").first().css("background-image") || "").replace(/^url\(["']?|["']?\)$/g, "")
				};
				if (await storageManager.addFavoriteActressList([actressData]) === 1) clog.log(`收藏演员成功: ${firstName} (ID: ${actorId})`);
				else clog.log(`收藏演员失败: ${firstName} (ID: ${actorId})`);
			});
			$("#button-uncollect-actor").click(async (_event) => {
				const match = $("#button-uncollect-actor").attr("href")?.match(actorIdRegex);
				const actorId = match ? match[1] : null;
				if (actorId) await this.removeActorFromStorage(actorId);
				else clog.error("无法获取演员ID进行取消收藏操作。");
			});
		}
		async replaceActressAvatar() {
			const actressId = this.getActressId();
			if (!actressId) return;
			const matchedActress = (await storageManager.getFavoriteActressList()).find((actress) => actress.starId === actressId);
			if (matchedActress && matchedActress.avatar) {
				const cssUrl = `url('${matchedActress.avatar}')`;
				let $avatar = $(".avatar").first();
				if ($avatar.length === 0) {
					const avatarColumnHtml = jsxToString((0, import_jsx_runtime.jsx)(FavoriteActressAvatarColumn, {}));
					$(".section-columns").prepend(avatarColumnHtml);
					$avatar = $(".avatar").first();
				}
				if ($avatar.length === 0) return;
				if ($avatar.css("background-image").trim().toLowerCase() !== cssUrl.trim().toLowerCase()) {
					$avatar.css("background-image", cssUrl);
					$avatar.css("background-size", "cover");
					$avatar.css("background-position", "top center");
					$avatar.css("background-repeat", "no-repeat");
				}
			}
		}
	};
	function ActressCard({ starId, avatar, name, allNameText, actressUrl, lastCheckTime, lastPublishTime, isStale, ruleTimeYears, remark, editSvg, deleteSvg, btnStyle, typeLabel, typeColor, newVideoCount }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "actress-card",
			"data-starId": starId,
			style: {
				...isStale ? { background: "#d4cece" } : null,
				minHeight: "370px"
			},
			children: [
				(0, import_jsx_runtime.jsx)("a", {
					href: actressUrl,
					target: "_blank",
					style: {
						textDecoration: "none",
						color: "inherit",
						display: "block",
						flexGrow: 1
					},
					children: (0, import_jsx_runtime.jsx)("img", {
						src: avatar,
						alt: allNameText,
						className: "actress-card-avatar"
					})
				}),
				(0, import_jsx_runtime.jsxs)("div", { children: [(0, import_jsx_runtime.jsx)("a", {
					href: actressUrl,
					target: "_blank",
					style: {
						textDecoration: "none",
						color: "inherit",
						display: "block",
						flexGrow: 1
					},
					children: (0, import_jsx_runtime.jsx)("div", {
						className: "actress-card-name",
						children: name
					})
				}), (0, import_jsx_runtime.jsx)("div", {
					className: "actress-card-allname",
					title: allNameText,
					children: allNameText
				})] }),
				(0, import_jsx_runtime.jsx)("div", {
					style: {
						fontSize: "0.8em",
						marginTop: "5px"
					},
					children: (0, import_jsx_runtime.jsxs)("span", { children: ["上次检测: ", lastCheckTime] })
				}),
				(0, import_jsx_runtime.jsx)("div", {
					style: {
						fontSize: "0.8em",
						marginTop: "5px"
					},
					children: (0, import_jsx_runtime.jsxs)("span", { children: ["最后发行作品: ", lastPublishTime] })
				}),
				(0, import_jsx_runtime.jsx)("div", {
					style: {
						fontSize: "0.7em",
						color: "#cc4444",
						marginTop: "5px",
						minHeight: "18px"
					},
					children: (0, import_jsx_runtime.jsx)("span", { children: isStale ? `停更${ruleTimeYears}年以上, 下轮任务不再进行检测` : "" })
				}),
				(0, import_jsx_runtime.jsx)("div", {
					style: {
						fontSize: "0.8em",
						marginTop: "5px",
						color: "#3765c5",
						minHeight: "10px"
					},
					children: (0, import_jsx_runtime.jsx)("span", { children: remark })
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						marginTop: "10px",
						display: "flex",
						justifyContent: "center",
						gap: "10px"
					},
					children: [(0, import_jsx_runtime.jsx)("a", {
						title: "编辑",
						className: "card-btn btn-edit-actress",
						style: btnStyle,
						"data-starId": starId,
						dangerouslySetInnerHTML: { __html: editSvg }
					}), (0, import_jsx_runtime.jsx)("a", {
						title: "取消收藏",
						className: "card-btn btn-delete-actress",
						style: btnStyle,
						"data-starId": starId,
						dangerouslySetInnerHTML: { __html: deleteSvg }
					})]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "card-tag",
					style: { backgroundColor: typeColor },
					children: typeLabel
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "card-new-count-tag",
					"data-tip": `最新作品数量: ${newVideoCount}`,
					children: ["🔔 ", newVideoCount]
				})
			]
		});
	}
	var NAV_BTN_STYLE = {
		padding: "8px 12px",
		margin: "0 5px",
		background: "#f0f0f0",
		border: "1px solid #ddd",
		borderRadius: "4px",
		cursor: "pointer"
	};
	var PAGE_BTN_BASE_STYLE = {
		padding: "8px 12px",
		margin: "0 3px",
		border: "1px solid #ddd",
		borderRadius: "4px",
		cursor: "pointer"
	};
	var ACTIVE_STYLE = {
		background: "#007bff",
		color: "white",
		borderColor: "#007bff"
	};
	function ActressPagination({ totalCount, totalPages, page }) {
		if (totalPages === 0) return (0, import_jsx_runtime.jsx)("span", {
			style: { color: "#666" },
			children: "共 0 条记录"
		});
		let rangeStart = Math.max(1, page - Math.floor(2.5));
		const rangeEnd = Math.min(totalPages, rangeStart + 5 - 1);
		if (rangeEnd - rangeStart < 4) rangeStart = Math.max(1, rangeEnd - 5 + 1);
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			page > 1 && totalPages > 5 && (0, import_jsx_runtime.jsx)("button", {
				className: "pagination-btn",
				"data-page": 1,
				style: NAV_BTN_STYLE,
				children: "首页"
			}),
			page > 1 && (0, import_jsx_runtime.jsx)("button", {
				className: "pagination-btn",
				"data-page": page - 1,
				style: NAV_BTN_STYLE,
				children: "上一页"
			}),
			Array.from({ length: rangeEnd - rangeStart + 1 }, (_, i) => {
				const pageNum = rangeStart + i;
				return (0, import_jsx_runtime.jsx)("button", {
					className: `pagination-btn page-number-btn ${pageNum === page ? "active" : ""}`,
					"data-page": pageNum,
					style: {
						...PAGE_BTN_BASE_STYLE,
						...pageNum === page ? ACTIVE_STYLE : null
					},
					children: pageNum
				}, pageNum);
			}),
			page < totalPages && (0, import_jsx_runtime.jsx)("button", {
				className: "pagination-btn",
				"data-page": page + 1,
				style: NAV_BTN_STYLE,
				children: "下一页"
			}),
			page < totalPages && totalPages > 5 && (0, import_jsx_runtime.jsx)("button", {
				className: "pagination-btn",
				"data-page": totalPages,
				style: NAV_BTN_STYLE,
				children: "尾页"
			}),
			(0, import_jsx_runtime.jsxs)("span", {
				style: {
					marginLeft: "20px",
					color: "#666"
				},
				children: [
					"共 ",
					totalCount,
					" 条记录 (第 ",
					page,
					"/",
					totalPages,
					" 页)"
				]
			})
		] });
	}
	var ACTRESS_TYPE_MAP = {
		[UNCENSORED]: {
			label: "无码",
			color: "#4CAF50"
		},
		[CENSORED]: {
			label: "有码",
			color: "#FF9800"
		}
	};
	var DEFAULT_TYPE = {
		label: "未知",
		color: "#9E9E9E"
	};
	async function renderActressCards(plugin) {
		const $container = $("#actress-card-container");
		if (!$container.length) return;
		let favoriteActresses = await storageManager.getFavoriteActressList();
		const typeFilter = String($("#paramActressType").val() ?? "");
		if (typeFilter !== "all") favoriteActresses = favoriteActresses.filter((actress) => actress.actressType === typeFilter);
		const sortedActresses = utils.genericSort(favoriteActresses, [{
			key: (actress) => actress.newVideoList?.length ?? 0,
			order: "desc"
		}, {
			key: "lastPublishTime",
			order: "desc"
		}]);
		const totalCount = sortedActresses.length;
		const totalPages = Math.ceil(totalCount / plugin.pageSize);
		const startIndex = (plugin.currentPage - 1) * plugin.pageSize;
		const endIndex = startIndex + plugin.pageSize;
		const pageActresses = sortedActresses.slice(startIndex, endIndex);
		const javDbUrl = await plugin.getBean("OtherSitePlugin").getJavDbUrl();
		const ruleTimeHours = await storageManager.getSetting("checkNewVideo_ruleTime") || 8760;
		const cardsHtml = pageActresses.map((actress) => {
			const allNameText = Array.isArray(actress.allName) ? actress.allName.join(",") : "";
			if (Array.isArray(actress.newVideoList)) actress.newVideoList.join(",");
			const actressUrl = `${javDbUrl}/actors/${actress.starId}?t=d`;
			let isStale = false;
			if (actress.lastPublishTime) isStale = !utils.isUnnecessaryCheck(actress.lastPublishTime, ruleTimeHours);
			const typeInfo = ACTRESS_TYPE_MAP[actress.actressType ?? ""] ?? DEFAULT_TYPE;
			let btnStyle = {};
			if (isStale) btnStyle = {
				background: "linear-gradient(145deg, #e0e0e0 0%, #cabdbd 100%)",
				boxShadow: "none"
			};
			return jsxToString((0, import_jsx_runtime.jsx)(ActressCard, {
				starId: actress.starId,
				avatar: actress.avatar || "https://c0.jdbstatic.com/images/actor_unknow.jpg",
				name: actress.name,
				allNameText,
				actressUrl,
				lastCheckTime: actress.lastCheckTime || "",
				lastPublishTime: actress.lastPublishTime || "",
				isStale,
				ruleTimeYears: ruleTimeHours / 24 / 365,
				remark: actress.remark || "",
				editSvg: plugin.editSvg,
				deleteSvg: plugin.deleteSvg,
				btnStyle,
				typeLabel: typeInfo.label,
				typeColor: typeInfo.color,
				newVideoCount: actress.newVideoList?.length || 0
			}));
		}).join("");
		$container.html(cardsHtml);
		$(".btn-delete-actress").off("click").on("click", (event) => {
			event.preventDefault();
			const starId = $(event.currentTarget).attr("data-starId") ?? "";
			const actress = sortedActresses.find((item) => item.starId === starId);
			if (!actress) return;
			utils.q(event, `是否取消收藏 ${actress.name}?`, async () => {
				const uncollectUrl = `${await plugin.getBean("OtherSitePlugin").getJavDbUrl()}/actors/${starId}/uncollect`;
				const csrfToken = document.querySelector("meta[name=csrf-token]")?.getAttribute("content") ?? "";
				const response = await gmHttp.post(uncollectUrl, void 0, { "x-csrf-token": csrfToken });
				if (response.includes("removeClass")) {
					await storageManager.removeFavoriteActress(starId);
					plugin.loadData();
				} else {
					show.error("移除失败");
					clog.error("移除失败,返回值:", response);
				}
			});
		});
		$(".btn-edit-actress").off("click").on("click", (event) => {
			event.preventDefault();
			const starId = $(event.currentTarget).attr("data-starId") ?? "";
			const actress = sortedActresses.find((item) => item.starId === starId);
			if (actress) plugin.editActress(actress);
			else show.error(`未找到 starId 为 ${starId} 的女优记录。`);
		});
		renderPagination$1(plugin, totalCount, totalPages);
		show.ok("加载完成");
	}
	function renderPagination$1(plugin, totalCount, totalPages) {
		const page = plugin.currentPage;
		$("#actress-pagination").html(jsxToString((0, import_jsx_runtime.jsx)(ActressPagination, {
			totalCount,
			totalPages,
			page
		})));
		$(".pagination-btn").off("click").on("click", (event) => {
			if ($(event.currentTarget).is("[disabled]")) return;
			const targetPage = parseInt($(event.currentTarget).data("page"));
			if (targetPage >= 1 && targetPage <= totalPages && targetPage !== plugin.currentPage) {
				plugin.currentPage = targetPage;
				plugin.renderActressCards();
			}
		});
	}
	function AvatarSelectDialog({ avatarUrls }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "gfriends-image-list-container",
			children: [(0, import_jsx_runtime.jsxs)("p", {
				id: "gfriends-prompt",
				style: {
					textAlign: "center",
					fontSize: "15px",
					marginBottom: "15px"
				},
				children: [
					"点击图片即可选择(初始共 ",
					avatarUrls.length,
					" 张)"
				]
			}), (0, import_jsx_runtime.jsx)("div", {
				style: {
					overflowY: "auto",
					height: "calc(100% - 40px)"
				},
				children: (0, import_jsx_runtime.jsx)("div", {
					id: "gfriends-image-list",
					children: avatarUrls.map((url, index) => (0, import_jsx_runtime.jsxs)("div", {
						id: `wrapper-${index}`,
						className: "gfriends-image-item-wrapper",
						children: [(0, import_jsx_runtime.jsx)("img", {
							alt: "",
							src: url,
							"data-url": url,
							className: "gfriends-selectable-img",
							"data-wrapper-id": `wrapper-${index}`
						}), (0, import_jsx_runtime.jsx)("div", {
							className: "gfriends-size-tag",
							"data-size-for": `wrapper-${index}`,
							children: "..."
						})]
					}, index))
				})
			})]
		});
	}
	var avatar_select_dialog_default = "/* 保持上一个回答的美化样式 */\n#gfriends-image-list-container {\n    padding: 15px;\n    height: 100%;\n    box-sizing: border-box;\n    background-color: #f8f9fa;\n}\n\n#gfriends-prompt {\n    color: #555;\n    font-weight: 500;\n    border-bottom: 1px solid #eee;\n    padding-bottom: 10px;\n}\n\n#gfriends-image-list {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 15px;\n    justify-content: center;\n}\n\n.gfriends-image-item-wrapper {\n    width: 160px;\n    height: 225px; /* 增加高度以容纳尺寸标签 */\n    overflow: hidden;\n    border-radius: 6px;\n    box-shadow: 0 4px 8px rgb(0, 0, 0, 0.1);\n    transition:\n        transform 0.2s ease,\n        box-shadow 0.2s ease;\n    cursor: pointer;\n    position: relative;\n    padding-bottom: 25px; /* 为尺寸标签留出空间 */\n}\n\n.gfriends-selectable-img {\n    width: 100%;\n    height: 200px; /* 固定图片高度 */\n    object-fit: cover;\n    border: 3px solid transparent;\n    border-radius: 6px;\n    transition: border 0.2s ease;\n}\n\n.gfriends-image-item-wrapper:hover {\n    transform: translateY(-4px) scale(1.02);\n    box-shadow: 0 8px 16px rgb(0, 0, 0, 0.2);\n}\n\n.gfriends-selectable-img.is-selected {\n    border-color: #ff6347;\n    box-shadow: 0 0 0 3px #ff6347;\n}\n\n/* 新增:尺寸标签样式 */\n.gfriends-size-tag {\n    position: absolute;\n    bottom: 0; /* 定位到图片容器底部 */\n    left: 0;\n    right: 0;\n    height: 25px;\n    line-height: 25px;\n    text-align: center;\n    background-color: rgb(0, 0, 0, 0.7); /* 半透明背景 */\n    color: #fff;\n    font-size: 11px;\n    font-weight: bold;\n    border-bottom-left-radius: 6px;\n    border-bottom-right-radius: 6px;\n    user-select: none;\n}\n";
	async function searchAvatar() {
		const $nameInput = $("#edit-actress-name");
		const $allNameInput = $("#edit-actress-allname");
		const nameText = $nameInput.val().trim();
		const searchNames = $allNameInput.val().trim().split(/[\uff0c,]/).map((name) => name.trim()).filter((name) => name.length > 0);
		if (nameText) searchNames.unshift(nameText);
		if (searchNames.length === 0) {
			show.error("请先填写女优主名称或别名进行搜索。");
			return;
		}
		const loader = loading("正在搜索头像...");
		let avatarUrls = [];
		try {
			avatarUrls = await loadGfriends(searchNames);
		} catch (error) {
			show.error(`头像数据加载或搜索失败: ${error instanceof Error ? error.message : String(error)}`);
			return;
		} finally {
			loader.close();
		}
		if (avatarUrls.length === 0) {
			show.error(`未找到与 '${searchNames.join("、")}' 相关的头像。请检查名称。`);
			return;
		}
		const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, { css: avatar_select_dialog_default })) + jsxToString((0, import_jsx_runtime.jsx)(AvatarSelectDialog, { avatarUrls }));
		let errorCount = 0;
		layer.open({
			type: 1,
			title: `选择女优头像 (${avatarUrls.length} 张)`,
			area: utils.getResponsiveArea(["900px", "85%"]),
			content: dialogContent,
			btn: ["关闭"],
			success: (layerEl, layerIndex) => {
				const $layer = $(layerEl);
				const $images = $layer.find(".gfriends-selectable-img");
				const $prompt = $layer.find("#gfriends-prompt");
				$images.each((_index, element) => {
					const $img = $(element);
					const wrapperId = String($img.data("wrapper-id") ?? "");
					const $wrapper = $layer.find(`#${wrapperId}`);
					const $sizeTag = $layer.find(`.gfriends-size-tag[data-size-for="${wrapperId}"]`);
					$img.on("load", () => {
						const width = element.naturalWidth;
						const height = element.naturalHeight;
						$sizeTag.text(`${width} x ${height}`);
					});
					$img.on("error", () => {
						$wrapper.remove();
						errorCount++;
						const remaining = avatarUrls.length - errorCount;
						$prompt.text(`点击图片即可选择(已移除 ${errorCount} 张错误图片,剩余 ${remaining} 张)`);
						if (remaining === 0) {
							show.error("所有搜索到的头像链接均已失效,无法选择。");
							layer.close(layerIndex);
						}
					});
					if (element.complete) {
						if (element.naturalWidth > 0) $img.trigger("load");
						else $img.trigger("error");
					}
				});
				$images.on("click", (event) => {
					const $img = $(event.currentTarget);
					const url = String($img.data("url") ?? "");
					$("#edit-actress-avatar").val(url);
					$("#edit-avatar-preview").attr("src", url);
					$images.removeClass("is-selected");
					$img.addClass("is-selected");
					setTimeout(() => {
						layer.close(layerIndex);
					}, 150);
				});
				utils.setupEscClose(layerIndex);
			}
		});
	}
	var LABEL_STYLE$1 = {
		display: "block",
		marginBottom: "5px",
		fontWeight: "bold"
	};
	var FIELD_ROW_STYLE$1 = { marginBottom: "15px" };
	function EditActressDialog({ avatar, name, textareaStyle, allNameText, actressType, newVideoText, remark }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: { padding: "20px" },
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						marginBottom: "15px",
						textAlign: "center"
					},
					children: [(0, import_jsx_runtime.jsx)("img", {
						id: "edit-avatar-preview",
						src: avatar,
						alt: "Avatar Preview",
						style: {
							width: "100px",
							height: "100px",
							borderRadius: "50%",
							objectFit: "cover",
							marginBottom: "10px",
							border: "2px solid #ddd"
						}
					}), (0, import_jsx_runtime.jsxs)("div", {
						style: { textAlign: "left" },
						children: [
							(0, import_jsx_runtime.jsx)("label", {
								style: LABEL_STYLE$1,
								children: "头像链接:"
							}),
							(0, import_jsx_runtime.jsx)("input", {
								type: "text",
								id: "edit-actress-avatar",
								value: avatar,
								style: {
									width: "100%",
									padding: "8px",
									border: "1px solid #ccc",
									borderRadius: "4px"
								}
							}),
							(0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "5px",
									marginTop: "5px"
								},
								children: [(0, import_jsx_runtime.jsx)("button", {
									type: "button",
									id: "search-avatar-btn",
									style: {
										flexGrow: 1,
										padding: "8px",
										backgroundColor: "#007bff",
										color: "white",
										border: "none",
										borderRadius: "4px",
										cursor: "pointer"
									},
									children: "搜索头像"
								}), (0, import_jsx_runtime.jsx)("button", {
									type: "button",
									id: "select-cdn-btn",
									style: {
										width: "100px",
										padding: "8px",
										backgroundColor: "#6c757d",
										color: "white",
										border: "none",
										borderRadius: "4px",
										cursor: "pointer",
										fontSize: "12px"
									},
									children: "选择 CDN 源"
								})]
							})
						]
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE$1,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE$1,
						children: "主名称:"
					}), (0, import_jsx_runtime.jsx)("input", {
						type: "text",
						id: "edit-actress-name",
						value: name,
						style: {
							width: "100%",
							padding: "8px",
							border: "1px solid #ccc",
							borderRadius: "4px"
						}
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE$1,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE$1,
						children: "所有别名(用逗号隔开):"
					}), (0, import_jsx_runtime.jsx)("textarea", {
						id: "edit-actress-allname",
						style: textareaStyle,
						children: allNameText
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE$1,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE$1,
						children: "演员类别:"
					}), (0, import_jsx_runtime.jsxs)("select", {
						id: "actressType",
						style: {
							width: "100%",
							padding: "10px",
							border: "1px solid #ddd"
						},
						children: [
							(0, import_jsx_runtime.jsx)("option", {
								value: "",
								selected: actressType === "",
								children: "未知"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "censored",
								selected: actressType === "censored",
								children: "有码"
							}),
							(0, import_jsx_runtime.jsx)("option", {
								value: "uncensored",
								selected: actressType === "uncensored",
								children: "无码"
							})
						]
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE$1,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE$1,
						children: "最新作品(用逗号隔开):"
					}), (0, import_jsx_runtime.jsx)("textarea", {
						id: "edit-actress-newvideolist",
						style: textareaStyle,
						children: newVideoText
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE$1,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE$1,
						children: "备注:"
					}), (0, import_jsx_runtime.jsx)("textarea", {
						id: "edit-remark",
						style: textareaStyle,
						children: remark
					})]
				})
			]
		});
	}
	function CdnSelectDialog({ sources, currentIndex }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: { padding: "20px" },
			children: [
				(0, import_jsx_runtime.jsxs)("p", {
					style: {
						marginBottom: "15px",
						fontWeight: "bold",
						color: "#333"
					},
					children: [
						"请选择头像数据源 (当前: ",
						sources[currentIndex].name,
						"):"
					]
				}),
				sources.map((source, index) => (0, import_jsx_runtime.jsxs)("div", {
					style: { marginBottom: "10px" },
					children: [(0, import_jsx_runtime.jsx)("input", {
						type: "radio",
						id: `cdn-${index}`,
						name: "cdn-source",
						value: index,
						checked: index === currentIndex,
						style: { marginRight: "10px" }
					}), (0, import_jsx_runtime.jsxs)("label", {
						for: `cdn-${index}`,
						children: [
							source.name,
							" ",
							source.json.includes("jsdelivr") ? "(推荐)" : ""
						]
					})]
				}, index)),
				(0, import_jsx_runtime.jsx)("p", {
					style: {
						marginTop: "20px",
						color: "#555",
						fontSize: "12px"
					},
					children: "切换源会清除本地缓存的数据,并在下次搜索时重新加载。"
				})
			]
		});
	}
	async function editActress(plugin, actress) {
		const name = actress.name;
		const avatar = actress.avatar;
		const remark = actress.remark || "";
		const allNameText = Array.isArray(actress.allName) ? actress.allName.join(",") : "";
		const newVideoText = Array.isArray(actress.newVideoList) ? actress.newVideoList.join(",") : "";
		const starId = actress.starId;
		const textareaStyle = {
			width: "100%",
			padding: "8px",
			border: "1px solid #ccc",
			borderRadius: "4px",
			minHeight: "60px",
			overflowY: "hidden"
		};
		const actressType = actress.actressType || "";
		const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(EditActressDialog, {
			avatar,
			name,
			textareaStyle,
			allNameText,
			actressType,
			newVideoText,
			remark
		}));
		layer.open({
			type: 1,
			title: `编辑女优: ${name} (${starId})`,
			area: ["500px", "750px"],
			content: dialogContent,
			btn: ["保存", "取消"],
			success: (_layerEl, layerIndex) => {
				const autoHeight = ($textarea) => {
					$textarea.css("height", "auto");
					$textarea.css("height", $textarea[0].scrollHeight + 15 + "px");
				};
				$("#edit-actress-avatar").on("input", (event) => {
					const val = $(event.currentTarget).val();
					$("#edit-avatar-preview").attr("src", val);
				});
				const $allNameArea = $("#edit-actress-allname");
				$allNameArea.on("input", (event) => {
					autoHeight($(event.currentTarget));
				});
				autoHeight($allNameArea);
				const $newVideoArea = $("#edit-actress-newvideolist");
				$newVideoArea.on("input", (event) => {
					autoHeight($(event.currentTarget));
				});
				autoHeight($newVideoArea);
				$("#search-avatar-btn").on("click", async () => {
					await plugin.searchAvatar();
				});
				$("#select-cdn-btn").on("click", async () => {
					const currentIndex = getCurrentCdnSource().index;
					const cdnDialogContent = jsxToString((0, import_jsx_runtime.jsx)(CdnSelectDialog, {
						sources: GFRIENDS_SOURCES,
						currentIndex
					}));
					layer.open({
						type: 1,
						title: "选择 CDN 源",
						area: ["400px", "auto"],
						content: cdnDialogContent,
						btn: ["确定", "取消"],
						success: (_cdnLayerEl, cdnLayerIndex) => {
							utils.setupEscClose(cdnLayerIndex);
						},
						yes: async (cdnLayerIndex) => {
							const selectedValue = String($("input[name=\"cdn-source\"]:checked").val() ?? "");
							const selectedIndex = parseInt(selectedValue, 10);
							if (selectedIndex !== currentIndex) {
								localStorage.setItem(GFRIENDS_CDN_INDEX_KEY, selectedIndex.toString());
								clearCache();
								try {
									await window.filetreeDb.set(FILETREE_DATA_KEY, null);
								} catch (error) {
									clog.error("清除 IndexedDB 缓存失败:", error);
								}
								show.ok(`CDN 源已切换为: ${GFRIENDS_SOURCES[selectedIndex].name}`);
								layer.close(cdnLayerIndex);
							} else layer.close(cdnLayerIndex);
						}
					});
				});
				utils.setupEscClose(layerIndex);
			},
			yes: async (layerIndex) => {
				const avatar = String($("#edit-actress-avatar").val() ?? "").trim();
				const name = String($("#edit-actress-name").val() ?? "").trim();
				const allNameText = String($("#edit-actress-allname").val() ?? "").trim();
				const newVideoText = String($("#edit-actress-newvideolist").val() ?? "").trim();
				const remark = String($("#edit-remark").val() ?? "").trim();
				const actressType = String($("#actressType").val() ?? "");
				if (!name) {
					show.error("主名称不能为空");
					return false;
				}
				const allNameArray = allNameText.split(/[\uff0c,]/).map((part) => part.trim()).filter((part) => part.length > 0);
				const newVideoArray = newVideoText.split(/[\uff0c,]/).map((part) => part.trim()).filter((part) => part.length > 0);
				actress.avatar = avatar;
				actress.name = name;
				actress.allName = allNameArray;
				actress.newVideoList = newVideoArray;
				actress.actressType = actressType;
				actress.remark = remark;
				if (await storageManager.updateFavoriteActress(actress)) show.error("修改失败");
				else {
					plugin.renderActressCards().then();
					show.ok(`女优 ${name} 信息已更新`);
					layer.close(layerIndex);
				}
			}
		});
	}
	function NewVideoDialog({ refreshSvg }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "newVideoToolBox",
			style: {
				display: "flex",
				flexDirection: "column",
				height: "100%",
				overflow: "hidden",
				padding: "10px"
			},
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						marginBottom: "15px",
						display: "flex",
						justifyContent: "space-between"
					},
					children: [(0, import_jsx_runtime.jsx)("div", { children: (0, import_jsx_runtime.jsx)("span", { id: "checkNewVideoMsg" }) }), (0, import_jsx_runtime.jsxs)("div", {
						style: {
							display: "flex",
							alignItems: "flex-start"
						},
						children: [(0, import_jsx_runtime.jsxs)("select", {
							id: "paramActressType",
							style: {
								textAlign: "center",
								height: "100%",
								minWidth: "150px",
								border: "1px solid #ddd",
								marginRight: "10px"
							},
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "all",
									selected: true,
									children: "所有"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "uncensored",
									children: "无码"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "censored",
									children: "有码"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "",
									children: "未知"
								})
							]
						}), (0, import_jsx_runtime.jsx)("a", {
							className: "a-normal",
							id: "reLoad",
							dangerouslySetInnerHTML: { __html: `${refreshSvg} &nbsp;&nbsp; 刷新` }
						})]
					})]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					id: "actress-card-container",
					className: "jhs-scrollbar"
				}),
				(0, import_jsx_runtime.jsx)("div", { id: "actress-pagination" })
			]
		});
	}
	function NewVideoDialogTitle() {
		return (0, import_jsx_runtime.jsx)("span", {
			style: { padding: "0 10px" },
			"data-tip": "数据来源: 女优页面首页,含磁链分类",
			children: "新作品检测 ❓"
		});
	}
	var new_video_plugin_default = "#actress-card-container {\n    display: grid;\n    grid-template-columns: repeat(auto-fill, minmax(243px, 1fr)); /* 响应式3-5列 */\n    gap: 20px;\n    padding-bottom: 20px;\n    padding-right: 10px;\n    background: #f9f9f9;\n    border-radius: 5px;\n    overflow-y: auto;\n}\n\n.actress-card {\n    background: #fff;\n    border: 1px solid #e0e0e0;\n    border-radius: 8px;\n    box-shadow: 0 4px 8px rgb(0, 0, 0, 0.08);\n    padding: 15px;\n    text-align: center;\n    display: flex;\n    flex-direction: column;\n    justify-content: space-between;\n    position: relative;\n    overflow: hidden;\n}\n\n.actress-card:hover {\n    box-shadow: 0 8px 15px rgb(0, 0, 0, 0.15);\n}\n\n.actress-card-name {\n    font-size: 1.2em;\n    font-weight: bold;\n    color: #007bff;\n    margin-top: 10px;\n}\n\n.actress-card-allname {\n    font-size: 0.9em;\n    color: #999;\n    margin-top: 5px;\n    height: 30px; /* 保证高度一致性 */\n    overflow: hidden;\n    white-space: nowrap; /* 防止文字换行 */\n    text-overflow: ellipsis; /* 当文本溢出时,显示省略号 */\n}\n\n.actress-card-avatar {\n    width: 100px;\n    height: 100px;\n    border-radius: 50%;\n    object-fit: contain;\n    margin: 0 auto;\n    border: 4px solid #f0f0f0;\n    box-shadow: 0 2px 4px rgb(0, 0, 0, 0.1);\n}\n\n.card-tag {\n    position: absolute;\n    top: 15px; /* 调整标签距离顶部的距离 */\n    right: -50px; /* 调整标签距离右侧的距离,负值让它移到外面一点 */\n    width: 150px; /* 标签的宽度,影响斜角长度 */\n    padding: 5px 0; /* 上下内边距 */\n    text-align: center;\n    background-color: #ff4757; /* 标签颜色 */\n    color: white; /* 文字颜色 */\n    font-size: 14px;\n    font-weight: bold;\n    z-index: 10; /* 确保标签在其他内容之上 */\n\n    /* 3. 核心:旋转标签,使其倾斜 */\n    transform: rotate(45deg); /* 45度斜角 */\n\n    /* 可选:添加一些阴影或边框效果 */\n    box-shadow: 0 0 10px rgb(0, 0, 0, 0.3);\n}\n\n.card-new-count-tag {\n    position: absolute;\n    top: 5px;\n    text-align: center;\n    font-size: 14px;\n    font-weight: bold;\n    z-index: 10;\n}\n\n#actress-pagination {\n    padding-top: 10px;\n    text-align: center;\n    border-top: 1px solid #ddd;\n}\n\n@media (width <= 600px) {\n    .page-number-btn {\n        display: none !important;\n    }\n}\n\n.card-btn {\n    width: 44px;\n    height: 44px;\n    border-radius: 50%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    text-decoration: none;\n    border: none;\n    cursor: pointer;\n    background: linear-gradient(145deg, #e0e0e0 0%, #f7f7f7 100%);\n    box-shadow:\n        8px 8px 16px rgb(0, 0, 0, 0.08),\n        -8px -8px 16px rgb(255, 255, 255, 1);\n    transition:\n        box-shadow 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        background-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n}\n\n.card-btn svg,\n.card-btn svg path {\n    transition: fill 0.3s ease;\n}\n\n.card-btn:hover {\n    box-shadow:\n        inset 5px 5px 10px rgb(0, 0, 0, 0.1),\n        inset -5px -5px 10px rgb(255, 255, 255, 0.9);\n    transform: scale(0.97);\n    background: #e0e0e0;\n}\n\n.btn-check-actress svg path {\n    fill: #4caf50;\n}\n\n.btn-check-actress:hover svg path {\n    fill: #388e3c;\n}\n\n.btn-edit-actress svg path {\n    fill: #ffc107;\n}\n\n.btn-edit-actress:hover svg path {\n    fill: #ffb300;\n}\n\n.btn-delete-actress svg path {\n    fill: #f44336;\n}\n\n.btn-delete-actress:hover svg path {\n    fill: #d32f2f;\n}\n";
	var NewVideoPlugin = class extends BasePlugin {
		currentPage = 1;
		pageSize = 30;
		getName() {
			return "NewVideoPlugin";
		}
		async initCss() {
			return new_video_plugin_default;
		}
		async handle() {
			await this.showNewVideoCount();
		}
		async showNewVideoCount() {
			const totalCount = (await storageManager.getFavoriteActressList()).reduce((total, actress) => {
				return total + (actress.newVideoList?.length ?? 0);
			}, 0);
			$("#newVideoCount").text(`${totalCount}`);
		}
		async resetBtnTip() {}
		async openDialog() {
			const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(NewVideoDialog, { refreshSvg: this.refreshSvg }));
			layer.open({
				type: 1,
				title: jsxToString((0, import_jsx_runtime.jsx)(NewVideoDialogTitle, {})),
				content: dialogContent,
				scrollbar: false,
				area: utils.getResponsiveArea(["80%", "90%"]),
				anim: -1,
				success: async (_layerEl, layerIndex) => {
					this.loadData();
					this.bindClick();
					utils.setupEscClose(layerIndex);
				}
			});
		}
		bindClick() {
			$("#reLoad").on("click", (_event) => {
				this.loadData();
				$("#checkNewVideoMsg").text("");
			});
			$("#paramActressType").on("change", (_event) => {
				this.loadData();
			});
		}
		loadData() {
			this.currentPage = 1;
			this.renderActressCards().then();
		}
		async renderActressCards() {
			await renderActressCards(this);
		}
		async editActress(actress) {
			await editActress(this, actress);
		}
		renderPagination(totalCount, totalPages) {
			renderPagination$1(this, totalCount, totalPages);
		}
		async searchAvatar() {
			await searchAvatar();
		}
	};
	function handleBatchAction(plugin, event, btn) {
		event.preventDefault();
		event.stopPropagation();
		const selectedRows = plugin.tableObj.getSelectedData();
		let actionLabel = "";
		let actionType = "";
		if (btn.hasClass("multiple-history-filterBtn")) {
			actionLabel = "屏蔽";
			actionType = FILTER_ACTION;
		} else if (btn.hasClass("multiple-history-favoriteBtn")) {
			actionLabel = "收藏";
			actionType = FAVORITE_ACTION;
		} else if (btn.hasClass("multiple-history-hasWatchBtn")) {
			actionLabel = "已观看";
			actionType = HAS_WATCH_ACTION;
		} else if (btn.hasClass("multiple-history-deleteBtn")) {
			actionLabel = "移除";
			actionType = "delete";
		}
		utils.q(event, `当前已勾选${selectedRows.length}条数据, 是否全标记为 ${actionLabel}?`, async () => {
			const loader = loading();
			try {
				if (actionType === "delete") {
					const carNums = selectedRows.map((row) => row.carNum);
					const removed = await storageManager.batchRemoveCars(carNums);
					if (Number(removed) > 0) show.ok(`已成功删除 ${removed} 个番号`);
					else if (removed === false) show.error("提供的番号中没有一个存在于列表中。");
				} else {
					const updates = JSON.parse(JSON.stringify(selectedRows));
					updates.forEach((item) => {
						item.actionType = actionType;
					});
					await storageManager.saveCarList(updates);
					show.ok("操作成功");
				}
				plugin.tableObj.deselectRow();
				plugin.reloadTable().then();
			} catch (err) {
				clog.error("批量操作失败:", err);
				show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
			} finally {
				loader.close();
			}
		});
	}
	function HistoryActionButtons({ carNum, url, watchedColor, watchedText, favoriteColor, favoriteText, blockColor, blockText }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "action-btns",
			style: {
				display: "flex",
				gap: "5px",
				justifyContent: "center"
			},
			"data-car-num": carNum,
			"data-href": url,
			children: [(0, import_jsx_runtime.jsxs)("div", {
				className: "sub-btns",
				children: [(0, import_jsx_runtime.jsx)("a", {
					className: "jhs-toolbar-menu-btn sub-btns-toggle",
					style: {
						backgroundColor: "#c59d36",
						color: "white",
						marginBottom: "5px"
					},
					children: (0, import_jsx_runtime.jsx)("span", { children: "✏️ 变更" })
				}), (0, import_jsx_runtime.jsxs)("div", {
					className: "sub-btns-menu",
					children: [
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn history-editBtn",
							style: {
								backgroundColor: "#007bff",
								color: "white",
								marginBottom: "5px"
							},
							children: (0, import_jsx_runtime.jsx)("span", { children: "✏️ 编辑" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn history-deleteBtn",
							style: {
								backgroundColor: "#8c8080",
								color: "white",
								marginBottom: "5px"
							},
							children: (0, import_jsx_runtime.jsx)("span", { children: "✂️ 移除" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn history-hasWatchBtn",
							style: {
								backgroundColor: watchedColor,
								marginBottom: "5px"
							},
							children: watchedText
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn history-favoriteBtn",
							style: {
								backgroundColor: favoriteColor,
								marginBottom: "5px"
							},
							children: favoriteText
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn history-filterBtn",
							style: {
								backgroundColor: blockColor,
								marginBottom: "5px"
							},
							children: blockText
						})
					]
				})]
			}), (0, import_jsx_runtime.jsx)("a", {
				className: "jhs-toolbar-menu-btn history-detailBtn",
				style: {
					backgroundColor: "#3397de",
					color: "white",
					marginBottom: "5px"
				},
				children: (0, import_jsx_runtime.jsx)("span", { children: "📄 详情页" })
			})]
		});
	}
	function TableLinkParam({ text }) {
		return (0, import_jsx_runtime.jsx)("a", {
			className: "table-link-param",
			children: text
		});
	}
	function buildHistoryTableOptions(plugin) {
		return {
			...createTabulatorOptions([
				{
					formatter: "rowSelection",
					titleFormatter: "rowSelection",
					hozAlign: "center",
					headerSort: false,
					responsive: 0,
					width: 40,
					titleFormatterParams: { rowRange: "active" },
					cellClick: (_event, cell) => {
						cell.getRow().toggleSelect();
					}
				},
				{
					title: "番号",
					field: "carNum",
					width: 120,
					sorter: "string",
					responsive: 0,
					formatter: (cell, _formatterParams, _onRendered) => {
						const carNum = cell.getData().carNum;
						const dashIndex = carNum.indexOf("-");
						if (dashIndex === -1) return carNum;
						return jsxToString((0, import_jsx_runtime.jsx)(TableLinkParam, { text: carNum.substring(0, dashIndex + 1) })) + carNum.substring(dashIndex + 1);
					}
				},
				{
					title: "演员",
					field: "names",
					minWidth: 200,
					sorter: "string",
					responsive: 5,
					headerSort: true,
					formatter: (cell, _formatterParams, _onRendered) => (cell.getData().names || "").split(" ").filter((part) => part.trim() !== "").map((part) => jsxToString((0, import_jsx_runtime.jsx)(TableLinkParam, { text: part }))).join(" ")
				},
				{
					title: "创建时间",
					field: "createDate",
					width: 170,
					sorter: "string",
					responsive: 4
				},
				{
					title: "修改时间",
					field: "updateDate",
					width: 170,
					sorter: "string",
					responsive: 4
				},
				{
					title: "发行时间",
					field: "publishTime",
					width: 170,
					sorter: "string",
					responsive: 4
				},
				{
					title: "来源",
					field: "url",
					width: 80,
					sorter: "string",
					responsive: 5,
					hozAlign: "left",
					formatter: (cell, _formatterParams, _onRendered) => {
						const url = cell.getData().url;
						if (url) {
							if (url.includes("javdb")) return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
								text: "Javdb",
								color: "#d34f9e"
							}));
							else return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
								text: url,
								color: "#050505"
							}));
						} else return "";
					}
				},
				{
					title: "状态",
					field: "status",
					width: 100,
					sorter: "string",
					responsive: 1,
					headerSort: false,
					formatter: (cell, _formatterParams, _onRendered) => {
						const status = cell.getData().status;
						let color = "";
						let text;
						switch (status) {
							case "filter":
								color = BLOCK_COLOR;
								text = BLOCK_TEXT;
								break;
							case "favorite":
								color = FAVORITE_COLOR;
								text = FAVORITE_TEXT;
								break;
							case "hasWatch":
								color = WATCHED_COLOR;
								text = WATCHED_TEXT;
								break;
							default: text = status;
						}
						return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
							text,
							color
						}));
					}
				},
				{
					title: "备注",
					field: "remark",
					width: 100,
					sorter: "string",
					responsive: 6
				},
				{
					title: "操作",
					sorter: "string",
					minWidth: 150,
					cssClass: "action-cell-dropdown",
					responsive: 0,
					headerSort: false,
					formatter: (cell, _formatterParams, onRendered) => {
						const data = cell.getData();
						onRendered(() => {
							const editBtn = cell.getElement().querySelector(".history-editBtn");
							if (editBtn != null) editBtn.addEventListener("click", () => {
								plugin.editRecord(data);
							});
						});
						return jsxToString((0, import_jsx_runtime.jsx)(HistoryActionButtons, {
							carNum: data.carNum,
							url: data.url || "",
							watchedColor: WATCHED_COLOR,
							watchedText: WATCHED_TEXT,
							favoriteColor: FAVORITE_COLOR,
							favoriteText: FAVORITE_TEXT,
							blockColor: BLOCK_COLOR,
							blockText: BLOCK_TEXT
						}));
					}
				}
			]),
			pagination: true,
			paginationMode: "remote",
			sortMode: "remote",
			ajaxURL: "queryRealm",
			dataLoader: false,
			ajaxRequestFunc: async (_url, _config, params) => {
				const page = params.page;
				const size = params.size;
				const sort = params.sort;
				return await plugin.getDataList(page, size, sort);
			},
			dataReceiveParams: {
				last_page: "maxPage",
				last_row: "totalCount",
				data: "dataList"
			},
			paginationSize: 50,
			paginationSizeSelector: [
				50,
				100,
				1e3,
				99999
			],
			paginationCounter: (_pageNumber, _pageSize, _totalPages, recordCount, _data) => `共 ${recordCount} 条记录`,
			selectableRowsPersistence: false,
			index: "carNum",
			initialSort: [{
				column: "updateDate",
				dir: "desc"
			}]
		};
	}
	async function getDataList(plugin, page, size, sort) {
		const carList = await storageManager.getCarList();
		plugin.allCount = carList.length;
		plugin.filterCount = 0;
		plugin.favoriteCount = 0;
		plugin.hasWatchCount = 0;
		carList.forEach((car) => {
			switch (car.status) {
				case FILTER_ACTION:
					plugin.filterCount++;
					break;
				case FAVORITE_ACTION:
					plugin.favoriteCount++;
					break;
				case HAS_WATCH_ACTION: plugin.hasWatchCount++;
			}
		});
		$("#dataType option[value=\"all\"]").text(`所有 (${plugin.allCount})`);
		$("#dataType option[value=\"filter\"]").text(`${BLOCKED_TEXT} (${plugin.filterCount})`);
		$("#dataType option[value=\"favorite\"]").text(`${FAVORITED_TEXT} (${plugin.favoriteCount})`);
		$("#dataType option[value=\"hasWatch\"]").text(`${WATCHED_TEXT} (${plugin.hasWatchCount})`);
		const selectedType = $("#dataType").val();
		let filtered = selectedType === "all" ? carList : carList.filter((car) => car.status === selectedType);
		const searchText = String($("#searchCarNum").val() ?? "").trim();
		if (searchText) {
			const normalizedSearch = searchText.toLowerCase().replace("-c", "").replace("-uc", "").replace("-4k", "");
			filtered = filtered.filter((row) => {
				const matchCar = row.carNum.toLowerCase().includes(normalizedSearch);
				const matchName = (row.names ? row.names : "").toLowerCase().includes(normalizedSearch);
				return matchCar || matchName;
			});
		}
		if (sort && sort.length > 0) {
			const sortConfig = sort[0];
			const field = sortConfig.field;
			const dir = sortConfig.dir;
			filtered.sort((rowA, rowB) => {
				const valA = rowA[field];
				const valB = rowB[field];
				const isEmptyA = valA == null || valA === "";
				const isEmptyB = valB == null || valB === "";
				if (isEmptyA && !isEmptyB) return 1;
				else if (!isEmptyA && isEmptyB) return -1;
				else if (isEmptyA && isEmptyB) return 0;
				else if (valA < valB) {
					if (dir === "asc") return -1;
					else return 1;
				} else if (valA > valB) {
					if (dir === "asc") return 1;
					else return -1;
				} else return 0;
			});
		}
		const totalCount = filtered.length;
		const maxPage = Math.ceil(totalCount / size);
		const start = (page - 1) * size;
		const end = start + size;
		filtered = filtered.slice(start, end);
		return {
			maxPage,
			dataList: filtered,
			totalCount
		};
	}
	var LABEL_STYLE = {
		display: "block",
		marginBottom: "5px",
		fontWeight: "bold"
	};
	var FIELD_ROW_STYLE = { marginBottom: "15px" };
	function EditRecordDialog({ carNum, names, status, url, remark, inputStyle, textareaStyle, statusOptions }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: { padding: "20px" },
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE,
						children: "番号:"
					}), (0, import_jsx_runtime.jsx)("input", {
						type: "text",
						id: "edit-carNum",
						value: carNum,
						style: {
							...inputStyle,
							backgroundColor: "#f0f0f0"
						},
						readOnly: true
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE,
						children: "演员 (用空格隔开):"
					}), (0, import_jsx_runtime.jsx)("textarea", {
						id: "edit-names",
						style: textareaStyle,
						children: names
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE,
						children: "状态:"
					}), (0, import_jsx_runtime.jsxs)("select", {
						id: "edit-status",
						style: {
							width: "100%",
							padding: "10px",
							border: "1px solid #ddd"
						},
						children: [(0, import_jsx_runtime.jsx)("option", {
							value: "",
							selected: status === "",
							children: "-- 请选择 --"
						}), statusOptions.map((opt) => (0, import_jsx_runtime.jsx)("option", {
							value: opt.value,
							selected: status === opt.value,
							children: opt.text
						}, opt.value))]
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE,
						children: "链接:"
					}), (0, import_jsx_runtime.jsx)("input", {
						type: "text",
						id: "edit-url",
						value: url,
						style: inputStyle
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: FIELD_ROW_STYLE,
					children: [(0, import_jsx_runtime.jsx)("label", {
						style: LABEL_STYLE,
						children: "备注:"
					}), (0, import_jsx_runtime.jsx)("textarea", {
						id: "edit-remark",
						style: textareaStyle,
						children: remark
					})]
				})
			]
		});
	}
	function HistoryDialog({ blockedText, favoritedText, watchedText, watchedColor, favoriteColor, favoriteText, blockColor, blockText }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				padding: "10px 20px",
				height: "100%",
				overflow: "hidden"
			},
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					id: "filterBox",
					style: {
						display: "flex",
						gap: "5px"
					},
					children: [
						(0, import_jsx_runtime.jsxs)("select", {
							id: "dataType",
							style: {
								textAlign: "center",
								minWidth: "150px"
							},
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "all",
									selected: true,
									children: "所有"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "filter",
									children: blockedText
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "favorite",
									children: favoritedText
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "hasWatch",
									children: watchedText
								})
							]
						}),
						(0, import_jsx_runtime.jsx)("input", {
							id: "searchCarNum",
							type: "text",
							placeholder: "搜索番号|演员",
							style: { padding: "4px 5px" }
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "clearSearchbtn",
							className: "a-info",
							style: { marginLeft: "0" },
							children: "重置"
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					id: "allSelectBox",
					style: {
						marginTop: "8px",
						display: "none"
					},
					children: [
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn multiple-history-deleteBtn",
							style: {
								backgroundColor: "#8c8080",
								color: "white",
								marginBottom: "5px"
							},
							children: (0, import_jsx_runtime.jsx)("span", { children: "✂️ 移除" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn multiple-history-hasWatchBtn",
							style: {
								backgroundColor: watchedColor,
								marginBottom: "5px"
							},
							children: watchedText
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn multiple-history-favoriteBtn",
							style: {
								backgroundColor: favoriteColor,
								marginBottom: "5px"
							},
							children: favoriteText
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-toolbar-menu-btn multiple-history-filterBtn",
							style: {
								backgroundColor: blockColor,
								marginBottom: "5px"
							},
							children: blockText
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					id: "table-container",
					style: {
						height: "calc(100% - 50px)",
						overflowX: "hidden"
					}
				})
			]
		});
	}
	function HistoryNavButton({ variant }) {
		switch (variant) {
			case "desktop": return (0, import_jsx_runtime.jsx)("div", {
				className: "navbar-item has-sub-btns is-hoverable historyBtnBox",
				children: (0, import_jsx_runtime.jsx)("a", {
					id: "historyBtn",
					className: "navbar-link nav-btn",
					style: {
						color: "#aade66 !important",
						paddingRight: "15px !important"
					},
					children: "鉴定记录"
				})
			});
			case "mini": return (0, import_jsx_runtime.jsx)("div", {
				className: "navbar-item miniHistoryBtnBox",
				children: (0, import_jsx_runtime.jsx)("a", {
					id: "miniHistoryBtn",
					className: "navbar-link nav-btn",
					style: {
						color: "#aade66 !important",
						paddingLeft: "0 !important",
						paddingRight: "0 !important"
					},
					children: "鉴定记录"
				})
			});
			default: return null;
		}
	}
	var history_plugin_default = "/* 下拉菜单容器(相对定位) */\n.sub-btns {\n    position: relative;\n    display: inline-block;\n}\n\n/* 下拉菜单内容(默认隐藏) */\n.sub-btns-menu {\n    display: none;\n    position: absolute;\n    right: 80px;\n    top: -10px;\n    background: white;\n    padding: 10px;\n    box-shadow: 0 2px 8px rgb(0, 0, 0, 0.15);\n    z-index: 100;\n    border-radius: 4px;\n    overflow: hidden;\n}\n\n/* 点击后显示菜单(JS 控制) */\n.sub-btns-menu.show {\n    display: flex !important;\n    flex-direction: column;\n}\n\n.table-link-param {\n    cursor: pointer;\n}\n";
	var HistoryPlugin = class extends BasePlugin {
		tableObj = null;
		allCount = 0;
		filterCount = 0;
		favoriteCount = 0;
		hasWatchCount = 0;
		constructor() {
			super();
		}
		getName() {
			return "HistoryPlugin";
		}
		async initCss() {
			return history_plugin_default;
		}
		handleResize() {
			if ($(".navbar-search").is(":hidden")) {
				$(".historyBtnBox").show();
				$(".miniHistoryBtnBox").hide();
			} else {
				$(".historyBtnBox").hide();
				$(".miniHistoryBtnBox").show();
			}
		}
		async handle() {
			if (isJavdbSite) {
				$(".navbar-end").prepend(jsxToString((0, import_jsx_runtime.jsx)(HistoryNavButton, { variant: "desktop" })));
				$(".navbar-search").css("margin-left", "0").before(jsxToString((0, import_jsx_runtime.jsx)(HistoryNavButton, { variant: "mini" })));
				this.handleResize();
				$(window).resize(() => {
					this.handleResize();
				});
				$("#historyBtn,#miniHistoryBtn").on("click", () => this.openHistory());
			}
			this.bindClick();
		}
		openHistory() {
			layer.open({
				type: 1,
				title: "鉴定记录",
				content: jsxToString((0, import_jsx_runtime.jsx)(HistoryDialog, {
					blockedText: BLOCKED_TEXT,
					favoritedText: FAVORITED_TEXT,
					watchedText: WATCHED_TEXT,
					watchedColor: WATCHED_COLOR,
					favoriteColor: FAVORITE_COLOR,
					favoriteText: FAVORITE_TEXT,
					blockColor: BLOCK_COLOR,
					blockText: BLOCK_TEXT
				})),
				scrollbar: false,
				shadeClose: true,
				area: utils.getResponsiveArea(["70%", "90%"]),
				anim: -1,
				success: async () => {
					await this.loadTableData();
					$(".layui-layer-content").on("click", "#clearSearchbtn", async () => {
						$("#searchCarNum").val("");
						$("#dataType").val("all");
						await this.reloadTable();
						$("#allSelectBox").hide();
					}).on("focusout keydown", "#searchCarNum", async (event) => {
						if (event.type === "focusout" || event.key === "Enter") {
							if (event.key === "Enter") event.preventDefault();
							if (event.type === "keydown" && event.key !== "Enter") return;
							await this.reloadTable();
						}
					}).on("click", ".table-link-param", async (event) => {
						const link = $(event.currentTarget);
						$("#searchCarNum").val(link.text());
						await this.reloadTable();
					}).on("change", "#dataType", async () => {
						await this.reloadTable();
					});
				},
				end: () => {
					if (this.tableObj) {
						this.tableObj.destroy();
						this.tableObj = null;
					}
					refresh();
				}
			});
		}
		async reloadTable() {
			this.tableObj.deselectRow();
			this.tableObj.setPage(1);
		}
		bindClick() {
			document.addEventListener("click", function(event) {
				const target = event.target;
				if (target.closest(".sub-btns-toggle")) {
					const menu = target.closest(".sub-btns").querySelector(".sub-btns-menu");
					document.querySelectorAll(".sub-btns-menu.show").forEach((el) => {
						if (el !== menu) el.classList.remove("show");
					});
					menu.classList.toggle("show");
				} else document.querySelectorAll(".sub-btns-menu.show").forEach((el) => {
					el.classList.remove("show");
				});
			});
			$(document).on("click", ".history-deleteBtn, .history-filterBtn, .history-favoriteBtn, .history-hasWatchBtn, .history-detailBtn", (event) => {
				event.preventDefault();
				event.stopPropagation();
				const btn = $(event.currentTarget);
				const actionBtns = btn.closest(".action-btns");
				const carNum = actionBtns.attr("data-car-num");
				const href = actionBtns.attr("data-href");
				const applyAction = async (actionType) => {
					await storageManager.saveCar({
						carNum: carNum ?? "",
						url: href,
						names: void 0,
						actionType
					});
					refresh();
					await this.reloadTable();
				};
				if (btn.hasClass("history-filterBtn")) utils.q(event, `是否屏蔽${carNum}?`, () => applyAction(FILTER_ACTION));
				else if (btn.hasClass("history-favoriteBtn")) applyAction(FAVORITE_ACTION).then();
				else if (btn.hasClass("history-hasWatchBtn")) applyAction(HAS_WATCH_ACTION).then();
				else if (btn.hasClass("history-deleteBtn")) this.handleDelete(event, carNum);
				else if (btn.hasClass("history-detailBtn")) this.handleClickDetail(event, {
					carNum,
					url: href
				}).then();
			});
			$(document).on("click", ".multiple-history-deleteBtn, .multiple-history-filterBtn, .multiple-history-favoriteBtn, .multiple-history-hasWatchBtn", (event) => {
				handleBatchAction(this, event, $(event.currentTarget));
			});
		}
		async getDataList(page, size, sort) {
			return getDataList(this, page, size, sort);
		}
		async loadTableData() {
			this.tableObj = new Tabulator("#table-container", buildHistoryTableOptions(this));
			this.tableObj.on("rowSelectionChanged", (rows, _deselectedRows, _selected, _deselected) => {
				const selectBox = $("#allSelectBox");
				const filterBox = $("#filterBox");
				if (rows && rows.length > 0) {
					filterBox.hide();
					selectBox.show();
				} else {
					filterBox.show();
					selectBox.hide();
				}
			});
			this.tableObj.on("rowDblClick", (_event, row) => {
				row.toggleSelect();
			});
			this.tableObj.on("tableBuilt", async () => {});
		}
		handleDelete(event, carNum) {
			utils.q(event, `是否移除${carNum}?`, async () => {
				await storageManager.removeCar(carNum);
				this.getBean("ListPagePlugin").showCarNumBox(carNum);
				this.reloadTable().then();
			});
		}
		async handleClickDetail(event, carInfo) {
			if (isJavdbSite) {
				if (carInfo.carNum.includes("FC2-")) {
					const movieId = this.parseMovieId(carInfo.url);
					this.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carInfo.carNum, carInfo.url);
				} else {
					if (!carInfo.url) {
						window.open("/search?q=" + encodeURIComponent(carInfo.carNum), "_blank", "noopener,noreferrer");
						return;
					}
					utils.openPage(carInfo.url, carInfo.carNum, false, event);
				}
			}
		}
		async editRecord(record) {
			const carNum = record.carNum;
			const names = record.names || "";
			const url = record.url || "";
			const status = record.status;
			const remark = record.remark || "";
			const textareaStyle = {
				width: "100%",
				padding: "8px",
				border: "1px solid #ccc",
				borderRadius: "4px",
				minHeight: "60px",
				overflowY: "hidden"
			};
			const inputStyle = {
				width: "100%",
				padding: "8px",
				border: "1px solid #ccc",
				borderRadius: "4px"
			};
			const statusOptions = [
				{
					value: FILTER_ACTION,
					text: BLOCK_TEXT
				},
				{
					value: FAVORITE_ACTION,
					text: FAVORITE_TEXT
				},
				{
					value: HAS_WATCH_ACTION,
					text: WATCHED_TEXT
				}
			];
			console.log(statusOptions);
			layer.open({
				type: 1,
				title: `编辑记录: ${carNum}`,
				area: ["500px", "650px"],
				content: jsxToString((0, import_jsx_runtime.jsx)(EditRecordDialog, {
					carNum,
					names,
					status,
					url,
					remark,
					inputStyle,
					textareaStyle,
					statusOptions
				})),
				btn: ["保存", "取消"],
				success: () => {
					const autoResize = (el) => {
						el.css("height", "auto");
						el.css("height", el[0].scrollHeight + 15 + "px");
					};
					const namesInput = $("#edit-names");
					namesInput.on("input", (event) => autoResize($(event.currentTarget)));
					autoResize(namesInput);
					const remarkInput = $("#edit-remark");
					remarkInput.on("input", (event) => autoResize($(event.currentTarget)));
					autoResize(remarkInput);
				},
				yes: async (index) => {
					const names = String($("#edit-names").val() ?? "").trim();
					const status = $("#edit-status").val();
					const url = String($("#edit-url").val() ?? "").trim();
					const remark = String($("#edit-remark").val() ?? "").trim();
					const updated = {
						...record,
						names,
						actionType: status,
						url,
						remark
					};
					await storageManager.updateCarInfo(updated);
					this.tableObj.setData();
					layer.close(index);
				}
			});
		}
	};
	var VIDEO_QUALITY_LIST = [
		{
			id: "video-mhb",
			quality: "dmb_w",
			text: "旧视频源-中画质宽版 (404p)",
			canSelect: false
		},
		{
			id: "video-mhb",
			quality: "sm_s",
			text: "旧视频源-低画质 (240p)",
			canSelect: false
		},
		{
			id: "video-mhb",
			quality: "dm_s",
			text: "旧视频源-中画质 (360p)",
			canSelect: false
		},
		{
			id: "video-mhb",
			quality: "dmb_s",
			text: "旧视频源-中画质 (480p)",
			canSelect: false
		},
		{
			id: "video-mhb",
			quality: "mhb_w",
			text: "旧视频源-高画质宽版 (404p)",
			canSelect: false
		},
		{
			id: "video-mmb",
			quality: "mmb",
			text: "中画质 (432p)",
			canSelect: true
		},
		{
			id: "video-mhb",
			quality: "mhb",
			text: "高画质 (576p)",
			canSelect: true
		},
		{
			id: "video-hmb",
			quality: "hmb",
			text: "HD (720p)",
			canSelect: true
		},
		{
			id: "video-hhb",
			quality: "hhb",
			text: "FullHD (1080p)",
			canSelect: true
		},
		{
			id: "video-hhbs",
			quality: "hhbs",
			text: "FullHD (1080p60fps)",
			canSelect: true
		},
		{
			id: "video-4k",
			quality: "4k",
			text: "4K (2160p)",
			canSelect: true
		},
		{
			id: "video-4ks",
			quality: "4ks",
			text: "4K (2160p60fps)",
			canSelect: true
		}
	];
	var CREDENTIAL_ID_KEY = "jhs:credential_id";
	var LAST_AUTO_BACKUP_DATE_KEY = "jhs:last_auto_backup_date";
	var DEFAULT_AUTO_BACKUP_CONFIG = {
		enabled: true,
		frequency: "daily"
	};
	function generateUuid() {
		if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID();
		const bytes = new Uint8Array(16);
		crypto.getRandomValues(bytes);
		bytes[6] = bytes[6] & 15 | 64;
		bytes[8] = bytes[8] & 63 | 128;
		const hex = [];
		bytes.forEach((b) => hex.push(b.toString(16).padStart(2, "0")));
		return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
	}
	function getCredentialId() {
		let id = GM_getValue(CREDENTIAL_ID_KEY, void 0);
		if (!id) {
			id = generateUuid();
			GM_setValue(CREDENTIAL_ID_KEY, id);
		}
		return id;
	}
	function getTodayStr() {
		const now = new Date();
		return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
	}
	function shouldAutoBackup(config) {
		if (!config.enabled || config.frequency === "disabled") return false;
		if (config.frequency === "everyOpen") return true;
		return getTodayStr() !== GM_getValue(LAST_AUTO_BACKUP_DATE_KEY, void 0);
	}
	function markAutoBackupDone() {
		GM_setValue(LAST_AUTO_BACKUP_DATE_KEY, getTodayStr());
	}
	function getAutoBackupFileName() {
		return `auto_${getCredentialId()}.json`;
	}
	var require_react_production = __commonJSMin(((exports) => {
		var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
		var ReactNoopUpdateQueue = {
			isMounted: function() {
				return !1;
			},
			enqueueForceUpdate: function() {},
			enqueueReplaceState: function() {},
			enqueueSetState: function() {}
		};
		var assign = Object.assign;
		var emptyObject = {};
		function Component(props, context, updater) {
			this.props = props;
			this.context = context;
			this.refs = emptyObject;
			this.updater = updater || ReactNoopUpdateQueue;
		}
		Component.prototype.isReactComponent = {};
		Component.prototype.setState = function(partialState, callback) {
			if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState) throw Error("takes an object of state variables to update or a function which returns an object of state variables.");
			this.updater.enqueueSetState(this, partialState, callback, "setState");
		};
		Component.prototype.forceUpdate = function(callback) {
			this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
		};
		function ComponentDummy() {}
		ComponentDummy.prototype = Component.prototype;
		function PureComponent(props, context, updater) {
			this.props = props;
			this.context = context;
			this.refs = emptyObject;
			this.updater = updater || ReactNoopUpdateQueue;
		}
		var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
		pureComponentPrototype.constructor = PureComponent;
		assign(pureComponentPrototype, Component.prototype);
		pureComponentPrototype.isPureReactComponent = !0;
		Array.isArray;
		var hasOwnProperty = Object.prototype.hasOwnProperty;
		function ReactElement(type, key, props) {
			var refProp = props.ref;
			return {
				$$typeof: REACT_ELEMENT_TYPE,
				type,
				key,
				ref: void 0 !== refProp ? refProp : null,
				props
			};
		}
		exports.createElement = function(type, config, children) {
			var propName, props = {}, key = null;
			if (null != config) for (propName in void 0 !== config.key && (key = "" + config.key), config) hasOwnProperty.call(config, propName) && "key" !== propName && "__self" !== propName && "__source" !== propName && (props[propName] = config[propName]);
			var childrenLength = arguments.length - 2;
			if (1 === childrenLength) props.children = children;
			else if (1 < childrenLength) {
				for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++) childArray[i] = arguments[i + 2];
				props.children = childArray;
			}
			if (type && type.defaultProps) for (propName in childrenLength = type.defaultProps, childrenLength) void 0 === props[propName] && (props[propName] = childrenLength[propName]);
			return ReactElement(type, key, props);
		};
	}));
	var import_react = __commonJSMin(((exports, module) => {
		module.exports = require_react_production();
	}))();
	function HelpDialog() {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "help-container",
			children: [
				(0, import_jsx_runtime.jsx)("h1", {
					style: {
						fontSize: "22px",
						marginBottom: "20px",
						color: "#2c3e50",
						borderBottom: "1px solid #eee",
						paddingBottom: "10px"
					},
					children: "使用说明"
				}),
				(0, import_jsx_runtime.jsxs)("details", {
					className: "help-section",
					children: [(0, import_jsx_runtime.jsx)("summary", { children: "1. 无法查看预览视频,提示分流?" }), (0, import_jsx_runtime.jsxs)("div", {
						className: "help-content",
						children: [
							(0, import_jsx_runtime.jsx)("p", { children: "JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。" }),
							(0, import_jsx_runtime.jsx)("p", { children: "这样会导致二者无法同时使用,需要对其一进行代理转发。" }),
							(0, import_jsx_runtime.jsx)("p", { children: "将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。" }),
							(0, import_jsx_runtime.jsxs)("p", { children: [
								(0, import_jsx_runtime.jsxs)("a", {
									href: "https://youtu.be/wQUK8z_YeU4?t=121",
									target: "_blank",
									children: ["Clash Verge分流规则设置", " "]
								}),
								" ",
								"(如果你是别的代理软件,自行搜索如何分流)"
							] })
						]
					})]
				}),
				(0, import_jsx_runtime.jsxs)("details", {
					className: "help-section",
					children: [(0, import_jsx_runtime.jsx)("summary", { children: "2. 如何屏蔽某一系列的番号?" }), (0, import_jsx_runtime.jsx)("div", {
						className: "help-content",
						children: (0, import_jsx_runtime.jsx)("p", { children: "设置中-屏蔽配置-添加视频标题关键词,如: VENX-" })
					})]
				}),
				(0, import_jsx_runtime.jsxs)("details", {
					className: "help-section",
					children: [(0, import_jsx_runtime.jsx)("summary", { children: "3. 屏蔽某演员,如何只屏蔽单体影片?" }), (0, import_jsx_runtime.jsxs)("div", {
						className: "help-content",
						children: [(0, import_jsx_runtime.jsx)("p", { children: "屏蔽演员前,先筛选分类,再点屏蔽" }), (0, import_jsx_runtime.jsx)("img", {
							src: "https://imgur.com/Ue7eCAi.png",
							alt: "屏蔽演员前,先筛选分类,再点屏蔽"
						})]
					})]
				})
			]
		});
	}
	var help_dialog_default = ".help-container {\n    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n    color: #333;\n    padding: 15px;\n    max-height: 100%;\n    overflow-y: auto;\n}\n\n.help-section {\n    margin-bottom: 25px;\n}\n\n.help-section summary {\n    font-size: 18px;\n    color: #3498db;\n    margin-bottom: 12px;\n    cursor: pointer;\n}\n\n.help-content {\n    background-color: #f9f9f9;\n    border-radius: 5px;\n    padding: 15px;\n    border-left: 4px solid #3498db;\n}\n\n.help-content p {\n    line-height: 1.6;\n    margin-bottom: 10px;\n}\n\n.help-section img {\n    max-width: 100%;\n    height: auto;\n    border: 1px solid #ddd;\n    border-radius: 4px;\n    box-shadow: 0 2px 4px rgb(0, 0, 0, 0.1);\n}\n";
	var DEFAULT_LIST_MERGE_PATTERN = "^(.+?)(\\d+)$";
	function parseListName(name, pattern) {
		const trimmed = name.trim();
		if (!trimmed) return null;
		const matched = pattern.exec(trimmed);
		if (!matched) return null;
		const base = (matched[1] ?? "").trim();
		const numRaw = matched[2] ?? "";
		if (!base || !numRaw) return null;
		if (!/[^\d]/.test(base)) return null;
		const num = Number(numRaw);
		if (!Number.isFinite(num)) return null;
		return {
			base,
			num
		};
	}
	function compileMergePattern(source) {
		const text = (source ?? "").trim() || "^(.+?)(\\d+)$";
		try {
			return new RegExp(text);
		} catch {
			return new RegExp(DEFAULT_LIST_MERGE_PATTERN);
		}
	}
	function isMergeExcluded(fullName, base, config) {
		if (config.excludeNames.size === 0) return false;
		if (config.excludeNames.has(fullName.trim().toLowerCase())) return true;
		if (base && config.excludeNames.has(base.trim().toLowerCase())) return true;
		return false;
	}
	function buildMergeConfig(settings) {
		const enabled = settings.listMergeEnabled !== "no";
		const pattern = compileMergePattern(settings.listMergePattern);
		const rawNames = settings.listMergeExcludeNames;
		const nameList = Array.isArray(rawNames) ? rawNames : typeof rawNames === "string" ? rawNames.split(",") : [];
		return {
			enabled,
			pattern,
			excludeNames: new Set(nameList.map((n) => String(n).trim().toLowerCase()).filter(Boolean))
		};
	}
	function isMemberFull(member) {
		if (member.count !== null && member.count >= 501) return true;
		if (member.disabled && !member.checked) return true;
		return false;
	}
	function sortMembersByNum(members) {
		return [...members].sort((a, b) => {
			if (a.num !== b.num) return a.num - b.num;
			return a.listId < b.listId ? -1 : a.listId > b.listId ? 1 : 0;
		});
	}
	function pickTargetMember(members) {
		for (const member of sortMembersByNum(members)) {
			if (member.checked) continue;
			if (isMemberFull(member)) continue;
			return member;
		}
		return null;
	}
	function nextListName(group) {
		const maxNum = group.members.reduce((max, m) => Math.max(max, m.num), 0);
		return `${group.base}${maxNum + 1}`;
	}
	function computeGroupCheckState(group) {
		const containing = group.members.filter((m) => m.checked);
		const checked = containing.length > 0;
		return {
			checked,
			indeterminate: checked && containing.length < group.members.length,
			containing
		};
	}
	function groupEntries(items, config) {
		const groupMap = new Map();
		const singleIndexes = [];
		if (!config.enabled) return {
			groups: [],
			singleIndexes: items.map((_, i) => i)
		};
		const pendingSingles = [];
		items.forEach((item, index) => {
			const parsed = parseListName(item.name, config.pattern);
			if (!parsed || isMergeExcluded(item.name, parsed.base, config)) {
				pendingSingles.push(index);
				return;
			}
			let group = groupMap.get(parsed.base);
			if (!group) {
				group = {
					base: parsed.base,
					members: []
				};
				groupMap.set(parsed.base, group);
			}
			group.members.push({
				listId: item.listId,
				name: item.name,
				num: parsed.num,
				count: item.count,
				checked: item.checked,
				disabled: item.disabled
			});
		});
		for (const index of pendingSingles) {
			const item = items[index];
			const group = groupMap.get(item.name.trim());
			if (group && !isMergeExcluded(item.name, null, config)) group.members.push({
				listId: item.listId,
				name: item.name,
				num: 0,
				count: item.count,
				checked: item.checked,
				disabled: item.disabled
			});
			else singleIndexes.push(index);
		}
		const collator = new Intl.Collator("zh-CN", {
			numeric: true,
			sensitivity: "base"
		});
		const groups = Array.from(groupMap.values()).sort((a, b) => collator.compare(a.base, b.base));
		singleIndexes.sort((a, b) => a - b);
		return {
			groups,
			singleIndexes
		};
	}
	var GM_KEY_WEBDAV_URL = "jhs_webdav_url";
	var GM_KEY_WEBDAV_USERNAME = "jhs_webdav_username";
	var GM_KEY_WEBDAV_PASSWORD = "jhs_webdav_password";
	var GM_KEY_BACKUP_PASSWORD = "jhs_backup_password";
	var GM_KEY_GIT_API_URL = "jhs_git_api_url";
	var GM_KEY_GIT_REPO = "jhs_git_repo";
	var GM_KEY_GIT_BRANCH = "jhs_git_branch";
	var GM_KEY_GIT_TOKEN = "jhs_git_token";
	var RATING_CONFIG = {
		DEBUG_MODE: false,
		IDB_NAME: "JAV-JHS",
		IDB_STORE: "appData",
		CAR_LIST_KEY: "car_list",
		RATING_CACHE_KEY: "jdb:rating_cache_v2",
		IDB_RATING_KEY: "jhsRatingDisplay_data",
		ITEM_SELECTOR: ".movie-list .item, .is-user-page .column.is-one-quarter",
		HOVER_DELAY: 500,
		FETCH_CONCURRENCY: 3,
		FETCH_TIMEOUT: 8e3,
		FETCH_RETRY: 1
	};
	var parser = new DOMParser();
	function log(action, ...args) {
		if (RATING_CONFIG.DEBUG_MODE) console.log(`%c[JHS-Rating][${action}]`, "color:#fff;background:#ffc107;padding:2px 4px;border-radius:2px;", ...args);
	}
	function debounce(fn, wait) {
		let timer;
		return function(...args) {
			clearTimeout(timer);
			timer = setTimeout(() => fn.apply(this, args), wait);
		};
	}
	function createLimiter(max) {
		let active = 0;
		const queue = [];
		const runNext = () => {
			if (active >= max || queue.length === 0) return;
			active++;
			const job = queue.shift();
			Promise.resolve().then(job.fn).then(job.resolve).catch(job.reject).finally(() => {
				active--;
				runNext();
			});
		};
		return { run(fn) {
			return new Promise((resolve, reject) => {
				queue.push({
					fn,
					resolve,
					reject
				});
				runNext();
			});
		} };
	}
	function getSafeUrl(url, base = window.location.origin) {
		if (!url) return "";
		try {
			if (url.startsWith("http")) return url;
			if (url.startsWith("//")) return "https:" + url;
			return new URL(url, base).href;
		} catch {
			return url;
		}
	}
	function normalizeCode(raw) {
		if (!raw) return "";
		return raw.trim().toUpperCase().replace(/\s+/g, "-");
	}
	function getCode$2(item) {
		if (item.dataset.code) return item.dataset.code;
		const code = normalizeCode(item.querySelector(".video-title strong")?.textContent || "");
		if (code) item.dataset.code = code;
		return code;
	}
	function getAnchor(item) {
		return item?.querySelector("a.box, .box a") ?? null;
	}
	var RatingUtils = {
		parser,
		log,
		debounce,
		createLimiter,
		getSafeUrl,
		normalizeCode,
		getCode: getCode$2,
		getAnchor
	};
	function openIdb() {
		return new Promise((resolve, reject) => {
			const req = indexedDB.open(RATING_CONFIG.IDB_NAME);
			req.onsuccess = () => resolve(req.result);
			req.onerror = () => reject(req.error);
		});
	}
	async function buildWatchedMap() {
		const list = await storageManager.getCarList();
		const map = new Map();
		list.forEach((item) => {
			if (item && item.status === "hasWatch" && item.carNum) map.set(RatingUtils.normalizeCode(item.carNum), item);
		});
		RatingUtils.log("WATCHED_MAP", `${map.size} 项`);
		return map;
	}
	var RatingCache = {
		_data: {},
		async load() {
			let legacyData = null;
			try {
				const raw = localStorage.getItem(RATING_CONFIG.RATING_CACHE_KEY);
				if (raw) {
					const parsed = JSON.parse(raw);
					if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) legacyData = parsed;
				}
			} catch {}
			let idbData = null;
			try {
				const db = await openIdb();
				idbData = await new Promise((resolve) => {
					try {
						const req = db.transaction(RATING_CONFIG.IDB_STORE, "readonly").objectStore(RATING_CONFIG.IDB_STORE).get(RATING_CONFIG.IDB_RATING_KEY);
						req.onsuccess = () => resolve(req.result ?? null);
						req.onerror = () => resolve(null);
					} catch {
						resolve(null);
					}
				});
			} catch (error) {
				RatingUtils.log("CACHE_IDB_LOAD_ERROR", error);
			}
			if (legacyData || idbData) {
				const merged = { ...idbData ?? {} };
				for (const [code, legacyEntry] of Object.entries(legacyData ?? {})) {
					const idbEntry = merged[code];
					if (!idbEntry || legacyEntry.updatedAt >= idbEntry.updatedAt) merged[code] = legacyEntry;
				}
				this._data = merged;
				if (idbData || legacyData) {
					if (await this._syncToIdb()) localStorage.removeItem(RATING_CONFIG.RATING_CACHE_KEY);
				}
				RatingUtils.log("CACHE", `IndexedDB: ${Object.keys(merged).length} 条`);
				return;
			}
			this._data = {};
		},
		save() {
			this._syncToIdb();
		},
		async _syncToIdb() {
			try {
				const db = await openIdb();
				await new Promise((resolve, reject) => {
					const tx = db.transaction(RATING_CONFIG.IDB_STORE, "readwrite");
					tx.objectStore(RATING_CONFIG.IDB_STORE).put(this._data, RATING_CONFIG.IDB_RATING_KEY);
					tx.oncomplete = () => resolve();
					tx.onerror = () => reject(tx.error);
					tx.onabort = () => reject(tx.error);
				});
				localStorage.removeItem(RATING_CONFIG.RATING_CACHE_KEY);
				return true;
			} catch (error) {
				RatingUtils.log("IDB_SYNC_ERROR", error);
				return false;
			}
		},
		get(code) {
			return this._data[code] || null;
		},
		set(code, rating) {
			const existed = this._data[code];
			if (existed && existed.rating === rating) return false;
			this._data[code] = {
				rating,
				updatedAt: Date.now()
			};
			this.save();
			return true;
		},
		clear() {
			this._data = {};
			localStorage.removeItem(RATING_CONFIG.RATING_CACHE_KEY);
			openIdb().then((db) => {
				try {
					db.transaction(RATING_CONFIG.IDB_STORE, "readwrite").objectStore(RATING_CONFIG.IDB_STORE).delete(RATING_CONFIG.IDB_RATING_KEY);
				} catch {}
			}).catch(() => {});
		},
		size() {
			return Object.keys(this._data).length;
		}
	};
	var PBKDF2_ITERATIONS = 1e5;
	var SALT_BYTES = 16;
	var IV_BYTES = 12;
	function base64ToBytes(b64) {
		const binary = atob(b64);
		const bytes = new Uint8Array(binary.length);
		for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
		return bytes;
	}
	function bytesToBase64(bytes) {
		let binary = "";
		for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
		return btoa(binary);
	}
	async function deriveKey(password, salt) {
		const encoder = new TextEncoder();
		const keyMaterial = await crypto.subtle.importKey("raw", encoder.encode(password), "PBKDF2", false, ["deriveKey"]);
		return crypto.subtle.deriveKey({
			name: "PBKDF2",
			salt,
			iterations: PBKDF2_ITERATIONS,
			hash: "SHA-256"
		}, keyMaterial, {
			name: "AES-GCM",
			length: 256
		}, false, ["encrypt", "decrypt"]);
	}
	async function encryptBackupV2(plaintext, password) {
		const encoder = new TextEncoder();
		const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
		const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
		const key = await deriveKey(password, salt);
		const ciphertext = await crypto.subtle.encrypt({
			name: "AES-GCM",
			iv
		}, key, encoder.encode(plaintext));
		const envelope = {
			v: 2,
			alg: "AES-GCM",
			kdf: "PBKDF2-SHA-256",
			iter: PBKDF2_ITERATIONS,
			salt: bytesToBase64(salt),
			iv: bytesToBase64(iv),
			ct: bytesToBase64(new Uint8Array(ciphertext)),
			ts: new Date().toISOString()
		};
		return JSON.stringify(envelope);
	}
	async function decryptBackupV2(envelopeJson, password) {
		const envelope = JSON.parse(envelopeJson);
		if (envelope.v !== 2) throw new Error(`不支持的备份格式版本: ${envelope.v}`);
		const salt = base64ToBytes(envelope.salt);
		const iv = base64ToBytes(envelope.iv);
		const ct = base64ToBytes(envelope.ct);
		const key = await deriveKey(password, salt);
		const plainBuffer = await crypto.subtle.decrypt({
			name: "AES-GCM",
			iv
		}, key, ct);
		return new TextDecoder().decode(plainBuffer);
	}
	function isBackupV2(content) {
		try {
			const parsed = JSON.parse(content);
			return parsed && parsed.v === 2 && parsed.alg === "AES-GCM";
		} catch {
			return false;
		}
	}
	var BACKUP_LOCAL_STORAGE_KEYS = [
		"jhs_other_site",
		"jhs_other_site_dmm",
		"jhs_dmm_video",
		"jhs_translate",
		"jhs_actress_info",
		"jhs_score_info",
		"jhs_screenShot",
		"jhs_visit_history",
		"jdb:rating_cache_v2",
		"jhs_enabled_sites",
		"jhs_foldCategory",
		"jhs_sortMethod",
		"jhs_fancyboxThumbs",
		"jhs_upgrade_flags",
		"jhs_videoMuted",
		"jhs_magnetHub_selectedEngine",
		"jhs_appAuthorization"
	];
	var BACKUP_GM_KEYS = [
		"jdb:list-reading-status",
		"jdb:list-rating",
		"jdb:list-last-uri",
		"jdb:list-sort",
		"jdb:list-filter-read",
		"jdb:list-filter-rating",
		"jdb:list-waterfall-enabled"
	];
	function collectLocalStorageBackup() {
		const out = {};
		for (const key of BACKUP_LOCAL_STORAGE_KEYS) try {
			const v = localStorage.getItem(key);
			if (v != null && v !== "") out[key] = v;
		} catch {}
		return out;
	}
	function collectPersistentDataBackup() {
		if (!persistentDataStore.isInitialized || !persistentDataStore.isActive) return void 0;
		return persistentDataStore.exportNamespaces(PERSISTENT_DATA_NAMESPACES);
	}
	function collectGmStorageBackup() {
		const out = {};
		for (const key of BACKUP_GM_KEYS) try {
			const v = GM_getValue(key, void 0);
			if (v !== void 0) out[key] = v;
		} catch {}
		return out;
	}
	var ALLOWED_LOCALSTORAGE_KEYS = new Set(BACKUP_LOCAL_STORAGE_KEYS);
	var ALLOWED_GM_KEYS = new Set(BACKUP_GM_KEYS);
	function stripBackupExtras(data) {
		const ls = data.__localStorage;
		const gm = data.__gmStorage;
		const persistent = data.__persistentData;
		delete data.__localStorage;
		delete data.__gmStorage;
		delete data.__persistentData;
		return {
			ls: ls && typeof ls === "object" ? ls : void 0,
			gm: gm && typeof gm === "object" ? gm : void 0,
			persistent: persistent && typeof persistent === "object" ? persistent : void 0
		};
	}
	async function applyBackupExtras(extras) {
		const appliedNamespaces = new Set();
		if (extras.persistent) {
			if (!persistentDataStore.isInitialized || !persistentDataStore.isActive) throw new Error("持久数据存储不可用,无法导入 IndexedDB 备份");
			for (const [namespace, values] of Object.entries(extras.persistent)) {
				if (!PERSISTENT_DATA_NAMESPACES.includes(namespace)) {
					console.warn(`[JHS 备份] 跳过未授权的持久数据 namespace: ${namespace}`);
					continue;
				}
				await persistentDataStore.replaceNamespace(namespace, values);
				appliedNamespaces.add(namespace);
			}
		}
		if (extras.ls) for (const key of Object.keys(extras.ls)) {
			const namespace = getPersistentNamespaceForLegacyKey(key);
			const raw = extras.ls[key];
			if (namespace && !appliedNamespaces.has(namespace)) {
				if (raw == null || raw === "") continue;
				try {
					const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
					if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("旧缓存不是对象");
					if (persistentDataStore.isInitialized && persistentDataStore.isActive) await persistentDataStore.replaceNamespace(namespace, parsed);
					else localStorage.setItem(key, JSON.stringify(parsed));
				} catch (error) {
					console.warn(`[JHS 备份] 恢复持久数据 ${key} 失败:`, error instanceof Error ? error.message : String(error));
				}
				continue;
			}
			if (!ALLOWED_LOCALSTORAGE_KEYS.has(key)) {
				console.warn(`[JHS 备份] 跳过未授权的 localStorage 键: ${key}`);
				continue;
			}
			if (raw == null || raw === "") continue;
			try {
				localStorage.setItem(key, typeof raw === "string" ? raw : JSON.stringify(raw));
			} catch (error) {
				console.warn(`[JHS 备份] 恢复 localStorage.${key} 失败:`, error instanceof Error ? error.message : String(error));
			}
		}
		if (extras.gm) for (const key of Object.keys(extras.gm)) {
			if (!ALLOWED_GM_KEYS.has(key)) {
				console.warn(`[JHS 备份] 跳过未授权的 GM 键: ${key}`);
				continue;
			}
			try {
				GM_setValue(key, extras.gm[key]);
			} catch (error) {
				console.warn(`[JHS 备份] 恢复 GM.${key} 失败:`, error instanceof Error ? error.message : String(error));
			}
		}
	}
	var STATUS_LIST = [
		"filter",
		"favorite",
		"hasDown",
		"hasWatch"
	];
	var STATUS_CONFIG = {
		favorite: {
			label: "⭐ 已收藏",
			color: "#25b1dc"
		},
		hasWatch: {
			label: "🔍 已观看",
			color: "#d7a80c"
		},
		filter: {
			label: "🚫 已屏蔽",
			color: "#de3333"
		},
		hasDown: {
			label: "💾 已下载",
			color: "#1f7a3d"
		}
	};
	var GM_KEY_CAR_STATUS_DATA = "jhs_car_status_data";
	var GM_KEY_CAR_STATUS_DELTA = "jhs_car_status_delta";
	var GM_KEY_CAR_STATUS_REVISION = "jhs_car_status_revision";
	var MISSAV_DB_NAME = "MissAV-CarStatus";
	var MISSAV_DB_STORE = "cars";
	var MISSAV_DB_META_STORE = "sync_meta";
	var MISSAV_SYNC_META_KEY = "car_status_revision";
	var MISSAV_SYNC_LOCK_NAME = "jhs-missav-car-status-sync";
	var LOG_PREFIX_JHS = "[JHS CarSync]";
	var LOG_PREFIX_MISSAV = "[MissAV]";
	function normalizeUrl(rawUrl) {
		let path;
		try {
			if (/^https?:\/\//.test(rawUrl)) {
				const u = new URL(rawUrl);
				path = u.pathname + (u.search || "");
			} else if (rawUrl.startsWith("/")) path = rawUrl;
			else return {
				code: rawUrl,
				malformed: true
			};
		} catch {
			return {
				code: rawUrl,
				malformed: true
			};
		}
		if (path.startsWith("/v/")) return {
			code: path.slice(3),
			malformed: false
		};
		return {
			code: path,
			malformed: false
		};
	}
	function buildJavdbUrl(urlPath) {
		if (!urlPath) return "https://javdb.com";
		if (urlPath.startsWith("/")) return "https://javdb.com" + urlPath;
		return "https://javdb.com/v/" + urlPath;
	}
	function toColumnar(carList) {
		const groups = Object.create(null);
		let dropped = 0;
		let malformed = 0;
		for (const item of carList) {
			if (!item || typeof item !== "object") {
				dropped++;
				continue;
			}
			if (!item.carNum || !item.url || !item.status) {
				dropped++;
				continue;
			}
			const status = item.status;
			if (!groups[status]) groups[status] = new Map();
			const { code, malformed: bad } = normalizeUrl(item.url);
			if (bad) malformed++;
			groups[status].set(item.carNum, {
				url: code,
				updateDate: item.updateDate || ""
			});
		}
		const byStatus = {};
		for (const status of Object.keys(groups)) {
			const map = groups[status];
			const carNums = [];
			const urls = [];
			const update_date = [];
			for (const [carNum, { url, updateDate }] of map) {
				carNums.push(carNum);
				urls.push(url);
				update_date.push(updateDate);
			}
			byStatus[status] = {
				carNums,
				urls,
				update_date
			};
		}
		return {
			byStatus,
			dropped,
			malformed
		};
	}
	function columnarToFlat(store) {
		const records = [];
		for (const status of Object.keys(store)) {
			const group = store[status];
			if (!group || !group.carNums || !group.urls) continue;
			const { carNums, urls } = group;
			for (let i = 0; i < carNums.length; i++) records.push({
				carNum: carNums[i],
				status,
				url_path: urls[i]
			});
		}
		return records;
	}
	function countColumnar(store) {
		let n = 0;
		for (const status of Object.keys(store)) {
			const col = store[status];
			if (col && col.carNums) n += col.carNums.length;
		}
		return n;
	}
	async function gzipToBase64(value) {
		try {
			if (typeof CompressionStream === "undefined") return null;
			const json = JSON.stringify(value);
			const blob = new Blob([json], { type: "application/json" });
			const cs = new CompressionStream("gzip");
			const stream = blob.stream().pipeThrough(cs);
			const buf = await new Response(stream).arrayBuffer();
			const bytes = new Uint8Array(buf);
			let binary = "";
			const chunkSize = 32768;
			for (let i = 0; i < bytes.length; i += chunkSize) binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
			return btoa(binary);
		} catch {
			return null;
		}
	}
	async function gunzipFromBase64(base64) {
		try {
			if (typeof DecompressionStream === "undefined") return null;
			const binary = atob(base64);
			const bytes = new Uint8Array(binary.length);
			for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
			const ds = new DecompressionStream("gzip");
			const stream = new Blob([bytes]).stream().pipeThrough(ds);
			const text = await new Response(stream).text();
			return JSON.parse(text);
		} catch {
			return null;
		}
	}
	var CONTROL_CHAR_REGEX$1 = new RegExp(`[\\u0000-\\u001f\\u007f]`);
	function isPlainRecord(value) {
		if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
		const prototype = Object.getPrototypeOf(value);
		return prototype === Object.prototype || prototype === null;
	}
	function isStringArray(value) {
		return Array.isArray(value) && value.every((item) => typeof item === "string");
	}
	function isSafeUrlPath(value) {
		const raw = value.trim();
		if (!raw || CONTROL_CHAR_REGEX$1.test(raw)) return false;
		if (/^https?:\/\//i.test(raw) || raw.startsWith("/")) return !normalizeUrl(raw).malformed;
		return !raw.startsWith("//") && !/^[a-z][a-z\d+.-]*:/i.test(raw);
	}
	function validateColumnarSnapshot(store, expectedCount) {
		if (!Number.isInteger(expectedCount) || expectedCount < 0) return {
			ok: false,
			reason: "快照 count 必须是非负整数"
		};
		if (!isPlainRecord(store)) return {
			ok: false,
			reason: "快照列存必须是普通对象"
		};
		const validStatuses = new Set(STATUS_LIST);
		const seenCarNums = new Set();
		const records = [];
		for (const [status, rawGroup] of Object.entries(store)) {
			if (!validStatuses.has(status)) return {
				ok: false,
				reason: `快照包含未知状态:${status}`
			};
			if (!isPlainRecord(rawGroup)) return {
				ok: false,
				reason: `状态 ${status} 的列不是普通对象`
			};
			const carNums = rawGroup.carNums;
			const urls = rawGroup.urls;
			const updateDates = rawGroup.update_date;
			if (!isStringArray(carNums) || !isStringArray(urls)) return {
				ok: false,
				reason: `状态 ${status} 的 carNums/urls 必须是字符串数组`
			};
			if (carNums.length !== urls.length) return {
				ok: false,
				reason: `状态 ${status} 的 carNums/urls 长度不一致`
			};
			if (updateDates !== void 0 && (!isStringArray(updateDates) || updateDates.length !== carNums.length)) return {
				ok: false,
				reason: `状态 ${status} 的 update_date 长度或类型无效`
			};
			for (let index = 0; index < carNums.length; index++) {
				const carNum = carNums[index].trim();
				const urlPath = urls[index].trim();
				if (!carNum || !urlPath) return {
					ok: false,
					reason: `状态 ${status} 包含空番号或空路径`
				};
				if (!isSafeUrlPath(urlPath)) return {
					ok: false,
					reason: `状态 ${status} 包含不安全链接`
				};
				const normalizedCarNum = carNum.toUpperCase();
				if (seenCarNums.has(normalizedCarNum)) return {
					ok: false,
					reason: `快照包含重复番号:${carNum}`
				};
				seenCarNums.add(normalizedCarNum);
				records.push({
					carNum,
					status,
					url_path: urlPath
				});
			}
		}
		const count = expectedCount;
		if (records.length !== count) return {
			ok: false,
			reason: `快照记录数不一致:声明 ${count},实际 ${records.length}`
		};
		return {
			ok: true,
			records,
			count
		};
	}
	async function withExclusiveLock(lockName, task) {
		const browserNavigator = typeof navigator === "undefined" ? void 0 : navigator;
		if (!browserNavigator?.locks) return task();
		return browserNavigator.locks.request(lockName, { mode: "exclusive" }, task);
	}
	var KeyedQueue = class {
		queues = new Map();
		enqueue(key, task) {
			const prev = this.queues.get(key) ?? Promise.resolve();
			let resolveTail;
			const tail = new Promise((resolve) => {
				resolveTail = resolve;
			});
			this.queues.set(key, tail);
			const result = prev.then(task, task);
			result.then(() => resolveTail(), () => resolveTail());
			tail.then(() => {
				if (this.queues.get(key) === tail) this.queues.delete(key);
			});
			return result;
		}
	};
	var INTENT_PREFIX = "jhs:car-sync-intent:";
	function writeIntent(revision, kind) {
		writePersistentValue("carSyncIntent", String(revision), INTENT_PREFIX + revision, {
			type: kind,
			revision,
			ts: Date.now()
		});
	}
	function clearIntent(revision) {
		removePersistentValue("carSyncIntent", String(revision), INTENT_PREFIX + revision);
	}
	function readPendingIntents() {
		const intents = [];
		const values = readPersistentPrefix("carSyncIntent", INTENT_PREFIX);
		for (const value of Object.values(values)) if (value && typeof value.revision === "number") intents.push(value);
		return intents.sort((a, b) => a.ts - b.ts);
	}
	function cleanStaleIntents(maxAgeMs = 864e5) {
		const cutoff = Date.now() - maxAgeMs;
		const values = readPersistentPrefix("carSyncIntent", INTENT_PREFIX);
		for (const [revision, value] of Object.entries(values)) if (!value || typeof value.ts !== "number" || value.ts < cutoff) removePersistentValue("carSyncIntent", revision, INTENT_PREFIX + revision);
	}
	function openLocalDB() {
		return new Promise((resolve, reject) => {
			const request = indexedDB.open(MISSAV_DB_NAME, 2);
			request.onupgradeneeded = (event) => {
				const db = event.target.result;
				const oldVersion = event.oldVersion;
				if (!db.objectStoreNames.contains("cars")) db.createObjectStore(MISSAV_DB_STORE, { keyPath: "carNum" });
				if (oldVersion < 2 && !db.objectStoreNames.contains("sync_meta")) db.createObjectStore(MISSAV_DB_META_STORE, { keyPath: "key" });
			};
			request.onsuccess = () => resolve(request.result);
			request.onerror = () => reject(request.error);
			request.onblocked = () => reject(new Error("数据库被阻塞,请关闭其他标签页后刷新"));
		});
	}
	async function queryLocalCars(carNums) {
		if (!carNums || carNums.length === 0) return new Map();
		const db = await openLocalDB();
		try {
			return await new Promise((resolve) => {
				const store = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE);
				const result = new Map();
				let pending = carNums.length;
				if (pending === 0) {
					resolve(result);
					return;
				}
				for (const cn of carNums) {
					const req = store.get(cn);
					req.onsuccess = () => {
						if (req.result) result.set(cn, {
							status: req.result.status,
							url: req.result.url
						});
						pending--;
						if (pending === 0) resolve(result);
					};
					req.onerror = () => {
						pending--;
						if (pending === 0) resolve(result);
					};
				}
			});
		} finally {
			db.close();
		}
	}
	async function deleteLocalCars(carNums) {
		if (!carNums || carNums.length === 0) return;
		const db = await openLocalDB();
		try {
			await new Promise((resolve) => {
				const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
				const store = tx.objectStore(MISSAV_DB_STORE);
				for (const cn of carNums) store.delete(cn);
				tx.oncomplete = () => resolve();
				tx.onerror = () => resolve();
			});
		} finally {
			db.close();
		}
	}
	async function exportLocalDB() {
		const db = await openLocalDB();
		try {
			return await new Promise((resolve, reject) => {
				const store = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE);
				const result = [];
				const req = store.openCursor();
				req.onsuccess = (event) => {
					const cursor = event.target.result;
					if (cursor) {
						result.push({
							carNum: cursor.value.carNum,
							status: cursor.value.status,
							url: cursor.value.url
						});
						cursor.continue();
					} else resolve(result);
				};
				req.onerror = () => reject(req.error);
			});
		} finally {
			db.close();
		}
	}
	var CONTROL_CHAR_REGEX = new RegExp(`[\\u0000-\\u001f\\u007f]`);
	var JAVDB_BASE_URL = "https://javdb.com";
	var JAVDB_HOSTNAME = "javdb.com";
	function normalizeJavdbUrlPath(value) {
		if (typeof value !== "string") throw new Error("车辆记录链接必须是字符串");
		const raw = value.trim();
		if (!raw || CONTROL_CHAR_REGEX.test(raw)) throw new Error("车辆记录链接为空或包含控制字符");
		let candidate;
		try {
			if (/^https?:\/\//i.test(raw)) candidate = new URL(raw);
			else if (raw.startsWith("/")) {
				if (raw.startsWith("//")) throw new Error("不允许协议相对链接");
				candidate = new URL(raw, JAVDB_BASE_URL);
			} else {
				if (/^[a-z][a-z\d+.-]*:/i.test(raw)) throw new Error("不允许非 HTTPS 链接");
				candidate = new URL(`/v/${raw}`, JAVDB_BASE_URL);
				if (!candidate.pathname.startsWith("/v/")) throw new Error("车辆短码路径无效");
			}
		} catch (error) {
			throw new Error(`车辆记录链接无效:${error instanceof Error ? error.message : String(error)}`);
		}
		if (candidate.protocol !== "https:" || candidate.hostname !== JAVDB_HOSTNAME) throw new Error("车辆记录链接必须指向 https://javdb.com");
		if (candidate.pathname === "/") throw new Error("车辆记录链接路径为空");
		const suffix = candidate.pathname.startsWith("/v/") ? candidate.pathname.slice(3) : candidate.pathname;
		if (!suffix) throw new Error("车辆记录链接路径为空");
		return `${suffix}${candidate.search}${candidate.hash}`;
	}
	function normalizeLocalRecord(record) {
		if (record === null || typeof record !== "object") throw new Error("车辆记录格式无效");
		if (typeof record.carNum !== "string" || record.carNum.trim() === "") throw new Error("车辆记录番号为空");
		if (typeof record.status !== "string" || !STATUS_LIST.includes(record.status.trim())) throw new Error(`车辆记录状态无效:${String(record.status)}`);
		return {
			carNum: record.carNum.trim().toUpperCase(),
			status: record.status.trim(),
			url_path: normalizeJavdbUrlPath(record.url_path)
		};
	}
	function canApplyRevision(incomingRevision, incomingKind, current) {
		if (!Number.isInteger(incomingRevision) || incomingRevision < 0) return false;
		if (!current) return true;
		if (incomingRevision < current.revision) return false;
		if (incomingRevision === current.revision && incomingKind === "full") return false;
		return true;
	}
	async function readAppliedRevision() {
		const db = await openLocalDB();
		try {
			return await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_META_STORE, "readonly");
				const request = tx.objectStore(MISSAV_DB_META_STORE).get(MISSAV_SYNC_META_KEY);
				request.onsuccess = () => {
					const value = request.result;
					if (value && value.key === "car_status_revision" && typeof value.revision === "number" && Number.isInteger(value.revision) && value.revision >= 0 && (value.kind === "full" || value.kind === "delta")) resolve(value);
					else resolve(null);
				};
				request.onerror = () => reject(request.error || new Error("读取同步修订号失败"));
				tx.onerror = () => reject(tx.error || new Error("读取同步元数据事务失败"));
			});
		} finally {
			db.close();
		}
	}
	async function writeAppliedRevision(meta) {
		const db = await openLocalDB();
		try {
			await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
				tx.objectStore(MISSAV_DB_META_STORE).put(meta);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error || new Error("写入同步修订号失败"));
				tx.onabort = () => reject(tx.error || new Error("写入同步修订号事务中止"));
			});
		} finally {
			db.close();
		}
	}
	async function clearAppliedRevision() {
		const db = await openLocalDB();
		try {
			await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
				tx.objectStore(MISSAV_DB_META_STORE).delete(MISSAV_SYNC_META_KEY);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error || new Error("清理同步修订号失败"));
				tx.onabort = () => reject(tx.error || new Error("清理同步修订号事务中止"));
			});
		} finally {
			db.close();
		}
	}
	async function withSyncRevision(revision, kind, task, rollback) {
		return withExclusiveLock(MISSAV_SYNC_LOCK_NAME, async () => {
			const current = await readAppliedRevision();
			if (!canApplyRevision(revision, kind, current)) return { applied: false };
			let metadataWriteStarted = false;
			try {
				const value = await task();
				metadataWriteStarted = true;
				await writeAppliedRevision({
					key: MISSAV_SYNC_META_KEY,
					revision,
					kind
				});
				return {
					applied: true,
					value
				};
			} catch (applyError) {
				const recoveryErrors = [];
				if (rollback) try {
					await rollback();
				} catch (rollbackError) {
					recoveryErrors.push(`数据回滚失败:${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
				}
				if (metadataWriteStarted) try {
					if (current) await writeAppliedRevision(current);
					else await clearAppliedRevision();
				} catch (metadataRollbackError) {
					recoveryErrors.push(`修订号回滚失败:${metadataRollbackError instanceof Error ? metadataRollbackError.message : String(metadataRollbackError)}`);
				}
				if (recoveryErrors.length > 0) {
					const applyMessage = applyError instanceof Error ? applyError.message : String(applyError);
					throw new Error(`同步提交失败(${applyMessage});${recoveryErrors.join(";")}`);
				}
				throw applyError;
			}
		});
	}
	async function upsertLocalCars(records) {
		const BATCH_SIZE = 3e3;
		const normalizedRecords = records.map(normalizeLocalRecord);
		let totalWritten = 0;
		const db = await openLocalDB();
		try {
			for (let i = 0; i < normalizedRecords.length; i += BATCH_SIZE) {
				const batch = normalizedRecords.slice(i, i + BATCH_SIZE);
				totalWritten += await new Promise((resolveBatch, rejectBatch) => {
					const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
					const store = tx.objectStore(MISSAV_DB_STORE);
					let count = 0;
					for (const rec of batch) {
						const req = store.put({
							carNum: rec.carNum,
							status: rec.status,
							url: buildJavdbUrl(rec.url_path),
							updateDate: ""
						});
						req.onsuccess = () => count++;
						req.onerror = () => console.warn("[MissAV] 写入失败", rec.carNum, req.error);
					}
					tx.oncomplete = () => resolveBatch(count);
					tx.onerror = () => rejectBatch(tx.error || new Error("事务失败"));
					tx.onabort = () => rejectBatch(tx.error || new Error("事务中止"));
				});
			}
			return totalWritten;
		} finally {
			db.close();
		}
	}
	async function readLocalCarsSnapshot() {
		const db = await openLocalDB();
		try {
			return await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_STORE, "readonly");
				const store = tx.objectStore(MISSAV_DB_STORE);
				const records = [];
				const request = store.openCursor();
				request.onsuccess = () => {
					const cursor = request.result;
					if (!cursor) return;
					records.push(cursor.value);
					cursor.continue();
				};
				request.onerror = () => reject(request.error || new Error("读取本地状态快照失败"));
				tx.oncomplete = () => resolve(records);
				tx.onerror = () => reject(tx.error || new Error("读取本地状态快照事务失败"));
				tx.onabort = () => reject(tx.error || new Error("读取本地状态快照事务中止"));
			});
		} finally {
			db.close();
		}
	}
	async function restoreLocalCarsSnapshot(snapshot) {
		const snapshotKeys = new Set(snapshot.map((record) => String(record.carNum)));
		const db = await openLocalDB();
		try {
			await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
				const store = tx.objectStore(MISSAV_DB_STORE);
				const request = store.openCursor();
				request.onsuccess = () => {
					const cursor = request.result;
					if (cursor) {
						if (!snapshotKeys.has(String(cursor.primaryKey))) cursor.delete();
						cursor.continue();
						return;
					}
					for (const record of snapshot) store.put(record);
				};
				request.onerror = () => reject(request.error || new Error("恢复本地状态游标失败"));
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error || new Error("恢复本地状态事务失败"));
				tx.onabort = () => reject(tx.error || new Error("恢复本地状态事务中止"));
			});
		} finally {
			db.close();
		}
	}
	async function readLocalCarsSelection(carNums) {
		const normalizedCarNums = Array.from(new Set(carNums.map((carNum) => carNum.trim().toUpperCase()).filter(Boolean)));
		if (normalizedCarNums.length === 0) return {
			carNums: [],
			records: []
		};
		const db = await openLocalDB();
		try {
			return {
				carNums: normalizedCarNums,
				records: await new Promise((resolve, reject) => {
					const tx = db.transaction(MISSAV_DB_STORE, "readonly");
					const store = tx.objectStore(MISSAV_DB_STORE);
					const snapshot = [];
					let settled = false;
					const rejectOnce = (error) => {
						if (settled) return;
						settled = true;
						reject(error);
					};
					for (const carNum of normalizedCarNums) {
						const request = store.get(carNum);
						request.onsuccess = () => {
							if (request.result) snapshot.push(request.result);
						};
						request.onerror = () => rejectOnce(request.error || new Error(`读取增量回滚快照失败:${carNum}`));
					}
					tx.oncomplete = () => {
						if (settled) return;
						settled = true;
						resolve(snapshot);
					};
					tx.onerror = () => rejectOnce(tx.error || new Error("读取增量回滚快照事务失败"));
					tx.onabort = () => rejectOnce(tx.error || new Error("读取增量回滚快照事务中止"));
				})
			};
		} finally {
			db.close();
		}
	}
	async function restoreLocalCarsSelection(snapshot) {
		if (snapshot.carNums.length === 0) return;
		const db = await openLocalDB();
		try {
			await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
				const store = tx.objectStore(MISSAV_DB_STORE);
				for (const carNum of snapshot.carNums) store.delete(carNum);
				for (const record of snapshot.records) store.put(record);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error || new Error("恢复增量回滚快照事务失败"));
				tx.onabort = () => reject(tx.error || new Error("恢复增量回滚快照事务中止"));
			});
		} finally {
			db.close();
		}
	}
	async function replaceLocalCars(records, previousSnapshot) {
		const recordsByCarNum = new Map();
		for (const record of records) {
			const normalized = normalizeLocalRecord(record);
			if (recordsByCarNum.has(normalized.carNum)) throw new Error(`快照包含重复番号:${normalized.carNum}`);
			recordsByCarNum.set(normalized.carNum, normalized);
		}
		const validRecords = Array.from(recordsByCarNum.values());
		const rollbackSnapshot = previousSnapshot ?? await readLocalCarsSnapshot();
		try {
			const written = await upsertLocalCars(validRecords);
			if (written !== validRecords.length) throw new Error(`快照写入条数不一致:声明 ${validRecords.length},实际 ${written},已停止删除旧数据`);
			const snapshotKeys = new Set(recordsByCarNum.keys());
			const db = await openLocalDB();
			let deleted = 0;
			try {
				deleted = await new Promise((resolve, reject) => {
					const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
					const request = tx.objectStore(MISSAV_DB_STORE).openCursor();
					let deletedCount = 0;
					request.onsuccess = () => {
						const cursor = request.result;
						if (!cursor) return;
						const rawCarNum = String(cursor.primaryKey);
						const carNum = rawCarNum.toUpperCase();
						if (!snapshotKeys.has(carNum) || rawCarNum !== carNum) {
							cursor.delete();
							deletedCount++;
						}
						cursor.continue();
					};
					request.onerror = () => reject(request.error || new Error("快照对账游标读取失败"));
					tx.oncomplete = () => resolve(deletedCount);
					tx.onerror = () => reject(tx.error || new Error("快照对账事务失败"));
					tx.onabort = () => reject(tx.error || new Error("快照对账事务中止"));
				});
			} finally {
				db.close();
			}
			return {
				written,
				deleted
			};
		} catch (replaceError) {
			try {
				await restoreLocalCarsSnapshot(rollbackSnapshot);
			} catch (rollbackError) {
				const replaceMessage = replaceError instanceof Error ? replaceError.message : String(replaceError);
				const rollbackMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
				throw new Error(`状态快照对账失败且回滚失败(对账错误:${replaceMessage};回滚错误:${rollbackMessage})`);
			}
			throw replaceError;
		}
	}
	async function importLocalDB(records) {
		return withExclusiveLock(MISSAV_SYNC_LOCK_NAME, async () => {
			if (!Array.isArray(records)) throw new Error("导入记录必须是数组");
			const normalizedRecords = records.map((record) => normalizeLocalRecord({
				carNum: record?.carNum,
				status: record?.status,
				url_path: record?.url
			}));
			const [previousCars, previousRevision] = await Promise.all([readLocalCarsSnapshot(), readAppliedRevision()]);
			let replaced = false;
			try {
				const { written } = await replaceLocalCars(normalizedRecords, previousCars);
				replaced = true;
				await clearAppliedRevision();
				return written;
			} catch (importError) {
				if (!replaced) throw importError;
				const recoveryErrors = [];
				try {
					await restoreLocalCarsSnapshot(previousCars);
				} catch (rollbackError) {
					recoveryErrors.push(`状态数据回滚失败:${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
				}
				try {
					if (previousRevision) await writeAppliedRevision(previousRevision);
					else await clearAppliedRevision();
				} catch (metadataRollbackError) {
					recoveryErrors.push(`修订号回滚失败:${metadataRollbackError instanceof Error ? metadataRollbackError.message : String(metadataRollbackError)}`);
				}
				if (recoveryErrors.length > 0) {
					const importMessage = importError instanceof Error ? importError.message : String(importError);
					throw new Error(`导入失败(${importMessage});${recoveryErrors.join(";")}`);
				}
				throw importError;
			}
		});
	}
	var DB_NAME = "JAV-JHS";
	var STORE_NAME = "appData";
	var MAX_COUNT = 501;
	var BOOTSTRAP_COLORS$1 = [
		{
			name: "primary",
			bg: "#0d6efd",
			text: "#fff"
		},
		{
			name: "secondary",
			bg: "#6c757d",
			text: "#fff"
		},
		{
			name: "success",
			bg: "#198754",
			text: "#fff"
		},
		{
			name: "danger",
			bg: "#dc3545",
			text: "#fff"
		},
		{
			name: "warning",
			bg: "#ffc107",
			text: "#212529"
		},
		{
			name: "info",
			bg: "#0dcaf0",
			text: "#212529"
		},
		{
			name: "dark",
			bg: "#212529",
			text: "#fff"
		}
	];
	var KEYS = {
		MOVIES: "vlt_movies",
		INVENTORY: "vlt_inventory",
		MOVIE_INVENTORY: "vlt_movie_inventory",
		META: "vlt_meta"
	};
	function randomBootstrapStyle() {
		return BOOTSTRAP_COLORS$1[Math.floor(Math.random() * BOOTSTRAP_COLORS$1.length)];
	}
	function openDb() {
		return new Promise((resolve, reject) => {
			const request = indexedDB.open(DB_NAME);
			request.onsuccess = () => resolve(request.result);
			request.onerror = () => reject(request.error);
			request.onupgradeneeded = (event) => {
				const db = event.target.result;
				if (!db.objectStoreNames.contains(STORE_NAME)) db.createObjectStore(STORE_NAME);
			};
		});
	}
	function requestResult(request) {
		return new Promise((resolve, reject) => {
			request.onsuccess = () => resolve(request.result);
			request.onerror = () => reject(request.error);
		});
	}
	function transactionDone(transaction) {
		return new Promise((resolve, reject) => {
			transaction.oncomplete = () => resolve();
			transaction.onabort = () => reject(transaction.error || new Error("IndexedDB 事务已中止"));
			transaction.onerror = () => reject(transaction.error || new Error("IndexedDB 事务失败"));
		});
	}
	function normalizeMeta(value) {
		return {
			...value,
			epoch: Number.isFinite(Number(value?.epoch)) ? Number(value?.epoch) : 0,
			listRevisions: value?.listRevisions ? { ...value.listRevisions } : {}
		};
	}
	function associationDesignations(movieInventory, listId) {
		const suffix = `::${listId}`;
		return Object.keys(movieInventory).filter((key) => movieInventory[key] && key.endsWith(suffix)).map((key) => key.slice(0, -suffix.length));
	}
	function listFingerprint(movieInventory, listId) {
		return associationDesignations(movieInventory, listId).sort().join("");
	}
	function bumpListRevision(meta, listId) {
		meta.listRevisions[listId] = (meta.listRevisions[listId] || 0) + 1;
	}
	async function withState(mode, callback) {
		const db = await openDb();
		const transaction = db.transaction(STORE_NAME, mode);
		const done = transactionDone(transaction);
		const store = transaction.objectStore(STORE_NAME);
		try {
			const [moviesValue, inventoryValue, movieInventoryValue, metaValue] = await Promise.all([
				requestResult(store.get(KEYS.MOVIES)),
				requestResult(store.get(KEYS.INVENTORY)),
				requestResult(store.get(KEYS.MOVIE_INVENTORY)),
				requestResult(store.get(KEYS.META))
			]);
			const state = {
				movies: moviesValue || {},
				inventory: inventoryValue || {},
				movieInventory: movieInventoryValue || {},
				meta: normalizeMeta(metaValue)
			};
			const result = callback(state);
			if (mode === "readwrite") {
				store.put(state.movies, KEYS.MOVIES);
				store.put(state.inventory, KEYS.INVENTORY);
				store.put(state.movieInventory, KEYS.MOVIE_INVENTORY);
				store.put(state.meta, KEYS.META);
			}
			await done;
			return result;
		} catch (error) {
			try {
				transaction.abort();
			} catch {}
			await done.catch(() => {});
			throw error;
		} finally {
			db.close();
		}
	}
	function validateSnapshot(snapshot) {
		if (!snapshot.listId || !snapshot.name || !snapshot.url) throw new Error("服务端清单快照缺少 listId/name/url");
		if (!Number.isInteger(snapshot.expectedCount) || snapshot.expectedCount < 0) throw new Error("服务端清单快照数量无效");
		if (snapshot.movies.length !== snapshot.expectedCount) throw new Error(`服务端清单快照不完整:期望 ${snapshot.expectedCount},实际 ${snapshot.movies.length}`);
		const designations = new Set();
		for (const movie of snapshot.movies) {
			if (!movie.designation || !movie.href) throw new Error("服务端清单快照包含空番号或空链接");
			if (designations.has(movie.designation)) throw new Error(`服务端清单快照包含重复番号:${movie.designation}`);
			designations.add(movie.designation);
		}
	}
	async function sync(designation, listId, movie, list, action, options = {}) {
		return withState("readwrite", (state) => {
			const { movies, inventory, movieInventory, meta } = state;
			const movieExisted = !!movies[designation];
			const score = movie.score != null ? Number(movie.score) : null;
			const previousMovie = movies[designation];
			movies[designation] = {
				designation,
				href: movie.href,
				title: movie.title ?? null,
				coverSrc: movie.cover_src ?? null,
				score: Number.isFinite(score) ? score : null,
				releaseDate: movie.release_date ?? null,
				createdAt: previousMovie?.createdAt ?? new Date().toISOString(),
				series: movie.series ?? null,
				code: movie.code ?? null
			};
			const listExisted = !!inventory[listId];
			if (!listExisted) inventory[listId] = {
				listId,
				name: list.name,
				url: list.url ?? null,
				count: 0,
				style: randomBootstrapStyle()
			};
			const assocKey = `${designation}::${listId}`;
			const actualCount = associationDesignations(movieInventory, listId).length;
			inventory[listId].count = actualCount;
			let association;
			if (action === "add") {
				if (movieInventory[assocKey]) association = "existed";
				else if (actualCount >= MAX_COUNT && !options.serverConfirmed) association = "limit_exceeded";
				else {
					movieInventory[assocKey] = true;
					inventory[listId].count = actualCount + 1;
					association = "created";
				}
			} else if (movieInventory[assocKey]) {
				delete movieInventory[assocKey];
				inventory[listId].count = Math.max(0, actualCount - 1);
				association = "deleted";
			} else association = "unchanged";
			bumpListRevision(meta, listId);
			return {
				movie: movieExisted ? "existed" : "created",
				list: listExisted ? "existed" : "created",
				association
			};
		});
	}
	async function queryMoviesLists(designations) {
		return withState("readonly", (state) => {
			const result = {};
			for (const designation of designations) result[designation] = [];
			for (const key of Object.keys(state.movieInventory)) {
				if (!state.movieInventory[key]) continue;
				const separator = key.indexOf("::");
				if (separator < 0) continue;
				const designation = key.slice(0, separator);
				if (result[designation] === void 0) continue;
				const listId = key.slice(separator + 2);
				const list = state.inventory[listId];
				if (!list) continue;
				result[designation].push({
					name: list.name,
					url: list.url,
					style: list.style
				});
			}
			return result;
		});
	}
	async function check(designation, listId) {
		return withState("readonly", (state) => ({
			movie: !!state.movies[designation],
			inventory: !!state.inventory[listId],
			movieInventory: !!state.movieInventory[`${designation}::${listId}`]
		}));
	}
	async function getAllInventory() {
		return withState("readonly", (state) => state.inventory);
	}
	async function deleteList(listId) {
		return withState("readwrite", (state) => {
			const inventoryDeleted = !!state.inventory[listId];
			delete state.inventory[listId];
			const suffix = `::${listId}`;
			let associations = 0;
			for (const key of Object.keys(state.movieInventory)) {
				if (!key.endsWith(suffix)) continue;
				delete state.movieInventory[key];
				associations++;
			}
			bumpListRevision(state.meta, listId);
			return {
				inventory: inventoryDeleted,
				associations
			};
		});
	}
	async function renameList(listId, newName) {
		return withState("readwrite", (state) => {
			if (!state.inventory[listId]) return false;
			state.inventory[listId].name = newName;
			bumpListRevision(state.meta, listId);
			return true;
		});
	}
	async function getListReconcileState(listId, designation) {
		return withState("readonly", (state) => ({
			inventory: state.inventory[listId] || null,
			actualCount: associationDesignations(state.movieInventory, listId).length,
			hasDesignation: designation === void 0 ? null : !!state.movieInventory[`${designation}::${listId}`],
			guard: {
				epoch: state.meta.epoch,
				revision: state.meta.listRevisions[listId] || 0,
				fingerprint: listFingerprint(state.movieInventory, listId)
			}
		}));
	}
	async function getListAssociationCounts() {
		return withState("readonly", (state) => {
			const counts = {};
			for (const listId of Object.keys(state.inventory)) counts[listId] = 0;
			for (const key of Object.keys(state.movieInventory)) {
				if (!state.movieInventory[key]) continue;
				const separator = key.indexOf("::");
				if (separator < 0) continue;
				const listId = key.slice(separator + 2);
				counts[listId] = (counts[listId] || 0) + 1;
			}
			return counts;
		});
	}
	async function reconcileListSnapshot(snapshot, guard) {
		validateSnapshot(snapshot);
		return withState("readwrite", (state) => {
			const currentGuard = {
				epoch: state.meta.epoch,
				revision: state.meta.listRevisions[snapshot.listId] || 0,
				fingerprint: listFingerprint(state.movieInventory, snapshot.listId)
			};
			if (currentGuard.epoch !== guard.epoch || currentGuard.revision !== guard.revision || currentGuard.fingerprint !== guard.fingerprint) return { status: "conflict" };
			const previous = new Set(associationDesignations(state.movieInventory, snapshot.listId));
			const incoming = new Set(snapshot.movies.map((movie) => movie.designation));
			const now = new Date().toISOString();
			for (const movie of snapshot.movies) {
				const existing = state.movies[movie.designation];
				state.movies[movie.designation] = existing ? {
					...existing,
					href: movie.href || existing.href,
					title: movie.title ?? existing.title,
					coverSrc: movie.coverSrc ?? existing.coverSrc,
					score: movie.score ?? existing.score,
					releaseDate: movie.releaseDate ?? existing.releaseDate,
					series: movie.series ?? existing.series,
					code: movie.code ?? existing.code,
					createdAt: existing.createdAt ?? movie.createdAt ?? now
				} : {
					...movie,
					createdAt: movie.createdAt ?? now
				};
			}
			const suffix = `::${snapshot.listId}`;
			for (const key of Object.keys(state.movieInventory)) if (key.endsWith(suffix)) delete state.movieInventory[key];
			for (const designation of incoming) state.movieInventory[`${designation}::${snapshot.listId}`] = true;
			const existingList = state.inventory[snapshot.listId];
			state.inventory[snapshot.listId] = {
				listId: snapshot.listId,
				name: snapshot.name,
				url: snapshot.url,
				count: snapshot.expectedCount,
				style: existingList?.style ?? randomBootstrapStyle()
			};
			bumpListRevision(state.meta, snapshot.listId);
			let added = 0;
			let removed = 0;
			for (const designation of incoming) if (!previous.has(designation)) added++;
			for (const designation of previous) if (!incoming.has(designation)) removed++;
			return {
				status: "applied",
				added,
				removed,
				count: snapshot.expectedCount
			};
		});
	}
	async function importData$1(data) {
		return withState("readwrite", (state) => {
			const nextEpoch = state.meta.epoch + 1;
			state.movies = data.movies;
			state.inventory = data.inventory;
			state.movieInventory = data.movieInventory;
			state.meta = {
				version: data._version,
				exportedAt: data._exportedAt,
				importedAt: new Date().toISOString(),
				source: data._source,
				epoch: nextEpoch,
				listRevisions: {}
			};
			return {
				movies: Object.keys(data.movies).length,
				inventory: Object.keys(data.inventory).length,
				movieInventory: Object.keys(data.movieInventory).length
			};
		});
	}
	async function isImported() {
		return withState("readonly", (state) => !!state.meta.importedAt);
	}
	async function exportData$1() {
		return withState("readonly", (state) => ({
			_version: state.meta.version ?? 1,
			_exportedAt: state.meta.exportedAt ?? new Date().toISOString(),
			_source: "IndexedDB (local export)",
			movies: state.movies,
			inventory: state.inventory,
			movieInventory: state.movieInventory
		}));
	}
	var VltDb = class {
		static sync = sync;
		static queryMoviesLists = queryMoviesLists;
		static check = check;
		static getListReconcileState = getListReconcileState;
		static getListAssociationCounts = getListAssociationCounts;
		static reconcileListSnapshot = reconcileListSnapshot;
		static importData = importData$1;
		static isImported = isImported;
		static getAllInventory = getAllInventory;
		static deleteList = deleteList;
		static renameList = renameList;
		static exportData = exportData$1;
	};
	var TOAST_SVG_ICONS = {
		success: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>",
		error: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/></svg>",
		warning: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"17.01\"/></svg>",
		info: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/></svg>"
	};
	function VltToastContent({ type, msg }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
			className: "jdb-toast__icon",
			dangerouslySetInnerHTML: { __html: TOAST_SVG_ICONS[type] }
		}), (0, import_jsx_runtime.jsx)("span", {
			className: "jdb-toast__msg",
			children: msg
		})] });
	}
	var queue$1 = {
		MAX_VISIBLE: 2,
		SIMULTANEOUS_WINDOW: 500,
		EXTRA_DURATION: 1e3,
		visible: [],
		overflow: [],
		lastAddTime: 0
	};
	function getToastContainer() {
		let el = document.getElementById("jdb-toast-container");
		if (!el) {
			el = document.createElement("div");
			el.id = "jdb-toast-container";
			document.body.appendChild(el);
		}
		return el;
	}
	function promoteOverflow() {
		if (queue$1.overflow.length === 0 || queue$1.visible.length >= queue$1.MAX_VISIBLE) return;
		const stacked = queue$1.visible.find((v) => v.el.classList.contains("jdb-toast--stacked"));
		if (stacked) stacked.el.classList.remove("jdb-toast--stacked");
		const next = queue$1.overflow.shift();
		getToastContainer().appendChild(next.el);
		next.el.offsetHeight;
		const now = Date.now();
		const isSimultaneous = now - queue$1.lastAddTime < queue$1.SIMULTANEOUS_WINDOW;
		let adjustedDuration = next.duration;
		if (isSimultaneous) adjustedDuration += queue$1.EXTRA_DURATION;
		queue$1.lastAddTime = now;
		queue$1.visible.push({
			el: next.el,
			timerId: setTimeout(() => dismissToast(next.el), adjustedDuration),
			createdAt: now
		});
	}
	function dismissToast(el) {
		const idx = queue$1.visible.findIndex((v) => v.el === el);
		if (idx < 0) return;
		const onTransitionEnd = () => {
			el.removeEventListener("transitionend", onTransitionEnd);
			el.remove();
			const i = queue$1.visible.findIndex((v) => v.el === el);
			if (i >= 0) queue$1.visible.splice(i, 1);
			promoteOverflow();
		};
		el.classList.add("removing");
		el.addEventListener("transitionend", onTransitionEnd);
		setTimeout(() => {
			if (el.parentNode) {
				el.remove();
				const i = queue$1.visible.findIndex((v) => v.el === el);
				if (i >= 0) queue$1.visible.splice(i, 1);
				promoteOverflow();
			}
		}, 300);
		if (idx >= 0) clearTimeout(queue$1.visible[idx].timerId);
	}
	function showToast$1(msg, type = "info", duration = 2500) {
		const container = getToastContainer();
		const toast = document.createElement("div");
		toast.className = `jdb-toast jdb-toast--${type}`;
		toast.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(VltToastContent, {
			type,
			msg
		}));
		toast.addEventListener("click", () => dismissToast(toast));
		if (queue$1.visible.length < queue$1.MAX_VISIBLE) {
			const now = Date.now();
			const isSimultaneous = now - queue$1.lastAddTime < queue$1.SIMULTANEOUS_WINDOW;
			let adjustedDuration = duration;
			if (isSimultaneous) adjustedDuration += queue$1.EXTRA_DURATION;
			queue$1.lastAddTime = now;
			container.appendChild(toast);
			queue$1.visible.push({
				el: toast,
				timerId: setTimeout(() => dismissToast(toast), adjustedDuration),
				createdAt: now
			});
		} else {
			toast.classList.add("jdb-toast--stacked");
			toast.addEventListener("click", (e) => {
				e.stopPropagation();
				if (queue$1.overflow.find((o) => o.el === toast)) {
					const i = queue$1.overflow.findIndex((o) => o.el === toast);
					queue$1.overflow.splice(i, 1);
					promoteOverflow();
				}
			});
			queue$1.overflow.push({
				el: toast,
				msg,
				type,
				duration
			});
		}
	}
	function downloadJson(content, filename) {
		const blob = new Blob([content], { type: "application/json" });
		const url = URL.createObjectURL(blob);
		const anchor = document.createElement("a");
		anchor.href = url;
		anchor.download = filename;
		document.body.appendChild(anchor);
		anchor.click();
		document.body.removeChild(anchor);
		URL.revokeObjectURL(url);
	}
	async function buildBackupPayload(settings) {
		const exportData = await storageManager.exportData();
		await flushPersistentData();
		exportData.__meta = {
			credentialId: getCredentialId(),
			autoBackupConfig: settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG,
			backupTime: utils.getNowStr("_", "_")
		};
		exportData.__localStorage = collectLocalStorageBackup();
		const persistentData = collectPersistentDataBackup();
		if (persistentData) exportData.__persistentData = persistentData;
		exportData.__gmStorage = collectGmStorageBackup();
		return exportData;
	}
	function importData(_plugin) {
		try {
			const fileInput = document.createElement("input");
			fileInput.type = "file";
			fileInput.accept = ".json";
			fileInput.onchange = (event) => {
				const file = event.target.files?.[0];
				if (!file) return;
				const reader = new FileReader();
				reader.onload = async (loadEvent) => {
					try {
						let fileContent = String(loadEvent.target?.result ?? "");
						if (isBackupV2(fileContent)) {
							const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
							if (!backupPassword) {
								show.error("该备份使用 V2 加密,请先在备份面板设置备份口令");
								return;
							}
							try {
								fileContent = await decryptBackupV2(fileContent, backupPassword);
							} catch {
								show.error("备份口令错误或文件已损坏");
								return;
							}
						}
						const parsedData = JSON.parse(fileContent);
						layer.confirm("确定是否要覆盖导入?", {
							icon: 3,
							title: "确认覆盖",
							btn: ["确定", "取消"]
						}, async (layerIndex) => {
							try {
								const extras = stripBackupExtras(parsedData);
								await storageManager.importData(parsedData);
								await applyBackupExtras(extras);
								show.ok("数据导入成功");
								layer.close(layerIndex);
								location.reload();
							} catch (error) {
								console.error(error);
								show.error("导入失败:" + (error instanceof Error ? error.message : String(error)));
							}
						});
					} catch (err) {
						console.error(err);
						show.error("导入失败:文件内容不是有效的JSON格式 " + (err instanceof Error ? err.message : String(err)));
					}
				};
				reader.onerror = () => {
					show.error("读取文件时出错");
				};
				reader.readAsText(file);
			};
			document.body.appendChild(fileInput);
			fileInput.click();
			setTimeout(() => document.body.removeChild(fileInput), 1e3);
		} catch (err) {
			console.error(err);
			show.error("导入数据时出错: " + (err instanceof Error ? err.message : String(err)));
		}
	}
	async function exportData(_plugin) {
		try {
			const settings = await storageManager.getSetting();
			const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
			let exportText;
			if (backupPassword) exportText = await encryptBackupV2(JSON.stringify(await buildBackupPayload(settings)), backupPassword);
			else exportText = JSON.stringify(await buildBackupPayload(settings));
			const fileName = `${utils.getNowStr("_", "_")}.json`;
			utils.download(exportText, fileName);
			show.ok("数据导出成功");
		} catch (err) {
			console.error(err);
			show.error("导出数据时出错: " + (err instanceof Error ? err.message : String(err)));
		}
	}
	function handleVltImportClick() {
		$("#vlt-file-input").trigger("click");
	}
	async function handleVltFileChange(event) {
		const input = event.target;
		const file = input.files?.[0];
		if (!file) return;
		const $status = $("#vlt-status");
		$status.text("⏳ 导入中...").css("color", "#0d6efd");
		try {
			const text = await file.text();
			const data = JSON.parse(text);
			const stats = await VltDb.importData(data);
			$status.html(`✓ 导入完成:${stats.movies} 部影片, ${stats.inventory} 个清单, ${stats.movieInventory} 条关联`).css("color", "#198754");
			showToast$1(`✓ 导入完成:${stats.movies} 部影片, ${stats.inventory} 个清单, ${stats.movieInventory} 条关联`, "success");
			setTimeout(() => window.location.reload(), 1500);
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			$status.text(`✗ 导入失败:${msg}`).css("color", "#dc3545");
			showToast$1(`✗ 导入失败:${msg}`, "error");
		}
		input.value = "";
	}
	async function handleVltExportClick() {
		const $status = $("#vlt-status");
		$status.text("⏳ 导出中...").css("color", "#0d6efd");
		try {
			const data = await VltDb.exportData();
			downloadJson(JSON.stringify(data, null, 2), `vlt-export-${new Date().toISOString().split("T")[0]}.json`);
			$status.html(`✓ 导出完成:${Object.keys(data.movies).length} 部影片, ${Object.keys(data.inventory).length} 个清单, ${Object.keys(data.movieInventory).length} 条关联`).css("color", "#198754");
			showToast$1("✓ 导出完成", "success");
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			$status.text(`✗ 导出失败:${msg}`).css("color", "#dc3545");
			showToast$1(`✗ 导出失败:${msg}`, "error");
		}
	}
	async function handleMissavSync() {
		const $status = $("#missav-status");
		$status.text("⏳ 同步中...").css("color", "#0d6efd");
		try {
			const carList = await storageManager.getCarList();
			if (carList.length === 0) {
				$status.text("✗ car_list 为空").css("color", "#dc3545");
				return;
			}
			const colRes = toColumnar(carList);
			const count = countColumnar(colRes.byStatus);
			const base64 = await gzipToBase64(colRes.byStatus);
			if (!base64) {
				$status.text("✗ 压缩失败").css("color", "#dc3545");
				return;
			}
			const payload = {
				data: base64,
				hwm: new Date().toISOString(),
				count,
				ts: Date.now()
			};
			GM_setValue(GM_KEY_CAR_STATUS_DATA, payload);
			$status.html(`✓ 同步完成:${count} 条记录已推送到 GM 存储`).css("color", "#198754");
			showToast$1(`✓ 同步完成:${count} 条`, "success");
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			$status.text(`✗ 同步失败:${msg}`).css("color", "#dc3545");
			showToast$1(`✗ 同步失败:${msg}`, "error");
		}
	}
	function handleMissavImportClick() {
		$("#missav-file-input").trigger("click");
	}
	async function handleMissavFileChange(event) {
		const input = event.target;
		const file = input.files?.[0];
		if (!file) return;
		const $status = $("#missav-status");
		$status.text("⏳ 导入中...").css("color", "#0d6efd");
		try {
			const text = await file.text();
			const data = JSON.parse(text);
			let records;
			if (Array.isArray(data)) records = data;
			else if (data.records && Array.isArray(data.records)) records = data.records;
			else records = columnarToFlat(data).map((r) => ({
				carNum: r.carNum,
				status: r.status,
				url: buildJavdbUrl(r.url_path)
			}));
			const written = await importLocalDB(records);
			$status.html(`✓ 导入完成:${written} 条记录`).css("color", "#198754");
			showToast$1(`✓ 导入完成:${written} 条`, "success");
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			$status.text(`✗ 导入失败:${msg}`).css("color", "#dc3545");
			showToast$1(`✗ 导入失败:${msg}`, "error");
		}
		input.value = "";
	}
	async function handleMissavExportClick() {
		const $status = $("#missav-status");
		$status.text("⏳ 导出中...").css("color", "#0d6efd");
		try {
			const records = await exportLocalDB();
			downloadJson(JSON.stringify(records, null, 2), `missav-status-${new Date().toISOString().split("T")[0]}.json`);
			$status.html(`✓ 导出完成:${records.length} 条记录`).css("color", "#198754");
			showToast$1("✓ 导出完成", "success");
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			$status.text(`✗ 导出失败:${msg}`).css("color", "#dc3545");
			showToast$1(`✗ 导出失败:${msg}`, "error");
		}
	}
	function PreloadCacheStatsText({ count, missAv, supJav, size }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			"共 ",
			(0, import_jsx_runtime.jsx)("b", { children: count }),
			" 条(MissAv ",
			missAv,
			" / SupJav ",
			supJav,
			")|占用 ",
			size
		] });
	}
	var CACHE_NAMESPACE_BY_KEY = {
		jhs_dmm_video: "dmmVideo",
		jhs_other_site: "otherSite",
		jhs_other_site_dmm: "otherSiteDmm",
		jhs_translate: "translate",
		jhs_actress_info: "actressInfo",
		jhs_score_info: "scoreInfo",
		jhs_screenShot: "screenShot",
		jhs_visit_history: "visitHistory"
	};
	function readCacheValue(key) {
		if (key === "jdb:rating_cache_v2" && Object.keys(RatingCache._data).length > 0) return RatingCache._data;
		const namespace = CACHE_NAMESPACE_BY_KEY[key];
		if (namespace) return readPersistentMap(namespace, key);
		try {
			const raw = localStorage.getItem(key);
			if (!raw) return null;
			const value = JSON.parse(raw);
			return value && typeof value === "object" ? value : null;
		} catch {
			return null;
		}
	}
	function getCacheStats(key) {
		const value = readCacheValue(key);
		if (!value) return {
			size: 0,
			count: 0
		};
		return {
			size: new Blob([JSON.stringify(value)]).size,
			count: Array.isArray(value) ? value.length : Object.keys(value).length
		};
	}
	function formatSize(bytes) {
		if (bytes === 0) return "0 B";
		if (bytes < 1024) return `${bytes} B`;
		if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
		return `${(bytes / 1048576).toFixed(1)} MB`;
	}
	function refreshCacheStats(_plugin, cacheKey) {
		const stats = getCacheStats(cacheKey);
		const $item = $(`.cache-item[data-cache-key="${cacheKey}"]`);
		$item.find(".cache-size").text(formatSize(stats.size));
		$item.find(".cache-count").text(stats.count > 0 ? `${stats.count} 条` : "空");
	}
	function refreshAllCacheStats(plugin) {
		let totalSize = 0;
		for (const item of plugin.cacheItems) {
			const stats = getCacheStats(item.key);
			totalSize += stats.size;
			const $item = $(`.cache-item[data-cache-key="${item.key}"]`);
			$item.find(".cache-size").text(formatSize(stats.size));
			$item.find(".cache-count").text(stats.count > 0 ? `${stats.count} 条` : "空");
		}
		totalSize += getCacheStats("jhs_other_site_dmm").size;
		$("#cache-total-size").text(`总占用 ${formatSize(totalSize)}`);
	}
	function refreshPreloadCacheStats(_plugin) {
		const stats = getCacheStats("jhs_other_site");
		const cache = readCacheValue("jhs_other_site") ?? {};
		const keys = Object.keys(cache);
		const missAv = keys.filter((key) => key.endsWith("_missAv")).length;
		const supJav = keys.filter((key) => key.endsWith("_supJav")).length;
		$("#preload-cache-stats").html(jsxToString((0, import_react.createElement)(PreloadCacheStatsText, {
			count: stats.count,
			missAv,
			supJav,
			size: formatSize(stats.size)
		})));
	}
	function handleCleanCache(plugin, event) {
		const cacheKey = String($(event.currentTarget).data("key"));
		const cacheItem = plugin.cacheItems.find((item) => item.key === cacheKey);
		const namespace = CACHE_NAMESPACE_BY_KEY[cacheKey];
		if (cacheKey === "jdb:rating_cache_v2") RatingCache.clear();
		else if (namespace) clearPersistentNamespace(namespace, cacheKey);
		else localStorage.removeItem(cacheKey);
		if (cacheKey === "jhs_dmm_video") clearPersistentNamespace("otherSiteDmm", "jhs_other_site_dmm");
		show.ok(`${cacheItem.text} 清理成功`);
		$("#cache-data-display").hide();
		refreshCacheStats(plugin, cacheKey);
		refreshAllCacheStats(plugin);
	}
	function handleCleanAllCache(plugin) {
		plugin.cacheItems.forEach((item) => {
			const namespace = CACHE_NAMESPACE_BY_KEY[item.key];
			if (item.key === "jdb:rating_cache_v2") RatingCache.clear();
			else if (namespace) clearPersistentNamespace(namespace, item.key);
			else localStorage.removeItem(item.key);
		});
		clearPersistentNamespace("otherSiteDmm", "jhs_other_site_dmm");
		show.ok("全部缓存已清理");
		$("#cache-data-display").hide();
		refreshAllCacheStats(plugin);
	}
	function handleViewCache(event) {
		const cachedData = readCacheValue(String($(event.currentTarget).data("key")));
		const $display = $("#cache-data-display");
		const $pre = $display.find("pre");
		$display.show();
		$pre.text(cachedData ? JSON.stringify(cachedData, null, 2) : "无数据");
	}
	function handleExportCache(plugin, event) {
		const cacheKey = String($(event.currentTarget).data("key"));
		const cachedData = readCacheValue(cacheKey);
		if (!cachedData) {
			show.error("缓存为空,无可导出数据");
			return;
		}
		const cacheItem = plugin.cacheItems.find((item) => item.key === cacheKey);
		downloadJson(JSON.stringify(cachedData), `${cacheKey}-${new Date().toISOString().split("T")[0]}.json`);
		show.ok(`${cacheItem.text} 已导出`);
	}
	function bindKeywordContainer(plugin, containerSelector, noun) {
		$(`${containerSelector} .add-tag-btn`).on("click", () => plugin.addKeyword(containerSelector, noun));
		$(`${containerSelector} .keyword-input`).on("keypress", (event) => {
			if (event.key === "Enter") plugin.addKeyword(containerSelector, noun);
		});
	}
	function readKeywordLabels(containerSelector) {
		const list = [];
		$(`${containerSelector} .keyword-label`).toArray().forEach((label) => {
			const keyword = $(label).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
			if (keyword) list.push(keyword);
		});
		return list;
	}
	async function loadForm(plugin) {
		const settings = await storageManager.getSetting();
		$("#videoQuality").val(settings.videoQuality);
		$("#reviewCount").val(settings.reviewCount || 20);
		$("#tagPosition").val(settings.tagPosition || "rightTop");
		$("#movieShowType").val(settings.movieShowType || "hide");
		$("#waitCheckCount").val(settings.waitCheckCount || 5);
		const tagNumber = settings.highlightedTagNumber || 1;
		const tagColor = settings.highlightedTagColor || "#ce2222";
		$("#highlightedTagNumber").val(settings.highlightedTagNumber || 1);
		$("#highlightedTagColor").val(settings.highlightedTagColor || "#ce2222");
		$("#highlightedTagLabel").css("border", `${tagNumber}px solid ${tagColor}`);
		$("#enableClog").val(settings.enableClog || "yes");
		$("#clogMsgCount").val(settings.clogMsgCount || 2e3);
		$("#httpTimeout").val(settings.httpTimeout || 5e3);
		$("#httpRetryCount").val(settings.httpRetryCount || 3);
		const webDavUrl = GM_getValue("jhs_webdav_url", "") || settings.webDavUrl || "";
		const webDavUsername = GM_getValue("jhs_webdav_username", "") || settings.webDavUsername || "";
		const webDavPassword = GM_getValue("jhs_webdav_password", "") || settings.webDavPassword || "";
		const backupPwd = GM_getValue("jhs_backup_password", "") || settings.backupPassword || "";
		if (webDavUrl && !GM_getValue("jhs_webdav_url", "")) GM_setValue(GM_KEY_WEBDAV_URL, webDavUrl);
		if (webDavUsername && !GM_getValue("jhs_webdav_username", "")) GM_setValue(GM_KEY_WEBDAV_USERNAME, webDavUsername);
		if (webDavPassword && !GM_getValue("jhs_webdav_password", "")) GM_setValue(GM_KEY_WEBDAV_PASSWORD, webDavPassword);
		if (backupPwd && !GM_getValue("jhs_backup_password", "")) GM_setValue(GM_KEY_BACKUP_PASSWORD, backupPwd);
		$("#webDavUrl").val(webDavUrl);
		$("#webDavUsername").val(webDavUsername);
		$("#webDavPassword").val(webDavPassword);
		$("#backupPassword").val(backupPwd);
		$("#gitApiUrl").val(GM_getValue(GM_KEY_GIT_API_URL, ""));
		$("#gitRepo").val(GM_getValue(GM_KEY_GIT_REPO, ""));
		$("#gitBranch").val(GM_getValue(GM_KEY_GIT_BRANCH, ""));
		$("#gitToken").val(GM_getValue(GM_KEY_GIT_TOKEN, ""));
		const autoBackupConfig = settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG;
		$("#enableAutoBackup").prop("checked", autoBackupConfig.enabled);
		$("#autoBackupFrequency").val(autoBackupConfig.frequency);
		const backupChannels = settings.backupChannels || {
			webdav: true,
			git: false
		};
		$("#enableWebdavChannel").prop("checked", backupChannels.webdav !== false);
		$("#enableGitChannel").prop("checked", backupChannels.git === true);
		$("#credentialIdDisplay").val(getCredentialId());
		$("#enableFavoriteActresses").prop("checked", !settings.enableFavoriteActresses || settings.enableFavoriteActresses === "yes");
		$("#enableSaveActressCarInfo").prop("checked", !!settings.enableSaveActressCarInfo && settings.enableSaveActressCarInfo === "yes");
		const otherSitePlugin = plugin.getBean("OtherSitePlugin");
		const missAvUrl = await otherSitePlugin.getMissAvUrl();
		const supJavUrl = await otherSitePlugin.getSupJavUrl();
		$("#missAvUrl").val(missAvUrl);
		$("#supJavUrl").val(supJavUrl);
		$("#enablePreload").prop("checked", !settings.enablePreload || settings.enablePreload === "yes");
		$("#enablePreloadStatus").prop("checked", !settings.enablePreloadStatus || settings.enablePreloadStatus === "yes");
		$("#preloadDebounce").val(settings.preloadDebounce || 300);
		$("#preloadConcurrency").val(settings.preloadConcurrency || 1);
		$("#preloadCacheTTL").val(settings.preloadCacheTTL ?? 0);
		const enabledSites = otherSitePlugin.getEnabledSites();
		$("#preload-enable-missAvBtn").prop("checked", enabledSites.includes("missAvBtn"));
		$("#preload-enable-supJavBtn").prop("checked", enabledSites.includes("supJavBtn"));
		refreshPreloadCacheStats(plugin);
		$("#listMergeEnabled").prop("checked", !settings.listMergeEnabled || settings.listMergeEnabled === "yes");
		$("#listReflowEnabled").prop("checked", !settings.listReflowEnabled || settings.listReflowEnabled === "yes");
		$("#listDefaultSort").val(settings.listDefaultSort || "name-desc");
		$("#autoReconcileDelay").val(settings.autoReconcileDelay ?? 800);
		$("#autoServerSync").prop("checked", !settings.autoServerSync || settings.autoServerSync === "yes");
		$("#favSyncInterval").val(settings.jhsSyncInterval ?? 500);
		$("#reflowInterval").val(settings.reflowInterval ?? 400);
		$("#reflowTrialCount").val(settings.reflowTrialCount ?? 10);
		$("#reflowBatchSize").val(settings.reflowBatchSize ?? 20);
		$("#reflowMaxRetry").val(settings.reflowMaxRetry ?? 3);
		$("#autoFavCleanupEnabled").prop("checked", !settings.autoFavCleanupEnabled || settings.autoFavCleanupEnabled === "yes");
		$("#listMergePattern").val(settings.listMergePattern || "^(.+?)(\\d+)$");
		const titleFilterKeyword = await storageManager.getTitleFilterKeyword();
		if (titleFilterKeyword) titleFilterKeyword.forEach((keyword) => {
			plugin.addLabelTag("#filterKeywordContainer", keyword);
		});
		bindKeywordContainer(plugin, "#filterKeywordContainer", "屏蔽词");
		(Array.isArray(settings.listMergeExcludeNames) ? settings.listMergeExcludeNames : []).forEach((name) => plugin.addLabelTag("#listMergeExcludeNamesContainer", name, "清单名"));
		bindKeywordContainer(plugin, "#listMergeExcludeNamesContainer", "清单名");
		(Array.isArray(settings.protectedListKeywords) ? settings.protectedListKeywords : ["值得复盘"]).forEach((kw) => plugin.addLabelTag("#protectedListKeywordsContainer", kw, "关键词"));
		bindKeywordContainer(plugin, "#protectedListKeywordsContainer", "关键词");
	}
	async function saveForm(plugin) {
		const settings = await storageManager.getSetting();
		settings.videoQuality = $("#videoQuality").val();
		settings.reviewCount = $("#reviewCount").val();
		settings.tagPosition = $("#tagPosition").val();
		settings.movieShowType = $("#movieShowType").val();
		settings.waitCheckCount = $("#waitCheckCount").val();
		settings.highlightedTagNumber = $("#highlightedTagNumber").val();
		settings.highlightedTagColor = $("#highlightedTagColor").val();
		settings.httpTimeout = $("#httpTimeout").val();
		settings.httpRetryCount = $("#httpRetryCount").val();
		settings.enableClog = $("#enableClog").val();
		if (settings.enableClog === "yes") clog.show();
		else clog.hide();
		settings.clogMsgCount = $("#clogMsgCount").val();
		GM_setValue(GM_KEY_WEBDAV_URL, $("#webDavUrl").val() || "");
		GM_setValue(GM_KEY_WEBDAV_USERNAME, $("#webDavUsername").val() || "");
		GM_setValue(GM_KEY_WEBDAV_PASSWORD, $("#webDavPassword").val() || "");
		GM_setValue(GM_KEY_BACKUP_PASSWORD, $("#backupPassword").val() || "");
		GM_setValue(GM_KEY_GIT_API_URL, $("#gitApiUrl").val() || "");
		GM_setValue(GM_KEY_GIT_REPO, $("#gitRepo").val() || "");
		GM_setValue(GM_KEY_GIT_BRANCH, $("#gitBranch").val() || "");
		GM_setValue(GM_KEY_GIT_TOKEN, $("#gitToken").val() || "");
		delete settings.webDavUrl;
		delete settings.webDavUsername;
		delete settings.webDavPassword;
		settings.autoBackupConfig = {
			enabled: $("#enableAutoBackup").is(":checked"),
			frequency: $("#autoBackupFrequency").val()
		};
		settings.backupChannels = {
			webdav: $("#enableWebdavChannel").is(":checked"),
			git: $("#enableGitChannel").is(":checked")
		};
		settings.missAvUrl = String($("#missAvUrl").val() ?? "").replace(/\/$/, "");
		settings.supJavUrl = String($("#supJavUrl").val() ?? "").replace(/\/$/, "");
		settings.enablePreload = $("#enablePreload").is(":checked") ? "yes" : "no";
		settings.enablePreloadStatus = $("#enablePreloadStatus").is(":checked") ? "yes" : "no";
		settings.preloadDebounce = $("#preloadDebounce").val();
		settings.preloadConcurrency = $("#preloadConcurrency").val();
		settings.preloadCacheTTL = $("#preloadCacheTTL").val();
		const otherSitePluginForSave = plugin.getBean("OtherSitePlugin");
		const enabledSiteIds = [];
		if ($("#preload-enable-missAvBtn").is(":checked")) enabledSiteIds.push("missAvBtn");
		if ($("#preload-enable-supJavBtn").is(":checked")) enabledSiteIds.push("supJavBtn");
		otherSitePluginForSave.saveEnabledSites(enabledSiteIds);
		settings.enableFavoriteActresses = $("#enableFavoriteActresses").is(":checked") ? "yes" : "no";
		settings.enableSaveActressCarInfo = $("#enableSaveActressCarInfo").is(":checked") ? "yes" : "no";
		settings.listMergeEnabled = $("#listMergeEnabled").is(":checked") ? "yes" : "no";
		settings.listReflowEnabled = $("#listReflowEnabled").is(":checked") ? "yes" : "no";
		settings.listDefaultSort = $("#listDefaultSort").val();
		settings.autoReconcileDelay = Number($("#autoReconcileDelay").val()) || 800;
		settings.jhsSyncInterval = Number($("#favSyncInterval").val()) || 500;
		settings.reflowInterval = Number($("#reflowInterval").val()) || 400;
		settings.reflowTrialCount = Number($("#reflowTrialCount").val()) || 10;
		settings.reflowBatchSize = Number($("#reflowBatchSize").val()) || 20;
		settings.reflowMaxRetry = Number($("#reflowMaxRetry").val()) || 3;
		settings.autoFavCleanupEnabled = $("#autoFavCleanupEnabled").is(":checked") ? "yes" : "no";
		settings.autoServerSync = $("#autoServerSync").is(":checked") ? "yes" : "no";
		settings.listMergePattern = String($("#listMergePattern").val() ?? "").trim();
		settings.listMergeExcludeNames = readKeywordLabels("#listMergeExcludeNamesContainer");
		settings.protectedListKeywords = readKeywordLabels("#protectedListKeywordsContainer");
		await storageManager.saveSetting(settings);
		const titleFilterKeywordList = readKeywordLabels("#filterKeywordContainer");
		await storageManager.saveTitleFilterKeyword(titleFilterKeywordList);
		show.ok("保存成功");
		refresh();
		const newVideoPlugin = plugin.getBean("NewVideoPlugin");
		if (newVideoPlugin) newVideoPlugin.resetBtnTip();
		plugin.getBean("BlacklistPlugin").resetBtnTip();
		plugin.getBean("BlacklistPlugin").reloadTable();
	}
	var SESSION_KEY = "jhs:sync_session_v1";
	function createSession() {
		const now = Date.now();
		return {
			createdAt: now,
			updatedAt: now,
			phase: "idle",
			wantPage: 0,
			watchedPage: 0,
			javdbWant: [],
			javdbWatched: [],
			queue: [],
			queueDone: 0,
			synced: 0,
			failed: [],
			skipped: 0,
			conflicts: []
		};
	}
	function loadSession() {
		return readPersistentValue("syncSession", "current", SESSION_KEY);
	}
	function saveSession(session) {
		session.updatedAt = Date.now();
		writePersistentValue("syncSession", "current", SESSION_KEY, session);
	}
	function clearSession() {
		removePersistentValue("syncSession", "current", SESSION_KEY);
	}
	function isResumable(session) {
		return session.phase !== "idle" && session.phase !== "done" && session.phase !== "error";
	}
	function getCsrfToken$2() {
		const meta = document.querySelector("meta[name=\"csrf-token\"]");
		return meta ? meta.content : null;
	}
	function extractVideoId(url) {
		if (!url) return null;
		const m = url.match(/\/v\/([^/?#]+)/);
		return m ? m[1] : null;
	}
	async function fetchPageHtml(url) {
		const res = await fetch(url, { credentials: "same-origin" });
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
		return res.text();
	}
	async function postWantToWatch(videoId) {
		const token = getCsrfToken$2();
		if (!token) throw new Error("无法获取 CSRF token");
		const res = await fetch(`/v/${videoId}/reviews/want_to_watch`, {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded",
				"X-CSRF-Token": token
			},
			body: `authenticity_token=${encodeURIComponent(token)}`,
			credentials: "same-origin"
		});
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
	}
	async function postWatched(videoId, score = 0) {
		const token = getCsrfToken$2();
		if (!token) throw new Error("无法获取 CSRF token");
		const body = `${`authenticity_token=${encodeURIComponent(token)}`}&video_review[status]=watched&video_review[score]=${score}&video_review[content]=`;
		const res = await fetch(`/v/${videoId}/reviews`, {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded",
				"X-CSRF-Token": token
			},
			body,
			credentials: "same-origin"
		});
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
	}
	async function readRatingCacheMap() {
		await RatingCache.load();
		const map = new Map();
		for (const [code, entry] of Object.entries(RatingCache._data)) if (entry && typeof entry.rating === "number" && entry.rating > 0) map.set(code, entry.rating);
		return map;
	}
	var session = createSession();
	var timer = null;
	var listeners = [];
	var ticking = false;
	function onSyncChange(fn) {
		listeners.push(fn);
		return () => {
			listeners = listeners.filter((l) => l !== fn);
		};
	}
	function emit() {
		for (const fn of listeners) fn(session);
	}
	function isRunning() {
		return timer !== null;
	}
	function hasResumableSession() {
		const saved = loadSession();
		return saved !== null && isResumable(saved);
	}
	async function fetchOnePage(url) {
		const absUrl = new URL(url, window.location.origin).href;
		const html = await fetchPageHtml(absUrl);
		const doc = new DOMParser().parseFromString(html, "text/html");
		const entries = [];
		doc.querySelectorAll(".movie-list .item").forEach((item) => {
			const a = item.querySelector("a[href*=\"/v/\"]");
			const strong = item.querySelector(".video-title strong");
			if (!a) return;
			const m = (a.getAttribute("href") || "").match(/\/v\/([^/?#]+)/);
			if (!m) return;
			entries.push([m[1], strong ? (strong.textContent || "").trim() : ""]);
		});
		const next = doc.querySelector("a.pagination-next");
		return {
			entries,
			nextUrl: next ? next.getAttribute("href") : null
		};
	}
	async function buildQueue() {
		const carList = await storageManager.getCarList();
		const ratingCache = await readRatingCacheMap();
		const javdbWantMap = new Map(session.javdbWant);
		const javdbWatchedMap = new Map(session.javdbWatched);
		const jhsWantByVideoId = new Map();
		const jhsWatchedByVideoId = new Map();
		for (const car of carList) {
			const vid = extractVideoId(car.url);
			if (!vid) continue;
			if (car.status === "favorite") jhsWantByVideoId.set(vid, car.carNum);
			else if (car.status === "hasWatch") jhsWatchedByVideoId.set(vid, car.carNum);
		}
		const queue = [];
		const conflicts = [];
		let skipped = 0;
		for (const [vid, carNum] of jhsWantByVideoId) {
			if (javdbWantMap.has(vid)) {
				skipped++;
				continue;
			}
			if (javdbWatchedMap.has(vid)) {
				conflicts.push({
					carNum,
					jhsStatus: "想看",
					javdbStatus: "已看"
				});
				continue;
			}
			queue.push({
				carNum,
				videoId: vid,
				action: "want-to-javdb"
			});
		}
		for (const car of carList) {
			if (car.status !== "hasWatch") continue;
			const vid = extractVideoId(car.url);
			if (!vid) continue;
			if (javdbWatchedMap.has(vid)) {
				skipped++;
				continue;
			}
			if (javdbWantMap.has(vid)) {
				conflicts.push({
					carNum: car.carNum,
					jhsStatus: "已看",
					javdbStatus: "想看"
				});
				continue;
			}
			const score = typeof car.score === "number" && car.score > 0 ? car.score : ratingCache.get(car.carNum) ?? 0;
			queue.push({
				carNum: car.carNum,
				videoId: vid,
				action: "watched-to-javdb",
				score
			});
		}
		for (const [vid, carNum] of javdbWantMap) {
			if (!carNum) continue;
			if (jhsWantByVideoId.has(vid)) {
				skipped++;
				continue;
			}
			if (jhsWatchedByVideoId.has(vid)) {
				conflicts.push({
					carNum,
					jhsStatus: "已看",
					javdbStatus: "想看"
				});
				continue;
			}
			queue.push({
				carNum,
				videoId: vid,
				action: "want-from-javdb"
			});
		}
		for (const [vid, carNum] of javdbWatchedMap) {
			if (!carNum) continue;
			if (jhsWatchedByVideoId.has(vid)) {
				skipped++;
				continue;
			}
			if (jhsWantByVideoId.has(vid)) {
				conflicts.push({
					carNum,
					jhsStatus: "想看",
					javdbStatus: "已看"
				});
				continue;
			}
			queue.push({
				carNum,
				videoId: vid,
				action: "watched-from-javdb"
			});
		}
		session.queue = queue;
		session.queueDone = 0;
		session.skipped = skipped;
		session.conflicts = conflicts;
		session.phase = "execute";
		saveSession(session);
		emit();
	}
	async function executeOp(op) {
		switch (op.action) {
			case "want-to-javdb":
				await postWantToWatch(op.videoId);
				break;
			case "watched-to-javdb":
				await postWatched(op.videoId, op.score ?? 0);
				break;
			case "want-from-javdb":
				await storageManager.saveCar({
					carNum: op.carNum,
					url: `/v/${op.videoId}`,
					actionType: FAVORITE_ACTION
				});
				break;
			case "watched-from-javdb": await storageManager.saveCar({
				carNum: op.carNum,
				url: `/v/${op.videoId}`,
				actionType: HAS_WATCH_ACTION,
				score: 0
			});
		}
	}
	async function tick() {
		if (ticking) return;
		ticking = true;
		try {
			switch (session.phase) {
				case "fetch-want": {
					const startUrl = session.wantPage === 0 ? "/users/want_watch_videos" : `/users/want_watch_videos?page=${session.wantPage + 1}`;
					if (session.wantPage >= 200) {
						session.phase = "fetch-watched";
						saveSession(session);
						emit();
						break;
					}
					const { entries, nextUrl } = await fetchOnePage(startUrl);
					session.javdbWant.push(...entries);
					session.wantPage++;
					if (!nextUrl || entries.length === 0) session.phase = "fetch-watched";
					saveSession(session);
					emit();
					break;
				}
				case "fetch-watched": {
					const startUrl = session.watchedPage === 0 ? "/users/watched_videos" : `/users/watched_videos?page=${session.watchedPage + 1}`;
					if (session.watchedPage >= 200) {
						session.phase = "compare";
						saveSession(session);
						emit();
						break;
					}
					const { entries, nextUrl } = await fetchOnePage(startUrl);
					session.javdbWatched.push(...entries);
					session.watchedPage++;
					if (!nextUrl || entries.length === 0) session.phase = "compare";
					saveSession(session);
					emit();
					break;
				}
				case "compare":
					await buildQueue();
					break;
				case "execute": {
					if (session.queueDone >= session.queue.length) {
						session.phase = "done";
						saveSession(session);
						emit();
						stopTimer();
						break;
					}
					const op = session.queue[session.queueDone];
					try {
						await executeOp(op);
						session.synced++;
					} catch (err) {
						session.failed.push(`${op.carNum}(${err instanceof Error ? err.message : "失败"})`);
					}
					session.queueDone++;
					if (session.queueDone % 10 === 0 || session.queueDone >= session.queue.length) saveSession(session);
					emit();
					break;
				}
				default: stopTimer();
			}
		} catch (err) {
			session.phase = "error";
			session.errorMsg = err instanceof Error ? err.message : String(err);
			saveSession(session);
			emit();
			stopTimer();
		} finally {
			ticking = false;
		}
	}
	function startSync(interval) {
		stopTimer();
		session = createSession();
		session.phase = "fetch-want";
		saveSession(session);
		emit();
		timer = setInterval(() => void tick(), interval);
	}
	function resumeSync(interval) {
		const saved = loadSession();
		if (!saved || !isResumable(saved)) return false;
		stopTimer();
		session = saved;
		emit();
		timer = setInterval(() => void tick(), interval);
		return true;
	}
	function pauseSync() {
		stopTimer();
		saveSession(session);
		emit();
	}
	function resetSync() {
		stopTimer();
		session = createSession();
		clearSession();
		emit();
	}
	function stopTimer() {
		if (timer !== null) {
			clearInterval(timer);
			timer = null;
		}
	}
	function getProgress() {
		switch (session.phase) {
			case "idle": return 0;
			case "fetch-want": return Math.min(10, session.wantPage * .5);
			case "fetch-watched": return 10 + Math.min(10, session.watchedPage * .5);
			case "compare": return 20;
			case "execute": {
				const total = session.queue.length || 1;
				return 20 + Math.round(session.queueDone / total * 80);
			}
			case "done": return 100;
			case "error": return -1;
			default: return 0;
		}
	}
	function getPhaseLabel() {
		switch (session.phase) {
			case "idle": return "就绪";
			case "fetch-want": return `抓取 JavDB 想看列表(第 ${session.wantPage} 页,已获 ${session.javdbWant.length} 条)`;
			case "fetch-watched": return `抓取 JavDB 已看列表(第 ${session.watchedPage} 页,已获 ${session.javdbWatched.length} 条)`;
			case "compare": return "对比中…";
			case "execute": return `执行同步 ${session.queueDone}/${session.queue.length}(成功 ${session.synced},失败 ${session.failed.length})`;
			case "done": return `同步完成:成功 ${session.synced},失败 ${session.failed.length},跳过 ${session.skipped},冲突 ${session.conflicts.length}`;
			case "error": return `错误:${session.errorMsg || "未知"}`;
			default: return "";
		}
	}
	var bound = false;
	function refreshUi(session) {
		const progress = getProgress();
		const $bar = $("#fav-sync-progress-bar");
		const $phase = $("#fav-sync-phase");
		const $report = $("#fav-sync-report");
		if (progress >= 0) $bar.css({
			width: `${progress}%`,
			background: "#48c774"
		});
		else $bar.css({
			width: "100%",
			background: "#f14668"
		});
		$phase.text(getPhaseLabel());
		$("#fav-sync-stat-synced").text(String(session.synced));
		$("#fav-sync-stat-failed").text(String(session.failed.length));
		$("#fav-sync-stat-skipped").text(String(session.skipped));
		$("#fav-sync-stat-conflicts").text(String(session.conflicts.length));
		if (session.conflicts.length > 0 || session.failed.length > 0 || session.phase === "done") {
			let text = "";
			if (session.conflicts.length > 0) {
				text += "⚠ 冲突(需手动处理):\n";
				text += session.conflicts.map((c) => `  ${c.carNum}(脚本${c.jhsStatus} / 网站${c.javdbStatus})`).join("\n");
				text += "\n\n";
			}
			if (session.failed.length > 0) {
				text += "✗ 失败:\n";
				text += session.failed.map((f) => `  ${f}`).join("\n");
				text += "\n\n";
			}
			if (session.phase === "done") text += `✓ 同步完成:成功 ${session.synced},失败 ${session.failed.length},跳过 ${session.skipped},冲突 ${session.conflicts.length}`;
			$report.text(text).show();
		} else $report.hide();
		const running = isRunning();
		$("#fav-sync-start-btn").css({
			"pointer-events": running ? "none" : "",
			opacity: running ? "0.5" : ""
		});
		$("#fav-sync-resume-btn").css({
			"pointer-events": running || !hasResumableSession() ? "none" : "",
			opacity: running || !hasResumableSession() ? "0.5" : ""
		});
		$("#fav-sync-pause-btn").css({
			"pointer-events": running ? "" : "none",
			opacity: running ? "" : "0.5"
		});
	}
	function bindFavSyncPanel() {
		if (bound) return;
		bound = true;
		onSyncChange((session) => refreshUi(session));
		$("#fav-sync-start-btn").on("click", () => {
			if (!isJavdbSite) {
				show.error("请在 JavDB 站点下执行此同步");
				return;
			}
			utils.q(null, "确定开始双向增量同步?<br>将分页抓取 JavDB 想看/已看列表,对比后增量同步。<br>支持暂停/继续,关闭弹窗不中断。<br><span style=\"color:#f40\">建议先备份数据。</span>", () => {
				startSync(Number($("#favSyncInterval").val()) || 500);
			});
		});
		$("#fav-sync-resume-btn").on("click", () => {
			if (!isJavdbSite) {
				show.error("请在 JavDB 站点下执行此同步");
				return;
			}
			if (!resumeSync(Number($("#favSyncInterval").val()) || 500)) show.error("没有可恢复的同步会话");
		});
		$("#fav-sync-pause-btn").on("click", () => {
			pauseSync();
			show.ok("同步已暂停,可稍后继续");
		});
		$("#fav-sync-reset-btn").on("click", () => {
			utils.q(null, "确定重置同步?将清除所有进度和会话数据。", () => {
				resetSync();
				$("#fav-sync-report").hide();
				show.ok("同步已重置");
			});
		});
		if (hasResumableSession()) {
			resumeSync(Number($("#favSyncInterval").val()) || 500);
			pauseSync();
		}
	}
	function BackupFileDialog() {
		return (0, import_jsx_runtime.jsx)("div", {
			style: {
				height: "100%",
				overflow: "hidden"
			},
			children: (0, import_jsx_runtime.jsx)("div", {
				id: "table-container",
				style: { margin: "auto auto !important" }
			})
		});
	}
	function BackupActionCell() {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("a", {
				className: "a-danger",
				children: "删除"
			}),
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				className: "a-primary",
				children: "下载"
			}),
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				className: "a-success",
				children: "导入"
			})
		] });
	}
	function getWebDavCredentials() {
		const url = $("#webDavUrl").val() || GM_getValue("jhs_webdav_url", "");
		const username = $("#webDavUsername").val() || GM_getValue("jhs_webdav_username", "");
		const password = $("#webDavPassword").val() || GM_getValue("jhs_webdav_password", "");
		if (!url) {
			show.error("请填写webDav服务地址并保存后, 再试此功能");
			return null;
		}
		if (!username) {
			show.error("请填写webDav用户名并保存后, 再试此功能");
			return null;
		}
		if (!password) {
			show.error("请填写webDav密码并保存后, 再试此功能");
			return null;
		}
		return {
			url,
			username,
			password
		};
	}
	function getWebDavCredentialsFromGm() {
		const url = GM_getValue(GM_KEY_WEBDAV_URL, "");
		if (!url) return null;
		const username = GM_getValue(GM_KEY_WEBDAV_USERNAME, "");
		const password = GM_getValue(GM_KEY_WEBDAV_PASSWORD, "");
		if (!username || !password) return null;
		return {
			url,
			username,
			password
		};
	}
	async function backupDataByWebDav(plugin) {
		const creds = getWebDavCredentials();
		if (!creds) return;
		const settings = await storageManager.getSetting();
		const fileName = utils.getNowStr("_", "_") + ".json";
		const exportText = JSON.stringify(await buildBackupPayload(settings));
		try {
			await withLoading(async () => {
				await new window.WebDavClient(creds.url, creds.username, creds.password).backup(plugin.folderName, fileName, exportText);
				show.ok("备份完成");
			});
		} catch (err) {
			console.error(err);
			show.error(String(err));
		}
	}
	async function backupListBtnByWebDav(plugin) {
		const creds = getWebDavCredentials();
		if (!creds) return;
		try {
			await withLoading(async () => {
				const webdavClient = new window.WebDavClient(creds.url, creds.username, creds.password);
				openFileListDialog(plugin, await webdavClient.getBackupList(plugin.folderName), webdavClient, "WebDav");
			});
		} catch (err) {
			console.error(err);
			show.error(`发生错误: ${err instanceof Error ? err.message : String(err)}`);
		}
	}
	function openFileListDialog(plugin, backupList, provider, titlePrefix) {
		layer.open({
			type: 1,
			title: titlePrefix + "备份文件",
			content: jsxToString((0, import_react.createElement)(BackupFileDialog)),
			area: ["800px", "70%"],
			anim: -1,
			success: () => {
				const table = new Tabulator("#table-container", {
					layout: "fitColumns",
					placeholder: "暂无数据",
					virtualDom: true,
					data: backupList,
					responsiveLayout: "collapse",
					responsiveLayoutCollapse: true,
					columnDefaults: {
						headerHozAlign: "center",
						hozAlign: "center"
					},
					columns: [
						{
							title: "文件名",
							field: "name",
							width: 200,
							headerSort: false,
							responsive: 0
						},
						{
							title: "文件大小",
							field: "size",
							responsive: 1,
							headerSort: false,
							formatter: (cell) => {
								const units = [
									"B",
									"KB",
									"MB",
									"GB",
									"TB",
									"PB"
								];
								let unitIndex = 0;
								let size = cell.getData().size;
								while (size >= 1024 && unitIndex < units.length - 1) {
									size /= 1024;
									unitIndex++;
								}
								return `${size % 1 == 0 ? size.toFixed(0) : size.toFixed(2)} ${units[unitIndex]}`;
							}
						},
						{
							title: "备份日期",
							field: "createTime",
							responsive: 2,
							headerSort: false,
							formatter: (cell) => {
								const data = cell.getData();
								return `${utils.getNowStr("-", ":", new Date(data.createTime).getTime())}`;
							}
						},
						{
							title: "操作",
							minWidth: 250,
							responsive: 0,
							headerSort: false,
							formatter: (cell, _params, onRendered) => {
								const rowData = cell.getData();
								onRendered(() => {
									const $dangerBtn = cell.getElement().querySelector(".a-danger");
									const $primaryBtn = cell.getElement().querySelector(".a-primary");
									const $successBtn = cell.getElement().querySelector(".a-success");
									if ($dangerBtn) $dangerBtn.addEventListener("click", () => {
										layer.confirm(`是否删除 ${rowData.name} ?`, {
											icon: 3,
											title: "提示",
											btn: ["确定", "取消"]
										}, async (layerIndex) => {
											layer.close(layerIndex);
											try {
												await withLoading(async () => {
													await provider.deleteFile(rowData.fileId);
													const newList = await provider.getBackupList(plugin.folderName);
													table.replaceData(newList);
													layer.alert("删除成功");
												});
											} catch (err) {
												console.error(err);
												show.error(`发生错误: ${err instanceof Error ? err.message : String(err)}`);
											}
										});
									});
									if ($primaryBtn) $primaryBtn.addEventListener("click", async () => {
										try {
											await withLoading(async () => {
												const fileContent = await provider.getFileContent(rowData.fileId);
												const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
												let decrypted;
												if (isBackupV2(fileContent)) {
													if (!backupPassword) {
														show.error("该备份使用 V2 加密,请先设置备份口令");
														return;
													}
													decrypted = await decryptBackupV2(fileContent, backupPassword);
												} else decrypted = window.decryptCredential(fileContent);
												utils.download(decrypted, rowData.name);
											});
										} catch (err) {
											clog.error(err);
											show.error("下载失败: " + (err instanceof Error ? err.message : String(err)));
										}
									});
									if ($successBtn) $successBtn.addEventListener("click", () => {
										layer.confirm(`是否将该云备份数据 ${rowData.name} 导入?`, {
											icon: 3,
											title: "提示",
											btn: ["确定", "取消"]
										}, async (layerIndex) => {
											layer.close(layerIndex);
											try {
												await withLoading(async () => {
													let fileContent = await provider.getFileContent(rowData.fileId);
													show.info("解密文件内容...");
													const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
													if (isBackupV2(fileContent)) {
														if (!backupPassword) {
															show.error("该备份使用 V2 加密,请先设置备份口令");
															return;
														}
														fileContent = await decryptBackupV2(fileContent, backupPassword);
													} else fileContent = window.decryptCredential(fileContent);
													show.info("解密完成, 开始导入...");
													const parsedData = JSON.parse(fileContent);
													const extras = stripBackupExtras(parsedData);
													await storageManager.importData(parsedData);
													await applyBackupExtras(extras);
													show.ok("导入成功!");
													window.location.reload();
												});
											} catch (err) {
												console.error(err);
												show.error(String(err));
											}
										});
									});
								});
								return jsxToString((0, import_react.createElement)(BackupActionCell));
							}
						}
					],
					locale: "zh-cn",
					langs: { "zh-cn": { pagination: {
						first: "首页",
						first_title: "首页",
						last: "尾页",
						last_title: "尾页",
						prev: "上一页",
						prev_title: "上一页",
						next: "下一页",
						next_title: "下一页",
						all: "所有",
						page_size: "每页行数"
					} } }
				});
			}
		});
	}
	var GitHttpError = class extends Error {
		status;
		constructor(status, statusText, serverMessage) {
			const base = `请求失败 ${status}: ${statusText}`;
			super(serverMessage ? `${base}(${serverMessage})` : base);
			this.name = "GitHttpError";
			this.status = status;
		}
	};
	function extractServerMessage(responseText) {
		try {
			const parsed = JSON.parse(responseText);
			return typeof parsed.message === "string" ? parsed.message : "";
		} catch {
			return "";
		}
	}
	function utf8ToBase64(text) {
		const bytes = new TextEncoder().encode(text);
		let binary = "";
		const chunkSize = 32768;
		for (let offset = 0; offset < bytes.length; offset += chunkSize) binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
		return btoa(binary);
	}
	function base64ToUtf8(base64) {
		const binary = atob(base64.replace(/\s/g, ""));
		const bytes = new Uint8Array(binary.length);
		for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
		return new TextDecoder().decode(bytes);
	}
	function buildContentsPath(folder, filename) {
		const parts = [folder];
		if (filename) parts.push(filename);
		return parts.map((part) => encodeURIComponent(part)).join("/");
	}
	var GitBackupClient = class {
		apiUrl;
		repo;
		branch;
		token;
		folderName = null;
		constructor(config) {
			const baseUrl = (config.apiUrl || "https://api.github.com").trim();
			this.apiUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
			this.repo = config.repo.trim();
			this.branch = (config.branch || "main").trim();
			this.token = config.token;
		}
		buildHeaders(extra = {}) {
			return {
				Accept: "application/vnd.github+json",
				Authorization: `Bearer ${this.token}`,
				"X-GitHub-Api-Version": "2022-11-28",
				"User-Agent": "JavDB-Power-Tools-Backup",
				...extra
			};
		}
		sendRequest(method, path, body, extraHeaders = {}) {
			return new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method,
					url: this.apiUrl + path,
					headers: this.buildHeaders(extraHeaders),
					data: body !== void 0 ? JSON.stringify(body) : void 0,
					onload: (response) => {
						if (response.status >= 200 && response.status < 300) resolve(response);
						else reject(new GitHttpError(response.status, response.statusText, extractServerMessage(response.responseText)));
					},
					onerror: (error) => {
						clog.error("请求 Git 备份服务发生错误:", error);
						reject(new Error("请求 Git 备份服务失败, 请检查网络/代理、API 地址与 Token"));
					}
				});
			});
		}
		contentsPath(folder, filename) {
			return `/repos/${this.repo}/contents/${buildContentsPath(folder, filename)}?ref=${encodeURIComponent(this.branch)}`;
		}
		async fetchFileMeta(folder, filename) {
			try {
				const response = await this.sendRequest("GET", this.contentsPath(folder, filename));
				const parsed = JSON.parse(response.responseText);
				if (parsed.type === "file" && parsed.sha) return parsed;
				throw new Error(`路径 ${folder}/${filename} 不是文件`);
			} catch (error) {
				if (error instanceof GitHttpError && error.status === 404) return null;
				throw error;
			}
		}
		async fetchLastCommitDate(folder, filename) {
			try {
				const path = `/repos/${this.repo}/commits?path=${buildContentsPath(folder, filename)}&sha=${encodeURIComponent(this.branch)}`;
				const response = await this.sendRequest("GET", path);
				const latest = JSON.parse(response.responseText)[0];
				return latest?.commit?.committer?.date || latest?.commit?.author?.date || "";
			} catch {
				return "";
			}
		}
		async backup(folder, filename, content) {
			const meta = await this.fetchFileMeta(folder, filename);
			const body = {
				message: `jhs-backup: ${filename} ${new Date().toISOString()}`,
				content: utf8ToBase64(content),
				branch: this.branch
			};
			if (meta) body.sha = meta.sha;
			await this.sendRequest("PUT", this.contentsPath(folder, filename), body);
		}
		async getBackupList(folder) {
			this.folderName = folder;
			let entries;
			try {
				const response = await this.sendRequest("GET", this.contentsPath(folder));
				const parsed = JSON.parse(response.responseText);
				entries = Array.isArray(parsed) ? parsed : [];
			} catch (error) {
				if (error instanceof GitHttpError && error.status === 404) return [];
				throw error;
			}
			const files = entries.filter((entry) => entry.type === "file");
			const items = await Promise.all(files.map(async (entry) => ({
				fileId: entry.name,
				name: entry.name,
				size: entry.size,
				createTime: await this.fetchLastCommitDate(folder, entry.name)
			})));
			items.reverse();
			return items;
		}
		async getFileContent(filename) {
			const folder = this.requireFolder();
			const path = this.contentsPath(folder, filename);
			const response = await this.sendRequest("GET", path);
			const parsed = JSON.parse(response.responseText);
			if (typeof parsed.content === "string" && parsed.content !== "") return parsed.encoding === "base64" ? base64ToUtf8(parsed.content) : parsed.content;
			if ((parsed.size ?? 0) === 0) return "";
			return (await this.sendRequest("GET", path, void 0, { Accept: "application/vnd.github.raw+json" })).responseText;
		}
		async deleteFile(filename) {
			const folder = this.requireFolder();
			const meta = await this.fetchFileMeta(folder, filename);
			if (!meta) throw new Error(`文件 ${filename} 不存在或已被删除`);
			await this.sendRequest("DELETE", this.contentsPath(folder, filename), {
				message: `jhs-delete: ${filename} ${new Date().toISOString()}`,
				sha: meta.sha,
				branch: this.branch
			});
		}
		requireFolder() {
			if (!this.folderName) throw new Error("Git 备份目录未初始化(需先调用 getBackupList)");
			return this.folderName;
		}
	};
	var GIT_REPO_PATTERN = /^[\w.-]+\/[\w.-]+$/;
	function getGitCredentials() {
		const apiUrl = ($("#gitApiUrl").val() || GM_getValue("jhs_git_api_url", "")).trim() || "https://api.github.com";
		const repo = ($("#gitRepo").val() || GM_getValue("jhs_git_repo", "")).trim();
		const branch = ($("#gitBranch").val() || GM_getValue("jhs_git_branch", "")).trim() || "main";
		const token = $("#gitToken").val() || GM_getValue("jhs_git_token", "");
		if (!repo) {
			show.error("请填写 Git 仓库(owner/repo)并保存后, 再试此功能");
			return null;
		}
		if (!GIT_REPO_PATTERN.test(repo)) {
			show.error("Git 仓库格式应为 owner/repo");
			return null;
		}
		if (!token) {
			show.error("请填写 Git Token 并保存后, 再试此功能");
			return null;
		}
		return {
			apiUrl,
			repo,
			branch,
			token
		};
	}
	function getGitCredentialsFromGm() {
		const repo = GM_getValue(GM_KEY_GIT_REPO, "").trim();
		const token = GM_getValue(GM_KEY_GIT_TOKEN, "");
		if (!repo || !GIT_REPO_PATTERN.test(repo) || !token) return null;
		return {
			apiUrl: GM_getValue("jhs_git_api_url", "").trim() || "https://api.github.com",
			repo,
			branch: GM_getValue("jhs_git_branch", "").trim() || "main",
			token
		};
	}
	function buildGitClient(creds) {
		return new GitBackupClient(creds);
	}
	async function backupDataByGit(plugin) {
		const creds = getGitCredentials();
		if (!creds) return;
		const settings = await storageManager.getSetting();
		const fileName = utils.getNowStr("_", "_") + ".json";
		const exportText = JSON.stringify(await buildBackupPayload(settings));
		try {
			await withLoading(async () => {
				await buildGitClient(creds).backup(plugin.folderName, fileName, exportText);
				show.ok("备份完成");
			});
		} catch (err) {
			clog.error(err);
			show.error("备份失败: " + (err instanceof Error ? err.message : String(err)));
		}
	}
	async function backupListBtnByGit(plugin) {
		const creds = getGitCredentials();
		if (!creds) return;
		try {
			await withLoading(async () => {
				const gitClient = buildGitClient(creds);
				openFileListDialog(plugin, await gitClient.getBackupList(plugin.folderName), gitClient, "Git");
			});
		} catch (err) {
			clog.error(err);
			show.error(`发生错误: ${err instanceof Error ? err.message : String(err)}`);
		}
	}
	function bindClick$1(plugin) {
		$(".side-menu-item").on("click", function() {
			$(".side-menu-item").removeClass("active");
			$(this).addClass("active");
			$(".content-panel").hide();
			const panelName = $(this).data("panel");
			$("#" + panelName).show();
			if (panelName === "cache-panel") {
				$("#saveBtn").hide();
				$("#clean-all").show();
				refreshAllCacheStats(plugin);
			} else if (panelName === "vlt-panel") {
				$("#saveBtn").show();
				$("#clean-all").hide();
			} else if (panelName === "missav-panel") {
				$("#saveBtn").hide();
				$("#clean-all").hide();
			} else if (panelName === "fav-sync-panel") {
				$("#saveBtn").hide();
				$("#clean-all").hide();
			} else if (panelName === "preload-panel") {
				$("#saveBtn").show();
				$("#clean-all").hide();
				refreshPreloadCacheStats(plugin);
			} else if (panelName === "diagnostics-panel") {
				$("#saveBtn").hide();
				$("#clean-all").hide();
				plugin.refreshDiagnostics();
			} else {
				$("#saveBtn").show();
				$("#clean-all").hide();
			}
		});
		$("#importBtn").on("click", () => importData(plugin));
		$("#exportBtn").on("click", () => exportData(plugin));
		$("#webdavBackupBtn").on("click", () => backupDataByWebDav(plugin));
		$("#webdavBackupListBtn").on("click", () => backupListBtnByWebDav(plugin));
		$("#gitBackupBtn").on("click", () => backupDataByGit(plugin));
		$("#gitBackupListBtn").on("click", () => backupListBtnByGit(plugin));
		$("#saveBtn").on("click", () => saveForm(plugin));
		$("#copy-diagnostics-btn").on("click", () => {
			const text = renderDiagnosticsText(plugin.pluginManager);
			navigator.clipboard.writeText(text).then(() => show.ok("诊断报告已复制到剪贴板"), () => show.error("复制失败,请手动选择文本复制"));
		});
		$("#refresh-diagnostics-btn").on("click", () => {
			plugin.refreshDiagnostics();
			show.ok("诊断报告已刷新");
		});
		refreshAllCacheStats(plugin);
		$(".clean-btn").on("click", (event) => handleCleanCache(plugin, event));
		$("#clean-all").on("click", () => handleCleanAllCache(plugin));
		$(".view-btn").on("click", (event) => handleViewCache(event));
		$(".export-btn").on("click", (event) => handleExportCache(plugin, event));
		const $tagNumber = $("#highlightedTagNumber");
		const $tagColor = $("#highlightedTagColor");
		const $tagLabel = $("#highlightedTagLabel");
		function updateBorder() {
			const number = $tagNumber.val();
			const color = $tagColor.val();
			$tagLabel.css("border", `${number}px solid ${color}`);
		}
		$tagNumber.on("input", updateBorder);
		$tagColor.on("input", updateBorder);
		$("#vlt-import-btn").on("click", () => handleVltImportClick());
		$("#vlt-file-input").on("change", (event) => handleVltFileChange(event));
		$("#vlt-export-btn").on("click", () => handleVltExportClick());
		bindFavSyncPanel();
		$("#missav-sync-btn").on("click", () => handleMissavSync());
		$("#missav-import-btn").on("click", () => handleMissavImportClick());
		$("#missav-file-input").on("change", (event) => handleMissavFileChange(event));
		$("#missav-export-btn").on("click", () => handleMissavExportClick());
	}
	async function initSimpleSettingForm(plugin) {
		const settings = await storageManager.getSetting();
		const ns = ".jhsQs";
		$("#containerColumns").val(settings.containerColumns || 4);
		$("#showContainerColumns").text(settings.containerColumns || 4);
		$("#containerWidth").val((settings.containerWidth || 70) - 70);
		$("#showContainerWidth").text((settings.containerWidth || 70) + "%");
		$("#dialogOpenDetail").prop("checked", !settings.dialogOpenDetail || settings.dialogOpenDetail === "yes");
		$("#needClosePage").prop("checked", !settings.needClosePage || settings.needClosePage === "yes");
		const loadMode = settings.autoPageLoadMode === "click" ? "click" : "auto";
		$("#autoPageLoadMode").attr("data-value", loadMode);
		$("#autoPageLoadMode .jhs-qs-seg-btn").removeClass("is-active").filter(`[data-value="${loadMode}"]`).addClass("is-active");
		$("#translateTitle").prop("checked", !settings.translateTitle || settings.translateTitle === "yes");
		$("#enableLoadActressInfo").prop("checked", !settings.enableLoadActressInfo || settings.enableLoadActressInfo === "yes");
		$("#enableLoadOtherSite").prop("checked", !settings.enableLoadOtherSite || settings.enableLoadOtherSite === "yes");
		$("#containerColumns").off(`input${ns}`).on(`input${ns}`, async () => {
			const columns = $("#containerColumns").val();
			$("#showContainerColumns").text(String(columns ?? ""));
			if (isJavdbSite) {
				const movieListEl = document.querySelector(".movie-list");
				if (movieListEl) movieListEl.style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`;
			}
			await storageManager.saveSettingItem("containerColumns", columns);
			plugin.applyImageMode();
		});
		$("#containerWidth").off(`input${ns}`).on(`input${ns}`, async (event) => {
			const rangeValue = parseInt(String($(event.target).val()), 10) || 0;
			const widthPercent = rangeValue + 70 + "%";
			$("#showContainerWidth").text(widthPercent);
			if (isJavdbSite) {
				const containerEl = document.querySelector("section .container");
				if (containerEl) containerEl.style.minWidth = widthPercent;
			}
			storageManager.saveSettingItem("containerWidth", rangeValue + 70);
		});
		$("#dialogOpenDetail").off(`change${ns}`).on(`change${ns}`, () => {
			const value = $("#dialogOpenDetail").is(":checked") ? "yes" : "no";
			storageManager.saveSettingItem("dialogOpenDetail", value);
		});
		$("#showFilterItem").prop("checked", !!settings.showFilterItem && settings.showFilterItem === "yes");
		$("#showFilterActorItem").prop("checked", !!settings.showFilterActorItem && settings.showFilterActorItem === "yes");
		$("#showFilterKeywordItem").prop("checked", !!settings.showFilterKeywordItem && settings.showFilterKeywordItem === "yes");
		$("#showFavoriteItem").prop("checked", !settings.showFavoriteItem || settings.showFavoriteItem === "yes");
		$("#showHasWatchItem").prop("checked", !settings.showHasWatchItem || settings.showHasWatchItem === "yes");
		$("#showFilterItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showFilterItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showFilterItem", value);
			refresh();
		});
		$("#showFilterActorItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showFilterActorItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showFilterActorItem", value);
			refresh();
		});
		$("#showFilterKeywordItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showFilterKeywordItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showFilterKeywordItem", value);
			refresh();
		});
		$("#showFavoriteItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showFavoriteItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showFavoriteItem", value);
			refresh();
		});
		$("#showHasWatchItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showHasWatchItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showHasWatchItem", value);
			refresh();
		});
		const $filterCheckboxes = $("#showFilterItem, #showFilterActorItem, #showFilterKeywordItem, #showFavoriteItem, #showHasWatchItem");
		const updateDisabledState = () => {
			const isChecked = $("#showAllItem").is(":checked");
			$filterCheckboxes.prop("disabled", isChecked);
			if (isChecked) {
				$filterCheckboxes.attr("data-tip", "请先关闭显示所有才可点击");
				$("#jhs-qs-filter-group").addClass("is-disabled");
			} else {
				$filterCheckboxes.removeAttr("data-tip");
				$("#jhs-qs-filter-group").removeClass("is-disabled");
			}
		};
		$("#showAllItem").prop("checked", !!settings.showAllItem && settings.showAllItem === "yes");
		$("#showAllItem").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#showAllItem").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("showAllItem", value);
			updateDisabledState();
			refresh();
		});
		updateDisabledState();
		$("#needClosePage").off(`change${ns}`).on(`change${ns}`, async () => {
			await storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? "yes" : "no");
			refresh();
		});
		$("#autoPageLoadMode").off(`click${ns}`).on(`click${ns}`, ".jhs-qs-seg-btn", async (event) => {
			const value = $(event.currentTarget).attr("data-value") === "click" ? "click" : "auto";
			const $seg = $("#autoPageLoadMode");
			if ($seg.attr("data-value") === value) return;
			$seg.attr("data-value", value);
			$seg.find(".jhs-qs-seg-btn").removeClass("is-active");
			$(event.currentTarget).addClass("is-active");
			await storageManager.saveSettingItem("autoPageLoadMode", value);
			const autoPagePlugin = plugin.getBean("AutoPagePlugin");
			autoPagePlugin?.setLoadMode?.(value);
			autoPagePlugin?.showLoadAllBtn?.();
		});
		$("#translateTitle").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#translateTitle").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("translateTitle", value);
			if (value === "yes") await plugin.getBean("ListPagePlugin").doFilter();
			else {
				await plugin.getBean("ListPagePlugin").revertTranslation();
				$(".translated-title").remove();
			}
		});
		$("#enableLoadActressInfo").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#enableLoadActressInfo").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("enableLoadActressInfo", value);
			if (value === "yes") plugin.getBean("ActressInfoPlugin").loadActressInfo();
			else $(".actress-info").remove();
		});
		$("#enableLoadOtherSite").off(`change${ns}`).on(`change${ns}`, async () => {
			const value = $("#enableLoadOtherSite").is(":checked") ? "yes" : "no";
			await storageManager.saveSettingItem("enableLoadOtherSite", value);
			if (value === "yes") plugin.getBean("OtherSitePlugin").loadOtherSite().then();
			else $("#otherSiteBox").remove();
		});
		$("#moreBtn").off(`click${ns}`).on(`click${ns}`, () => {
			plugin.cancelSimpleSettingHide();
			$(".simple-setting, .mini-simple-setting").html("").hide();
			plugin.openSettingDialog("base-panel");
		});
		$("#helpBtn").off(`click${ns}`).on(`click${ns}`, () => {
			layer.open({
				type: 1,
				title: "",
				shadeClose: true,
				scrollbar: false,
				content: jsxToString((0, import_react.createElement)(StyleBlock, { css: help_dialog_default })) + jsxToString((0, import_react.createElement)(HelpDialog, null)),
				area: utils.getResponsiveArea(["50%", "90%"])
			});
		});
	}
	function KeywordLabel({ keyword, bgColor, textColor, variant, href }) {
		const style = {
			backgroundColor: bgColor,
			color: textColor
		};
		if (variant === "link") return (0, import_jsx_runtime.jsxs)("a", {
			className: "keyword-label",
			"data-keyword": keyword,
			style,
			href,
			target: "_blank",
			children: [keyword, (0, import_jsx_runtime.jsx)("span", {
				className: "keyword-remove",
				children: "×"
			})]
		});
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "keyword-label",
			"data-keyword": keyword,
			style,
			children: [keyword, (0, import_jsx_runtime.jsx)("span", {
				className: "keyword-remove",
				children: "×"
			})]
		});
	}
	function addLabelTag(containerSelector, keyword, noun = "屏蔽词") {
		const $tagBox = $(`${containerSelector} .tag-box`);
		let $label;
		const bgColor = "#cbd5e1";
		let textColor = "#333";
		if (/^[a-z]{2,}-/i.test(keyword) && isJavdbSite) {
			textColor = "#3477ad";
			$label = $(jsxToString((0, import_react.createElement)(KeywordLabel, {
				keyword,
				bgColor,
				textColor,
				variant: "link",
				href: `/video_codes/${keyword.replace("-", "")}`
			})));
		} else $label = $(jsxToString((0, import_react.createElement)(KeywordLabel, {
			keyword,
			bgColor,
			textColor,
			variant: "div"
		})));
		$label.find(".keyword-remove").click((event) => {
			event.stopPropagation();
			event.preventDefault();
			const $removeBtn = $(event.currentTarget);
			const removeKeyword = ($removeBtn.closest(".keyword-label").attr("data-keyword") ?? "").split(" ")[0];
			utils.q(event, `是否移除${noun}  ${removeKeyword}?`, async () => {
				$removeBtn.parent().remove();
			});
		});
		$tagBox.append($label);
	}
	function addKeyword(containerSelector, noun = "屏蔽词") {
		const $input = $(`${containerSelector} .keyword-input`);
		const keyword = String($input.val() ?? "").trim();
		if (keyword) {
			addLabelTag(containerSelector, keyword, noun);
			$input.val("");
		}
	}
	function SettingButton({ id, color, children }) {
		return (0, import_jsx_runtime.jsx)("a", {
			id,
			className: `jhs-setting-btn jhs-setting-btn--${color}`,
			children
		});
	}
	function SettingCheckbox({ id, label }) {
		return (0, import_jsx_runtime.jsxs)("label", {
			className: "jhs-checkbox-label",
			children: [(0, import_jsx_runtime.jsx)("input", {
				id,
				type: "checkbox"
			}), (0, import_jsx_runtime.jsx)("span", { children: label })]
		});
	}
	function SettingInput({ id, type = "text", placeholder, readOnly, min, max, step, style }) {
		return (0, import_jsx_runtime.jsx)("input", {
			id,
			type,
			placeholder,
			readOnly,
			min,
			max,
			step,
			style
		});
	}
	function SettingRow({ label, tooltip, children, className }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: `setting-item${className ? " " + className : ""}`,
			children: [(0, import_jsx_runtime.jsxs)("span", {
				className: "setting-label",
				children: [label, tooltip ? (0, import_jsx_runtime.jsx)("span", {
					className: "jhs-tooltip-icon",
					"data-tip": tooltip,
					children: "?"
				}) : null]
			}), (0, import_jsx_runtime.jsx)("div", {
				className: "form-content",
				children
			})]
		});
	}
	function SettingSection({ title, icon, children }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "jhs-setting-section",
			children: [title ? (0, import_jsx_runtime.jsxs)("div", {
				className: "jhs-setting-section-title",
				children: [icon ? (0, import_jsx_runtime.jsx)("span", { children: icon }) : null, title]
			}) : null, children]
		});
	}
	function SettingSelect({ id, children, dangerouslySetInnerHTML }) {
		return (0, import_jsx_runtime.jsx)("select", {
			id,
			dangerouslySetInnerHTML,
			children
		});
	}
	function BackupPanel({ panelName }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "backup-panel",
			className: "content-panel",
			style: { display: panelName === "backup-panel" ? "block" : "none" },
			children: [
				(0, import_jsx_runtime.jsx)(SettingSection, { children: (0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						gap: "10px"
					},
					children: [(0, import_jsx_runtime.jsx)(SettingButton, {
						id: "importBtn",
						color: "pink",
						children: "📥 导入数据"
					}), (0, import_jsx_runtime.jsx)(SettingButton, {
						id: "exportBtn",
						color: "mint",
						children: "📤 导出数据"
					})]
				}) }),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "WebDav 备份",
					icon: "☁️",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "WebDav备份",
							children: (0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "8px"
								},
								children: [(0, import_jsx_runtime.jsx)(SettingButton, {
									id: "webdavBackupListBtn",
									color: "primary",
									children: "查看备份"
								}), (0, import_jsx_runtime.jsx)(SettingButton, {
									id: "webdavBackupBtn",
									color: "success",
									children: "备份数据"
								})]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "服务地址:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "webDavUrl" })
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "用户名:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "webDavUsername" })
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "密码:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "webDavPassword",
								type: "password"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "备份口令:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "backupPassword",
								type: "password",
								placeholder: "加密备份的口令(V2 格式)"
							})
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "Git 备份(GitHub 兼容)",
					icon: "🔀",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "Git备份",
							children: (0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "8px"
								},
								children: [(0, import_jsx_runtime.jsx)(SettingButton, {
									id: "gitBackupListBtn",
									color: "primary",
									children: "查看备份"
								}), (0, import_jsx_runtime.jsx)(SettingButton, {
									id: "gitBackupBtn",
									color: "success",
									children: "备份数据"
								})]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "API 地址:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "gitApiUrl",
								placeholder: "默认 https://api.github.com(自建 Gitea 填 …/api/v1)"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "仓库:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "gitRepo",
								placeholder: "owner/repo(请先建好私有仓库)"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "分支:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "gitBranch",
								placeholder: "默认 main"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "Token:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "gitToken",
								type: "password",
								placeholder: "PAT(仅需仓库内容读写权限)"
							})
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "自动备份",
					icon: "⏰",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "启用自动备份",
							children: (0, import_jsx_runtime.jsx)(SettingCheckbox, {
								id: "enableAutoBackup",
								label: "开启后按频率自动备份到已启用的通道"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "备份通道:",
							children: (0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "16px"
								},
								children: [(0, import_jsx_runtime.jsx)(SettingCheckbox, {
									id: "enableWebdavChannel",
									label: "WebDav"
								}), (0, import_jsx_runtime.jsx)(SettingCheckbox, {
									id: "enableGitChannel",
									label: "Git"
								})]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "备份频率:",
							children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
								id: "autoBackupFrequency",
								children: [
									(0, import_jsx_runtime.jsx)("option", {
										value: "daily",
										children: "每天第一次打开(推荐)"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "everyOpen",
										children: "每次打开"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "disabled",
										children: "不自动备份"
									})
								]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "本机凭证:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "credentialIdDisplay",
								readOnly: true,
								style: {
									fontSize: "11px",
									fontFamily: "monospace"
								}
							})
						})
					]
				})
			]
		});
	}
	function SettingColorRow({ numberId, colorId, labelId, label, className }) {
		return (0, import_jsx_runtime.jsx)(SettingRow, {
			label,
			className,
			children: (0, import_jsx_runtime.jsxs)("div", {
				style: {
					display: "flex",
					gap: "8px",
					alignItems: "center"
				},
				children: [
					(0, import_jsx_runtime.jsx)("input", {
						id: numberId,
						type: "number",
						style: {
							width: "80px",
							flexShrink: 0
						}
					}),
					(0, import_jsx_runtime.jsx)("input", {
						id: colorId,
						type: "color",
						style: { flexShrink: 0 }
					}),
					(0, import_jsx_runtime.jsx)("span", {
						id: labelId,
						style: {
							display: "inline-block",
							padding: "4px 12px",
							borderRadius: "4px",
							fontSize: "13px",
							border: "1px solid #ce2222"
						},
						children: "示例标签"
					})
				]
			})
		});
	}
	function SettingToggle({ id, label, tooltip, className }) {
		return (0, import_jsx_runtime.jsx)(SettingRow, {
			label,
			tooltip,
			className,
			children: (0, import_jsx_runtime.jsx)("input", {
				id,
				type: "checkbox",
				className: "jhs-toggle"
			})
		});
	}
	function BasePanel({ panelName, qualityOptionsHtml, isJavdbSite }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "base-panel",
			className: "content-panel",
			style: { display: panelName === "base-panel" ? "block" : "none" },
			children: [
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "展示配置",
					icon: "🎛️",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "打开待鉴定|已收藏 窗口数:",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "waitCheckCount",
								type: "number",
								min: "1",
								max: "20"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "已鉴定标签展示位置:",
							children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
								id: "tagPosition",
								children: [(0, import_jsx_runtime.jsx)("option", {
									value: "rightTop",
									children: "右上"
								}), (0, import_jsx_runtime.jsx)("option", {
									value: "leftTop",
									children: "左上"
								})]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "已鉴定内容展示方式",
							tooltip: "hide=直接隐藏;visibility=透明占位保留布局",
							children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
								id: "movieShowType",
								children: [(0, import_jsx_runtime.jsx)("option", {
									value: "hide",
									children: "隐藏 (hide)"
								}), (0, import_jsx_runtime.jsx)("option", {
									value: "visibility",
									children: "透明占位 (visibility)"
								})]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "enableSaveActressCarInfo",
							label: "鉴定补录演员信息",
							tooltip: "在列表页进行鉴定是获取不到演员名称的, 开启后, 额外解析详情页补录演员名称, 因发请求解析费时, 会被以往慢1秒左右"
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "播放与评论",
					icon: "🎬",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "预览视频默认画质:",
							children: (0, import_jsx_runtime.jsx)(SettingSelect, {
								id: "videoQuality",
								dangerouslySetInnerHTML: { __html: qualityOptionsHtml }
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "评论区条数:",
							children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
								id: "reviewCount",
								children: [
									(0, import_jsx_runtime.jsx)("option", {
										value: "10",
										children: "10条"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "20",
										children: "20条"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "30",
										children: "30条"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "40",
										children: "40条"
									}),
									(0, import_jsx_runtime.jsx)("option", {
										value: "50",
										children: "50条"
									})
								]
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "enableFavoriteActresses",
							label: "高亮已收藏演员",
							tooltip: "详情页, 对已收藏的演员进行边框高亮提醒",
							className: isJavdbSite ? "" : "do-hide"
						}),
						(0, import_jsx_runtime.jsx)(SettingColorRow, {
							numberId: "highlightedTagNumber",
							colorId: "highlightedTagColor",
							labelId: "highlightedTagLabel",
							label: "分类标签|高亮演员-边框样式:",
							className: isJavdbSite ? "" : "do-hide"
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "网络请求",
					icon: "🌐",
					children: [(0, import_jsx_runtime.jsx)(SettingRow, {
						label: "请求超时时间(毫秒):",
						children: (0, import_jsx_runtime.jsx)(SettingInput, {
							id: "httpTimeout",
							type: "number",
							min: "1000",
							max: "10000"
						})
					}), (0, import_jsx_runtime.jsx)(SettingRow, {
						label: "请求失败重试次数:",
						children: (0, import_jsx_runtime.jsx)(SettingInput, {
							id: "httpRetryCount",
							type: "number",
							min: "0",
							max: "10"
						})
					})]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "日志",
					icon: "📝",
					children: [(0, import_jsx_runtime.jsx)(SettingRow, {
						label: "启用控制台日志:",
						children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
							id: "enableClog",
							children: [(0, import_jsx_runtime.jsx)("option", {
								value: "no",
								children: "禁用"
							}), (0, import_jsx_runtime.jsx)("option", {
								value: "yes",
								children: "开启"
							})]
						})
					}), (0, import_jsx_runtime.jsx)(SettingRow, {
						label: "日志最大行数:",
						children: (0, import_jsx_runtime.jsx)(SettingInput, {
							id: "clogMsgCount",
							type: "number",
							min: "100",
							max: "3000"
						})
					})]
				})
			]
		});
	}
	function FilterPanel({ panelName }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "filter-panel",
			className: "content-panel",
			style: { display: panelName === "filter-panel" ? "block" : "none" },
			children: (0, import_jsx_runtime.jsx)(SettingSection, {
				title: "屏蔽关键词",
				icon: "🚫",
				children: (0, import_jsx_runtime.jsxs)("div", {
					id: "filterKeywordContainer",
					className: "filter-keyword-container",
					children: [(0, import_jsx_runtime.jsx)("div", { className: "tag-box" }), (0, import_jsx_runtime.jsxs)("div", {
						style: {
							display: "flex",
							gap: "8px",
							marginTop: "8px"
						},
						children: [(0, import_jsx_runtime.jsx)("input", {
							type: "text",
							className: "keyword-input",
							placeholder: "输入关键词后按回车添加",
							style: { flex: 1 }
						}), (0, import_jsx_runtime.jsx)("a", {
							className: "add-tag-btn jhs-setting-btn jhs-setting-btn--primary",
							style: { flexShrink: 0 },
							children: "添加"
						})]
					})]
				})
			})
		});
	}
	function DomainPanel({ panelName }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "domain-panel",
			className: "content-panel",
			style: { display: panelName === "domain-panel" ? "block" : "none" },
			children: (0, import_jsx_runtime.jsxs)(SettingSection, {
				title: "第三方视频资源域名",
				icon: "🌐",
				children: [(0, import_jsx_runtime.jsx)(SettingRow, {
					label: "域名 - MissAv:",
					children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "missAvUrl" })
				}), (0, import_jsx_runtime.jsx)(SettingRow, {
					label: "域名 - SupJav:",
					children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "supJavUrl" })
				})]
			})
		});
	}
	function CachePanel({ panelName, cacheItemsHtml }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "cache-panel",
			className: "content-panel",
			style: { display: panelName === "cache-panel" ? "block" : "none" },
			children: (0, import_jsx_runtime.jsxs)(SettingSection, {
				title: "缓存管理",
				icon: "🧹",
				children: [
					(0, import_jsx_runtime.jsx)("div", {
						style: {
							display: "flex",
							justifyContent: "space-between",
							alignItems: "center",
							marginBottom: "4px"
						},
						children: (0, import_jsx_runtime.jsx)("span", {
							id: "cache-total-size",
							style: {
								fontSize: "13px",
								color: "#999"
							}
						})
					}),
					(0, import_jsx_runtime.jsx)("p", {
						style: {
							color: "#999",
							fontSize: "12px",
							margin: "0 0 16px 0"
						},
						children: "以下操作不会对核心数据造成影响,可安全清理"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						style: {
							display: "grid",
							gridTemplateColumns: "repeat(2, 1fr)",
							gap: "15px"
						},
						dangerouslySetInnerHTML: { __html: cacheItemsHtml }
					}),
					(0, import_jsx_runtime.jsx)("div", {
						id: "cache-data-display",
						style: {
							marginTop: "20px",
							display: "none"
						},
						children: (0, import_jsx_runtime.jsx)("pre", { style: {
							background: "#f5f5f5",
							padding: "10px",
							borderRadius: "5px",
							maxHeight: "400px",
							overflow: "auto"
						} })
					})
				]
			})
		});
	}
	function SyncPanel({ panelId, panelName, title, icon, description, buttons, statusId, fileInputId }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: panelId,
			className: "content-panel",
			style: { display: panelName === panelId ? "block" : "none" },
			children: (0, import_jsx_runtime.jsxs)(SettingSection, {
				title,
				icon,
				children: [
					(0, import_jsx_runtime.jsx)("p", {
						style: {
							margin: "0 0 16px 0",
							color: "#6c757d",
							fontSize: "13px",
							lineHeight: "1.6"
						},
						children: description
					}),
					(0, import_jsx_runtime.jsx)("div", {
						style: {
							display: "flex",
							gap: "12px",
							marginBottom: "20px"
						},
						children: buttons.map((btn) => (0, import_jsx_runtime.jsx)(SettingButton, {
							id: btn.id,
							color: btn.color,
							children: btn.text
						}, btn.id))
					}),
					(0, import_jsx_runtime.jsx)("div", {
						id: statusId,
						style: {
							fontSize: "13px",
							color: "#6c757d",
							marginTop: "8px"
						}
					}),
					(0, import_jsx_runtime.jsx)("input", {
						type: "file",
						id: fileInputId,
						accept: ".json,application/json",
						style: { display: "none" }
					})
				]
			})
		});
	}
	function ListManagerPanel({ panelName }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "vlt-panel",
			className: "content-panel",
			style: { display: panelName === "vlt-panel" ? "block" : "none" },
			children: [
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "序号合并",
					icon: "🗂️",
					children: [
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "listMergeEnabled",
							label: "启用序号清单合并",
							tooltip: "详情页清单面板中,将「文字+数字」结尾的清单(如 等待更新1/等待更新2)按前缀合并为一行;勾选时自动放入最靠前的未满清单,全满自动新建下一序号清单"
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "合并匹配正则:",
							tooltip: "需含两个捕获组:第1组=前缀文字,第2组=末尾序号。留空使用默认规则",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "listMergePattern",
								placeholder: "^(.+?)(\\d+)$"
							})
						}),
						(0, import_jsx_runtime.jsx)("div", {
							style: {
								margin: "12px 0 4px",
								fontWeight: 600,
								fontSize: "13px",
								color: "#3d4652"
							},
							children: "不参与合并的清单名:"
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							id: "listMergeExcludeNamesContainer",
							className: "filter-keyword-container",
							children: [(0, import_jsx_runtime.jsx)("div", { className: "tag-box" }), (0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "8px",
									marginTop: "8px"
								},
								children: [(0, import_jsx_runtime.jsx)("input", {
									type: "text",
									className: "keyword-input",
									placeholder: "输入清单名或前缀后按回车添加",
									style: { flex: 1 }
								}), (0, import_jsx_runtime.jsx)("a", {
									className: "add-tag-btn jhs-setting-btn jhs-setting-btn--primary",
									style: { flexShrink: 0 },
									children: "添加"
								})]
							})]
						})
					]
				}),
				(0, import_jsx_runtime.jsx)(SettingSection, {
					title: "排序显示",
					icon: "📐",
					children: (0, import_jsx_runtime.jsx)(SettingRow, {
						label: "默认排序方式:",
						tooltip: "详情页清单面板打开时的默认排序(字段+方向,仍可在面板内临时切换)",
						children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
							id: "listDefaultSort",
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "name-desc",
									children: "名称降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "name-asc",
									children: "名称升序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "count-desc",
									children: "数量降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "count-asc",
									children: "数量升序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "num-desc",
									children: "序号降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "num-asc",
									children: "序号升序"
								})
							]
						})
					})
				}),
				(0, import_jsx_runtime.jsx)(SettingSection, {
					title: "编号整理",
					icon: "⚙️",
					children: (0, import_jsx_runtime.jsx)(SettingToggle, {
						id: "listReflowEnabled",
						label: "启用清单页编号整理",
						tooltip: "在 /lists/ 清单页显示「编号清单整理」面板:识别同前缀编号组、生成组内压实计划预览(当前版本只读,不执行搬运)"
					})
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "压实执行",
					icon: "🚀",
					children: [
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "请求间隔(毫秒):",
							tooltip: "压实搬运时每个加入/移除请求之间的间隔;越大越温和、越慢,建议 300–800",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "reflowInterval",
								type: "number",
								min: "100",
								max: "5000"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "试运行数量:",
							tooltip: "执行整理时先搬运这么多部并暂停,确认无误后再继续剩余",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "reflowTrialCount",
								type: "number",
								min: "1",
								max: "100"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "每批数量:",
							tooltip: "断点续传的检查点粒度(每搬运这么多部存一次进度,期2b 生效)",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "reflowBatchSize",
								type: "number",
								min: "1",
								max: "200"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "连续失败上限:",
							tooltip: "连续失败达到该次数后暂停并提示(期2b 自适应退避生效)",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "reflowMaxRetry",
								type: "number",
								min: "1",
								max: "20"
							})
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "自动收藏联动",
					icon: "⭐",
					children: [
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "autoFavCleanupEnabled",
							label: "启用自动收藏/移除联动",
							tooltip: "加入「除受保护清单外」的任意清单自动收藏(想看);从收藏改为已读星级时,自动从「除受保护清单外」的所有清单移出"
						}),
						(0, import_jsx_runtime.jsx)("div", {
							style: {
								margin: "12px 0 4px",
								fontWeight: 600,
								fontSize: "13px",
								color: "#3d4652"
							},
							children: "受保护清单关键词:"
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							id: "protectedListKeywordsContainer",
							className: "filter-keyword-container",
							children: [(0, import_jsx_runtime.jsx)("div", { className: "tag-box" }), (0, import_jsx_runtime.jsxs)("div", {
								style: {
									display: "flex",
									gap: "8px",
									marginTop: "8px"
								},
								children: [(0, import_jsx_runtime.jsx)("input", {
									type: "text",
									className: "keyword-input",
									placeholder: "输入关键词后按回车添加(包含匹配)",
									style: { flex: 1 }
								}), (0, import_jsx_runtime.jsx)("a", {
									className: "add-tag-btn jhs-setting-btn jhs-setting-btn--primary",
									style: { flexShrink: 0 },
									children: "添加"
								})]
							})]
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "对账校准",
					icon: "🔄",
					children: [(0, import_jsx_runtime.jsx)(SettingToggle, {
						id: "autoServerSync",
						label: "打开页面时自动校验同步",
						tooltip: "打开影片详情页时核实脚本与 JavDB 的状态:评分/已读/收藏或清单关联如果脚本已写入但服务器未同步(如网络失败),自动按脚本记录补推 JavDB;服务器有而脚本无则拉取补齐。勾选状态与服务器冲突时跳过,避免误覆盖"
					}), (0, import_jsx_runtime.jsx)(SettingRow, {
						label: "自动对账延迟(毫秒):",
						tooltip: "详情页清单弹窗变动后,触发本地镜像自动对账的防抖延迟;过小易与服务端写入竞速产生良性校准提示,建议 600–1500",
						children: (0, import_jsx_runtime.jsx)(SettingInput, {
							id: "autoReconcileDelay",
							type: "number",
							min: "200",
							max: "5000"
						})
					})]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "数据管理",
					icon: "💾",
					children: [
						(0, import_jsx_runtime.jsxs)("p", {
							style: {
								margin: "0 0 16px 0",
								color: "#6c757d",
								fontSize: "13px",
								lineHeight: "1.6"
							},
							children: [
								"管理影片与清单的关联数据(本地 IndexedDB,随 WebDav 备份)。",
								(0, import_jsx_runtime.jsx)("br", {}),
								(0, import_jsx_runtime.jsx)("strong", { children: "导入" }),
								":从 JSON 文件导入数据(如 PostgreSQL 迁移数据)。",
								(0, import_jsx_runtime.jsx)("br", {}),
								(0, import_jsx_runtime.jsx)("strong", { children: "导出" }),
								":将当前数据导出为 JSON 文件(备份/迁移)。"
							]
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							style: {
								display: "flex",
								gap: "12px",
								marginBottom: "20px"
							},
							children: [(0, import_jsx_runtime.jsx)(SettingButton, {
								id: "vlt-import-btn",
								color: "primary",
								children: "导入数据"
							}), (0, import_jsx_runtime.jsx)(SettingButton, {
								id: "vlt-export-btn",
								color: "success",
								children: "导出数据"
							})]
						}),
						(0, import_jsx_runtime.jsx)("div", {
							id: "vlt-status",
							style: {
								fontSize: "13px",
								color: "#6c757d",
								marginTop: "8px"
							}
						}),
						(0, import_jsx_runtime.jsx)("input", {
							type: "file",
							id: "vlt-file-input",
							accept: ".json,application/json",
							style: { display: "none" }
						})
					]
				})
			]
		});
	}
	function FavSyncPanel({ panelName }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "fav-sync-panel",
			className: "content-panel",
			style: {
				display: panelName === "fav-sync-panel" ? "block" : "none",
				overflowY: "auto",
				height: "100%"
			},
			children: [(0, import_jsx_runtime.jsxs)(SettingSection, {
				title: "双向增量同步",
				icon: "🔄",
				children: [
					(0, import_jsx_runtime.jsxs)("p", {
						style: {
							margin: "0 0 12px 0",
							color: "#6c757d",
							fontSize: "13px",
							lineHeight: "1.6"
						},
						children: [
							"双向增量同步「想看」和「已看」:脚本有/网站无 → 补到网站;网站有/脚本无 → 补到脚本。 冲突(双方状态不一致)仅报告番号,不自动处理。",
							(0, import_jsx_runtime.jsx)("br", {}),
							"支持 5 万+ 条目:分页抓取、断点续接、后台处理。关闭弹窗不中断同步,重新打开自动恢复进度。",
							(0, import_jsx_runtime.jsx)("br", {}),
							(0, import_jsx_runtime.jsx)("span", {
								style: { color: "#f40" },
								children: "建议先备份数据。"
							})
						]
					}),
					(0, import_jsx_runtime.jsx)(SettingRow, {
						label: "同步请求间隔(毫秒):",
						tooltip: "逐个操作时的请求间隔;越大越温和、越慢(建议 300-1000)",
						children: (0, import_jsx_runtime.jsx)(SettingInput, {
							id: "favSyncInterval",
							type: "number",
							min: "100",
							max: "5000"
						})
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						style: {
							display: "flex",
							gap: "10px",
							alignItems: "center",
							marginTop: "12px",
							flexWrap: "wrap"
						},
						children: [
							(0, import_jsx_runtime.jsx)(SettingButton, {
								id: "fav-sync-start-btn",
								color: "primary",
								children: "开始同步"
							}),
							(0, import_jsx_runtime.jsx)(SettingButton, {
								id: "fav-sync-resume-btn",
								color: "mint",
								children: "继续同步"
							}),
							(0, import_jsx_runtime.jsx)(SettingButton, {
								id: "fav-sync-pause-btn",
								color: "pink",
								children: "暂停"
							}),
							(0, import_jsx_runtime.jsx)(SettingButton, {
								id: "fav-sync-reset-btn",
								color: "danger",
								children: "重置"
							})
						]
					})
				]
			}), (0, import_jsx_runtime.jsxs)(SettingSection, {
				title: "同步进度",
				icon: "📊",
				children: [
					(0, import_jsx_runtime.jsxs)("div", {
						style: { marginBottom: "12px" },
						children: [(0, import_jsx_runtime.jsx)("div", {
							style: {
								height: "8px",
								borderRadius: "4px",
								background: "#e2e8f0",
								overflow: "hidden"
							},
							children: (0, import_jsx_runtime.jsx)("div", {
								id: "fav-sync-progress-bar",
								style: {
									height: "100%",
									width: "0%",
									borderRadius: "4px",
									background: "#48c774",
									transition: "width 0.3s ease"
								}
							})
						}), (0, import_jsx_runtime.jsx)("div", {
							id: "fav-sync-phase",
							style: {
								fontSize: "13px",
								color: "#6c757d",
								marginTop: "6px"
							},
							children: "就绪"
						})]
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						id: "fav-sync-stats",
						style: {
							display: "grid",
							gridTemplateColumns: "repeat(4, 1fr)",
							gap: "8px",
							fontSize: "13px",
							marginBottom: "12px"
						},
						children: [
							(0, import_jsx_runtime.jsxs)("div", {
								style: {
									textAlign: "center",
									padding: "8px",
									background: "#f0fdf4",
									borderRadius: "6px"
								},
								children: [(0, import_jsx_runtime.jsx)("div", {
									id: "fav-sync-stat-synced",
									style: {
										fontSize: "18px",
										fontWeight: 700,
										color: "#16a34a"
									},
									children: "0"
								}), (0, import_jsx_runtime.jsx)("div", {
									style: { color: "#6c757d" },
									children: "成功"
								})]
							}),
							(0, import_jsx_runtime.jsxs)("div", {
								style: {
									textAlign: "center",
									padding: "8px",
									background: "#fef2f2",
									borderRadius: "6px"
								},
								children: [(0, import_jsx_runtime.jsx)("div", {
									id: "fav-sync-stat-failed",
									style: {
										fontSize: "18px",
										fontWeight: 700,
										color: "#dc2626"
									},
									children: "0"
								}), (0, import_jsx_runtime.jsx)("div", {
									style: { color: "#6c757d" },
									children: "失败"
								})]
							}),
							(0, import_jsx_runtime.jsxs)("div", {
								style: {
									textAlign: "center",
									padding: "8px",
									background: "#f8fafc",
									borderRadius: "6px"
								},
								children: [(0, import_jsx_runtime.jsx)("div", {
									id: "fav-sync-stat-skipped",
									style: {
										fontSize: "18px",
										fontWeight: 700,
										color: "#6b7280"
									},
									children: "0"
								}), (0, import_jsx_runtime.jsx)("div", {
									style: { color: "#6c757d" },
									children: "跳过"
								})]
							}),
							(0, import_jsx_runtime.jsxs)("div", {
								style: {
									textAlign: "center",
									padding: "8px",
									background: "#fffbeb",
									borderRadius: "6px"
								},
								children: [(0, import_jsx_runtime.jsx)("div", {
									id: "fav-sync-stat-conflicts",
									style: {
										fontSize: "18px",
										fontWeight: 700,
										color: "#d97706"
									},
									children: "0"
								}), (0, import_jsx_runtime.jsx)("div", {
									style: { color: "#6c757d" },
									children: "冲突"
								})]
							})
						]
					}),
					(0, import_jsx_runtime.jsx)("div", {
						id: "fav-sync-report",
						style: {
							fontSize: "12px",
							color: "#374151",
							whiteSpace: "pre-wrap",
							maxHeight: "200px",
							overflowY: "auto",
							padding: "10px",
							background: "#f9fafb",
							borderRadius: "6px",
							border: "1px solid #e5e7eb",
							display: "none"
						}
					})
				]
			})]
		});
	}
	function PreloadPanel({ panelName }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			id: "preload-panel",
			className: "content-panel",
			style: { display: panelName === "preload-panel" ? "block" : "none" },
			children: [
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "预加载行为",
					icon: "⚡",
					children: [
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "enablePreload",
							label: "列表页预加载",
							tooltip: "在视频流(列表页/清单详情页/搜索页)后台预加载 missav 等站点搜索结果并缓存,打开详情页时按钮零延迟变绿。关闭后不预加载、不显示状态徽标与筛选栏(详情页按钮仍受「外部网站功能」总开关控制)"
						}),
						(0, import_jsx_runtime.jsx)(SettingToggle, {
							id: "enablePreloadStatus",
							label: "显示状态徽标与筛选栏",
							tooltip: "在每个卡片标题下方显示预加载状态(排队中/请求中/成功匹配/匹配失败),并在筛选栏实时计数、可点击过滤。关闭后仍后台预加载缓存,但不显示 UI"
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "预加载防抖延迟 (毫秒)",
							tooltip: "列表页瀑布流追加新卡片后,等待多久再开始批量入队预加载。过小可能与页面渲染抢资源;过大则新卡片状态延迟出现。缺省 300",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "preloadDebounce",
								type: "number",
								min: "0",
								max: "5000",
								step: "50"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "预加载并发数 (1-10)",
							tooltip: "同时向 missav 等站点发起预加载请求的数量。1=串行最稳(默认,降低 Cloudflare 拦截风险);2~5 可明显加快列表预加载;过高易被拦截并触发本轮跳过该站点。建议从 2 试起",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "preloadConcurrency",
								type: "number",
								min: "1",
								max: "10",
								step: "1"
							})
						}),
						(0, import_jsx_runtime.jsx)(SettingRow, {
							label: "预加载缓存有效期 (天)",
							tooltip: "命中缓存超过此天数后视为过期,下次打开列表页会重新预加载。0=永不过期(缺省)。建议 7-30 天平衡新鲜度与请求量",
							children: (0, import_jsx_runtime.jsx)(SettingInput, {
								id: "preloadCacheTTL",
								type: "number",
								min: "0",
								max: "365",
								step: "1"
							})
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "预加载站点",
					icon: "🌍",
					children: [(0, import_jsx_runtime.jsx)(SettingRow, {
						label: "预加载站点:",
						children: (0, import_jsx_runtime.jsxs)("div", {
							style: {
								display: "flex",
								gap: "16px",
								flexWrap: "wrap",
								alignItems: "center"
							},
							children: [(0, import_jsx_runtime.jsx)(SettingCheckbox, {
								id: "preload-enable-missAvBtn",
								label: "MissAv"
							}), (0, import_jsx_runtime.jsx)(SettingCheckbox, {
								id: "preload-enable-supJavBtn",
								label: "SupJav"
							})]
						})
					}), (0, import_jsx_runtime.jsx)("p", {
						style: {
							fontSize: "12px",
							color: "#6c757d",
							margin: "8px 0 0"
						},
						children: "SupJav 全站 Cloudflare 拦截严重,列表页预加载已跳过(仅详情页显示黄色搜索页链接),MissAv 为主要预加载源。"
					})]
				}),
				(0, import_jsx_runtime.jsxs)(SettingSection, {
					title: "缓存状态",
					icon: "📦",
					children: [(0, import_jsx_runtime.jsx)(SettingRow, {
						label: "预加载缓存状态:",
						children: (0, import_jsx_runtime.jsx)("div", {
							id: "preload-cache-stats",
							style: {
								fontSize: "13px",
								color: "#495057",
								lineHeight: "1.8"
							},
							children: "加载中..."
						})
					}), (0, import_jsx_runtime.jsx)("p", {
						style: {
							fontSize: "12px",
							color: "#6c757d",
							margin: "8px 0 0"
						},
						children: "此缓存即「缓存管理」面板的「第三方站点缓存」(jhs_other_site),清理请前往该面板。总开关「外部网站功能」位于快捷设置面板。"
					})]
				})
			]
		});
	}
	function DiagnosticsPanel({ panelName }) {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "diagnostics-panel",
			className: "content-panel",
			style: {
				display: panelName === "diagnostics-panel" ? "block" : "none",
				overflowY: "auto"
			},
			children: (0, import_jsx_runtime.jsxs)(SettingSection, {
				title: "插件执行状态",
				icon: "📊",
				children: [(0, import_jsx_runtime.jsx)("div", { id: "diagnostics-content" }), (0, import_jsx_runtime.jsxs)("div", {
					style: { marginTop: "12px" },
					children: [(0, import_jsx_runtime.jsx)("button", {
						id: "copy-diagnostics-btn",
						className: "jhs-setting-btn jhs-setting-btn--primary",
						type: "button",
						children: "📋 复制诊断报告"
					}), (0, import_jsx_runtime.jsx)("button", {
						id: "refresh-diagnostics-btn",
						className: "jhs-setting-btn",
						type: "button",
						style: { marginLeft: "8px" },
						children: "🔄 刷新诊断"
					})]
				})]
			})
		});
	}
	function SettingDialog({ panelName, cacheItemsHtml, qualityOptionsHtml, isJavdbSite }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				display: "flex",
				height: "100%"
			},
			children: [(0, import_jsx_runtime.jsxs)("div", {
				style: {
					width: "170px",
					flexShrink: 0,
					padding: "12px 0",
					background: "#fbfcfd",
					borderRight: "1px solid #e2e8f0",
					overflowY: "auto"
				},
				children: [
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "backup-panel" ? "active" : ""}`,
						"data-panel": "backup-panel",
						children: "💾 数据备份"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "base-panel" ? "active" : ""}`,
						"data-panel": "base-panel",
						children: "⚙️ 基础配置"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "filter-panel" ? "active" : ""}`,
						"data-panel": "filter-panel",
						children: "🚫 屏蔽配置"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "domain-panel" ? "active" : ""}`,
						"data-panel": "domain-panel",
						title: "第三方视频资源域名配置",
						children: "🌐 外部网站"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "cache-panel" ? "active" : ""}`,
						"data-panel": "cache-panel",
						children: "🧹 缓存管理"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "vlt-panel" ? "active" : ""}`,
						"data-panel": "vlt-panel",
						children: "📋 清单管理器"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "fav-sync-panel" ? "active" : ""}`,
						"data-panel": "fav-sync-panel",
						title: "JHS ↔ JavDB 双向增量同步",
						children: "🔄 收藏同步"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "missav-panel" ? "active" : ""}`,
						"data-panel": "missav-panel",
						children: "🎬 MissAV 同步"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "preload-panel" ? "active" : ""}`,
						"data-panel": "preload-panel",
						title: "视频流外部网站预加载配置",
						children: "⚡ 预加载配置"
					}),
					(0, import_jsx_runtime.jsx)("div", {
						className: `side-menu-item ${panelName === "diagnostics-panel" ? "active" : ""}`,
						"data-panel": "diagnostics-panel",
						title: "插件执行状态诊断",
						children: "📊 插件诊断"
					})
				]
			}), (0, import_jsx_runtime.jsxs)("div", {
				style: {
					flex: 1,
					display: "flex",
					flexDirection: "column",
					height: "100%"
				},
				children: [
					(0, import_jsx_runtime.jsxs)("div", {
						style: {
							flex: 1,
							margin: "0 10px",
							paddingBottom: "20px",
							overflow: "hidden"
						},
						children: [
							(0, import_jsx_runtime.jsx)(BackupPanel, { panelName }),
							(0, import_jsx_runtime.jsx)(BasePanel, {
								panelName,
								qualityOptionsHtml,
								isJavdbSite
							}),
							(0, import_jsx_runtime.jsx)(FilterPanel, { panelName }),
							(0, import_jsx_runtime.jsx)(DomainPanel, { panelName }),
							(0, import_jsx_runtime.jsx)(CachePanel, {
								panelName,
								cacheItemsHtml
							}),
							(0, import_jsx_runtime.jsx)(ListManagerPanel, { panelName }),
							(0, import_jsx_runtime.jsx)(FavSyncPanel, { panelName }),
							(0, import_jsx_runtime.jsx)(SyncPanel, {
								panelId: "missav-panel",
								panelName,
								title: "MissAV 状态标签同步",
								icon: "🎬",
								description: (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
									"在 MissAV 站点显示 JHS 鉴定状态标签(已收藏/已观看/已屏蔽/已下载)。",
									(0, import_jsx_runtime.jsx)("br", {}),
									"数据通过油猴 GM 存储跨域同步,无需后端服务器。",
									(0, import_jsx_runtime.jsx)("br", {}),
									(0, import_jsx_runtime.jsx)("strong", { children: "立即同步" }),
									":读取当前 car_list 并推送到 GM 存储。",
									(0, import_jsx_runtime.jsx)("br", {}),
									(0, import_jsx_runtime.jsx)("strong", { children: "导入" }),
									":从后端服务器导出的 JSON 导入历史数据。",
									(0, import_jsx_runtime.jsx)("br", {}),
									(0, import_jsx_runtime.jsx)("strong", { children: "导出" }),
									":将 MissAV 本地 IndexedDB 数据导出为 JSON(可走 WebDav 备份)。"
								] }),
								buttons: [
									{
										id: "missav-sync-btn",
										text: "立即同步",
										color: "primary"
									},
									{
										id: "missav-import-btn",
										text: "导入数据",
										color: "primary"
									},
									{
										id: "missav-export-btn",
										text: "导出数据",
										color: "success"
									}
								],
								statusId: "missav-status",
								fileInputId: "missav-file-input"
							}),
							(0, import_jsx_runtime.jsx)(PreloadPanel, { panelName })
						]
					}),
					(0, import_jsx_runtime.jsx)(DiagnosticsPanel, { panelName }),
					(0, import_jsx_runtime.jsxs)("div", {
						style: {
							flexShrink: 0,
							padding: "14px 20px",
							textAlign: "right",
							borderTop: "1px solid #e2e8f0",
							background: "#fbfcfd",
							display: "flex",
							justifyContent: "flex-end",
							gap: "10px",
							alignItems: "center"
						},
						children: [(0, import_jsx_runtime.jsx)("button", {
							id: "saveBtn",
							className: "jhs-setting-btn jhs-setting-btn--success",
							children: "💾 保存设置"
						}), (0, import_jsx_runtime.jsx)("button", {
							id: "clean-all",
							className: "jhs-setting-btn jhs-setting-btn--danger",
							style: { display: "none" },
							children: "♾️ 清理全部缓存"
						})]
					})
				]
			})]
		});
	}
	function BackToTopButton() {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "jhs-back-to-top",
			title: "回到顶部",
			children: (0, import_jsx_runtime.jsx)("svg", {
				viewBox: "0 0 24 24",
				children: (0, import_jsx_runtime.jsx)("path", { d: "M12 4l-8 8h6v8h4v-8h6z" })
			})
		});
	}
	function CacheItemHtml({ text, cacheKey, title, size, count }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "cache-item",
			"data-cache-key": cacheKey,
			style: {
				border: "1px solid #eee",
				borderRadius: "8px",
				padding: "12px"
			},
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					style: {
						fontWeight: "bold",
						marginBottom: "4px"
					},
					children: text
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						fontSize: "12px",
						color: "#999",
						marginBottom: "8px"
					},
					children: [
						(0, import_jsx_runtime.jsx)("span", {
							className: "cache-size",
							children: size
						}),
						" · ",
						(0, import_jsx_runtime.jsx)("span", {
							className: "cache-count",
							children: count
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						gap: "6px"
					},
					children: [
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-setting-menu-btn clean-btn",
							"data-key": cacheKey,
							style: {
								backgroundColor: "#448cc2",
								flex: 1,
								textAlign: "center",
								fontSize: "13px"
							},
							title,
							children: (0, import_jsx_runtime.jsx)("span", { children: "清理" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-setting-menu-btn view-btn",
							"data-key": cacheKey,
							style: {
								backgroundColor: "#b2bec0",
								flex: 1,
								textAlign: "center",
								fontSize: "13px"
							},
							children: (0, import_jsx_runtime.jsx)("span", { children: "查看" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							className: "jhs-setting-menu-btn export-btn",
							"data-key": cacheKey,
							style: {
								backgroundColor: "#85d0a3",
								flex: 1,
								textAlign: "center",
								fontSize: "13px"
							},
							title: "导出为 JSON 文件",
							children: (0, import_jsx_runtime.jsx)("span", { children: "导出" })
						})
					]
				})
			]
		});
	}
	function SettingMountBox({ variant }) {
		switch (variant) {
			case "navbar": return (0, import_jsx_runtime.jsxs)("div", {
				className: "navbar-item has-dropdown is-hoverable setting-box",
				style: { position: "relative" },
				children: [(0, import_jsx_runtime.jsx)("a", {
					id: "setting-btn",
					className: "navbar-link nav-btn",
					style: {
						color: "#ff8400 !important",
						paddingRight: "15px !important"
					},
					children: "设置"
				}), (0, import_jsx_runtime.jsx)("div", { className: "simple-setting" })]
			});
			case "mini": return (0, import_jsx_runtime.jsxs)("div", {
				className: "navbar-item mini-setting-box",
				style: {
					position: "relative",
					marginLeft: "auto"
				},
				children: [(0, import_jsx_runtime.jsx)("a", {
					id: "mini-setting-btn",
					className: "navbar-link nav-btn",
					style: {
						color: "#ff8400 !important",
						paddingLeft: "0 !important",
						paddingRight: "0 !important"
					},
					children: "设置"
				}), (0, import_jsx_runtime.jsx)("div", { className: "mini-simple-setting" })]
			});
		}
	}
	function Row({ label, tip, children, stacked }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: stacked ? "jhs-qs-row jhs-qs-row-stacked" : "jhs-qs-row",
			children: [(0, import_jsx_runtime.jsxs)("div", {
				className: "jhs-qs-label",
				children: [tip ? (0, import_jsx_runtime.jsx)("span", {
					className: "jhs-qs-tip",
					"data-tip": tip,
					children: "?"
				}) : null, (0, import_jsx_runtime.jsx)("span", { children: label })]
			}), (0, import_jsx_runtime.jsx)("div", {
				className: "jhs-qs-control",
				children
			})]
		});
	}
	function SimpleSettingPanel({ isJavdbSite }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "jhs-qs-panel",
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-qs-header",
					children: [(0, import_jsx_runtime.jsx)("span", {
						className: "jhs-qs-title",
						children: "快捷设置"
					}), (0, import_jsx_runtime.jsx)("span", {
						className: "jhs-qs-sub",
						children: "改动即时生效"
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-qs-body jhs-scrollbar",
					children: [
						(0, import_jsx_runtime.jsxs)("div", {
							className: "jhs-qs-section",
							children: [
								(0, import_jsx_runtime.jsx)("div", {
									className: "jhs-qs-section-title",
									children: "列表显示"
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "显示所有已鉴定",
									tip: "开启后忽略下方分项,强制显示全部已鉴定内容",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "showAllItem",
										className: "mini-switch"
									})
								}),
								(0, import_jsx_runtime.jsxs)("div", {
									className: "jhs-qs-subcard",
									id: "jhs-qs-filter-group",
									children: [
										(0, import_jsx_runtime.jsx)(Row, {
											label: "屏蔽单番号",
											children: (0, import_jsx_runtime.jsx)("input", {
												type: "checkbox",
												id: "showFilterItem",
												className: "mini-switch"
											})
										}),
										(0, import_jsx_runtime.jsx)(Row, {
											label: "屏蔽演员",
											children: (0, import_jsx_runtime.jsx)("input", {
												type: "checkbox",
												id: "showFilterActorItem",
												className: "mini-switch"
											})
										}),
										(0, import_jsx_runtime.jsx)(Row, {
											label: "屏蔽关键词",
											children: (0, import_jsx_runtime.jsx)("input", {
												type: "checkbox",
												id: "showFilterKeywordItem",
												className: "mini-switch"
											})
										}),
										(0, import_jsx_runtime.jsx)(Row, {
											label: "收藏",
											children: (0, import_jsx_runtime.jsx)("input", {
												type: "checkbox",
												id: "showFavoriteItem",
												className: "mini-switch"
											})
										}),
										(0, import_jsx_runtime.jsx)(Row, {
											label: "已观看",
											children: (0, import_jsx_runtime.jsx)("input", {
												type: "checkbox",
												id: "showHasWatchItem",
												className: "mini-switch"
											})
										})
									]
								})
							]
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							className: "jhs-qs-section",
							children: [
								(0, import_jsx_runtime.jsx)("div", {
									className: "jhs-qs-section-title",
									children: "浏览行为"
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "弹窗打开详情",
									tip: "点击封面:弹窗打开,或新窗口打开",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "dialogOpenDetail",
										className: "mini-switch"
									})
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "鉴定后关闭页面",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "needClosePage",
										className: "mini-switch"
									})
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "触底加载",
									tip: "瀑布流常开。自动=滑到底自动加载下一页;点按钮=滑到底后点「点击加载下一页」。右下角「加载全部」始终可用",
									children: (0, import_jsx_runtime.jsxs)("div", {
										id: "autoPageLoadMode",
										className: "jhs-qs-seg",
										role: "group",
										"aria-label": "触底加载方式",
										children: [(0, import_jsx_runtime.jsx)("button", {
											type: "button",
											className: "jhs-qs-seg-btn",
											"data-value": "auto",
											children: "自动"
										}), (0, import_jsx_runtime.jsx)("button", {
											type: "button",
											className: "jhs-qs-seg-btn",
											"data-value": "click",
											children: "点按钮"
										})]
									})
								})
							]
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							className: "jhs-qs-section",
							children: [
								(0, import_jsx_runtime.jsx)("div", {
									className: "jhs-qs-section-title",
									children: "功能"
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "标题翻译",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "translateTitle",
										className: "mini-switch"
									})
								}),
								isJavdbSite ? (0, import_jsx_runtime.jsx)(Row, {
									label: "加载女优信息",
									tip: "详情页展示年龄、三围等信息",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "enableLoadActressInfo",
										className: "mini-switch"
									})
								}) : null,
								(0, import_jsx_runtime.jsx)(Row, {
									label: "第三方视频资源",
									tip: "详情页 missAv / SupJav 等检测",
									children: (0, import_jsx_runtime.jsx)("input", {
										type: "checkbox",
										id: "enableLoadOtherSite",
										className: "mini-switch"
									})
								})
							]
						}),
						(0, import_jsx_runtime.jsxs)("div", {
							className: "jhs-qs-section",
							children: [
								(0, import_jsx_runtime.jsx)("div", {
									className: "jhs-qs-section-title",
									children: "页面布局"
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "列数",
									stacked: true,
									children: (0, import_jsx_runtime.jsxs)("div", {
										className: "jhs-qs-range",
										children: [(0, import_jsx_runtime.jsx)("input", {
											type: "range",
											id: "containerColumns",
											min: "2",
											max: "10",
											step: "1"
										}), (0, import_jsx_runtime.jsx)("span", {
											id: "showContainerColumns",
											className: "jhs-qs-range-val"
										})]
									})
								}),
								(0, import_jsx_runtime.jsx)(Row, {
									label: "宽度",
									stacked: true,
									children: (0, import_jsx_runtime.jsxs)("div", {
										className: "jhs-qs-range",
										children: [(0, import_jsx_runtime.jsx)("input", {
											type: "range",
											id: "containerWidth",
											min: "0",
											max: "30",
											step: "1"
										}), (0, import_jsx_runtime.jsx)("span", {
											id: "showContainerWidth",
											className: "jhs-qs-range-val"
										})]
									})
								})
							]
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-qs-footer",
					children: [(0, import_jsx_runtime.jsx)("button", {
						type: "button",
						id: "helpBtn",
						className: "jhs-qs-btn jhs-qs-btn-ghost",
						children: "常见问题"
					}), (0, import_jsx_runtime.jsx)("button", {
						type: "button",
						id: "moreBtn",
						className: "jhs-qs-btn jhs-qs-btn-primary",
						children: "更多设置"
					})]
				})
			]
		});
	}
	function VideoQualityOption({ quality, text }) {
		return (0, import_jsx_runtime.jsx)("option", {
			value: quality,
			children: text
		});
	}
	var setting_plugin_default = "/* stylelint-disable-next-line selector-type-case -- 运行时占位符(须保持大写),由 setting-plugin.tsx initCss() 的 replace 调用替换为动态容器宽度 CSS */\n__CSS_TEXT__\n\n.nav-btn::after {\n    content: none !important;\n}\n\n#cache-data-display pre {\n    font-family: Consolas, Monaco, 'Andale Mono', monospace;\n    white-space: pre-wrap;\n    overflow-wrap: break-word;\n    line-height: 1.5;\n    color: #333;\n    border: 1px solid #ddd;\n}\n\n.cache-item {\n    transition: box-shadow 0.2s ease, transform 0.2s ease;\n}\n\n.cache-item:hover {\n    box-shadow: 0 2px 8px rgb(0, 0, 0, 0.1);\n    transform: translateY(-2px);\n}\n\n.tooltip-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    line-height: 16px;\n    text-align: center;\n    border-radius: 50%;\n    background-color: #ccc;\n    color: white;\n    font-size: 12px;\n    margin-right: 5px;\n    cursor: help;\n}\n\n/* ===== 设置项布局(统一表单网格)===== */\n.setting-item {\n    display: grid;\n    grid-template-columns: 180px 1fr;\n    align-items: center;\n    gap: 12px;\n    padding: 8px 12px;\n    border-radius: 6px;\n    transition: background 0.15s ease;\n}\n\n.setting-item:hover {\n    background: rgb(93, 135, 194, 0.04);\n}\n\n.simple-setting .setting-item {\n    align-items: center;\n}\n\n.setting-label {\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    gap: 5px;\n    font-size: 13px;\n    font-weight: 600;\n    color: #475569;\n    text-align: right;\n    min-width: 0;\n}\n\n.form-content {\n    min-width: 0;\n}\n\n/* form-content 内开关/颜色选择器宽度显式锁定,避免被通用控件规则拉宽 */\n.form-content .jhs-toggle {\n    width: 40px !important;\n    min-width: 40px;\n    padding: 0 !important;\n}\n\n.form-content input[type='color'] {\n    width: 36px !important;\n    min-width: 36px;\n    padding: 2px !important;\n}\n\n/* 统一表单控件(text/number/password/url/select/textarea) */\n.setting-item input[type='text'],\n.setting-item input[type='number'],\n.setting-item input[type='password'],\n.setting-item input[type='url'],\n.setting-item input:not([type]),\n.setting-item select,\n.setting-item textarea {\n    width: 100%;\n    height: 36px;\n    padding: 6px 12px;\n    border: 1px solid #d1d5db;\n    border-radius: 6px;\n    font-size: 13px;\n    color: #1e293b;\n    background: #fff;\n    box-sizing: border-box;\n    transition:\n        border-color 0.15s,\n        box-shadow 0.15s;\n    outline: none;\n}\n\n.setting-item textarea {\n    height: auto;\n    min-height: 72px;\n    resize: vertical;\n}\n\n.setting-item input:focus,\n.setting-item select:focus,\n.setting-item textarea:focus {\n    border-color: #5d87c2;\n    box-shadow: 0 0 0 2px rgb(93, 135, 194, 0.15);\n}\n\n.setting-item input[readonly] {\n    background: #f8fafc;\n    color: #94a3b8;\n    cursor: default;\n    border-color: #e2e8f0;\n}\n\n/* 原生颜色选择器 */\n.setting-item input[type='color'] {\n    width: 36px;\n    height: 36px;\n    padding: 2px;\n    border: 1px solid #d1d5db;\n    border-radius: 6px;\n    cursor: pointer;\n    background: #fff;\n    box-sizing: border-box;\n}\n\n/* 行内问号提示图标 */\n.jhs-tooltip-icon {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 15px;\n    height: 15px;\n    flex-shrink: 0;\n    border-radius: 50%;\n    background: #cbd5e1;\n    color: #fff;\n    font-size: 10px;\n    font-weight: 700;\n    line-height: 1;\n    cursor: help;\n}\n\n/* iOS 风格开关(设置面板内) */\n.jhs-toggle {\n    position: relative;\n    width: 40px;\n    height: 22px;\n    appearance: none;\n    -webkit-appearance: none;\n    background: #d1d5db;\n    border-radius: 11px;\n    cursor: pointer;\n    transition: background 0.2s;\n    border: none;\n    outline: none;\n    flex-shrink: 0;\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n    accent-color: unset;\n}\n\n.jhs-toggle:checked {\n    background: #22c55e;\n}\n\n.jhs-toggle::before {\n    content: '';\n    position: absolute;\n    top: 2px;\n    left: 2px;\n    width: 18px;\n    height: 18px;\n    background: #fff;\n    border-radius: 50%;\n    transition: transform 0.2s;\n    box-shadow: 0 1px 3px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-toggle:checked::before {\n    transform: translateX(18px);\n}\n\n/* 面板分区(替代渐变 hr 分隔线) */\n.jhs-setting-section {\n    margin-top: 20px;\n    padding-top: 16px;\n    border-top: 1px solid #e5e7eb;\n}\n\n.jhs-setting-section:first-child {\n    margin-top: 0;\n    padding-top: 0;\n    border-top: none;\n}\n\n.jhs-setting-section-title {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 14px;\n    font-weight: 600;\n    color: #5d87c2;\n    margin-bottom: 12px;\n}\n\n/* 面板操作按钮(语义色修饰符) */\n.jhs-setting-btn {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    gap: 6px;\n    height: 34px;\n    padding: 6px 16px;\n    border: none;\n    border-radius: 6px;\n    font-size: 13px;\n    font-weight: 500;\n    cursor: pointer;\n    transition:\n        opacity 0.15s,\n        transform 0.1s;\n    color: #fff;\n    text-decoration: none;\n    white-space: nowrap;\n    box-sizing: border-box;\n}\n\n.jhs-setting-btn:hover {\n    opacity: 0.9;\n}\n\n.jhs-setting-btn:active {\n    transform: scale(0.97);\n}\n\n.jhs-setting-btn--primary {\n    background: #5d87c2;\n}\n\n.jhs-setting-btn--success {\n    background: #22c55e;\n}\n\n.jhs-setting-btn--danger {\n    background: #ef4444;\n}\n\n.jhs-setting-btn--pink {\n    background: #d25a88;\n}\n\n.jhs-setting-btn--mint {\n    background: #85d0a3;\n}\n\n/* 复选框 + 文字标签 */\n.jhs-checkbox-label {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    cursor: pointer;\n    font-size: 13px;\n    color: #475569;\n}\n\n.jhs-checkbox-label input[type='checkbox'] {\n    width: 16px;\n    height: 16px;\n    accent-color: #5d87c2;\n    cursor: pointer;\n    margin: 0;\n    flex-shrink: 0;\n}\n\n.keyword-label {\n    display: inline-flex;\n    align-items: center;\n    padding: 4px 8px;\n    border-radius: 4px;\n    font-size: 14px;\n    position: relative;\n    margin-left: 8px;\n    margin-bottom: 5px;\n}\n\n.keyword-remove {\n    margin-left: 6px;\n    cursor: pointer;\n    font-size: 12px;\n    line-height: 1;\n}\n\n.keyword-input {\n    padding: 6px 12px;\n    border: 1px solid #cbd5e1;\n    border-radius: 6px;\n    font-size: 14px;\n    float: right;\n}\n\n.add-tag-btn {\n    padding: 6px 12px;\n    background-color: #e2e8f0;\n    color: #334155;\n    border: none;\n    border-radius: 6px;\n    cursor: pointer;\n    font-size: 14px;\n    margin-left: 8px;\n    float: right;\n}\n\n.add-tag-btn:hover {\n    background-color: #cbd5e1;\n}\n\n.tag-box {\n    margin-top: 15px;\n}\n\n/* 屏蔽关键词容器:标签流式换行 + 输入行自然布局 */\n.filter-keyword-container {\n    padding: 8px 0;\n}\n\n.filter-keyword-container .tag-box {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    min-height: 32px;\n    margin-top: 0;\n}\n\n/* ===== 通用按钮(jhs-setting-menu-btn 统一样式)===== */\n.jhs-setting-menu-btn {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    padding: 7px 16px;\n    border-radius: 6px;\n    text-decoration: none;\n    font-size: 13px;\n    font-weight: 500;\n    transition: opacity 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;\n    cursor: pointer;\n    border: none;\n    white-space: nowrap;\n    color: #fff !important;\n}\n\n.jhs-setting-menu-btn span {\n    color: #fff !important;\n}\n\n.jhs-setting-menu-btn:hover {\n    opacity: 0.88;\n    transform: translateY(-1px);\n    box-shadow: 0 2px 8px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-setting-menu-btn:active {\n    transform: translateY(0);\n}\n\n/* ===== 底部操作按钮(jhs-setting-btn 统一按钮系统)===== */\n#saveBtn,\n#clean-all {\n    margin-top: 0;\n}\n\n/* ===== 悬浮快捷设置面板 ===== */\n.simple-setting,\n.mini-simple-setting {\n    display: none;\n    position: absolute;\n    top: calc(100% + 6px);\n    right: 0;\n    left: auto;\n    z-index: 10000;\n    width: 340px;\n    max-width: min(340px, 92vw);\n    padding: 0;\n    margin: 0;\n    color: #1e293b;\n    background: #fff;\n    border: 1px solid #e2e8f0;\n    border-radius: 12px;\n    box-shadow:\n        0 10px 40px rgb(15, 23, 42, 0.12),\n        0 2px 8px rgb(15, 23, 42, 0.06);\n    overflow: hidden;\n}\n\n/* 与触发按钮之间的隐形热区,减少移入时误关 */\n.simple-setting::before,\n.mini-simple-setting::before {\n    content: '';\n    position: absolute;\n    top: -10px;\n    left: 0;\n    right: 0;\n    height: 10px;\n}\n\n.mini-simple-setting {\n    right: 0;\n    left: auto;\n}\n\n/* 快捷设置内部布局 */\n.jhs-qs-panel {\n    display: flex;\n    flex-direction: column;\n    max-height: min(78vh, 560px);\n}\n\n.jhs-qs-header {\n    display: flex;\n    align-items: baseline;\n    justify-content: space-between;\n    gap: 8px;\n    padding: 14px 16px 10px;\n    border-bottom: 1px solid #f1f5f9;\n    background: linear-gradient(180deg, #f8fafc 0%, #fff 100%);\n}\n\n.jhs-qs-title {\n    font-size: 15px;\n    font-weight: 700;\n    color: #0f172a;\n    letter-spacing: 0.02em;\n}\n\n.jhs-qs-sub {\n    font-size: 11px;\n    color: #94a3b8;\n    font-weight: 500;\n}\n\n.jhs-qs-body {\n    flex: 1;\n    overflow-x: hidden;\n    overflow-y: auto;\n    padding: 8px 10px 12px;\n}\n\n.jhs-qs-section {\n    margin-bottom: 10px;\n    padding: 8px 8px 4px;\n    background: #f8fafc;\n    border: 1px solid #f1f5f9;\n    border-radius: 10px;\n}\n\n.jhs-qs-section:last-child {\n    margin-bottom: 0;\n}\n\n.jhs-qs-section-title {\n    font-size: 11px;\n    font-weight: 700;\n    color: #64748b;\n    text-transform: none;\n    letter-spacing: 0.04em;\n    padding: 2px 6px 8px;\n}\n\n.jhs-qs-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 12px;\n    min-height: 36px;\n    padding: 6px 8px;\n    margin-bottom: 2px;\n    border-radius: 8px;\n    transition: background 0.12s ease;\n}\n\n.jhs-qs-row:hover {\n    background: rgb(255, 255, 255, 0.9);\n}\n\n.jhs-qs-row-stacked {\n    flex-direction: column;\n    align-items: stretch;\n    gap: 6px;\n    min-height: 0;\n    padding-bottom: 10px;\n}\n\n.jhs-qs-label {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 13px;\n    font-weight: 600;\n    color: #334155;\n    line-height: 1.35;\n    min-width: 0;\n}\n\n.jhs-qs-tip {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 15px;\n    height: 15px;\n    flex-shrink: 0;\n    border-radius: 50%;\n    background: #cbd5e1;\n    color: #fff;\n    font-size: 10px;\n    font-weight: 700;\n    cursor: help;\n    line-height: 1;\n}\n\n.jhs-qs-control {\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    flex-shrink: 0;\n}\n\n.jhs-qs-row-stacked .jhs-qs-control {\n    justify-content: stretch;\n}\n\n.jhs-qs-subcard {\n    margin: 2px 0 6px;\n    padding: 4px;\n    background: #fff;\n    border: 1px solid #e2e8f0;\n    border-radius: 8px;\n}\n\n.jhs-qs-subcard.is-disabled {\n    opacity: 0.55;\n    pointer-events: none;\n}\n\n.jhs-qs-subcard .jhs-qs-row {\n    min-height: 32px;\n    padding: 4px 8px;\n}\n\n.jhs-qs-subcard .jhs-qs-label {\n    font-weight: 500;\n    font-size: 12.5px;\n    color: #475569;\n}\n\n/* 触底加载等二选一:分段按钮 */\n.jhs-qs-seg {\n    display: inline-flex;\n    align-items: stretch;\n    flex-shrink: 0;\n    padding: 2px;\n    background: #e2e8f0;\n    border-radius: 9px;\n    box-sizing: border-box;\n}\n\n.jhs-qs-seg-btn {\n    appearance: none;\n    border: none;\n    margin: 0;\n    padding: 6px 12px;\n    min-width: 64px;\n    border-radius: 7px;\n    font-size: 12px;\n    font-weight: 600;\n    line-height: 1.2;\n    color: #64748b;\n    background: transparent;\n    cursor: pointer;\n    transition:\n        background 0.15s ease,\n        color 0.15s ease,\n        box-shadow 0.15s ease;\n    white-space: nowrap;\n}\n\n.jhs-qs-seg-btn:hover:not(.is-active) {\n    color: #334155;\n    background: rgb(255, 255, 255, 0.55);\n}\n\n.jhs-qs-seg-btn.is-active {\n    color: #fff;\n    background: linear-gradient(135deg, #5d87c2, #4a90d9);\n    box-shadow: 0 1px 4px rgb(74, 144, 217, 0.35);\n}\n\n.jhs-qs-seg-btn:focus-visible {\n    outline: 2px solid #5d87c2;\n    outline-offset: 1px;\n}\n\n.jhs-qs-range {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    width: 100%;\n}\n\n.jhs-qs-range input[type='range'] {\n    flex: 1;\n    min-width: 0;\n    height: 6px;\n    padding: 0 !important;\n    accent-color: #5d87c2;\n    cursor: pointer;\n}\n\n.jhs-qs-range-val {\n    min-width: 36px;\n    text-align: right;\n    font-size: 12px;\n    font-weight: 700;\n    color: #5d87c2;\n    font-variant-numeric: tabular-nums;\n}\n\n.jhs-qs-footer {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 10px;\n    padding: 10px 14px 12px;\n    border-top: 1px solid #f1f5f9;\n    background: #fafbfc;\n}\n\n.jhs-qs-btn {\n    flex: 1;\n    padding: 8px 12px;\n    border: none;\n    border-radius: 8px;\n    font-size: 13px;\n    font-weight: 600;\n    cursor: pointer;\n    transition:\n        background 0.15s ease,\n        transform 0.12s ease,\n        box-shadow 0.15s ease;\n}\n\n.jhs-qs-btn:hover {\n    transform: translateY(-1px);\n}\n\n.jhs-qs-btn:active {\n    transform: translateY(0);\n}\n\n.jhs-qs-btn-ghost {\n    background: #fff;\n    color: #64748b;\n    border: 1px solid #e2e8f0;\n}\n\n.jhs-qs-btn-ghost:hover {\n    background: #f8fafc;\n    color: #475569;\n    border-color: #cbd5e1;\n}\n\n.jhs-qs-btn-primary {\n    background: linear-gradient(135deg, #5d87c2, #4a90d9);\n    color: #fff;\n    box-shadow: 0 2px 8px rgb(74, 144, 217, 0.28);\n}\n\n.jhs-qs-btn-primary:hover {\n    box-shadow: 0 4px 12px rgb(74, 144, 217, 0.35);\n}\n\n/* 快捷设置内开关:避免被 form-content 通配拉宽 */\n.jhs-qs-panel input.mini-switch[type='checkbox'] {\n    width: 40px !important;\n    min-width: 40px !important;\n    max-width: 40px !important;\n}\n\n/* ===== 开关(mini-switch)===== */\n\n/* 排除 form-content 通配与 content-panel checkbox 规则的干扰 */\ninput.mini-switch[type='checkbox'] {\n    appearance: none;\n    -webkit-appearance: none;\n    width: 40px;\n    height: 20px;\n    min-width: 40px;\n    max-width: 40px;\n    padding: 0;\n    margin: 0;\n    background: #e0e0e0;\n    border: none;\n    border-radius: 20px;\n    position: relative;\n    cursor: pointer;\n    outline: none;\n    box-sizing: border-box;\n    flex-shrink: 0;\n    vertical-align: middle;\n    accent-color: unset;\n}\n\ninput.mini-switch[type='checkbox']:checked {\n    background: #4caf50;\n}\n\ninput.mini-switch[type='checkbox']::before {\n    content: '';\n    position: absolute;\n    width: 16px;\n    height: 16px;\n    border-radius: 50%;\n    background: white;\n    top: 2px;\n    left: 2px;\n    box-shadow: 0 1px 3px rgb(0, 0, 0, 0.2);\n    transition: left 0.15s ease;\n}\n\ninput.mini-switch[type='checkbox']:checked::before {\n    left: calc(100% - 18px);\n}\n\n/* ===== 侧栏导航 ===== */\n.side-menu-item {\n    padding: 11px 16px;\n    cursor: pointer;\n    color: #64748b;\n    border-left: 3px solid transparent;\n    transition: color 0.2s, background-color 0.2s, border-color 0.2s;\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    font-size: 14px;\n    font-weight: 500;\n}\n\n.side-menu-item .icon {\n    height: 24px;\n    width: 24px;\n}\n\n.side-menu-item:hover {\n    background-color: rgb(93, 135, 194, 0.08);\n    color: #5d87c2;\n}\n\n.side-menu-item.active {\n    background-color: rgb(93, 135, 194, 0.12);\n    border-left: 3px solid #5d87c2;\n    color: #5d87c2;\n    font-weight: 600;\n}\n\n/* ===== 内容面板 ===== */\n.content-panel {\n    display: none;\n    margin-top: 16px;\n    padding: 0 12px 10px 4px;\n    height: 100%;\n    overflow-x: hidden;\n    overflow-y: auto;\n}\n\n.content-panel.active {\n    display: block;\n}\n\n/* 表单控件统一样式已收敛至「设置项布局」区块的 .setting-item 作用域规则 */\n\n/* ===== 复选框美化(不含开关)===== */\n.content-panel input[type='checkbox']:not(.mini-switch, .jhs-toggle) {\n    width: 16px;\n    height: 16px;\n    accent-color: #5d87c2;\n    cursor: pointer;\n}\n\ninput[type='checkbox']:disabled {\n    opacity: 0.6;\n    cursor: default !important;\n}\n\n";
	var back_to_top_button_default = "#jhs-back-to-top,\n#jdb-wf-back-to-top {\n    position: fixed;\n    bottom: 40px;\n    right: 40px;\n    width: 44px;\n    height: 44px;\n    background-color: rgb(30, 30, 30, 0.9);\n    backdrop-filter: blur(4px);\n    color: #e0e0e0;\n    border-radius: 50%;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    z-index: var(--jhs-z-page);\n    opacity: 0;\n    visibility: hidden;\n    transform: translateY(20px) scale(0.9);\n    transition:\n        opacity 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        visibility 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        background-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        box-shadow 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n        border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n    box-shadow: 0 4px 12px rgb(0, 0, 0, 0.3);\n    border: 1px solid rgb(255, 255, 255, 0.05);\n    user-select: none;\n}\n\n#jhs-back-to-top:hover,\n#jdb-wf-back-to-top:hover {\n    background-color: #ff8400;\n    color: #fff;\n    transform: translateY(0) scale(1);\n    box-shadow: 0 8px 20px rgb(255, 132, 0, 0.3);\n    border-color: #ff8400;\n}\n\n#jhs-back-to-top.show,\n#jdb-wf-back-to-top.show {\n    opacity: 1;\n    visibility: visible;\n    transform: translateY(0) scale(1);\n}\n\n#jhs-back-to-top svg,\n#jdb-wf-back-to-top svg {\n    width: 22px;\n    height: 22px;\n    fill: currentcolor;\n    stroke: currentcolor;\n    stroke-width: 0;\n}\n";
	var setting_image_mode_horizontal_default = ".cover {\n    min-height: auto !important;\n    padding-top: 67% !important;\n}\n\n.cover img {\n    object-fit: contain !important;\n    object-position: 50% 50% !important;\n}\n\n/* bus的 */\n.masonry .movie-box img {\n    min-height: auto !important;\n    object-fit: contain !important;\n    object-position: top;\n}\n";
	var SettingPlugin = class extends BasePlugin {
		folderName = "JHS-数据备份";
		simpleSettingHideTimer = null;
		cacheItems = [
			{
				key: "jhs_dmm_video",
				text: "🎥 预览视频缓存",
				title: "DMM 预告片画质→URL 映射(联动清理 jhs_other_site_dmm)"
			},
			{
				key: "jhs_other_site",
				text: "🌍 第三方站点缓存",
				title: "missav/supjav 等站点搜索结果(随 WebDav 备份)"
			},
			{
				key: "jhs_other_site_dmm",
				text: "🌍 DMM 预览伴生缓存",
				title: "DMM 预览相关第三方链接(随 WebDav 备份)"
			},
			{
				key: "jhs_translate",
				text: "🆎 标题翻译",
				title: "番号→中文标题翻译(Google Translate,随备份)"
			},
			{
				key: "jhs_actress_info",
				text: "👩 演员信息",
				title: "演员身高/体重/三围/罩杯/生日(随备份)"
			},
			{
				key: "jhs_score_info",
				text: "⭐ Top250|热播 评分数据",
				title: "影片评分 HTML 快照(随备份)"
			},
			{
				key: "jhs_screenShot",
				text: "🖼️ 截图墙缓存",
				title: "javstore 截图墙(随备份)"
			},
			{
				key: "jhs_visit_history",
				text: "🕐 访问记录",
				title: "页面访问时间记录(随备份)"
			},
			{
				key: "jdb:rating_cache_v2",
				text: "⭐ 列表评分缓存",
				title: "评分显示插件缓存(随备份)"
			}
		];
		getName() {
			return "SettingPlugin";
		}
		async initCss() {
			const settings = await storageManager.getSetting();
			const containerWidth = (settings == null ? void 0 : settings.containerWidth) ?? "70";
			const containerColumns = utils.isMobile() && window.innerWidth < 1e3 ? 1 : (settings == null ? void 0 : settings.containerColumns) ?? 4;
			this.applyImageMode().then();
			const cssText = `\n            section .container{\n                max-width: 1000px !important;\n                min-width: ${containerWidth}%;\n            }\n            .movie-list, .movie-list.v{\n                grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr));\n            }\n        `;
			return setting_plugin_default.replace("__CSS_TEXT__", cssText);
		}
		cancelSimpleSettingHide() {
			if (this.simpleSettingHideTimer != null) {
				clearTimeout(this.simpleSettingHideTimer);
				this.simpleSettingHideTimer = null;
			}
		}
		scheduleSimpleSettingHide(selector) {
			this.cancelSimpleSettingHide();
			this.simpleSettingHideTimer = setTimeout(() => {
				$(selector).html("").hide();
				this.simpleSettingHideTimer = null;
			}, 220);
		}
		openSimpleSetting(selector) {
			this.cancelSimpleSettingHide();
			$(selector).html(this.simpleSetting()).show();
			this.initSimpleSettingForm().then();
			clog.lowZIndex();
		}
		async handle() {
			if (await storageManager.getSetting("enableClog", "yes") === "yes") clog.show();
			if (isJavdbSite) {
				const toggleSettingBox = function() {
					if ($(".navbar-search").is(":hidden")) {
						$(".mini-setting-box").hide();
						$(".setting-box").show();
					} else {
						$(".mini-setting-box").show();
						$(".setting-box").hide();
					}
				};
				$("#navbar-menu-user .navbar-end").prepend(jsxToString((0, import_jsx_runtime.jsx)(SettingMountBox, { variant: "navbar" })));
				utils.loopDetector(() => $("#miniHistoryBtn").length > 0, () => {
					$(".miniHistoryBtnBox").before(jsxToString((0, import_jsx_runtime.jsx)(SettingMountBox, { variant: "mini" })));
					toggleSettingBox();
				});
				$(window).resize(toggleSettingBox);
			}
			$(".main-nav, .container-fluid").on("click", "#setting-btn, #mini-setting-btn", () => {
				clog.lowZIndex();
				this.openSettingDialog();
			});
			$(".main-nav, .container-fluid").on("mouseenter", ".setting-box", () => {
				this.openSimpleSetting(".simple-setting");
			}).on("mouseleave", ".setting-box", () => {
				this.scheduleSimpleSettingHide(".simple-setting");
			});
			$(".main-nav, .container-fluid").on("mouseenter", ".mini-setting-box", () => {
				this.openSimpleSetting(".mini-simple-setting");
			}).on("mouseleave", ".mini-setting-box", () => {
				this.scheduleSimpleSettingHide(".mini-simple-setting");
			});
			this.addBackToTopBtn();
		}
		addBackToTopBtn() {
			utils.insertStyle(back_to_top_button_default);
			const btn = $(jsxToString((0, import_jsx_runtime.jsx)(BackToTopButton, {})));
			$("body").append(btn);
			btn.on("click", () => {
				utils.smoothScrollToTop(500);
			});
			let ticking = false;
			$(window).on("scroll", () => {
				if (!ticking) {
					window.requestAnimationFrame(() => {
						if (($(window).scrollTop() ?? 0) > 300) btn.addClass("show");
						else btn.removeClass("show");
						ticking = false;
					});
					ticking = true;
				}
			});
		}
		async openSettingDialog(panelName = "backup-panel", callback) {
			const cacheItemsHtml = this.cacheItems.map((item) => {
				const stats = getCacheStats(item.key);
				return jsxToString((0, import_jsx_runtime.jsx)(CacheItemHtml, {
					text: item.text,
					cacheKey: item.key,
					title: item.title,
					size: formatSize(stats.size),
					count: stats.count > 0 ? `${stats.count} 条` : "空"
				}));
			}).join("");
			let qualityOptionsHtml = "";
			VIDEO_QUALITY_LIST.forEach((option) => {
				if (option.canSelect) qualityOptionsHtml += jsxToString((0, import_jsx_runtime.jsx)(VideoQualityOption, {
					quality: option.quality,
					text: option.text
				}));
			});
			const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(SettingDialog, {
				panelName,
				cacheItemsHtml,
				qualityOptionsHtml,
				isJavdbSite
			}));
			layer.open({
				type: 1,
				title: "设置",
				content: dialogHtml,
				area: utils.getResponsiveArea(["55%", "90%"]),
				scrollbar: false,
				success: (layerEl, layerIndex) => {
					$(layerEl).find(".layui-layer-content").css("position", "relative");
					this.loadForm();
					this.bindClick();
					utils.setupEscClose(layerIndex);
					if (callback) callback();
				}
			});
		}
		simpleSetting() {
			return jsxToString((0, import_jsx_runtime.jsx)(SimpleSettingPanel, { isJavdbSite }));
		}
		async applyImageMode() {
			$("#verticalImgStyle").remove();
			$(jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, {
				id: "verticalImgStyle",
				css: setting_image_mode_horizontal_default
			}))).appendTo("head");
		}
		refreshDiagnostics() {
			$("#diagnostics-content").html(renderDiagnosticsHtml(this.pluginManager));
		}
		addLabelTag(containerSelector, keyword, noun = "屏蔽词") {
			addLabelTag(containerSelector, keyword, noun);
		}
		addKeyword(containerSelector, noun = "屏蔽词") {
			addKeyword(containerSelector, noun);
		}
		async autoBackup() {
			const settings = await storageManager.getSetting();
			if (!shouldAutoBackup(settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG)) return;
			const channels = settings.backupChannels || {
				webdav: true,
				git: false
			};
			const webdavCreds = channels.webdav !== false ? getWebDavCredentialsFromGm() : null;
			const gitCreds = channels.git === true ? getGitCredentialsFromGm() : null;
			if (!webdavCreds && !gitCreds) return;
			const exportText = JSON.stringify(await this.buildBackupPayload(settings));
			const fileName = getAutoBackupFileName();
			let succeeded = false;
			if (webdavCreds) try {
				await new window.WebDavClient(webdavCreds.url, webdavCreds.username, webdavCreds.password).backup(this.folderName, fileName, exportText);
				succeeded = true;
				console.log("[JHS 自动备份] WebDav 通道完成");
			} catch (err) {
				console.warn("[JHS 自动备份] WebDav 通道失败:", err instanceof Error ? err.message : String(err));
			}
			if (gitCreds) try {
				await buildGitClient(gitCreds).backup(this.folderName, fileName, exportText);
				succeeded = true;
				console.log("[JHS 自动备份] Git 通道完成");
			} catch (err) {
				console.warn("[JHS 自动备份] Git 通道失败:", err instanceof Error ? err.message : String(err));
			}
			if (succeeded) markAutoBackupDone();
		}
		async loadForm() {
			return loadForm(this);
		}
		async saveForm() {
			return saveForm(this);
		}
		bindClick() {
			bindClick$1(this);
		}
		async initSimpleSettingForm() {
			return initSimpleSettingForm(this);
		}
		refreshCacheStats(cacheKey) {
			refreshCacheStats(this, cacheKey);
		}
		refreshPreloadCacheStats() {
			refreshPreloadCacheStats(this);
		}
		refreshAllCacheStats() {
			refreshAllCacheStats(this);
		}
		importData() {
			importData(this);
		}
		async exportData() {
			return exportData(this);
		}
		async buildBackupPayload(settings) {
			return buildBackupPayload(settings);
		}
		async backupDataByWebDav() {
			return backupDataByWebDav(this);
		}
		async backupListBtnByWebDav() {
			return backupListBtnByWebDav(this);
		}
		openFileListDialog(backupList, webdavClient, titlePrefix) {
			openFileListDialog(this, backupList, webdavClient, titlePrefix);
		}
	};
	function broadcastSend(key, payload, opts = {}) {
		const { eventName = key, eventDetail = payload, onError } = opts;
		const json = JSON.stringify(payload);
		try {
			GM_setValue(key, json);
		} catch (error) {
			onError?.("gm", error);
		}
		try {
			localStorage.setItem(key, json);
		} catch (error) {
			onError?.("localStorage", error);
		}
		try {
			document.dispatchEvent(new CustomEvent(eventName, { detail: eventDetail }));
		} catch (error) {
			onError?.("customEvent", error);
		}
	}
	function broadcastSubscribe(key, handler, opts = {}) {
		const eventName = opts.eventName ?? key;
		const raw = opts.raw === true;
		const onDelivery = opts.onDelivery;
		document.addEventListener(eventName, (e) => {
			onDelivery?.({ channel: "customEvent" });
			handler(e.detail, { channel: "customEvent" });
		});
		window.addEventListener("storage", (e) => {
			if (e.key !== key) return;
			const delivery = {
				channel: "storage",
				rawValue: e.newValue ?? void 0
			};
			onDelivery?.(delivery);
			if (!e.newValue) return;
			if (raw) {
				handler(e.newValue, delivery);
				return;
			}
			let payload;
			try {
				payload = JSON.parse(e.newValue);
			} catch {
				return;
			}
			handler(payload, delivery);
		});
		try {
			GM_addValueChangeListener(key, (_name, _oldValue, newValue, remote) => {
				const delivery = {
					channel: "gm",
					rawValue: newValue,
					remote
				};
				onDelivery?.(delivery);
				if (!newValue) return;
				if (raw) {
					handler(newValue, delivery);
					return;
				}
				let payload;
				try {
					payload = JSON.parse(newValue);
				} catch {
					return;
				}
				handler(payload, delivery);
			});
		} catch {}
	}
	var LOG_PREFIX$6 = "[JavDB]";
	var PENDING_SYNC_PREFIX = "jdb:vlt-pending-server-sync:";
	var AUTO_FAVORITE_KEYWORD = "等待更新";
	function getSeries$1(d) {
		if (!d) return d;
		const i = d.indexOf("-");
		return i > 0 ? d.slice(0, i) : d;
	}
	function getCode$1(d) {
		if (!d) return null;
		const i = d.indexOf("-");
		return i > 0 ? d.slice(i + 1) : null;
	}
	function getScore$1() {
		const spans = document.querySelectorAll("nav span");
		const found = Array.from(spans).find((s) => s.textContent?.trim().endsWith("人評價"));
		if (!found) return 0;
		const m = found.innerHTML.match(/(\d+\.\d+)/);
		return m ? parseFloat(m[1]) : 0;
	}
	var _cachedScore;
	function getScoreCached() {
		if (_cachedScore === void 0) _cachedScore = getScore$1();
		return _cachedScore;
	}
	function getMovieInfo(videoId) {
		const detail = document.querySelector(".video-detail");
		if (!detail) {
			console.warn(`${LOG_PREFIX$6} 未找到 .video-detail,跳过`);
			return null;
		}
		const desEl = detail.querySelector(".panel-block.first-block a[data-clipboard-text]");
		if (!desEl) {
			console.warn(`${LOG_PREFIX$6} 未找到番号元素,跳过`);
			return null;
		}
		const designation = desEl.dataset.clipboardText || "";
		if (!designation) {
			console.warn(`${LOG_PREFIX$6} 番号为空,跳过`);
			return null;
		}
		const href = "https://javdb.com/v/" + videoId;
		const titleEl = detail.querySelector("strong.current-title");
		const title = titleEl ? titleEl.innerHTML : "";
		const dateEl = detail.querySelector(".video-meta-panel nav > div:nth-child(2) > span");
		const release_date = dateEl ? dateEl.innerHTML : "";
		const coverEl = detail.querySelector(".column-video-cover img");
		return {
			designation,
			info: {
				href,
				title,
				release_date,
				cover_src: coverEl ? coverEl.src : "",
				score: getScoreCached(),
				series: getSeries$1(designation),
				code: getCode$1(designation) || ""
			}
		};
	}
	function extractListNameFromInput(input) {
		const label = input.closest("label");
		if (!label) return "";
		const clone = label.cloneNode(true);
		clone.querySelector("input")?.remove();
		(clone.querySelector(".jhs-list-item__count") || clone.querySelector("span"))?.remove();
		return (clone.textContent || "").replace(/\s+/g, " ").trim();
	}
	function getListName(listId) {
		const modalInputs = Array.from(document.querySelectorAll("#modal-save-list input[data-list-id]"));
		const panelInputs = Array.from(document.querySelectorAll(".jhs-list-panel input[data-list-id]"));
		const input = modalInputs.find((item) => item.dataset.listId === listId) || panelInputs.find((item) => item.dataset.listId === listId) || null;
		if (!input) return "";
		return extractListNameFromInput(input);
	}
	function getListInfo(listId) {
		return {
			list_id: listId,
			info: {
				url: "https://javdb.com/lists/" + listId + "?locale=zh",
				name: getListName(listId)
			}
		};
	}
	function normalizeListName(value) {
		return value.normalize("NFKC").replace(/\s+/g, " ").trim();
	}
	function extractListIdFromHref(href) {
		const m = href.match(/[?&]id=([^&]+)/);
		return m ? m[1] : null;
	}
	function getCsrfToken$1() {
		return document.querySelector("meta[name=\"csrf-token\"]")?.content || "";
	}
	function getCurrentListContainer() {
		return document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
	}
	function getCheckboxCount(input) {
		const label = input.closest("label");
		const match = ((label?.querySelector(".jhs-list-item__count") || label?.querySelector("span"))?.textContent || "").match(/\d+/);
		if (!match) return null;
		const count = Number(match[0]);
		return Number.isSafeInteger(count) ? count : null;
	}
	function matchingListCheckboxes(listId) {
		return Array.from(document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-list-id], .jhs-list-panel input[type=\"checkbox\"][data-list-id]")).filter((input) => input.dataset.listId === listId);
	}
	function setListCheckboxState(listId, checked) {
		for (const input of matchingListCheckboxes(listId)) input.checked = checked;
	}
	function setListCheckboxBusy(listId, busy) {
		const panel = document.querySelector(".jhs-list-panel");
		const activeElement = document.activeElement;
		if (busy && panel && activeElement instanceof HTMLInputElement && panel.contains(activeElement) && activeElement.dataset.listId === listId) panel.__jhsPendingFocusListId = listId;
		for (const input of matchingListCheckboxes(listId)) if (busy) {
			if (input.dataset.vltWasDisabled === void 0) input.dataset.vltWasDisabled = input.disabled ? "1" : "0";
			input.disabled = true;
			input.setAttribute("aria-busy", "true");
		} else {
			if (input.dataset.vltWasDisabled === void 0) continue;
			input.disabled = input.dataset.vltWasDisabled === "1";
			delete input.dataset.vltWasDisabled;
			input.removeAttribute("aria-busy");
		}
		if (!busy && panel?.__jhsPendingFocusListId === listId) {
			delete panel.__jhsPendingFocusListId;
			requestAnimationFrame(() => {
				const currentActive = document.activeElement;
				if (!(!currentActive || currentActive === document.body || currentActive instanceof HTMLInputElement && currentActive.dataset.listId === listId)) return;
				Array.from(panel.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId && !input.disabled)?.focus({ preventScroll: true });
			});
		}
	}
	function setListDisplayedCount(listId, count) {
		for (const input of matchingListCheckboxes(listId)) {
			const label = input.closest("label");
			const span = label?.querySelector(".jhs-list-item__count") || label?.querySelector("span");
			if (!span) continue;
			if (span.classList.contains("jhs-list-item__count")) {
				span.textContent = String(count);
				span.setAttribute("title", `清单内 ${count} 部影片`);
				span.setAttribute("aria-label", `${count} 部影片`);
			} else span.textContent = `(${count})`;
		}
	}
	function refreshListPanel() {
		const panel = document.querySelector(".jhs-list-panel");
		if (!panel) return;
		if (panel.__jhsRefresh) {
			panel.__jhsRefresh();
			return;
		}
		const lc = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
		if (!lc) return;
		const items = panel.querySelector(".jhs-list-panel__items") || panel;
		items.innerHTML = "";
		Array.from(lc.children).forEach((child) => {
			if (/[预預][设設]清[单單]/.test(child.textContent)) return;
			items.appendChild(child.cloneNode(true));
		});
	}
	var LOG_PREFIX$5 = "[JavDB]";
	var LAST_SYNC_KEY$1 = "jdb:last-sync";
	var LIST_MGMT_KEY$1 = "jdb:list-mgmt";
	function broadcastSyncComplete(designation, action, association) {
		broadcastSend(LAST_SYNC_KEY$1, {
			designation,
			action,
			association,
			time: Date.now()
		}, {
			eventName: "jdb:sync-complete",
			onError: (channel, error) => {
				console.warn(`${LOG_PREFIX$5} ${channel === "gm" ? "GM 同步广播失败" : channel === "localStorage" ? "localStorage 同步广播失败" : "页面同步广播失败"}`, error);
			}
		});
	}
	function broadcastListMgmt(type, listId, extra) {
		broadcastSend(LIST_MGMT_KEY$1, {
			type,
			listId,
			newName: extra?.newName,
			time: Date.now()
		}, { onError: () => {} });
	}
	var queue = new KeyedQueue();
	var LOCK_PREFIX = "javdb-power-tools:vlt:";
	function enqueueAssociationTask(key, task) {
		return queue.enqueue(key, () => withExclusiveLock(LOCK_PREFIX + key, task));
	}
	var SIMPLE_LISTS_PAGE_TIMEOUT_MS = 1e4;
	async function fetchSimpleListsPage(url, vid) {
		const controller = new AbortController();
		const timeout = window.setTimeout(() => controller.abort(), SIMPLE_LISTS_PAGE_TIMEOUT_MS);
		try {
			const response = await fetch(url, {
				credentials: "same-origin",
				cache: "no-store",
				headers: { Accept: "application/json" },
				signal: controller.signal
			});
			if (!response.ok) throw new Error(`清单分页请求失败:HTTP ${response.status}`);
			const payload = await response.json();
			if (typeof payload.lists !== "string") throw new Error("清单分页响应格式异常");
			const pageHtml = typeof payload.page === "string" ? payload.page : "";
			const nextHref = new DOMParser().parseFromString(pageHtml, "text/html").querySelector("a[rel=\"next\"]")?.getAttribute("href");
			if (!nextHref) return {
				entries: payload.lists,
				nextUrl: null
			};
			const nextUrl = new URL(nextHref, url);
			if (nextUrl.origin !== window.location.origin || nextUrl.pathname !== "/users/simple_lists") throw new Error("清单下一页地址异常");
			if (vid) nextUrl.searchParams.set("vid", vid);
			return {
				entries: payload.lists,
				nextUrl: nextUrl.href
			};
		} finally {
			window.clearTimeout(timeout);
		}
	}
	async function createPendingJournal(entry) {
		try {
			const randomPart = typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Math.random().toString(36).slice(2)}-${Date.now()}`;
			const key = `${PENDING_SYNC_PREFIX}${entry.createdAt}:${randomPart}`;
			await writePersistentValueAndWait("vltPending", key.slice(PENDING_SYNC_PREFIX.length), key, entry);
			return key;
		} catch (error) {
			clog.error(`${LOG_PREFIX$6} 无法写入清单同步恢复日志`, error);
			return null;
		}
	}
	function removePendingJournal(key) {
		if (!key) return;
		removePersistentValue("vltPending", key.startsWith("jdb:vlt-pending-server-sync:") ? key.slice(PENDING_SYNC_PREFIX.length) : key, key);
	}
	function readPendingJournals() {
		const entries = [];
		const values = readPersistentPrefix("vltPending", PENDING_SYNC_PREFIX);
		for (const [entryKey, entry] of Object.entries(values)) {
			const key = PENDING_SYNC_PREFIX + entryKey;
			if (entry.version !== 1 || !entry.videoId || !entry.movieInfo?.designation || !entry.listInfo?.list_id || typeof entry.desiredChecked !== "boolean" || typeof entry.createdAt !== "number") {
				removePersistentValue("vltPending", entryKey, key);
				continue;
			}
			entries.push({
				key,
				entry
			});
		}
		return entries.sort((left, right) => left.entry.createdAt - right.entry.createdAt);
	}
	async function postJavdbListMutation(videoId, listId, checked) {
		const csrfToken = getCsrfToken$1();
		if (!csrfToken) return {
			kind: "rejected",
			message: "无法获取 CSRF token"
		};
		try {
			const response = await fetch(new URL("/users/save_video_to_list", window.location.origin).href, {
				method: "POST",
				body: JSON.stringify({
					video_id: videoId,
					checked,
					list_id: listId
				}),
				headers: {
					"Content-Type": "application/json",
					"X-CSRF-Token": csrfToken
				},
				credentials: "same-origin"
			});
			const data = await response.json().catch(() => null);
			const message = typeof data?.message === "string" ? data.message : `HTTP ${response.status}`;
			if (response.ok && data?.success === true) return {
				kind: "confirmed",
				message
			};
			if (response.ok && data?.success === false) return {
				kind: "rejected",
				message
			};
			return {
				kind: "unknown",
				message
			};
		} catch (error) {
			return {
				kind: "unknown",
				message: error instanceof Error ? error.message : String(error)
			};
		}
	}
	async function fetchAuthoritativeCheckboxState(videoId, listId) {
		try {
			let url = new URL("/users/simple_lists", window.location.origin);
			url.searchParams.set("vid", videoId);
			const visited = new Set();
			for (let page = 0; url && page < 50; page++) {
				if (visited.has(url.href)) return null;
				visited.add(url.href);
				const result = await fetchSimpleListsPage(url.href, videoId);
				const documentNode = new DOMParser().parseFromString(result.entries, "text/html");
				const input = Array.from(documentNode.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((item) => item.dataset.listId === listId);
				if (input) return {
					checked: input.checked,
					count: getCheckboxCount(input),
					name: extractListNameFromInput(input) || null
				};
				if (!result.nextUrl) return {
					checked: false,
					count: null,
					name: null
				};
				url = new URL(result.nextUrl);
			}
			return null;
		} catch (error) {
			clog.error(`${LOG_PREFIX$6} 查询 JavDB 权威清单状态失败`, error);
			return null;
		}
	}
	var LOG_PREFIX$4 = "[JavDB][清单对账]";
	var MAX_PAGES$2 = 100;
	var autoReconcileDelay = 800;
	var autoServerSyncEnabled = true;
	function setAutoReconcileDelay(ms) {
		if (Number.isFinite(ms) && ms > 0) autoReconcileDelay = ms;
	}
	function setAutoServerSyncEnabled(enabled) {
		autoServerSyncEnabled = enabled;
	}
	var reconcileQueues = new Map();
	var automaticReconciliationInstalled = false;
	var automaticReconciliationInstalling = false;
	var automaticReconciliationTimer = null;
	var automaticReconciliationRunning = false;
	function normalizeText(value) {
		return (value || "").replace(/\s+/g, " ").trim();
	}
	function getSeries(designation) {
		const index = designation.indexOf("-");
		return index > 0 ? designation.slice(0, index) : designation || null;
	}
	function getCode(designation) {
		const index = designation.indexOf("-");
		return index > 0 ? designation.slice(index + 1) : null;
	}
	function getDisplayedListCount(input) {
		const label = input.closest("label");
		const match = (label?.querySelector("span")?.textContent || label?.textContent || "").match(/\((\d+)\)/);
		if (!match) return null;
		const count = Number(match[1]);
		return Number.isSafeInteger(count) ? count : null;
	}
	function getDisplayedListName(input) {
		return normalizeText(input.closest("label")?.textContent).replace(/\s*\(\d+\).*$/, "").trim();
	}
	async function fetchHtmlDocument(url) {
		const html = await fetchJavdbPageHtml(url);
		return new DOMParser().parseFromString(html, "text/html");
	}
	function parseExpectedCountAndName(documentNode) {
		const headings = Array.from(documentNode.querySelectorAll("h1, h2, h3, .title"));
		for (const heading of headings) {
			const match = normalizeText(heading.textContent).match(/^(.*?)\s+(\d+)\s*部影片/);
			if (!match) continue;
			return {
				name: match[1].trim(),
				expectedCount: Number(match[2])
			};
		}
		throw new Error("无法从清单标题读取服务端影片总数");
	}
	function parseMovie(item, pageUrl) {
		const link = item.querySelector("a[href*=\"/v/\"]");
		const designation = normalizeText(item.querySelector(".video-title strong")?.textContent);
		const hrefValue = link?.getAttribute("href") || "";
		if (!designation || !hrefValue) throw new Error("清单分页中存在无法识别的影片卡片");
		const titleText = normalizeText(item.querySelector(".video-title")?.textContent);
		const title = titleText.startsWith(designation) ? titleText.slice(designation.length).trim() : titleText;
		const image = item.querySelector("img");
		const coverValue = image?.getAttribute("src") || image?.getAttribute("data-src") || image?.currentSrc || "";
		const scoreMatch = normalizeText(item.querySelector(".score")?.textContent).match(/(\d+(?:\.\d+)?)/);
		const releaseDate = normalizeText(item.querySelector(".meta")?.textContent);
		return {
			designation,
			href: new URL(hrefValue, pageUrl).href,
			title: title || null,
			coverSrc: coverValue ? new URL(coverValue, pageUrl).href : null,
			score: scoreMatch ? Number(scoreMatch[1]) : null,
			releaseDate: releaseDate || null,
			createdAt: null,
			series: getSeries(designation),
			code: getCode(designation)
		};
	}
	function parseListPage(documentNode, pageUrl, listId, firstPage) {
		const heading = firstPage ? parseExpectedCountAndName(documentNode) : null;
		const movies = Array.from(documentNode.querySelectorAll(".movie-list .item")).map((item) => parseMovie(item, pageUrl));
		const nextHref = documentNode.querySelector("a.pagination-next")?.getAttribute("href");
		let nextUrl = null;
		if (nextHref) {
			const candidate = new URL(nextHref, pageUrl);
			const expectedPath = `/lists/${encodeURIComponent(listId)}`;
			if (candidate.origin !== window.location.origin || candidate.pathname !== expectedPath) throw new Error(`清单下一页地址异常:${candidate.href}`);
			nextUrl = candidate.href;
		}
		return {
			expectedCount: heading?.expectedCount ?? null,
			name: heading?.name ?? null,
			movies,
			nextUrl
		};
	}
	async function fetchCompleteListPass(listId) {
		const firstUrl = new URL(`/lists/${encodeURIComponent(listId)}`, window.location.origin);
		firstUrl.searchParams.set("locale", "zh");
		const visited = new Set();
		const movies = [];
		let expectedCount = null;
		let name = null;
		let nextUrl = firstUrl.href;
		while (nextUrl) {
			if (visited.size >= MAX_PAGES$2) throw new Error(`清单分页超过 ${MAX_PAGES$2} 页,已中止`);
			const normalizedUrl = new URL(nextUrl).href;
			if (visited.has(normalizedUrl)) throw new Error("清单分页出现循环,已中止");
			visited.add(normalizedUrl);
			const parsed = parseListPage(await fetchHtmlDocument(normalizedUrl), normalizedUrl, listId, visited.size === 1);
			if (visited.size === 1) {
				expectedCount = parsed.expectedCount;
				name = parsed.name;
			}
			movies.push(...parsed.movies);
			nextUrl = parsed.nextUrl;
		}
		if (expectedCount === null || name === null) throw new Error("清单首页信息不完整");
		const uniqueDesignations = new Set(movies.map((movie) => movie.designation));
		if (movies.length !== expectedCount || uniqueDesignations.size !== expectedCount) throw new Error(`清单分页不完整或含重复番号:服务端 ${expectedCount},抓取 ${movies.length},唯一 ${uniqueDesignations.size}`);
		return {
			listId,
			name,
			url: firstUrl.href,
			expectedCount,
			movies
		};
	}
	async function fetchCompleteListSnapshot(listId) {
		const first = await fetchCompleteListPass(listId);
		await new Promise((resolve) => setTimeout(resolve, 250));
		const second = await fetchCompleteListPass(listId);
		const firstSequence = first.movies.map((movie) => movie.designation).join("");
		const secondSequence = second.movies.map((movie) => movie.designation).join("");
		if (first.expectedCount !== second.expectedCount || first.name !== second.name || firstSequence !== secondSequence) throw new Error("清单在两次完整抓取之间发生变化,已放弃本次快照");
		return second;
	}
	function snapshotMatchesExpectation(snapshot, expectation) {
		if (expectation.expectedCount !== void 0 && snapshot.expectedCount !== expectation.expectedCount) return false;
		if (expectation.designation !== void 0 && expectation.checked !== void 0) return snapshot.movies.some((movie) => movie.designation === expectation.designation) === expectation.checked;
		return true;
	}
	async function performReconciliation(listId, expectation) {
		try {
			for (let attempt = 0; attempt < 2; attempt++) {
				const state = await VltDb.getListReconcileState(listId);
				const snapshot = await fetchCompleteListSnapshot(listId);
				if (!snapshotMatchesExpectation(snapshot, expectation)) throw new Error("服务端清单状态已变化,与触发对账时的状态不一致");
				const result = await VltDb.reconcileListSnapshot(snapshot, state.guard);
				if (result.status === "conflict") {
					console.warn(`${LOG_PREFIX$4} 本地数据在抓取期间变化,准备重试 listId=${listId}`);
					continue;
				}
				console.log(`${LOG_PREFIX$4} 完成 listId=${listId} count=${result.count} added=${result.added} removed=${result.removed}`);
				if (!expectation.quiet && (result.added > 0 || result.removed > 0)) showToast$1(`✓ 已按 JavDB 校准「${snapshot.name}」:新增 ${result.added},移除 ${result.removed}`, "success");
				return true;
			}
			throw new Error("本地清单持续变化,两次对账均发生冲突");
		} catch (error) {
			clog.error(`${LOG_PREFIX$4} 失败 listId=${listId}`, error);
			if (!expectation.quiet) showToast$1(`⚠ 清单自动校准失败:${error instanceof Error ? error.message : String(error)}`, "warning");
			return false;
		}
	}
	function reconcileListWithJavdb(listId, expectation = {}) {
		const current = (reconcileQueues.get(listId) || Promise.resolve(true)).catch(() => false).then(() => performReconciliation(listId, expectation));
		reconcileQueues.set(listId, current);
		current.finally(() => {
			if (reconcileQueues.get(listId) === current) reconcileQueues.delete(listId);
		});
		return current;
	}
	async function reconcileListBeforeMutation(input, designation, checkedBeforeMutation) {
		const listId = input.dataset.listId || "";
		const serverCount = getDisplayedListCount(input);
		if (!listId || serverCount === null) return;
		const state = await VltDb.getListReconcileState(listId, designation);
		if (!state.inventory) return;
		if (state.actualCount === serverCount && state.hasDesignation === checkedBeforeMutation) return;
		console.warn(`${LOG_PREFIX$4} 检测到差异 listId=${listId} local=${state.actualCount} server=${serverCount} localChecked=${state.hasDesignation} serverChecked=${checkedBeforeMutation}`);
		await reconcileListWithJavdb(listId, {
			expectedCount: serverCount,
			designation,
			checked: checkedBeforeMutation,
			quiet: true
		});
	}
	function reconcileAfterConfirmedMutation(listId, designation, checked) {
		return reconcileListWithJavdb(listId, {
			designation,
			checked,
			quiet: false
		});
	}
	async function pushLocalOnlyAssociation(listId, designation) {
		const videoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
		if (!videoId) return;
		const movieInfo = getMovieInfo(videoId);
		const listInfo = getListInfo(listId);
		if (!movieInfo || !listInfo.info.name) return;
		await enqueueAssociationTask(`${designation}::${listId}`, async () => {
			try {
				const result = await postJavdbListMutation(videoId, listId, true);
				if (result.kind === "confirmed") {
					const state = await VltDb.getListReconcileState(listId);
					if (state.inventory && state.actualCount !== null) setListDisplayedCount(listId, state.actualCount);
					console.log(`${LOG_PREFIX$4} 已补推本地独有关联:${designation} → ${listInfo.info.name} (listId=${listId})`);
					showToast$1(`✓ [${designation}] 已补推至「${listInfo.info.name}」(服务器未同步)`, "success");
				} else showToast$1(`⚠ [${designation}] 补推至「${listInfo.info.name}」未确认:${result.message}`, "warning");
			} catch (error) {
				const message = error instanceof Error ? error.message : String(error);
				console.error(`${LOG_PREFIX$4} 补推本地独有关联失败:${designation} → ${listId}`, error);
				showToast$1(`⚠ [${designation}] 补推至「${listInfo.info.name}」失败:${message}`, "warning");
			}
		});
	}
	async function runAutomaticReconciliation(designation) {
		if (automaticReconciliationRunning) return;
		automaticReconciliationRunning = true;
		try {
			const inputs = Array.from(document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-list-id][data-action=\"change->list#listCheckboxChanged\"]"));
			for (const input of inputs) {
				const listId = input.dataset.listId || "";
				const serverCount = getDisplayedListCount(input);
				if (!listId || serverCount === null) continue;
				const state = await VltDb.getListReconcileState(listId, designation);
				if (!state.inventory) continue;
				if (state.hasDesignation === input.checked) {
					if (state.actualCount === serverCount) continue;
					console.warn(`${LOG_PREFIX$4} 页面加载发现差异「${getDisplayedListName(input)}」 local=${state.actualCount} server=${serverCount}`);
					await reconcileListWithJavdb(listId, {
						expectedCount: serverCount,
						designation,
						checked: input.checked,
						quiet: true
					});
					continue;
				}
				if (state.hasDesignation && !input.checked) {
					if (autoServerSyncEnabled) {
						console.warn(`${LOG_PREFIX$4} 页面加载发现本地独有关联「${getDisplayedListName(input)}」 ${designation} local=关联 server=未勾选,补推 JavDB`);
						await pushLocalOnlyAssociation(listId, designation);
					} else console.warn(`${LOG_PREFIX$4} 自动补推已关闭,跳过本地独有关联「${getDisplayedListName(input)}」 ${designation}`);
					continue;
				}
				console.warn(`${LOG_PREFIX$4} 页面加载发现差异「${getDisplayedListName(input)}」 local=${state.actualCount} server=${serverCount}`);
				await reconcileListWithJavdb(listId, {
					expectedCount: serverCount,
					designation,
					checked: input.checked,
					quiet: true
				});
			}
		} finally {
			automaticReconciliationRunning = false;
		}
	}
	function setupAutomaticListReconciliation(designation) {
		if (automaticReconciliationInstalled || automaticReconciliationInstalling) return;
		automaticReconciliationInstalling = true;
		const schedule = () => {
			if (automaticReconciliationTimer) clearTimeout(automaticReconciliationTimer);
			automaticReconciliationTimer = setTimeout(() => {
				automaticReconciliationTimer = null;
				runAutomaticReconciliation(designation).then();
			}, autoReconcileDelay);
		};
		const attach = () => {
			const modal = document.querySelector("#modal-save-list");
			if (!modal) {
				setTimeout(attach, 250);
				return;
			}
			automaticReconciliationInstalling = false;
			automaticReconciliationInstalled = true;
			new MutationObserver(schedule).observe(modal, {
				childList: true,
				subtree: true
			});
			schedule();
		};
		attach();
	}
	async function commitAuthoritativeState(entry, authoritative, notify) {
		if (authoritative.checked !== entry.desiredChecked) {
			if ((await postJavdbListMutation(entry.videoId, entry.listInfo.list_id, entry.desiredChecked)).kind !== "confirmed") {
				console.warn(`${LOG_PREFIX$6} 权威状态与期望相反且重推未确认,保留恢复日志:${entry.movieInfo.designation} ${entry.listInfo.list_id}`);
				return false;
			}
			const recheck = await fetchAuthoritativeCheckboxState(entry.videoId, entry.listInfo.list_id);
			if (!recheck || recheck.checked !== entry.desiredChecked) {
				console.warn(`${LOG_PREFIX$6} 重推后仍无法确认期望状态,保留恢复日志:${entry.movieInfo.designation} ${entry.listInfo.list_id}`);
				return false;
			}
			authoritative = recheck;
		}
		const listInfo = {
			...entry.listInfo,
			info: {
				...entry.listInfo.info,
				name: authoritative.name || entry.listInfo.info.name
			}
		};
		let reconciledBeforeCommit = false;
		if (authoritative.count !== null) {
			const before = await VltDb.getListReconcileState(entry.listInfo.list_id, entry.movieInfo.designation);
			if (before.actualCount + (authoritative.checked && before.hasDesignation === false ? 1 : !authoritative.checked && before.hasDesignation === true ? -1 : 0) !== authoritative.count) {
				reconciledBeforeCommit = await reconcileListWithJavdb(entry.listInfo.list_id, {
					expectedCount: authoritative.count,
					designation: entry.movieInfo.designation,
					checked: authoritative.checked,
					quiet: !notify
				});
				if (!reconciledBeforeCommit) return false;
			}
		}
		if (!await handleCheckboxChange(entry.movieInfo, listInfo, authoritative.checked, {
			serverConfirmed: true,
			silent: !notify || reconciledBeforeCommit,
			skipAutomation: !notify
		})) return false;
		setListCheckboxState(entry.listInfo.list_id, authoritative.checked);
		if (authoritative.count !== null) {
			setListDisplayedCount(entry.listInfo.list_id, authoritative.count);
			const state = await VltDb.getListReconcileState(entry.listInfo.list_id, entry.movieInfo.designation);
			if (state.inventory && (state.actualCount !== authoritative.count || state.hasDesignation !== authoritative.checked)) return reconcileListWithJavdb(entry.listInfo.list_id, {
				expectedCount: authoritative.count,
				designation: entry.movieInfo.designation,
				checked: authoritative.checked,
				quiet: !notify
			});
		}
		return true;
	}
	async function recoverPendingServerSyncs() {
		for (const { key, entry } of readPendingJournals()) await enqueueAssociationTask(`${entry.movieInfo.designation}::${entry.listInfo.list_id}`, async () => {
			const authoritative = await fetchAuthoritativeCheckboxState(entry.videoId, entry.listInfo.list_id);
			if (!authoritative) return;
			if (await commitAuthoritativeState(entry, authoritative, false)) {
				removePendingJournal(key);
				console.log(`${LOG_PREFIX$6} 已恢复未完成清单同步:${entry.movieInfo.designation} ${authoritative.checked ? "add" : "remove"} ${entry.listInfo.list_id}`);
			}
		});
	}
	var checkboxListenerInstalled = false;
	async function performServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation) {
		const listId = listInfo.list_id;
		let confirmedChecked = null;
		setListCheckboxState(listId, checked);
		setListCheckboxBusy(listId, true);
		try {
			if (input) await reconcileListBeforeMutation(input, movieInfo.designation, checkedBeforeMutation);
			const pending = {
				version: 1,
				videoId: input?.value || movieInfo.info.href.match(/\/v\/([^/?#]+)/)?.[1] || "",
				desiredChecked: checked,
				movieInfo,
				listInfo,
				createdAt: Date.now()
			};
			if (!pending.videoId) throw new Error("无法识别 JavDB 影片 ID");
			const journalKey = await createPendingJournal(pending);
			if (!journalKey) throw new Error("无法建立清单同步恢复日志,本次操作已取消");
			const serverResult = await postJavdbListMutation(pending.videoId, listId, checked);
			if (serverResult.kind === "rejected") {
				removePendingJournal(journalKey);
				setListCheckboxState(listId, checkedBeforeMutation);
				showToast$1(`✗ JavDB 清单更新失败:${serverResult.message}`, "error");
				return;
			}
			if (serverResult.kind === "unknown") {
				const observed = await fetchAuthoritativeCheckboxState(pending.videoId, listId);
				setListCheckboxState(listId, observed?.checked ?? checkedBeforeMutation);
				if (observed?.count !== null && observed?.count !== void 0) setListDisplayedCount(listId, observed.count);
				showToast$1(`⚠ 无法确认 JavDB 最终状态(${serverResult.message}),本地未改动;下次打开页面会自动核对`, "warning");
				return;
			}
			confirmedChecked = checked;
			const authoritative = await fetchAuthoritativeCheckboxState(pending.videoId, listId);
			if (authoritative) {
				confirmedChecked = authoritative.checked;
				if (await commitAuthoritativeState(pending, authoritative, true)) removePendingJournal(journalKey);
				else showToast$1("⚠ JavDB 已更新,本地完整清单将在下次打开页面时继续校准", "warning");
				return;
			}
			setListCheckboxState(listId, checked);
			if (await reconcileAfterConfirmedMutation(listId, movieInfo.designation, checked) ? await handleCheckboxChange(movieInfo, listInfo, checked, { serverConfirmed: true }) : null) removePendingJournal(journalKey);
			else showToast$1("⚠ JavDB 已更新,但暂时无法复核最终清单;恢复日志已保留", "warning");
		} catch (error) {
			setListCheckboxState(listId, confirmedChecked === null ? checkedBeforeMutation : confirmedChecked);
			const message = error instanceof Error ? error.message : String(error);
			console.error(`${LOG_PREFIX$6} JavDB 清单同步失败`, error);
			showToast$1(confirmedChecked === null ? `✗ JavDB 清单同步失败:${message}` : `⚠ JavDB 已更新,本地镜像将在下次打开页面时继续恢复:${message}`, confirmedChecked === null ? "error" : "warning");
		} finally {
			setListCheckboxBusy(listId, false);
		}
	}
	function enqueueServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation) {
		return enqueueAssociationTask(`${movieInfo.designation}::${listInfo.list_id}`, () => performServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation));
	}
	function setupCheckboxListener() {
		if (checkboxListenerInstalled) return;
		checkboxListenerInstalled = true;
		document.addEventListener("change", (event) => {
			const input = event.target;
			if (input.tagName !== "INPUT" || input.type !== "checkbox" || input.dataset.action !== "change->list#listCheckboxChanged" || !input.closest("#modal-save-list")) return;
			event.preventDefault();
			event.stopImmediatePropagation();
			const checked = input.checked;
			const checkedBeforeMutation = !checked;
			const movieInfo = getMovieInfo(input.value);
			const listInfo = getListInfo(input.dataset.listId || "");
			if (!movieInfo || !listInfo.list_id || !listInfo.info.name) {
				setListCheckboxState(listInfo.list_id, checkedBeforeMutation);
				showToast$1("✗ 无法读取影片或清单信息,本次操作已取消", "error");
				return;
			}
			enqueueServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation).then();
		}, true);
		const currentVideoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
		const currentMovie = currentVideoId ? getMovieInfo(currentVideoId) : null;
		if (currentMovie) setupAutomaticListReconciliation(currentMovie.designation);
		recoverPendingServerSyncs().then();
	}
	var autoFavCleanupEnabled = true;
	var protectedKeywords = ["值得复盘"];
	function setAutoFavCleanupEnabled(enabled) {
		autoFavCleanupEnabled = enabled;
	}
	function setProtectedKeywords(keywords) {
		if (keywords.length > 0) protectedKeywords = keywords;
	}
	function isProtectedList(name) {
		return protectedKeywords.some((kw) => kw !== "" && name.includes(kw));
	}
	var ASSOC_TOAST = {
		created: (des, lname, created) => {
			return {
				msg: `✓ [${des}] 已添加至「${lname}」${created.length > 0 ? `(已登记${created.join("和")})` : ""}`,
				type: "success"
			};
		},
		existed: (des, lname) => ({
			msg: `✓ [${des}] 已在「${lname}」中,数据一致`,
			type: "success"
		}),
		limit_exceeded: (des, lname) => ({
			msg: `✗ [${des}]「${lname}」已达收藏上限(501 条)`,
			type: "error"
		}),
		deleted: (des, lname) => ({
			msg: `✓ [${des}] 已从「${lname}」移除`,
			type: "success"
		}),
		unchanged: (des, lname) => ({
			msg: `✓ [${des}] 未关联「${lname}」,数据一致`,
			type: "success"
		})
	};
	async function syncMoviesLists(movieInfo, listInfo, action, serverConfirmed = false) {
		console.log(`${LOG_PREFIX$6} 同步(IDB): ${movieInfo.designation} → ${listInfo.info.name} (${action})`);
		const result = await VltDb.sync(movieInfo.designation, listInfo.list_id, {
			href: movieInfo.info.href,
			title: movieInfo.info.title,
			cover_src: movieInfo.info.cover_src,
			score: movieInfo.info.score,
			release_date: movieInfo.info.release_date,
			series: movieInfo.info.series,
			code: movieInfo.info.code
		}, {
			url: listInfo.info.url,
			name: listInfo.info.name
		}, action, { serverConfirmed });
		console.log(`${LOG_PREFIX$6} 同步结果: movie=${result.movie} list=${result.list} association=${result.association}`);
		return result;
	}
	async function autoFavoriteIfPendingUpdate(movieInfo, lname) {
		if (!autoFavCleanupEnabled) return;
		if (isProtectedList(lname)) return;
		const des = movieInfo.designation;
		try {
			const carRecord = await storageManager.getCar(des);
			if (carRecord && carRecord.status === "favorite") return;
			if (carRecord && carRecord.status) {
				showToast$1(`ℹ️ [${des}] 已标记为「${carRecord.status}」,跳过自动收藏`, "info");
				return;
			}
		} catch (err) {
			clog.error(`${LOG_PREFIX$6} 自动收藏前读取状态失败: ${des}`, err);
			return;
		}
		const favBtn = document.querySelector(".jhs-fav-btn");
		if (!favBtn) {
			console.warn(`${LOG_PREFIX$6} 未找到收藏按钮,跳过自动收藏: ${des}`);
			return;
		}
		favBtn.click();
		console.log(`${LOG_PREFIX$6} 自动收藏: ${des}(触发清单:${lname})`);
	}
	async function handleCheckboxChange(movieInfo, listInfo, checked, options = {}) {
		const des = movieInfo.designation;
		const lname = listInfo.info.name;
		const action = checked ? "add" : "remove";
		console.log(`${LOG_PREFIX$6} ═══ ${checked ? "勾选" : "取消"} [${des}] → ${lname} ═══`);
		let result;
		try {
			result = await syncMoviesLists(movieInfo, listInfo, action, options.serverConfirmed === true);
		} catch (err) {
			const message = err instanceof Error ? err.message : "请检查 IndexedDB";
			clog.error(`${LOG_PREFIX$6} 同步失败`, err);
			if (!options.silent) showToast$1(`✗ [${des}] 同步失败:${message}`, "error");
			return null;
		}
		const created = [];
		if (result.movie === "created") created.push("影片");
		if (result.list === "created") created.push("清单");
		if (!options.silent) {
			const entry = ASSOC_TOAST[result.association];
			if (entry) {
				const { msg, type } = entry(des, lname, created);
				showToast$1(msg, type);
			} else showToast$1(`✗ [${des}] 未知响应:${result.association}`, "error");
		}
		if (checked && !options.skipAutomation && result.association !== "limit_exceeded" && autoFavCleanupEnabled) {
			autoFavoriteIfPendingUpdate(movieInfo, lname).then();
			if (!lname.includes("等待更新")) uncheckPendingUpdateListCheckboxes();
		}
		console.log(`${LOG_PREFIX$6} ═══ 完成 ═══`);
		broadcastSyncComplete(des, action, result.association);
		return result;
	}
	async function removeFromNonProtectedListsOnWatch() {
		if (!autoFavCleanupEnabled) return;
		try {
			if (uncheckNonProtectedListCheckboxes() > 0) return;
			const pending = await findCurrentMovieListsMatching((name) => !isProtectedList(name));
			if (pending.length === 0) return;
			for (let i = 0; i < 15; i++) {
				await new Promise((r) => setTimeout(r, 200));
				if (uncheckNonProtectedListCheckboxes() > 0) return;
			}
			for (const item of pending) {
				const movieInfo = getMovieInfo(item.videoId);
				if (!movieInfo) continue;
				await enqueueServerCheckboxMutation(movieInfo, {
					list_id: item.listId,
					info: {
						url: item.url,
						name: item.name
					}
				}, false, null, true);
			}
		} catch (err) {
			clog.error(`${LOG_PREFIX$6} 评分后移出非受保护清单失败`, err);
		}
	}
	function uncheckListsMatching(predicate) {
		const inputs = document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-action=\"change->list#listCheckboxChanged\"]");
		const seen = new Set();
		let count = 0;
		for (const input of inputs) {
			if (!input.checked) continue;
			const listId = input.dataset.listId || "";
			if (!listId || seen.has(listId)) continue;
			const name = getListName(listId);
			if (!predicate(name)) continue;
			seen.add(listId);
			count++;
			input.checked = false;
			input.dispatchEvent(new Event("change", { bubbles: true }));
			const panelCb = document.querySelector(`.jhs-list-panel input[data-list-id="${listId}"]`);
			if (panelCb) panelCb.checked = false;
			console.log(`${LOG_PREFIX$6} 取消勾选「${name}」(listId=${listId})`);
		}
		return count;
	}
	function uncheckPendingUpdateListCheckboxes() {
		return uncheckListsMatching((name) => name.includes(AUTO_FAVORITE_KEYWORD));
	}
	function uncheckNonProtectedListCheckboxes() {
		return uncheckListsMatching((name) => !isProtectedList(name));
	}
	async function findCurrentMovieListsMatching(predicate) {
		const videoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
		if (!videoId) return [];
		const movieInfo = getMovieInfo(videoId);
		if (!movieInfo) return [];
		const lists = (await VltDb.queryMoviesLists([movieInfo.designation]))[movieInfo.designation] || [];
		const result = [];
		for (const item of lists) {
			if (!predicate(item.name)) continue;
			const listId = item.url?.match(/\/lists\/([^/?#]+)/)?.[1];
			if (!listId) continue;
			result.push({
				videoId,
				listId,
				name: item.name,
				url: item.url || `https://javdb.com/lists/${listId}?locale=zh`
			});
		}
		return result;
	}
	async function createList(listName) {
		const modal = document.querySelector("#modal-save-list");
		if (!modal) {
			showToast$1("✗ 未找到存入清单弹窗,请重新进入详情页", "error");
			return "failed";
		}
		const nameInput = modal.querySelector("input[data-list-target=\"listNewNameInput\"]");
		const form = modal.querySelector("#new_list");
		const listContainer = modal.querySelector("[data-list-target=\"listContainer\"]");
		if (!nameInput || !form || !listContainer) {
			showToast$1("✗ 清单新建表单未就绪,请重新进入详情页", "error");
			return "failed";
		}
		const formData = {};
		Array.from(form.querySelectorAll("input, textarea, select")).forEach((el) => {
			const field = el;
			if (field.name && field.type !== "submit" && field.type !== "button" && field.type !== "reset") formData[field.name] = field.value;
		});
		if (nameInput.name) formData[nameInput.name] = listName;
		const csrfToken = document.querySelector("meta[name=\"csrf-token\"]")?.content || formData["authenticity_token"] || "";
		const beforeIds = new Set(Array.from(listContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).map((el) => el.dataset.listId));
		const videoId = formData["video_id"] || form.querySelector("input[name=\"video_id\"]")?.value || "";
		if (!videoId) {
			showToast$1("✗ 无法识别当前影片,清单未新建", "error");
			return "failed";
		}
		showToast$1("正在新建清单…", "info");
		console.log(`${LOG_PREFIX$6} ═══ 新增清单「${listName}」(video_id=${videoId}) ═══`);
		let responseText;
		try {
			responseText = await new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method: "POST",
					url: "https://javdb.com/lists/remote_create",
					headers: {
						"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
						Accept: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*;q=0.01",
						"X-CSRF-Token": csrfToken,
						"X-Requested-With": "XMLHttpRequest"
					},
					data: Object.entries(formData).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&"),
					timeout: 15e3,
					onload: (r) => {
						if (r.status >= 200 && r.status < 300) resolve(r.responseText || "");
						else reject(new Error(`HTTP ${r.status}: ${r.responseText?.slice(0, 200) || ""}`));
					},
					onerror: () => reject(new Error("网络错误")),
					ontimeout: () => reject(new Error("请求超时"))
				});
			});
		} catch (err) {
			const message = err instanceof Error ? err.message : "请稍后重试";
			console.error(`${LOG_PREFIX$6} 新增清单失败`, err);
			showToast$1(`✗ 新建清单失败:${message}`, "error");
			return "failed";
		}
		console.log(`${LOG_PREFIX$6} 服务端响应(前 500 字): ${responseText.slice(0, 500)}`);
		if (responseText.trim()) try {
			const script = document.createElement("script");
			script.textContent = responseText;
			(document.body || document.documentElement).appendChild(script);
			script.remove();
			console.log(`${LOG_PREFIX$6} JS 响应已注入执行`);
		} catch (e) {
			console.warn(`${LOG_PREFIX$6} JS 响应注入失败`, e);
		}
		if (/\btoastr\s*(?:\.\s*error|\[\s*['"]error['"]\s*\])\s*\(/i.test(responseText)) {
			showToast$1("✗ JavDB 拒绝新建清单,请检查名称后重试", "error");
			return "failed";
		}
		let newCheckboxes = await pollForNewCheckboxes(modal, beforeIds, 200);
		if (newCheckboxes.length > 0) {
			console.log(`${LOG_PREFIX$6} 侦测到 ${newCheckboxes.length} 个新 checkbox,走正常完成流程`);
			const confirmedCheckbox = await resolveCreatedListCheckbox(newCheckboxes, listName, videoId, beforeIds);
			if (confirmedCheckbox) {
				finishCreateList(confirmedCheckbox, listName);
				return "created";
			}
		}
		const listIdMatch = responseText.match(/data-list-id=["']([^"']+)["']/);
		if (listIdMatch) {
			const listId = listIdMatch[1];
			const confirmedCheckbox = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
			if (confirmedCheckbox) {
				console.log(`${LOG_PREFIX$6} 从响应提取并权威确认 list-id=${listId}`);
				finishCreateList(confirmedCheckbox, listName);
				return "created";
			}
		}
		console.log(`${LOG_PREFIX$6} 响应无 list-id,通过切换 #save-list-button 重载清单列表`);
		const saveListBtn = document.querySelector("#save-list-button");
		if (saveListBtn) {
			saveListBtn.click();
			await new Promise((r) => setTimeout(r, 200));
			saveListBtn.click();
			newCheckboxes = await pollForNewCheckboxes(modal, beforeIds, 5e3);
			if (newCheckboxes.length > 0) {
				console.log(`${LOG_PREFIX$6} 重载后侦测到 ${newCheckboxes.length} 个新 checkbox,走正常完成流程`);
				const confirmedCheckbox = await resolveCreatedListCheckbox(newCheckboxes, listName, videoId, beforeIds);
				if (confirmedCheckbox) {
					finishCreateList(confirmedCheckbox, listName);
					return "created";
				}
			}
		}
		console.log(`${LOG_PREFIX$6} 按钮重载未找到新 checkbox,尝试 /users/lists 作为最后手段`);
		const listId = await fetchListIdByName(listName);
		if (listId) {
			const confirmedCheckbox = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
			if (confirmedCheckbox) {
				console.log(`${LOG_PREFIX$6} 从 /users/lists 查得并权威确认 list_id=${listId}`);
				finishCreateList(confirmedCheckbox, listName);
				return "created";
			}
		}
		console.error(`${LOG_PREFIX$6} 新增清单后无法定位新 checkbox。响应前 300 字: ${responseText.slice(0, 300)}`);
		refreshListPanel();
		showToast$1(`⚠ 清单「${listName}」可能已新建,请刷新页面确认`, "warning");
		return "unknown";
	}
	function pollForNewCheckboxes(modal, beforeIds, timeoutMs) {
		return new Promise((resolve) => {
			const start = Date.now();
			const check = () => {
				const lc = modal.querySelector("[data-list-target=\"listContainer\"]");
				if (lc) {
					const newOnes = Array.from(lc.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).filter((cb) => !beforeIds.has(cb.dataset.listId));
					if (newOnes.length > 0) {
						resolve(newOnes);
						return;
					}
				}
				if (Date.now() - start < timeoutMs) setTimeout(check, 100);
				else resolve([]);
			};
			check();
		});
	}
	function findCurrentListCheckbox(listId) {
		const container = getCurrentListContainer();
		if (!container) return null;
		return Array.from(container.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId) || null;
	}
	async function resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds) {
		if (!listId || !videoId || beforeIds.has(listId)) return null;
		const authoritative = await fetchAuthoritativeCheckboxState(videoId, listId);
		if (!authoritative?.checked || !authoritative.name || normalizeListName(authoritative.name) !== normalizeListName(listName) || authoritative.count !== null && authoritative.count < 1) return null;
		let input = findCurrentListCheckbox(listId);
		if (!input) {
			const container = getCurrentListContainer();
			input = container ? manuallyBuildCheckbox(container, listId, authoritative.name, videoId, authoritative.count ?? 1) : null;
		}
		if (!input) return null;
		input.checked = true;
		if (authoritative.count !== null) setListDisplayedCount(listId, authoritative.count);
		return input;
	}
	async function resolveCreatedListCheckbox(candidates, listName, videoId, beforeIds) {
		const candidateIds = Array.from(new Set(candidates.map((input) => input.dataset.listId).filter(Boolean)));
		const confirmed = [];
		for (const listId of candidateIds) {
			const input = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
			if (input) confirmed.push(input);
		}
		if (confirmed.length === 1) return confirmed[0];
		if (confirmed.length > 1) console.warn(`${LOG_PREFIX$6} 新建清单出现多个权威候选,拒绝写入本地`, candidateIds);
		return null;
	}
	async function fetchListIdByName(listName) {
		try {
			const html = await new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method: "GET",
					url: "https://javdb.com/users/lists",
					timeout: 1e4,
					onload: (r) => {
						if (r.status >= 200 && r.status < 300) resolve(r.responseText || "");
						else reject(new Error(`HTTP ${r.status}`));
					},
					onerror: () => reject(new Error("网络错误")),
					ontimeout: () => reject(new Error("请求超时"))
				});
			});
			const doc = new DOMParser().parseFromString(html, "text/html");
			let links = doc.querySelectorAll("a[href*=\"/lists/\"]");
			if (links.length === 0) links = doc.querySelectorAll("[href*=\"/lists/\"]");
			console.log(`${LOG_PREFIX$6} /users/lists 页面解析到 ${links.length} 条 /lists/ 链接`);
			const matchedIds = new Set();
			const expectedName = normalizeListName(listName);
			for (const link of Array.from(links)) {
				const m = (link.getAttribute("href") || "").match(/\/lists\/([^/?#]+)/);
				if (!m) continue;
				const id = m[1];
				if (id === "users" || id === "" || id === "new") continue;
				if (normalizeListName((link.textContent || "").replace(/\s*\(\d+\)\s*$/, "")) === expectedName) matchedIds.add(id);
			}
			if (matchedIds.size === 1) {
				const [id] = matchedIds;
				console.log(`${LOG_PREFIX$6} /users/lists 精确匹配清单「${listName}」→ list_id=${id}`);
				return id;
			}
			if (matchedIds.size > 1) {
				console.warn(`${LOG_PREFIX$6} /users/lists 存在重名清单,拒绝猜测 list-id`, matchedIds);
				return null;
			}
			const bodyText = doc.body?.textContent?.slice(0, 1e3) || "";
			console.warn(`${LOG_PREFIX$6} /users/lists 未精确找到「${listName}」(共 ${links.length} 条链接)。页面文本前 500 字: ${bodyText.slice(0, 500)}`);
			return null;
		} catch (err) {
			console.error(`${LOG_PREFIX$6} 获取 /users/lists 失败`, err);
			return null;
		}
	}
	function manuallyBuildCheckbox(listContainer, listId, listName, videoId, count) {
		const duplicate = Array.from(listContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId);
		if (duplicate) {
			duplicate.checked = true;
			return duplicate;
		}
		const existingCb = listContainer.querySelector("input[type=\"checkbox\"][data-list-id]");
		if (!existingCb) return null;
		const existingLabel = existingCb.closest("label");
		if (!existingLabel) return null;
		const clone = (existingLabel.closest("p.control") || existingLabel).cloneNode(true);
		const label = clone.matches("label") ? clone : clone.querySelector("label");
		const cb = label?.querySelector("input[type=\"checkbox\"]");
		if (!label || !cb) return null;
		cb.dataset.listId = listId;
		cb.value = videoId;
		cb.checked = true;
		cb.disabled = false;
		cb.removeAttribute("id");
		cb.removeAttribute("disabled");
		delete cb.dataset.vltWasDisabled;
		cb.removeAttribute("aria-busy");
		label.removeAttribute("for");
		for (const node of Array.from(label.childNodes)) if (node.nodeType === Node.TEXT_NODE) node.remove();
		const countSpan = label.querySelector("span");
		const nameNode = document.createTextNode(` ${listName} `);
		if (cb.parentNode === label) label.insertBefore(nameNode, cb.nextSibling);
		else label.appendChild(nameNode);
		if (countSpan) countSpan.textContent = `(${count})`;
		else {
			const createdCount = document.createElement("span");
			createdCount.textContent = `(${count})`;
			label.appendChild(createdCount);
		}
		listContainer.appendChild(clone);
		return cb;
	}
	function finishCreateList(checkbox, listName) {
		const modal = document.querySelector("#modal-save-list");
		if (modal) try {
			modal.querySelector("a[data-action=\"list#cancelNewList\"]")?.click();
		} catch {}
		restoreCreateListUi();
		showToast$1(`✓ 清单「${listName}」已新建,并已关联当前影片`, "success");
		refreshListPanel();
		const movieInfo = getMovieInfo(checkbox.value);
		if (!movieInfo) {
			console.warn(`${LOG_PREFIX$6} 新建清单后无法取得影片资讯,跳过同步`, checkbox);
			return;
		}
		const listInfo = getListInfo(checkbox.dataset.listId || "");
		if (!listInfo.info.name) {
			console.warn(`${LOG_PREFIX$6} 新建清单后无法从 DOM 取得清单名称,使用传入名称「${listName}」`);
			listInfo.info.name = listName;
		}
		handleCheckboxChange(movieInfo, listInfo, true, { serverConfirmed: true }).then();
	}
	function restoreCreateListUi() {
		const w = document.querySelector(".jhs-list-create-wrap");
		if (!w) return;
		const activeElement = document.activeElement;
		const shouldRestoreFocus = !activeElement || activeElement === document.body || w.contains(activeElement);
		const btn = w.querySelector(".jhs-list-create-btn");
		const f = w.querySelector(".jhs-list-create-form");
		const inp = w.querySelector(".jhs-list-create-input");
		if (btn) {
			btn.style.display = "inline-flex";
			btn.setAttribute("aria-expanded", "false");
		}
		if (f) f.style.display = "none";
		if (inp) inp.value = "";
		if (btn && shouldRestoreFocus) btn.focus({ preventScroll: true });
	}
	function VltCreateListForm() {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("button", {
			type: "button",
			className: "button is-info is-small jhs-list-create-btn",
			"aria-expanded": "false",
			"aria-controls": "jhs-list-create-form",
			children: "+ 新建清单"
		}), (0, import_jsx_runtime.jsxs)("span", {
			id: "jhs-list-create-form",
			className: "jhs-list-create-form",
			style: { display: "none" },
			children: [
				(0, import_jsx_runtime.jsx)("label", {
					className: "jhs-visually-hidden",
					htmlFor: "jhs-list-create-input",
					children: "清单名称"
				}),
				(0, import_jsx_runtime.jsx)("input", {
					id: "jhs-list-create-input",
					type: "text",
					className: "input is-small jhs-list-create-input",
					placeholder: "清单名称",
					maxLength: 50,
					autoComplete: "off"
				}),
				(0, import_jsx_runtime.jsx)("button", {
					type: "button",
					className: "button is-info is-small jhs-list-create-save",
					children: "新建"
				}),
				(0, import_jsx_runtime.jsx)("button", {
					type: "button",
					className: "button is-light is-small jhs-list-create-cancel",
					children: "取消"
				})
			]
		})] });
	}
	var _createListUiInjected = false;
	var _createListInFlight = false;
	function setupCreateListButton() {
		if (_createListUiInjected) return;
		const panel = document.querySelector(".jhs-list-panel");
		if (!panel) {
			setTimeout(setupCreateListButton, 400);
			return;
		}
		if (document.querySelector(".jhs-list-create-wrap")) {
			_createListUiInjected = true;
			return;
		}
		const wrap = document.createElement("div");
		wrap.className = "jhs-list-create-wrap";
		wrap.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(VltCreateListForm, {}));
		panel.insertAdjacentElement("afterend", wrap);
		bindCreateListEvents(wrap);
		bindCreateListAvailability(panel, wrap);
		_createListUiInjected = true;
	}
	function bindCreateListAvailability(panel, wrap) {
		const items = panel.querySelector(".jhs-list-panel__items");
		const btn = wrap.querySelector(".jhs-list-create-btn");
		if (!items || !btn) return;
		const sync = () => {
			const ready = isCreateListPanelReady();
			btn.disabled = !ready;
			btn.title = ready ? "新建清单" : "清单载入后可新建";
		};
		const observer = new MutationObserver(sync);
		observer.observe(items, {
			attributes: true,
			attributeFilter: ["aria-busy"]
		});
		observer.observe(panel, {
			attributes: true,
			attributeFilter: ["data-list-coverage"]
		});
		sync();
	}
	function isCreateListPanelReady() {
		const panel = document.querySelector(".jhs-list-panel");
		const items = document.querySelector(".jhs-list-panel__items");
		return panel?.dataset.listCoverage === "complete" && items?.getAttribute("aria-busy") === "false" && Boolean(getCurrentListContainer()?.querySelector("input[data-list-id]"));
	}
	function bindCreateListEvents(wrap) {
		const btn = wrap.querySelector(".jhs-list-create-btn");
		const form = wrap.querySelector(".jhs-list-create-form");
		const input = wrap.querySelector(".jhs-list-create-input");
		const saveBtn = wrap.querySelector(".jhs-list-create-save");
		const cancelBtn = wrap.querySelector(".jhs-list-create-cancel");
		btn.addEventListener("click", () => {
			btn.style.display = "none";
			btn.setAttribute("aria-expanded", "true");
			form.style.display = "inline-flex";
			input.value = "";
			input.focus();
		});
		cancelBtn.addEventListener("click", () => {
			if (_createListInFlight) return;
			restoreCreateListUi();
		});
		saveBtn.addEventListener("click", async () => {
			if (_createListInFlight) return;
			if (!isCreateListPanelReady()) {
				showToast$1("清单仍在载入,请稍后再新建", "warning");
				return;
			}
			const name = input.value.trim();
			if (!name) {
				showToast$1("请输入清单名称", "warning");
				input.focus();
				return;
			}
			_createListInFlight = true;
			setCreateListBusy(wrap, true);
			let result = "failed";
			try {
				result = await createList(name);
			} catch (error) {
				console.error(`${LOG_PREFIX$6} 新增清单发生未处理异常`, error);
				showToast$1("✗ 新增清单失败,请稍后重试", "error");
			} finally {
				_createListInFlight = false;
				setCreateListBusy(wrap, false);
			}
			if (result !== "created") input.focus();
		});
		input.addEventListener("keydown", (e) => {
			if (e.isComposing) return;
			if (e.key === "Enter") {
				e.preventDefault();
				saveBtn.click();
			} else if (e.key === "Escape") cancelBtn.click();
		});
	}
	function setCreateListBusy(wrap, busy) {
		const input = wrap.querySelector(".jhs-list-create-input");
		const saveBtn = wrap.querySelector(".jhs-list-create-save");
		const cancelBtn = wrap.querySelector(".jhs-list-create-cancel");
		wrap.setAttribute("aria-busy", String(busy));
		if (input) input.disabled = busy;
		if (saveBtn) {
			saveBtn.disabled = busy;
			saveBtn.textContent = busy ? "新建中…" : "新建";
		}
		if (cancelBtn) cancelBtn.disabled = busy;
	}
	function setupListManagementListener() {
		console.log(`${LOG_PREFIX$6} /users/lists 清单管理监听已启动`);
		document.addEventListener("click", (e) => {
			const deleteLink = e.target.closest?.("a[href*=\"remove_list\"][data-method=\"delete\"]");
			if (!deleteLink) return;
			e.preventDefault();
			e.stopPropagation();
			const href = deleteLink.getAttribute("href") || "";
			const listId = extractListIdFromHref(href);
			if (!listId) {
				console.warn(`${LOG_PREFIX$6} 删除链接无法提取 listId: ${href}`);
				return;
			}
			const confirmMsg = deleteLink.dataset.confirm || "確認移除嗎?";
			if (!confirm(confirmMsg)) return;
			handleListDeletion(listId, href).then();
		}, true);
		const editing = {
			listId: "",
			oldName: ""
		};
		document.addEventListener("click", (e) => {
			const editBtn = e.target.closest?.(".modal-edit-list-button");
			if (!editBtn) return;
			const listId = editBtn.dataset.listId || "";
			if (!listId) return;
			const nameEl = editBtn.closest("[id^=\"list-\"]")?.querySelector(".list-name");
			editing.listId = listId;
			editing.oldName = nameEl?.textContent?.trim() || "";
			console.log(`${LOG_PREFIX$6} 编辑清单快照: listId=${listId} oldName=${editing.oldName}`);
			openEditModal(listId, editing.oldName);
		}, true);
		document.addEventListener("click", (e) => {
			if (!e.target.closest?.("[data-action=\"list#updateList\"]")) return;
			e.preventDefault();
			e.stopPropagation();
			if (!editing.listId) {
				console.warn(`${LOG_PREFIX$6} 保存清单改名但无快照,跳过`);
				return;
			}
			const newName = (document.querySelector("#modal-edit-list")?.querySelector("[data-list-target=\"inputName\"]"))?.value?.trim() || "";
			if (!newName) {
				showToast$1("請輸入清單名稱", "warning");
				return;
			}
			if (newName === editing.oldName) {
				console.log(`${LOG_PREFIX$6} 清单改名前后名称相同(${newName}),跳过`);
				closeEditModal();
				return;
			}
			handleListRename(editing.listId, newName).then();
		}, true);
	}
	function openEditModal(listId, listName) {
		const modal = document.querySelector("#modal-edit-list");
		if (!modal) return;
		const nameInput = modal.querySelector("[data-list-target=\"inputName\"]");
		const idInput = modal.querySelector("[data-list-target=\"inputId\"]");
		if (nameInput) nameInput.value = listName;
		if (idInput) idInput.value = listId;
		modal.classList.add("is-active");
		document.documentElement.classList.add("is-clipped");
	}
	function closeEditModal() {
		const modal = document.querySelector("#modal-edit-list");
		if (!modal) return;
		modal.classList.remove("is-active");
		document.documentElement.classList.remove("is-clipped");
	}
	async function handleListDeletion(listId, href) {
		const csrfToken = getCsrfToken$1();
		if (!csrfToken) {
			showToast$1("✗ 无法获取 CSRF token,删除失败", "error");
			return;
		}
		console.log(`${LOG_PREFIX$6} ═══ 删除清单 listId=${listId} ═══`);
		showToast$1("正在同步删除…", "info");
		const serverOk = await sendDeleteRequest(href, csrfToken);
		if (!serverOk) {
			showToast$1("✗ JavDB 删除清单失败,本地数据未改动", "error");
			return;
		}
		const idbResult = await VltDb.deleteList(listId);
		document.querySelector(`#list-${listId}`)?.remove();
		console.log(`${LOG_PREFIX$6} 删除完成: listId=${listId} server=${serverOk} inventory=${idbResult.inventory} associations=${idbResult.associations}`);
		broadcastListMgmt("delete", listId);
		showToast$1(`✓ 清单已删除(${idbResult.associations} 条关联已清除)`, "success");
		console.log(`${LOG_PREFIX$6} ═══ 删除完成 ═══`);
	}
	function sendDeleteRequest(href, csrfToken) {
		return new Promise((resolve) => {
			GM_xmlhttpRequest({
				method: "DELETE",
				url: window.location.origin + href,
				headers: {
					"X-CSRF-Token": csrfToken,
					"X-Requested-With": "XMLHttpRequest",
					Accept: "text/javascript, application/javascript, application/xhtml+xml, */*"
				},
				timeout: 15e3,
				onload: (r) => resolve(r.status >= 200 && r.status < 400),
				onerror: () => resolve(false),
				ontimeout: () => resolve(false)
			});
		});
	}
	async function handleListRename(listId, newName) {
		const csrfToken = getCsrfToken$1();
		if (!csrfToken) {
			showToast$1("✗ 无法获取 CSRF token,改名失败", "error");
			return;
		}
		showToast$1("正在改名…", "info");
		console.log(`${LOG_PREFIX$6} ═══ 改名清单 listId=${listId} newName=${newName} ═══`);
		try {
			const response = await new Promise((resolve) => {
				GM_xmlhttpRequest({
					method: "POST",
					url: "https://javdb.com/users/update_list",
					headers: {
						"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
						"X-CSRF-Token": csrfToken,
						"X-Requested-With": "XMLHttpRequest",
						Accept: "application/json, text/javascript, */*;q=0.01"
					},
					data: `id=${encodeURIComponent(listId)}&name=${encodeURIComponent(newName)}`,
					timeout: 15e3,
					onload: (r) => {
						if (r.status >= 200 && r.status < 300) try {
							resolve(JSON.parse(r.responseText));
						} catch {
							resolve({
								success: false,
								name: "",
								message: "响应解析失败"
							});
						}
						else resolve({
							success: false,
							name: "",
							message: `HTTP ${r.status}`
						});
					},
					onerror: () => resolve({
						success: false,
						name: "",
						message: "网络错误"
					}),
					ontimeout: () => resolve({
						success: false,
						name: "",
						message: "请求超时"
					})
				});
			});
			if (!response.success) {
				showToast$1(`✗ 改名失败:${response.message || "未知错误"}`, "error");
				return;
			}
			const finalName = response.name || newName;
			console.log(`${LOG_PREFIX$6} 服务端改名成功: listId=${listId} name=${finalName}`);
			if (await VltDb.renameList(listId, finalName)) console.log(`${LOG_PREFIX$6} IDB 清单改名完成: listId=${listId} name=${finalName}`);
			else console.warn(`${LOG_PREFIX$6} IDB 清单改名跳过: listId=${listId} 不在本地 inventory 中`);
			broadcastListMgmt("rename", listId, { newName: finalName });
			const nameEl = document.querySelector(`#list-${listId} .list-name`);
			if (nameEl) nameEl.textContent = finalName;
			closeEditModal();
			showToast$1(`✓ 清单已改名为「${finalName}」`, "success");
			console.log(`${LOG_PREFIX$6} ═══ 改名完成 ═══`);
		} catch (err) {
			const message = err instanceof Error ? err.message : "未知错误";
			console.error(`${LOG_PREFIX$6} 清单改名失败: listId=${listId}`, err);
			showToast$1(`✗ 清单改名失败:${message}`, "error");
		}
	}
	function removeDetailPageCheckbox(listId) {
		const panelCb = document.querySelector(`.jhs-list-panel input[data-list-id="${listId}"]`);
		if (panelCb) (panelCb.closest("p.control") ?? panelCb.closest("label"))?.remove();
		const modalCb = document.querySelector(`#modal-save-list input[data-list-id="${listId}"]`);
		if (modalCb) (modalCb.closest("p.control") ?? modalCb.closest("label"))?.remove();
		refreshListPanel();
		console.log(`${LOG_PREFIX$6} 详情页已移除清单 ${listId} 的 checkbox`);
	}
	function updateDetailPageCheckboxLabel(listId, newName) {
		document.querySelectorAll(`input[data-list-id="${listId}"]`).forEach((cb) => {
			const label = cb.closest("label");
			if (!label) return;
			const displayName = label.querySelector(".jhs-list-item__name");
			if (displayName) {
				displayName.textContent = newName;
				return;
			}
			Array.from(label.childNodes).forEach((node) => {
				if (node.nodeType === Node.TEXT_NODE) node.textContent = "";
			});
			const inputEl = label.querySelector("input");
			if (inputEl && inputEl.nextSibling) inputEl.nextSibling.textContent = `\u00A0\n    ${newName}\u00A0\n    `;
			else if (inputEl) inputEl.insertAdjacentText("afterend", `\u00A0${newName}\u00A0`);
		});
		refreshListPanel();
		console.log(`${LOG_PREFIX$6} 详情页已更新清单 ${listId} 的标签为「${newName}」`);
	}
	function hookWantAndWatchedButtons(plugin) {
		if (!isJavdbSite) return;
		if (plugin._wantWatchedObserved) return;
		const self = plugin;
		const ensure = () => {
			const container = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav > div.review-buttons");
			if (!container) {
				setTimeout(ensure, 200);
				return;
			}
			if (container.__jhsObserved) return;
			container.__jhsObserved = true;
			self._wantWatchedObserved = true;
			self._lastWantState = self.detectWantWatchedState(container);
			new MutationObserver(() => {
				if (self._wantWatchedSyncing) return;
				clearTimeout(self._wantWatchedDebounce ?? void 0);
				self._wantWatchedDebounce = setTimeout(() => {
					self._wantWatchedSyncing = true;
					try {
						const currentState = self.detectWantWatchedState(container);
						const lastState = self._lastWantState || {
							want: false,
							watched: false
						};
						if (currentState.want !== lastState.want) {
							if (currentState.want) self.onWantAdded();
							else self.onWantRemoved();
						}
						if (currentState.watched !== lastState.watched) {
							if (currentState.watched) self.onWatchedAdded();
							else self.onWatchedRemoved();
						}
						self._lastWantState = currentState;
					} finally {
						self._wantWatchedSyncing = false;
					}
				}, 150);
			}).observe(container, {
				childList: true,
				subtree: true
			});
		};
		ensure();
	}
	function detectWantWatchedState(container) {
		const wantTag = container.querySelector("a[href='/users/want_watch_videos'] .tag.is-info.is-light");
		const watchedTag = container.querySelector("a[href='/users/watched_videos'] .tag.is-success.is-light");
		return {
			want: !!wantTag,
			watched: !!watchedTag
		};
	}
	async function onWantAdded(plugin) {
		const pageInfo = plugin.getPageInfo();
		try {
			const carRecord = await storageManager.getCar(pageInfo.carNum);
			if (carRecord && carRecord.status === "favorite") {} else {
				await storageManager.saveCar({
					carNum: pageInfo.carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: FAVORITE_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				plugin.broadcastWantWatchedSync({
					carNum: pageInfo.carNum,
					status: FAVORITE_ACTION,
					op: "add"
				});
				show.ok(`${pageInfo.carNum} 已收藏`);
			}
		} catch (err) {
			clog.error("[JHS-想看自动同步] 写入失败", err);
		}
		plugin.showStatus(pageInfo.carNum).then();
	}
	async function onWantRemoved(plugin) {
		const pageInfo = plugin.getPageInfo();
		try {
			if (await plugin.removeCarIfStatus(pageInfo.carNum, "favorite")) {
				plugin.broadcastWantWatchedSync({
					carNum: pageInfo.carNum,
					status: FAVORITE_ACTION,
					op: "remove"
				});
				show.ok(`${pageInfo.carNum} 已取消收藏`);
			}
		} catch (err) {
			clog.error("[JHS-想看自动同步] 移除失败", err);
		}
		plugin.showStatus(pageInfo.carNum).then();
	}
	async function onWatchedAdded(plugin) {
		const pageInfo = plugin.getPageInfo();
		try {
			const carRecord = await storageManager.getCar(pageInfo.carNum);
			const wasFavorite = carRecord?.status === FAVORITE_ACTION;
			if (carRecord && carRecord.status === "hasWatch") {} else {
				await storageManager.saveCar({
					carNum: pageInfo.carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: HAS_WATCH_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				plugin.broadcastWantWatchedSync({
					carNum: pageInfo.carNum,
					status: HAS_WATCH_ACTION,
					op: "add"
				});
				show.ok(`${pageInfo.carNum} 已标记看过`);
			}
			if (wasFavorite) removeFromNonProtectedListsOnWatch().then();
		} catch (err) {
			clog.error("[JHS-观看自动同步] 写入失败", err);
		}
		plugin.showStatus(pageInfo.carNum).then();
	}
	async function onWatchedRemoved(plugin) {
		const pageInfo = plugin.getPageInfo();
		try {
			if (await plugin.removeCarIfStatus(pageInfo.carNum, "hasWatch")) {
				plugin.broadcastWantWatchedSync({
					carNum: pageInfo.carNum,
					status: HAS_WATCH_ACTION,
					op: "remove"
				});
				show.ok(`${pageInfo.carNum} 已取消看过`);
			}
		} catch (err) {
			clog.error("[JHS-观看自动同步] 移除失败", err);
		}
		plugin.showStatus(pageInfo.carNum).then();
	}
	async function removeCarIfStatus(carNum, status) {
		const carRecord = await storageManager.getCar(carNum);
		if (!carRecord) return false;
		if (carRecord.status !== status) return false;
		return await storageManager.removeCar(carNum);
	}
	function broadcastWantWatchedSync(payload) {
		try {
			broadcastSend("jdb:want-watched-sync", {
				...payload,
				time: Date.now()
			}, {
				eventDetail: payload,
				onError: () => {}
			});
		} catch (err) {
			clog.error("[JHS-想看/观看同步] 广播失败", err);
		}
	}
	function setupWantWatchedSyncListener(plugin) {
		if (!isJavdbSite) return;
		if (plugin._wantWatchedListenerInstalled) return;
		plugin._wantWatchedListenerInstalled = true;
		const self = plugin;
		const handleSync = (rawData) => {
			const payload = rawData?.detail || (() => {
				try {
					return JSON.parse(rawData);
				} catch {
					return null;
				}
			})();
			if (!payload || !payload.carNum) return;
			storageManager.clearCarListCache();
			try {
				const currentCarNum = self.getPageInfo().carNum;
				if (currentCarNum && payload.carNum === currentCarNum) self.showStatus(currentCarNum).then(() => {});
			} catch {}
			self.refreshItemStatusTag(payload.carNum);
		};
		broadcastSubscribe("jdb:want-watched-sync", (rawData) => handleSync(rawData), { raw: true });
	}
	function refreshItemStatusTag(plugin, carNum) {
		try {
			const itemSelector = plugin.getSelector().itemSelector;
			const items = document.querySelectorAll(itemSelector);
			for (const item of items) {
				const strongEl = item.querySelector("a > div.video-title > strong");
				if (!strongEl || strongEl.innerHTML !== carNum) continue;
				const listPagePlugin = plugin.getBean("ListPagePlugin");
				if (!listPagePlugin) continue;
				listPagePlugin.renderItemStatusTag(item, carNum);
			}
		} catch (err) {
			clog.error("[JHS-想看/观看] 刷新列表项 status-tag 失败", err);
		}
	}
	function RatingBarHtml() {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
			className: "jhs-stars",
			"data-score": 0,
			children: [
				1,
				2,
				3,
				4,
				5
			].map((n) => (0, import_jsx_runtime.jsx)("span", {
				className: "jhs-star",
				"data-score": n,
				children: "★"
			}, n))
		}), (0, import_jsx_runtime.jsxs)("div", {
			className: "jhs-rating-actions",
			children: [
				(0, import_jsx_runtime.jsx)("button", {
					className: "jhs-fav-btn",
					type: "button",
					title: "设为想看(收藏)",
					children: "♥ 收藏"
				}),
				(0, import_jsx_runtime.jsx)("button", {
					className: "jhs-read-btn",
					type: "button",
					title: "设为已观看(0星)",
					children: "已读"
				}),
				(0, import_jsx_runtime.jsx)("button", {
					className: "jhs-block-btn",
					type: "button",
					title: "拉黑该影片(屏蔽,设为已读0星后关闭页面)",
					children: "🚫 拉黑"
				})
			]
		})] });
	}
	var rating_bar_default = "/* 隐藏 review-buttons 内第二个 panel-block(下載/訂正磁力按钮) */\n.review-buttons > .panel-block ~ .panel-block {\n    display: none !important;\n}\n\n/* 隐藏 review-buttons 后紧跟的 panel-block(N人想看/看過统计) */\n.review-buttons + .panel-block {\n    display: none !important;\n}\n\n/* 隐藏原生评价状态标签和按钮(星星组件已替代) */\n.review-buttons .review-title {\n    display: none !important;\n}\n\n.review-buttons .buttons.are-small.review-buttons {\n    display: none !important;\n}\n\n/* 永久隐藏清单 modal(DOM 保留供 Stimulus ajax 操作) */\n#modal-save-list {\n    display: none !important;\n}\n\n/* 星星评分组件 */\n.jhs-rating-bar {\n    display: flex;\n    flex-direction: column;\n    gap: 6px;\n    margin: 4px 0 12px;\n    user-select: none;\n}\n\n.jhs-stars {\n    display: inline-flex;\n    gap: 6px;\n    align-items: center;\n}\n\n.jhs-star {\n    font-size: 26px;\n    line-height: 1.2;\n    padding: 2px 4px;\n    cursor: pointer;\n    color: #d0d0d0;\n    transition:\n        color 0.15s,\n        transform 0.15s;\n}\n\n.jhs-star:hover {\n    transform: scale(1.15);\n}\n\n.jhs-star.is-preview {\n    color: #f5b301;\n}\n\n.jhs-star.is-active {\n    color: #f5b301;\n}\n\n.jhs-star.is-active:hover {\n    color: #ffce3a;\n}\n\n.jhs-star.is-popping {\n    animation: jhs-star-pop 0.3s ease;\n}\n\n@keyframes jhs-star-pop {\n    0% {\n        transform: scale(1);\n    }\n\n    40% {\n        transform: scale(1.4);\n    }\n\n    100% {\n        transform: scale(1);\n    }\n}\n\n.jhs-stars.is-disabled .jhs-star {\n    cursor: default;\n    opacity: 0.4;\n    pointer-events: none;\n}\n\n.jhs-rating-actions {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n}\n\n.jhs-read-btn,\n.jhs-fav-btn,\n.jhs-block-btn {\n    border: 1px solid #dbdbdb;\n    border-radius: 6px;\n    padding: 5px 16px;\n    font-size: 14px;\n    background: #fff;\n    cursor: pointer;\n    transition: color 0.15s, background-color 0.15s, border-color 0.15s, box-shadow 0.15s;\n    white-space: nowrap;\n}\n\n.jhs-read-btn:hover,\n.jhs-fav-btn:hover,\n.jhs-block-btn:hover {\n    border-color: #b5b5b5;\n    box-shadow: 0 1px 4px rgb(0, 0, 0, 0.08);\n}\n\n.jhs-read-btn.is-active {\n    background: #48c774;\n    border-color: #48c774;\n    color: #fff;\n}\n\n.jhs-fav-btn.is-active {\n    background: #3273dc;\n    border-color: #3273dc;\n    color: #fff;\n}\n\n.jhs-block-btn.is-active {\n    background: #de3333;\n    border-color: #de3333;\n    color: #fff;\n}\n\n.jhs-read-btn.is-popping,\n.jhs-fav-btn.is-popping,\n.jhs-block-btn.is-popping {\n    animation: jhs-star-pop 0.3s ease;\n}\n\n.jhs-rating-bar.is-busy {\n    pointer-events: none;\n    opacity: 0.6;\n}\n\n/* 详情页清单面板(位于 #otherSiteBox 下方) */\n.jhs-list-panel {\n    --jhs-list-accent: #3273dc;\n    --jhs-list-border: #dfe4ea;\n    --jhs-list-muted: #5f6b7a;\n    --jhs-list-surface: #fff;\n    --jhs-list-subtle: #f6f8fa;\n\n    display: block;\n    margin: 10px 0 0;\n    padding: 0;\n    background: var(--jhs-list-surface);\n    border: 1px solid var(--jhs-list-border);\n    border-radius: 10px;\n    color: #2f3540;\n}\n\n.jhs-list-panel__header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 10px;\n    min-height: 48px;\n    padding: 8px 10px;\n    border-bottom: 1px solid #edf0f3;\n}\n\n.jhs-list-panel__heading {\n    display: flex;\n    min-width: 0;\n    flex-direction: column;\n    gap: 1px;\n}\n\n.jhs-list-panel__title {\n    margin: 0;\n    color: #2f3540;\n    font-size: 14px;\n    font-weight: 700;\n    line-height: 1.3;\n}\n\n.jhs-list-panel__summary {\n    color: var(--jhs-list-muted);\n    font-size: 12px;\n    line-height: 1.3;\n}\n\n.jhs-list-panel__summary.is-error {\n    color: #a32626;\n    font-weight: 600;\n}\n\n.jhs-list-panel__sort {\n    display: inline-flex;\n    flex-shrink: 0;\n    align-items: center;\n    gap: 5px;\n    color: var(--jhs-list-muted);\n    font-size: 12px;\n    font-weight: 600;\n}\n\n.jhs-list-panel__sort-select {\n    height: 32px;\n    padding: 0 8px;\n    border: 1px solid #cbd2da;\n    border-radius: 6px;\n    background: #fff;\n    color: #3d4652;\n    cursor: pointer;\n    font-size: 12px;\n    font-weight: 600;\n}\n\n.jhs-list-panel__sort-select:hover:not(:disabled) {\n    border-color: #9aa6b2;\n}\n\n.jhs-list-panel__sort-select:disabled {\n    cursor: wait;\n    opacity: 0.58;\n}\n\n.jhs-list-panel__search-wrap {\n    position: relative;\n    display: flex;\n    align-items: center;\n    margin: 10px;\n}\n\n.jhs-list-panel__search-icon {\n    position: absolute;\n    left: 10px;\n    color: #6b7683;\n    font-size: 15px;\n    line-height: 1;\n    pointer-events: none;\n}\n\n.jhs-list-panel__search {\n    width: 100%;\n    height: 34px;\n    padding: 6px 32px 6px 30px;\n    border: 1px solid #cbd2da;\n    border-radius: 7px;\n    background: #fff;\n    color: #2f3540;\n    font-size: 13px;\n    outline: none;\n    transition:\n        border-color 0.15s ease,\n        box-shadow 0.15s ease;\n}\n\n.jhs-list-panel__search::placeholder {\n    color: #667281;\n}\n\n.jhs-list-panel__search::-webkit-search-cancel-button {\n    display: none;\n}\n\n.jhs-list-panel__search:focus-visible,\n.jhs-list-panel__sort-select:focus-visible {\n    border-color: var(--jhs-list-accent);\n    box-shadow: 0 0 0 2px rgb(50, 115, 220, 0.2);\n    outline: none;\n}\n\n.jhs-list-panel__search:disabled {\n    cursor: wait;\n    opacity: 0.58;\n}\n\n.jhs-list-panel__search-clear {\n    position: absolute;\n    right: 5px;\n    display: inline-flex;\n    width: 26px;\n    height: 26px;\n    align-items: center;\n    justify-content: center;\n    padding: 0;\n    border: 0;\n    border-radius: 50%;\n    background: transparent;\n    color: #596574;\n    cursor: pointer;\n    font-size: 18px;\n    line-height: 1;\n}\n\n.jhs-list-panel__search-clear[hidden] {\n    display: none !important;\n}\n\n.jhs-list-panel__search-clear:hover {\n    background: #e9edf2;\n    color: #27313c;\n}\n\n.jhs-list-panel__search-clear:focus-visible,\n.jhs-list-panel__retry:focus-visible {\n    outline: 2px solid var(--jhs-list-accent);\n    outline-offset: 2px;\n}\n\n.jhs-list-panel__items {\n    display: grid;\n    grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));\n    gap: 6px;\n    max-height: 320px;\n    padding: 0 10px 10px;\n    overflow-y: auto;\n    overscroll-behavior-y: auto;\n}\n\n.jhs-list-panel .jhs-list-item {\n    min-width: 0;\n    margin: 0;\n}\n\n.jhs-list-panel .jhs-list-item[hidden] {\n    display: none !important;\n}\n\n.jhs-list-panel .jhs-list-item__label {\n    display: grid;\n    grid-template-columns: auto minmax(0, 1fr) auto;\n    min-height: 40px;\n    align-items: center;\n    gap: 7px;\n    padding: 6px 8px;\n    border: 1px solid #e0e5eb;\n    border-radius: 8px;\n    background: #fff;\n    color: #3d4652;\n    cursor: pointer;\n    font-size: 13px;\n    line-height: 1.25;\n    transition:\n        border-color 0.15s ease,\n        background-color 0.15s ease,\n        color 0.15s ease;\n}\n\n.jhs-list-panel .jhs-list-item__label:hover {\n    border-color: #aeb8c3;\n    background: #f8fafc;\n}\n\n.jhs-list-panel .jhs-list-item__label:focus-within {\n    border-color: var(--jhs-list-accent);\n    box-shadow: 0 0 0 2px rgb(50, 115, 220, 0.18);\n}\n\n.jhs-list-panel .jhs-list-item:has(input:checked) .jhs-list-item__label {\n    border-color: var(--jhs-list-accent);\n    background: #eef4ff;\n    color: #234f96;\n}\n\n.jhs-list-panel .jhs-list-item:has(input:disabled) .jhs-list-item__label {\n    border-color: #e1e5ea;\n    background: #f7f8fa;\n    color: #65707d;\n    cursor: not-allowed;\n}\n\n.jhs-list-panel\n    .jhs-list-item:has(input[data-vlt-was-disabled])\n    .jhs-list-item__label {\n    border-color: #eadba7;\n    background: #fffdf5;\n    cursor: wait;\n}\n\n.jhs-list-panel .jhs-list-item__label input[type='checkbox'] {\n    width: 16px;\n    height: 16px;\n    margin: 0;\n    accent-color: var(--jhs-list-accent);\n}\n\n.jhs-list-item__name {\n    display: -webkit-box;\n    min-width: 0;\n    max-height: 2.5em;\n    overflow: hidden;\n    font-weight: 600;\n    overflow-wrap: anywhere;\n    -webkit-box-orient: vertical;\n    -webkit-line-clamp: 2;\n    line-clamp: 2;\n    white-space: normal;\n}\n\n.jhs-list-item__count {\n    min-width: 24px;\n    padding: 2px 6px;\n    border-radius: 50rem;\n    background: #edf0f4;\n    color: #4d5967;\n    font-size: 11px;\n    font-variant-numeric: tabular-nums;\n    font-weight: 700;\n    text-align: center;\n}\n\n.jhs-list-panel .jhs-list-item:has(input:checked) .jhs-list-item__count {\n    background: #d9e7ff;\n    color: #234f96;\n}\n\n.jhs-list-item__status {\n    display: inline-flex;\n    min-width: 32px;\n    align-items: center;\n    justify-content: center;\n    padding: 2px 5px;\n    border-radius: 50rem;\n    background: #edf0f4;\n    color: #596574;\n    font-size: 10px;\n    font-weight: 700;\n    white-space: nowrap;\n}\n\n.jhs-list-item__status[hidden] {\n    display: none !important;\n}\n\n.jhs-list-item__status[data-state='busy'] {\n    background: #fff3cd;\n    color: #725500;\n}\n\n.jhs-list-item__status[data-state='full'],\n.jhs-list-item__status[data-state='unavailable'] {\n    background: #f8e2e2;\n    color: #8d2929;\n}\n\n.jhs-list-panel .jhs-list-item:has(.jhs-list-item__status:not([hidden])) .jhs-list-item__count {\n    display: none;\n}\n\n.jhs-list-panel__empty {\n    margin: 0;\n    padding: 4px 12px 14px;\n    color: var(--jhs-list-muted);\n    font-size: 12px;\n    line-height: 1.45;\n    text-align: center;\n}\n\n/* ── 序号清单合并行(详情页清单面板) ─────────────────────── */\n.jhs-list-panel .jhs-list-group:has(input:indeterminate) .jhs-list-item__label {\n    border-color: var(--jhs-list-accent);\n    border-style: dashed;\n}\n\n.jhs-list-group__bar {\n    display: flex;\n    gap: 6px;\n    align-items: center;\n    padding: 3px 8px 0;\n}\n\n.jhs-list-group__toggle,\n.jhs-list-group__reflow {\n    display: inline-flex;\n    align-items: center;\n    gap: 3px;\n    padding: 2px 8px;\n    border: 1px solid #e0e5eb;\n    border-radius: 50rem;\n    background: #f6f8fa;\n    color: #4d5967;\n    cursor: pointer;\n    font-size: 11px;\n    font-weight: 600;\n    line-height: 1.4;\n    transition:\n        border-color 0.15s ease,\n        background-color 0.15s ease,\n        color 0.15s ease;\n}\n\n.jhs-list-group__toggle:hover,\n.jhs-list-group__reflow:hover {\n    border-color: #aeb8c3;\n    background: #eef1f5;\n    color: #27313c;\n}\n\n.jhs-list-group__reflow {\n    margin-left: auto;\n}\n\n.jhs-list-group__detail {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    margin: 4px 0 2px;\n    padding: 4px 6px 4px 12px;\n    border-left: 2px solid #e0e5eb;\n    list-style: none;\n}\n\n.jhs-list-group__detail[hidden] {\n    display: none !important;\n}\n\n.jhs-list-group__member {\n    display: grid;\n    grid-template-columns: auto minmax(0, 1fr) auto auto auto;\n    gap: 6px;\n    align-items: center;\n    padding: 2px 4px;\n    border-radius: 5px;\n    color: #4d5967;\n    font-size: 12px;\n    line-height: 1.3;\n}\n\n.jhs-list-group__member-num {\n    min-width: 18px;\n    padding: 0 4px;\n    border-radius: 4px;\n    background: #edf0f4;\n    color: #4d5967;\n    font-size: 10px;\n    font-weight: 700;\n    text-align: center;\n}\n\n.jhs-list-group__member-name {\n    min-width: 0;\n    overflow: hidden;\n    overflow-wrap: anywhere;\n    font-weight: 600;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n}\n\n.jhs-list-group__member-count {\n    padding: 1px 6px;\n    border-radius: 50rem;\n    background: #edf0f4;\n    color: #4d5967;\n    font-size: 10px;\n    font-variant-numeric: tabular-nums;\n    font-weight: 700;\n}\n\n.jhs-list-group__member-flag {\n    padding: 1px 5px;\n    border-radius: 50rem;\n    font-size: 10px;\n    font-weight: 700;\n    white-space: nowrap;\n}\n\n.jhs-list-group__member-flag.is-checked {\n    background: #d9e7ff;\n    color: #234f96;\n}\n\n.jhs-list-group__member-flag.is-full {\n    background: #f8e2e2;\n    color: #8d2929;\n}\n\n.jhs-list-panel__skeleton {\n    height: 40px;\n    border-radius: 8px;\n    background: linear-gradient(90deg, #edf0f3 25%, #f7f8fa 50%, #edf0f3 75%);\n    background-size: 200% 100%;\n    animation: jhs-list-skeleton 1.1s ease-in-out infinite;\n}\n\n@keyframes jhs-list-skeleton {\n    from {\n        background-position: 100% 0;\n    }\n\n    to {\n        background-position: -100% 0;\n    }\n}\n\n.jhs-list-panel__error {\n    grid-column: 1 / -1;\n    display: flex;\n    min-height: 56px;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    color: #a32626;\n    font-size: 12px;\n    text-align: center;\n}\n\n.jhs-list-panel__retry {\n    padding: 5px 9px;\n    border: 1px solid #c5ccd5;\n    border-radius: 6px;\n    background: #fff;\n    color: #344252;\n    cursor: pointer;\n    font-size: 12px;\n    font-weight: 600;\n}\n\n.jhs-list-panel__retry:hover {\n    border-color: #8e9aa7;\n    background: #f6f8fa;\n}\n\n@media (width <= 768px) {\n    .jhs-list-panel__header {\n        align-items: flex-start;\n        flex-wrap: wrap;\n    }\n\n    .jhs-list-panel__sort {\n        margin-left: auto;\n    }\n\n    .jhs-list-panel__sort-select,\n    .jhs-list-panel__search {\n        height: 44px;\n    }\n\n    .jhs-list-panel__search {\n        padding-right: 48px;\n    }\n\n    .jhs-list-panel__search-clear {\n        right: 0;\n        width: 44px;\n        height: 44px;\n    }\n\n    .jhs-list-panel__items {\n        grid-template-columns: 1fr;\n        max-height: none;\n        overflow: visible;\n    }\n\n    .jhs-list-panel .jhs-list-item__label,\n    .jhs-list-panel__skeleton {\n        min-height: 44px;\n    }\n\n    .jhs-list-panel__retry {\n        min-width: 44px;\n        min-height: 44px;\n    }\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .jhs-list-panel *,\n    .jhs-list-panel *::before,\n    .jhs-list-panel *::after {\n        scroll-behavior: auto !important;\n        animation-duration: 0.01ms !important;\n        animation-iteration-count: 1 !important;\n        transition-duration: 0.01ms !important;\n    }\n}\n\n/* 隐藏详情页菜单按钮组的状态行(屏蔽/收藏/已观看)\n   快捷评分条(星星+已读+收藏)已覆盖这三个功能,拉黑待第二步移入评分条\n   DOM 保留:createMenuBtn 的事件绑定 + showStatus 文案更新仍可运行 */\n.jhs-menu-status-row {\n    display: none !important;\n}\n";
	function ReviewConflictDialog({ carNum, localDesc, serverDesc }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: {
				padding: "8px 4px 4px",
				fontSize: "14px",
				lineHeight: "1.8"
			},
			children: [
				(0, import_jsx_runtime.jsxs)("p", {
					style: { margin: "0 0 10px" },
					children: [
						"「",
						carNum,
						"」的脚本记录与 JavDB 状态不一致,请选择以哪一侧为准:"
					]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						justifyContent: "space-between",
						gap: "12px",
						marginBottom: "8px"
					},
					children: [(0, import_jsx_runtime.jsx)("span", {
						style: { color: "#6c757d" },
						children: "脚本记录:"
					}), (0, import_jsx_runtime.jsx)("strong", { children: localDesc })]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						justifyContent: "space-between",
						gap: "12px",
						marginBottom: "14px"
					},
					children: [(0, import_jsx_runtime.jsx)("span", {
						style: { color: "#6c757d" },
						children: "网站数据:"
					}), (0, import_jsx_runtime.jsx)("strong", { children: serverDesc })]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: {
						display: "flex",
						gap: "10px",
						justifyContent: "flex-end"
					},
					children: [
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							"data-conflict-choice": "local",
							style: {
								padding: "6px 14px",
								border: "1px solid #2e8ded",
								borderRadius: "3px",
								background: "#2e8ded",
								color: "#fff",
								cursor: "pointer"
							},
							children: "使用脚本记录"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							"data-conflict-choice": "server",
							style: {
								padding: "6px 14px",
								border: "1px solid #48c774",
								borderRadius: "3px",
								background: "#48c774",
								color: "#fff",
								cursor: "pointer"
							},
							children: "使用网站数据"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							"data-conflict-choice": "cancel",
							style: {
								padding: "6px 14px",
								border: "1px solid #dedede",
								borderRadius: "3px",
								background: "#f1f1f1",
								color: "#333",
								cursor: "pointer"
							},
							children: "取消"
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("p", {
					style: {
						margin: "12px 0 0",
						fontSize: "12px",
						color: "#999"
					},
					children: "选择后立即同步;冲突未处理前不会自动覆盖任一侧数据。"
				})
			]
		});
	}
	var REVIEW_SYNC_TIMEOUT_MS = 1e4;
	var REVIEW_SYNC_START_DELAY_MS = 1200;
	function readServerReviewState() {
		const rb = document.querySelector(".review-buttons");
		if (!rb) return {
			want: false,
			watched: false,
			score: 0
		};
		const want = !!rb.querySelector("a[href='/users/want_watch_videos'] .tag.is-info.is-light");
		const watched = !!rb.querySelector("a[href='/users/watched_videos'] .tag.is-success.is-light");
		const checked = rb.querySelector("input[name=\"video_review[score]\"][checked]");
		const score = checked ? Number(checked.value) : 0;
		return {
			want,
			watched,
			score: Number.isFinite(score) ? score : 0
		};
	}
	function setupAutoReviewSync(plugin, enabled) {
		if (!enabled || plugin._autoSyncChecked) return;
		const startedAt = Date.now();
		const ensure = () => {
			if (!document.querySelector(".review-buttons")) {
				if (Date.now() - startedAt > REVIEW_SYNC_TIMEOUT_MS) {
					plugin._autoSyncChecked = true;
					clog.warn("[JHS] 评价状态自动校验:等待 .review-buttons 超时,本次跳过");
					return;
				}
				setTimeout(ensure, 400);
				return;
			}
			plugin._autoSyncChecked = true;
			autoSyncReviewState(plugin).then();
		};
		setTimeout(ensure, REVIEW_SYNC_START_DELAY_MS);
	}
	async function autoSyncReviewState(plugin) {
		const pageInfo = plugin.getPageInfo();
		const carNum = pageInfo.carNum;
		if (!carNum) return;
		const server = readServerReviewState();
		const carRecord = await storageManager.getCar(carNum);
		const localStatus = carRecord?.status;
		const localScore = typeof carRecord?.score === "number" ? carRecord.score : 0;
		if (server.want && localStatus === "favorite") return;
		if (server.watched && localStatus === "hasWatch" && server.score === localScore) return;
		if (!server.want && !server.watched) {
			if (localStatus === "favorite") await enqueuePush(plugin, carNum, "want");
			else if (localStatus === "hasWatch") await enqueuePush(plugin, carNum, "review", localScore);
			return;
		}
		if (!localStatus) {
			if (server.watched) {
				await storageManager.saveCar({
					carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: HAS_WATCH_ACTION,
					publishTime: pageInfo.publishTime ?? void 0,
					score: server.score || 0
				});
				plugin.broadcastWantWatchedSync({
					carNum,
					status: HAS_WATCH_ACTION,
					op: "add",
					score: server.score || 0
				});
				clog.log(`[JHS] 评价状态自动校验:已从服务器补齐本地已看记录 ${carNum}(${server.score} 星)`);
				plugin.showStatus(carNum).then();
			} else if (server.want) {
				await storageManager.saveCar({
					carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: FAVORITE_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				plugin.broadcastWantWatchedSync({
					carNum,
					status: FAVORITE_ACTION,
					op: "add"
				});
				clog.log(`[JHS] 评价状态自动校验:已从服务器补齐本地收藏记录 ${carNum}`);
				plugin.showStatus(carNum).then();
			}
			return;
		}
		const choice = await confirmReviewConflict(carNum, localStatus === "favorite" ? "⭐ 已收藏(想看)" : localStatus === "hasWatch" ? `🔍 已看${localScore > 0 ? ` ${localScore} 星` : ""}` : localStatus === "filter" ? "🚫 已拉黑" : localStatus || "无记录", server.want ? "⭐ 想看" : server.watched ? `🔍 已看${server.score > 0 ? ` ${server.score} 星` : ""}` : "无评价");
		if (choice === "local") {
			if (localStatus === "favorite") await enqueuePush(plugin, carNum, "want");
			else if (localStatus === "hasWatch") await enqueuePush(plugin, carNum, "review", localScore);
			else if (localStatus === "filter") await enqueuePush(plugin, carNum, "review", 0);
		} else if (choice === "server") {
			if (server.watched) {
				await storageManager.saveCar({
					carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: HAS_WATCH_ACTION,
					publishTime: pageInfo.publishTime ?? void 0,
					score: server.score || 0
				});
				plugin.broadcastWantWatchedSync({
					carNum,
					status: HAS_WATCH_ACTION,
					op: "add",
					score: server.score || 0
				});
				clog.log(`[JHS] 评价状态冲突:已按网站数据更新本地已看记录 ${carNum}(${server.score} 星)`);
				plugin.showStatus(carNum).then();
			} else if (server.want) {
				await storageManager.saveCar({
					carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: FAVORITE_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				plugin.broadcastWantWatchedSync({
					carNum,
					status: FAVORITE_ACTION,
					op: "add"
				});
				clog.log(`[JHS] 评价状态冲突:已按网站数据更新本地收藏记录 ${carNum}`);
				plugin.showStatus(carNum).then();
			}
		}
	}
	function confirmReviewConflict(carNum, localDesc, serverDesc) {
		return new Promise((resolve) => {
			let settled = false;
			const settle = (choice) => {
				if (settled) return;
				settled = true;
				resolve(choice);
			};
			layer.open({
				type: 1,
				title: `评价状态冲突 - ${carNum}`,
				area: ["520px", "auto"],
				content: jsxToString((0, import_jsx_runtime.jsx)(ReviewConflictDialog, {
					carNum,
					localDesc,
					serverDesc
				})),
				success: (layerEl, layerIndex) => {
					const $layer = $(layerEl);
					$layer.find("[data-conflict-choice=\"local\"]").on("click", function() {
						layer.close(layerIndex);
						settle("local");
					});
					$layer.find("[data-conflict-choice=\"server\"]").on("click", function() {
						layer.close(layerIndex);
						settle("server");
					});
					$layer.find("[data-conflict-choice=\"cancel\"]").on("click", function() {
						layer.close(layerIndex);
						settle(null);
					});
				},
				cancel: () => {
					settle(null);
				}
			});
		});
	}
	function enqueuePush(plugin, carNum, kind, score = 0) {
		return new Promise((resolve) => {
			plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
				plugin._wantWatchedSyncing = true;
				try {
					if (kind === "want") {
						await plugin._triggerJavdbWant();
						show.ok(`${carNum} 已同步收藏到 JavDB`);
					} else {
						await plugin._triggerJavdbReview(score);
						show.ok(`${carNum} 已同步${score > 0 ? `评分 ${score} 星` : "已读"}到 JavDB`);
					}
					plugin._syncRatingBar();
				} catch (err) {
					clog.error(`[JHS] 评价状态自动校验:推送失败 ${carNum}`, err);
					show.error(`同步失败: ${carNum} ` + (err instanceof Error ? err.message : String(err)));
				} finally {
					plugin._wantWatchedSyncing = false;
				}
			}).catch(() => {}).finally(() => resolve());
		});
	}
	function QuickBlockConfirmMessage({ carNum }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			"是否拉黑 ",
			carNum,
			"?",
			(0, import_jsx_runtime.jsx)("br", {}),
			(0, import_jsx_runtime.jsx)("span", {
				style: { color: "#f40" },
				children: "拉黑后该影片将被屏蔽,列表页自动隐藏,且设为已读0星。此操作不可在详情页撤销。"
			})
		] });
	}
	async function quickSetHasWatch(plugin, score) {
		const pageInfo = plugin.getPageInfo();
		if (!pageInfo.carNum) return;
		try {
			const carRecord = await storageManager.getCar(pageInfo.carNum);
			const wasFavorite = carRecord?.status === FAVORITE_ACTION;
			const serverState = readServerReviewState();
			if (serverState.watched && serverState.score === score) {
				show.ok(pageInfo.carNum + " 评分未变化");
				return;
			}
			if (carRecord && carRecord.status === "favorite") await storageManager.removeCar(pageInfo.carNum);
			await storageManager.saveCar({
				carNum: pageInfo.carNum,
				url: pageInfo.url ?? void 0,
				names: pageInfo.actress ?? void 0,
				actionType: HAS_WATCH_ACTION,
				publishTime: pageInfo.publishTime ?? void 0,
				score
			});
			plugin.broadcastWantWatchedSync({
				carNum: pageInfo.carNum,
				status: HAS_WATCH_ACTION,
				op: "add",
				score
			});
			show.ok(pageInfo.carNum + " 已标记看过 " + (score > 0 ? "★" + score : ""));
			if (wasFavorite) removeFromNonProtectedListsOnWatch().then();
		} catch (err) {
			clog.error("[JHS-快键] 设为已观看失败", err);
			show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
			return;
		}
		plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
			plugin._wantWatchedSyncing = true;
			try {
				await plugin._triggerJavdbReview(score);
				plugin._syncRatingBar();
			} finally {
				plugin._wantWatchedSyncing = false;
			}
		}).catch(() => {});
	}
	async function quickBlock(plugin) {
		const pageInfo = plugin.getPageInfo();
		if (!pageInfo.carNum) return;
		utils.q(null, jsxToString((0, import_jsx_runtime.jsx)(QuickBlockConfirmMessage, { carNum: pageInfo.carNum })), async () => {
			plugin._setRatingBusy(true);
			try {
				if (await storageManager.getCar(pageInfo.carNum)) await storageManager.removeCar(pageInfo.carNum);
				await storageManager.saveCar({
					carNum: pageInfo.carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: FILTER_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				plugin.broadcastWantWatchedSync({
					carNum: pageInfo.carNum,
					status: FILTER_ACTION,
					op: "add"
				});
				show.ok(`${pageInfo.carNum} 已拉黑`);
			} catch (err) {
				clog.error("[JHS-快键] 拉黑失败", err);
				show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
				return;
			}
			plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
				plugin._wantWatchedSyncing = true;
				try {
					await plugin._triggerJavdbReview(0);
					plugin._syncRatingBar();
				} finally {
					plugin._wantWatchedSyncing = false;
				}
			}).catch(() => {});
			plugin.showStatus(pageInfo.carNum).then();
			refresh();
			utils.closePage();
		});
	}
	async function quickConvertToFav(plugin) {
		const pageInfo = plugin.getPageInfo();
		if (!pageInfo.carNum) return;
		try {
			if (readServerReviewState().want) {
				show.ok(pageInfo.carNum + " 已是已收藏");
				return;
			}
			if (await storageManager.getCar(pageInfo.carNum)) await storageManager.removeCar(pageInfo.carNum);
			await storageManager.saveCar({
				carNum: pageInfo.carNum,
				url: pageInfo.url ?? void 0,
				names: pageInfo.actress ?? void 0,
				actionType: FAVORITE_ACTION,
				publishTime: pageInfo.publishTime ?? void 0
			});
			plugin.broadcastWantWatchedSync({
				carNum: pageInfo.carNum,
				status: FAVORITE_ACTION,
				op: "add"
			});
			show.ok(pageInfo.carNum + " 已转为收藏");
		} catch (err) {
			clog.error("[JHS-快键] 转为已收藏失败", err);
			show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
			return;
		}
		plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
			plugin._wantWatchedSyncing = true;
			try {
				await plugin._triggerJavdbWant();
				plugin._syncRatingBar();
			} finally {
				plugin._wantWatchedSyncing = false;
			}
		}).catch(() => {});
	}
	function getCsrfToken() {
		const meta = document.querySelector("meta[name=\"csrf-token\"]");
		return meta ? meta.content : null;
	}
	function getVideoId() {
		return location.pathname.match(/\/v\/([^/]+)/)?.[1] || null;
	}
	function getReviewId() {
		const del = document.querySelector(".review-buttons a[data-method='delete'][href*='/reviews/']");
		if (!del) return null;
		return del.getAttribute("href")?.match(/\/reviews\/(\d+)/)?.[1] || null;
	}
	function execRailsJs(jsText) {
		try {
			const script = document.createElement("script");
			script.textContent = jsText;
			document.head?.appendChild(script);
			script.remove();
		} catch (err) {
			clog.error("[JHS-快键] 执行 Rails JS 失败", err);
		}
	}
	async function javdbReviewApi(plugin, action, score = 0) {
		const token = getCsrfToken();
		if (!token) throw new Error("无法获取 CSRF token");
		const videoId = getVideoId();
		if (!videoId) throw new Error("无法获取 videoId");
		const reviewId = getReviewId();
		const headers = {
			"Content-Type": "application/x-www-form-urlencoded",
			"X-CSRF-Token": token
		};
		const tokenParam = `authenticity_token=${encodeURIComponent(token)}`;
		if (action === "watched") {
			const url = reviewId ? `/v/${videoId}/reviews/${reviewId}` : `/v/${videoId}/reviews`;
			const body = `${tokenParam}${reviewId ? "&_method=patch" : ""}&video_review[status]=watched&video_review[score]=${score}&video_review[content]=`;
			const res = await fetch(url, {
				method: "POST",
				headers,
				body,
				credentials: "same-origin"
			});
			if (!res.ok) throw new Error(`设为已观看失败: HTTP ${res.status}`);
			execRailsJs(await res.text());
		} else if (action === "wanted") {
			if (reviewId) {
				const delRes = await fetch(`/v/${videoId}/reviews/${reviewId}`, {
					method: "POST",
					headers,
					body: `${tokenParam}&_method=delete`,
					credentials: "same-origin"
				});
				if (!delRes.ok) throw new Error(`删除旧评价失败: HTTP ${delRes.status}`);
				execRailsJs(await delRes.text());
			}
			const res = await fetch(`/v/${videoId}/reviews/want_to_watch`, {
				method: "POST",
				headers,
				body: tokenParam,
				credentials: "same-origin"
			});
			if (!res.ok) throw new Error(`设为想看失败: HTTP ${res.status}`);
			execRailsJs(await res.text());
		}
		const rb = document.querySelector(".review-buttons");
		if (rb && plugin._wantWatchedObserved) plugin._lastWantState = plugin.detectWantWatchedState(rb);
	}
	async function triggerJavdbReview(plugin, score) {
		await javdbReviewApi(plugin, "watched", score);
	}
	async function triggerJavdbWant(plugin) {
		await javdbReviewApi(plugin, "wanted");
	}
	function addQuickActionButtons(plugin) {
		if (!window.isDetailPage) return;
		if (plugin._quickActionAdded) return;
		plugin._quickActionAdded = true;
		const self = plugin;
		plugin._injectRatingStyles();
		const ensure = () => {
			const nav = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav");
			if (!nav) {
				setTimeout(ensure, 400);
				return;
			}
			self._buildRatingBar(nav);
			self._syncRatingBar();
			self._ensureListPanel(nav);
			const rb = nav.querySelector(".review-buttons");
			if (rb && !rb.__jhsRatingObserved) {
				rb.__jhsRatingObserved = true;
				new MutationObserver(() => {
					if (self._wantWatchedSyncing) return;
					clearTimeout(self._ratingSyncDebounce ?? void 0);
					self._ratingSyncDebounce = setTimeout(() => {
						self._buildRatingBar(nav);
						self._syncRatingBar();
					}, 200);
				}).observe(rb, {
					childList: true,
					subtree: true
				});
			}
		};
		ensure();
	}
	function buildRatingBar(plugin, nav) {
		const column = nav.querySelector("div.review-buttons > div:nth-child(1) > div > div");
		if (!column) return;
		if (column.querySelector(".jhs-rating-bar")) return;
		const self = plugin;
		const bar = document.createElement("div");
		bar.className = "jhs-rating-bar";
		bar.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(RatingBarHtml, {}));
		const starsEl = bar.querySelector(".jhs-stars");
		const stars = bar.querySelectorAll(".jhs-star");
		const readBtn = bar.querySelector(".jhs-read-btn");
		const favBtn = bar.querySelector(".jhs-fav-btn");
		const blockBtn = bar.querySelector(".jhs-block-btn");
		if (!starsEl || !readBtn || !favBtn || !blockBtn) return;
		starsEl.addEventListener("pointerover", (e) => {
			const star = e.target.closest(".jhs-star");
			if (!star) return;
			const score = +star.dataset.score;
			stars.forEach((s, i) => s.classList.toggle("is-preview", i < score));
		});
		starsEl.addEventListener("pointerleave", () => stars.forEach((s) => s.classList.remove("is-preview")));
		stars.forEach((star) => {
			star.addEventListener("click", async (e) => {
				e.preventDefault();
				const score = +star.dataset.score;
				star.classList.add("is-popping");
				setTimeout(() => star.classList.remove("is-popping"), 300);
				self._setRatingBusy(true);
				try {
					await self.quickSetHasWatch(score);
				} finally {
					self._setRatingBusy(false);
					self.showStatus(self.getPageInfo().carNum).then();
				}
			});
		});
		readBtn.addEventListener("click", async (e) => {
			e.preventDefault();
			readBtn.classList.add("is-popping");
			setTimeout(() => readBtn.classList.remove("is-popping"), 300);
			self._setRatingBusy(true);
			try {
				await self.quickSetHasWatch(0);
			} finally {
				self._setRatingBusy(false);
				self.showStatus(self.getPageInfo().carNum).then();
			}
		});
		favBtn.addEventListener("click", async (e) => {
			e.preventDefault();
			favBtn.classList.add("is-popping");
			setTimeout(() => favBtn.classList.remove("is-popping"), 300);
			self._setRatingBusy(true);
			try {
				await self.quickConvertToFav();
			} finally {
				self._setRatingBusy(false);
				self.showStatus(self.getPageInfo().carNum).then();
			}
		});
		blockBtn.addEventListener("click", async (e) => {
			e.preventDefault();
			blockBtn.classList.add("is-popping");
			setTimeout(() => blockBtn.classList.remove("is-popping"), 300);
			await self.quickBlock();
		});
		column.insertBefore(bar, column.firstChild);
	}
	function injectRatingStyles() {
		if (document.getElementById("jhs-rating-styles")) return;
		const style = document.createElement("style");
		style.id = "jhs-rating-styles";
		style.textContent = rating_bar_default;
		document.head?.appendChild(style);
	}
	function syncRatingBar(plugin) {
		let bar = document.querySelector(".jhs-rating-bar");
		if (!bar) {
			const nav = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav");
			if (nav) plugin._buildRatingBar(nav);
			bar = document.querySelector(".jhs-rating-bar");
		}
		if (!bar) return;
		const rb = document.querySelector(".review-buttons");
		if (!rb) return;
		const want = !!rb.querySelector("a[href='/users/want_watch_videos'] .tag.is-info.is-light");
		const watched = !!rb.querySelector("a[href='/users/watched_videos'] .tag.is-success.is-light");
		const checked = rb.querySelector("input[name=\"video_review[score]\"][checked]");
		const score = checked ? +checked.value : 0;
		const stars = bar.querySelectorAll(".jhs-star");
		const starsEl = bar.querySelector(".jhs-stars");
		const readBtn = bar.querySelector(".jhs-read-btn");
		const favBtn = bar.querySelector(".jhs-fav-btn");
		const blockBtn = bar.querySelector(".jhs-block-btn");
		if (!starsEl || !readBtn || !favBtn || !blockBtn) return;
		stars.forEach((s) => s.classList.remove("is-active"));
		starsEl.classList.remove("is-disabled");
		readBtn.classList.remove("is-active");
		favBtn.classList.remove("is-active");
		blockBtn.classList.remove("is-active");
		if (want) favBtn.classList.add("is-active");
		else if (watched) {
			stars.forEach((s, i) => s.classList.toggle("is-active", i < score));
			readBtn.classList.toggle("is-active", score === 0);
		}
		const carNum = plugin.getPageInfo().carNum;
		if (carNum) storageManager.getCar(carNum).then((carRecord) => {
			if (carRecord && carRecord.status === "filter") blockBtn.classList.add("is-active");
		});
	}
	function setRatingBusy(busy) {
		const bar = document.querySelector(".jhs-rating-bar");
		if (bar) bar.classList.toggle("is-busy", busy);
	}
	var NATIVE_LIST_CONTAINER = "#modal-save-list [data-list-target=\"listContainer\"]";
	var SERIAL_CHANGE_GAP_MS = 220;
	async function loadListMergeConfig() {
		return buildMergeConfig(await storageManager.getSetting());
	}
	function cssEscape(value) {
		return typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/"/g, "\\\"");
	}
	function findNativeInput(listId) {
		const container = document.querySelector(NATIVE_LIST_CONTAINER);
		if (!container) return null;
		return container.querySelector(`input[type="checkbox"][data-list-id="${cssEscape(listId)}"]`);
	}
	function readNativeCount(input) {
		const matched = (input.closest("label")?.querySelector("span"))?.textContent?.match(/\d+/);
		return matched ? Number(matched[0]) : null;
	}
	function readItemMeta(element) {
		const input = element.querySelector("input[type=\"checkbox\"]");
		const listId = element.dataset.listId || input?.dataset.listId || "";
		if (!listId) return null;
		const matched = (element.querySelector(".jhs-list-item__count")?.textContent || "").match(/\d+/);
		return {
			name: element.dataset.listName || "",
			listId,
			count: matched ? Number(matched[0]) : null,
			checked: Boolean(input?.checked),
			disabled: Boolean(input?.disabled)
		};
	}
	function parseMemberRefs(groupElement) {
		try {
			const parsed = JSON.parse(groupElement.dataset.listMembers || "[]");
			return Array.isArray(parsed) ? parsed : [];
		} catch {
			return [];
		}
	}
	function liveMembers(groupElement) {
		const members = [];
		for (const ref of parseMemberRefs(groupElement)) {
			const input = findNativeInput(ref.id);
			if (!input) continue;
			members.push({
				listId: ref.id,
				name: ref.name,
				num: ref.num,
				count: readNativeCount(input),
				checked: input.checked,
				disabled: input.disabled
			});
		}
		return members;
	}
	function setNativeCheckbox(listId, checked) {
		const input = findNativeInput(listId);
		if (!input) return false;
		if (checked && input.disabled) return false;
		input.checked = checked;
		input.dispatchEvent(new Event("change", { bubbles: true }));
		return true;
	}
	function renderMemberDetail(groupElement, members) {
		const detail = groupElement.querySelector(".jhs-list-group__detail");
		if (!detail) return;
		detail.replaceChildren();
		for (const member of sortMembersByNum(members)) {
			const li = document.createElement("li");
			li.className = "jhs-list-group__member";
			li.dataset.listId = member.listId;
			const num = document.createElement("span");
			num.className = "jhs-list-group__member-num";
			num.textContent = member.num === 0 ? "★" : String(member.num);
			num.title = member.num === 0 ? "裸名清单(最优先)" : `序号 ${member.num}`;
			const name = document.createElement("span");
			name.className = "jhs-list-group__member-name";
			name.textContent = member.name;
			name.title = member.name;
			const count = document.createElement("span");
			count.className = "jhs-list-group__member-count";
			count.textContent = member.count === null ? "—" : String(member.count);
			li.append(num, name, count);
			if (member.checked) {
				const flag = document.createElement("span");
				flag.className = "jhs-list-group__member-flag is-checked";
				flag.textContent = "已含";
				li.append(flag);
			}
			if (isMemberFull(member)) {
				const flag = document.createElement("span");
				flag.className = "jhs-list-group__member-flag is-full";
				flag.textContent = "已满";
				li.append(flag);
			}
			detail.append(li);
		}
	}
	function refreshMergedRowDisplay(groupElement) {
		const members = liveMembers(groupElement);
		const checkbox = groupElement.querySelector(".jhs-list-item__label input[type=\"checkbox\"]");
		const countElement = groupElement.querySelector(".jhs-list-item__count");
		const statusElement = groupElement.querySelector(".jhs-list-item__status");
		const toggle = groupElement.querySelector(".jhs-list-group__toggle");
		const detail = groupElement.querySelector(".jhs-list-group__detail");
		if (!checkbox || !countElement || !statusElement) return;
		const state = computeGroupCheckState({
			base: groupElement.dataset.listGroup || "",
			members
		});
		checkbox.checked = state.checked;
		checkbox.indeterminate = state.indeterminate;
		const allKnown = members.length > 0 && members.every((m) => m.count !== null);
		const total = members.reduce((sum, m) => sum + (m.count ?? 0), 0);
		countElement.textContent = allKnown ? String(total) : "—";
		countElement.title = allKnown ? `组内共 ${total} 部影片` : "部分清单数量未知";
		if (members.length > 0 && members.every((m) => isMemberFull(m)) && !state.checked) {
			statusElement.hidden = false;
			statusElement.dataset.state = "full";
			statusElement.textContent = "全满";
		} else {
			statusElement.hidden = true;
			delete statusElement.dataset.state;
			statusElement.textContent = "";
		}
		if (toggle) toggle.textContent = `${Boolean(detail && !detail.hidden) ? "▾" : "▸"} ${members.length} 个清单`;
		renderMemberDetail(groupElement, members);
	}
	function buildMergedRow(group) {
		const element = document.createElement("div");
		element.className = "jhs-list-item jhs-list-group";
		element.dataset.listName = group.base;
		element.dataset.listGroup = group.base;
		const refs = sortMembersByNum(group.members).map((m) => ({
			id: m.listId,
			num: m.num,
			name: m.name
		}));
		element.dataset.listMembers = JSON.stringify(refs);
		const label = document.createElement("label");
		label.className = "jhs-list-item__label";
		const input = document.createElement("input");
		input.type = "checkbox";
		input.dataset.listGroup = group.base;
		const name = document.createElement("span");
		name.className = "jhs-list-item__name";
		name.textContent = group.base;
		name.title = group.base;
		const count = document.createElement("span");
		count.className = "jhs-list-item__count";
		const status = document.createElement("span");
		status.className = "jhs-list-item__status";
		status.setAttribute("aria-live", "polite");
		status.setAttribute("aria-atomic", "true");
		status.hidden = true;
		label.append(input, name, count, status);
		const bar = document.createElement("div");
		bar.className = "jhs-list-group__bar";
		const toggle = document.createElement("button");
		toggle.type = "button";
		toggle.className = "jhs-list-group__toggle";
		toggle.setAttribute("aria-expanded", "false");
		const reflow = document.createElement("button");
		reflow.type = "button";
		reflow.className = "jhs-list-group__reflow";
		reflow.textContent = "整理";
		reflow.title = "将当前影片收敛到组内最靠前的清单";
		bar.append(toggle, reflow);
		const detail = document.createElement("ul");
		detail.className = "jhs-list-group__detail";
		detail.hidden = true;
		element.append(label, bar, detail);
		refreshMergedRowDisplay(element);
		return element;
	}
	function buildListPanelRows(plugin, entries) {
		const config = plugin._listMergeConfig;
		const { groups, singleIndexes } = groupEntries(entries.map((entry) => readItemMeta(entry.element) ?? {
			name: "",
			listId: "",
			count: null,
			checked: false,
			disabled: false
		}), config);
		const rows = groups.map((group) => buildMergedRow(group));
		for (const index of singleIndexes) rows.push(entries[index].element);
		return rows;
	}
	function setGroupBusy(groupElement, busy) {
		const checkbox = groupElement.querySelector(".jhs-list-item__label input[type=\"checkbox\"]");
		const status = groupElement.querySelector(".jhs-list-item__status");
		if (checkbox) checkbox.disabled = busy;
		if (busy && status) {
			status.hidden = false;
			status.dataset.state = "busy";
			status.textContent = "处理中";
		}
	}
	async function addToGroup(plugin, panel, groupElement) {
		const base = groupElement.dataset.listGroup || "";
		const members = liveMembers(groupElement);
		if (computeGroupCheckState({
			base,
			members
		}).containing.length > 0) {
			refreshMergedRowDisplay(groupElement);
			plugin._showListPanelNotice(panel, "该影片已在此清单组中");
			return;
		}
		const target = pickTargetMember(members);
		if (target) {
			if (!setNativeCheckbox(target.listId, true)) plugin._showListPanelNotice(panel, `无法加入「${target.name}」,请稍后重试`);
			refreshMergedRowDisplay(groupElement);
			return;
		}
		groupElement.dataset.jhsBusy = "1";
		setGroupBusy(groupElement, true);
		const newName = nextListName({
			base,
			members
		});
		let result;
		try {
			result = await createList(newName);
		} catch {
			result = "failed";
		}
		delete groupElement.dataset.jhsBusy;
		setGroupBusy(groupElement, false);
		if (result === "created") plugin._scheduleListPanelSync(0);
		else {
			refreshMergedRowDisplay(groupElement);
			plugin._showListPanelNotice(panel, result === "unknown" ? `清单「${newName}」状态未知,请刷新确认` : "自动新建清单失败,请手动处理");
		}
	}
	async function removeFromGroup(plugin, panel, groupElement) {
		const containing = sortMembersByNum(liveMembers(groupElement).filter((m) => m.checked));
		if (containing.length === 0) {
			refreshMergedRowDisplay(groupElement);
			return;
		}
		groupElement.dataset.jhsBusy = "1";
		setGroupBusy(groupElement, true);
		const failed = [];
		for (const member of containing) {
			if (!setNativeCheckbox(member.listId, false)) failed.push(member.name);
			await sleep$1(SERIAL_CHANGE_GAP_MS);
		}
		delete groupElement.dataset.jhsBusy;
		plugin._scheduleListPanelSync(0);
		if (failed.length > 0) plugin._showListPanelNotice(panel, `以下清单移除失败:${failed.join("、")}`);
	}
	function forwardListGroupChange(plugin, panel, displayInput) {
		const groupElement = displayInput.closest(".jhs-list-group");
		if (!groupElement) return;
		if (groupElement.dataset.jhsBusy === "1") {
			refreshMergedRowDisplay(groupElement);
			return;
		}
		if (displayInput.checked) addToGroup(plugin, panel, groupElement);
		else removeFromGroup(plugin, panel, groupElement);
	}
	async function reflowGroup(plugin, panel, groupElement) {
		if (groupElement.dataset.jhsBusy === "1") return;
		const containing = sortMembersByNum(liveMembers(groupElement).filter((m) => m.checked));
		if (containing.length <= 1) {
			plugin._showListPanelNotice(panel, "当前影片在该清单组内无需整理");
			return;
		}
		const keep = containing.find((m) => !isMemberFull(m)) ?? containing[0];
		groupElement.dataset.jhsBusy = "1";
		setGroupBusy(groupElement, true);
		for (const member of containing) {
			if (member.listId === keep.listId) continue;
			setNativeCheckbox(member.listId, false);
			await sleep$1(SERIAL_CHANGE_GAP_MS);
		}
		delete groupElement.dataset.jhsBusy;
		plugin._scheduleListPanelSync(0);
		plugin._showListPanelNotice(panel, `已整理:当前影片仅保留在「${keep.name}」`);
	}
	function toggleGroupDetail(toggleButton) {
		const groupElement = toggleButton.closest(".jhs-list-group");
		const detail = groupElement?.querySelector(".jhs-list-group__detail");
		if (!groupElement || !detail) return;
		detail.hidden = !detail.hidden;
		toggleButton.setAttribute("aria-expanded", String(!detail.hidden));
		refreshMergedRowDisplay(groupElement);
	}
	function syncMergedRowsForListId(listId) {
		document.querySelectorAll(".jhs-list-group").forEach((groupElement) => {
			if (parseMemberRefs(groupElement).some((ref) => ref.id === listId)) refreshMergedRowDisplay(groupElement);
		});
	}
	function ListPanel() {
		return (0, import_jsx_runtime.jsxs)("section", {
			className: "jhs-list-panel",
			"aria-labelledby": "jhs-list-panel-title",
			children: [
				(0, import_jsx_runtime.jsxs)("header", {
					className: "jhs-list-panel__header",
					children: [(0, import_jsx_runtime.jsxs)("div", {
						className: "jhs-list-panel__heading",
						children: [(0, import_jsx_runtime.jsx)("h3", {
							id: "jhs-list-panel-title",
							className: "jhs-list-panel__title",
							children: "加入清单"
						}), (0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__summary",
							"aria-live": "polite",
							children: "正在读取…"
						})]
					}), (0, import_jsx_runtime.jsxs)("label", {
						className: "jhs-list-panel__sort",
						children: [(0, import_jsx_runtime.jsx)("span", { children: "排序" }), (0, import_jsx_runtime.jsxs)("select", {
							className: "jhs-list-panel__sort-select",
							"aria-label": "清单排序",
							disabled: true,
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "name-desc",
									children: "名称降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "name-asc",
									children: "名称升序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "count-desc",
									children: "数量降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "count-asc",
									children: "数量升序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "num-desc",
									children: "序号降序"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "num-asc",
									children: "序号升序"
								})
							]
						})]
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-list-panel__search-wrap",
					children: [
						(0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__search-icon",
							"aria-hidden": "true",
							children: "⌕"
						}),
						(0, import_jsx_runtime.jsx)("input", {
							className: "jhs-list-panel__search",
							type: "search",
							placeholder: "搜索清单",
							"aria-label": "搜索清单",
							autoComplete: "off",
							disabled: true
						}),
						(0, import_jsx_runtime.jsx)("button", {
							className: "jhs-list-panel__search-clear",
							type: "button",
							"aria-label": "清空清单搜索",
							title: "清空搜索",
							hidden: true,
							children: "×"
						})
					]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-list-panel__items",
					role: "group",
					"aria-label": "清单列表",
					"aria-busy": "true",
					children: [
						(0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__skeleton",
							"aria-hidden": "true"
						}),
						(0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__skeleton",
							"aria-hidden": "true"
						}),
						(0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__skeleton",
							"aria-hidden": "true"
						}),
						(0, import_jsx_runtime.jsx)("span", {
							className: "jhs-list-panel__skeleton",
							"aria-hidden": "true"
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("p", {
					className: "jhs-list-panel__empty",
					role: "status",
					hidden: true
				})
			]
		});
	}
	function ListPanelSkeletonSpan() {
		return (0, import_jsx_runtime.jsx)("span", {
			className: "jhs-list-panel__skeleton",
			"aria-hidden": "true"
		});
	}
	function createListPanelEntry(plugin, source) {
		const element = source.cloneNode(true);
		const label = element.matches("label") ? element : element.querySelector("label");
		const input = element.querySelector("input[type=\"checkbox\"]");
		const originalCount = label?.querySelector("span") || null;
		const listId = input?.dataset.listId || "";
		if (!label || !input || !listId) return null;
		input.removeAttribute("id");
		input.removeAttribute("name");
		input.removeAttribute("data-action");
		label.removeAttribute("for");
		const name = Array.from(label.childNodes).filter((node) => node !== input && node !== originalCount).map((node) => node.textContent || "").join(" ").replace(/\s+/g, " ").trim() || "未命名清单";
		const countMatch = originalCount?.textContent?.match(/\d+/);
		const count = countMatch ? Number(countMatch[0]) : null;
		const nameElement = document.createElement("span");
		nameElement.className = "jhs-list-item__name";
		nameElement.textContent = name;
		nameElement.title = name;
		const countElement = document.createElement("span");
		countElement.className = "jhs-list-item__count";
		countElement.textContent = count === null ? "—" : String(count);
		countElement.title = count === null ? "影片数量未知" : `清单内 ${count} 部影片`;
		countElement.setAttribute("aria-label", count === null ? "影片数量未知" : `${count} 部影片`);
		const statusElement = document.createElement("span");
		statusElement.className = "jhs-list-item__status";
		statusElement.setAttribute("aria-live", "polite");
		statusElement.setAttribute("aria-atomic", "true");
		statusElement.hidden = true;
		element.classList.add("jhs-list-item");
		element.dataset.listId = listId;
		element.dataset.listName = name;
		label.classList.add("jhs-list-item__label");
		label.replaceChildren(input, nameElement, countElement, statusElement);
		plugin._updateListPanelEntryState(input);
		return { element };
	}
	function showListPanelLoading(plugin, panel) {
		const items = panel.querySelector(".jhs-list-panel__items");
		const summary = panel.querySelector(".jhs-list-panel__summary");
		const empty = panel.querySelector(".jhs-list-panel__empty");
		const search = panel.querySelector(".jhs-list-panel__search");
		const sort = panel.querySelector(".jhs-list-panel__sort-select");
		const clear = panel.querySelector(".jhs-list-panel__search-clear");
		plugin._clearListPanelNotice(panel);
		if (summary) summary.textContent = "正在读取…";
		if (empty) empty.hidden = true;
		if (search) search.disabled = true;
		if (sort) sort.disabled = true;
		if (clear) clear.hidden = true;
		if (items) {
			if (items.getAttribute("aria-busy") === "true" && items.firstElementChild) return;
			items.setAttribute("aria-busy", "true");
			items.innerHTML = Array.from({ length: 4 }, () => jsxToString((0, import_jsx_runtime.jsx)(ListPanelSkeletonSpan, {}))).join("");
		}
	}
	function showListPanelError(plugin, panel, message) {
		const items = panel.querySelector(".jhs-list-panel__items");
		const summary = panel.querySelector(".jhs-list-panel__summary");
		const search = panel.querySelector(".jhs-list-panel__search");
		const sort = panel.querySelector(".jhs-list-panel__sort-select");
		const clear = panel.querySelector(".jhs-list-panel__search-clear");
		plugin._clearListPanelNotice(panel);
		if (summary) summary.textContent = "读取失败";
		if (search) search.disabled = true;
		if (sort) sort.disabled = true;
		if (clear) clear.hidden = true;
		if (!items) return;
		items.setAttribute("aria-busy", "false");
		items.replaceChildren();
		const state = document.createElement("div");
		state.className = "jhs-list-panel__error";
		state.setAttribute("role", "alert");
		const text = document.createElement("span");
		text.textContent = message;
		const retry = document.createElement("button");
		retry.type = "button";
		retry.className = "jhs-list-panel__retry";
		retry.dataset.listPanelRetry = "true";
		retry.textContent = "重新加载";
		state.append(text, retry);
		items.appendChild(state);
	}
	var LIST_PANEL_MAX_PAGES = 50;
	function loadAllListPages(plugin, panel, container) {
		if (plugin._listPanelPaginationPromise) return;
		const videoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
		if (!videoId) {
			container.dataset.jhsPagesStatus = "complete";
			plugin._scheduleListPanelSync(0);
			return;
		}
		container.dataset.jhsPagesStatus = "loading";
		const generation = plugin._listPanelPaginationGeneration;
		plugin._listPanelPaginationPromise = plugin._fetchAllListPageEntries(videoId).then((fetchedEntries) => {
			const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
			if (!currentContainer) throw new Error("原生清单容器已移除");
			if (generation !== plugin._listPanelPaginationGeneration || currentContainer !== container) return;
			const existingIds = new Set(Array.from(currentContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).map((input) => input.dataset.listId));
			const fragment = document.createDocumentFragment();
			for (const entry of fetchedEntries) {
				const listId = entry.querySelector("input[type=\"checkbox\"][data-list-id]")?.dataset.listId;
				if (!listId || existingIds.has(listId)) continue;
				existingIds.add(listId);
				fragment.appendChild(document.importNode(entry, true));
			}
			currentContainer.dataset.jhsPagesStatus = "complete";
			currentContainer.appendChild(fragment);
		}).catch((error) => {
			if (generation !== plugin._listPanelPaginationGeneration) return;
			const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
			if (currentContainer) currentContainer.dataset.jhsPagesStatus = "partial";
			console.warn("详情页清单分页聚合失败,将显示当前已加载内容", error);
			plugin._showListPanelNotice(panel, "部分清单未载入,排序与搜索仅覆盖已显示项");
		}).finally(() => {
			plugin._listPanelPaginationPromise = null;
			plugin._scheduleListPanelSync(0);
		});
	}
	async function fetchAllListPageEntries(videoId) {
		const entries = [];
		const visited = new Set();
		let url = new URL("/users/simple_lists", window.location.origin);
		url.searchParams.set("vid", videoId);
		while (url) {
			if (visited.size >= LIST_PANEL_MAX_PAGES) throw new Error(`清单分页超过 ${LIST_PANEL_MAX_PAGES} 页`);
			if (visited.has(url.href)) throw new Error("清单分页出现循环");
			visited.add(url.href);
			const page = await fetchSimpleListsPage(url.href, videoId);
			const listDocument = new DOMParser().parseFromString(page.entries, "text/html");
			for (const child of Array.from(listDocument.body.children)) if (child.querySelector("input[type=\"checkbox\"][data-list-id]")) entries.push(child);
			if (!page.nextUrl) {
				url = null;
				continue;
			}
			url = new URL(page.nextUrl);
		}
		return entries;
	}
	var DETAIL_LIST_COLLATOR = new Intl.Collator("zh-CN", {
		numeric: true,
		sensitivity: "base"
	});
	function readItemCount(item) {
		const matched = (item.querySelector(".jhs-list-item__count")?.textContent || "").match(/\d+/);
		return matched ? Number(matched[0]) : -1;
	}
	function readItemNum(item) {
		const matched = (item.dataset.listName || "").match(/(\d+)\s*$/);
		return matched ? Number(matched[1]) : -1;
	}
	var LIST_PANEL_INIT_TIMEOUT_MS = 1e4;
	var DETAIL_DOM_WAIT_TIMEOUT_MS = 15e3;
	function ensureListPanel(plugin, nav, startTime = Date.now()) {
		if (plugin._listPanelEnsured) return;
		const otherSite = nav.querySelector("#otherSiteBox");
		if (!otherSite) {
			if (Date.now() - startTime > DETAIL_DOM_WAIT_TIMEOUT_MS) clog.warn("[JHS] 清单面板:等待 #otherSiteBox 超时,回退挂载到 nav");
			else {
				setTimeout(() => ensureListPanel(plugin, nav, startTime), 400);
				return;
			}
		}
		plugin._listPanelEnsured = true;
		if (!nav.querySelector(".jhs-list-panel")) {
			if (otherSite) otherSite.insertAdjacentHTML("afterend", jsxToString((0, import_jsx_runtime.jsx)(ListPanel, {})));
			else nav.insertAdjacentHTML("beforeend", jsxToString((0, import_jsx_runtime.jsx)(ListPanel, {})));
		}
		const listPanel = nav.querySelector(".jhs-list-panel");
		if (!listPanel) return;
		const sortSelectInit = listPanel.querySelector(".jhs-list-panel__sort-select");
		if (sortSelectInit) sortSelectInit.value = plugin._listDisplayConfig.defaultSort;
		plugin._bindListPanelControls(listPanel);
		listPanel.__jhsRefresh = () => plugin._scheduleListPanelSync(0);
		plugin._initListPanel();
	}
	function bindListPanelControls(plugin, panel) {
		if (panel.__jhsListControlsBound) return;
		panel.__jhsListControlsBound = true;
		const search = panel.querySelector(".jhs-list-panel__search");
		const sort = panel.querySelector(".jhs-list-panel__sort-select");
		const clear = panel.querySelector(".jhs-list-panel__search-clear");
		search?.addEventListener("input", () => plugin._applyListPanelView(panel));
		sort?.addEventListener("change", () => plugin._applyListPanelView(panel));
		clear?.addEventListener("click", () => {
			if (!search) return;
			search.value = "";
			search.focus();
			plugin._applyListPanelView(panel);
		});
		panel.addEventListener("click", (event) => {
			const target = event.target;
			if (!(target instanceof Element) || !target.closest("[data-list-panel-retry]")) return;
			plugin._listPanelIniting = false;
			plugin._showListPanelLoading(panel);
			plugin._initListPanel(true);
		});
		panel.addEventListener("click", (event) => {
			const target = event.target;
			if (!(target instanceof Element)) return;
			const toggleButton = target.closest(".jhs-list-group__toggle");
			if (toggleButton) {
				toggleGroupDetail(toggleButton);
				return;
			}
			const reflowButton = target.closest(".jhs-list-group__reflow");
			if (reflowButton) {
				const groupElement = reflowButton.closest(".jhs-list-group");
				if (groupElement) reflowGroup(plugin, panel, groupElement);
			}
		});
		panel.addEventListener("change", (event) => {
			const displayInput = event.target;
			if (!(displayInput instanceof HTMLInputElement) || displayInput.type !== "checkbox") return;
			plugin._forwardListPanelChange(panel, displayInput);
		});
	}
	function forwardListPanelChange(plugin, panel, displayInput) {
		if (displayInput.dataset.listGroup) {
			forwardListGroupChange(plugin, panel, displayInput);
			return;
		}
		const checkedBeforeMutation = !displayInput.checked;
		const listId = displayInput.dataset.listId;
		const listContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
		const sourceInput = listId ? Array.from(listContainer?.querySelectorAll("input[type=\"checkbox\"][data-list-id]") || []).find((input) => input.dataset.listId === listId) ?? null : null;
		if (!sourceInput || sourceInput.disabled) {
			displayInput.checked = checkedBeforeMutation;
			plugin._applyListPanelView(panel);
			const message = "清单状态已变化,已恢复原选择,请稍后重试";
			plugin._showListPanelNotice(panel, message);
			show.error(message, { duration: 3e3 });
			plugin._scheduleListPanelSync(80);
			return;
		}
		sourceInput.checked = displayInput.checked;
		sourceInput.dispatchEvent(new Event("change", { bubbles: true }));
		plugin._applyListPanelView(panel);
	}
	function initListPanel(plugin, forceReload = false) {
		if (plugin._listPanelIniting) return;
		plugin._listPanelIniting = true;
		const startedAt = Date.now();
		const ensure = () => {
			const btn = document.querySelector("#save-list-button");
			const modal = document.querySelector("#modal-save-list");
			const panel = document.querySelector(".jhs-list-panel");
			if (!panel) return;
			if (!btn || !modal) {
				if (Date.now() - startedAt >= LIST_PANEL_INIT_TIMEOUT_MS) {
					plugin._listPanelIniting = false;
					plugin._showListPanelError(panel, "清单加载失败,请重试");
					return;
				}
				setTimeout(ensure, 400);
				return;
			}
			if (!plugin._listPanelObserver) {
				plugin._listPanelObserver = new MutationObserver((mutations) => {
					if (mutations.some((mutation) => mutation.type === "childList" && Array.from(mutation.removedNodes).some((node) => node instanceof Element && (node.matches("input[data-list-id]") || Boolean(node.querySelector("input[data-list-id]")))))) {
						plugin._listPanelPaginationGeneration++;
						const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
						if (currentContainer) delete currentContainer.dataset.jhsPagesStatus;
					}
					if (mutations.every((mutation) => mutation.type === "attributes" && mutation.target instanceof HTMLInputElement)) {
						for (const mutation of mutations) plugin._syncListPanelInputState(mutation.target);
						const currentPanel = document.querySelector(".jhs-list-panel");
						if (currentPanel) plugin._applyListPanelView(currentPanel);
						return;
					}
					plugin._scheduleListPanelSync();
				});
				plugin._listPanelObserver.observe(modal, {
					childList: true,
					subtree: true,
					characterData: true,
					attributes: true,
					attributeFilter: ["checked", "disabled"]
				});
			}
			plugin._startListPanelLoadTimeout(panel);
			if (forceReload && plugin._listAjaxTriggered) {
				btn.click();
				setTimeout(() => btn.click(), 200);
			} else if (!plugin._listAjaxTriggered) {
				plugin._listAjaxTriggered = true;
				btn.click();
			}
			setTimeout(() => plugin._scheduleListPanelSync(0), 500);
		};
		ensure();
	}
	function syncListPanelInputState(plugin, sourceInput) {
		const listId = sourceInput.dataset.listId;
		if (!listId) return;
		syncMergedRowsForListId(listId);
		const displayInput = Array.from(document.querySelectorAll(".jhs-list-panel input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId) || null;
		if (!displayInput) return;
		displayInput.checked = sourceInput.checked;
		displayInput.disabled = sourceInput.disabled;
		if (sourceInput.dataset.vltWasDisabled !== void 0) {
			displayInput.dataset.vltWasDisabled = sourceInput.dataset.vltWasDisabled;
			displayInput.setAttribute("aria-busy", "true");
		} else {
			delete displayInput.dataset.vltWasDisabled;
			displayInput.removeAttribute("aria-busy");
		}
		plugin._updateListPanelEntryState(displayInput);
	}
	function updateListPanelEntryState(input) {
		const label = input.closest(".jhs-list-item__label");
		const status = label?.querySelector(".jhs-list-item__status");
		if (!label || !status) return;
		const countMatch = (label.querySelector(".jhs-list-item__count")?.textContent || "").match(/\d+/);
		const count = countMatch ? Number(countMatch[0]) : null;
		const state = input.dataset.vltWasDisabled !== void 0 ? "busy" : input.disabled && count !== null && count >= 501 && !input.checked ? "full" : input.disabled ? "unavailable" : null;
		label.removeAttribute("title");
		status.hidden = state === null;
		if (state === null) {
			status.textContent = "";
			delete status.dataset.state;
		} else {
			status.dataset.state = state;
			status.textContent = state === "busy" ? "同步中" : state === "full" ? "已满" : "不可用";
		}
	}
	function scheduleListPanelSync(plugin, delay = 48) {
		if (plugin._listPanelSyncTimer !== null) window.clearTimeout(plugin._listPanelSyncTimer);
		plugin._listPanelSyncTimer = window.setTimeout(() => {
			plugin._listPanelSyncTimer = null;
			plugin._renderListPanel();
		}, delay);
	}
	function renderListPanel(plugin) {
		const panel = document.querySelector(".jhs-list-panel");
		const listContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
		const itemsContainer = panel?.querySelector(".jhs-list-panel__items");
		if (!panel || !itemsContainer) return;
		if (!plugin._isListContainerLoaded(listContainer)) {
			plugin._showListPanelLoading(panel);
			plugin._startListPanelLoadTimeout(panel);
			return;
		}
		const nativeContainer = listContainer;
		const pagesStatus = nativeContainer.dataset.jhsPagesStatus;
		if (!pagesStatus || pagesStatus === "loading") {
			plugin._showListPanelLoading(panel);
			plugin._loadAllListPages(panel, nativeContainer);
			return;
		}
		panel.dataset.listCoverage = pagesStatus;
		const activeElement = document.activeElement;
		const activeListId = activeElement instanceof HTMLInputElement && itemsContainer.contains(activeElement) ? activeElement.dataset.listId : void 0;
		const entries = Array.from(listContainer.children).filter((child) => !/[预預][设設]清[单單]/.test(child.textContent || "")).map((child) => plugin._createListPanelEntry(child)).filter((entry) => entry !== null);
		itemsContainer.replaceChildren(...buildListPanelRows(plugin, entries));
		itemsContainer.setAttribute("aria-busy", "false");
		plugin._clearListPanelLoadTimeout();
		plugin._listPanelIniting = false;
		const search = panel.querySelector(".jhs-list-panel__search");
		const sort = panel.querySelector(".jhs-list-panel__sort-select");
		if (search) search.disabled = false;
		if (sort) sort.disabled = false;
		plugin._applyListPanelView(panel);
		if (activeListId) Array.from(itemsContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === activeListId)?.focus({ preventScroll: true });
	}
	function applyListPanelView(panel) {
		const itemsContainer = panel.querySelector(".jhs-list-panel__items");
		const sortSelect = panel.querySelector(".jhs-list-panel__sort-select");
		const searchInput = panel.querySelector(".jhs-list-panel__search");
		const clearButton = panel.querySelector(".jhs-list-panel__search-clear");
		const summary = panel.querySelector(".jhs-list-panel__summary");
		const empty = panel.querySelector(".jhs-list-panel__empty");
		if (!itemsContainer || !sortSelect || !searchInput || !summary || !empty) return;
		const sortValue = sortSelect.value || "name-desc";
		const dashIndex = sortValue.lastIndexOf("-");
		const field = dashIndex > 0 ? sortValue.slice(0, dashIndex) : "name";
		const direction = sortValue.slice(dashIndex + 1) === "asc" ? 1 : -1;
		const items = Array.from(itemsContainer.querySelectorAll(".jhs-list-item"));
		items.sort((left, right) => {
			let primary;
			if (field === "count") primary = readItemCount(left) - readItemCount(right);
			else if (field === "num") primary = readItemNum(left) - readItemNum(right);
			else primary = DETAIL_LIST_COLLATOR.compare(left.dataset.listName || "", right.dataset.listName || "");
			if (primary !== 0) return primary * direction;
			const byName = DETAIL_LIST_COLLATOR.compare(left.dataset.listName || "", right.dataset.listName || "");
			if (byName !== 0) return byName;
			return DETAIL_LIST_COLLATOR.compare(left.dataset.listId || "", right.dataset.listId || "");
		});
		itemsContainer.append(...items);
		const query = searchInput.value.trim().toLocaleLowerCase();
		let visibleCount = 0;
		let selectedCount = 0;
		for (const item of items) {
			if (item.querySelector("input[type=\"checkbox\"]")?.checked) selectedCount++;
			const visible = !query || (item.dataset.listName || "").toLocaleLowerCase().includes(query);
			item.hidden = !visible;
			if (visible) visibleCount++;
		}
		if (clearButton) clearButton.hidden = query.length === 0;
		const coverageSuffix = panel.dataset.listCoverage === "partial" ? " · 部分清单" : "";
		const summaryText = query ? `已加入 ${selectedCount} · 显示 ${visibleCount}/${items.length}${coverageSuffix}` : `已加入 ${selectedCount} / ${items.length}${coverageSuffix}`;
		summary.textContent = panel.__jhsNotice || summaryText;
		summary.classList.toggle("is-error", Boolean(panel.__jhsNotice));
		empty.hidden = visibleCount > 0;
		empty.textContent = items.length === 0 ? "还没有自建清单,可在下方创建。" : `没有找到"${searchInput.value.trim()}"`;
	}
	function isListContainerLoaded(container) {
		return Boolean(container?.querySelector("input[type=\"checkbox\"][data-list-id]"));
	}
	function startListPanelLoadTimeout(plugin, panel) {
		if (plugin._listPanelLoadTimer !== null) return;
		plugin._listPanelLoadTimer = window.setTimeout(() => {
			plugin._listPanelLoadTimer = null;
			const container = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
			if (plugin._isListContainerLoaded(container)) return;
			plugin._listPanelIniting = false;
			plugin._showListPanelError(panel, "清单内容未能载入,请重试");
		}, LIST_PANEL_INIT_TIMEOUT_MS);
	}
	function clearListPanelLoadTimeout(plugin) {
		if (plugin._listPanelLoadTimer === null) return;
		window.clearTimeout(plugin._listPanelLoadTimer);
		plugin._listPanelLoadTimer = null;
	}
	function showListPanelNotice(plugin, panel, message) {
		if (panel.__jhsNoticeTimer !== void 0) window.clearTimeout(panel.__jhsNoticeTimer);
		panel.__jhsNotice = message;
		plugin._applyListPanelView(panel);
		panel.__jhsNoticeTimer = window.setTimeout(() => {
			delete panel.__jhsNotice;
			delete panel.__jhsNoticeTimer;
			plugin._applyListPanelView(panel);
		}, 3e3);
	}
	function clearListPanelNotice(panel) {
		if (panel.__jhsNoticeTimer !== void 0) window.clearTimeout(panel.__jhsNoticeTimer);
		delete panel.__jhsNotice;
		delete panel.__jhsNoticeTimer;
		panel.querySelector(".jhs-list-panel__summary")?.classList.remove("is-error");
	}
	function SubtitleActionCell() {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("a", {
				className: "a-primary",
				children: "预览"
			}),
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				className: "a-success",
				children: "下载"
			})
		] });
	}
	function SubtitleLine({ paddedNum, line }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("span", {
				style: { color: "#AAA" },
				children: `${paddedNum}. `
			}),
			line,
			"\n"
		] });
	}
	function SubtitlePreviewDialog({ content }) {
		return (0, import_jsx_runtime.jsx)("div", {
			style: {
				padding: "15px 5px",
				background: "#1E1E1E",
				color: "#FFF",
				fontFamily: "Consolas,Monaco,monospace",
				whiteSpace: "pre-wrap",
				overflow: "auto",
				height: "100%"
			},
			dangerouslySetInnerHTML: { __html: content }
		});
	}
	function SubtitleTableDialog() {
		return (0, import_jsx_runtime.jsx)("div", {
			style: {
				height: "100%",
				overflow: "hidden"
			},
			children: (0, import_jsx_runtime.jsx)("div", {
				id: "xunlei-table-container",
				style: {
					height: "100%",
					paddingBottom: "20px"
				}
			})
		});
	}
	async function searchXunLeiSubtitle(plugin, carNum) {
		try {
			await withLoading(async () => {
				const subtitleList = (await gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${carNum}`)).data;
				if (subtitleList && subtitleList.length !== 0) layer.open({
					type: 1,
					title: "迅雷字幕",
					content: jsxToString((0, import_jsx_runtime.jsx)(SubtitleTableDialog, {})),
					scrollbar: false,
					area: utils.getResponsiveArea(["60%", "70%"]),
					anim: -1,
					success: (_layero, index) => {
						new Tabulator("#xunlei-table-container", createTabulatorOptions([
							{
								title: "文件名",
								field: "name",
								headerSort: false,
								responsive: 0
							},
							{
								title: "类型",
								field: "ext",
								headerSort: false,
								responsive: 0
							},
							{
								title: "操作",
								responsive: 0,
								headerSort: false,
								formatter: (cell, _formatterParams, onRendered) => {
									const rowData = cell.getData();
									onRendered(() => {
										const previewBtn = cell.getElement().querySelector(".a-primary");
										const downloadBtn = cell.getElement().querySelector(".a-success");
										if (previewBtn) previewBtn.addEventListener("click", async (_event) => {
											const subUrl = rowData.url;
											const subFilename = carNum + "." + rowData.ext;
											plugin.previewSubtitle(subUrl, subFilename);
										});
										if (downloadBtn) downloadBtn.addEventListener("click", async (_event) => {
											const subUrl = rowData.url;
											const subFilename = carNum + "." + rowData.ext;
											const content = await gmHttp.get(subUrl);
											utils.download(content, subFilename);
										});
									});
									return jsxToString((0, import_jsx_runtime.jsx)(SubtitleActionCell, {}));
								}
							}
						], subtitleList));
						utils.setupEscClose(index);
					}
				});
				else show.error("迅雷中找不到相关字幕!");
			});
		} catch (err) {
			clog.error(err);
			show.error("字幕搜索失败: " + (err instanceof Error ? err.message : String(err)));
		}
	}
	async function previewSubtitle(url, filename) {
		if (!url) {
			clog.error("未提供文件URL");
			return;
		}
		const ext = url.split(".").pop().toLowerCase();
		if (ext === "ass" || ext === "srt") try {
			const subtitleContent = await gmHttp.get(url);
			let title = "字幕预览";
			if (ext === "ass") title = "ASS字幕预览 - " + filename;
			else if (ext === "srt") title = "SRT字幕预览 - " + filename;
			const lines = subtitleContent.split("\n");
			let output = "";
			const numWidth = String(lines.length).length;
			lines.forEach((line, idx) => {
				const paddedNum = String(idx + 1).padStart(numWidth, " ");
				output += jsxToString((0, import_jsx_runtime.jsx)(SubtitleLine, {
					paddedNum,
					line
				}));
			});
			const htmlContent = output;
			layer.open({
				type: 1,
				title,
				area: ["80%", "80%"],
				scrollbar: false,
				content: jsxToString((0, import_jsx_runtime.jsx)(SubtitlePreviewDialog, { content: htmlContent })),
				btn: ["下载", "关闭"],
				btn1: function(_index, _layero, _instance) {
					utils.download(subtitleContent, filename);
					return false;
				}
			});
		} catch (err) {
			show.error(`预览失败: ${err instanceof Error ? err.message : String(err)}`);
			clog.error("预览字幕文件出错:", err);
		}
		else show.error("仅支持预览ASS和SRT字幕文件");
	}
	function waitForDomChange(selector, ms) {
		return new Promise((resolve) => {
			const start = Date.now();
			const el = document.querySelector(selector);
			if (!el) {
				resolve(null);
				return;
			}
			const before = el.innerHTML;
			const check = () => {
				const cur = document.querySelector(selector);
				if (!cur) {
					resolve(null);
					return;
				}
				if (cur.innerHTML !== before) {
					resolve(cur);
					return;
				}
				if (Date.now() - start > ms) {
					resolve(cur);
					return;
				}
				setTimeout(check, 200);
			};
			setTimeout(check, 200);
		});
	}
	function waitForEl(fn, ms) {
		return new Promise((resolve) => {
			const start = Date.now();
			const check = () => {
				const el = fn();
				if (el) {
					resolve(el);
					return;
				}
				if (Date.now() - start > ms) {
					resolve(null);
					return;
				}
				setTimeout(check, 150);
			};
			check();
		});
	}
	var wrapperStyle$1 = {
		margin: "10px auto",
		display: "flex",
		justifyContent: "space-between",
		alignItems: "center",
		flexWrap: "wrap",
		gap: "20px"
	};
	var rowStyle = {
		display: "flex",
		gap: "10px",
		flexWrap: "wrap"
	};
	function statusBtnStyle(width, backgroundColor) {
		return {
			width,
			backgroundColor,
			color: "white",
			textAlign: "center",
			padding: "8px 0"
		};
	}
	function DetailMenuButtons({ filterText, filterColor, favoriteText, favoriteColor, watchedText, watchedColor, showMagnetSearch }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: wrapperStyle$1,
			children: [(0, import_jsx_runtime.jsxs)("div", {
				className: "jhs-menu-status-row",
				style: rowStyle,
				children: [
					(0, import_jsx_runtime.jsx)("a", {
						id: "filterBtn",
						className: "jhs-toolbar-menu-btn",
						style: statusBtnStyle("120px", filterColor),
						children: (0, import_jsx_runtime.jsx)("span", { children: filterText })
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "favoriteBtn",
						className: "jhs-toolbar-menu-btn",
						style: statusBtnStyle("120px", favoriteColor),
						children: (0, import_jsx_runtime.jsx)("span", { children: favoriteText })
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "hasWatchBtn",
						className: "jhs-toolbar-menu-btn",
						style: statusBtnStyle("120px", watchedColor),
						children: (0, import_jsx_runtime.jsx)("span", { children: watchedText })
					})
				]
			}), (0, import_jsx_runtime.jsxs)("div", {
				className: "jhs-menu-tools-row",
				style: rowStyle,
				children: [
					(0, import_jsx_runtime.jsx)("a", {
						id: "enable-magnets-filter",
						className: "jhs-toolbar-menu-btn",
						style: {
							width: "140px",
							backgroundColor: "#c2bd4c",
							color: "white",
							textAlign: "center",
							padding: "8px 0"
						},
						children: (0, import_jsx_runtime.jsx)("span", {
							id: "magnets-span",
							children: "关闭磁力过滤"
						})
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "xunLeiSubtitleBtn",
						className: "jhs-toolbar-menu-btn",
						style: {
							width: "120px",
							background: "linear-gradient(to left, #375f7c, #2196F3)",
							color: "white",
							textAlign: "center",
							padding: "8px 0"
						},
						children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (迅雷)" })
					}),
					(0, import_jsx_runtime.jsx)("a", {
						id: "search-subtitle-btn",
						className: "jhs-toolbar-menu-btn",
						style: {
							width: "160px",
							background: "linear-gradient(to bottom, #8d5656, rgb(196,159,91))",
							color: "white",
							textAlign: "center",
							padding: "8px 0"
						},
						children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (SubTitleCat)" })
					}),
					showMagnetSearch && (0, import_jsx_runtime.jsx)("a", {
						id: "magnetSearchBtn",
						className: "jhs-toolbar-menu-btn",
						style: {
							width: "120px",
							background: "linear-gradient(to right, rgb(245,140,1), rgb(84,161,29))",
							color: "white",
							textAlign: "center",
							padding: "8px 0"
						},
						children: (0, import_jsx_runtime.jsx)("span", { children: "磁力搜索" })
					})
				]
			})]
		});
	}
	function MagnetHubMountBox() {
		return (0, import_jsx_runtime.jsx)("div", {
			id: "magnet-hub-mount",
			style: { padding: "10px" }
		});
	}
	var DetailPageButtonPlugin = class extends BasePlugin {
		_wantWatchedObserved = false;
		_lastWantState = null;
		_wantWatchedSyncing = false;
		_wantWatchedDebounce = null;
		_wantWatchedListenerInstalled = false;
		_quickActionAdded = false;
		_listPanelEnsured = false;
		_listPanelIniting = false;
		_listAjaxTriggered = false;
		_listPanelObserver = null;
		_listPanelSyncTimer = null;
		_listPanelLoadTimer = null;
		_listPanelPaginationPromise = null;
		_listPanelPaginationGeneration = 0;
		_listMergeConfig = {
			enabled: false,
			pattern: compileMergePattern(DEFAULT_LIST_MERGE_PATTERN),
			excludeNames: new Set()
		};
		_listDisplayConfig = { defaultSort: "name-desc" };
		_ratingSyncDebounce = null;
		_reviewChain = null;
		_autoSyncChecked = false;
		getName() {
			return "DetailPageButtonPlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		async handle() {
			this.hideVideoControls();
			if (window.isDetailPage) {
				try {
					this._listMergeConfig = await loadListMergeConfig();
				} catch (error) {
					clog.error("[JHS] 清单合并配置加载失败,已按关闭处理: " + (error instanceof Error ? error.message : String(error)));
				}
				try {
					const settings = await storageManager.getSetting();
					const validListSorts = [
						"name-desc",
						"name-asc",
						"count-desc",
						"count-asc",
						"num-desc",
						"num-asc"
					];
					this._listDisplayConfig = { defaultSort: validListSorts.includes(settings.listDefaultSort) ? settings.listDefaultSort : "name-desc" };
				} catch (error) {
					clog.error("[JHS] 清单显示配置加载失败,已按默认处理: " + (error instanceof Error ? error.message : String(error)));
				}
				this.createMenuBtn();
				this.hookWantAndWatchedButtons();
				this.addQuickActionButtons();
				this.setupWantWatchedSyncListener();
				try {
					const syncSettings = await storageManager.getSetting("autoServerSync", "yes");
					setupAutoReviewSync(this, syncSettings !== "no");
				} catch (error) {
					clog.error("[JHS] 评价状态自动校验配置加载失败,已按默认开启处理: " + (error instanceof Error ? error.message : String(error)));
				}
			}
		}
		async createMenuBtn() {
			const carNum = this.getPageInfo().carNum;
			const menuHtml = jsxToString((0, import_jsx_runtime.jsx)(DetailMenuButtons, {
				filterText: BLOCK_TEXT,
				filterColor: BLOCK_COLOR,
				favoriteText: FAVORITE_TEXT,
				favoriteColor: FAVORITE_COLOR,
				watchedText: WATCHED_TEXT,
				watchedColor: WATCHED_COLOR,
				showMagnetSearch: featureFlags.magnetHubPlugin
			}));
			if (isJavdbSite) $(".tabs").after(menuHtml);
			$("#favoriteBtn").on("click", () => this.favoriteOne());
			$("#filterBtn").on("click", (event) => this.filterOne(event));
			$("#hasWatchBtn").on("click", async () => this.hasWatchOne());
			const highlightPlugin = this.getBean("HighlightMagnetPlugin");
			const enableFilter = await storageManager.getSetting("enableMagnetsFilter", "yes");
			$("#magnets-span").text(enableFilter === "yes" ? "关闭磁力过滤" : "开启磁力过滤");
			if (enableFilter === "yes") highlightPlugin.doFilterMagnet();
			$("#enable-magnets-filter").on("click", (_event) => {
				const $span = $("#magnets-span");
				if ($span.text() === "关闭磁力过滤") {
					highlightPlugin.showAll();
					$span.text("开启磁力过滤");
					storageManager.saveSettingItem("enableMagnetsFilter", "no");
				} else {
					highlightPlugin.doFilterMagnet();
					$span.text("关闭磁力过滤");
					storageManager.saveSettingItem("enableMagnetsFilter", "yes");
				}
			});
			$("#search-subtitle-btn").on("click", (event) => utils.openPage(`https://subtitlecat.com/index.php?search=${carNum}`, carNum, false, event));
			$("#xunLeiSubtitleBtn").on("click", () => this.searchXunLeiSubtitle(carNum));
			if (featureFlags.magnetHubPlugin) $("#magnetSearchBtn").on("click", () => {
				const hub = this.getBean("MagnetHubPlugin");
				if (!hub) return;
				layer.open({
					type: 1,
					title: `磁力搜索 - ${carNum}`,
					content: jsxToString((0, import_jsx_runtime.jsx)(MagnetHubMountBox, {})),
					area: utils.getResponsiveArea(["80%", "80%"]),
					success: () => {
						$("#magnet-hub-mount").append(hub.createMagnetHub(carNum ?? ""));
					}
				});
			});
			this.showStatus(carNum).then();
		}
		async showStatus(carNum) {
			const $filterSpan = $("#filterBtn span");
			const $favoriteSpan = $("#favoriteBtn span");
			const $hasWatchSpan = $("#hasWatchBtn span");
			$filterSpan.text(BLOCK_TEXT);
			$favoriteSpan.text(FAVORITE_TEXT);
			$hasWatchSpan.text(WATCHED_TEXT);
			const carRecord = await storageManager.getCar(carNum);
			if (carRecord) switch (carRecord.status) {
				case FILTER_ACTION:
					$filterSpan.text(BLOCKED_TEXT);
					break;
				case FAVORITE_ACTION:
					$favoriteSpan.text(FAVORITED_TEXT);
					break;
				case HAS_WATCH_ACTION: $hasWatchSpan.text("🔍 已标记观看");
			}
		}
		hookWantAndWatchedButtons() {
			hookWantAndWatchedButtons(this);
		}
		detectWantWatchedState(container) {
			return detectWantWatchedState(container);
		}
		async onWantAdded() {
			return onWantAdded(this);
		}
		async onWantRemoved() {
			return onWantRemoved(this);
		}
		async onWatchedAdded() {
			return onWatchedAdded(this);
		}
		async onWatchedRemoved() {
			return onWatchedRemoved(this);
		}
		async removeCarIfStatus(carNum, status) {
			return removeCarIfStatus(carNum, status);
		}
		broadcastWantWatchedSync(payload) {
			broadcastWantWatchedSync(payload);
		}
		setupWantWatchedSyncListener() {
			setupWantWatchedSyncListener(this);
		}
		refreshItemStatusTag(carNum) {
			refreshItemStatusTag(this, carNum);
		}
		addQuickActionButtons() {
			addQuickActionButtons(this);
		}
		_buildRatingBar(nav) {
			buildRatingBar(this, nav);
		}
		_injectRatingStyles() {
			injectRatingStyles();
		}
		_syncRatingBar() {
			syncRatingBar(this);
		}
		_setRatingBusy(busy) {
			setRatingBusy(busy);
		}
		async quickSetHasWatch(score) {
			return quickSetHasWatch(this, score);
		}
		async quickBlock() {
			return quickBlock(this);
		}
		async quickConvertToFav() {
			return quickConvertToFav(this);
		}
		async _triggerJavdbReview(score) {
			return triggerJavdbReview(this, score);
		}
		async _triggerJavdbWant() {
			return triggerJavdbWant(this);
		}
		_ensureListPanel(nav) {
			ensureListPanel(this, nav);
		}
		_bindListPanelControls(panel) {
			bindListPanelControls(this, panel);
		}
		_forwardListPanelChange(panel, displayInput) {
			forwardListPanelChange(this, panel, displayInput);
		}
		_initListPanel(forceReload = false) {
			initListPanel(this, forceReload);
		}
		_syncListPanelInputState(sourceInput) {
			syncListPanelInputState(this, sourceInput);
		}
		_updateListPanelEntryState(input) {
			updateListPanelEntryState(input);
		}
		_scheduleListPanelSync(delay = 48) {
			scheduleListPanelSync(this, delay);
		}
		_renderListPanel() {
			renderListPanel(this);
		}
		_createListPanelEntry(source) {
			return createListPanelEntry(this, source);
		}
		_applyListPanelView(panel) {
			applyListPanelView(panel);
		}
		_showListPanelLoading(panel) {
			showListPanelLoading(this, panel);
		}
		_showListPanelError(panel, message) {
			showListPanelError(this, panel, message);
		}
		_isListContainerLoaded(container) {
			return isListContainerLoaded(container);
		}
		_startListPanelLoadTimeout(panel) {
			startListPanelLoadTimeout(this, panel);
		}
		_clearListPanelLoadTimeout() {
			clearListPanelLoadTimeout(this);
		}
		_loadAllListPages(panel, container) {
			loadAllListPages(this, panel, container);
		}
		async _fetchAllListPageEntries(videoId) {
			return fetchAllListPageEntries(videoId);
		}
		_showListPanelNotice(panel, message) {
			showListPanelNotice(this, panel, message);
		}
		_clearListPanelNotice(panel) {
			clearListPanelNotice(panel);
		}
		searchXunLeiSubtitle(carNum) {
			searchXunLeiSubtitle(this, carNum);
		}
		async previewSubtitle(url, filename) {
			return previewSubtitle(url, filename);
		}
		_waitForDomChange(selector, ms) {
			return waitForDomChange(selector, ms);
		}
		_waitForEl(fn, ms) {
			return waitForEl(fn, ms);
		}
		async favoriteOne() {
			const pageInfo = this.getPageInfo();
			await storageManager.saveCar({
				carNum: pageInfo.carNum,
				url: pageInfo.url ?? void 0,
				names: pageInfo.actress ?? void 0,
				actionType: FAVORITE_ACTION,
				publishTime: pageInfo.publishTime ?? void 0
			});
			const clr = this.getBean("CarListReaderPlugin");
			if (clr) await clr.waitForPendingPush();
			this.showStatus(pageInfo.carNum).then();
			refresh();
			utils.closePage();
		}
		async hasWatchOne() {
			const pageInfo = this.getPageInfo();
			await storageManager.saveCar({
				carNum: pageInfo.carNum,
				url: pageInfo.url ?? void 0,
				names: pageInfo.actress ?? void 0,
				actionType: HAS_WATCH_ACTION,
				publishTime: pageInfo.publishTime ?? void 0
			});
			const clr = this.getBean("CarListReaderPlugin");
			if (clr) await clr.waitForPendingPush();
			this.showStatus(pageInfo.carNum).then();
			refresh();
			utils.closePage();
		}
		async filterOne(event) {
			if (event) event.preventDefault();
			const pageInfo = this.getPageInfo();
			utils.q(event, `是否屏蔽${pageInfo.carNum}?`, async () => {
				await storageManager.saveCar({
					carNum: pageInfo.carNum,
					url: pageInfo.url ?? void 0,
					names: pageInfo.actress ?? void 0,
					actionType: FILTER_ACTION,
					publishTime: pageInfo.publishTime ?? void 0
				});
				const clr = this.getBean("CarListReaderPlugin");
				if (clr) await clr.waitForPendingPush();
				this.showStatus(pageInfo.carNum).then();
				refresh();
				utils.closePage();
			});
		}
		speedVideo() {
			if ($("#preview-video").is(":visible")) {
				const videoEl = document.getElementById("preview-video");
				if (videoEl) {
					videoEl.muted = false;
					videoEl.controls = false;
					if (videoEl.currentTime + 5 < videoEl.duration) videoEl.currentTime += 5;
					else {
						show.info("预览视频结束, 已回到开头");
						videoEl.currentTime = 1;
					}
				}
				return;
			}
			const $iframe = $("iframe[id^=\"layui-layer-iframe\"]");
			if ($iframe.length > 0) {
				$iframe[0].contentWindow?.postMessage("speedVideo", "*");
				return;
			}
			const $container = $(".preview-video-container");
			if ($container.length > 0) {
				$container[0].click();
				const videoEl = document.getElementById("preview-video");
				if (videoEl) {
					videoEl.currentTime += 5;
					videoEl.muted = false;
				}
			} else $("#javTrailersBtn").click();
		}
		hideVideoControls() {
			$(document).on("mouseenter", "#preview-video", function() {
				$(this).prop("controls", true);
			});
		}
	};
	var MAX_PAGES$1 = 200;
	async function fetchText(url) {
		const response = await fetch(url, { credentials: "same-origin" });
		if (!response.ok) throw new Error(`HTTP ${response.status}`);
		return response.text();
	}
	function absoluteNextUrl(doc, baseUrl) {
		const href = doc.querySelector("a.pagination-next")?.getAttribute("href");
		if (!href) return null;
		return new URL(href, baseUrl).href;
	}
	function extractListId(href) {
		const pathMatch = href.match(/\/lists\/([^/?#]+)/);
		if (pathMatch && pathMatch[1]) return pathMatch[1];
		try {
			return new URL(href, window.location.origin).searchParams.get("id") || null;
		} catch {
			return null;
		}
	}
	function getCurrentListId() {
		return window.location.pathname.match(/\/lists\/([^/?#]+)/)?.[1] ?? null;
	}
	async function fetchAllUserLists() {
		const lists = [];
		const seenIds = new Set();
		let url = new URL("/users/lists", window.location.origin).href;
		const visited = new Set();
		for (let page = 0; url && page < MAX_PAGES$1; page++) {
			if (visited.has(url)) break;
			visited.add(url);
			const html = await fetchText(url);
			const doc = new DOMParser().parseFromString(html, "text/html");
			doc.querySelectorAll("#lists > ul > li").forEach((li) => {
				const anchor = li.querySelector("div.column.is-10 > a") || li.querySelector("a");
				if (!anchor) return;
				const listId = extractListId(anchor.getAttribute("href") || "");
				if (!listId || listId === "users" || listId === "new" || seenIds.has(listId)) return;
				seenIds.add(listId);
				const text = (anchor.textContent || "").trim();
				const countMatch = text.match(/\((\d+)\)\s*$/);
				const count = countMatch ? Number(countMatch[1]) : null;
				const name = text.replace(/\s*\(\d+\)\s*$/, "").trim();
				if (name) lists.push({
					listId,
					name,
					count
				});
			});
			url = absoluteNextUrl(doc, url);
		}
		return lists;
	}
	function discoverGroup(currentListId, allLists, config) {
		const current = allLists.find((l) => l.listId === currentListId);
		if (!current) return null;
		const parsed = parseListName(current.name, config.pattern);
		if (!parsed || isMergeExcluded(current.name, parsed.base, config)) return null;
		const base = parsed.base;
		const members = [];
		for (const list of allLists) {
			if (list.listId === currentListId) {
				members.push({
					listId: list.listId,
					name: list.name,
					num: parsed.num,
					count: list.count
				});
				continue;
			}
			const p = parseListName(list.name, config.pattern);
			if (p && p.base === base && !isMergeExcluded(list.name, p.base, config)) members.push({
				listId: list.listId,
				name: list.name,
				num: p.num,
				count: list.count
			});
			else if (list.name.trim() === base && !isMergeExcluded(list.name, null, config)) members.push({
				listId: list.listId,
				name: list.name,
				num: 0,
				count: list.count
			});
		}
		members.sort((a, b) => a.num !== b.num ? a.num - b.num : a.listId < b.listId ? -1 : 1);
		return {
			base,
			members
		};
	}
	async function enumerateListVideos(listId) {
		const videoIds = [];
		const seen = new Set();
		let url = new URL(`/lists/${listId}`, window.location.origin).href;
		const visited = new Set();
		for (let page = 0; url && page < MAX_PAGES$1; page++) {
			if (visited.has(url)) break;
			visited.add(url);
			const html = await fetchText(url);
			const doc = new DOMParser().parseFromString(html, "text/html");
			doc.querySelectorAll(".movie-list .item").forEach((item) => {
				const matched = (item.querySelector("a[href*=\"/v/\"]")?.getAttribute("href") || "").match(/\/v\/([^/?#]+)/);
				if (!matched) return;
				const videoId = matched[1];
				if (!seen.has(videoId)) {
					seen.add(videoId);
					videoIds.push(videoId);
				}
			});
			url = absoluteNextUrl(doc, url);
		}
		return videoIds;
	}
	var JOURNAL_KEY = "jhs:reflow:journal";
	var ROLLBACK_PREFIX = "jhs:reflow:rollback:";
	function saveJournal(entry) {
		entry.updatedAt = Date.now();
		writePersistentValue("reflowJournal", "current", JOURNAL_KEY, entry);
	}
	function loadJournal() {
		return readPersistentValue("reflowJournal", "current", JOURNAL_KEY);
	}
	function clearJournal() {
		removePersistentValue("reflowJournal", "current", JOURNAL_KEY);
	}
	function hasResumableJournal() {
		const j = loadJournal();
		return j !== null && (j.status === "running" || j.status === "paused");
	}
	function appendRollback(sessionId, entry) {
		const key = ROLLBACK_PREFIX + sessionId;
		const list = readPersistentValue("reflowRollback", sessionId, key) ?? [];
		list.push(entry);
		writePersistentValue("reflowRollback", sessionId, key, list);
	}
	function loadRollback(sessionId) {
		return readPersistentValue("reflowRollback", sessionId, ROLLBACK_PREFIX + sessionId) ?? [];
	}
	function createSessionId() {
		return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
	}
	function sleep(ms) {
		return new Promise((resolve) => setTimeout(resolve, ms));
	}
	async function executeMove(move, nameOf) {
		const errors = [];
		let addedTo = null;
		if (move.addTo !== null) {
			const add = await postJavdbListMutation(move.videoId, move.addTo, true);
			if (add.kind === "rejected") {
				errors.push(`[${move.videoId}] 加入「${nameOf(move.addTo)}」被拒:${add.message}(跳过,不删来源)`);
				return {
					errors,
					rollback: null,
					cleanup: false
				};
			}
			if (add.kind === "unknown") {
				if (!(await fetchAuthoritativeCheckboxState(move.videoId, move.addTo))?.checked) {
					errors.push(`[${move.videoId}] 加入「${nameOf(move.addTo)}」未确认(跳过,不删来源)`);
					return {
						errors,
						rollback: null,
						cleanup: false
					};
				}
			}
			addedTo = move.addTo;
		}
		const removedFrom = [];
		let hasCleanup = false;
		for (const removeId of move.removeFrom) {
			const rm = await postJavdbListMutation(move.videoId, removeId, false);
			if (rm.kind === "confirmed") removedFrom.push(removeId);
			else if (rm.kind === "rejected") errors.push(`[${move.videoId}] 移除「${nameOf(removeId)}」被拒:${rm.message}`);
			else if ((await fetchAuthoritativeCheckboxState(move.videoId, removeId))?.checked) {
				errors.push(`[${move.videoId}] 移除「${nameOf(removeId)}」未确认(待清理)`);
				hasCleanup = true;
			} else removedFrom.push(removeId);
		}
		return {
			errors,
			rollback: addedTo !== null || removedFrom.length > 0 ? {
				videoId: move.videoId,
				addedTo,
				removedFrom
			} : null,
			cleanup: hasCleanup
		};
	}
	async function executeCompactionPlan(plan, options) {
		const { startIndex, count, signal, onProgress } = options;
		const nameOf = options.nameOf ?? ((id) => id);
		const baseInterval = options.interval;
		const batchSize = options.batchSize ?? 20;
		const maxRetry = options.maxRetry ?? 3;
		const sessionId = options.sessionId ?? createSessionId();
		const base = options.base ?? plan.base;
		const end = Math.min(plan.moves.length, startIndex + count);
		const errors = [];
		const pendingCleanup = [];
		let completed = 0;
		let failed = 0;
		let nextIndex = startIndex;
		let currentInterval = baseInterval;
		let backoffLevel = 0;
		let consecutiveFailures = 0;
		const journal = {
			sessionId,
			base,
			totalMoves: plan.moves.length,
			nextIndex: startIndex,
			completed: 0,
			failed: 0,
			createdAt: Date.now(),
			updatedAt: Date.now(),
			status: "running"
		};
		saveJournal(journal);
		for (let i = startIndex; i < end; i++) {
			if (signal.aborted) {
				journal.status = "aborted";
				journal.nextIndex = i;
				journal.completed = completed;
				journal.failed = failed;
				saveJournal(journal);
				return {
					status: "aborted",
					completed,
					failed,
					nextIndex: i,
					errors,
					sessionId,
					pendingCleanup
				};
			}
			if (consecutiveFailures >= maxRetry) {
				journal.status = "paused";
				journal.nextIndex = i;
				journal.completed = completed;
				journal.failed = failed;
				saveJournal(journal);
				errors.push(`连续失败 ${consecutiveFailures} 次,暂停执行(可从 ${i} 续传)`);
				return {
					status: "suspended",
					completed,
					failed,
					nextIndex: i,
					errors,
					sessionId,
					pendingCleanup
				};
			}
			const move = plan.moves[i];
			onProgress?.({
				done: i - startIndex,
				total: end - startIndex,
				videoId: move.videoId,
				currentInterval,
				backoffLevel
			});
			try {
				const result = await executeMove(move, nameOf);
				if (result.errors.length === 0) {
					completed++;
					consecutiveFailures = 0;
					if (backoffLevel > 0) {
						backoffLevel = Math.max(0, backoffLevel - 1);
						currentInterval = baseInterval * Math.pow(2, backoffLevel);
					}
				} else {
					failed++;
					consecutiveFailures++;
					errors.push(...result.errors);
					if (backoffLevel < 3) {
						backoffLevel++;
						currentInterval = baseInterval * Math.pow(2, backoffLevel);
					}
				}
				if (result.cleanup) pendingCleanup.push(move.videoId);
				if (result.rollback) appendRollback(sessionId, result.rollback);
			} catch (err) {
				failed++;
				consecutiveFailures++;
				errors.push(`[${move.videoId}] 异常:${err instanceof Error ? err.message : String(err)}`);
				if (backoffLevel < 3) {
					backoffLevel++;
					currentInterval = baseInterval * Math.pow(2, backoffLevel);
				}
			}
			nextIndex = i + 1;
			if ((i - startIndex + 1) % batchSize === 0 || i + 1 >= end) {
				journal.nextIndex = nextIndex;
				journal.completed = completed;
				journal.failed = failed;
				saveJournal(journal);
			}
			if (i + 1 < end && !signal.aborted) await sleep(currentInterval);
		}
		const status = nextIndex >= plan.moves.length ? "done" : "paused";
		journal.status = status === "done" ? "done" : "paused";
		journal.nextIndex = nextIndex;
		journal.completed = completed;
		journal.failed = failed;
		saveJournal(journal);
		if (status === "done") clearJournal();
		return {
			status,
			completed,
			failed,
			nextIndex,
			errors,
			sessionId,
			pendingCleanup
		};
	}
	async function undoSession(sessionId, options) {
		const rollbackLog = loadRollback(sessionId);
		const errors = [];
		let undone = 0;
		let failed = 0;
		for (let i = rollbackLog.length - 1; i >= 0; i--) {
			if (options.signal.aborted) break;
			const entry = rollbackLog[i];
			options.onProgress?.({
				done: rollbackLog.length - 1 - i,
				total: rollbackLog.length,
				videoId: entry.videoId
			});
			try {
				for (const listId of entry.removedFrom) await postJavdbListMutation(entry.videoId, listId, true);
				if (entry.addedTo) await postJavdbListMutation(entry.videoId, entry.addedTo, false);
				undone++;
			} catch (err) {
				failed++;
				errors.push(`[${entry.videoId}] 撤销失败:${err instanceof Error ? err.message : String(err)}`);
			}
			if (i > 0 && !options.signal.aborted) await sleep(options.interval);
		}
		return {
			undone,
			failed,
			errors
		};
	}
	function sortListsAsc(lists) {
		return [...lists].sort((a, b) => {
			if (a.num !== b.num) return a.num - b.num;
			return a.listId < b.listId ? -1 : a.listId > b.listId ? 1 : 0;
		});
	}
	function dedupePreserveOrder(ids) {
		const seen = new Set();
		const out = [];
		for (const id of ids) if (!seen.has(id)) {
			seen.add(id);
			out.push(id);
		}
		return out;
	}
	function planCompaction(base, lists) {
		const sorted = sortListsAsc(lists);
		const count = sorted.length;
		const dedupedVideos = sorted.map((list) => dedupePreserveOrder(list.videoIds));
		const membership = new Map();
		sorted.forEach((list, index) => {
			for (const videoId of dedupedVideos[index]) {
				const existing = membership.get(videoId);
				if (existing) existing.push(list.listId);
				else membership.set(videoId, [list.listId]);
			}
		});
		const uniqueVideos = Array.from(membership.keys());
		const totalUnique = uniqueVideos.length;
		const targets = new Array(count).fill(0);
		let remaining = totalUnique;
		for (let i = 0; i < count; i++) {
			const take = Math.min(501, remaining);
			targets[i] = take;
			remaining -= take;
		}
		const cap = [...targets];
		const final = new Map();
		const placed = new Set();
		for (let i = 0; i < count; i++) for (const videoId of dedupedVideos[i]) if (cap[i] > 0 && !placed.has(videoId)) {
			final.set(videoId, sorted[i].listId);
			placed.add(videoId);
			cap[i]--;
		}
		const overflow = [];
		for (const videoId of uniqueVideos) {
			if (placed.has(videoId)) continue;
			let assigned = false;
			for (let i = 0; i < count; i++) if (cap[i] > 0) {
				final.set(videoId, sorted[i].listId);
				placed.add(videoId);
				cap[i]--;
				assigned = true;
				break;
			}
			if (!assigned) overflow.push(videoId);
		}
		const moves = [];
		for (const [videoId, finalList] of final) {
			const current = membership.get(videoId) ?? [];
			const removeFrom = current.filter((id) => id !== finalList);
			const addTo = current.includes(finalList) ? null : finalList;
			if (removeFrom.length > 0 || addTo !== null) moves.push({
				videoId,
				removeFrom,
				addTo
			});
		}
		const duplicates = uniqueVideos.filter((videoId) => (membership.get(videoId)?.length ?? 0) > 1);
		const targetRows = sorted.map((list, i) => ({
			listId: list.listId,
			num: list.num,
			name: list.name,
			current: dedupedVideos[i].length,
			target: targets[i]
		}));
		const addCount = moves.filter((m) => m.addTo !== null).length;
		const removeCount = moves.reduce((sum, m) => sum + m.removeFrom.length, 0);
		return {
			base,
			totalUnique,
			duplicateCount: duplicates.length,
			duplicates,
			targets: targetRows,
			moves,
			addCount,
			removeCount,
			overflow
		};
	}
	var MOVE_PREVIEW_CAP = 100;
	var ID_PREVIEW_CAP = 50;
	function div(className, text) {
		const element = document.createElement("div");
		element.className = className;
		if (text !== void 0) element.textContent = text;
		return element;
	}
	function previewIds(ids, cap) {
		const head = ids.slice(0, cap).join("、");
		return ids.length > cap ? `${head}…` : head;
	}
	function renderPlan(planElement, plan) {
		planElement.replaceChildren();
		planElement.appendChild(div("jhs-reflow-plan__summary", `组内共 ${plan.totalUnique} 部(去重后)· 重复 ${plan.duplicateCount} 部 · 需搬运 ${plan.moves.length} 部(加入 ${plan.addCount} 次 / 移除 ${plan.removeCount} 次)`));
		planElement.appendChild(div("jhs-reflow-plan__targets", "目标分布:" + plan.targets.map((t) => `${t.name} ${t.current}→${t.target}`).join(" | ")));
		if (plan.overflow.length > 0) planElement.appendChild(div("jhs-reflow-plan__overflow", `⚠ 组总容量不足,${plan.overflow.length} 部无法安置(期2 可自动新建清单消化):` + previewIds(plan.overflow, ID_PREVIEW_CAP)));
		if (plan.duplicates.length > 0) planElement.appendChild(div("jhs-reflow-plan__dups", `⚠ 收藏错误审计——${plan.duplicateCount} 部在组内多个清单重复:` + previewIds(plan.duplicates, ID_PREVIEW_CAP)));
		const nameOf = new Map(plan.targets.map((t) => [t.listId, t.name]));
		const movesList = document.createElement("ul");
		movesList.className = "jhs-reflow-plan__moves";
		for (const move of plan.moves.slice(0, MOVE_PREVIEW_CAP)) {
			const li = document.createElement("li");
			li.className = "jhs-reflow-plan__move";
			const parts = [];
			if (move.removeFrom.length > 0) parts.push(`移出 ${move.removeFrom.map((id) => nameOf.get(id) ?? id).join("、")}`);
			if (move.addTo !== null) parts.push(`移入 ${nameOf.get(move.addTo) ?? move.addTo}`);
			li.textContent = `${move.videoId}:${parts.join(",")}`;
			movesList.appendChild(li);
		}
		if (plan.moves.length > MOVE_PREVIEW_CAP) {
			const more = document.createElement("li");
			more.className = "jhs-reflow-plan__more";
			more.textContent = `…及其他 ${plan.moves.length - MOVE_PREVIEW_CAP} 条`;
			movesList.appendChild(more);
		}
		planElement.appendChild(movesList);
		planElement.appendChild(div("jhs-reflow-plan__note", "期1 仅预览,不执行任何写入;搬运顺序优化与执行为期2 内容。"));
	}
	async function runPlanPreview(panel, group, setStatus) {
		const planElement = panel.querySelector("[data-reflow-plan]");
		if (!planElement) return null;
		planElement.hidden = false;
		planElement.replaceChildren(div("jhs-reflow-plan__summary", "正在枚举组内影片…"));
		const inputs = [];
		for (let i = 0; i < group.members.length; i++) {
			const member = group.members[i];
			setStatus(`正在枚举 ${member.name}(${i + 1}/${group.members.length})…`);
			const videoIds = await enumerateListVideos(member.listId);
			inputs.push({
				listId: member.listId,
				num: member.num,
				name: member.name,
				videoIds
			});
		}
		const plan = planCompaction(group.base, inputs);
		renderPlan(planElement, plan);
		setStatus(`计划已生成:需搬运 ${plan.moves.length} 部`, "is-ok");
		return plan;
	}
	function ReflowPanel() {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "jhs-reflow-panel",
			children: [
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-reflow-panel__header",
					children: [(0, import_jsx_runtime.jsx)("span", {
						className: "jhs-reflow-panel__title",
						children: "🗂️ 编号清单整理"
					}), (0, import_jsx_runtime.jsx)("span", {
						className: "jhs-reflow-panel__status",
						"data-reflow-status": true,
						children: "待识别"
					})]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "jhs-reflow-panel__lists",
					"data-reflow-lists": true,
					hidden: true
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "jhs-reflow-panel__plan",
					"data-reflow-plan": true,
					hidden: true
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-reflow-panel__progress",
					"data-reflow-progress": true,
					hidden: true,
					children: [(0, import_jsx_runtime.jsx)("div", {
						className: "jhs-reflow-panel__progress-bar",
						children: (0, import_jsx_runtime.jsx)("div", {
							className: "jhs-reflow-panel__progress-fill",
							"data-reflow-progress-fill": true
						})
					}), (0, import_jsx_runtime.jsx)("span", {
						className: "jhs-reflow-panel__progress-text",
						"data-reflow-progress-text": true
					})]
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "jhs-reflow-panel__actions",
					children: [
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__identify",
							"data-reflow-identify": true,
							children: "识别编号组"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__plan-btn",
							"data-reflow-plan-btn": true,
							hidden: true,
							children: "生成整理计划"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__exec-btn",
							"data-reflow-exec-btn": true,
							hidden: true,
							children: "执行整理"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__abort-btn",
							"data-reflow-abort-btn": true,
							hidden: true,
							children: "中止"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__resume-btn",
							"data-reflow-resume-btn": true,
							hidden: true,
							children: "继续上次整理"
						}),
						(0, import_jsx_runtime.jsx)("button", {
							type: "button",
							className: "jhs-reflow-panel__undo-btn",
							"data-reflow-undo-btn": true,
							hidden: true,
							children: "撤销本次整理"
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "jhs-reflow-panel__report",
					"data-reflow-report": true,
					hidden: true
				})
			]
		});
	}
	function toElement(html) {
		const template = document.createElement("template");
		template.innerHTML = html.trim();
		return template.content.firstElementChild;
	}
	function renderMemberRows(listsElement, group) {
		listsElement.replaceChildren();
		for (const member of group.members) {
			const row = document.createElement("div");
			row.className = "jhs-reflow-list";
			row.dataset.listId = member.listId;
			const num = document.createElement("span");
			num.className = "jhs-reflow-list__num";
			num.textContent = member.num === 0 ? "★" : String(member.num);
			num.title = member.num === 0 ? "裸名清单(压实最优先)" : `序号 ${member.num}`;
			const name = document.createElement("span");
			name.className = "jhs-reflow-list__name";
			name.textContent = member.name;
			name.title = member.name;
			const count = document.createElement("span");
			count.className = "jhs-reflow-list__count";
			count.textContent = member.count === null ? "—" : String(member.count);
			row.append(num, name, count);
			if (member.num === 0) {
				const flag = document.createElement("span");
				flag.className = "jhs-reflow-list__flag is-bare";
				flag.textContent = "裸名";
				row.append(flag);
			}
			if (member.count !== null && member.count >= 501) {
				const flag = document.createElement("span");
				flag.className = "jhs-reflow-list__flag is-full";
				flag.textContent = "已满";
				row.append(flag);
			}
			listsElement.append(row);
		}
	}
	function renderReport(reportElement, result) {
		reportElement.replaceChildren();
		reportElement.hidden = false;
		const lines = [];
		lines.push(`✓ 成功:${result.completed}`);
		if (result.failed > 0) lines.push(`✗ 失败:${result.failed}`);
		if (result.pendingCleanup.length > 0) lines.push(`⚠ 待清理重复:${result.pendingCleanup.length}(${result.pendingCleanup.join("、")})`);
		if (result.errors.length > 0) {
			lines.push("");
			lines.push("失败明细:");
			for (const err of result.errors.slice(0, 20)) lines.push(`  ${err}`);
			if (result.errors.length > 20) lines.push(`  …共 ${result.errors.length} 条`);
		}
		const pre = document.createElement("pre");
		pre.className = "jhs-reflow-panel__report-text";
		pre.textContent = lines.join("\n");
		reportElement.append(pre);
	}
	function updateProgress(progressElement, fillElement, textElement, done, total, backoffLevel) {
		progressElement.hidden = false;
		const pct = total > 0 ? Math.round(done / total * 100) : 0;
		fillElement.style.width = `${pct}%`;
		textElement.textContent = `${done}/${total} (${pct}%)${backoffLevel > 0 ? `(退避 ×${Math.pow(2, backoffLevel)})` : ""}`;
	}
	function initReflowPanel(config) {
		const currentListId = getCurrentListId();
		if (!currentListId) return;
		if (document.querySelector(".jhs-reflow-panel")) return;
		const panel = toElement(jsxToString((0, import_jsx_runtime.jsx)(ReflowPanel, {})));
		const anchor = document.querySelector(".movie-list");
		if (anchor) anchor.insertAdjacentElement("beforebegin", panel);
		else (document.querySelector("main") || document.body).prepend(panel);
		const statusElement = panel.querySelector("[data-reflow-status]");
		const listsElement = panel.querySelector("[data-reflow-lists]");
		const planElement = panel.querySelector("[data-reflow-plan]");
		const progressElement = panel.querySelector("[data-reflow-progress]");
		const progressFill = panel.querySelector("[data-reflow-progress-fill]");
		const progressText = panel.querySelector("[data-reflow-progress-text]");
		const reportElement = panel.querySelector("[data-reflow-report]");
		const identifyButton = panel.querySelector("[data-reflow-identify]");
		const planButton = panel.querySelector("[data-reflow-plan-btn]");
		const execButton = panel.querySelector("[data-reflow-exec-btn]");
		const abortButton = panel.querySelector("[data-reflow-abort-btn]");
		const resumeButton = panel.querySelector("[data-reflow-resume-btn]");
		const undoButton = panel.querySelector("[data-reflow-undo-btn]");
		if (!statusElement || !listsElement || !planElement || !progressElement || !progressFill || !progressText || !reportElement || !identifyButton || !planButton || !execButton || !abortButton || !resumeButton || !undoButton) return;
		const setStatus = (message, stateClass = "") => {
			statusElement.textContent = message;
			statusElement.className = "jhs-reflow-panel__status" + (stateClass ? ` ${stateClass}` : "");
		};
		let group = null;
		let plan = null;
		let execSignal = { aborted: false };
		let lastSessionId = null;
		if (hasResumableJournal()) {
			resumeButton.hidden = false;
			const journal = loadJournal();
			if (journal) setStatus(`检测到上次未完成的整理(${journal.base},已完成 ${journal.completed}/${journal.totalMoves})`);
		}
		identifyButton.addEventListener("click", () => {
			identifyButton.disabled = true;
			setStatus("正在识别编号组…");
			(async () => {
				try {
					const allLists = await fetchAllUserLists();
					clog.log(`[编号整理] /users/lists 识别到 ${allLists.length} 个清单`);
					group = discoverGroup(currentListId, allLists, config);
					if (!group) {
						clog.log(`[编号整理] 当前清单 ${currentListId} 未归入编号组(名称未匹配序号规则或被排除)`);
						listsElement.hidden = true;
						planButton.hidden = true;
						if (planElement) planElement.hidden = true;
						setStatus("此清单不属于编号组");
						return;
					}
					renderMemberRows(listsElement, group);
					listsElement.hidden = false;
					planButton.hidden = false;
					setStatus(`编号组「${group.base}」· ${group.members.length} 个清单`, "is-ok");
				} catch (error) {
					setStatus("识别失败:" + (error instanceof Error ? error.message : String(error)), "is-error");
				} finally {
					identifyButton.disabled = false;
				}
			})();
		});
		planButton.addEventListener("click", () => {
			if (!group) return;
			planButton.disabled = true;
			execButton.hidden = true;
			runPlanPreview(panel, group, setStatus).then((result) => {
				plan = result;
				if (plan && plan.moves.length > 0) execButton.hidden = false;
			}).catch((error) => {
				setStatus("生成计划失败:" + (error instanceof Error ? error.message : String(error)), "is-error");
			}).finally(() => {
				planButton.disabled = false;
			});
		});
		const finishExecution = (result) => {
			execButton.disabled = false;
			execButton.hidden = false;
			abortButton.hidden = true;
			resumeButton.hidden = true;
			if (result) {
				lastSessionId = result.sessionId;
				if (result.completed > 0) undoButton.hidden = false;
				renderReport(reportElement, result);
			}
		};
		const runExecution = async (thePlan, startIndex) => {
			execSignal = { aborted: false };
			execButton.disabled = true;
			execButton.hidden = true;
			abortButton.hidden = false;
			resumeButton.hidden = true;
			undoButton.hidden = true;
			reportElement.hidden = true;
			progressElement.hidden = false;
			const settings = await storageManager.getSetting();
			const interval = Number(settings.reflowInterval) || 400;
			const trialCount = Number(settings.reflowTrialCount) || 10;
			const batchSize = Number(settings.reflowBatchSize) || 20;
			const maxRetry = Number(settings.reflowMaxRetry) || 3;
			const nameOf = (id) => thePlan.targets.find((t) => t.listId === id)?.name ?? id;
			const onProgress = (p) => {
				updateProgress(progressElement, progressFill, progressText, p.done, p.total, p.backoffLevel);
				setStatus(`执行中 ${p.done + 1}/${p.total}:${p.videoId}`);
			};
			const trial = await executeCompactionPlan(thePlan, {
				startIndex,
				count: startIndex === 0 ? trialCount : Infinity,
				interval,
				signal: execSignal,
				onProgress,
				nameOf,
				batchSize,
				maxRetry,
				base: thePlan.base
			});
			if (trial.status === "aborted") {
				setStatus(`已中止(完成 ${trial.completed},失败 ${trial.failed})`, "is-error");
				finishExecution(trial);
				return;
			}
			if (trial.status === "suspended") {
				setStatus(`连续失败暂停(完成 ${trial.completed},失败 ${trial.failed})`, "is-error");
				resumeButton.hidden = false;
				finishExecution(trial);
				return;
			}
			if (trial.status === "done") {
				setStatus(`完成:成功 ${trial.completed},失败 ${trial.failed}`, trial.failed > 0 ? "is-error" : "is-ok");
				finishExecution(trial);
				return;
			}
			utils.q(null, `试运行完成:成功 ${trial.completed},失败 ${trial.failed}。继续剩余 ${thePlan.moves.length - trial.nextIndex} 个?`, () => {
				(async () => {
					const rest = await executeCompactionPlan(thePlan, {
						startIndex: trial.nextIndex,
						count: Infinity,
						interval,
						signal: execSignal,
						onProgress,
						nameOf,
						batchSize,
						maxRetry,
						base: thePlan.base,
						sessionId: trial.sessionId
					});
					const merged = {
						status: rest.status === "aborted" ? "aborted" : "done",
						completed: trial.completed + rest.completed,
						failed: trial.failed + rest.failed,
						nextIndex: rest.nextIndex,
						errors: [...trial.errors, ...rest.errors],
						sessionId: rest.sessionId,
						pendingCleanup: [...trial.pendingCleanup, ...rest.pendingCleanup]
					};
					setStatus(merged.status === "aborted" ? `已中止(完成 ${merged.completed},失败 ${merged.failed})` : `完成:成功 ${merged.completed},失败 ${merged.failed}`, merged.status === "aborted" || merged.failed > 0 ? "is-error" : "is-ok");
					finishExecution(merged);
				})();
			}, () => {
				setStatus(`试运行后取消(已完成 ${trial.completed})`);
				finishExecution(trial);
			});
		};
		execButton.addEventListener("click", () => {
			if (!plan || plan.moves.length === 0) return;
			utils.q(null, `将搬运 ${plan.moves.length} 部影片(先试运行,确认无误后继续)。确认开始?`, () => {
				runExecution(plan, 0);
			});
		});
		abortButton.addEventListener("click", () => {
			execSignal.aborted = true;
			setStatus("正在中止…");
		});
		resumeButton.addEventListener("click", () => {
			const journal = loadJournal();
			if (!journal) {
				setStatus("无可续传的会话");
				return;
			}
			if (!plan) {
				setStatus("请先生成整理计划再续传", "is-error");
				return;
			}
			utils.q(null, `检测到上次未完成的整理(已完成 ${journal.completed}/${journal.totalMoves}),从第 ${journal.nextIndex + 1} 步继续?`, () => {
				runExecution(plan, journal.nextIndex);
			});
		});
		undoButton.addEventListener("click", () => {
			if (!lastSessionId) return;
			utils.q(null, "确定撤销本次整理?将按回滚日志逆序执行反向搬运(同样先加后删 + 限速)。", () => {
				undoButton.disabled = true;
				setStatus("正在撤销…");
				execSignal = { aborted: false };
				abortButton.hidden = false;
				(async () => {
					const settings = await storageManager.getSetting();
					const interval = Number(settings.reflowInterval) || 400;
					const nameOf = (id) => plan?.targets.find((t) => t.listId === id)?.name ?? id;
					const result = await undoSession(lastSessionId, {
						interval,
						signal: execSignal,
						nameOf,
						onProgress: (p) => {
							setStatus(`撤销中 ${p.done + 1}/${p.total}:${p.videoId}`);
							updateProgress(progressElement, progressFill, progressText, p.done, p.total, 0);
						}
					});
					setStatus(`撤销完成:成功 ${result.undone},失败 ${result.failed}`, result.failed > 0 ? "is-error" : "is-ok");
					undoButton.disabled = false;
					undoButton.hidden = true;
					abortButton.hidden = true;
					clearJournal();
				})();
			});
		});
	}
	var list_reflow_default = "/* ── 清单页「编号清单整理」面板(doc/202,期1 只读) ─────────── */\n.jhs-reflow-panel {\n    --jhs-reflow-accent: #3273dc;\n    --jhs-reflow-border: #dfe4ea;\n    --jhs-reflow-muted: #5f6b7a;\n\n    display: block;\n    margin: 12px 0;\n    padding: 10px 12px;\n    border: 1px solid var(--jhs-reflow-border);\n    border-radius: 10px;\n    background: #fff;\n}\n\n.jhs-reflow-panel__header {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n    justify-content: space-between;\n}\n\n.jhs-reflow-panel__title {\n    color: #2f3540;\n    font-size: 14px;\n    font-weight: 700;\n}\n\n.jhs-reflow-panel__status {\n    padding: 2px 8px;\n    border-radius: 50rem;\n    background: #edf0f4;\n    color: var(--jhs-reflow-muted);\n    font-size: 11px;\n    font-weight: 700;\n    white-space: nowrap;\n}\n\n.jhs-reflow-panel__status.is-error {\n    background: #f8e2e2;\n    color: #8d2929;\n}\n\n.jhs-reflow-panel__status.is-ok {\n    background: #d9e7ff;\n    color: #234f96;\n}\n\n.jhs-reflow-panel__lists {\n    display: flex;\n    flex-direction: column;\n    gap: 3px;\n    margin-top: 10px;\n}\n\n.jhs-reflow-panel__lists[hidden],\n.jhs-reflow-panel__plan[hidden] {\n    display: none !important;\n}\n\n.jhs-reflow-list {\n    display: grid;\n    grid-template-columns: auto minmax(0, 1fr) auto auto;\n    gap: 7px;\n    align-items: center;\n    padding: 5px 8px;\n    border: 1px solid #eef1f5;\n    border-radius: 7px;\n    background: #fafbfc;\n    font-size: 12px;\n}\n\n.jhs-reflow-list__num {\n    min-width: 20px;\n    padding: 0 5px;\n    border-radius: 4px;\n    background: #edf0f4;\n    color: #4d5967;\n    font-size: 10px;\n    font-weight: 700;\n    text-align: center;\n}\n\n.jhs-reflow-list__name {\n    min-width: 0;\n    overflow: hidden;\n    color: #3d4652;\n    font-weight: 600;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n}\n\n.jhs-reflow-list__count {\n    padding: 1px 7px;\n    border-radius: 50rem;\n    background: #edf0f4;\n    color: #4d5967;\n    font-size: 10px;\n    font-variant-numeric: tabular-nums;\n    font-weight: 700;\n}\n\n.jhs-reflow-list__flag {\n    padding: 1px 6px;\n    border-radius: 50rem;\n    font-size: 10px;\n    font-weight: 700;\n    white-space: nowrap;\n}\n\n.jhs-reflow-list__flag.is-full {\n    background: #f8e2e2;\n    color: #8d2929;\n}\n\n.jhs-reflow-list__flag.is-bare {\n    background: #d9e7ff;\n    color: #234f96;\n}\n\n.jhs-reflow-panel__actions {\n    display: flex;\n    gap: 8px;\n    margin-top: 10px;\n}\n\n.jhs-reflow-panel__identify,\n.jhs-reflow-panel__plan-btn {\n    display: inline-flex;\n    align-items: center;\n    padding: 5px 12px;\n    border: 1px solid var(--jhs-reflow-accent);\n    border-radius: 7px;\n    background: #fff;\n    color: var(--jhs-reflow-accent);\n    cursor: pointer;\n    font-size: 12px;\n    font-weight: 700;\n    transition:\n        background-color 0.15s ease,\n        color 0.15s ease,\n        opacity 0.15s ease;\n}\n\n.jhs-reflow-panel__identify:hover,\n.jhs-reflow-panel__plan-btn:hover {\n    background: var(--jhs-reflow-accent);\n    color: #fff;\n}\n\n.jhs-reflow-panel__identify:disabled,\n.jhs-reflow-panel__plan-btn:disabled {\n    cursor: wait;\n    opacity: 0.55;\n}\n\n.jhs-reflow-panel__plan-btn[hidden] {\n    display: none !important;\n}\n\n.jhs-reflow-panel__plan {\n    margin-top: 10px;\n    padding: 10px;\n    border: 1px dashed #cdd5df;\n    border-radius: 8px;\n    background: #f8fafc;\n}\n\n.jhs-reflow-plan__summary {\n    color: #2f3540;\n    font-size: 13px;\n    font-weight: 700;\n    line-height: 1.5;\n}\n\n.jhs-reflow-plan__targets {\n    margin-top: 6px;\n    color: #3d4652;\n    font-size: 12px;\n    line-height: 1.6;\n}\n\n.jhs-reflow-plan__dups,\n.jhs-reflow-plan__overflow {\n    margin-top: 6px;\n    padding: 6px 8px;\n    border-radius: 6px;\n    background: #fff3cd;\n    color: #725500;\n    font-size: 12px;\n    line-height: 1.5;\n    overflow-wrap: anywhere;\n}\n\n.jhs-reflow-plan__overflow {\n    background: #f8e2e2;\n    color: #8d2929;\n}\n\n.jhs-reflow-plan__moves {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    max-height: 240px;\n    margin: 8px 0 0;\n    padding: 6px 8px;\n    border-radius: 6px;\n    overflow-y: auto;\n    background: #fff;\n    list-style: none;\n    font-size: 12px;\n}\n\n.jhs-reflow-plan__move {\n    color: #4d5967;\n    line-height: 1.5;\n}\n\n.jhs-reflow-plan__move-add {\n    color: #1f7a3d;\n    font-weight: 700;\n}\n\n.jhs-reflow-plan__move-remove {\n    color: #8d2929;\n    font-weight: 700;\n}\n\n.jhs-reflow-plan__more {\n    color: var(--jhs-reflow-muted);\n    font-weight: 700;\n}\n\n.jhs-reflow-plan__note {\n    margin: 8px 0 0;\n    color: var(--jhs-reflow-muted);\n    font-size: 11px;\n}\n\n/* ── 期2b/2c:执行按钮 + 进度条 + 报告区 ─────────── */\n\n.jhs-reflow-panel__exec-btn,\n.jhs-reflow-panel__abort-btn,\n.jhs-reflow-panel__resume-btn,\n.jhs-reflow-panel__undo-btn {\n    display: inline-flex;\n    align-items: center;\n    padding: 5px 12px;\n    border: 1px solid var(--jhs-reflow-accent);\n    border-radius: 7px;\n    background: #fff;\n    color: var(--jhs-reflow-accent);\n    cursor: pointer;\n    font-size: 12px;\n    font-weight: 700;\n    transition:\n        background-color 0.15s ease,\n        color 0.15s ease,\n        opacity 0.15s ease;\n}\n\n.jhs-reflow-panel__exec-btn:hover,\n.jhs-reflow-panel__resume-btn:hover {\n    background: var(--jhs-reflow-accent);\n    color: #fff;\n}\n\n.jhs-reflow-panel__abort-btn {\n    border-color: #f14668;\n    color: #f14668;\n}\n\n.jhs-reflow-panel__abort-btn:hover {\n    background: #f14668;\n    color: #fff;\n}\n\n.jhs-reflow-panel__undo-btn {\n    border-color: #ff9800;\n    color: #ff9800;\n}\n\n.jhs-reflow-panel__undo-btn:hover {\n    background: #ff9800;\n    color: #fff;\n}\n\n.jhs-reflow-panel__exec-btn:disabled,\n.jhs-reflow-panel__abort-btn:disabled,\n.jhs-reflow-panel__resume-btn:disabled,\n.jhs-reflow-panel__undo-btn:disabled {\n    cursor: wait;\n    opacity: 0.55;\n}\n\n.jhs-reflow-panel__exec-btn[hidden],\n.jhs-reflow-panel__abort-btn[hidden],\n.jhs-reflow-panel__resume-btn[hidden],\n.jhs-reflow-panel__undo-btn[hidden] {\n    display: none !important;\n}\n\n.jhs-reflow-panel__progress {\n    margin-top: 10px;\n}\n\n.jhs-reflow-panel__progress[hidden] {\n    display: none !important;\n}\n\n.jhs-reflow-panel__progress-bar {\n    height: 8px;\n    border-radius: 4px;\n    background: #e2e8f0;\n    overflow: hidden;\n}\n\n.jhs-reflow-panel__progress-fill {\n    height: 100%;\n    width: 0%;\n    border-radius: 4px;\n    background: #48c774;\n    transition: width 0.3s ease;\n}\n\n.jhs-reflow-panel__progress-text {\n    display: block;\n    margin-top: 4px;\n    color: var(--jhs-reflow-muted);\n    font-size: 11px;\n    font-variant-numeric: tabular-nums;\n}\n\n.jhs-reflow-panel__report {\n    margin-top: 10px;\n}\n\n.jhs-reflow-panel__report[hidden] {\n    display: none !important;\n}\n\n.jhs-reflow-panel__report-text {\n    max-height: 200px;\n    margin: 0;\n    padding: 10px;\n    border: 1px solid #e5e7eb;\n    border-radius: 6px;\n    overflow-y: auto;\n    background: #f9fafb;\n    color: #374151;\n    font-family: inherit;\n    font-size: 12px;\n    line-height: 1.6;\n    white-space: pre-wrap;\n    overflow-wrap: anywhere;\n}\n";
	var ListReflowPlugin = class extends BasePlugin {
		_reflowConfig = null;
		getName() {
			return "ListReflowPlugin";
		}
		get pageTypes() {
			return ["list"];
		}
		get sites() {
			return ["javdb"];
		}
		async initCss() {
			return list_reflow_default;
		}
		async handle() {
			if (!getCurrentListId()) return;
			const settings = await storageManager.getSetting();
			if (settings.listReflowEnabled === "no") return;
			this._reflowConfig = buildMergeConfig(settings);
			initReflowPanel(this._reflowConfig);
		}
	};
	function parsePositionStyle(positionStyle) {
		if (positionStyle.includes("right")) return {
			right: 0,
			top: "5px"
		};
		return {
			left: 0,
			top: "5px"
		};
	}
	function StatusTagHtml({ text, color, dataTip, positionStyle }) {
		const posStyle = parsePositionStyle(positionStyle);
		const spanStyle = {
			marginRight: "5px",
			borderRadius: "10px",
			position: "absolute",
			zIndex: 10,
			backgroundColor: `${color} !important`,
			...posStyle
		};
		return (0, import_jsx_runtime.jsx)("span", {
			className: "tag is-success status-tag",
			"data-tip": dataTip,
			title: "",
			style: spanStyle,
			children: text
		});
	}
	var STATUS_TAG_CONFIG = {
		IS_FILTERED: {
			text: BLOCKED_TEXT,
			color: BLOCK_COLOR,
			reasonType: "单番号屏蔽",
			isCounted: true,
			countKey: "currentPageFilterCount"
		},
		IS_FAVORITE: {
			text: FAVORITED_TEXT,
			color: FAVORITE_COLOR,
			reasonType: "",
			isCounted: true,
			countKey: "currentPageFavoriteCount"
		},
		IS_HAS_WATCH: {
			text: WATCHED_TEXT,
			color: WATCHED_COLOR,
			reasonType: "",
			isCounted: true,
			countKey: "currentPageHasWatchCount"
		},
		IS_KEYWORD_FILTER: {
			text: "❌ 关键词屏蔽",
			color: "#de3333",
			reasonType: "",
			isCounted: true,
			countKey: "currentPageKeywordFilterCount"
		},
		IS_ACTOR_FILTER: {
			text: "♂️ 男演员屏蔽",
			color: "#b22222",
			reasonType: "",
			isCounted: true,
			countKey: "currentPageActorFilterCount"
		},
		IS_ACTRESS_FILTER: {
			text: "♀️ 女演员屏蔽",
			color: "#cd5c5c",
			reasonType: "",
			isCounted: true,
			countKey: "currentPageActorFilterCount"
		},
		IS_WAIT_CHECK: {
			text: "",
			color: "",
			reasonType: "",
			isCounted: true,
			countKey: "currentPageWaitCheckCount"
		}
	};
	function getStatusTagPositionStyle(tagPosition) {
		return tagPosition === "rightTop" ? "right: 0; top:5px;" : "left: 0; top:5px;";
	}
	function buildStatusTagHtml(variant, text, color, dataTip, positionStyle) {
		return jsxToString((0, import_jsx_runtime.jsx)(StatusTagHtml, {
			variant,
			text,
			color,
			dataTip,
			positionStyle
		}));
	}
	function injectStatusTag($item, tagHtml) {
		const tagsEl = $item.find(".tags");
		if (tagsEl.length) {
			tagsEl.append(tagHtml);
			return;
		}
		const itemTagEl = $item.find(".item-tag");
		if (itemTagEl.length) {
			itemTagEl.append(tagHtml);
			return;
		}
		$item.find(".photo-info > span > div").append(tagHtml);
	}
	function PageCountTable({ readDataTime, assembleDataTime, processPageTime, totalTime, filterCount, favoriteCount, actorFilterCount, keywordFilterCount, hasWatchCount, waitCheckCount, totalCount }) {
		const tableStyle = {
			borderCollapse: "collapse",
			width: "100%"
		};
		const tdStyle = {
			padding: "3px",
			border: "1px solid #ccc"
		};
		const tdBoldStyle = {
			padding: "3px",
			border: "1px solid #ccc",
			fontWeight: "bold"
		};
		return (0, import_jsx_runtime.jsxs)("table", {
			className: "countTable",
			style: tableStyle,
			children: [
				(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
					colSpan: 2,
					style: tdStyle,
					children: readDataTime
				}), (0, import_jsx_runtime.jsx)("td", {
					colSpan: 2,
					style: tdStyle,
					children: assembleDataTime
				})] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
					colSpan: 2,
					style: tdStyle,
					children: processPageTime
				}), (0, import_jsx_runtime.jsx)("td", {
					colSpan: 2,
					style: tdStyle,
					children: totalTime
				})] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [
					(0, import_jsx_runtime.jsx)("td", {
						style: tdBoldStyle,
						children: "项目"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdBoldStyle,
						children: "数量"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdBoldStyle,
						children: "项目"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdBoldStyle,
						children: "数量"
					})
				] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "屏蔽单番号"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: filterCount })
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "收藏"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: favoriteCount })
					})
				] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "屏蔽演员"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: actorFilterCount })
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "屏蔽关键词"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: keywordFilterCount })
					})
				] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "已观看"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: hasWatchCount })
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: "待鉴定"
					}),
					(0, import_jsx_runtime.jsx)("td", {
						style: tdStyle,
						children: (0, import_jsx_runtime.jsx)("strong", { children: waitCheckCount })
					})
				] }),
				(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
					style: tdStyle,
					children: (0, import_jsx_runtime.jsx)("strong", { children: "总数" })
				}), (0, import_jsx_runtime.jsx)("td", {
					style: tdStyle,
					children: (0, import_jsx_runtime.jsx)("strong", { children: totalCount })
				})] })
			]
		});
	}
	async function renderItemStatusTag(item, carNum) {
		try {
			const $item = $(item);
			const carInfo = await storageManager.getCar(carNum);
			const status = carInfo ? carInfo.status : "";
			$item.find(".status-tag").remove();
			let tagConfig = null;
			if (status === "favorite") tagConfig = STATUS_TAG_CONFIG.IS_FAVORITE;
			else if (status === "hasWatch") tagConfig = STATUS_TAG_CONFIG.IS_HAS_WATCH;
			else if (status === "filter") tagConfig = STATUS_TAG_CONFIG.IS_FILTERED;
			if (!tagConfig || !tagConfig.text) return;
			const positionStyle = getStatusTagPositionStyle((await storageManager.getSetting()).tagPosition || "rightTop");
			injectStatusTag($item, buildStatusTagHtml("render", tagConfig.text, tagConfig.color, tagConfig.reasonType, positionStyle));
		} catch (err) {
			clog.error("[JHS-想看/观看] renderItemStatusTag 失败", err);
		}
	}
	async function filterMovieList(plugin, itemEls) {
		utils.time("累计耗费时间");
		utils.time("读取数据耗时");
		const [carList, titleFilterKeywords, blacklist, blacklistCarList, setting] = await Promise.all([
			storageManager.getCarList(),
			storageManager.getTitleFilterKeyword(),
			storageManager.getBlacklist(),
			storageManager.getBlacklistCarList(),
			storageManager.getSetting()
		]);
		const readDataTime = utils.time("读取数据耗时");
		const useLower = featureFlags.caseInsensitiveCarNum;
		const statusCarSets = carList.reduce((acc, car) => {
			const status = car.status ?? "";
			if (Object.prototype.hasOwnProperty.call(acc, status)) acc[status].add(useLower ? car.carNum.toLowerCase() : car.carNum);
			return acc;
		}, {
			[FILTER_ACTION]: new Set(),
			[FAVORITE_ACTION]: new Set(),
			[HAS_WATCH_ACTION]: new Set()
		});
		utils.time("组装数据耗时");
		const blacklistRoleMap = new Map(blacklist.map((item) => [item.starId, item.role]));
		const { actorCarNumToNameMap, actressCarNumToNameMap } = blacklistCarList.reduce((acc, item) => {
			const role = blacklistRoleMap.get(item.starId);
			if (!role) {
				clog.error("黑名单数据源丢失演员信息", item);
				return acc;
			}
			const targetMap = role === "actor" ? acc.actorCarNumToNameMap : acc.actressCarNumToNameMap;
			if (!targetMap.has(item.carNum)) targetMap.set(item.carNum, item.names);
			return acc;
		}, {
			actorCarNumToNameMap: new Map(),
			actressCarNumToNameMap: new Map()
		});
		const assembleDataTime = utils.time("组装数据耗时");
		const showFilterItem = setting?.showFilterItem ?? "no";
		const showFilterActorItem = setting?.showFilterActorItem ?? "no";
		const showFilterKeywordItem = setting?.showFilterKeywordItem ?? "no";
		const showFavoriteItem = setting?.showFavoriteItem ?? "yes";
		const showHasWatchItem = setting?.showHasWatchItem ?? "yes";
		const showAllItem = setting?.showAllItem ?? "no";
		const tagPosition = setting?.tagPosition || "rightTop";
		const movieShowType = featureFlags.movieShowTypeVisibility ? setting?.movieShowType || "hide" : "hide";
		plugin.currentPageFilterCount = 0;
		plugin.currentPageFavoriteCount = 0;
		plugin.currentPageHasWatchCount = 0;
		plugin.currentPageKeywordFilterCount = 0;
		plugin.currentPageActorFilterCount = 0;
		plugin.currentPageWaitCheckCount = 0;
		plugin.currentPageTotalCount = 0;
		utils.time("处理页面耗时");
		await Promise.all(itemEls.map(async (itemEl) => {
			const $item = $(itemEl);
			const { carNum, title } = plugin.findCarNumAndHref($item);
			const { filter: filterSet, favorite: favoriteSet, hasWatch: hasWatchSet } = statusCarSets;
			const lookupCarNum = useLower ? carNum.toLowerCase() : carNum;
			const lowerTitle = useLower ? (title || "").toLowerCase() : title || "";
			const isFavorite = favoriteSet.has(lookupCarNum);
			const isHasWatch = hasWatchSet.has(lookupCarNum);
			const isFiltered = filterSet.has(lookupCarNum);
			const isActorBlacklist = actorCarNumToNameMap.has(carNum);
			const isActressBlacklist = actressCarNumToNameMap.has(carNum);
			const isBlacklistActor = isActorBlacklist || isActressBlacklist;
			const matchedKeyword = titleFilterKeywords.find((keyword) => {
				if (useLower) return lowerTitle.includes(keyword.toLowerCase()) || lookupCarNum.startsWith(keyword.toLowerCase());
				return title.includes(keyword) || carNum.startsWith(keyword);
			});
			const hasKeywordMatch = !!matchedKeyword;
			if (!isSearchOrUserPage) {
				let shouldHide = showFavoriteItem === "no" && isFavorite || showHasWatchItem === "no" && isHasWatch || showFilterItem === "no" && isFiltered && !isFavorite && !isHasWatch || showFilterActorItem === "no" && isBlacklistActor || showFilterKeywordItem === "no" && hasKeywordMatch;
				if ($item.attr("data-movieShowType") !== movieShowType) {
					$item.css("border", "");
					$item.children().css("visibility", "");
					$item.removeAttr("data-hide");
					$item.show();
				}
				const isHidden = $item.attr("data-hide") === "yes";
				if (showAllItem === "yes") shouldHide = false;
				if (shouldHide !== isHidden) {
					if (shouldHide) $item.attr("data-hide", "yes");
					else $item.removeAttr("data-hide");
					if (movieShowType === "hide") {
						if (shouldHide) $item.hide();
						else $item.show();
					} else if (movieShowType === "visibility") {
						const borderStyle = shouldHide ? "1px solid rgb(192 176 176)" : "none";
						const visibilityValue = shouldHide ? "hidden" : "visible";
						$item.css("border", borderStyle);
						$item.children().css("visibility", visibilityValue);
					} else throw new Error("movieShowType值有误:" + movieShowType);
					if ($item.attr("data-movieShowType") !== movieShowType) $item.attr("data-movieShowType", movieShowType);
				}
			}
			let tagConfig = STATUS_TAG_CONFIG.IS_WAIT_CHECK;
			let reasonText = null;
			if (isFiltered) tagConfig = STATUS_TAG_CONFIG.IS_FILTERED;
			else if (isFavorite) tagConfig = STATUS_TAG_CONFIG.IS_FAVORITE;
			else if (isHasWatch) tagConfig = STATUS_TAG_CONFIG.IS_HAS_WATCH;
			else if (hasKeywordMatch) {
				tagConfig = STATUS_TAG_CONFIG.IS_KEYWORD_FILTER;
				reasonText = matchedKeyword || "未知";
			} else if (isActorBlacklist) {
				tagConfig = STATUS_TAG_CONFIG.IS_ACTOR_FILTER;
				reasonText = actorCarNumToNameMap.get(carNum) || "";
			} else if (isActressBlacklist) {
				tagConfig = STATUS_TAG_CONFIG.IS_ACTRESS_FILTER;
				reasonText = actressCarNumToNameMap.get(carNum) || "";
			}
			reasonText ||= tagConfig.reasonType;
			if (tagConfig.isCounted) plugin[tagConfig.countKey]++;
			plugin.currentPageTotalCount++;
			$item.find(".status-tag").remove();
			const positionStyle = getStatusTagPositionStyle(tagPosition);
			if (tagConfig.text) {
				const tagHtml = buildStatusTagHtml("filter", tagConfig.text, tagConfig.color, reasonText, positionStyle);
				if (isJavdbSite) $item.find(".tags").append(tagHtml);
			}
			await plugin.translate($item);
		}));
		const processPageTime = utils.time("处理页面耗时");
		const totalTime = utils.time("累计耗费时间");
		$("#waitDownBtn span").text(`打开已收藏 (${statusCarSets.favorite.size})`);
		clog.log(jsxToString((0, import_jsx_runtime.jsx)(PageCountTable, {
			readDataTime: readDataTime ?? "",
			assembleDataTime: assembleDataTime ?? "",
			processPageTime: processPageTime ?? "",
			totalTime: totalTime ?? "",
			filterCount: plugin.currentPageFilterCount,
			favoriteCount: plugin.currentPageFavoriteCount,
			actorFilterCount: plugin.currentPageActorFilterCount,
			keywordFilterCount: plugin.currentPageKeywordFilterCount,
			hasWatchCount: plugin.currentPageHasWatchCount,
			waitCheckCount: plugin.currentPageWaitCheckCount,
			totalCount: plugin.currentPageTotalCount
		})));
	}
	async function translateText(text, sourceLang = "ja", targetLang = "zh-CN") {
		if (!text) throw new Error("翻译文本不能为空");
		const url = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({
			"params.client": "gtx",
			dataTypes: "TRANSLATION",
			key: "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA",
			"query.sourceLanguage": sourceLang,
			"query.targetLanguage": targetLang,
			"query.text": text
		});
		const controller = new AbortController();
		const timeoutId = setTimeout(() => controller.abort(), 1e4);
		try {
			const response = await fetch(url, { signal: controller.signal });
			if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
			return (await response.json()).translation;
		} finally {
			clearTimeout(timeoutId);
		}
	}
	function replaceTitleTextNodes($videoTitle, newText, excludeCarNum) {
		$videoTitle.contents().each((_index, node) => {
			if (node.nodeType === 3 && node.textContent.trim() !== "" && (excludeCarNum === void 0 || !node.textContent.includes(excludeCarNum))) node.textContent = " " + newText + " ";
		});
	}
	function findCarNumAndHref($item) {
		let carNum;
		let title;
		let publishTime;
		const linkEl = $item.find("a");
		const aHref = linkEl.attr("href");
		const videoTitleEl = $item.find(".video-title");
		if (videoTitleEl.length > 0) {
			const strongEl = videoTitleEl.find("strong");
			if (strongEl.length > 0) carNum = strongEl.text().trim();
			title = linkEl.attr("title") ? linkEl.attr("title").trim() : carNum ? videoTitleEl.text().replace(carNum, "").trim() : videoTitleEl.text().trim();
			publishTime = $item.find(".meta").text().trim();
		}
		if (!carNum) {
			const imgEl = $item.find("img");
			if (aHref && imgEl.length > 0) title = imgEl.attr("title")?.trim() || imgEl.attr("data-title")?.trim();
			const isDate = (val) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(val);
			publishTime = $item.find("date").map((_index, el) => $(el).text().trim()).get().find(isDate);
			carNum = $item.find("date").map((_index, el) => $(el).text().trim()).get().find((val) => !isDate(val));
		}
		if (!carNum) failWithToast("提取番号信息失败");
		return {
			carNum,
			aHref: aHref || "",
			url: aHref || "",
			title: title || "",
			publishTime: publishTime || ""
		};
	}
	function replaceHdImg(plugin, imgEls) {
		if (imgEls && typeof imgEls.jquery == "string") imgEls = imgEls.toArray();
		imgEls ||= document.querySelectorAll(plugin.getSelector().coverImgSelector);
		if (isJavdbSite) imgEls.forEach((img) => {
			img.src = img.src.replace("thumbs", "covers");
			img.title = "";
		});
	}
	function showCarNumBox(carNum) {
		const matchedEl = $(".movie-list .item").toArray().find((itemEl) => $(itemEl).find(".video-title strong").text() === carNum);
		if (matchedEl) {
			const $matched = $(matchedEl);
			if ($matched.attr("data-hide") === `${carNum}-hide`) {
				$matched.show();
				$matched.removeAttr("data-hide");
			}
		}
	}
	async function bindClick(plugin) {
		const selectorConfig = plugin.getSelector();
		$(selectorConfig.boxSelector).on("click", ".item .cover", async (event) => {
			event.preventDefault();
			event.stopPropagation();
			if ($(event.target).closest("div.meta-buttons").length) return;
			const $item = $(event.target).closest(".item");
			const { carNum, aHref } = plugin.findCarNumAndHref($item);
			const dialogOpenDetail = await storageManager.getSetting("dialogOpenDetail", "yes");
			if (carNum.includes("FC2-")) {
				const movieId = plugin.parseMovieId(aHref);
				plugin.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carNum, aHref);
			} else if (dialogOpenDetail === "yes") utils.openPage(aHref, carNum, true, event);
			else window.open(aHref, "_blank", "noopener,noreferrer");
		});
		$(selectorConfig.boxSelector).on("click", ".item video", async (event) => {
			const videoEl = event.currentTarget;
			if (videoEl.paused) videoEl.play().catch((err) => clog.error("播放失败:", err));
			else videoEl.pause();
			event.preventDefault();
			event.stopPropagation();
		});
		$(selectorConfig.boxSelector).on("click", ".item .video-title", async (event) => {
			if ($(event.target).closest("[class^=\"jhs-match-\"]").length) return;
			const $item = $(event.currentTarget).closest(".item");
			const { carNum, aHref } = plugin.findCarNumAndHref($item);
			if (carNum.includes("FC2-")) {
				event.preventDefault();
				const movieId = plugin.parseMovieId(aHref);
				plugin.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carNum, aHref);
			}
		});
	}
	async function translate(plugin, $item) {
		if (await storageManager.getSetting("translateTitle", "yes") !== "yes") return;
		let sourceText = "";
		let carNum = "";
		const videoTitleEl = $item.find(".video-title");
		if (isJavdbSite) {
			sourceText = videoTitleEl.contents().filter((_index, node) => node.nodeType === 3 && node.textContent.trim() !== "").text().trim();
			carNum = $item.find(".video-title strong").text().trim();
		}
		if (plugin.cache[carNum]) {
			replaceTitleTextNodes(videoTitleEl, plugin.cache[carNum]);
			videoTitleEl.attr("title", plugin.cache[carNum]);
			return;
		}
		translateText(sourceText).then((translation) => {
			if (isJavdbSite) {
				replaceTitleTextNodes(videoTitleEl, translation, carNum);
				videoTitleEl.attr("title", translation);
			}
			plugin.writeQueue = plugin.writeQueue.then(() => {
				plugin.cache[carNum] = translation;
				writePersistentEntry("translate", "jhs_translate", carNum, translation);
			});
		}).catch((err) => {
			clog.error("翻译失败:", err);
		});
	}
	async function revertTranslation(plugin) {
		$(plugin.getSelector().itemSelector).toArray().forEach((itemEl) => {
			const $item = $(itemEl);
			const originalTitle = $item.find(".box").attr("title") || $item.find(".video-title").attr("title") || $item.find("img").attr("data-title");
			const carNum = $item.find(".video-title strong").text().trim();
			const videoTitleEl = $item.find(".video-title");
			replaceTitleTextNodes(videoTitleEl, originalTitle, carNum);
			videoTitleEl.removeAttr("title");
		});
	}
	function JumpPageControl({ controlId, value }) {
		return (0, import_jsx_runtime.jsxs)("li", {
			id: controlId,
			children: [(0, import_jsx_runtime.jsx)("input", {
				type: "number",
				id: "jumpPageInput",
				placeholder: "页码",
				min: "1",
				style: {
					width: "60px",
					marginLeft: "10px",
					padding: "10px",
					border: "1px solid #ccc",
					fontSize: "14px"
				},
				value
			}), (0, import_jsx_runtime.jsx)("button", {
				style: {
					marginLeft: "5px",
					padding: "9px 8px",
					cursor: "pointer",
					border: "1px solid #ccc",
					backgroundColor: "#f0f0f0",
					fontSize: "14px"
				},
				children: "跳转"
			})]
		});
	}
	function addJumpPageControl() {
		const controlId = "gemini-jump-page-control";
		if ($("#gemini-jump-page-control").length > 0) return;
		if ($(".pagination-link.is-current").length === 0) return;
		const currentPage = utils.getUrlParam(currentHref, "page") || 1;
		const $li = $(jsxToString((0, import_jsx_runtime.jsx)(JumpPageControl, {
			controlId,
			value: Number(currentPage) + 1
		})));
		$(".pagination-list").append($li);
		const $pageInput = $li.find("#jumpPageInput");
		const $jumpBtn = $li.find("button");
		const jumpToPage = () => {
			const pageNum = parseInt(String($pageInput.val()), 10);
			if (isNaN(pageNum) || pageNum < 1) {
				$pageInput.focus();
				return;
			}
			const url = new URL(window.location.href);
			url.searchParams.set("page", pageNum.toString());
			window.location.href = url.toString();
		};
		$jumpBtn.on("click", jumpToPage);
		$pageInput.on("keypress", (event) => {
			if (event.which === 13) {
				jumpToPage();
				event.preventDefault();
			}
		});
	}
	var ListPagePlugin = class extends BasePlugin {
		currentPageFilterCount = 0;
		currentPageFavoriteCount = 0;
		currentPageHasWatchCount = 0;
		currentPageKeywordFilterCount = 0;
		currentPageActorFilterCount = 0;
		currentPageWaitCheckCount = 0;
		currentPageTotalCount = 0;
		supervisor = new TaskSupervisor();
		cache = {};
		writeQueue = Promise.resolve();
		getName() {
			return "ListPagePlugin";
		}
		destroy() {
			this.supervisor.abort();
		}
		get pageTypes() {
			return ["list"];
		}
		async handle() {
			this.cache = readPersistentMap("translate", "jhs_translate");
			new BroadcastChannel("channel-refresh").addEventListener("message", async (event) => {
				const msgType = event.data.type;
				if (msgType === "refresh") {
					storageManager.clearCarListCache();
					await this.doFilter();
					const historyPlugin = this.getBean("HistoryPlugin");
					if (historyPlugin.tableObj) historyPlugin.tableObj.setData();
					const newVideoPlugin = this.getBean("NewVideoPlugin");
					if (newVideoPlugin) {
						newVideoPlugin.showNewVideoCount().then();
						newVideoPlugin.loadData();
					}
				} else if (msgType === "cleanCache_filter_actor_actress_car_list") storageManager.clearFilterActorActressCarListCache();
				else if (msgType === "clean_cacheSettingObj") storageManager.clearSettingCache();
			});
			this.replaceHdImg();
			this.addJumpPageControl();
			await this.doFilter();
			this.bindClick().then();
			this.rememberTagExpand();
			$(this.getSelector().itemSelector + " a").attr("target", "_blank");
			this.checkDom();
			this.getBean("DetailPageButtonPlugin").setupWantWatchedSyncListener();
		}
		rememberTagExpand() {
			if (!window.location.href.includes("actors")) return;
			const expandBtn = $(".tag-expand");
			if (expandBtn.length === 0) return;
			const storageKey = "jhs_tag_expand";
			const wasExpanded = localStorage.getItem(storageKey) === "true";
			const contentEl = $(".actor-tags .content");
			if (wasExpanded && contentEl.hasClass("collapse")) expandBtn[0].click();
			expandBtn.on("click", function() {
				const isExpanded = !contentEl.hasClass("collapse");
				localStorage.setItem(storageKey, isExpanded.toString());
			});
		}
		checkDom() {
			if (!window.isListPage) return;
			const selectorConfig = this.getSelector();
			const containerEl = document.querySelector(selectorConfig.boxSelector);
			if (!containerEl) {
				clog.error("没有找到容器节点!");
				return;
			}
			const observerOptions = {
				childList: true,
				subtree: false
			};
			const observer = this.supervisor.observe(containerEl, async () => {
				observer.disconnect();
				try {
					this.replaceHdImg();
					this.addJumpPageControl();
					await this.doFilter();
					await this.getBean("ListPageButtonPlugin").sortItems();
					$(this.getSelector().itemSelector + " a").attr("target", "_blank");
					this.getBean("AutoPagePlugin").checkLoad();
				} finally {
					observer.observe(containerEl, observerOptions);
				}
			}, observerOptions);
		}
		async doFilter() {
			if (!window.isListPage) return;
			const itemEls = $(this.getSelector().itemSelector).toArray();
			if (itemEls.length) await this.filterMovieList(itemEls);
		}
		async parseActressName(url) {
			let actressName = null;
			if (await storageManager.getSetting("enableSaveActressCarInfo", "no") === "yes") {
				clog.debug("鉴定补录演员信息-已启用, 开始解析详情页");
				clog.debug("开始解析演员详情页", url);
				const html = await gmHttp.get(url);
				const $dom = utils.htmlTo$dom(String(html));
				if (isJavdbSite) actressName = $dom.find(".female").prev().map((_index, el) => $(el).text()).get().join(" ");
				clog.debug("解析到名称:", actressName);
			}
			return actressName;
		}
		async renderItemStatusTag(item, carNum) {
			return renderItemStatusTag(item, carNum);
		}
		async filterMovieList(itemEls) {
			return filterMovieList(this, itemEls);
		}
		async translate($item) {
			return translate(this, $item);
		}
		async revertTranslation() {
			return revertTranslation(this);
		}
		findCarNumAndHref($item) {
			return findCarNumAndHref($item);
		}
		replaceHdImg(imgEls) {
			return replaceHdImg(this, imgEls);
		}
		showCarNumBox(carNum) {
			return showCarNumBox(carNum);
		}
		async bindClick() {
			return bindClick(this);
		}
		addJumpPageControl() {
			return addJumpPageControl();
		}
	};
	var DMM_VIDEO_CACHE_KEY = "jhs_dmm_video";
	var DMM_OTHER_SITE_KEY = "jhs_other_site_dmm";
	var DmmPreviewVideoResolver = class {
		carNum;
		showErrorMessages;
		constructor(carNum, showErrorMessages = true) {
			this.carNum = carNum;
			this.showErrorMessages = showErrorMessages;
		}
		_checkCache() {
			const cache = readPersistentMap("dmmVideo", DMM_VIDEO_CACHE_KEY);
			if (cache[this.carNum]) {
				clog.debug("缓存中存在预览视频信息", cache[this.carNum]);
				return cache[this.carNum];
			}
			return null;
		}
		_updateCache(videoMap) {
			const cache = readPersistentMap("dmmVideo", DMM_VIDEO_CACHE_KEY);
			cache[this.carNum] = videoMap;
			clog.debug("成功解析出预览视频并已缓存:", videoMap);
			writePersistentEntry("dmmVideo", DMM_VIDEO_CACHE_KEY, this.carNum, videoMap);
		}
		async _searchContentIds() {
			const carNum = this.carNum;
			const noHyphen = carNum.replace(/-/g, "");
			const candidates = [
				{
					keyword: carNum.replace("-", "00"),
					name: "00-替换关键词"
				},
				{
					keyword: carNum,
					name: "原始番号关键词"
				},
				{
					keyword: noHyphen,
					name: "无连字符关键词"
				}
			];
			const carNumLower = carNum.toLowerCase();
			for (const candidate of candidates) {
				const { keyword, name } = candidate;
				const keywordLower = keyword.toLowerCase();
				clog.debug(`--- 尝试使用 ${name} (${keyword}) 进行 API 搜索 ---`);
				const apiUrl = `https://api.dmm.com/affiliate/v3/ItemList?${new URLSearchParams({
					api_id: "UrwskPfkqQ0DuVry2gYL",
					affiliate_id: "10278-996",
					output: "json",
					site: "FANZA",
					sort: "match",
					keyword
				}).toString()}`;
				let response;
				try {
					response = await gmHttp.get(apiUrl);
				} catch (err) {
					clog.error(`API 请求失败,跳过 ${name}:`, err);
					continue;
				}
				if (!response || !response.result || !response.result.result_count) {
					clog.debug("API 返回无结果,尝试下一个关键词。");
					continue;
				}
				const matched = [];
				for (const item of response.result.items) {
					if (matched.length >= 2) break;
					const contentId = item.content_id || "";
					const makerProduct = item.maker_product || "";
					if (contentId.includes(keywordLower.replace("-", "")) || carNumLower === makerProduct.toLowerCase() || contentId.includes(noHyphen.toLowerCase())) {
						matched.push({
							serviceCode: item.service_code || "",
							floorCode: item.floor_code || "",
							contentId,
							pageUrl: item.URL || ""
						});
						clog.debug(`[${name}] cid|makerProduct 匹配成功:`, contentId, makerProduct);
					}
				}
				if (matched.length > 0) {
					clog.debug(`--- 成功通过 ${name} 找到 Content IDs ---`);
					const $fanzaBtn = $("#fanzaBtn");
					let siteUrl = `https://www.dmm.co.jp/search/=/searchstr=${keyword}`;
					let linkType = "single";
					if (matched.length > 1) {
						$fanzaBtn.attr("href", siteUrl);
						$fanzaBtn.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
						$fanzaBtn.css("backgroundColor", "#7bc73b");
						linkType = "multiple";
					} else {
						siteUrl = matched[0].pageUrl;
						$fanzaBtn.attr("href", siteUrl);
						$fanzaBtn.css("backgroundColor", "#7bc73b");
					}
					const otherSiteCache = readPersistentMap("otherSiteDmm", DMM_OTHER_SITE_KEY);
					otherSiteCache[this.carNum] = {
						type: linkType,
						url: siteUrl
					};
					writePersistentEntry("otherSiteDmm", DMM_OTHER_SITE_KEY, this.carNum, otherSiteCache[this.carNum]);
					return matched;
				}
				clog.debug(`[${name}] API 返回结果数 ${response.result.result_count},但无精确匹配的 Content ID。`);
			}
			clog.warn("所有关键词尝试均未找到匹配的Content ID, 解析Dmm视频失败");
			const $fanzaBtn = $("#fanzaBtn");
			$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
			$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
			$fanzaBtn.css("backgroundColor", "#de3333");
			return null;
		}
		async _extractTrailerLinks({ contentId, serviceCode, floorCode }) {
			const playerUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`;
			const html = await gmHttp.get(playerUrl, void 0, {
				"accept-language": "ja-JP,ja;q=0.9",
				Cookie: "age_check_done=1"
			});
			if (typeof html != "string") {
				clog.error(html);
				throw new Error("解析播放页内容失败, 非文本内容");
			}
			if (html.includes("このサービスはお住まいの地域からは")) throw new Error("节点不可用,请将DMM域名分流到日本ip");
			const argsMatch = html.match(/const\s+args\s+=\s+(.*);/);
			if (!argsMatch) throw new Error("未在脚本中找到 const args = ... 变量");
			let bitrates;
			try {
				({bitrates} = JSON.parse(argsMatch[1]));
			} catch (err) {
				throw new Error(`解析播放器脚本 JSON 失败: ${err.message}`);
			}
			const videoMap = {};
			const qualityPattern = VIDEO_QUALITY_LIST.map((opt) => opt.quality).join("|");
			const qualityRegex = new RegExp(`(${qualityPattern})\\.mp4$`);
			if (!Array.isArray(bitrates)) {
				clog.error("解析画质链接失败: bitrates 字段不是一个数组或不存在");
				throw new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在");
			}
			clog.debug("原始数据返回:", bitrates);
			for (const item of bitrates) {
				const src = item == null ? void 0 : item.src;
				if (!src || typeof src != "string" || !src.endsWith(".mp4")) continue;
				const qualityMatch = src.match(qualityRegex);
				let quality = "";
				if (qualityMatch && qualityMatch[1]) quality = qualityMatch[1];
				if (quality && !videoMap[quality]) videoMap[quality] = src;
			}
			if (Object.keys(videoMap).length === 0) throw new Error("未找到匹配要求的预览画质视频");
			return videoMap;
		}
		async fetchVideo() {
			const cached = this._checkCache();
			if (cached) return cached;
			let contentIds;
			try {
				const carNumLower = this.carNum.toLowerCase();
				if (carNumLower.startsWith("heyzo") || /^(n\d+|\d+(-\d+)*)$/.test(carNumLower) || /^n\d+$/.test(carNumLower)) throw new Error("无码番号类型, 取消dmm解析");
				if (this.carNum.includes("VR-")) throw new Error("VR类型, 取消dmm解析");
				contentIds = await this._searchContentIds();
			} catch (err) {
				clog.error("DMM API 搜索失败:", err);
				const $fanzaBtn = $("#fanzaBtn");
				$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
				$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
				$fanzaBtn.css("backgroundColor", "#de3333");
				return null;
			}
			if (!contentIds || contentIds.length === 0) return null;
			try {
				const videoMap = await Promise.any(contentIds.map((id) => this._extractTrailerLinks(id)));
				this._updateCache(videoMap);
				return videoMap;
			} catch (err) {
				const errors = err && typeof err === "object" && "errors" in err && Array.isArray(err.errors) ? err.errors : [err];
				if (errors.some((e) => e instanceof Error && e.message.includes("节点不可用"))) {
					if (this.showErrorMessages) show.error("节点不可用,请将DMM域名分流到日本ip");
				} else {
					const first = errors[0];
					const firstMsg = first instanceof Error && first.message ? first.message : first;
					clog.error(`解析失败: ${firstMsg}`, errors);
					if (this.showErrorMessages) show.error(`解析失败: ${firstMsg}`);
				}
				const $fanzaBtn = $("#fanzaBtn");
				$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
				$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
				$fanzaBtn.css("backgroundColor", "#de3333");
				return null;
			}
		}
	};
	var fetchDmmPreviewVideo = async (carNum, showErrorMessages = true) => new DmmPreviewVideoResolver(carNum, showErrorMessages).fetchVideo();
	var selectAvailableVideoQuality = (available, desired) => {
		if (!available || available.length === 0) return null;
		const availableSet = new Set(available);
		if (availableSet.has(desired)) return desired;
		const descending = VIDEO_QUALITY_LIST.map((opt) => opt.quality).reverse();
		for (const quality of descending) if (availableSet.has(quality)) return quality;
		return available[0];
	};
	function PreviewVideoActionBtn({ id, color, label }) {
		return (0, import_jsx_runtime.jsx)("button", {
			className: "jhs-toolbar-menu-btn",
			id,
			style: {
				minWidth: "120px",
				backgroundColor: color
			},
			children: label
		});
	}
	function PreviewVideoQualityBtn({ opt, src, isActive }) {
		return (0, import_jsx_runtime.jsx)("button", {
			className: `video-control-btn${isActive ? " active" : ""}`,
			id: opt.id,
			"data-quality": opt.quality,
			"data-video-src": src,
			style: {
				minWidth: "40px",
				border: "1px solid #ccc",
				backgroundColor: isActive ? "#007bff" : "#fff",
				color: isActive ? "white" : "black"
			},
			children: opt.text
		});
	}
	var VIDEO_MUTED_KEY = "jhs_videoMuted";
	async function buildVideoToolbar(plugin) {
		const $video = $("#preview-video");
		if (!$video.length) return;
		const $videoWrap = $video.parent();
		$videoWrap.css("position", "relative");
		const videoEl = $video[0];
		const mutedStored = localStorage.getItem(VIDEO_MUTED_KEY);
		if (mutedStored) videoEl.muted = mutedStored === "yes";
		videoEl.addEventListener("volumechange", () => {
			localStorage.setItem(VIDEO_MUTED_KEY, videoEl.muted ? "yes" : "no");
		});
		videoEl.play();
		const carNum = plugin.getPageInfo().carNum;
		const videoMap = await fetchDmmPreviewVideo(carNum);
		const $toolbar = $("<div></div>").attr("id", "video-bottom-toolbar").css({
			display: "flex",
			gap: "5px",
			"align-items": "center",
			"flex-wrap": "wrap"
		});
		const $qualityGroup = $("<div></div>").css({
			display: "flex",
			gap: "5px",
			"align-items": "center"
		});
		let currentQuality = null;
		if (videoMap) {
			const desiredQuality = await storageManager.getSetting("videoQuality");
			currentQuality = selectAvailableVideoQuality(Object.keys(videoMap), desiredQuality);
			const currentUrl = videoMap[currentQuality];
			if ($video.attr("src") !== currentUrl) {
				$video.attr("src", currentUrl);
				videoEl.load();
				videoEl.play();
			}
			VIDEO_QUALITY_LIST.forEach((opt) => {
				const src = videoMap[opt.quality];
				if (src) {
					const isActive = currentQuality === opt.quality;
					const $btn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoQualityBtn, {
						opt,
						src,
						isActive
					})));
					$qualityGroup.append($btn);
				}
			});
		}
		$toolbar.append($qualityGroup);
		const $actionGroup = $("<div></div>").css({
			display: "flex",
			gap: "5px",
			"align-items": "center",
			"margin-left": "auto"
		});
		const $filterBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
			id: "video-filterBtn",
			color: "#de3333",
			label: "屏蔽"
		})));
		$actionGroup.append($filterBtn);
		const $favoriteBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
			id: "video-favoriteBtn",
			color: "#25b1dc",
			label: "收藏"
		})));
		$actionGroup.append($favoriteBtn);
		const $speedBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
			id: "speed-btn",
			color: "#76b45d",
			label: "快进"
		})));
		$actionGroup.append($speedBtn);
		$toolbar.append($actionGroup);
		$videoWrap.append($toolbar);
		$toolbar.on("click", ".video-control-btn", async (event) => {
			const $btn = $(event.currentTarget);
			const src = $btn.data("video-src");
			if (!$btn.hasClass("active")) try {
				const currentTime = videoEl.currentTime;
				$video.attr("src", src);
				videoEl.load();
				videoEl.currentTime = currentTime;
				await videoEl.play();
				$toolbar.find(".video-control-btn").removeClass("active").css({
					"background-color": "#fff",
					color: "black"
				});
				$btn.addClass("active").css({
					"background-color": "#007bff",
					color: "white"
				});
			} catch (err) {
				console.error("切换画质失败:", err);
			}
		});
		$("#speed-btn").on("click", () => {
			plugin.getBean("DetailPageButtonPlugin").speedVideo();
		});
		utils.rightClick(document.body, "#speed-btn", (event) => {
			plugin.getBean("DetailPageButtonPlugin").filterOne(event);
		});
		$("#video-filterBtn").on("click", (event) => {
			plugin.getBean("DetailPageButtonPlugin").filterOne(event);
		});
		$("#video-favoriteBtn").on("click", () => {
			plugin.getBean("DetailPageButtonPlugin").favoriteOne();
		});
	}
	function PreviewVideoContainer({ coverSrc }) {
		return (0, import_jsx_runtime.jsxs)("a", {
			className: "preview-video-container",
			"data-fancybox": "gallery",
			href: "#preview-video",
			children: [(0, import_jsx_runtime.jsx)("span", { children: "預告片" }), (0, import_jsx_runtime.jsx)("img", {
				src: coverSrc,
				className: "video-cover",
				style: {
					width: "150px",
					height: "auto"
				},
				alt: ""
			})]
		});
	}
	var preview_video_plugin_default = ".video-control-btn {\n    min-width: 120px;\n    padding: 7px 12px;\n    font-size: 12px;\n    background: rgb(0, 0, 0, 0.7);\n    color: white;\n    border: none;\n    border-radius: 4px;\n    cursor: pointer;\n}\n\n.video-control-btn.active {\n    background-color: #1890ff;\n    color: white;\n    font-weight: bold;\n    border: 2px solid #096dd9;\n}\n";
	var PreviewVideoPlugin = class extends BasePlugin {
		getName() {
			return "PreviewVideoPlugin";
		}
		async initCss() {
			return preview_video_plugin_default;
		}
		async handle() {
			if (!window.isDetailPage) return;
			const $container = $(".preview-video-container");
			$container.on("click", () => {
				utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, () => {
					this.handleVideo().then();
				});
			});
			if (!currentHref.includes("autoPlay=1")) this.initDmm().then();
			const href = window.location.href;
			if (href.includes("gallery-1") || href.includes("gallery-2")) utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, () => {
				if ($(".fancybox-content #preview-video").length > 0) this.handleVideo().then();
			});
			if (href.includes("autoPlay=1") && $container.length > 0) $container[0].click();
		}
		async initDmm() {
			try {
				const videoMap = await fetchDmmPreviewVideo(this.getPageInfo().carNum, false);
				if (!videoMap) return;
				const desiredQuality = await storageManager.getSetting("videoQuality");
				clog.debug("解析其它画质预览视频", "设置-期望画质", desiredQuality);
				const videoUrl = videoMap[selectAvailableVideoQuality(Object.keys(videoMap), desiredQuality)];
				clog.log("切换其它画质预览视频: ", videoUrl);
				const $video = $("#preview-video");
				const videoEl = $video.length ? $video[0] : null;
				const isHidden = !videoEl || utils.isHidden($video);
				if ($video.length) {
					if (videoEl) {
						const currentTime = videoEl.currentTime;
						$video.attr("src", videoUrl);
						if (!isHidden) {
							clog.debug("播放器已手动打开, 变更进度条");
							videoEl.currentTime = currentTime;
							videoEl.play();
						}
					}
				} else {
					clog.debug("JavDB没有视频播放元素, 开始创建...");
					const coverSrc = $(".column-video-cover img").attr("src");
					$(".preview-images").prepend(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoContainer, { coverSrc: coverSrc ?? "" })));
					$(".preview-video-container").on("click", () => {
						utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, async () => {
							await this.handleVideo();
						});
					});
				}
			} catch (err) {
				clog.error("预加载dmm失败:", err);
			}
		}
		async handleVideo() {
			await buildVideoToolbar(this);
		}
	};
	var RatingRenderer = {
		hideNativeBadge(cover) {
			cover.querySelectorAll(":scope > span").forEach((s) => {
				if (s.classList.length === 0 || s.className === "score") s.style.display = "none";
			});
		},
		showPlaceholder(item) {
			const cover = item.querySelector(".cover");
			if (!cover) return;
			if (cover.querySelector(".jhs-user-rating")) return;
			this.hideNativeBadge(cover);
			const el = document.createElement("div");
			el.className = "jhs-user-rating is-placeholder";
			el.textContent = "已看";
			el.title = "已看(悬停加载评分)";
			cover.appendChild(el);
		},
		showRating(item, rating) {
			const cover = item.querySelector(".cover");
			if (!cover) return;
			let el = cover.querySelector(".jhs-user-rating");
			if (!el) {
				this.hideNativeBadge(cover);
				el = document.createElement("div");
				el.className = "jhs-user-rating";
				cover.appendChild(el);
			}
			el.className = "jhs-user-rating";
			el.innerHTML = "";
			if (rating === 0) {
				el.classList.add("is-rated");
				el.innerHTML = jsxToString((0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
					className: "jhs-rd-icon",
					children: "★"
				}), (0, import_jsx_runtime.jsx)("span", {
					className: "jhs-rd-num",
					children: "0"
				})] }));
				el.title = "已看 · 0/5 星(未评分)";
			} else if (rating && rating >= 1 && rating <= 5) {
				el.classList.add("is-rated");
				el.innerHTML = jsxToString((0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
					className: "jhs-rd-icon",
					children: "★"
				}), (0, import_jsx_runtime.jsx)("span", {
					className: "jhs-rd-num",
					children: rating
				})] }));
				el.title = `已看 · ${rating}/5 星`;
			} else {
				el.classList.add("is-placeholder");
				el.textContent = "已看";
				el.title = "已看(未评分)";
			}
		},
		removeFrom(item) {
			const el = item?.querySelector(".jhs-user-rating");
			if (el) el.remove();
			const cover = item?.querySelector(".cover");
			if (cover) cover.querySelectorAll(":scope > span").forEach((s) => {
				if (s.style.display === "none" && (s.classList.length === 0 || s.className === "score")) s.style.display = "";
			});
		}
	};
	var limiter = RatingUtils.createLimiter(RATING_CONFIG.FETCH_CONCURRENCY);
	async function request(url, retries = RATING_CONFIG.FETCH_RETRY) {
		for (let i = 0; i <= retries; i++) try {
			return await new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method: "GET",
					url,
					timeout: RATING_CONFIG.FETCH_TIMEOUT,
					headers: { "X-Requested-With": "XMLHttpRequest" },
					onload: resolve,
					onerror: reject,
					ontimeout: reject,
					onabort: reject
				});
			});
		} catch (err) {
			if (i === retries) throw err;
			await new Promise((resolve) => setTimeout(resolve, 400 * (i + 1)));
		}
		throw new Error("unreachable");
	}
	function parseRating(doc) {
		if (!doc.querySelector(".review-title .tag.is-success.is-light")) return null;
		const chk = doc.querySelector("input[name=\"video_review[score]\"][checked], .rating-star input:checked");
		if (!chk) return null;
		const ratingValue = parseInt(chk.value, 10);
		return ratingValue >= 1 && ratingValue <= 5 ? ratingValue : null;
	}
	async function fetchRating(item) {
		if (item.dataset.jhsrdFetching === "true" || item.dataset.jhsrdLoaded === "true") return;
		const anchor = RatingUtils.getAnchor(item);
		if (!anchor) return;
		const code = RatingUtils.getCode(item);
		if (!code) return;
		item.dataset.jhsrdFetching = "true";
		try {
			const url = RatingUtils.getSafeUrl(anchor.href);
			const res = await limiter.run(() => request(url));
			if (res.status !== 200) {
				item.dataset.jhsrdLoaded = "true";
				return;
			}
			const rating = parseRating(RatingUtils.parser.parseFromString(res.responseText, "text/html"));
			if (rating !== null) {
				RatingCache.set(code, rating);
				RatingRenderer.showRating(item, rating);
			}
			item.dataset.jhsrdLoaded = "true";
		} catch (err) {
			RatingUtils.log("FETCH_ERROR", code, err);
		} finally {
			delete item.dataset.jhsrdFetching;
		}
	}
	var rating_display_default = ".jhs-user-rating {\n    position: absolute;\n    bottom: 6px;\n    right: 6px;\n    z-index: 5;\n    pointer-events: auto;\n    font-size: 13px;\n    font-weight: 700;\n    line-height: 1;\n    padding: 5px 10px;\n    border-radius: 12px;\n    box-shadow: 0 2px 8px rgb(0, 0, 0, 0.35), 0 0 0 1px rgb(255,255,255,0.1) inset;\n    text-shadow: 0 1px 2px rgb(0, 0, 0, 0.4);\n    color: #fff;\n    white-space: nowrap;\n    opacity: 0.95;\n    transition: transform 0.25s ease, opacity 0.2s ease, background 0.3s ease;\n    display: inline-flex;\n    align-items: center;\n    gap: 3px;\n    letter-spacing: 0.5px;\n}\n\n/* 占位(绿色) */\n.jhs-user-rating.is-placeholder {\n    background: linear-gradient(145deg, #48c78e, #2fb972);\n}\n\n/* 已评分(金色) */\n.jhs-user-rating.is-rated {\n    background: linear-gradient(145deg, #ffc107, #f57c00);\n}\n\n.jhs-rd-icon {\n    font-size: 14px;\n    filter: drop-shadow(0 1px 1px rgb(0,0,0,0.3));\n}\n\n.jhs-rd-num {\n    font-size: 14px;\n    font-weight: 900;\n}\n\n.jhs-user-rating:hover {\n    transform: scale(1.1);\n    opacity: 1;\n}\n\n/* 加载中 */\n.jhs-user-rating.is-loading {\n    animation: jhs-rd-pulse 1s infinite;\n    pointer-events: none;\n}\n\n@keyframes jhs-rd-pulse {\n    0%, 100% { opacity: 0.55; }\n    50% { opacity: 1; }\n}\n\n/* Toast */\n.jhs-rd-toast {\n    position: fixed;\n    left: 50%;\n    transform: translateX(-50%);\n    bottom: 2.5rem;\n    background: rgb(0, 0, 0, 0.88);\n    color: #fff;\n    padding: 0.5rem 0.9rem;\n    border-radius: 0.5rem;\n    z-index: 20000;\n    font-size: 0.85rem;\n    box-shadow: 0 4px 16px rgb(0, 0, 0, 0.4);\n    animation: jhs-rd-toast-in 0.22s;\n    pointer-events: none;\n}\n\n@keyframes jhs-rd-toast-in {\n    from { opacity: 0; transform: translateX(-50%) translateY(8px); }\n    to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n";
	var RatingDisplayPlugin = class extends BasePlugin {
		watchedMap = new Map();
		_dirty = false;
		getName() {
			return "RatingDisplayPlugin";
		}
		async initCss() {
			return rating_display_default;
		}
		toast(msg) {
			document.querySelector(".jhs-rd-toast")?.remove();
			const div = document.createElement("div");
			div.className = "jhs-rd-toast";
			div.textContent = msg;
			document.body.appendChild(div);
			setTimeout(() => div.remove(), 2500);
		}
		processItem(item) {
			if (!item || item.dataset.jhsrdProcessed === "true") return;
			item.dataset.jhsrdProcessed = "true";
			const code = RatingUtils.getCode(item);
			if (!code) return;
			if (!this.watchedMap.has(code)) {
				RatingRenderer.removeFrom(item);
				return;
			}
			const cached = RatingCache.get(code);
			if (cached) {
				RatingRenderer.showRating(item, cached.rating);
				item.dataset.jhsrdLoaded = "true";
				item.dataset.jhsrdFromCache = "true";
			} else RatingRenderer.showPlaceholder(item);
		}
		refreshAll() {
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
				const el = item;
				RatingRenderer.removeFrom(el);
				el.dataset.jhsrdProcessed = "false";
				delete el.dataset.jhsrdLoaded;
				delete el.dataset.jhsrdFetching;
				delete el.dataset.jhsrdFromCache;
				this.processItem(el);
			});
		}
		clearPageCache() {
			const codes = new Set();
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
				const code = RatingUtils.getCode(item);
				if (code) codes.add(code);
			});
			let cleared = 0;
			codes.forEach((code) => {
				if (RatingCache._data[code]) {
					delete RatingCache._data[code];
					cleared++;
				}
			});
			if (cleared > 0) RatingCache.save();
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
				const el = item;
				RatingRenderer.removeFrom(el);
				delete el.dataset.jhsrdLoaded;
				delete el.dataset.jhsrdFromCache;
				el.dataset.jhsrdProcessed = "false";
				this.processItem(el);
			});
			this.toast(`已清除 ${cleared} 个评分缓存,当前页 ${codes.size} 张卡片`);
		}
		loadAll() {
			const targets = [];
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
				const el = item;
				if (el.dataset.jhsrdFetching !== "true" && el.dataset.jhsrdLoaded !== "true") targets.push(el);
			});
			if (targets.length === 0) {
				this.toast("当前页面所有卡片已加载评分");
				return;
			}
			this.toast(`开始加载 ${targets.length} 个评分...`);
			targets.forEach((item) => {
				const tag = item.querySelector(".jhs-user-rating");
				if (tag) tag.classList.add("is-loading");
				fetchRating(item).finally(() => {
					if (tag) tag.classList.remove("is-loading");
				});
			});
		}
		_invalidateCards(code, status, op, score) {
			if (status === "hasWatch" && op === "add") {
				if (!this.watchedMap.has(code)) this.watchedMap.set(code, {
					carNum: code,
					status: "hasWatch"
				});
			} else this.watchedMap.delete(code);
			if (status === "hasWatch" && op === "add" && typeof score === "number") RatingCache.set(code, score);
			else {
				delete RatingCache._data[code];
				RatingCache.save();
			}
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
				const el = item;
				if (RatingUtils.getCode(el) === code) {
					RatingRenderer.removeFrom(el);
					delete el.dataset.jhsrdLoaded;
					delete el.dataset.jhsrdFromCache;
					el.dataset.jhsrdProcessed = "false";
					this.processItem(el);
				}
			});
			this._dirty = true;
			const cur = localStorage.getItem("jdb:want-watched-sync");
			if (cur) localStorage.setItem("jhsrd:last-sync-digest", cur);
		}
		async init() {
			await RatingCache.load();
			const syncVal = localStorage.getItem("jdb:want-watched-sync");
			const lastDigest = localStorage.getItem("jhsrd:last-sync-digest");
			if (syncVal && syncVal !== lastDigest) {
				RatingUtils.log("INIT_SYNC", "jhs 有新的同步事件,全量清空评分缓存");
				RatingCache.clear();
				this._dirty = true;
				localStorage.setItem("jhsrd:last-sync-digest", syncVal);
			}
			this.watchedMap = await buildWatchedMap();
			document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((i) => this.processItem(i));
			document.querySelectorAll(".jhs-user-rating.is-rated").forEach((el) => {
				const item = el.closest(RATING_CONFIG.ITEM_SELECTOR);
				if (item && item.dataset.jhsrdFromCache === "true") {
					delete item.dataset.jhsrdLoaded;
					delete item.dataset.jhsrdFromCache;
				}
			});
			const pending = new Set();
			const flushPending = RatingUtils.debounce(() => {
				pending.forEach((item) => this.processItem(item));
				pending.clear();
			}, 150);
			new MutationObserver((muts) => {
				for (const mutation of muts) {
					for (const addedNode of mutation.addedNodes) {
						if (addedNode.nodeType !== 1) continue;
						const el = addedNode;
						if (el.matches(RATING_CONFIG.ITEM_SELECTOR)) pending.add(el);
						const found = el.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR);
						if (found && found.length) found.forEach((node) => pending.add(node));
					}
					const target = mutation.target;
					if (target instanceof Element && target.closest(".tags.has-addons")) {
						const item = target.closest(RATING_CONFIG.ITEM_SELECTOR);
						if (item) {
							item.dataset.jhsrdProcessed = "false";
							this.processItem(item);
						}
					}
				}
				flushPending();
			}).observe(document.body, {
				childList: true,
				subtree: true,
				attributes: true,
				attributeFilter: ["class"]
			});
			let hoverTimer = null;
			let hoveredItem = null;
			document.body.addEventListener("pointerover", (e) => {
				const item = e.target?.closest(RATING_CONFIG.ITEM_SELECTOR);
				if (!item || item === hoveredItem) return;
				hoveredItem = item;
				if (item.dataset.jhsrdLoaded === "true") return;
				const tag = item.querySelector(".jhs-user-rating.is-placeholder");
				if (!tag) return;
				if (hoverTimer) clearTimeout(hoverTimer);
				hoverTimer = setTimeout(() => {
					if (hoveredItem !== item) return;
					tag.classList.add("is-loading");
					fetchRating(item).finally(() => {
						tag.classList.remove("is-loading");
					});
				}, RATING_CONFIG.HOVER_DELAY);
			});
			document.body.addEventListener("pointerout", (e) => {
				const item = e.target?.closest(RATING_CONFIG.ITEM_SELECTOR);
				if (!item || item !== hoveredItem) return;
				hoveredItem = null;
				if (hoverTimer) clearTimeout(hoverTimer);
				const tag = item.querySelector(".jhs-user-rating.is-loading");
				if (tag) tag.classList.remove("is-loading");
			});
			GM_registerMenuCommand("全量加载当前页评分", () => {
				this.loadAll();
			});
			GM_registerMenuCommand("清空当前页评分缓存", () => {
				this.clearPageCache();
			});
			GM_registerMenuCommand("清空所有评分缓存", () => {
				RatingCache.clear();
				this.refreshAll();
				this.toast("所有评分缓存已清空");
			});
			GM_registerMenuCommand("刷新已看列表", async () => {
				this.watchedMap = await buildWatchedMap();
				this.refreshAll();
				this.toast(`已看: ${this.watchedMap.size} 项 · 缓存: ${RatingCache.size()} 条`);
			});
			document.addEventListener("jdb:want-watched-sync", (e) => {
				const { carNum, status, op, score } = e.detail || {};
				if (!carNum || !status || !op) return;
				const code = RatingUtils.normalizeCode(carNum);
				RatingUtils.log("SYNC_EVENT", code, status, op, score);
				this._invalidateCards(code, status, op, score);
			}, { passive: true });
			window.addEventListener("storage", (e) => {
				if (e.key === "jdb:want-watched-sync" && e.newValue) try {
					const payload = JSON.parse(e.newValue);
					if (payload && payload.carNum && payload.status && payload.op) {
						const code = RatingUtils.normalizeCode(payload.carNum);
						RatingUtils.log("STORAGE_SYNC", "跨标签页精确刷新", code, payload.status, payload.op);
						this._invalidateCards(code, payload.status, payload.op, payload.score);
					}
				} catch {
					this._dirty = true;
				}
			});
			document.addEventListener("visibilitychange", () => {
				if (document.hidden) return;
				document.querySelectorAll(".jhs-user-rating.is-rated").forEach((el) => {
					const item = el.closest(RATING_CONFIG.ITEM_SELECTOR);
					if (item) item.dataset.jhsrdLoaded = "false";
				});
				if (this._dirty) {
					this._dirty = false;
					this.refreshAll();
				}
			}, { passive: true });
			RatingUtils.log("INIT", `已看 ${this.watchedMap.size} · 缓存 ${RatingCache.size()}`);
		}
		async handle() {
			await this.init();
		}
	};
	var panelInfoStyle$1 = { marginTop: "20px" };
	var toolbarStyle$1 = { margin: "30px 0" };
	var videoPanelStyle = { marginTop: "20px" };
	var magnetsStyle = { margin: "0 0.75rem" };
	var hiddenStyle$2 = { display: "none" };
	function Fc2DetailDialog() {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "movie-detail-container",
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					className: "movie-info-container",
					children: (0, import_jsx_runtime.jsx)("div", {
						className: "search-loading",
						children: "加载中..."
					})
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "movie-panel-info fc2-movie-panel-info",
					style: panelInfoStyle$1,
					children: (0, import_jsx_runtime.jsx)("strong", { children: "第三方资源: " })
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: toolbarStyle$1,
					children: [
						(0, import_jsx_runtime.jsx)("a", {
							id: "filterBtn",
							className: "jhs-toolbar-menu-btn",
							style: { backgroundColor: BLOCK_COLOR },
							children: (0, import_jsx_runtime.jsx)("span", { children: BLOCK_TEXT })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "favoriteBtn",
							className: "jhs-toolbar-menu-btn",
							style: { backgroundColor: FAVORITE_COLOR },
							children: (0, import_jsx_runtime.jsx)("span", { children: FAVORITE_TEXT })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "hasDownBtn",
							className: "jhs-toolbar-menu-btn",
							style: { backgroundColor: HAS_DOWN_COLOR },
							children: (0, import_jsx_runtime.jsx)("span", { children: HAS_DOWN_TEXT$1 })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "hasWatchBtn",
							className: "jhs-toolbar-menu-btn",
							style: { backgroundColor: WATCHED_COLOR },
							children: (0, import_jsx_runtime.jsx)("span", { children: WATCHED_TEXT })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "search-subtitle-btn",
							className: "jhs-toolbar-menu-btn fr-btn",
							style: { background: "linear-gradient(to bottom, #8d5656, rgb(196,159,91))" },
							children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (SubTitleCat)" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "xunLeiSubtitleBtn",
							className: "jhs-toolbar-menu-btn fr-btn",
							style: { background: "linear-gradient(to left, #375f7c, #2196F3)" },
							children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (迅雷)" })
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "magnetSearchBtn",
							className: "jhs-toolbar-menu-btn fr-btn",
							style: {
								width: "120px",
								background: "linear-gradient(to right, rgb(245,140,1), rgb(84,161,29))",
								color: "white",
								textAlign: "center",
								padding: "8px 0"
							},
							children: (0, import_jsx_runtime.jsx)("span", { children: "磁力搜索" })
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "message video-panel",
					style: videoPanelStyle,
					children: (0, import_jsx_runtime.jsx)("div", {
						id: "magnets-content",
						className: "magnet-links",
						style: magnetsStyle,
						children: (0, import_jsx_runtime.jsx)("div", {
							className: "search-loading",
							children: "加载中..."
						})
					})
				}),
				(0, import_jsx_runtime.jsx)("div", { id: "reviews-content" }),
				(0, import_jsx_runtime.jsx)("div", { id: "related-content" }),
				(0, import_jsx_runtime.jsx)("span", {
					id: "data-actress",
					style: hiddenStyle$2
				})
			]
		});
	}
	function Fc2MagnetItem({ magnet, rowClass }) {
		const magnetUri = `magnet:?xt=urn:btih:${magnet.hash}`;
		return (0, import_jsx_runtime.jsxs)("div", {
			className: `item columns is-desktop ${rowClass}`,
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					className: "magnet-name column is-four-fifths",
					children: (0, import_jsx_runtime.jsxs)("a", {
						href: magnetUri,
						title: "右鍵點擊並選擇「複製鏈接地址」",
						children: [
							(0, import_jsx_runtime.jsx)("span", {
								className: "name",
								children: magnet.name
							}),
							(0, import_jsx_runtime.jsx)("br", {}),
							(0, import_jsx_runtime.jsxs)("span", {
								className: "meta",
								children: [
									(magnet.size / 1024).toFixed(2),
									"GB, ",
									magnet.files_count,
									"個文件"
								]
							}),
							(0, import_jsx_runtime.jsx)("br", {}),
							(0, import_jsx_runtime.jsxs)("div", {
								className: "tags",
								children: [magnet.hd ? (0, import_jsx_runtime.jsx)("span", {
									className: "tag is-primary is-small is-light",
									children: "高清"
								}) : "", magnet.cnsub ? (0, import_jsx_runtime.jsx)("span", {
									className: "tag is-warning is-small is-light",
									children: "字幕"
								}) : ""]
							})
						]
					})
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "buttons column",
					children: (0, import_jsx_runtime.jsx)("button", {
						className: "button is-info is-small copy-to-clipboard",
						"data-clipboard-text": magnetUri,
						type: "button",
						children: "\xA0複製\xA0"
					})
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "date column",
					children: (0, import_jsx_runtime.jsx)("span", {
						className: "time",
						children: magnet.created_at
					})
				})
			]
		});
	}
	var galleryStyle$1 = { marginTop: "10px" };
	var fc2MarketStyle = { marginLeft: "5px" };
	var hiddenStyle$1 = { display: "none" };
	function Fc2MovieDetail({ detail, actors, imgList }) {
		const carNumWithoutPrefix = (detail.carNum || "").replace("FC2-", "");
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("h3", {
				className: "movie-title",
				children: (0, import_jsx_runtime.jsx)("strong", {
					className: "current-title",
					children: detail.title || "无标题"
				})
			}),
			(0, import_jsx_runtime.jsxs)("div", {
				className: "movie-meta",
				children: [
					(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "番号: " }), detail.carNum || "未知"] }),
					(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "年份: " }), detail.releaseDate || "未知"] }),
					(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "评分: " }), detail.score || "无"] }),
					(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "时长: " }), detail.duration + " m" || "无"] })
				]
			}),
			(0, import_jsx_runtime.jsx)("div", {
				className: "movie-meta",
				children: (0, import_jsx_runtime.jsxs)("span", { children: [
					(0, import_jsx_runtime.jsx)("strong", { children: "站点: " }),
					(0, import_jsx_runtime.jsx)("a", {
						href: `https://fc2ppvdb.com/articles/${carNumWithoutPrefix}`,
						target: "_blank",
						children: "fc2ppvdb"
					}),
					(0, import_jsx_runtime.jsx)("a", {
						style: fc2MarketStyle,
						href: `https://adult.contents.fc2.com/article/${carNumWithoutPrefix}/`,
						target: "_blank",
						children: "fc2电子市场"
					})
				] })
			}),
			(0, import_jsx_runtime.jsx)("div", {
				className: "movie-actors",
				children: (0, import_jsx_runtime.jsxs)("div", {
					className: "actor-list",
					children: [(0, import_jsx_runtime.jsx)("strong", { children: "主演: " }), actors.length > 0 ? actors.map((actor) => (0, import_jsx_runtime.jsx)("span", {
						className: "actor-tag",
						children: (0, import_jsx_runtime.jsx)("a", {
							href: `/actors/${actor.id}`,
							target: "_blank",
							children: actor.name
						})
					})) : (0, import_jsx_runtime.jsx)("span", {
						className: "no-data",
						children: "暂无演员信息"
					})]
				})
			}),
			(0, import_jsx_runtime.jsxs)("div", {
				className: "movie-gallery",
				style: galleryStyle$1,
				children: [(0, import_jsx_runtime.jsx)("strong", { children: "剧照: " }), (0, import_jsx_runtime.jsx)("div", {
					className: "image-list",
					children: Array.isArray(imgList) && imgList.length > 0 ? imgList.map((img, idx) => (0, import_jsx_runtime.jsx)("a", {
						href: img,
						"data-fancybox": "movie-gallery",
						"data-caption": `剧照 ${idx + 1}`,
						children: (0, import_jsx_runtime.jsx)("img", {
							src: img,
							className: "movie-image-thumb",
							alt: ""
						})
					})) : (0, import_jsx_runtime.jsx)("div", {
						className: "no-data",
						children: "暂无剧照"
					})
				})]
			}),
			(0, import_jsx_runtime.jsx)("div", {
				id: "data-releaseDate",
				style: hiddenStyle$1,
				children: detail.releaseDate || ""
			})
		] });
	}
	function MovieError({ message }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "movie-error",
			children: ["加载失败: ", message]
		});
	}
	var innerStyle = {
		marginTop: "50px",
		color: "#000",
		cursor: "auto"
	};
	function ScreenLoadingPlaceholder({ maxHeight }) {
		return (0, import_jsx_runtime.jsx)("a", {
			className: "tile-item screen-container",
			style: {
				overflow: "hidden",
				maxHeight,
				maxWidth: maxHeight,
				textAlign: "center"
			},
			children: (0, import_jsx_runtime.jsx)("div", {
				style: innerStyle,
				children: "正在加载缩略图"
			})
		});
	}
	var fc2_plugin_default = "/* Fc2Plugin 弹层样式(提取自未删减版 jhs.user.js Fc2Plugin.initCss) */\n\n/* 弹层样式 */\n.movie-detail-layer .layui-layer-title {\n    font-size: 18px;\n    color: #333;\n    background: #f8f8f8;\n}\n\n/* 容器样式 */\n.movie-detail-container {\n    margin: 40px;\n    height: 100%;\n    background: #fff;\n}\n\n.movie-poster-container {\n    flex: 0 0 60%;\n    padding: 15px;\n}\n\n.right-box {\n    flex: 1;\n    padding: 20px;\n    overflow-y: auto;\n}\n\n/* 预告片iframe */\n.movie-trailer {\n    width: 100%;\n    height: 100%;\n    min-height: 400px;\n    background: #000;\n    border-radius: 4px;\n}\n\n/* 电影信息样式 */\n.movie-title {\n    font-size: 24px;\n    margin-bottom: 15px;\n    color: #333;\n}\n\n.movie-meta {\n    margin-bottom: 20px;\n    color: #666;\n}\n\n.movie-meta span {\n    margin-right: 15px;\n}\n\n/* 演员列表 */\n.actor-list {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n    margin-top: 10px;\n}\n\n.actor-tag {\n    padding: 4px 12px;\n    background: #f0f0f0;\n    border-radius: 15px;\n    font-size: 12px;\n    color: #555;\n}\n\n/* 图片列表 */\n.image-list {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin-top: 10px;\n}\n\n.movie-image-thumb {\n    width: 120px;\n    height: 80px;\n    object-fit: cover;\n    border-radius: 4px;\n    cursor: pointer;\n    transition: transform 0.3s;\n}\n\n.movie-image-thumb:hover {\n    transform: scale(1.05);\n}\n\n/* 加载中和错误状态 */\n.search-loading,\n.movie-error {\n    padding: 40px;\n    text-align: center;\n    color: #999;\n}\n\n.movie-error {\n    color: #f56c6c;\n}\n\n.fancybox-container {\n    z-index: var(--jhs-z-top);\n}\n\n/* 错误提示样式 */\n.movie-not-found,\n.movie-error {\n    text-align: center;\n    padding: 30px;\n    color: #666;\n}\n\n.movie-not-found h3,\n.movie-error h3 {\n    color: #f56c6c;\n    margin: 15px 0;\n}\n\n.icon-warning,\n.icon-error {\n    font-size: 50px;\n    color: #e6a23c;\n}\n\n.icon-error {\n    color: #f56c6c;\n}\n\n.fc2-movie-panel-info .panel-block {\n    padding: 0 !important;\n}\n";
	var Fc2Plugin = class extends BasePlugin {
		getName() {
			return "Fc2Plugin";
		}
		async initCss() {
			return fc2_plugin_default;
		}
		async handle() {
			const fc2SearchHref = "/advanced_search?type=3&score_min=0&d=1";
			$(".navbar-item:contains(\"FC2\")").attr("href", fc2SearchHref);
			$(".tabs a:contains(\"FC2\")").attr("href", fc2SearchHref);
			if (currentHref.includes("advanced_search?type=3")) {
				$("h2.section-title").contents().first().replaceWith("Fc2PPV");
				$(".section .container > .box").remove();
			}
			if (currentHref.includes("collection_codes?movieId")) {
				$("section").html("");
				const params = new URLSearchParams(window.location.search);
				const movieId = params.get("movieId");
				const carNum = params.get("carNum");
				const url = params.get("url");
				if (movieId && carNum && url) this.openFc2Dialog(movieId, carNum, url);
			}
		}
		openFc2Dialog(movieId, carNum, url) {
			const carNumWithoutPrefix = carNum.replace("FC2-", "");
			if (url.includes("123av")) {
				const fc2By123Av = this.getBean("Fc2By123AvPlugin");
				if (fc2By123Av) {
					fc2By123Av.open123AvFc2Dialog(carNum, url);
					return;
				}
			}
			const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(Fc2DetailDialog, {}));
			layer.open({
				type: 1,
				title: carNum,
				content: dialogHtml,
				area: utils.getResponsiveArea(["70%", "90%"]),
				skin: "movie-detail-layer",
				scrollbar: false,
				success: (_layerEl, layerIndex) => {
					this.loadData(movieId, carNum);
					$("#favoriteBtn").on("click", async () => {
						const names = $("#data-actress").text();
						const publishTime = $("#data-releaseDate").text();
						await storageManager.saveCar({
							carNum,
							url,
							names,
							actionType: FAVORITE_ACTION,
							publishTime
						});
						window.refresh();
						layer.closeAll();
					});
					$("#filterBtn").on("click", (event) => {
						utils.q(event, `是否屏蔽${carNum}?`, async () => {
							const names = $("#data-actress").text();
							const publishTime = $("#data-releaseDate").text();
							await storageManager.saveCar({
								carNum,
								url,
								names,
								actionType: FILTER_ACTION,
								publishTime
							});
							window.refresh();
							layer.closeAll();
							if (window.location.href.includes("collection_codes?movieId")) utils.closePage();
						});
					});
					$("#hasDownBtn").on("click", async () => {
						const names = $("#data-actress").text();
						const publishTime = $("#data-releaseDate").text();
						await storageManager.saveCar({
							carNum,
							url,
							names,
							actionType: HAS_DOWN_ACTION,
							publishTime
						});
						window.refresh();
						layer.closeAll();
					});
					$("#hasWatchBtn").on("click", async () => {
						const names = $("#data-actress").text();
						const publishTime = $("#data-releaseDate").text();
						await storageManager.saveCar({
							carNum,
							url,
							names,
							actionType: HAS_WATCH_ACTION,
							publishTime
						});
						window.refresh();
						layer.closeAll();
					});
					$("#search-subtitle-btn").on("click", (event) => utils.openPage(`https://subtitlecat.com/index.php?search=${carNum}`, carNum, false, event));
					$("#xunLeiSubtitleBtn").on("click", () => this.getBean("DetailPageButtonPlugin")?.searchXunLeiSubtitle(carNum));
					$("#magnetSearchBtn").on("click", () => {
						const magnetHub = this.getBean("MagnetHubPlugin");
						if (!magnetHub) return;
						const magnetHubHtml = magnetHub.createMagnetHub(carNum);
						layer.open({
							type: 1,
							title: "磁力搜索",
							content: jsxToString((0, import_jsx_runtime.jsx)("div", { id: "magnetHubBox" })),
							area: utils.getResponsiveArea(["60%", "80%"]),
							scrollbar: false,
							success: () => {
								$("#magnetHubBox").append(magnetHubHtml);
							}
						});
					});
					this.getBean("OtherSitePlugin")?.loadOtherSite(carNumWithoutPrefix, carNum).then();
					utils.setupEscClose(layerIndex);
				},
				end() {
					if (window.location.href.includes("collection_codes?movieId")) utils.closePage();
				}
			});
		}
		loadData(movieId, carNum) {
			const carNumWithoutPrefix = carNum.replace("FC2-", "");
			this.handleMovieDetail(movieId);
			this.handleLongImg(carNumWithoutPrefix);
			this.handleMagnets(movieId);
			this.getBean("ReviewPlugin")?.showReview(String(movieId), $("#reviews-content")).then();
			this.getBean("RelatedPlugin")?.showRelated($("#related-content"), String(movieId)).then();
		}
		handleMovieDetail(movieId) {
			fetchMovieDetail(movieId).then((detail) => {
				const actors = detail.actors || [];
				const imgList = detail.imgList || [];
				let actressNames = "";
				for (let i = 0; i < actors.length; i++) {
					const actor = actors[i];
					if (actor.gender === 0) actressNames += actor.name + " ";
				}
				if (actressNames) $("#data-actress").text(actressNames);
				$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(Fc2MovieDetail, {
					detail,
					actors,
					imgList
				})));
				this.getBean("TranslatePlugin")?.translate(detail.carNum, false)?.then();
			}).catch((err) => {
				clog.error(err);
				$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: err instanceof Error ? err.message : String(err) })));
			});
		}
		handleLongImg(carNumWithoutPrefix) {
			utils.loopDetector(() => $(".movie-gallery .image-list").length > 0, async () => {
				$(".movie-gallery .image-list").prepend(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "150px" })));
				const screenShot = this.getBean("ScreenShotPlugin");
				if (!screenShot) return;
				const screenshotUrl = await screenShot.getScreenshot(carNumWithoutPrefix);
				if (screenshotUrl) await screenShot.addImg("缩略图", screenshotUrl);
			});
		}
		handleMagnets(movieId) {
			(async () => {
				const url = `${API_BASE}/v1/movies/${movieId}/magnets`;
				const headers = { jdSignature: await reBuildSignature() };
				return (await gmHttp.get(url, void 0, headers)).data.magnets;
			})().then((magnets) => {
				let magnetsHtml = "";
				if (magnets.length > 0) for (let i = 0; i < magnets.length; i++) {
					const magnet = magnets[i];
					let rowClass = "";
					if (i % 2 === 0) rowClass = "odd";
					magnetsHtml += jsxToString((0, import_jsx_runtime.jsx)(Fc2MagnetItem, {
						magnet,
						rowClass
					}));
				}
				else magnetsHtml = jsxToString((0, import_jsx_runtime.jsx)("span", {
					className: "no-data",
					children: "暂无磁力信息"
				}));
				$("#magnets-content").html(magnetsHtml);
			}).catch((err) => {
				clog.error(err);
				$("#magnets-content").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: err instanceof Error ? err.message : String(err) })));
			});
		}
		async openFc2Page(movieId, carNum, url) {
			const javdbUrl = await this.getBean("OtherSitePlugin")?.getJavDbUrl();
			window.open(`${javdbUrl}/users/collection_codes?movieId=${encodeURIComponent(movieId)}&carNum=${encodeURIComponent(carNum)}&url=${encodeURIComponent(url)}`, "_blank", "noopener,noreferrer");
		}
	};
	var SELECTOR_LEFT = "body > section > div > nav > a.pagination-previous";
	var SELECTOR_RIGHT = "body > section > div > nav > a.pagination-next";
	var KeyPageTurningPlugin = class extends BasePlugin {
		lockLeft = false;
		lockRight = false;
		getName() {
			return "KeyPageTurningPlugin";
		}
		get pageTypes() {
			return [
				"list",
				"fc2",
				"unknown"
			];
		}
		async initCss() {
			return "";
		}
		async handle() {
			if (window.isDetailPage) return;
			window.addEventListener("keydown", (e) => {
				if (this.isTyping()) return;
				if (e.key === "ArrowLeft" && !this.lockLeft) {
					const a = document.querySelector(SELECTOR_LEFT) || null;
					if (a) {
						e.preventDefault();
						this.safeClick(a);
						this.lockLeft = true;
					}
				} else if (e.key === "ArrowRight" && !this.lockRight) {
					const a = document.querySelector(SELECTOR_RIGHT) || null;
					if (a) {
						e.preventDefault();
						this.safeClick(a);
						this.lockRight = true;
					}
				}
			}, { passive: false });
			window.addEventListener("keyup", (e) => {
				if (e.key === "ArrowLeft") this.lockLeft = false;
				if (e.key === "ArrowRight") this.lockRight = false;
			});
		}
		isTyping() {
			const el = document.activeElement;
			if (!el) return false;
			const tag = el.tagName;
			return el.isContentEditable || tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT";
		}
		safeClick(a) {
			if (!a) return false;
			if (typeof a.click === "function") {
				a.click();
				return true;
			}
			const evt = new MouseEvent("click", {
				bubbles: true,
				cancelable: true
			});
			return a.dispatchEvent(evt);
		}
	};
	var ModMyListOpenWayPlugin = class extends BasePlugin {
		getName() {
			return "ModMyListOpenWayPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			if (!location.pathname.includes("/users/lists")) return;
			const segments = location.pathname.split("/").filter(Boolean);
			const last = segments[segments.length - 1];
			const ul = document.querySelector("#lists > ul");
			if (!ul) return;
			const lis = ul.children;
			const isListsPage = last === "lists";
			const page = Number(new URLSearchParams(location.search).get("page")) || 1;
			if (isListsPage && page < 2) lis[0].style.display = "none";
			for (let i = isListsPage && page < 2 ? 1 : 0; i < lis.length; i++) {
				const a = lis[i].querySelector("div.column.is-10 > a");
				if (!a) continue;
				a.target = "_blank";
				if (isListsPage) a.href = `/lists/${new URL(a.href).searchParams.get("id")}`;
			}
		}
	};
	var page_sort_plugin_default = "/**\n * page-sort 插件样式 —— 排序按钮选中态。\n *\n * 原脚本 injectSortStyles() 用 document.createElement('style') 注入:\n *   style.textContent = `\n *       .button.is-small.selected-method,\n *       .button.is-small.selected-method:hover {\n *           background-color: rgb(25, 135, 84) !important;\n *           border-color: transparent !important;\n *           color: #fff !important;\n *       }\n *   `;\n *\n * 此处提取为独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入。\n * CSS 规则内容与原脚本一致(缩进标准化为 4 空格,CSS 解析忽略空白,渲染等价)。\n */\n.button.is-small.selected-method,\n.button.is-small.selected-method:hover {\n    background-color: rgb(25, 135, 84) !important;\n    border-color: transparent !important;\n    color: #fff !important;\n}\n";
	var LOG$2 = "[pageSort]";
	function compareItems($itemA, $itemB) {
		const getStrongText = ($item) => {
			const $strong = $item.find("a > div.video-title > strong");
			return $strong.length ? $strong.text().trim() : "";
		};
		const textA = getStrongText($itemA);
		const textB = getStrongText($itemB);
		return textA.localeCompare(textB);
	}
	function getScore($item) {
		const $span = $item.find("a > div.score > span");
		if (!$span.length) return 0;
		const match = $span.text().trim().match(/(\d+\.\d+)/);
		if (!match) return 0;
		return parseFloat(match[1]) || 0;
	}
	function newOption(text) {
		return $("<span>", {
			text,
			class: "button is-small"
		});
	}
	var SORT_CONFIGS = [
		{
			label: "按照名称升序",
			sortFn: (items) => items.sort((a, b) => compareItems($(a), $(b))),
			implemented: true
		},
		{
			label: "按照名称降序",
			sortFn: (items) => items.sort((a, b) => compareItems($(b), $(a))),
			implemented: true
		},
		{
			label: "按照评分升序",
			sortFn: (items) => items.sort((a, b) => getScore($(a)) - getScore($(b))),
			implemented: true
		},
		{
			label: "按照评分降序",
			sortFn: (items) => items.sort((a, b) => getScore($(b)) - getScore($(a))),
			implemented: true
		}
	];
	var PageSortPlugin = class extends BasePlugin {
		$container = null;
		activeSort = null;
		isApplyingSort = false;
		sortGuardRetries = 0;
		MAX_GUARD_RETRIES = 5;
		sortGuard = null;
		$buttons = null;
		getName() {
			return "PageSortPlugin";
		}
		async initCss() {
			return page_sort_plugin_default;
		}
		async handle() {
			if (!window.isListPage) return;
			this.waitForContainer();
		}
		waitForContainer() {
			if (this.createSortSelector()) return;
			console.log(`${LOG$2} 页面容器未就绪,开始 MutationObserver 等待`);
			new MutationObserver((_mutations, obs) => {
				if (this.createSortSelector()) obs.disconnect();
			}).observe(document.body, {
				childList: true,
				subtree: true
			});
		}
		createSortSelector() {
			const $toolbar = $("body > section > div > div.toolbar");
			if (!$toolbar.length) {
				console.warn(`${LOG$2} 未找到工具栏 .toolbar,跳过排序选择器注入`);
				return false;
			}
			const $container = $("body > section > div > div.movie-list.h.cols-4.vcols-8");
			if (!$container.length) {
				console.warn(`${LOG$2} 未找到视频列表容器 .movie-list,跳过排序选择器注入`);
				return false;
			}
			this.$container = $container;
			const $items = $container.children(".item");
			if ($items.length === 0) {
				console.warn(`${LOG$2} 视频列表为空,跳过排序选择器注入`);
				return false;
			}
			$items.each(function(i) {
				if (!this.hasAttribute("data-original-index")) this.setAttribute("data-original-index", String(i));
			});
			const sortGuard = new MutationObserver(() => {
				if (this.activeSort && !this.isApplyingSort) {
					if (this.sortGuardRetries >= this.MAX_GUARD_RETRIES) {
						console.warn(`${LOG$2} 排序守卫重试已达上限 (${this.MAX_GUARD_RETRIES}),放弃自动恢复`);
						return;
					}
					this.sortGuardRetries++;
					console.log(`${LOG$2} 检测到 DOM 顺序被外部修改,重新应用排序 (${this.sortGuardRetries}/${this.MAX_GUARD_RETRIES})`);
					this.applySort(this.activeSort);
				}
			});
			this.sortGuard = sortGuard;
			sortGuard.observe($container[0], { childList: true });
			const $buttonGroup = $("<div>", { css: {
				display: "inline-block",
				marginBottom: ".1rem",
				lineHeight: "2.2rem",
				marginRight: ".4rem",
				marginLeft: "auto"
			} });
			const $buttons = $("<div>", { class: "buttons has-addons" });
			this.$buttons = $buttons;
			$buttonGroup.append($buttons);
			$toolbar.append($buttonGroup);
			SORT_CONFIGS.forEach((config) => {
				$buttons.append(newOption(config.label));
			});
			$buttons.on("click", ".button.is-small", (e) => {
				e.preventDefault();
				e.stopPropagation();
				const $this = $(e.currentTarget);
				const isActive = $this.hasClass("selected-method");
				this.sortGuardRetries = 0;
				localStorage.removeItem("jhs_sortMethod");
				if (isActive) {
					$this.removeClass("selected-method");
					this.activeSort = null;
					this.applySort(null);
				} else {
					$this.siblings(".selected-method").removeClass("selected-method");
					$this.addClass("selected-method");
					const method = $this.text();
					this.activeSort = method;
					this.applySort(method);
				}
			});
			$("#sort-toggle-btn").on("click", () => {
				this.activeSort = null;
				this.sortGuardRetries = 0;
				if (this.$buttons) this.$buttons.find(".selected-method").removeClass("selected-method");
			});
			console.log(`${LOG$2} 排序选择器已注入,共 ${$items.length} 个影片`);
			return true;
		}
		applySort(sortMethod) {
			if (this.isApplyingSort) return;
			this.isApplyingSort = true;
			this.sortGuard?.disconnect();
			try {
				const currentItems = this.$container.children(".item").get();
				if (currentItems.length === 0) return;
				if (sortMethod === null) {
					currentItems.sort((a, b) => {
						return (parseInt(a.getAttribute("data-original-index") || "0") || 0) - (parseInt(b.getAttribute("data-original-index") || "0") || 0);
					});
					this.$container.append(currentItems);
					console.log(`${LOG$2} 已恢复原始顺序`);
				} else {
					const config = SORT_CONFIGS.find((c) => c.label === sortMethod);
					if (!config) {
						console.warn(`${LOG$2} 未识别的排序选项: ${sortMethod}`);
						return;
					}
					if (!config.implemented || !config.sortFn) {
						console.log(`${LOG$2} ${sortMethod} - 待实现`);
						return;
					}
					config.sortFn(currentItems);
					this.$container.append(currentItems);
					console.log(`${LOG$2} 已${sortMethod}排列 ${currentItems.length} 个影片`);
				}
				this.sortGuard?.takeRecords();
			} finally {
				if (this.$container && this.sortGuard) this.sortGuard.observe(this.$container[0], { childList: true });
				this.isApplyingSort = false;
			}
		}
	};
	var STATUS_TAG_SELECTOR = ".item .tags.has-addons .tag.is-success.status-tag";
	var JHS_HIDE_ATTR = "data-hide";
	var NO_TAG_VALUE$1 = "no-tag";
	function collectStatusTagCounts() {
		const counts = {};
		document.querySelectorAll(STATUS_TAG_SELECTOR).forEach((el) => {
			const itemEl = el.closest(".item");
			if (itemEl && itemEl.hasAttribute("data-hide")) return;
			const text = el.textContent?.trim() || "";
			if (text) counts[text] = (counts[text] || 0) + 1;
		});
		return counts;
	}
	function countNoStatusItems() {
		let count = 0;
		document.querySelectorAll(".item").forEach((item) => {
			if (item.hasAttribute("data-hide")) return;
			if (!item.querySelector(".item .tags.has-addons .tag.is-success.status-tag")) count++;
		});
		return count;
	}
	var HIDDEN_ATTR = "data-status-tag-hidden";
	function applyFilter$3() {
		const activeChips = document.querySelectorAll(".status-tag-filter-chip.active");
		const selectedValues = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
		if (selectedValues.size === 0) {
			document.querySelectorAll(`.item[${HIDDEN_ATTR}]`).forEach((item) => {
				const htmlItem = item;
				if (htmlItem.hasAttribute("data-hide")) return;
				htmlItem.removeAttribute(HIDDEN_ATTR);
				htmlItem.style.display = "";
			});
			return;
		}
		const showNoTag = selectedValues.has(NO_TAG_VALUE$1);
		const selectedTags = new Set([...selectedValues].filter((v) => v !== NO_TAG_VALUE$1));
		document.querySelectorAll(".item").forEach((item) => {
			const htmlItem = item;
			const hiddenByJhs = htmlItem.hasAttribute(JHS_HIDE_ATTR);
			const hiddenByOther = htmlItem.style.display === "none" && !htmlItem.hasAttribute(HIDDEN_ATTR);
			if (hiddenByJhs || hiddenByOther) return;
			const itemStatusTags = new Set();
			item.querySelectorAll(STATUS_TAG_SELECTOR).forEach((el) => {
				const text = el.textContent?.trim() || "";
				if (text) itemStatusTags.add(text);
			});
			let tagMatch = false;
			if (selectedTags.size > 0) tagMatch = [...selectedTags].some((t) => itemStatusTags.has(t));
			const noTagMatch = showNoTag && itemStatusTags.size === 0;
			if (tagMatch || noTagMatch) {
				htmlItem.removeAttribute(HIDDEN_ATTR);
				htmlItem.style.display = "";
			} else {
				htmlItem.setAttribute(HIDDEN_ATTR, "");
				htmlItem.style.display = "none";
			}
		});
	}
	var LOG$1 = "[状态标签筛选]";
	function createFilterChip$2(labelText, value, opts = {}) {
		const { isNoTag = false, count } = opts;
		const chip = document.createElement("span");
		chip.className = "status-tag-filter-chip";
		if (isNoTag) chip.classList.add("no-status");
		chip.textContent = count !== void 0 ? `${labelText} ${count}` : labelText;
		chip.dataset.value = value;
		chip.addEventListener("click", () => {
			chip.classList.toggle("active");
			applyFilter$3();
		});
		return chip;
	}
	function doBuild(mountTarget) {
		if (document.querySelector(".status-tag-filter-bar")) return;
		const filterBar = document.createElement("div");
		filterBar.className = "status-tag-filter-bar";
		const label = document.createElement("span");
		label.className = "status-tag-filter-label";
		label.textContent = "状态:";
		filterBar.appendChild(label);
		const chipsContainer = document.createElement("div");
		chipsContainer.className = "status-tag-filter-chips";
		filterBar.appendChild(chipsContainer);
		const refreshChips = () => {
			const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".status-tag-filter-chip.active")).map((c) => c.dataset.value || ""));
			chipsContainer.innerHTML = "";
			const noTagChip = createFilterChip$2("无状态标签", NO_TAG_VALUE$1, {
				isNoTag: true,
				count: countNoStatusItems()
			});
			if (activeValues.has("no-tag")) noTagChip.classList.add("active");
			chipsContainer.appendChild(noTagChip);
			const allCounts = collectStatusTagCounts();
			Object.keys(allCounts).sort().forEach((tagName) => {
				const chip = createFilterChip$2(tagName, tagName, { count: allCounts[tagName] });
				if (activeValues.has(tagName)) chip.classList.add("active");
				chipsContainer.appendChild(chip);
			});
		};
		refreshChips();
		if (mountTarget.matches(".jhs-vlt-filter-bar, .actor-tags.tags, .tabs.is-boxed")) mountTarget.insertAdjacentElement("afterend", filterBar);
		else mountTarget.insertAdjacentElement("beforebegin", filterBar);
		filterBar._refreshChips = refreshChips;
		console.log(`${LOG$1} 筛选栏已挂载`);
	}
	function findMountTarget() {
		const tagFilterBar = document.querySelector(".jhs-vlt-filter-bar");
		if (tagFilterBar) return tagFilterBar;
		if (/^\/actors\//.test(window.location.pathname)) {
			const actorTags = document.querySelector("body > section > div > div.actor-tags.tags");
			if (actorTags) return actorTags;
		} else {
			const tabsBoxed = document.querySelector("body > section > div > div.tabs.is-boxed");
			if (tabsBoxed) return tabsBoxed;
		}
		const section = document.querySelector("body > section > div > div");
		if (section && section.firstElementChild) {
			if (!section.firstElementChild.matches(".jhs-vlt-filter-bar, .status-tag-filter-bar")) return section.firstElementChild;
		}
		const container = document.querySelector("body > section > div");
		if (container && container.firstElementChild) {
			if (!container.firstElementChild.matches(".jhs-vlt-filter-bar, .status-tag-filter-bar")) return container.firstElementChild;
		}
		return null;
	}
	var status_tag_filter_plugin_default = "/**\n * status-tag-filter 插件样式 —— 状态标签筛选栏 + 芯片。\n *\n * 原脚本 GM_addStyle(`...`) 注入,此处提取为独立 .css,由 initCss() 返回 →\n * PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n\n.status-tag-filter-chip.active {\n    background: #198754;\n    border-color: #198754;\n    color: #fff;\n}\n\n.status-tag-filter-chip.no-status {\n    border-style: dashed;\n    color: #6c757d;\n}\n\n.status-tag-filter-chip.no-status.active {\n    background: #6c757d;\n    border-color: #6c757d;\n    border-style: solid;\n    color: #fff;\n}\n";
	var StatusTagFilterPlugin = class extends BasePlugin {
		observerDebounce = 0;
		supervisor = new TaskSupervisor();
		getName() {
			return "StatusTagFilterPlugin";
		}
		get pageTypes() {
			return ["list"];
		}
		async initCss() {
			return status_tag_filter_plugin_default;
		}
		async handle() {
			if (!window.isListPage) return;
			this.init();
		}
		init() {
			console.log(`${LOG$1} 初始化, pathname:`, window.location.pathname);
			if (!document.body) {
				console.warn(`${LOG$1} document.body 不存在,延后初始化`);
				this.supervisor.setTimeout(() => this.init(), 100);
				return;
			}
			if (this.tryBuild()) {
				this.startObserving();
				return;
			}
			console.log(`${LOG$1} 未找到挂载目标,开始 MutationObserver 等待`);
			let mountDebounce = 0;
			const mountObserver = this.supervisor.observe(document.body, () => {
				clearTimeout(mountDebounce);
				mountDebounce = this.supervisor.setTimeout(() => {
					if (this.tryBuild()) {
						console.log(`${LOG$1} MutationObserver 挂载成功`);
						mountObserver.disconnect();
						this.startObserving();
						clearTimeout(mountTimeout);
					}
				}, 100);
			}, {
				childList: true,
				subtree: true
			});
			const mountTimeout = this.supervisor.setTimeout(() => {
				mountObserver.disconnect();
				if (!document.querySelector(".status-tag-filter-bar")) {
					const fallback = findMountTarget() || document.body.firstElementChild;
					if (fallback) {
						doBuild(fallback);
						this.startObserving();
						console.warn(`${LOG$1} 通过最终回退挂载`, fallback);
					} else console.warn(`${LOG$1} 超时后仍无挂载目标`);
				}
			}, 1e4);
		}
		tryBuild() {
			if (document.querySelector(".status-tag-filter-bar")) return true;
			const mountTarget = findMountTarget();
			if (!mountTarget) return false;
			doBuild(mountTarget);
			return true;
		}
		updateFilterBar() {
			const filterBar = document.querySelector(".status-tag-filter-bar");
			if (filterBar && filterBar._refreshChips) {
				filterBar._refreshChips();
				applyFilter$3();
			}
		}
		startObserving() {
			this.supervisor.observe(document.body, (mutations) => {
				let shouldRefresh = false;
				for (const mutation of mutations) {
					for (const node of mutation.addedNodes) {
						if (node.nodeType !== Node.ELEMENT_NODE) continue;
						const el = node;
						if (el.matches?.(".item") || el.querySelectorAll?.(".item").length > 0 || el.matches?.(".tag.is-success.status-tag") || el.querySelectorAll?.(".tag.is-success.status-tag").length > 0) {
							shouldRefresh = true;
							break;
						}
					}
					if (shouldRefresh) break;
				}
				if (shouldRefresh) {
					clearTimeout(this.observerDebounce);
					this.observerDebounce = this.supervisor.setTimeout(() => {
						this.updateFilterBar();
					}, 150);
				}
			}, {
				childList: true,
				subtree: true
			});
		}
		destroy() {
			this.supervisor.abort();
		}
	};
	var SCROLL_TOP_SVG_PATH = "M12 4l-8 8h6v8h4v-8h6z";
	function ScrollTopIcon() {
		return (0, import_jsx_runtime.jsx)("svg", {
			viewBox: "0 0 24 24",
			children: (0, import_jsx_runtime.jsx)("path", { d: SCROLL_TOP_SVG_PATH })
		});
	}
	function scrollTopIconHtml() {
		return jsxToString((0, import_jsx_runtime.jsx)(ScrollTopIcon, {}));
	}
	function createBackToTopButton(options) {
		const id = options?.selector ?? "jdb-wf-back-to-top";
		const threshold = options?.threshold ?? 300;
		if (document.getElementById("jhs-back-to-top") || document.getElementById(id)) return () => {};
		const btn = document.createElement("div");
		btn.id = id;
		btn.title = "回到顶部";
		btn.innerHTML = scrollTopIconHtml();
		document.body.appendChild(btn);
		let ticking = false;
		const onScroll = () => {
			if (ticking) return;
			ticking = true;
			window.requestAnimationFrame(() => {
				if (window.scrollY > threshold) btn.classList.add("show");
				else btn.classList.remove("show");
				ticking = false;
			});
		};
		const onClick = () => {
			window.scrollTo({
				top: 0,
				behavior: "smooth"
			});
		};
		window.addEventListener("scroll", onScroll, { passive: true });
		btn.addEventListener("click", onClick);
		return () => {
			window.removeEventListener("scroll", onScroll);
			btn.removeEventListener("click", onClick);
			btn.remove();
		};
	}
	var LOG_PREFIX$3 = "[listWaterfall]";
	var MAX_PAGES = 200;
	var SEL = {
		box: "#lists > ul",
		item: "#lists > ul > li",
		nextLink: "a.pagination-next",
		paginationNav: "nav.pagination",
		itemLink: "div.column.is-10 > a"
	};
	function findNextUrl$1(doc) {
		const a = doc.querySelector(SEL.nextLink);
		return a ? a.getAttribute("href") : null;
	}
	function toAbsolute(href) {
		return new URL(href, location.href).href;
	}
	function rewriteItemLinks(container) {
		container.querySelectorAll(SEL.itemLink).forEach((a) => {
			const anchor = a;
			anchor.target = "_blank";
			const href = anchor.getAttribute("href") || "";
			if (/^\/lists\/[^/]+$/.test(href)) return;
			const m = href.match(/[?&]id=([^&]+)/);
			if (m) anchor.href = `/lists/${m[1]}`;
		});
	}
	function setState(plugin, cls, text) {
		if (!plugin.loader) return;
		plugin.loader.className = `jdb-wf-loader ${cls}`.trim();
		plugin.loader.textContent = text;
	}
	async function loadNextPage(plugin) {
		if (plugin.isLoading || !plugin.nextUrl || !plugin.hasMore) return;
		if (plugin.currentPage >= MAX_PAGES) {
			setState(plugin, "no-more", `已达最大页数 ${MAX_PAGES},停止加载`);
			plugin.hasMore = false;
			return;
		}
		plugin.isLoading = true;
		setState(plugin, "loading", "加载中...");
		try {
			const html = await fetchJavdbPageHtml(plugin.nextUrl);
			const doc = new DOMParser().parseFromString(html, "text/html");
			const remoteUl = doc.querySelector(SEL.box);
			if (!remoteUl) throw new Error("下一页未找到列表容器");
			const newLis = Array.from(remoteUl.children).filter((li) => li.tagName === "LI");
			if (newLis.length > 0 && newLis[0].id && plugin.loadedIds.has(newLis[0].id)) {
				plugin.hasMore = false;
				plugin.nextUrl = null;
				setState(plugin, "no-more", "已经到底了");
				console.log(`${LOG_PREFIX$3} 检测到重复数据,停止瀑布流`);
				return;
			}
			const topBefore = plugin.container.scrollHeight;
			const fragment = document.createDocumentFragment();
			newLis.forEach((li) => {
				if (li.id) plugin.loadedIds.add(li.id);
				fragment.appendChild(li);
			});
			plugin.container.appendChild(fragment);
			rewriteItemLinks(plugin.container);
			plugin.currentPage += 1;
			plugin.pageItems.push({
				page: plugin.currentPage,
				top: topBefore,
				url: plugin.nextUrl
			});
			const nextHref = findNextUrl$1(doc);
			plugin.nextUrl = nextHref ? toAbsolute(nextHref) : null;
			plugin.hasMore = !!plugin.nextUrl;
			const remoteNav = doc.querySelector(SEL.paginationNav);
			if (remoteNav) {
				const currentNav = document.querySelector(SEL.paginationNav);
				if (currentNav) currentNav.replaceWith(remoteNav);
			}
			setState(plugin, "", "");
			if (!plugin.hasMore) setState(plugin, "no-more", "已经到底了");
			console.log(`${LOG_PREFIX$3} 已加载第 ${plugin.currentPage} 页,追加 ${newLis.length} 项`);
		} catch (err) {
			clog.error(`${LOG_PREFIX$3} 加载失败:`, err);
			setState(plugin, "error", "加载失败,点击重试");
		} finally {
			plugin.isLoading = false;
		}
	}
	var PRELOAD_DISTANCE$1 = 800;
	function checkLoad(plugin) {
		if (!plugin.loader || !plugin.hasMore) return;
		if (plugin.loader.getBoundingClientRect().top < window.innerHeight + PRELOAD_DISTANCE$1) loadNextPage(plugin);
	}
	function updateCurrentPageFromScroll(plugin) {
		const y = window.scrollY;
		for (let i = plugin.pageItems.length - 1; i >= 0; i--) {
			const item = plugin.pageItems[i];
			if (y >= item.top) {
				if (plugin.currentPage !== item.page) plugin.currentPage = item.page;
				break;
			}
		}
	}
	var list_waterfall_plugin_default = "/**\n * list-waterfall 插件样式 —— 瀑布流 loader 三态 + 回到顶部按钮。\n *\n * 原脚本 GM_addStyle(`...`) 注入(L80-153),此处提取为独立 .css,由 initCss()\n * 返回 → PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进\n * 标准化,CSS 解析忽略空白,渲染等价)。\n */\n.jdb-wf-loader {\n    text-align: center;\n    padding: 24px 0 40px;\n    font-size: 0.9rem;\n    color: #6c757d;\n    min-height: 44px;\n    box-sizing: border-box;\n}\n\n.jdb-wf-loader.loading {\n    color: #485fc7;\n}\n\n.jdb-wf-loader.loading::before {\n    content: \"\";\n    display: inline-block;\n    width: 14px;\n    height: 14px;\n    margin-right: 8px;\n    border: 2px solid currentcolor;\n    border-top-color: transparent;\n    border-radius: 50%;\n    vertical-align: -2px;\n    animation: jdb-wf-spin 0.8s linear infinite;\n}\n\n.jdb-wf-loader.error {\n    color: #f44336;\n    cursor: pointer;\n    text-decoration: underline;\n}\n\n.jdb-wf-loader.no-more {\n    color: #4CAF50;\n}\n\n@keyframes jdb-wf-spin {\n    to {\n        transform: rotate(360deg);\n    }\n}\n";
	var ENABLED_KEY = "jdb:list-waterfall-enabled";
	var ALLOWED_PATHS = new Set(["/users/lists", "/users/favorite_lists"]);
	function getInitialPage() {
		const m = location.search.match(/[?&]page=(\d+)/);
		return m ? parseInt(m[1], 10) : 1;
	}
	function isEnabled() {
		return GM_getValue(ENABLED_KEY, true);
	}
	var ListWaterfallPlugin = class extends BasePlugin {
		currentPage = getInitialPage();
		nextUrl = null;
		hasMore = false;
		isLoading = false;
		loader = null;
		container = null;
		loadedIds = new Set();
		pageItems = [];
		_onScroll = null;
		cleanupBackToTop = null;
		supervisor = new TaskSupervisor();
		getName() {
			return "ListWaterfallPlugin";
		}
		async initCss() {
			return list_waterfall_plugin_default;
		}
		async handle() {
			if (!ALLOWED_PATHS.has(location.pathname)) return;
			if (!isEnabled()) {
				console.log(`${LOG_PREFIX$3} 瀑布流已关闭,退出`);
				return;
			}
			GM_registerMenuCommand(`${isEnabled() ? "✓" : "✗"} 清单瀑布流自动翻页`, this.toggleEnabled.bind(this));
			this.init();
		}
		toggleEnabled() {
			const next = !isEnabled();
			GM_setValue(ENABLED_KEY, next);
			console.log(`${LOG_PREFIX$3} 瀑布流开关: ${next ? "开启" : "关闭"}`);
			if (window.confirm(`清单瀑布流自动翻页已${next ? "开启" : "关闭"},是否刷新页面应用?`)) window.location.reload();
		}
		init() {
			this.container = document.querySelector(SEL.box);
			if (!this.container) {
				console.warn(`${LOG_PREFIX$3} 未找到容器 ${SEL.box},退出`);
				return;
			}
			this.container.querySelectorAll(SEL.item).forEach((li) => {
				if (li.id) this.loadedIds.add(li.id);
			});
			this.loader = document.createElement("div");
			this.loader.className = "jdb-wf-loader";
			this.container.parentNode.insertBefore(this.loader, this.container.nextSibling);
			this.supervisor.addEventListener(this.loader, "click", () => {
				if (this.loader.classList.contains("error")) this.loadNextPage();
			});
			const nextHref = findNextUrl$1(document);
			this.nextUrl = nextHref ? toAbsolute(nextHref) : null;
			this.hasMore = !!this.nextUrl;
			this.pageItems.push({
				page: this.currentPage,
				top: 0,
				url: window.location.href
			});
			this.cleanupBackToTop = createBackToTopButton();
			this._onScroll = () => {
				this.checkLoad();
				this.updateCurrentPageFromScroll();
			};
			this.supervisor.addEventListener(window, "scroll", this._onScroll, { passive: true });
			this.supervisor.setTimeout(() => this.checkLoad(), 500);
			if (!this.hasMore) this.setState("no-more", "已经到底了");
			else this.setState("", "");
			console.log(`${LOG_PREFIX$3} 已启动 (页码 ${this.currentPage})`);
		}
		async loadNextPage() {
			return loadNextPage(this);
		}
		checkLoad() {
			checkLoad(this);
		}
		updateCurrentPageFromScroll() {
			updateCurrentPageFromScroll(this);
		}
		setState(cls, text) {
			setState(this, cls, text);
		}
		destroy() {
			this.cleanupBackToTop?.();
			this.supervisor.abort();
		}
	};
	var LOG_PREFIX$2 = "[listReadingStatus]";
	var STORAGE_KEY = "jdb:list-reading-status";
	var RATING_STORAGE_KEY = "jdb:list-rating";
	var LAST_URI_KEY = "jdb:list-last-uri";
	var SORT_KEY = "jdb:list-sort";
	var FILTER_READ_KEY = "jdb:list-filter-read";
	var FILTER_RATING_KEY = "jdb:list-filter-rating";
	var JHS_DB_NAME = "JAV-JHS";
	var JHS_STORE_NAME = "appData";
	var BACKUP_KEY = "listReadingStatus_data";
	function getRatingMap() {
		const raw = GM_getValue(RATING_STORAGE_KEY);
		if (!raw) return {};
		try {
			return JSON.parse(raw);
		} catch {
			return {};
		}
	}
	function saveRatingMap(map) {
		GM_setValue(RATING_STORAGE_KEY, JSON.stringify(map));
		syncToIndexedDB();
	}
	function setRating(listId, rating) {
		const map = getRatingMap();
		if (rating > 0) map[listId] = rating;
		else delete map[listId];
		saveRatingMap(map);
		console.log(`${LOG_PREFIX$2} 评分 ${listId}: ${rating || "清除"}`);
	}
	function getRating(listId) {
		return getRatingMap()[listId] || 0;
	}
	function getLastUriMap() {
		const raw = GM_getValue(LAST_URI_KEY);
		if (!raw) return {};
		try {
			return JSON.parse(raw);
		} catch {
			return {};
		}
	}
	function saveLastUri(listId) {
		const map = getLastUriMap();
		map[listId] = {
			path: location.pathname + location.search,
			timestamp: Date.now()
		};
		GM_setValue(LAST_URI_KEY, JSON.stringify(map));
		syncToIndexedDB();
	}
	function getLastUri(listId) {
		return getLastUriMap()[listId] || null;
	}
	function getReadSet() {
		const raw = GM_getValue(STORAGE_KEY);
		if (!raw) return new Set();
		try {
			const arr = JSON.parse(raw);
			return new Set(Array.isArray(arr) ? arr : []);
		} catch {
			return new Set();
		}
	}
	function saveReadSet(readSet) {
		GM_setValue(STORAGE_KEY, JSON.stringify([...readSet]));
		syncToIndexedDB();
	}
	function markAsRead(listId) {
		const readSet = getReadSet();
		readSet.add(listId);
		saveReadSet(readSet);
		console.log(`${LOG_PREFIX$2} 已标记已读完: ${listId}`);
	}
	function markAsUnread(listId) {
		const readSet = getReadSet();
		readSet.delete(listId);
		saveReadSet(readSet);
		console.log(`${LOG_PREFIX$2} 已取消标记: ${listId}`);
	}
	function isRead(listId) {
		return getReadSet().has(listId);
	}
	function openJhsDB() {
		return new Promise((resolve, reject) => {
			const request = indexedDB.open(JHS_DB_NAME);
			request.onsuccess = () => resolve(request.result);
			request.onerror = () => reject(request.error);
			request.onupgradeneeded = (event) => {
				const db = event.target.result;
				if (!db.objectStoreNames.contains(JHS_STORE_NAME)) db.createObjectStore(JHS_STORE_NAME);
			};
		});
	}
	function syncToIndexedDB() {
		try {
			const data = {
				readingStatus: [...getReadSet()],
				ratings: getRatingMap(),
				lastUris: getLastUriMap(),
				_updatedAt: new Date().toISOString()
			};
			openJhsDB().then((db) => {
				const tx = db.transaction(JHS_STORE_NAME, "readwrite");
				tx.objectStore(JHS_STORE_NAME).put(data, BACKUP_KEY);
				tx.oncomplete = () => db.close();
				tx.onerror = () => db.close();
			}).catch(() => {});
		} catch {}
	}
	async function restoreFromIndexedDB() {
		try {
			const db = await openJhsDB();
			const store = db.transaction(JHS_STORE_NAME, "readonly").objectStore(JHS_STORE_NAME);
			const data = await new Promise((resolve, reject) => {
				const req = store.get(BACKUP_KEY);
				req.onsuccess = () => resolve(req.result);
				req.onerror = reject;
			});
			db.close();
			if (!data || !data.readingStatus) return false;
			let restored = false;
			const localReadSet = getReadSet();
			const cloudRead = Array.isArray(data.readingStatus) ? data.readingStatus : [];
			const mergedRead = new Set([...localReadSet, ...cloudRead]);
			if (mergedRead.size > localReadSet.size) {
				GM_setValue(STORAGE_KEY, JSON.stringify([...mergedRead]));
				restored = true;
			}
			const localRatings = getRatingMap();
			const cloudRatings = data.ratings && typeof data.ratings === "object" ? data.ratings : {};
			let ratingChanged = false;
			for (const [id, rating] of Object.entries(cloudRatings)) if (!(id in localRatings)) {
				localRatings[id] = rating;
				ratingChanged = true;
			}
			if (ratingChanged) {
				GM_setValue(RATING_STORAGE_KEY, JSON.stringify(localRatings));
				restored = true;
			}
			const localLastUris = getLastUriMap();
			const cloudLastUris = data.lastUris && typeof data.lastUris === "object" ? data.lastUris : {};
			let lastUriChanged = false;
			for (const [id, uri] of Object.entries(cloudLastUris)) {
				const local = localLastUris[id];
				const cloudUri = uri;
				if (!local || cloudUri.timestamp && cloudUri.timestamp > (local.timestamp || 0)) {
					localLastUris[id] = cloudUri;
					lastUriChanged = true;
				}
			}
			if (lastUriChanged) {
				GM_setValue(LAST_URI_KEY, JSON.stringify(localLastUris));
				restored = true;
			}
			if (restored) console.log(`${LOG_PREFIX$2} 已从云端合并恢复数据 (更新于 ${data._updatedAt || "未知"})`);
			return restored;
		} catch {
			return false;
		}
	}
	var STAR_PATH = "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z";
	var LRS_HIDDEN_ATTR = "data-lrs-hidden";
	var LRS_ORDER_ATTR = "data-lrs-order";
	function updateSelectAppearance(select) {
		select.setAttribute("data-status", select.value);
	}
	function renderStars(starsEl, rating) {
		starsEl.querySelectorAll(".list-rating-star").forEach((svg, i) => {
			if (i < rating) svg.classList.add("filled");
			else svg.classList.remove("filled");
		});
	}
	function createStarWidget(plugin, listId) {
		const container = document.createElement("span");
		container.className = "list-rating-stars";
		container.title = "评分 1-5 星,再次点击取消评分";
		const currentRating = getRating(listId);
		const NS = "http://www.w3.org/2000/svg";
		for (let i = 1; i <= 5; i++) {
			const svg = document.createElementNS(NS, "svg");
			svg.setAttribute("viewBox", "0 0 24 24");
			svg.classList.add("list-rating-star");
			svg.dataset.rating = String(i);
			const path = document.createElementNS(NS, "path");
			path.setAttribute("d", STAR_PATH);
			svg.appendChild(path);
			if (i <= currentRating) svg.classList.add("filled");
			plugin.supervisor.addEventListener(svg, "click", (e) => {
				e.stopPropagation();
				const clicked = parseInt(svg.dataset.rating || "0");
				const newRating = clicked === getRating(listId) ? 0 : clicked;
				setRating(listId, newRating);
				renderStars(container, newRating);
			});
			plugin.supervisor.addEventListener(svg, "mouseenter", () => {
				renderStars(container, parseInt(svg.dataset.rating || "0"));
			});
			container.appendChild(svg);
		}
		plugin.supervisor.addEventListener(container, "mouseleave", () => {
			renderStars(container, getRating(listId));
		});
		return container;
	}
	function createDropdown(plugin, listId) {
		const select = document.createElement("select");
		select.className = "list-reading-dropdown";
		const optionUnread = document.createElement("option");
		optionUnread.value = "unread";
		optionUnread.textContent = "未读完";
		const optionRead = document.createElement("option");
		optionRead.value = "read";
		optionRead.textContent = "已读完";
		select.appendChild(optionUnread);
		select.appendChild(optionRead);
		select.value = isRead(listId) ? "read" : "unread";
		updateSelectAppearance(select);
		plugin.supervisor.addEventListener(select, "change", () => {
			if (select.value === "read") markAsRead(listId);
			else markAsUnread(listId);
			updateSelectAppearance(select);
		});
		return select;
	}
	function ensureWidgets(plugin, li) {
		const listId = li.id;
		if (!listId) return;
		const container = li.querySelector(":scope > div");
		if (!container) return;
		let select = container.querySelector(".list-reading-dropdown");
		if (select) {
			select.value = isRead(listId) ? "read" : "unread";
			updateSelectAppearance(select);
		} else {
			select = createDropdown(plugin, listId);
			container.prepend(select);
		}
		let stars = container.querySelector(".list-rating-stars");
		if (stars) renderStars(stars, getRating(listId));
		else {
			stars = createStarWidget(plugin, listId);
			container.prepend(stars);
		}
		let uriLink = container.querySelector(".list-last-uri-link");
		const lastUri = getLastUri(listId);
		if (lastUri) {
			if (!uriLink) {
				uriLink = document.createElement("a");
				uriLink.className = "list-last-uri-link";
				const d = new Date(lastUri.timestamp);
				const pad = (n) => String(n).padStart(2, "0");
				const dateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
				const search = lastUri.path.includes("?") ? " " + lastUri.path.slice(lastUri.path.indexOf("?") + 1) : "";
				uriLink.textContent = `继续浏览 →${search} (${dateStr})`;
				container.appendChild(uriLink);
			}
			uriLink.href = lastUri.path;
		} else if (uriLink) uriLink.remove();
	}
	function ensureHeaderWidgets(plugin) {
		if (plugin.isProcessing) return;
		plugin.isProcessing = true;
		const m = location.pathname.match(/\/lists\/([^/]+)$/);
		if (!m) {
			plugin.isProcessing = false;
			return;
		}
		const listId = "list-" + m[1];
		saveLastUri(listId);
		const h2 = document.querySelector("body > section > div > div.columns.is-mobile.section-columns > div > h2");
		if (!h2) {
			plugin.isProcessing = false;
			return;
		}
		let select = h2.querySelector(".list-reading-dropdown");
		if (select) {
			select.value = isRead(listId) ? "read" : "unread";
			updateSelectAppearance(select);
		} else {
			select = createDropdown(plugin, listId);
			h2.prepend(select);
		}
		let stars = h2.querySelector(".list-rating-stars");
		if (stars) renderStars(stars, getRating(listId));
		else {
			stars = createStarWidget(plugin, listId);
			h2.prepend(stars);
		}
		Promise.resolve().then(() => {
			plugin.isProcessing = false;
		});
	}
	function processAllItems(plugin) {
		if (plugin.isProcessing) return false;
		plugin.isProcessing = true;
		const ul = document.querySelector("#lists > ul");
		if (!ul) {
			plugin.isProcessing = false;
			return false;
		}
		const items = ul.querySelectorAll(":scope > li");
		items.forEach((li) => ensureWidgets(plugin, li));
		plugin.applySortAndFilter();
		Promise.resolve().then(() => {
			plugin.isProcessing = false;
		});
		return items.length > 0;
	}
	function getListMeta(_plugin, li) {
		const listId = li.id || "";
		const titleEl = li.querySelector("a[href*='/lists/']");
		const title = titleEl ? titleEl.textContent.trim() : "";
		const metaTxt = li.querySelector(".meta")?.textContent ?? "";
		const moviesMatch = metaTxt.match(/([\d,]+)\s*部影片/);
		const clicksMatch = metaTxt.match(/點擊了\s*([\d,]+)/);
		return {
			listId,
			title,
			movies: moviesMatch ? parseInt(moviesMatch[1].replace(/,/g, "")) : 0,
			clicks: clicksMatch ? parseInt(clicksMatch[1].replace(/,/g, "")) : 0,
			rating: getRating(listId),
			visited: getLastUri(listId)?.timestamp ?? 0,
			order: parseInt(li.getAttribute("data-lrs-order") || "0") || 0
		};
	}
	function ensureLiOrder(plugin, li) {
		if (!li.hasAttribute("data-lrs-order")) li.setAttribute(LRS_ORDER_ATTR, String(plugin.orderCounter++));
	}
	function compareLi(plugin, a, b) {
		const ma = getListMeta(plugin, a);
		const mb = getListMeta(plugin, b);
		let cmp;
		switch (plugin.currentSort) {
			case "rating-desc":
				cmp = mb.rating - ma.rating;
				break;
			case "rating-asc":
				cmp = ma.rating - mb.rating;
				break;
			case "movies-desc":
				cmp = mb.movies - ma.movies;
				break;
			case "movies-asc":
				cmp = ma.movies - mb.movies;
				break;
			case "clicks-desc":
				cmp = mb.clicks - ma.clicks;
				break;
			case "clicks-asc":
				cmp = ma.clicks - mb.clicks;
				break;
			case "visited-desc":
				cmp = mb.visited - ma.visited;
				break;
			case "visited-asc":
				cmp = ma.visited - mb.visited;
				break;
			case "title-asc":
				cmp = ma.title.localeCompare(mb.title, "zh");
				break;
			default: cmp = 0;
		}
		if (cmp === 0) cmp = ma.order - mb.order;
		return cmp;
	}
	function applySort(plugin) {
		if (plugin.currentSort === "default") return;
		const ul = document.querySelector("#lists > ul");
		if (!ul) return;
		const lis = [...ul.querySelectorAll(":scope > li")];
		if (lis.length < 2) return;
		lis.forEach((li) => ensureLiOrder(plugin, li));
		lis.sort((a, b) => compareLi(plugin, a, b));
		if (lis.map((li) => li.id).join(",") === [...ul.querySelectorAll(":scope > li")].map((li) => li.id).join(",")) return;
		const frag = document.createDocumentFragment();
		lis.forEach((li) => frag.appendChild(li));
		ul.appendChild(frag);
	}
	function saveFilterReadState(plugin, v) {
		plugin.filterReadStatus = v;
		GM_setValue(FILTER_READ_KEY, v);
	}
	function saveFilterRatingState(plugin, set) {
		plugin.filterRatingChips = set;
		GM_setValue(FILTER_RATING_KEY, JSON.stringify([...set]));
	}
	function matchesFilter(plugin, li) {
		const listId = li.id;
		if (!listId) return true;
		if (plugin.filterReadStatus !== "all") {
			const read = isRead(listId);
			if (plugin.filterReadStatus === "read" && !read) return false;
			if (plugin.filterReadStatus === "unread" && read) return false;
		}
		if (plugin.filterRatingChips.size > 0) {
			const rating = getRating(listId);
			let match = false;
			if (plugin.filterRatingChips.has("rated") && rating > 0) match = true;
			if (plugin.filterRatingChips.has("unrated") && rating === 0) match = true;
			for (let i = 1; i <= 5; i++) if (plugin.filterRatingChips.has(`rating-${i}`) && rating === i) {
				match = true;
				break;
			}
			if (!match) return false;
		}
		return true;
	}
	function applyFilter$2(plugin) {
		const ul = document.querySelector("#lists > ul");
		if (!ul) return;
		const lis = ul.querySelectorAll(":scope > li");
		if (plugin.filterReadStatus === "all" && plugin.filterRatingChips.size === 0) {
			lis.forEach((li) => {
				const htmlLi = li;
				if (htmlLi.hasAttribute("data-lrs-hidden")) {
					htmlLi.removeAttribute(LRS_HIDDEN_ATTR);
					htmlLi.style.display = "";
				}
			});
			return;
		}
		lis.forEach((li) => {
			const htmlLi = li;
			if (htmlLi.style.display === "none" && !htmlLi.hasAttribute("data-lrs-hidden")) return;
			if (matchesFilter(plugin, htmlLi)) {
				htmlLi.removeAttribute(LRS_HIDDEN_ATTR);
				htmlLi.style.display = "";
			} else {
				htmlLi.setAttribute(LRS_HIDDEN_ATTR, "");
				htmlLi.style.display = "none";
			}
		});
	}
	function applySortAndFilter(plugin) {
		if (plugin.isToolbarProcessing) return;
		plugin.isToolbarProcessing = true;
		try {
			applySort(plugin);
			applyFilter$2(plugin);
		} finally {
			plugin.isToolbarProcessing = false;
		}
	}
	function countFilterStats() {
		const stats = {
			read: 0,
			unread: 0,
			rated: 0,
			unrated: 0,
			stars: [
				0,
				0,
				0,
				0,
				0
			]
		};
		const ul = document.querySelector("#lists > ul");
		if (!ul) return stats;
		ul.querySelectorAll(":scope > li").forEach((li) => {
			const id = li.id;
			if (!id) return;
			if (isRead(id)) stats.read++;
			else stats.unread++;
			const r = getRating(id);
			if (r > 0) {
				stats.rated++;
				if (r >= 1 && r <= 5) stats.stars[r - 1]++;
			} else stats.unrated++;
		});
		return stats;
	}
	function createFilterChip$1(plugin, label, value, count) {
		const chip = document.createElement("span");
		chip.className = "list-filter-chip";
		chip.dataset.value = value;
		const labelText = document.createElement("span");
		labelText.textContent = label;
		chip.appendChild(labelText);
		const countSpan = document.createElement("span");
		countSpan.className = "chip-count";
		countSpan.textContent = String(count);
		chip.appendChild(countSpan);
		plugin.supervisor.addEventListener(chip, "click", () => {
			if (value === "read" || value === "unread") {
				const wasActive = chip.classList.contains("active");
				document.querySelectorAll(".list-filter-chip[data-value='read'], .list-filter-chip[data-value='unread']").forEach((c) => c.classList.remove("active"));
				if (!wasActive) {
					chip.classList.add("active");
					saveFilterReadState(plugin, value);
				} else saveFilterReadState(plugin, "all");
			} else {
				chip.classList.toggle("active");
				const next = new Set(plugin.filterRatingChips);
				if (chip.classList.contains("active")) next.add(value);
				else next.delete(value);
				saveFilterRatingState(plugin, next);
			}
			applySortAndFilter(plugin);
		});
		return chip;
	}
	function loadToolbarState(plugin) {
		const s = GM_getValue(SORT_KEY);
		if (s) plugin.currentSort = s;
		const r = GM_getValue(FILTER_READ_KEY);
		if (r === "read" || r === "unread") plugin.filterReadStatus = r;
		const rc = GM_getValue(FILTER_RATING_KEY);
		if (rc) try {
			const arr = JSON.parse(rc);
			if (Array.isArray(arr)) plugin.filterRatingChips = new Set(arr);
		} catch {
			plugin.filterRatingChips = new Set();
		}
	}
	function saveSortState(plugin, v) {
		plugin.currentSort = v;
		GM_setValue(SORT_KEY, v);
	}
	function buildToolbar(plugin) {
		if (plugin.isDetailPage) return null;
		const lists = document.querySelector("#lists");
		if (!lists) return null;
		if (document.querySelector(".list-toolbar")) return document.querySelector(".list-toolbar");
		const toolbar = document.createElement("div");
		toolbar.className = "list-toolbar";
		const sortGroup = document.createElement("div");
		sortGroup.className = "list-toolbar-group";
		const sortLabel = document.createElement("span");
		sortLabel.className = "list-toolbar-label";
		sortLabel.textContent = "排序:";
		const sortSelect = document.createElement("select");
		sortSelect.className = "list-sort-select";
		[
			["default", "默认"],
			["rating-desc", "评分 ↓"],
			["rating-asc", "评分 ↑"],
			["movies-desc", "影片数 ↓"],
			["movies-asc", "影片数 ↑"],
			["clicks-desc", "点击数 ↓"],
			["clicks-asc", "点击数 ↑"],
			["visited-desc", "最近访问 ↓"],
			["visited-asc", "最久访问 ↓"],
			["title-asc", "标题 A-Z"]
		].forEach(([val, txt]) => {
			const opt = document.createElement("option");
			opt.value = val;
			opt.textContent = txt;
			if (val === plugin.currentSort) opt.selected = true;
			sortSelect.appendChild(opt);
		});
		plugin.supervisor.addEventListener(sortSelect, "change", () => {
			saveSortState(plugin, sortSelect.value);
			applySortAndFilter(plugin);
		});
		sortGroup.appendChild(sortLabel);
		sortGroup.appendChild(sortSelect);
		toolbar.appendChild(sortGroup);
		const div1 = document.createElement("div");
		div1.className = "list-filter-divider";
		toolbar.appendChild(div1);
		const readGroup = document.createElement("div");
		readGroup.className = "list-toolbar-group";
		const readLabel = document.createElement("span");
		readLabel.className = "list-toolbar-label";
		readLabel.textContent = "阅读:";
		readGroup.appendChild(readLabel);
		const readChipsHost = document.createElement("div");
		readGroup.appendChild(readChipsHost);
		toolbar.appendChild(readGroup);
		const div2 = document.createElement("div");
		div2.className = "list-filter-divider";
		toolbar.appendChild(div2);
		const ratingGroup = document.createElement("div");
		ratingGroup.className = "list-toolbar-group";
		const ratingLabel = document.createElement("span");
		ratingLabel.className = "list-toolbar-label";
		ratingLabel.textContent = "评分:";
		ratingGroup.appendChild(ratingLabel);
		const ratingChipsHost = document.createElement("div");
		ratingGroup.appendChild(ratingChipsHost);
		toolbar.appendChild(ratingGroup);
		const reset = document.createElement("a");
		reset.className = "list-toolbar-reset";
		reset.textContent = "重置筛选";
		plugin.supervisor.addEventListener(reset, "click", (e) => {
			e.preventDefault();
			saveSortState(plugin, "default");
			saveFilterReadState(plugin, "all");
			saveFilterRatingState(plugin, new Set());
			sortSelect.value = "default";
			toolbar.querySelectorAll(".list-filter-chip.active").forEach((c) => c.classList.remove("active"));
			applySortAndFilter(plugin);
		});
		toolbar.appendChild(reset);
		lists.insertAdjacentElement("beforebegin", toolbar);
		toolbar._readHost = readChipsHost;
		toolbar._ratingHost = ratingChipsHost;
		toolbar._sortSelect = sortSelect;
		refreshChips$1(plugin, toolbar);
		console.log(`${LOG_PREFIX$2} 排序与筛选工具栏已挂载`);
		return toolbar;
	}
	function refreshChips$1(plugin, toolbar) {
		const stats = countFilterStats();
		const readHost = toolbar._readHost;
		if (readHost) {
			readHost.innerHTML = "";
			const readChip = createFilterChip$1(plugin, "已读完", "read", stats.read);
			if (plugin.filterReadStatus === "read") readChip.classList.add("active");
			readHost.appendChild(readChip);
			const unreadChip = createFilterChip$1(plugin, "未读完", "unread", stats.unread);
			if (plugin.filterReadStatus === "unread") unreadChip.classList.add("active");
			readHost.appendChild(unreadChip);
		}
		const ratingHost = toolbar._ratingHost;
		if (ratingHost) {
			ratingHost.innerHTML = "";
			const ratedChip = createFilterChip$1(plugin, "有评分", "rated", stats.rated);
			if (plugin.filterRatingChips.has("rated")) ratedChip.classList.add("active");
			ratingHost.appendChild(ratedChip);
			const unratedChip = createFilterChip$1(plugin, "无评分", "unrated", stats.unrated);
			if (plugin.filterRatingChips.has("unrated")) unratedChip.classList.add("active");
			ratingHost.appendChild(unratedChip);
			for (let i = 1; i <= 5; i++) {
				const starChip = createFilterChip$1(plugin, `${i}星`, `rating-${i}`, stats.stars[i - 1]);
				if (plugin.filterRatingChips.has(`rating-${i}`)) starChip.classList.add("active");
				ratingHost.appendChild(starChip);
			}
		}
	}
	function tryBuildToolbar(plugin) {
		if (plugin.isDetailPage) return false;
		const toolbar = buildToolbar(plugin);
		if (!toolbar) return false;
		refreshChips$1(plugin, toolbar);
		applySortAndFilter(plugin);
		return true;
	}
	function restoreToolbarUI(plugin) {
		const toolbar = document.querySelector(".list-toolbar");
		if (!toolbar) return;
		const sortSelect = toolbar._sortSelect;
		if (sortSelect) sortSelect.value = plugin.currentSort;
		refreshChips$1(plugin, toolbar);
	}
	var list_reading_status_plugin_default = "/**\n * list-reading-status 插件样式 —— 阅读进度下拉框 + 星级评分 + 排序筛选工具栏。\n *\n * 原脚本 3 段 GM_addStyle(L263-303 下拉框 / L305-349 星级评分+访问链接 /\n * L353-445 工具栏),此处合并提取为独立 .css,由 initCss() 返回 →\n * PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n\n/* ---------- 阅读进度下拉框 ---------- */\n.list-reading-dropdown {\n    appearance: none;\n    -webkit-appearance: none;\n    display: inline-flex;\n    align-items: center;\n    height: 24px;\n    padding: 0 24px 0 10px;\n    font-size: 0.72rem;\n    font-weight: 600;\n    line-height: 22px;\n    border-radius: 50rem;\n    cursor: pointer;\n    user-select: none;\n    border: 1.5px solid #ced4da;\n    color: #6c757d;\n    background-color: #fff;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236c757d'/%3E%3C/svg%3E\");\n    background-repeat: no-repeat;\n    background-position: right 8px center;\n    background-size: 10px 6px;\n    transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;\n    margin-right: 6px;\n    vertical-align: middle;\n}\n\n.list-reading-dropdown:hover {\n    border-color: #adb5bd;\n    background-color: #f8f9fa;\n}\n\n.list-reading-dropdown[data-status=\"read\"] {\n    color: #fff;\n    background-color: #198754;\n    border-color: #198754;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.list-reading-dropdown[data-status=\"read\"]:hover {\n    background-color: #157347;\n    border-color: #157347;\n}\n\n/* ---------- 星级评分 ---------- */\n.list-rating-stars {\n    display: inline-flex;\n    gap: 2px;\n    cursor: pointer;\n    user-select: none;\n    margin-right: 10px;\n    vertical-align: middle;\n    align-items: center;\n}\n\n.list-rating-star {\n    width: 18px;\n    height: 18px;\n    flex-shrink: 0;\n    transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),\n                filter 0.2s ease;\n}\n\n.list-rating-star path {\n    fill: #cbd5e1;\n    stroke: #94a3b8;\n    stroke-width: 0.6;\n    transition: fill 0.2s ease, stroke 0.2s ease;\n}\n\n.list-rating-star.filled path {\n    fill: #f59e0b;\n    stroke: #d97706;\n}\n\n.list-rating-star:hover {\n    transform: scale(1.25);\n}\n\n.list-rating-star.filled:hover {\n    filter: drop-shadow(0 0 5px rgb(245, 158, 11, 0.45));\n}\n\n.list-last-uri-link {\n    font-size: 0.72rem;\n    color: #485fc7;\n    margin-left: 8px;\n    text-decoration: none;\n    transition: color 0.15s ease;\n}\n\n.list-last-uri-link:hover {\n    color: #363636;\n    text-decoration: underline;\n}\n\n/* ---------- 排序与筛选工具栏 ---------- */\n.list-toolbar {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    gap: 8px 16px;\n    padding: 10px 0;\n    margin-bottom: 6px;\n    border-bottom: 1px solid #e9ecef;\n    font-size: 0.82rem;\n}\n\n.list-toolbar-group {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    flex-wrap: wrap;\n}\n\n.list-toolbar-label {\n    font-weight: 600;\n    color: #6c757d;\n    flex-shrink: 0;\n}\n\n.list-sort-select {\n    appearance: none;\n    -webkit-appearance: none;\n    height: 26px;\n    padding: 0 22px 0 10px;\n    font-size: 0.78rem;\n    font-weight: 600;\n    border: 1.5px solid #ced4da;\n    border-radius: 50rem;\n    color: #495057;\n    background-color: #fff;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236c757d'/%3E%3C/svg%3E\");\n    background-repeat: no-repeat;\n    background-position: right 8px center;\n    background-size: 10px 6px;\n    cursor: pointer;\n    transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n\n.list-sort-select:hover {\n    border-color: #adb5bd;\n    background-color: #f8f9fa;\n}\n\n.list-filter-chip {\n    display: inline-flex;\n    align-items: center;\n    padding: 2px 10px;\n    font-size: 0.76rem;\n    font-weight: 600;\n    border: 1.5px solid #dee2e6;\n    border-radius: 50rem;\n    color: #6c757d;\n    background-color: #fff;\n    cursor: pointer;\n    user-select: none;\n    transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;\n    line-height: 1.4;\n}\n\n.list-filter-chip:hover {\n    border-color: #adb5bd;\n    background-color: #f8f9fa;\n}\n\n.list-filter-chip.active {\n    color: #fff;\n    background-color: #485fc7;\n    border-color: #485fc7;\n}\n\n.list-filter-chip .chip-count {\n    margin-left: 4px;\n    opacity: 0.85;\n    font-weight: 700;\n}\n\n.list-filter-divider {\n    width: 1px;\n    height: 18px;\n    background: #dee2e6;\n    margin: 0 4px;\n    flex-shrink: 0;\n}\n\n.list-toolbar-reset {\n    font-size: 0.74rem;\n    color: #adb5bd;\n    cursor: pointer;\n    text-decoration: none;\n    margin-left: auto;\n    transition: color 0.15s ease;\n}\n\n.list-toolbar-reset:hover {\n    color: #6c757d;\n    text-decoration: underline;\n}\n";
	var ListReadingStatusPlugin = class extends BasePlugin {
		isProcessing = false;
		currentSort = "default";
		filterReadStatus = "all";
		filterRatingChips = new Set();
		isToolbarProcessing = false;
		orderCounter = 0;
		lastLiCount = -1;
		supervisor = new TaskSupervisor();
		isDetailPage = false;
		getName() {
			return "ListReadingStatusPlugin";
		}
		async initCss() {
			return list_reading_status_plugin_default;
		}
		async handle() {
			const pathname = location.pathname;
			const isListPage = pathname.startsWith("/users/favorite_lists") || pathname.startsWith("/users/lists");
			const isDetail = /\/lists\/[^/]+$/.test(pathname);
			if (!isListPage && !isDetail) return;
			this.isDetailPage = isDetail;
			this.loadToolbarState();
			if (this.isDetailPage) this.ensureHeaderWidgets();
			else {
				this.processAllItems();
				this.tryBuildToolbar();
			}
			restoreFromIndexedDB().then((restored) => {
				if (restored) {
					if (this.isDetailPage) this.ensureHeaderWidgets();
					else {
						this.processAllItems();
						const tb = document.querySelector(".list-toolbar");
						if (tb) this.refreshChips(tb);
					}
				}
			});
			this.supervisor.observe(document.body, () => {
				if (this.isProcessing) return;
				if (this.isDetailPage) this.ensureHeaderWidgets();
				else {
					this.processAllItems();
					const ul = document.querySelector("#lists > ul");
					const cnt = ul ? ul.querySelectorAll(":scope > li").length : 0;
					if (cnt !== this.lastLiCount) {
						this.lastLiCount = cnt;
						const tb = document.querySelector(".list-toolbar");
						if (tb) this.refreshChips(tb);
					}
				}
			}, {
				childList: true,
				subtree: true
			});
			if (typeof GM_addValueChangeListener !== "undefined") {
				const refreshListToolbar = () => {
					if (this.isDetailPage) return;
					const tb = document.querySelector(".list-toolbar");
					if (tb) this.refreshChips(tb);
				};
				GM_addValueChangeListener(STORAGE_KEY, () => {
					if (this.isDetailPage) this.ensureHeaderWidgets();
					else {
						this.processAllItems();
						refreshListToolbar();
					}
				});
				GM_addValueChangeListener(RATING_STORAGE_KEY, () => {
					if (this.isDetailPage) this.ensureHeaderWidgets();
					else {
						this.processAllItems();
						refreshListToolbar();
					}
				});
				GM_addValueChangeListener(SORT_KEY, () => {
					const v = GM_getValue(SORT_KEY);
					if (v) this.currentSort = v;
					if (this.isDetailPage) return;
					this.restoreToolbarUI();
					this.applySortAndFilter();
				});
				GM_addValueChangeListener(FILTER_READ_KEY, () => {
					const v = GM_getValue(FILTER_READ_KEY);
					if (v === "read" || v === "unread") this.filterReadStatus = v;
					else this.filterReadStatus = "all";
					if (this.isDetailPage) return;
					this.restoreToolbarUI();
					this.applySortAndFilter();
				});
				GM_addValueChangeListener(FILTER_RATING_KEY, () => {
					const rc = GM_getValue(FILTER_RATING_KEY);
					try {
						const arr = JSON.parse(rc);
						this.filterRatingChips = new Set(Array.isArray(arr) ? arr : []);
					} catch {
						this.filterRatingChips = new Set();
					}
					if (this.isDetailPage) return;
					this.restoreToolbarUI();
					this.applySortAndFilter();
				});
			}
		}
		loadToolbarState() {
			loadToolbarState(this);
		}
		ensureHeaderWidgets() {
			ensureHeaderWidgets(this);
		}
		processAllItems() {
			return processAllItems(this);
		}
		tryBuildToolbar() {
			return tryBuildToolbar(this);
		}
		refreshChips(toolbar) {
			refreshChips$1(this, toolbar);
		}
		restoreToolbarUI() {
			restoreToolbarUI(this);
		}
		applySortAndFilter() {
			applySortAndFilter(this);
		}
		destroy() {
			this.supervisor.abort();
		}
	};
	var LOG_PREFIX$1 = "[modalListDisabler]";
	var TARGET_LIST_ID = 501;
	var CONTAINER_SELECTOR = "#modal-save-list > div.modal-card.fixed-height-600 > section > div > div:nth-child(1)";
	var ModalListDisablerPlugin = class extends BasePlugin {
		supervisor = new TaskSupervisor();
		getName() {
			return "ModalListDisablerPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			this.tryDisable();
			this.supervisor.observe(document.body, () => {
				this.tryDisable();
			}, {
				childList: true,
				subtree: true
			});
		}
		destroy() {
			this.supervisor.abort();
		}
		disableTargetItem(container) {
			let found = false;
			for (const child of Array.from(container.children)) {
				const span = child.querySelector("label > span");
				if (!span) continue;
				const match = span.innerHTML.match(/\((\d+)\)/);
				if (match && parseInt(match[1], 10) === TARGET_LIST_ID) {
					const input = child.querySelector("label > input");
					if (input && !input.disabled && !input.checked) {
						input.disabled = true;
						console.log(`${LOG_PREFIX$1} 已禁用清单 #${TARGET_LIST_ID}`);
					}
					found = true;
				}
			}
			return found;
		}
		tryDisable() {
			const container = document.querySelector(CONTAINER_SELECTOR);
			if (!container) return false;
			return this.disableTargetItem(container);
		}
	};
	var TAG_HIDDEN_ATTR = "data-video-lists-tag-hidden";
	var FILTER_MODES = {
		CONTAINS_ANY: "contains-any",
		CONTAINS_ALL: "contains-all",
		EXCLUDES_ALL: "excludes-all",
		EXCLUDES_ANY: "excludes-any"
	};
	function collectAllUniqueTags() {
		const tagNames = new Set();
		document.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").forEach((el) => {
			const name = el.textContent?.trim() || "";
			if (name && name !== "—") tagNames.add(name);
		});
		return [...tagNames].sort();
	}
	function collectTagCounts() {
		const counts = {};
		document.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").forEach((el) => {
			const name = el.textContent?.trim() || "";
			if (name && name !== "—") counts[name] = (counts[name] || 0) + 1;
		});
		return counts;
	}
	function countNoTagItems() {
		let count = 0;
		document.querySelectorAll(".item").forEach((item) => {
			if (item.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").length === 0) count++;
		});
		return count;
	}
	function applyFilter$1(plugin) {
		const activeChips = document.querySelectorAll(".jhs-vlt-filter-chip.jhs-vlt-active");
		const selectedValues = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
		if (selectedValues.size === 0) {
			document.querySelectorAll(`.item[${TAG_HIDDEN_ATTR}]`).forEach((item) => {
				item.removeAttribute(TAG_HIDDEN_ATTR);
				item.style.display = "";
			});
			return;
		}
		const showNoTag = selectedValues.has("no-tag");
		const selectedTags = new Set([...selectedValues].filter((v) => v !== "no-tag"));
		document.querySelectorAll(".item").forEach((item) => {
			const htmlItem = item;
			if (htmlItem.style.display === "none" && !htmlItem.hasAttribute("data-video-lists-tag-hidden")) return;
			const tagLinks = item.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])");
			const itemTagNames = new Set(Array.from(tagLinks).map((el) => el.textContent?.trim() || ""));
			let tagMatch = false;
			if (selectedTags.size > 0) switch (plugin.currentFilterMode) {
				case FILTER_MODES.CONTAINS_ANY:
					tagMatch = [...selectedTags].some((t) => itemTagNames.has(t));
					break;
				case FILTER_MODES.CONTAINS_ALL:
					tagMatch = [...selectedTags].every((t) => itemTagNames.has(t));
					break;
				case FILTER_MODES.EXCLUDES_ALL:
					tagMatch = ![...selectedTags].some((t) => itemTagNames.has(t));
					break;
				case FILTER_MODES.EXCLUDES_ANY:
					tagMatch = ![...selectedTags].every((t) => itemTagNames.has(t));
					break;
				default: tagMatch = [...selectedTags].some((t) => itemTagNames.has(t));
			}
			const noTagMatch = showNoTag && itemTagNames.size === 0;
			if (tagMatch || noTagMatch) {
				htmlItem.removeAttribute(TAG_HIDDEN_ATTR);
				htmlItem.style.display = "";
			} else {
				htmlItem.setAttribute(TAG_HIDDEN_ATTR, "");
				htmlItem.style.display = "none";
			}
		});
	}
	var FILTER_MODE_CONFIG = [
		{
			value: FILTER_MODES.CONTAINS_ANY,
			label: "包含任意一个",
			group: "include"
		},
		{
			value: FILTER_MODES.CONTAINS_ALL,
			label: "全都包含",
			group: "include"
		},
		{
			value: FILTER_MODES.EXCLUDES_ALL,
			label: "不包含以下标签",
			group: "exclude"
		},
		{
			value: FILTER_MODES.EXCLUDES_ANY,
			label: "不包含以下任意一个",
			group: "exclude"
		}
	];
	function buildFilterModeDropdown(plugin) {
		const details = document.createElement("details");
		details.className = "jhs-vlt-filter-mode-dropdown";
		const summary = document.createElement("summary");
		summary.className = "jhs-vlt-filter-mode-summary";
		const indicator = document.createElement("span");
		indicator.className = "jhs-vlt-filter-mode-indicator";
		summary.appendChild(indicator);
		const modeText = document.createElement("span");
		modeText.className = "jhs-vlt-filter-mode-text";
		summary.appendChild(modeText);
		const arrow = document.createElement("span");
		arrow.className = "jhs-vlt-filter-mode-arrow";
		arrow.textContent = "▼";
		summary.appendChild(arrow);
		details.appendChild(summary);
		const panel = document.createElement("div");
		panel.className = "jhs-vlt-filter-mode-panel";
		const updateModeUI = () => {
			const config = FILTER_MODE_CONFIG.find((m) => m.value === plugin.currentFilterMode) || FILTER_MODE_CONFIG[0];
			modeText.textContent = config.label;
			indicator.className = "jhs-vlt-filter-mode-indicator jhs-vlt-" + config.group;
			panel.querySelectorAll(".jhs-vlt-filter-mode-item").forEach((el) => {
				el.classList.toggle("jhs-vlt-selected", el.dataset.mode === plugin.currentFilterMode);
			});
		};
		let lastGroup = null;
		FILTER_MODE_CONFIG.forEach((mode) => {
			if (lastGroup && lastGroup !== mode.group) {
				const separator = document.createElement("div");
				separator.className = "jhs-vlt-filter-mode-separator";
				panel.appendChild(separator);
			}
			lastGroup = mode.group;
			const item = document.createElement("div");
			item.className = "jhs-vlt-filter-mode-item";
			if (mode.group === "exclude") item.classList.add("jhs-vlt-exclude");
			item.dataset.mode = mode.value;
			const dot = document.createElement("span");
			dot.className = "jhs-vlt-filter-mode-indicator jhs-vlt-" + mode.group;
			item.appendChild(dot);
			const text = document.createElement("span");
			text.textContent = mode.label;
			item.appendChild(text);
			item.addEventListener("click", () => {
				plugin.currentFilterMode = mode.value;
				updateModeUI();
				details.open = false;
				applyFilter$1(plugin);
			});
			panel.appendChild(item);
		});
		details.appendChild(panel);
		details._updateModeUI = updateModeUI;
		updateModeUI();
		return details;
	}
	function createFilterChip(plugin, labelText, value, opts = {}) {
		const { isNoTag = false, count } = opts;
		const chip = document.createElement("span");
		chip.className = "jhs-vlt-filter-chip";
		if (isNoTag) chip.classList.add("jhs-vlt-no-tag");
		chip.textContent = count !== void 0 ? `${labelText} ${count}` : labelText;
		chip.dataset.value = value;
		chip.addEventListener("click", () => {
			chip.classList.toggle("jhs-vlt-active");
			applyFilter$1(plugin);
		});
		return chip;
	}
	function refreshChips(plugin, chipsContainer) {
		const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".jhs-vlt-filter-chip.jhs-vlt-active")).map((c) => c.dataset.value || ""));
		chipsContainer.innerHTML = "";
		const allCounts = collectTagCounts();
		const noTagChip = createFilterChip(plugin, "无标签", "no-tag", {
			isNoTag: true,
			count: countNoTagItems()
		});
		if (activeValues.has("no-tag")) noTagChip.classList.add("jhs-vlt-active");
		chipsContainer.appendChild(noTagChip);
		collectAllUniqueTags().forEach((tagName) => {
			const chip = createFilterChip(plugin, tagName, tagName, { count: allCounts[tagName] });
			if (activeValues.has(tagName)) chip.classList.add("jhs-vlt-active");
			chipsContainer.appendChild(chip);
		});
	}
	function buildFilterBar(plugin) {
		if (document.querySelector(".jhs-vlt-filter-bar")) return;
		const mountTarget = /^\/actors\//.test(window.location.pathname) ? document.querySelector("body > section > div > div.actor-tags.tags") : document.querySelector("body > section > div > div.tabs.is-boxed");
		if (!mountTarget) return;
		const filterBar = document.createElement("div");
		filterBar.className = "jhs-vlt-filter-bar";
		const label = document.createElement("span");
		label.className = "jhs-vlt-filter-label";
		label.textContent = "筛选:";
		filterBar.appendChild(label);
		const modeDropdown = buildFilterModeDropdown(plugin);
		filterBar.appendChild(modeDropdown);
		filterBar._modeDropdown = modeDropdown;
		const chipsContainer = document.createElement("div");
		chipsContainer.className = "jhs-vlt-filter-chips";
		filterBar.appendChild(chipsContainer);
		const toggleWrapper = document.createElement("label");
		toggleWrapper.className = "jhs-vlt-auto-refresh-toggle";
		toggleWrapper.title = "监听清单操作,自动刷新标签";
		const switchSpan = document.createElement("span");
		switchSpan.className = "jhs-vlt-auto-refresh-switch";
		const checkbox = document.createElement("input");
		checkbox.type = "checkbox";
		checkbox.checked = plugin.autoRefreshEnabled;
		const slider = document.createElement("span");
		slider.className = "jhs-vlt-auto-refresh-slider";
		switchSpan.appendChild(checkbox);
		switchSpan.appendChild(slider);
		const toggleLabel = document.createElement("span");
		toggleLabel.textContent = "自动刷新";
		toggleWrapper.appendChild(switchSpan);
		toggleWrapper.appendChild(toggleLabel);
		filterBar.appendChild(toggleWrapper);
		checkbox.addEventListener("change", () => {
			plugin.autoRefreshEnabled = checkbox.checked;
			plugin.saveAutoRefreshState(plugin.autoRefreshEnabled);
			if (plugin.autoRefreshEnabled) console.log("[视频清单标签] 自动刷新已开启");
			else console.log("[视频清单标签] 自动刷新已关闭");
		});
		const boundRefreshChips = () => refreshChips(plugin, chipsContainer);
		boundRefreshChips();
		mountTarget.insertAdjacentElement("afterend", filterBar);
		filterBar._refreshChips = boundRefreshChips;
	}
	function updateFilterBar(plugin) {
		const filterBar = document.querySelector(".jhs-vlt-filter-bar");
		if (filterBar && filterBar._refreshChips) {
			filterBar._refreshChips();
			applyFilter$1(plugin);
		}
	}
	var BOOTSTRAP_COLORS = [
		{
			name: "primary",
			bg: "#0d6efd",
			text: "#fff"
		},
		{
			name: "secondary",
			bg: "#6c757d",
			text: "#fff"
		},
		{
			name: "success",
			bg: "#198754",
			text: "#fff"
		},
		{
			name: "danger",
			bg: "#dc3545",
			text: "#fff"
		},
		{
			name: "warning",
			bg: "#ffc107",
			text: "#212529"
		},
		{
			name: "info",
			bg: "#0dcaf0",
			text: "#212529"
		},
		{
			name: "dark",
			bg: "#212529",
			text: "#fff"
		}
	];
	function getRandomBootstrapColor() {
		const color = BOOTSTRAP_COLORS[Math.floor(Math.random() * BOOTSTRAP_COLORS.length)];
		return {
			bg: color.bg,
			text: color.text,
			isLight: color.text !== "#fff"
		};
	}
	function tagsEqual(a, b) {
		if (a === b) return true;
		if (!a || !b) return false;
		if (a.length !== b.length) return false;
		return JSON.stringify(a) === JSON.stringify(b);
	}
	function createTagBadge(tag) {
		const urlTrimmed = tag.url?.trim();
		const hasUrl = !!urlTrimmed;
		const el = document.createElement(hasUrl ? "a" : "span");
		el.className = "jhs-vlt-tag-link";
		if (hasUrl && urlTrimmed) {
			const anchor = el;
			anchor.href = urlTrimmed;
			anchor.target = "_blank";
			anchor.rel = "noopener noreferrer";
		}
		el.textContent = tag.name;
		const style = tag.style;
		if (style && style.bg && style.text) {
			el.style.backgroundColor = style.bg;
			el.style.color = style.text;
			if (style.text !== "#fff") el.classList.add("jhs-vlt-light-bg");
		} else {
			const color = getRandomBootstrapColor();
			el.style.backgroundColor = color.bg;
			el.style.color = color.text;
			if (color.isLight) el.classList.add("jhs-vlt-light-bg");
		}
		return el;
	}
	function addTagDisplay(plugin, item) {
		if (item.querySelector(".jhs-vlt-tags-display")) return;
		const strongEl = item.querySelector("a > div.video-title > strong");
		if (!strongEl) return;
		const designation = strongEl.innerHTML;
		const tagsCache = plugin.getTagsCache();
		if (!tagsCache) return;
		const meta = item.querySelector(".meta");
		if (!meta) return;
		const container = document.createElement("div");
		container.className = "jhs-vlt-tags-display";
		const tags = tagsCache[designation];
		if (tags && tags.length > 0) tags.forEach((tag) => {
			container.appendChild(createTagBadge(tag));
		});
		else {
			const placeholder = document.createElement("span");
			placeholder.className = "jhs-vlt-tag-link";
			placeholder.textContent = "—";
			placeholder.style.backgroundColor = "#e9ecef";
			placeholder.style.color = "#6c757d";
			placeholder.style.pointerEvents = "none";
			placeholder.style.userSelect = "none";
			container.appendChild(placeholder);
		}
		container.addEventListener("wheel", (e) => {
			if (e.deltaY !== 0) {
				const atStart = container.scrollLeft <= 0 && e.deltaY < 0;
				const atEnd = container.scrollLeft + container.clientWidth >= container.scrollWidth - 1 && e.deltaY > 0;
				if (!atStart && !atEnd) {
					e.preventDefault();
					container.scrollLeft += e.deltaY;
				}
			}
		}, { passive: false });
		meta.insertAdjacentElement("afterend", container);
	}
	function renderTags(plugin) {
		document.querySelectorAll(".item").forEach((item) => addTagDisplay(plugin, item));
	}
	var LAST_SYNC_KEY = "jdb:last-sync";
	var NO_CHANGE_ASSOCS = {
		existed: true,
		limit_exceeded: true,
		unchanged: true
	};
	var SYNC_DEBOUNCE_MS = 500;
	function handleSyncNotify(plugin, payload, onSync, state) {
		if (!plugin.autoRefreshEnabled) {
			console.log("[视频清单标签] 收到同步事件但自动刷新已关闭,跳过");
			return;
		}
		if (!payload || !payload.designation) {
			console.warn("[视频清单标签] 收到同步事件但无 designation:", payload);
			return;
		}
		if (NO_CHANGE_ASSOCS[payload.association ?? ""]) {
			console.log(`[视频清单标签] association=${payload.association} 不影响标签,跳过`);
			return;
		}
		const now = Date.now();
		if (now - state.lastRefreshTime < SYNC_DEBOUNCE_MS) {
			console.log(`[视频清单标签] 距上次刷新 ${now - state.lastRefreshTime}ms < ${SYNC_DEBOUNCE_MS}ms,防抖跳过`);
			return;
		}
		state.lastRefreshTime = now;
		console.log("[视频清单标签] 收到同步事件 → 精准刷新", payload);
		onSync(payload);
	}
	function setupAutoRefreshListener(plugin, onSync) {
		const state = { lastRefreshTime: 0 };
		broadcastSubscribe(LAST_SYNC_KEY, (payload) => handleSyncNotify(plugin, payload, onSync, state), {
			eventName: "jdb:sync-complete",
			onDelivery: (delivery) => {
				if (delivery.channel === "customEvent") console.log("[视频清单标签] [CustomEvent] 收到 jdb:sync-complete");
				else if (delivery.channel === "storage") console.log(`[视频清单标签] [storage] key=${LAST_SYNC_KEY} newValue=${delivery.rawValue ? "<set>" : "<empty>"}`);
				else console.log(`[视频清单标签] [GM_addValueChangeListener] remote=${delivery.remote} newValue=${delivery.rawValue ? "<set>" : "<empty>"}`);
			}
		});
		console.log(`[视频清单标签] 自动刷新监听已就绪 (autoRefreshEnabled=${plugin.autoRefreshEnabled})`);
	}
	var AUTO_REFRESH_KEY = "jdb:auto-refresh-enabled";
	var VltTags = class {
		tagsCache = null;
		currentFilterMode = "contains-any";
		autoRefreshEnabled = true;
		pendingNewItems = [];
		debounceTimer = null;
		DEBOUNCE_MS = 300;
		getTagsCache() {
			return this.tagsCache;
		}
		async fetchTags(designations) {
			this.tagsCache = await VltDb.queryMoviesLists(designations);
		}
		async fetchAndMergeTags(designations) {
			const newTags = await VltDb.queryMoviesLists(designations);
			if (!this.tagsCache) this.tagsCache = newTags;
			else Object.assign(this.tagsCache, newTags);
		}
		addTagDisplay(item) {
			addTagDisplay(this, item);
		}
		async refreshAllTags() {
			const items = document.querySelectorAll(".item");
			const lists = Array.from(items).map((item) => {
				const strong = item.querySelector("a > div.video-title > strong");
				return strong ? strong.innerHTML : null;
			}).filter((s) => !!s);
			if (lists.length === 0) return;
			console.log(`[视频清单标签] 开始刷新,共 ${lists.length} 个番号`);
			try {
				await this.fetchTags(lists);
			} catch (err) {
				clog.error(`自动刷新标签数据失败(${lists.length} 个番号):`, err);
				return;
			}
			document.querySelectorAll(".jhs-vlt-tags-display").forEach((el) => el.remove());
			renderTags(this);
			this.updateFilterBar();
			console.log("[视频清单标签] 自动刷新完成");
		}
		async refreshDesignation(designation) {
			console.log(`[视频清单标签] refreshDesignation: ${designation}`);
			const allItems = document.querySelectorAll(".item");
			const targetItem = Array.from(allItems).find((item) => {
				const strong = item.querySelector("a > div.video-title > strong");
				return strong && strong.innerHTML === designation;
			});
			if (!targetItem) {
				console.log(`[视频清单标签] ${designation} 不在当前页面 (共 ${allItems.length} 张卡片)`);
				return;
			}
			const oldTags = this.tagsCache ? this.tagsCache[designation] || [] : null;
			try {
				await this.fetchAndMergeTags([designation]);
			} catch (err) {
				clog.error(`[视频清单标签] 刷新 ${designation} 失败:`, err);
				return;
			}
			const newTags = this.tagsCache[designation] || [];
			if (tagsEqual(oldTags, newTags)) {
				console.log(`[视频清单标签] ${designation} 标签未变化,跳过 DOM 更新 (${newTags.length} 个标签)`);
				return;
			}
			const oldContainer = targetItem.querySelector(".jhs-vlt-tags-display");
			if (oldContainer) oldContainer.remove();
			this.addTagDisplay(targetItem);
			this.updateFilterBar();
			console.log(`[视频清单标签] ${designation} 标签已更新 (${oldTags ? oldTags.length : 0} → ${newTags.length})`);
		}
		loadAutoRefreshState() {
			try {
				const val = GM_getValue(AUTO_REFRESH_KEY);
				if (val === void 0) {
					this.autoRefreshEnabled = true;
					console.log("[视频清单标签] 自动刷新: 未设置过 → 默认开启");
				} else {
					this.autoRefreshEnabled = val === true || val === "true";
					console.log(`[视频清单标签] 自动刷新: 存储值 = ${this.autoRefreshEnabled} (原始=${JSON.stringify(val)})`);
				}
			} catch (e) {
				this.autoRefreshEnabled = true;
				console.warn("[视频清单标签] 自动刷新: 读取异常 → 默认开启", e);
			}
		}
		saveAutoRefreshState(enabled) {
			try {
				GM_setValue(AUTO_REFRESH_KEY, enabled);
			} catch {}
		}
		applyFilter() {
			applyFilter$1(this);
		}
		buildFilterBar() {
			buildFilterBar(this);
		}
		updateFilterBar() {
			updateFilterBar(this);
		}
		collectAllUniqueTags() {
			return collectAllUniqueTags();
		}
		collectTagCounts() {
			return collectTagCounts();
		}
		countNoTagItems() {
			return countNoTagItems();
		}
		async initExistingItems() {
			this.loadAutoRefreshState();
			const items = document.querySelectorAll(".item");
			if (items.length === 0) return;
			const lists = Array.from(items).map((item) => {
				const strong = item.querySelector("a > div.video-title > strong");
				return strong ? strong.innerHTML : null;
			}).filter((s) => !!s);
			if (lists.length === 0) return;
			try {
				await this.fetchTags(lists);
				console.log(`[视频清单标签] 初始化加载完成: ${lists.length} 个番号, ${Object.keys(this.tagsCache || {}).length} 个有标签`);
			} catch (err) {
				clog.error("获取标签数据失败:", err);
				this.tagsCache = {};
				return;
			}
			renderTags(this);
			this.buildFilterBar();
		}
		async handleNewItems(newItems) {
			newItems.forEach((item) => this.addTagDisplay(item));
			this.pendingNewItems.push(...newItems);
			if (this.debounceTimer) clearTimeout(this.debounceTimer);
			this.debounceTimer = setTimeout(async () => {
				const itemsToRefresh = [...this.pendingNewItems];
				this.pendingNewItems = [];
				this.debounceTimer = null;
				const uncachedCodes = [];
				const seen = new Set();
				for (const item of itemsToRefresh) {
					const strong = item.querySelector("a > div.video-title > strong");
					if (!strong) continue;
					const code = strong.innerHTML;
					if (!code || seen.has(code)) continue;
					if (!this.tagsCache || !(code in this.tagsCache)) {
						uncachedCodes.push(code);
						seen.add(code);
					}
				}
				if (uncachedCodes.length === 0) {
					this.updateFilterBar();
					return;
				}
				try {
					await this.fetchAndMergeTags(uncachedCodes);
				} catch (err) {
					clog.error("[视频清单标签] 获取新增标签数据失败:", err);
					return;
				}
				itemsToRefresh.forEach((item) => {
					const oldContainer = item.querySelector(".jhs-vlt-tags-display");
					if (oldContainer) oldContainer.remove();
					this.addTagDisplay(item);
				});
				this.updateFilterBar();
				console.log(`[视频清单标签] 已为 ${uncachedCodes.length} 个新番号获取标签`);
			}, this.DEBOUNCE_MS);
		}
		setupAutoRefreshListener(onSync) {
			setupAutoRefreshListener(this, onSync);
		}
	};
	var LOG_PREFIX = "[JavDB]";
	var LIST_MGMT_KEY = "jdb:list-mgmt";
	function setupListMgmtBroadcastListener(onDelete, onRename) {
		const handlePayload = (payload) => {
			if (!payload || !payload.type || !payload.listId) return;
			console.log(`${LOG_PREFIX} 收到清单管理广播:`, payload);
			if (payload.type === "delete") onDelete(payload.listId);
			else if (payload.type === "rename" && payload.newName) onRename(payload.listId, payload.newName);
		};
		broadcastSubscribe(LIST_MGMT_KEY, (payload) => handlePayload(payload));
	}
	var video_lists_tag_default = "/**\n * video-lists-tag 插件样式 —— 标签显示 + 筛选栏 + Toast。\n *\n * 合并自 archetype/videoListsTag.user.js 的 GM_addStyle(L24-275)\n * 和 archetype/listsOptionSync.user.js 的 injectToastStyles(L40-115)。\n * CSS 规则内容与原脚本一致(缩进标准化,渲染等价)。\n */\n\n/* ---------- 标签显示(Bootstrap Badge Pill 风格) ---------- */\n.jhs-vlt-tags-display {\n    margin: 5px 0;\n    padding: 6px;\n    white-space: nowrap;\n    overflow-x: auto;\n    overflow-y: hidden;\n    scrollbar-width: none;\n    -webkit-overflow-scrolling: touch;\n    padding-bottom: 2px;\n}\n\n.jhs-vlt-tags-display::-webkit-scrollbar {\n    display: none;\n}\n\n.jhs-vlt-tag-link {\n    display: inline-block;\n    padding: 0.35em 0.65em;\n    margin-right: 6px;\n    font-size: 0.75em;\n    font-weight: 700;\n    line-height: 1;\n    text-align: center;\n    white-space: nowrap;\n    vertical-align: baseline;\n    border-radius: 50rem;\n    text-decoration: none;\n    color: #fff;\n    transition:\n        filter 0.15s ease,\n        transform 0.15s ease;\n    box-shadow: 0 1px 2px rgb(0, 0, 0, 0.1);\n}\n\n.jhs-vlt-tag-link:hover {\n    filter: brightness(0.92);\n    transform: translateY(-1px);\n    box-shadow: 0 2px 5px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-vlt-tag-link.jhs-vlt-light-bg {\n    color: #212529;\n}\n\n/* ---------- 标签筛选栏 ---------- */\n\n.jhs-vlt-filter-chip.jhs-vlt-active {\n    background: #0d6efd;\n    border-color: #0d6efd;\n    color: #fff;\n}\n\n.jhs-vlt-filter-chip.jhs-vlt-no-tag {\n    border-style: dashed;\n    color: #6c757d;\n}\n\n.jhs-vlt-filter-chip.jhs-vlt-no-tag.jhs-vlt-active {\n    background: #6c757d;\n    border-color: #6c757d;\n    border-style: solid;\n    color: #fff;\n}\n\n/* ---------- 自动刷新开关 ---------- */\n.jhs-vlt-auto-refresh-toggle {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    margin-left: auto;\n    flex-shrink: 0;\n    font-size: 0.78em;\n    color: #6c757d;\n    user-select: none;\n}\n\n.jhs-vlt-auto-refresh-switch {\n    position: relative;\n    width: 36px;\n    height: 20px;\n    flex-shrink: 0;\n}\n\n.jhs-vlt-auto-refresh-switch input {\n    opacity: 0;\n    width: 0;\n    height: 0;\n}\n\n.jhs-vlt-auto-refresh-slider {\n    position: absolute;\n    inset: 0;\n    background: #ced4da;\n    border-radius: 50rem;\n    cursor: pointer;\n    transition: background 0.2s;\n}\n\n.jhs-vlt-auto-refresh-slider::before {\n    content: '';\n    position: absolute;\n    left: 2px;\n    top: 2px;\n    width: 16px;\n    height: 16px;\n    border-radius: 50%;\n    background: #fff;\n    transition: transform 0.2s;\n}\n\n.jhs-vlt-auto-refresh-switch input:checked + .jhs-vlt-auto-refresh-slider {\n    background: #198754;\n}\n\n.jhs-vlt-auto-refresh-switch input:checked + .jhs-vlt-auto-refresh-slider::before {\n    transform: translateX(16px);\n}\n\n/* ---------- 筛选模式下拉 ---------- */\n.jhs-vlt-filter-mode-dropdown {\n    position: relative;\n    display: inline-block;\n    flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-summary {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    padding: 0.25em 0.7em;\n    font-size: 0.75em;\n    font-weight: 600;\n    line-height: 1.5;\n    border-radius: 50rem;\n    cursor: pointer;\n    user-select: none;\n    border: 1.5px solid #dee2e6;\n    background: #fff;\n    color: #495057;\n    transition: background-color 0.15s ease, border-color 0.15s ease;\n    list-style: none;\n}\n\n.jhs-vlt-filter-mode-summary::-webkit-details-marker {\n    display: none;\n}\n\n.jhs-vlt-filter-mode-summary:hover {\n    border-color: #adb5bd;\n    background: #f8f9fa;\n}\n\n.jhs-vlt-filter-mode-indicator {\n    display: inline-block;\n    width: 8px;\n    height: 8px;\n    border-radius: 50%;\n    flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-indicator.jhs-vlt-include {\n    background: #0d6efd;\n    box-shadow: 0 0 0 2px rgb(13, 110, 253, 0.2);\n}\n\n.jhs-vlt-filter-mode-indicator.jhs-vlt-exclude {\n    background: #dc3545;\n    box-shadow: 0 0 0 2px rgb(220, 53, 69, 0.2);\n}\n\n.jhs-vlt-filter-mode-arrow {\n    font-size: 0.7em;\n    color: #adb5bd;\n    transition: transform 0.2s;\n}\n\n.jhs-vlt-filter-mode-dropdown[open] .jhs-vlt-filter-mode-arrow {\n    transform: rotate(180deg);\n}\n\n.jhs-vlt-filter-mode-panel {\n    position: absolute;\n    top: calc(100% + 4px);\n    left: 0;\n    min-width: 190px;\n    background: #fff;\n    border: 1px solid #dee2e6;\n    border-radius: 8px;\n    box-shadow: 0 4px 16px rgb(0, 0, 0, 0.12);\n    padding: 4px 0;\n    z-index: 1000;\n    animation: jhs-vlt-filter-mode-in 0.15s ease;\n}\n\n@keyframes jhs-vlt-filter-mode-in {\n    from {\n        opacity: 0;\n        transform: translateY(-4px);\n    }\n\n    to {\n        opacity: 1;\n        transform: translateY(0);\n    }\n}\n\n.jhs-vlt-filter-mode-item {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 6px 14px;\n    font-size: 0.78em;\n    font-weight: 500;\n    color: #495057;\n    cursor: pointer;\n    transition: background 0.1s;\n    white-space: nowrap;\n}\n\n.jhs-vlt-filter-mode-item:hover {\n    background: #f8f9fa;\n}\n\n.jhs-vlt-filter-mode-item.jhs-vlt-selected {\n    background: rgb(13, 110, 253, 0.08);\n    color: #0d6efd;\n    font-weight: 600;\n}\n\n.jhs-vlt-filter-mode-item.jhs-vlt-exclude.jhs-vlt-selected {\n    background: rgb(220, 53, 69, 0.08);\n    color: #dc3545;\n}\n\n.jhs-vlt-filter-mode-item .jhs-vlt-filter-mode-indicator {\n    flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-separator {\n    height: 1px;\n    margin: 4px 8px;\n    background: #e9ecef;\n}\n\n/* ---------- Toast 通知 ---------- */\n#jdb-toast-container {\n    position: fixed;\n    top: 72px;\n    left: 20px;\n    z-index: var(--jhs-z-page);\n    display: flex;\n    flex-direction: column;\n    pointer-events: none;\n}\n\n.jdb-toast {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    min-width: 220px;\n    max-width: 380px;\n    max-height: 200px;\n    padding: 12px 16px;\n    border-radius: 8px;\n    font-size: 13px;\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n    color: #fff;\n    line-height: 1.4;\n    box-shadow: 0 4px 16px rgb(0, 0, 0, 0.28);\n    backdrop-filter: blur(6px);\n    pointer-events: auto;\n    overflow: hidden;\n    margin-bottom: 8px;\n    animation: jdb-toast-in 0.2s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\n    transition:\n        opacity 0.2s,\n        transform 0.2s,\n        max-height 0.2s,\n        margin-bottom 0.2s;\n}\n\n.jdb-toast:last-child {\n    margin-bottom: 0;\n}\n\n.jdb-toast.removing {\n    opacity: 0;\n    max-height: 0;\n    margin-bottom: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    transform: translateX(-20px);\n}\n\n.jdb-toast--stacked {\n    max-height: 10px;\n    opacity: 0.35;\n    transform: translateX(12px);\n    margin-bottom: 2px;\n    cursor: pointer;\n}\n\n.jdb-toast--success {\n    background: rgb(22, 163, 74, 0.92);\n}\n\n.jdb-toast--error {\n    background: rgb(220, 38, 38, 0.92);\n}\n\n.jdb-toast--warning {\n    background: rgb(217, 119, 6, 0.92);\n}\n\n.jdb-toast--info {\n    background: rgb(37, 99, 235, 0.92);\n}\n\n.jdb-toast__icon {\n    flex-shrink: 0;\n    width: 20px;\n    height: 20px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n.jdb-toast__msg {\n    flex: 1;\n    word-break: break-word;\n}\n\n@keyframes jdb-toast-in {\n    from {\n        opacity: 0;\n        transform: translateX(-30px);\n    }\n\n    to {\n        opacity: 1;\n        transform: translateX(0);\n    }\n}\n\n/* ---------- 新增清單(詳情頁展開面板旁) ---------- */\n.jhs-list-panel:has(+ .jhs-list-create-wrap) {\n    border-radius: 10px 10px 0 0;\n}\n\n.jhs-list-create-wrap {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    min-height: 44px;\n    margin: -1px 0 10px;\n    padding: 7px 10px;\n    border: 1px solid #dfe4ea;\n    border-top-color: #edf0f3;\n    border-radius: 0 0 10px 10px;\n    background: #f6f8fa;\n    flex-wrap: wrap;\n}\n\n.jhs-list-create-btn {\n    min-height: 32px;\n    font-size: 12px;\n    font-weight: 600;\n}\n\n.jhs-list-create-form {\n    display: flex;\n    min-width: 0;\n    flex: 1;\n    align-items: center;\n    gap: 6px;\n    flex-wrap: nowrap;\n}\n\n.jhs-list-create-form .input.is-small {\n    width: auto;\n    min-width: 120px;\n    flex: 1;\n    font-size: 12px;\n}\n\n.jhs-list-create-wrap[aria-busy='true'] {\n    cursor: wait;\n}\n\n.jhs-list-create-wrap .button:focus-visible,\n.jhs-list-create-wrap .input:focus-visible {\n    outline: 2px solid #3273dc;\n    outline-offset: 2px;\n}\n\n.jhs-visually-hidden {\n    position: absolute !important;\n    width: 1px !important;\n    height: 1px !important;\n    padding: 0 !important;\n    margin: -1px !important;\n    overflow: hidden !important;\n    clip: rect(0, 0, 0, 0) !important;\n    white-space: nowrap !important;\n    border: 0 !important;\n}\n\n@media (width <= 768px) {\n    .jhs-list-create-wrap .button,\n    .jhs-list-create-wrap .input.is-small {\n        min-height: 44px;\n    }\n\n    .jhs-list-create-form {\n        width: 100%;\n        flex-wrap: wrap;\n    }\n\n    .jhs-list-create-form .input.is-small {\n        flex-basis: 100%;\n    }\n}\n";
	var VideoListsTagPlugin = class extends BasePlugin {
		vltTags = null;
		observer = null;
		getName() {
			return "VideoListsTagPlugin";
		}
		async initCss() {
			return video_lists_tag_default;
		}
		async handle() {
			if (window.isDetailPage) {
				const settings = await storageManager.getSetting();
				setAutoReconcileDelay(Number(settings.autoReconcileDelay));
				setAutoServerSyncEnabled(settings.autoServerSync !== "no");
				setAutoFavCleanupEnabled(settings.autoFavCleanupEnabled !== "no");
				setProtectedKeywords(String(settings.protectedListKeywords || "值得复盘").split(",").map((s) => s.trim()).filter(Boolean));
				setupCheckboxListener();
				setupCreateListButton();
				setupListMgmtBroadcastListener((listId) => removeDetailPageCheckbox(listId), (listId, newName) => updateDetailPageCheckboxLabel(listId, newName));
				return;
			}
			if (window.location.href.includes("/users/lists")) {
				setupListManagementListener();
				return;
			}
			if (!window.isListPage) return;
			this.vltTags = new VltTags();
			await this.vltTags.initExistingItems();
			this.setupItemObserver();
			setupListMgmtBroadcastListener(() => this.vltTags?.refreshAllTags().then(), () => this.vltTags?.refreshAllTags().then());
			this.vltTags.setupAutoRefreshListener((payload) => {
				if (payload?.designation) this.vltTags?.refreshDesignation(payload.designation).then();
			});
		}
		setupItemObserver() {
			this.observer = new MutationObserver((mutations) => {
				const newItems = [];
				for (const mutation of mutations) for (const node of mutation.addedNodes) {
					if (node.nodeType !== Node.ELEMENT_NODE) continue;
					const el = node;
					if (el.matches?.(".item")) newItems.push(el);
					else if (el.querySelectorAll?.(".item").length > 0) newItems.push(...Array.from(el.querySelectorAll(".item")));
				}
				if (newItems.length > 0) this.vltTags?.handleNewItems(newItems).then();
			});
			this.observer.observe(document.body, {
				childList: true,
				subtree: true
			});
		}
	};
	function getPayloadRevision(payload) {
		const revision = payload.revision ?? payload.ts;
		return Number.isInteger(revision) && revision >= 0 ? revision : -1;
	}
	function isDeltaPayload(value) {
		if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
		const payload = value;
		if (payload.action !== "upsert" && payload.action !== "delete") return false;
		if (!Array.isArray(payload.items) || payload.items.length === 0) return false;
		if (typeof payload.ts !== "number" || !Number.isInteger(payload.ts) || payload.ts < 0) return false;
		if (payload.revision !== void 0 && (typeof payload.revision !== "number" || !Number.isInteger(payload.revision) || payload.revision < 0)) return false;
		return payload.items.every((item) => {
			if (item === null || typeof item !== "object" || Array.isArray(item)) return false;
			const record = item;
			if (typeof record.carNum !== "string" || record.carNum.trim() === "") return false;
			if (payload.action === "delete") return true;
			return typeof record.status === "string" && record.status.trim() !== "" && STATUS_LIST.includes(record.status) && typeof record.url_path === "string" && record.url_path.trim() !== "";
		});
	}
	function readDeltaJournal(value) {
		return (Array.isArray(value) ? value : value ? [value] : []).filter(isDeltaPayload);
	}
	function sortDeltaJournal(journal) {
		return journal.map((payload, index) => ({
			payload,
			index
		})).sort((left, right) => {
			return getPayloadRevision(left.payload) - getPayloadRevision(right.payload) || left.index - right.index;
		}).map(({ payload }) => payload);
	}
	function countDeltaItems(journal) {
		return journal.reduce((total, payload) => total + payload.items.length, 0);
	}
	function compactDeltaJournal(journal) {
		if (countDeltaItems(journal) <= 256) return journal;
		const latestByCar = new Map();
		for (const payload of sortDeltaJournal(journal)) for (const item of payload.items) latestByCar.set(item.carNum.trim().toUpperCase(), {
			payload,
			item
		});
		const grouped = new Map();
		for (const { payload, item } of latestByCar.values()) {
			const key = `${getPayloadRevision(payload)}:${payload.action}:${payload.ts}`;
			const existing = grouped.get(key);
			if (existing) existing.items.push(item);
			else grouped.set(key, {
				...payload,
				items: [item]
			});
		}
		return sortDeltaJournal(Array.from(grouped.values()));
	}
	function createLogger(prefix) {
		return {
			info(step, ...args) {
				console.log(`%c${prefix}%c ${step}`, "color:#25b1dc;font-weight:bold;", "", ...args);
			},
			ok(step, ...args) {
				console.log(`%c${prefix} ✓%c ${step}`, "color:#1f7a3d;font-weight:bold;", "", ...args);
			},
			warn(step, ...args) {
				console.warn(`%c${prefix} ⚠%c ${step}`, "color:#d7a80c;font-weight:bold;", "", ...args);
			},
			err(step, ...args) {
				console.error(`%c${prefix} ✗%c ${step}`, "color:#de3333;font-weight:bold;", "", ...args);
			}
		};
	}
	var { info: logInfo$1, ok: logOk$1, warn: logWarn$1, err: logErr$1 } = createLogger(LOG_PREFIX_JHS);
	var GM_KEY_CAR_LIST_REV = "jhs_car_list_rev";
	var GM_KEY_SYNCED_REV = "jhs_car_status_synced_rev";
	var GM_KEY_LAST_SYNC_TIME = "jhs_car_status_last_sync";
	var DAILY_SYNC_INTERVAL = 864e5;
	var CarListReaderPlugin = class extends BasePlugin {
		getName() {
			return "CarListReaderPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			storageManager.setCarListChangeCallback((event) => {
				this.pushDelta(event);
			});
			if (window.self !== window.top) return;
			try {
				GM_registerMenuCommand("🔁 全量同步车辆状态到 missav", () => {
					this.syncFullCarStatus();
				});
			} catch {}
			try {
				GM_registerMenuCommand("🔍 诊断:跨站同步发布侧", () => {
					(async () => {
						const delta = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
						const data = GM_getValue(GM_KEY_CAR_STATUS_DATA);
						const journal = Array.isArray(delta) ? delta : delta ? [delta] : [];
						const info = {
							site: "javdb",
							version: GM_info.script.version,
							gmRevision: GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0),
							carListRev: GM_getValue(GM_KEY_CAR_LIST_REV, 0),
							syncedRev: GM_getValue(GM_KEY_SYNCED_REV, -1),
							journal: {
								length: journal.length,
								entries: journal.slice(-5).map((e) => {
									const entry = e;
									return {
										action: entry?.action,
										revision: entry?.revision ?? entry?.ts,
										carNums: Array.isArray(entry?.items) ? entry.items.map((i) => i?.carNum) : void 0
									};
								})
							},
							fullSnapshot: data ? {
								revision: data.revision ?? data.ts,
								count: data.count,
								ready: data.ready
							} : null
						};
						const text = JSON.stringify(info, null, 2);
						console.log("[JHS CarSync 诊断-发布侧]", text);
						try {
							await navigator.clipboard.writeText(text);
						} catch {}
						show.ok("诊断信息已输出到控制台并复制到剪贴板");
					})();
				});
			} catch {}
			const currentRev = GM_getValue(GM_KEY_CAR_LIST_REV, 0);
			const syncedRev = GM_getValue(GM_KEY_SYNCED_REV, -1);
			const lastSyncTime = GM_getValue(GM_KEY_LAST_SYNC_TIME, 0);
			if (currentRev !== syncedRev || Date.now() - lastSyncTime > DAILY_SYNC_INTERVAL) {
				logInfo$1("全量同步已调度", `rev=${currentRev}, synced=${syncedRev}`);
				setTimeout(() => {
					this.syncFullCarStatus();
				}, 2e3);
			} else logInfo$1("跳过全量同步", `数据未变更 (rev=${currentRev}),上次同步 ${new Date(lastSyncTime).toLocaleString()}`);
		}
		pendingPush = Promise.resolve();
		pushDelta(event) {
			this.pendingPush = this.pushDeltaAsync(event).catch((err) => {
				logErr$1("增量推送失败", err instanceof Error ? err.message : String(err));
			});
		}
		async waitForPendingPush() {
			await this.pendingPush;
		}
		async pushDeltaAsync(event) {
			if (event.action === "upsert" && event.upserts) {
				const items = event.upserts.map((car) => {
					const { code } = normalizeUrl(car.url || "");
					return {
						carNum: car.carNum,
						status: car.status,
						url_path: code
					};
				});
				if (items.length === 0) return;
				if (items.length > 500) {
					logWarn$1("批量变更超过增量上限,改发压缩全量快照", `${items.length} 条`);
					await this.syncFullCarStatus();
					return;
				}
				const revision = await nextRevision();
				const needsFullSnapshot = await appendDeltaJournal({
					action: "upsert",
					items,
					ts: revision,
					revision
				});
				logInfo$1("增量推送", `upsert ${items.length} 条`);
				if (needsFullSnapshot) {
					logInfo$1("增量日志超过阈值,发布压缩全量快照");
					await this.syncFullCarStatus();
				}
			} else if (event.action === "delete" && event.deletes) {
				if (event.deletes.length === 0) return;
				if (event.deletes.length > 500) {
					logWarn$1("批量删除超过增量上限,改发压缩全量快照", `${event.deletes.length} 条`);
					await this.syncFullCarStatus();
					return;
				}
				const revision = await nextRevision();
				const needsFullSnapshot = await appendDeltaJournal({
					action: "delete",
					items: event.deletes.map((carNum) => ({ carNum })),
					ts: revision,
					revision
				});
				logInfo$1("增量推送", `delete ${event.deletes.length} 条`);
				if (needsFullSnapshot) {
					logInfo$1("增量日志超过阈值,发布压缩全量快照");
					await this.syncFullCarStatus();
				}
			}
			GM_setValue(GM_KEY_CAR_LIST_REV, GM_getValue(GM_KEY_CAR_LIST_REV, 0) + 1);
		}
		async syncFullCarStatus() {
			if (isFullSyncing) {
				fullResyncRequested = true;
				logWarn$1("全量同步任务在进行中,已安排完成后补发最新快照");
				return false;
			}
			isFullSyncing = true;
			try {
				logInfo$1("全量同步开始");
				const revision = await nextRevision();
				const carList = await storageManager.getCarList();
				logInfo$1("1-读取", `本地 car_list 记录数:${carList.length}`);
				if (carList.length === 0) {
					logWarn$1("car_list 为空,跳过同步(jhs 可能尚未写入数据)");
					return false;
				}
				const colRes = toColumnar(carList);
				const count = countColumnar(colRes.byStatus);
				logInfo$1("2-转列存", `列存 ${count} 条 / 丢弃 ${colRes.dropped} / url 异常 ${colRes.malformed}`);
				if (count === 0) {
					logWarn$1("列存后无有效记录,跳过");
					return false;
				}
				const base64 = await gzipToBase64(colRes.byStatus);
				if (!base64) {
					logErr$1("gzip 压缩失败(CompressionStream 不可用?非 https 环境?)");
					return false;
				}
				logInfo$1("3-压缩", `gzip+base64 体积:约 ${(base64.length / 1024).toFixed(1)} KB`);
				const payload = {
					data: base64,
					hwm: new Date(revision).toISOString(),
					count,
					ts: revision,
					revision,
					mode: "snapshot",
					ready: true
				};
				if (!await publishSnapshot(payload)) {
					logWarn$1("发现更新的全量快照,放弃发布本次旧快照", `revision=${revision}`);
					return false;
				}
				logOk$1("4-完成", `已写入 GM 存储,count=${count},hwm=${payload.hwm}`);
				GM_setValue(GM_KEY_SYNCED_REV, GM_getValue(GM_KEY_CAR_LIST_REV, 0));
				GM_setValue(GM_KEY_LAST_SYNC_TIME, Date.now());
				return true;
			} catch (err) {
				logErr$1("全量同步失败", err instanceof Error ? err.message : String(err), err);
				return false;
			} finally {
				isFullSyncing = false;
				if (fullResyncRequested) {
					fullResyncRequested = false;
					setTimeout(() => void this.syncFullCarStatus(), 0);
				}
			}
		}
	};
	var isFullSyncing = false;
	var fullResyncRequested = false;
	var lastIssuedRevision = 0;
	var JAVDB_SYNC_LOCK_NAME = "jhs-javdb-car-status-publisher";
	async function nextRevision() {
		return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
			const now = Date.now();
			let persisted = 0;
			try {
				const value = GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0);
				if (typeof value === "number" && Number.isInteger(value) && value >= 0) persisted = value;
			} catch {}
			lastIssuedRevision = Math.max(now, persisted + 1, lastIssuedRevision + 1);
			GM_setValue(GM_KEY_CAR_STATUS_REVISION, lastIssuedRevision);
			return lastIssuedRevision;
		});
	}
	async function appendDeltaJournal(payload) {
		return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
			const journal = readDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
			journal.push(payload);
			const compacted = compactDeltaJournal(sortDeltaJournal(journal));
			GM_setValue(GM_KEY_CAR_STATUS_DELTA, compacted);
			return countDeltaItems(compacted) > 256;
		});
	}
	async function publishSnapshot(payload) {
		return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
			const current = GM_getValue(GM_KEY_CAR_STATUS_DATA);
			if ((current ? getPayloadRevision(current) : -1) >= getPayloadRevision(payload)) return false;
			GM_setValue(GM_KEY_CAR_STATUS_DATA, payload);
			const journal = readDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
			const retained = journal.filter((delta) => getPayloadRevision(delta) >= getPayloadRevision(payload));
			if (retained.length !== journal.length) GM_setValue(GM_KEY_CAR_STATUS_DELTA, sortDeltaJournal(retained));
			return true;
		});
	}
	var visit_history_tooltip_default = "/* 访问记录悬浮提示:时间分级配色(由近=安全 → 远=危险) */\n.jhs-visit-tooltip {\n    position: fixed;\n    padding: 8px 12px;\n    border-radius: 6px;\n    white-space: nowrap;\n    max-width: 600px;\n    pointer-events: none;\n    font-size: 14px;\n    line-height: 1.5;\n    font-weight: 600;\n    z-index: var(--jhs-z-top);\n    box-shadow: 0 6px 20px rgb(0, 0, 0, 0.22);\n    display: none;\n    border: 1px solid transparent;\n}\n\n/* 无访问记录:白底灰字 */\n.jhs-visit-tooltip--none {\n    background: #fff;\n    color: #64748b;\n    border-color: #e2e8f0;\n    box-shadow: 0 4px 14px rgb(15, 23, 42, 0.12);\n    font-weight: 500;\n}\n\n/* <1 分钟:最安全 · 翠绿 */\n.jhs-visit-tooltip--sec {\n    background: #ecfdf5;\n    color: #047857;\n    border-color: #6ee7b7;\n}\n\n/* <1 小时:安全 · 青绿 */\n.jhs-visit-tooltip--min {\n    background: #f0fdf4;\n    color: #15803d;\n    border-color: #86efac;\n}\n\n/* <1 天:留意 · 琥珀 */\n.jhs-visit-tooltip--hour {\n    background: #fffbeb;\n    color: #b45309;\n    border-color: #fcd34d;\n}\n\n/* <1 周:警告 · 橙 */\n.jhs-visit-tooltip--day {\n    background: #fff7ed;\n    color: #c2410c;\n    border-color: #fdba74;\n}\n\n/* ≥1 周:危险 · 红 */\n.jhs-visit-tooltip--old {\n    background: #fef2f2;\n    color: #b91c1c;\n    border-color: #fca5a5;\n}\n";
	var VISIT_HISTORY_STORAGE_KEY = "jhs_visit_history";
	var MAX_ENTRIES = 5e3;
	var TICK_MS = 500;
	var TIER_CLASS = {
		none: "jhs-visit-tooltip--none",
		sec: "jhs-visit-tooltip--sec",
		min: "jhs-visit-tooltip--min",
		hour: "jhs-visit-tooltip--hour",
		day: "jhs-visit-tooltip--day",
		old: "jhs-visit-tooltip--old"
	};
	var ALL_TIER_CLASSES = Object.values(TIER_CLASS).join(" ");
	var VisitHistoryPlugin = class extends BasePlugin {
		supervisor = new TaskSupervisor();
		tooltipEl = null;
		tooltipTimer = null;
		activePath = null;
		dismissListenerBound = false;
		pageshowBound = false;
		getName() {
			return "VisitHistoryPlugin";
		}
		async initCss() {
			return visit_history_tooltip_default;
		}
		async handle() {
			if (!isJavdbSite) return;
			this.recordVisit();
			this.bindPageshow();
			if (window.isDetailPage) this.injectMetaLinkTooltips();
		}
		bindPageshow() {
			if (this.pageshowBound) return;
			this.pageshowBound = true;
			this.supervisor.addEventListener(window, "pageshow", (event) => {
				if (!isJavdbSite) return;
				this.recordVisit();
				if (event.persisted && window.isDetailPage) this.injectMetaLinkTooltips(true);
			});
		}
		recordVisit() {
			const path = location.pathname + location.search;
			const history = this.readHistory();
			history[path] = Date.now();
			this.evictIfNeeded(history);
			replacePersistentMap("visitHistory", VISIT_HISTORY_STORAGE_KEY, history);
		}
		readHistory() {
			return readPersistentMap("visitHistory", VISIT_HISTORY_STORAGE_KEY);
		}
		evictIfNeeded(history) {
			const keys = Object.keys(history);
			if (keys.length <= MAX_ENTRIES) return;
			keys.sort((a, b) => history[a] - history[b]);
			const removeCount = keys.length - MAX_ENTRIES;
			for (let i = 0; i < removeCount; i++) delete history[keys[i]];
		}
		injectMetaLinkTooltips(forceRebind = false) {
			const container = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2)") || document.querySelector(".movie-panel-info");
			if (!container) return;
			container.querySelectorAll(".panel-block .value a[href]").forEach((a) => {
				const href = a.getAttribute("href") || "";
				const path = this.normalizePath(href);
				if (!path) return;
				const el = a;
				if (forceRebind && el.__jhsVisitEnter) {
					el.removeEventListener("mouseenter", el.__jhsVisitEnter);
					el.removeEventListener("mouseleave", el.__jhsVisitLeave);
				}
				if (el.__jhsVisitEnter && !forceRebind) return;
				const onEnter = () => {
					this.activePath = path;
					this.showVisitTooltip(el);
				};
				const onLeave = () => {
					this.hideVisitTooltip();
				};
				el.__jhsVisitEnter = onEnter;
				el.__jhsVisitLeave = onLeave;
				this.supervisor.addEventListener(el, "mouseenter", onEnter);
				this.supervisor.addEventListener(el, "mouseleave", onLeave);
			});
		}
		normalizePath(href) {
			if (!href || href.startsWith("javascript:")) return null;
			try {
				const url = new URL(href, location.origin);
				if (url.origin !== location.origin) return null;
				return url.pathname + url.search;
			} catch {
				return null;
			}
		}
		ensureTooltipEl() {
			if (!this.tooltipEl || !this.tooltipEl.isConnected) {
				const el = document.createElement("div");
				el.className = "jhs-visit-tooltip";
				document.body.appendChild(el);
				this.tooltipEl = el;
			}
			if (!this.dismissListenerBound) {
				this.dismissListenerBound = true;
				this.supervisor.addEventListener(window, "scroll", () => this.hideVisitTooltip(), { capture: true });
				this.supervisor.addEventListener(window, "resize", () => this.hideVisitTooltip(), { capture: true });
			}
			return this.tooltipEl;
		}
		applyTier(el, tier) {
			el.classList.remove(...ALL_TIER_CLASSES.split(" "));
			el.classList.add(TIER_CLASS[tier]);
		}
		showVisitTooltip(link) {
			const el = this.ensureTooltipEl();
			const update = () => {
				let ts;
				if (this.activePath) ts = this.readHistory()[this.activePath];
				if (!ts) {
					this.applyTier(el, "none");
					el.textContent = "无访问记录";
				} else {
					const { text, tier } = this.formatVisitTime(ts);
					this.applyTier(el, tier);
					el.textContent = text;
				}
				const rect = link.getBoundingClientRect();
				const tw = el.offsetWidth;
				let left = rect.left + rect.width / 2 - tw / 2;
				if (left < 8) left = 8;
				if (left + tw > window.innerWidth - 8) left = window.innerWidth - tw - 8;
				el.style.left = `${Math.round(left)}px`;
			};
			el.style.display = "block";
			update();
			const rect = link.getBoundingClientRect();
			const th = el.offsetHeight;
			let top = rect.top - th - 6;
			if (top < 8) top = rect.bottom + 6;
			el.style.top = `${Math.round(top)}px`;
			update();
			if (this.tooltipTimer) clearInterval(this.tooltipTimer);
			this.tooltipTimer = this.supervisor.setInterval(update, TICK_MS);
		}
		hideVisitTooltip() {
			this.activePath = null;
			if (this.tooltipTimer) {
				clearInterval(this.tooltipTimer);
				this.tooltipTimer = null;
			}
			if (this.tooltipEl) {
				this.tooltipEl.style.display = "none";
				this.tooltipEl.textContent = "";
				this.tooltipEl.classList.remove(...ALL_TIER_CLASSES.split(" "));
			}
		}
		destroy() {
			this.supervisor.abort();
		}
		formatVisitTime(ts) {
			const diff = Date.now() - ts;
			if (diff < 6e4) return {
				text: `${Math.max(1, Math.floor(diff / 1e3))}秒前打开过`,
				tier: "sec"
			};
			if (diff < 36e5) return {
				text: `${Math.floor(diff / 6e4)}分钟前打开过`,
				tier: "min"
			};
			if (diff < 864e5) return {
				text: `${Math.floor(diff / 36e5)}小时前打开过`,
				tier: "hour"
			};
			if (diff < 6048e5) return {
				text: `${Math.floor(diff / 864e5)}天前打开过`,
				tier: "day"
			};
			return {
				text: `${this.formatDate(ts)} 打开过`,
				tier: "old"
			};
		}
		formatDate(ts) {
			const d = new Date(ts);
			const p = (n) => String(n).padStart(2, "0");
			return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
		}
	};
	function normalizeCarNum(raw) {
		if (!raw) return "";
		const filename = raw.split("/").pop() || "";
		if (/^FC2-PPV-\d+$/i.test(filename)) return filename.toUpperCase();
		const sep = filename.includes("_") ? "_" : "-";
		const parts = filename.split(sep);
		return (parts.length >= 2 ? parts[0] + "-" + parts[1] : filename).toUpperCase();
	}
	function createBadge(status, url) {
		const cfg = STATUS_CONFIG[status];
		if (!cfg) return null;
		const a = document.createElement("a");
		a.className = "missav-status-tag";
		a.dataset.status = status;
		a.textContent = cfg.label;
		const safeUrl = sanitizeUrl(url || "");
		if (safeUrl && safeUrl !== "#") {
			a.href = safeUrl;
			a.target = "_blank";
			a.rel = "noopener noreferrer";
		} else a.removeAttribute("href");
		a.title = "点击跳转到 javdb 详情页";
		a.style.cssText = [
			"border-radius:10px",
			"position:absolute",
			"z-index:10",
			"right:5px",
			"top:5px",
			"left:auto",
			"-webkit-font-smoothing:antialiased",
			"text-rendering:optimizelegibility",
			"font-family:\"Helvetica Neue\",\"Luxi Sans\",\"DejaVu Sans\",Tahoma,\"Hiragino Sans GB\",\"Microsoft Yahei\",sans-serif",
			"cursor:pointer",
			"align-items:center",
			"display:inline-flex",
			"font-size:0.75rem",
			"height:2em",
			"justify-content:center",
			"line-height:1.5",
			"white-space:nowrap",
			"color:rgb(255,255,255)",
			"pointer-events:auto",
			`background-color:${cfg.color} !important`,
			"width:80px",
			"text-decoration:none"
		].join(";");
		return a;
	}
	function renderBadges(container, carMap) {
		if (!container || carMap.size === 0) return {
			total: 0,
			matched: 0
		};
		const items = container.querySelectorAll(".thumbnail.group");
		let total = 0;
		let matched = 0;
		for (const item of items) {
			const link = item.querySelector("a[alt]");
			if (!link) continue;
			const carNum = link.getAttribute("alt");
			if (!carNum) continue;
			const normalized = normalizeCarNum(carNum);
			const record = carMap.get(normalized);
			if (!record) continue;
			const existing = item.querySelector(".missav-status-tag");
			if (existing) {
				if (existing.dataset.status === record.status) continue;
				existing.remove();
			}
			total++;
			const thumbDiv = item.querySelector("div.relative");
			if (!thumbDiv) continue;
			thumbDiv.style.position = "relative";
			const badge = createBadge(record.status, record.url);
			if (badge) {
				thumbDiv.appendChild(badge);
				matched++;
			}
		}
		return {
			total,
			matched
		};
	}
	function isVideoPage() {
		return document.querySelector("#sprite-plyr") !== null;
	}
	var { info: logInfo, ok: logOk, warn: logWarn, err: logErr } = createLogger(LOG_PREFIX_MISSAV);
	var MissavStatusTagPlugin = class extends BasePlugin {
		consumeQueue = new KeyedQueue();
		lastAppliedRevision = -1;
		lastAppliedKind = null;
		getName() {
			return "MissavStatusTagPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			logInfo("脚本启动", `页面类型: ${isVideoPage() ? "视频播放页" : "列表页"}`);
			try {
				GM_registerMenuCommand("🔄 MissAV 刷新标签", () => {
					this.processAll();
				});
			} catch {}
			try {
				GM_registerMenuCommand("🔄 强制重新同步(清除水位线)", () => {
					(async () => {
						try {
							show.ok("正在清除同步水位线...");
							await clearAppliedRevisionLocal();
							this.lastAppliedRevision = -1;
							this.lastAppliedKind = "delta";
							show.ok("水位线已清除,开始重新消费...");
							await this.consumeFull(void 0);
							await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
							await this.processAll();
							show.ok("强制重新同步完成");
						} catch (err) {
							show.error("强制重新同步失败: " + (err instanceof Error ? err.message : String(err)));
						}
					})();
				});
			} catch {}
			try {
				GM_registerMenuCommand("🔍 诊断:跨站同步消费侧", () => {
					(async () => {
						const delta = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
						const data = GM_getValue(GM_KEY_CAR_STATUS_DATA);
						const journal = Array.isArray(delta) ? delta : delta ? [delta] : [];
						let syncMeta = null;
						let carCount = -1;
						let mgt140 = null;
						try {
							const db = await openLocalDB();
							try {
								syncMeta = await new Promise((resolve) => {
									const req = db.transaction(MISSAV_DB_META_STORE, "readonly").objectStore(MISSAV_DB_META_STORE).get(MISSAV_SYNC_META_KEY);
									req.onsuccess = () => resolve(req.result ?? null);
									req.onerror = () => resolve(null);
								});
								carCount = await new Promise((resolve) => {
									const req = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE).count();
									req.onsuccess = () => resolve(req.result);
									req.onerror = () => resolve(-1);
								});
								mgt140 = await new Promise((resolve) => {
									const req = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE).get("MGT-140");
									req.onsuccess = () => resolve(req.result ?? null);
									req.onerror = () => resolve(null);
								});
							} finally {
								db.close();
							}
						} catch {}
						const info = {
							site: "missav",
							version: GM_info.script.version,
							gmRevision: GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0),
							journal: {
								length: journal.length,
								lastEntries: journal.slice(-3).map((e) => {
									const entry = e;
									return {
										action: entry?.action,
										revision: entry?.revision ?? entry?.ts,
										carNums: Array.isArray(entry?.items) ? entry.items.map((i) => i?.carNum) : void 0
									};
								})
							},
							fullSnapshot: data ? {
								revision: data.revision ?? data.ts,
								count: data.count,
								ready: data.ready
							} : null,
							idb: {
								syncMeta,
								carCount,
								MGT140: mgt140
							},
							inMemory: {
								lastAppliedRevision: this.lastAppliedRevision,
								lastAppliedKind: this.lastAppliedKind
							}
						};
						const text = JSON.stringify(info, null, 2);
						console.log("[MissAV 诊断-消费侧]", text);
						try {
							await navigator.clipboard.writeText(text);
						} catch {}
						show.ok("诊断信息已输出到控制台并复制到剪贴板");
					})();
				});
			} catch {}
			try {
				GM_addValueChangeListener(GM_KEY_CAR_STATUS_DELTA, (_key, _oldValue, newValue, remote) => {
					if (remote && newValue) this.consumeDeltaJournal(newValue);
				});
				logOk("已注册增量变更监听");
			} catch {
				logWarn("GM_addValueChangeListener 不可用");
			}
			try {
				GM_addValueChangeListener(GM_KEY_CAR_STATUS_DATA, (_key, _oldValue, newValue, remote) => {
					if (remote && newValue) {
						logInfo("收到 javdb 端全量数据通知");
						this.consumeFull(newValue);
					}
				});
			} catch {}
			this.observeAndProcess();
			await this.consumeFull(void 0);
			await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
			try {
				const gmRevision = GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0);
				if (typeof gmRevision === "number" && gmRevision > 0 && this.lastAppliedRevision < gmRevision) {
					logWarn("消费验证:IDB 水位线落后于 GM 修订号", `idb=${this.lastAppliedRevision} gm=${gmRevision},尝试补消费`);
					await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
				}
			} catch {}
			const pending = readPendingIntents();
			if (pending.length > 0) {
				logInfo("恢复未完成消费", `${pending.length} 条待处理意图`);
				for (const intent of pending) {
					if (intent.type === "delta") {
						const journal = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
						await this.consumeDeltaJournal(journal);
					}
					clearIntent(intent.revision);
				}
			}
			cleanStaleIntents();
		}
		async processAll() {
			try {
				const allCarNums = new Set();
				const allThumbs = document.querySelectorAll(".thumbnail.group");
				for (const thumb of allThumbs) {
					const link = thumb.querySelector("a[alt]");
					if (link) {
						const cn = link.getAttribute("alt");
						if (cn) allCarNums.add(normalizeCarNum(cn));
					}
				}
				if (allCarNums.size === 0) return;
				const carMap = await queryLocalCars(Array.from(allCarNums));
				if (carMap.size === 0) return;
				this.processPage(carMap);
			} catch (err) {
				logErr("处理页面失败", err instanceof Error ? err.message : String(err));
			}
		}
		processPage(carMap) {
			if (isVideoPage()) this.processVideoPage(carMap);
			else this.processListPage(carMap);
		}
		processVideoPage(carMap) {
			const bottomGrid = document.querySelector("body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div.relative.overflow-hidden > div.grid.grid-cols-2.md\\:grid-cols-3.xl\\:grid-cols-4.gap-5");
			if (bottomGrid) this.queryAndRender(bottomGrid, carMap.size > 0 ? carMap : null, "视频页-底部推荐");
			const sideContainer = document.querySelector("body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.hidden.lg\\:flex.h-full.ml-6.order-last > div");
			if (sideContainer) this.queryAndRender(sideContainer, carMap.size > 0 ? carMap : null, "视频页-侧边推荐");
			if (!bottomGrid && !sideContainer) {
				logWarn("视频页选择器未命中,回退到全页扫描");
				this.queryAndRender(document.body, carMap.size > 0 ? carMap : null, "视频页-兜底全页");
			}
		}
		processListPage(carMap) {
			this.queryAndRender(document.body, carMap.size > 0 ? carMap : null, "列表页");
		}
		async queryAndRender(container, existingMap, label) {
			if (!container) return;
			let map = existingMap;
			if (!map || map.size === 0) {
				const carNums = new Set();
				const thumbs = container.querySelectorAll(".thumbnail.group");
				for (const thumb of thumbs) {
					const link = thumb.querySelector("a[alt]");
					if (link) {
						const cn = link.getAttribute("alt");
						if (cn) carNums.add(normalizeCarNum(cn));
					}
				}
				if (carNums.size === 0) return;
				map = await queryLocalCars(Array.from(carNums));
			}
			if (!map || map.size === 0) return;
			const { total, matched } = renderBadges(container, map);
			const cumulativeMatched = container.querySelectorAll(".missav-status-tag").length;
			const totalThumbs = container.querySelectorAll(".thumbnail.group").length;
			logInfo("渲染标签", `${label}: +${total} 新增, +${matched} 命中 | 累计 ${cumulativeMatched}/${totalThumbs} 个标签`);
		}
		observeAndProcess() {
			let retries = 0;
			const maxRetries = 30;
			const tryProcess = () => {
				if (document.querySelectorAll(".thumbnail.group").length > 0) {
					this.processAll();
					return;
				}
				retries++;
				if (retries < maxRetries) setTimeout(tryProcess, 500);
			};
			tryProcess();
			let debounceTimer = null;
			new MutationObserver((mutations) => {
				let hasNewThumbnails = false;
				for (const mutation of mutations) {
					if (mutation.type === "childList") for (const node of mutation.addedNodes) {
						if (node.nodeType !== Node.ELEMENT_NODE) continue;
						const el = node;
						if (el.matches?.(".thumbnail.group") || el.querySelector?.(".thumbnail.group")) {
							hasNewThumbnails = true;
							break;
						}
					}
					if (mutation.type === "attributes" && mutation.attributeName === "alt" && mutation.target.matches?.("a[alt]") && mutation.target.closest?.(".thumbnail.group")) hasNewThumbnails = true;
					if (hasNewThumbnails) break;
				}
				if (hasNewThumbnails) {
					if (debounceTimer) clearTimeout(debounceTimer);
					debounceTimer = setTimeout(() => this.processAll(), 300);
				}
			}).observe(document.body, {
				childList: true,
				subtree: true,
				attributes: true,
				attributeFilter: ["alt"]
			});
		}
		async consumeDeltaJournal(value) {
			const journal = sortDeltaJournal(readDeltaJournal(value));
			for (const delta of journal) await this.consumeQueue.enqueue("consume", () => this.applyDelta(delta));
			if (journal.length > 0) await this.processAll();
		}
		async consumeFull(payload) {
			return this.consumeQueue.enqueue("consume", () => this.applyFull(payload));
		}
		async applyDelta(delta) {
			try {
				const revision = getPayloadRevision(delta);
				if (revision < 0 || !isDeltaPayload(delta)) {
					logWarn("忽略无效增量载荷");
					return;
				}
				if (!this.shouldApply(revision, "delta")) return;
				writeIntent(revision, "delta");
				let rollback;
				if (!(await withSyncRevision(revision, "delta", async () => {
					const previous = await readLocalCarsSelection(delta.items.map((item) => item.carNum));
					rollback = () => restoreLocalCarsSelection(previous);
					if (delta.action === "upsert") {
						const records = delta.items.map((item) => ({
							carNum: item.carNum.trim(),
							status: (item.status || "").trim(),
							url_path: (item.url_path || "").trim()
						}));
						await upsertLocalCars(records);
						logInfo("增量更新", `upsert ${records.length} 条`);
					} else {
						const carNums = delta.items.map((item) => item.carNum.trim());
						await deleteLocalCars(carNums);
						logInfo("增量更新", `delete ${carNums.length} 条`);
					}
				}, async () => {
					if (rollback) await rollback();
				})).applied) {
					this.markApplied(revision, "delta");
					clearIntent(revision);
					return;
				}
				this.markApplied(revision, "delta");
				clearIntent(revision);
			} catch (err) {
				logErr("增量消费失败", err instanceof Error ? err.message : String(err));
			}
		}
		async applyFull(payload) {
			try {
				let data = payload ?? GM_getValue("jhs_car_status_data");
				if (!payload && data) {
					const rev1 = data.revision ?? data.ts;
					await new Promise((resolve) => setTimeout(resolve, 50));
					const data2 = GM_getValue(GM_KEY_CAR_STATUS_DATA);
					const rev2 = data2 ? data2.revision ?? data2.ts : -1;
					if (rev1 !== rev2) {
						logWarn("全量快照双读不一致", `rev1=${rev1} rev2=${rev2},使用第二次读取结果`);
						data = data2;
					}
				}
				if (!data || typeof data !== "object" || typeof data.data !== "string" || !data.data) {
					if (payload !== void 0) logWarn("GM 存储中无全量数据");
					return;
				}
				const revision = getPayloadRevision(data);
				if (revision < 0 || !Number.isInteger(data.count) || data.count < 0 || data.mode !== void 0 && data.mode !== "snapshot" || data.ready === false || data.count === 0 && data.ready !== true) {
					logWarn("拒绝未就绪或格式无效的全量快照");
					return;
				}
				if (!this.shouldApply(revision, "full")) return;
				writeIntent(revision, "full");
				logInfo("全量消费开始", `count=${data.count} hwm=${data.hwm} revision=${revision}`);
				const store = await gunzipFromBase64(data.data);
				if (!store) {
					logErr("解压失败(DecompressionStream 不可用?数据损坏?)");
					return;
				}
				const validation = validateColumnarSnapshot(store, data.count);
				if (!validation.ok) {
					logErr("全量快照校验失败,拒绝覆盖本地数据", validation.reason);
					return;
				}
				logInfo("解压完成", `${validation.records.length} 条记录`);
				let rollback;
				const result = await withSyncRevision(revision, "full", async () => {
					const previous = await readLocalCarsSnapshot();
					rollback = () => restoreLocalCarsSnapshot(previous);
					return replaceLocalCars(validation.records, previous);
				}, async () => {
					if (rollback) await rollback();
				});
				if (!result.applied) {
					this.markApplied(revision, "full");
					clearIntent(revision);
					return;
				}
				const { written, deleted } = result.value;
				logOk("对账本地 IDB", `写入 ${written} 条,删除 ${deleted} 条`);
				this.markApplied(revision, "full");
				await this.processAll();
				clearIntent(revision);
			} catch (err) {
				logErr("全量消费失败", err instanceof Error ? err.message : String(err));
			}
		}
		shouldApply(revision, kind) {
			if (revision < this.lastAppliedRevision) {
				logWarn("忽略过期同步载荷", `${kind} revision=${revision} < ${this.lastAppliedRevision}`);
				return false;
			}
			if (revision === this.lastAppliedRevision && kind === "full") {
				logWarn("忽略重复或落后的全量快照", `revision=${revision}, last=${this.lastAppliedKind || "none"}`);
				return false;
			}
			return true;
		}
		markApplied(revision, kind) {
			this.lastAppliedRevision = Math.max(this.lastAppliedRevision, revision);
			this.lastAppliedKind = kind;
		}
	};
	async function clearAppliedRevisionLocal() {
		const db = await openLocalDB();
		try {
			await new Promise((resolve, reject) => {
				const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
				tx.objectStore(MISSAV_DB_META_STORE).delete(MISSAV_SYNC_META_KEY);
				tx.oncomplete = () => resolve();
				tx.onerror = () => reject(tx.error || new Error("清理同步修订号失败"));
				tx.onabort = () => reject(tx.error || new Error("清理同步修订号事务中止"));
			});
		} finally {
			db.close();
		}
	}
	var JAVDB_ICON_SVG = `<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-letter-j-small"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 8h4v6a2 2 0 1 1 -4 0" /></svg>`;
	function JavDBIcon() {
		return (0, import_jsx_runtime.jsx)("span", { dangerouslySetInnerHTML: { __html: JAVDB_ICON_SVG } });
	}
	var LOG = "[MissAV 快速复制]";
	var MENU_BAR_SELECTOR = "body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div.mt-4 > div";
	function extractCode(pathname) {
		const filename = pathname.split("/").pop() || "";
		if (/^FC2-PPV-\d+$/i.test(filename)) return filename;
		const parts = filename.split("-");
		return parts.length >= 2 ? parts[0] + "-" + parts[1] : filename;
	}
	function copyToClipboard(text) {
		return navigator.clipboard.writeText(text).then(() => true).catch(() => false);
	}
	function showToast(message, duration = 2e3) {
		const toast = document.createElement("div");
		toast.textContent = message;
		Object.assign(toast.style, {
			position: "fixed",
			bottom: "20px",
			left: "50%",
			transform: "translateX(-50%)",
			background: "rgba(0,0,0,0.7)",
			color: "#fff",
			padding: "8px 16px",
			borderRadius: "4px",
			fontSize: "14px",
			zIndex: "9999",
			transition: "opacity 0.3s",
			opacity: "1"
		});
		document.body.appendChild(toast);
		setTimeout(() => {
			toast.style.opacity = "0";
			setTimeout(() => toast.remove(), 300);
		}, duration);
	}
	function openInNewTab(url) {
		console.log(`${LOG} openInNewTab 触发`);
		try {
			const a = document.createElement("a");
			a.href = url;
			a.target = "_blank";
			a.style.display = "none";
			document.body.appendChild(a);
			a.click();
			console.log(`${LOG} 执行 模拟单击 a 标签`);
			document.body.removeChild(a);
			return true;
		} catch (e) {
			console.log(`${LOG} 单击 a 标签失败:`, e);
			return false;
		}
	}
	function createButton(label, svgElement, onClick) {
		const button = document.createElement("button");
		button.type = "button";
		button.className = "inline-flex items-center whitespace-nowrap text-sm text-nord4 leading-4 font-medium focus:outline-none hover:text-nord6";
		button.appendChild(svgElement);
		button.appendChild(document.createTextNode(label));
		button.addEventListener("click", (e) => {
			e.preventDefault();
			e.stopPropagation();
			onClick();
		});
		return button;
	}
	function createSVGClipboard() {
		const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
		svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
		svg.setAttribute("fill", "currentColor");
		svg.setAttribute("class", "mr-1 md:mr-2 h-3 w-3 xs:h-4 xs:w-4");
		svg.setAttribute("viewBox", "0 0 16 16");
		const path1 = document.createElementNS(svg.namespaceURI, "path");
		path1.setAttribute("fill-rule", "evenodd");
		path1.setAttribute("d", "M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0");
		const path2 = document.createElementNS(svg.namespaceURI, "path");
		path2.setAttribute("d", "M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z");
		const path3 = document.createElementNS(svg.namespaceURI, "path");
		path3.setAttribute("d", "M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z");
		svg.appendChild(path1);
		svg.appendChild(path2);
		svg.appendChild(path3);
		return svg;
	}
	function createSVGJavDB() {
		const html = jsxToString((0, import_jsx_runtime.jsx)(JavDBIcon, {}));
		const template = document.createElement("template");
		template.innerHTML = html;
		const svg = template.content.querySelector("svg");
		if (svg instanceof SVGElement) {
			svg.removeAttribute("width");
			svg.removeAttribute("height");
			svg.setAttribute("class", "mr-1 md:mr-2 h-3 w-3 xs:h-4 xs:w-4");
		}
		return svg;
	}
	function insertButtons(menuBar) {
		const fanhao = extractCode(window.location.pathname);
		const copyBtn = createButton("复制番号", createSVGClipboard(), () => {
			copyToClipboard(fanhao).then((success) => {
				showToast(success ? `番号 ${fanhao} 已复制` : "复制失败");
			});
		});
		menuBar.prepend(copyBtn);
		const javdbBtn = createButton("转到JavDB搜索", createSVGJavDB(), () => {
			const javdbUrl = `https://javdb.com/search?q=${encodeURIComponent(fanhao)}&f=all`;
			console.log(`${LOG} 尝试打开 JavDB 链接:`, javdbUrl);
			if (openInNewTab(javdbUrl)) showToast(`正在跳转到 JavDB 搜索: ${fanhao}`);
			else {
				showToast("跳转失败,请检查弹窗阻止设置");
				copyToClipboard(javdbUrl).then((copied) => {
					if (copied) showToast("链接已复制到剪贴板,请手动打开");
				});
			}
		});
		menuBar.appendChild(javdbBtn);
		console.log(`${LOG} ✅ Copy 和 JavDB 按钮已插入 menu_bar`);
	}
	var MissavQuickCopyPlugin = class extends BasePlugin {
		observer = null;
		getName() {
			return "MissavQuickCopyPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			const existing = document.querySelector(MENU_BAR_SELECTOR);
			if (existing) {
				insertButtons(existing);
				return;
			}
			this.observer = new MutationObserver(() => {
				const menuBar = document.querySelector(MENU_BAR_SELECTOR);
				if (menuBar) {
					this.observer?.disconnect();
					this.observer = null;
					insertButtons(menuBar);
				}
			});
			this.observer.observe(document.body, {
				childList: true,
				subtree: true
			});
		}
	};
	var NO_TAG_VALUE = "no-tag";
	var STATUS_ORDER = [
		"favorite",
		"hasWatch",
		"filter",
		"hasDown"
	];
	function isVideoDetailPath(path) {
		if (path.includes("/search/")) return false;
		const last = path.split("/").pop() || "";
		return /^[a-z0-9]+(?:-[a-z0-9]+)+$/i.test(last);
	}
	function statusToLabel(status) {
		return STATUS_CONFIG[status]?.label || status;
	}
	function getItemStatus(item) {
		return item.querySelector(".missav-status-tag")?.dataset.status || null;
	}
	function collectStatusCounts() {
		const counts = {};
		let noTag = 0;
		document.querySelectorAll(".thumbnail.group").forEach((item) => {
			const status = getItemStatus(item);
			if (status) {
				const name = statusToLabel(status);
				counts[name] = (counts[name] || 0) + 1;
			} else noTag++;
		});
		if (noTag > 0) counts[NO_TAG_VALUE] = noTag;
		return counts;
	}
	var MAP_SEL = {
		thumb: ".thumbnail.group",
		grid: "div.grid.grid-cols-2.md\\:grid-cols-3.xl\\:grid-cols-4.gap-5",
		nextLink: "a[href*='page=']"
	};
	function findNextUrl(doc) {
		const next = Array.from(doc.querySelectorAll(MAP_SEL.nextLink)).find((a) => {
			const text = a.textContent?.trim() || "";
			return text === "下一页" || text === "Next";
		});
		return next ? next.href : null;
	}
	function extractThumbs(container) {
		return Array.from(container.querySelectorAll(MAP_SEL.thumb));
	}
	function collectCarNums(thumbs) {
		return thumbs.map((t) => {
			const a = t.querySelector("a[alt]");
			return a ? normalizeCarNum(a.getAttribute("alt") || "") : "";
		}).filter(Boolean);
	}
	function liftDataSrcToSrc(container) {
		let lifted = 0;
		container.querySelectorAll("img").forEach((img) => {
			const dataSrc = img.getAttribute("data-src");
			if (dataSrc) {
				img.src = dataSrc;
				lifted++;
			}
		});
		return lifted;
	}
	function gmGet(url) {
		return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
				method: "GET",
				url,
				timeout: 3e4,
				onload: (resp) => {
					if (resp.status >= 200 && resp.status < 300) resolve(resp.responseText);
					else reject(new Error(`HTTP ${resp.status}`));
				},
				onerror: () => reject(new Error("network error")),
				ontimeout: () => reject(new Error("timeout"))
			});
		});
	}
	async function fetchNextPage(plugin) {
		if (!plugin.pagination.canLoad || !plugin.nextUrl) return;
		plugin.pagination.startLoading();
		plugin.setLoader("jhs-map-loading", "加载中...");
		try {
			const html = await gmGet(plugin.nextUrl);
			const doc = new DOMParser().parseFromString(String(html), "text/html");
			const thumbs = extractThumbs(doc);
			if (thumbs.length === 0) throw new Error("下一页无缩略图");
			const carNums = collectCarNums(thumbs);
			if (carNums.length > 0 && plugin.loadedCarNums.has(carNums[0])) {
				plugin.nextUrl = null;
				plugin.pagination.loadSuccess(false);
				plugin.setLoader("jhs-map-no-more", "已经到底了");
				return;
			}
			carNums.forEach((c) => c && plugin.loadedCarNums.add(c));
			liftDataSrcToSrc(doc);
			const fragment = document.createDocumentFragment();
			thumbs.forEach((t) => fragment.appendChild(t));
			plugin.container.appendChild(fragment);
			plugin.loadedPages += 1;
			const next = findNextUrl(doc);
			plugin.nextUrl = next;
			plugin.pagination.loadSuccess(!!next);
			plugin.setLoader("", "");
			if (!plugin.nextUrl) plugin.setLoader("jhs-map-no-more", "已经到底了");
		} catch (err) {
			clog.error("[MissAV 瀑布流] 加载失败:", err);
			plugin.pagination.loadError();
			plugin.setLoader("jhs-map-error", "加载失败,点击重试");
		} finally {
			if (!plugin.pagination.isLoading && plugin.pagination.hasMore && plugin.nextUrl) plugin.checkLoad();
		}
	}
	var missav_auto_page_default = "/* MissAV 列表页瀑布流(MissavAutoPagePlugin)—— loader / 加载全部按钮样式。\n * 回到顶部按钮样式复用 back-to-top-button.css(initCss 拼接注入)。\n */\n\n/* 底部 loader:空闲透明占位,click 态显示「点击加载下一页」按钮 */\n.jhs-map-loader {\n    text-align: center;\n    padding: 16px;\n    color: #888;\n    user-select: none;\n}\n\n.jhs-map-loader.jhs-map-loading {\n    color: #000;\n    cursor: default;\n}\n\n.jhs-map-loader.jhs-map-error {\n    color: #f44336;\n    cursor: pointer;\n}\n\n.jhs-map-loader.jhs-map-no-more {\n    color: #4caf50;\n    cursor: default;\n    font-size: 13px;\n}\n\n.jhs-map-loader.jhs-map-click {\n    color: #1976d2;\n    cursor: pointer;\n    font-weight: 600;\n}\n\n.jhs-map-loader.jhs-map-click:hover {\n    color: #0d47a1;\n    text-decoration: underline;\n}\n\n/* 「加载全部」浮动按钮(左下角,避开右侧回到顶部按钮;nord 暗色与 missav 页面融合) */\n.jhs-map-load-all-btn {\n    position: fixed;\n    left: 20px;\n    bottom: 80px;\n    z-index: var(--jhs-z-page);\n    padding: 10px 20px;\n    background: #2e3440; /* nord0 */\n    border: 1px solid #4c566a; /* nord3 */\n    color: #d8dee9; /* nord4 */\n    border-radius: 20px;\n    cursor: pointer;\n    font-size: 14px;\n    box-shadow: 0 2px 8px rgb(0 0 0 / 0.3);\n    transition:\n        transform 0.2s ease,\n        box-shadow 0.2s ease,\n        border-color 0.2s ease,\n        background-color 0.2s ease,\n        opacity 0.5s ease;\n}\n\n.jhs-map-load-all-btn:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 4px 12px rgb(0 0 0 / 0.4);\n    background: #3b4252; /* nord1 */\n    border-color: #81a1c1; /* frost */\n}\n\n.jhs-map-load-all-btn.jhs-map-load-all-disabled {\n    opacity: 0.6;\n    cursor: wait;\n}\n\n.jhs-map-load-all-btn.jhs-map-load-all-fadeout {\n    opacity: 0;\n}";
	var PRELOAD_DISTANCE = 800;
	var MissavAutoPagePlugin = class extends BasePlugin {
		container = null;
		nextUrl = null;
		pagination = new PaginationStateMachine();
		supervisor = new TaskSupervisor();
		loader = null;
		loadAllBtn = null;
		isLoadingAll = false;
		loadedPages = 1;
		loadedCarNums = new Set();
		backToTopCleanup = null;
		getName() {
			return "MissavAutoPagePlugin";
		}
		async initCss() {
			return missav_auto_page_default + back_to_top_button_default;
		}
		async handle() {
			if (isVideoPage() || isVideoDetailPath(location.pathname)) return;
			let container = document.querySelector(MAP_SEL.grid);
			if (!container) container = document.querySelector(MAP_SEL.thumb)?.parentElement?.parentElement ?? null;
			if (!container) return;
			this.container = container;
			const loader = document.createElement("div");
			loader.className = "jhs-map-loader";
			container.parentNode?.insertBefore(loader, container.nextSibling);
			this.loader = loader;
			this.nextUrl = findNextUrl(document);
			this.pagination.init(!!this.nextUrl);
			collectCarNums(extractThumbs(container)).forEach((c) => c && this.loadedCarNums.add(c));
			this.supervisor.addEventListener(loader, "click", () => {
				if (this.pagination.isLoading) return;
				if (this.pagination.phase === "error") this.pagination.retry();
				if (this.pagination.canLoad && this.nextUrl) fetchNextPage(this);
			});
			this.supervisor.addEventListener(window, "scroll", () => this.checkLoad(), { passive: true });
			this.showLoadAllBtn();
			this.backToTopCleanup = createBackToTopButton();
			this.supervisor.setTimeout(() => {
				this.checkLoad();
			}, 500);
			if (!this.pagination.hasMore) this.setLoader("jhs-map-no-more", "已经到底了");
		}
		checkLoad() {
			if (!this.loader) return;
			if (this.loader.getBoundingClientRect().top < window.innerHeight + PRELOAD_DISTANCE) {
				if (this.pagination.isLoading || !this.pagination.hasMore || !this.nextUrl || this.loader.classList.contains("jhs-map-error") || this.loader.classList.contains("jhs-map-loading") || this.loader.classList.contains("jhs-map-no-more")) return;
				this.setLoader("jhs-map-click", "点击加载下一页");
			}
		}
		showLoadAllBtn() {
			if (this.loadAllBtn) return;
			if (!this.container || !this.pagination.hasMore) return;
			const btn = document.createElement("div");
			btn.className = "jhs-map-load-all-btn";
			btn.textContent = "加载全部";
			this.supervisor.addEventListener(btn, "click", () => {
				if (!this.isLoadingAll) this.loadAllPages();
			});
			document.body.appendChild(btn);
			this.loadAllBtn = btn;
		}
		async loadAllPages() {
			if (this.isLoadingAll || !this.pagination.hasMore) return;
			this.isLoadingAll = true;
			this.updateLoadAllBtn("加载中...", true);
			try {
				while (this.pagination.hasMore && this.nextUrl) {
					const before = this.loadedCarNums.size;
					await fetchNextPage(this);
					if (this.loadedCarNums.size === before) break;
					this.updateLoadAllBtn(`加载中...(第 ${this.loadedPages} 页)`, true);
				}
				if (this.loader?.classList.contains("jhs-map-error")) this.updateLoadAllBtn("加载失败,点击重试", false);
				else {
					this.updateLoadAllBtn("✓ 已全部加载", false);
					this.supervisor.setTimeout(() => {
						this.loadAllBtn?.classList.add("jhs-map-load-all-fadeout");
						this.supervisor.setTimeout(() => {
							this.loadAllBtn?.remove();
							this.loadAllBtn = null;
						}, 500);
					}, 2e3);
				}
			} catch (err) {
				clog.error("[MissAV 瀑布流] 加载全部失败:", err);
				this.updateLoadAllBtn("加载失败,点击重试", false);
			} finally {
				this.isLoadingAll = false;
			}
		}
		updateLoadAllBtn(text, disabled) {
			if (!this.loadAllBtn) return;
			this.loadAllBtn.textContent = text;
			this.loadAllBtn.classList.toggle("jhs-map-load-all-disabled", disabled);
		}
		setLoader(cls, text) {
			if (!this.loader) return;
			this.loader.className = `jhs-map-loader ${cls}`.trim();
			this.loader.textContent = text;
		}
		destroy() {
			this.supervisor.abort();
			this.backToTopCleanup?.();
			this.backToTopCleanup = null;
		}
	};
	function shouldShow(itemStatus, activeValues) {
		if (activeValues.size === 0) return true;
		return activeValues.has(itemStatus ?? "no-tag");
	}
	function getCell(item) {
		const parent = item.parentElement;
		if (parent && parent !== document.body && !String(parent.className).includes("grid-cols")) return parent;
		return item;
	}
	function applyFilter(activeValues) {
		document.querySelectorAll(".thumbnail.group").forEach((item) => {
			const status = item.querySelector(".missav-status-tag")?.dataset.status ?? null;
			const cell = getCell(item);
			if (shouldShow(status, activeValues)) {
				cell.style.display = "";
				cell.removeAttribute("data-mstf-hidden");
			} else {
				cell.style.display = "none";
				cell.setAttribute("data-mstf-hidden", "yes");
			}
		});
	}
	var missav_status_filter_default = "/* MissAV 状态标签筛选栏 —— missav 是 Tailwind nord 暗色主题,样式独立定义\n * (不参与 filter-chip-base.css 的 javdb 浅色共享基础,doc/223)。\n * 配色参考 missav 页面 Tailwind nord 令牌:nord0 #2e3440 / nord1 #3b4252 /\n * nord3 #4c566a / nord4 #d8dee9 / frost #81a1c1。\n */\n\n/* ---------- 筛选栏容器 ---------- */\n.missav-status-filter-bar {\n    display: flex;\n    align-items: flex-start;\n    gap: 8px;\n    padding: 8px 0 16px 0; /* 无分割线,用底部间距与下方内容自然分离(doc/227) */\n}\n\n/* ---------- 筛选栏标签 ---------- */\n.missav-status-filter-label {\n    flex-shrink: 0;\n    font-size: 0.8em;\n    font-weight: 600;\n    color: #d8dee9; /* nord4 */\n    line-height: 1.8;\n    margin-right: 4px;\n}\n\n/* ---------- 芯片容器 ---------- */\n.missav-status-filter-chips {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n}\n\n/* ---------- 芯片基础 ---------- */\n.missav-status-filter-chip {\n    display: inline-block;\n    padding: 0.25em 0.7em;\n    font-size: 0.75em;\n    font-weight: 600;\n    line-height: 1.5;\n    border-radius: 50rem;\n    cursor: pointer;\n    user-select: none;\n    border: 1.5px solid #4c566a; /* nord3 */\n    background: #2e3440; /* nord0 */\n    color: #d8dee9; /* nord4 */\n    transition:\n        color 0.15s ease,\n        background-color 0.15s ease,\n        border-color 0.15s ease;\n}\n\n/* ---------- 芯片悬停 ---------- */\n.missav-status-filter-chip:hover {\n    border-color: #81a1c1; /* frost */\n    background: #3b4252; /* nord1 */\n}\n\n/* ---------- 芯片选中 ---------- */\n.missav-status-filter-chip.active {\n    background: #5d87c2;\n    border-color: #5d87c2;\n    border-style: solid;\n    color: #fff;\n}\n\n/* ---------- 无状态标签芯片 ---------- */\n.missav-status-filter-chip.no-status {\n    color: #7c8794;\n    border-style: dashed;\n}\n\n.missav-status-filter-chip.no-status.active {\n    background: #8fbcbb; /* aurora frost */\n    border-color: #8fbcbb;\n    color: #1a1a1a;\n}";
	var MOUNT_SELECTOR = "div.flex.justify-between.mb-6";
	var MissavStatusFilterPlugin = class extends BasePlugin {
		supervisor = new TaskSupervisor();
		observerDebounce = 0;
		filterBar = null;
		getName() {
			return "MissavStatusFilterPlugin";
		}
		async initCss() {
			return missav_status_filter_default;
		}
		async handle() {
			if (isVideoPage() || isVideoDetailPath(location.pathname)) return;
			this.tryBuild();
			this.startObserving();
		}
		tryBuild() {
			if (document.querySelector(".missav-status-filter-bar")) return true;
			const mount = document.querySelector(MOUNT_SELECTOR) ?? document.querySelector("h1") ?? document.querySelector(".thumbnail.group")?.parentElement?.parentElement;
			if (!mount) return false;
			this.buildFilterBar(mount);
			return true;
		}
		buildFilterBar(mount) {
			const bar = document.createElement("div");
			bar.className = "missav-status-filter-bar";
			const label = document.createElement("span");
			label.className = "missav-status-filter-label";
			label.textContent = "状态:";
			const chips = document.createElement("div");
			chips.className = "missav-status-filter-chips";
			bar.append(label, chips);
			mount.insertAdjacentElement("afterend", bar);
			this.filterBar = bar;
			this.renderChips(chips);
			chips.addEventListener("click", (e) => {
				const chip = e.target.closest?.(".missav-status-filter-chip");
				if (chip) {
					chip.classList.toggle("active");
					this.applyActiveFilter();
				}
			});
		}
		renderChips(chips) {
			const active = new Set(Array.from(chips.querySelectorAll(".missav-status-filter-chip.active")).map((c) => c.dataset.value || ""));
			chips.innerHTML = "";
			const counts = collectStatusCounts();
			const ordered = [...STATUS_ORDER, ...counts["no-tag"] ? [NO_TAG_VALUE] : []];
			for (const status of ordered) {
				const label = statusToLabel(status);
				const c = counts[label] ?? 0;
				if (c === 0) continue;
				const chip = document.createElement("span");
				chip.className = "missav-status-filter-chip";
				if (status === "no-tag") chip.classList.add("no-status");
				chip.textContent = status === "no-tag" ? `无状态标签 ${c}` : `${label} ${c}`;
				chip.dataset.value = status;
				if (active.has(status)) chip.classList.add("active");
				chips.appendChild(chip);
			}
		}
		applyActiveFilter() {
			if (!this.filterBar) return;
			const chips = this.filterBar.querySelector(".missav-status-filter-chips");
			applyFilter(new Set(Array.from(chips?.querySelectorAll(".missav-status-filter-chip.active") ?? []).map((c) => c.dataset.value || "")));
		}
		startObserving() {
			this.supervisor.observe(document.body, (mutations) => {
				if (!this.observerNeedsRefresh(mutations)) return;
				if (!this.filterBar) this.tryBuild();
				clearTimeout(this.observerDebounce);
				this.observerDebounce = this.supervisor.setTimeout(() => {
					const bar = this.filterBar;
					if (!bar) return;
					const chips = bar.querySelector(".missav-status-filter-chips");
					if (chips) {
						const active = new Set(Array.from(chips.querySelectorAll(".missav-status-filter-chip.active")).map((c) => c.dataset.value || ""));
						this.renderChips(chips);
						applyFilter(active);
					}
				}, 150);
			}, {
				childList: true,
				subtree: true,
				attributes: true,
				attributeFilter: ["data-status"]
			});
		}
		observerNeedsRefresh(mutations) {
			for (const mutation of mutations) {
				if (mutation.type === "attributes" && mutation.attributeName === "data-status") return true;
				if (mutation.type !== "childList") continue;
				for (const node of mutation.addedNodes) {
					if (node.nodeType !== Node.ELEMENT_NODE) continue;
					const el = node;
					if (el.matches?.(".thumbnail.group, .missav-status-tag") || el.querySelector?.(".thumbnail.group, .missav-status-tag")) return true;
				}
				for (const node of mutation.removedNodes) {
					if (node.nodeType !== Node.ELEMENT_NODE) continue;
					const el = node;
					if (el.matches?.(".thumbnail.group, .missav-status-tag") || el.querySelector?.(".thumbnail.group, .missav-status-tag")) return true;
				}
			}
			return false;
		}
		destroy() {
			this.supervisor.abort();
		}
	};
	var CARD_SEL = ".thumbnail.group";
	function findDetailUrl(card) {
		const a = card.querySelector("a[href]");
		return a ? a.href : null;
	}
	function isPlainLeftClick(e) {
		return e.button === 0 && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey;
	}
	var TOP_BAR_SEL = "div.fixed.z-max.w-full.bg-gradient-to-b.from-darkest";
	var MissavVideoPreviewPlugin = class extends BasePlugin {
		supervisor = new TaskSupervisor();
		getName() {
			return "MissavVideoPreviewPlugin";
		}
		async initCss() {
			return "";
		}
		async handle() {
			this.supervisor.addEventListener(document, "click", (evt) => {
				const e = evt;
				const target = e.target;
				const card = target?.closest(CARD_SEL);
				if (!card || !isPlainLeftClick(e)) return;
				if (target?.closest(".missav-status-tag")) return;
				const url = findDetailUrl(card);
				if (!url) return;
				e.preventDefault();
				e.stopPropagation();
				const width = Math.min(1023, window.innerWidth - 40);
				const topBar = document.querySelector(TOP_BAR_SEL);
				const topBarPrevDisplay = topBar ? topBar.style.display : "";
				if (topBar) topBar.style.display = "none";
				layer.open({
					type: 2,
					content: url,
					scrollbar: false,
					area: [`${width}px`, "100%"],
					isOutAnim: false,
					anim: -1,
					end: () => {
						if (topBar) topBar.style.display = topBarPrevDisplay;
					}
				});
			}, { capture: true });
		}
		destroy() {
			this.supervisor.abort();
		}
	};
	var errorStyle = { color: "red" };
	function TranslatedTitle({ error, children }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "translated-title",
			style: error ? errorStyle : void 0,
			children
		});
	}
	var translate_plugin_default = "/* 标题翻译插件样式(原 translate-plugin.tsx initCss 返回的 <style> 块,\n   改走 CSS 管线:utils.insertStyle 自动包裹 <style> 注入 head,时机/作用域与原先一致)。 */\n.translated-title {\n    margin-top: 8px;\n    padding: 12px;\n    border-radius: 5px;\n    border-left: 4px solid rgb(76, 175, 80);\n    background: linear-gradient(135deg, rgb(255, 255, 255) 0%, rgb(245, 245, 245) 100%);\n    box-shadow: 0 4px 8px rgb(0, 0, 0, 0.08);\n    font-size: 20px;\n}\n";
	var TranslatePlugin = class extends BasePlugin {
		getName() {
			return "TranslatePlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		async initCss() {
			return translate_plugin_default;
		}
		async handle() {
			if (!featureFlags.translatePlugin) return;
			if (window.isDetailPage) this.translate().then();
		}
		async translate(carNum, showCarNum = true) {
			if (await storageManager.getSetting("translateTitle", "yes") !== "yes") return;
			let $titleElement = $(".origin-title");
			if (!$titleElement.length) $titleElement = $(".current-title");
			if (!$titleElement.length) $titleElement = $("h3");
			if (!$titleElement.length) return;
			const originalText = $titleElement.text().trim();
			if (!originalText) {
				show.error("获取标题失败, 无法进行翻译");
				return;
			}
			if ($titleElement.next(".translated-title").length) return;
			$titleElement.after(jsxToString((0, import_jsx_runtime.jsx)(TranslatedTitle, { children: "翻译中..." })));
			const $loadingElement = $titleElement.next(".translated-title");
			if (!carNum) try {
				carNum = this.getPageInfo().carNum || "";
			} catch {
				carNum = "";
			}
			const cache = readPersistentMap("translate", "jhs_translate");
			if (carNum && cache[carNum]) {
				$loadingElement.html(showCarNum ? carNum + "&nbsp;&nbsp;&nbsp;" + cache[carNum] : cache[carNum]);
				return;
			}
			try {
				const translatedText = await translateText(originalText, "ja", "zh-CN");
				$loadingElement.html(showCarNum && carNum ? carNum + "&nbsp;&nbsp;&nbsp;" + translatedText : translatedText);
				if (carNum) {
					cache[carNum] = translatedText;
					writePersistentEntry("translate", "jhs_translate", carNum, translatedText);
				}
			} catch (error) {
				clog.error("翻译失败:", error);
				const message = error instanceof DOMException && error.name === "AbortError" ? "翻译超时,请稍后重试" : `翻译失败: ${error instanceof Error ? error.message : String(error)}`;
				$loadingElement.replaceWith(jsxToString((0, import_jsx_runtime.jsx)(TranslatedTitle, {
					error: true,
					children: message
				})));
			}
		}
	};
	var reloadingStyle = {
		marginTop: "50px",
		cursor: "auto",
		color: "#000"
	};
	function ScreenReloading() {
		return (0, import_jsx_runtime.jsx)("div", {
			style: reloadingStyle,
			children: "正在重新加载..."
		});
	}
	var fallbackStyle = {
		marginTop: "50px",
		cursor: "auto",
		color: "#000"
	};
	function ScreenShotFallback({ carNum }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("div", {
				style: fallbackStyle,
				children: "获取缩略图失败"
			}),
			(0, import_jsx_runtime.jsx)("br", {}),
			(0, import_jsx_runtime.jsx)("a", {
				href: "#",
				className: "retry-link",
				children: "点击重试"
			}),
			" ",
			"或",
			" ",
			(0, import_jsx_runtime.jsx)("a", {
				className: "check-link",
				href: `https://javstore.net/search?q=${carNum}`,
				target: "_blank",
				children: "前往确认"
			})
		] });
	}
	var fullWidthStyle = { width: "100%" };
	function ScreenShotImage({ src, alt }) {
		return (0, import_jsx_runtime.jsx)("img", {
			src,
			alt,
			loading: "lazy",
			style: fullWidthStyle
		});
	}
	var ScreenShotPlugin = class extends BasePlugin {
		getName() {
			return "ScreenShotPlugin";
		}
		get pageTypes() {
			return ["detail"];
		}
		async handle() {
			if (!featureFlags.screenShotPlugin) return;
			this.loadScreenShot().then();
		}
		async loadScreenShot() {
			if (!window.isDetailPage) return;
			const carNum = this.getPageInfo().carNum;
			$(".preview-images .tile-item").first().before(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "215px" })));
			try {
				const imgUrl = await this.getScreenshot(carNum);
				this.addImg("缩略图", imgUrl);
				clog.log("加载缩略图:", imgUrl);
			} catch (e) {
				this.showErrorFallback(carNum, e);
			}
		}
		async getScreenshot(carNum) {
			const cacheData = readPersistentMap("screenShot", "jhs_screenShot");
			if (cacheData[carNum]) {
				clog.debug("缓存中存在缩略图:", carNum, cacheData[carNum]);
				return cacheData[carNum];
			}
			let imgUrl = null;
			try {
				imgUrl = await this.getJavStoreScreenShot(carNum);
			} catch (e) {
				clog.error("获取缩略图资源失败:", imgUrl, e);
				throw e;
			}
			if (!imgUrl) {
				this.showErrorFallback(carNum, null);
				return null;
			}
			const httpsIndex = imgUrl.indexOf("https://");
			if (httpsIndex !== -1) imgUrl = imgUrl.substring(httpsIndex);
			cacheData[carNum] = imgUrl;
			clog.log("缩略图获取成功:", imgUrl);
			writePersistentEntry("screenShot", "jhs_screenShot", carNum, imgUrl);
			return imgUrl;
		}
		async getJavStoreScreenShot(carNum) {
			const url = `https://javstore.net/search?q=${encodeURIComponent(carNum.toLowerCase().replace("fc2-", ""))}`;
			clog.log("正在解析缩略图:", url);
			const html = await gmHttp.get(url);
			const $dom = utils.htmlTo$dom(String(html));
			const tempCarNum = carNum.toLowerCase().replace(/fc2-(ppv-)?/g, "").replace(/-/g, "");
			let detailPageUrl = null;
			const $itemList = $dom.find("main .grid a");
			for (let i = 0; i < $itemList.length; i++) {
				const href = $($itemList[i]).attr("href") || "";
				if (href.toLowerCase().replace(/fc2-(ppv-)?/g, "").replace(/-/g, "").includes(tempCarNum)) {
					detailPageUrl = new URL(href, "https://javstore.net").href;
					break;
				}
			}
			if (!detailPageUrl) {
				clog.error("JavStore, 查询番号失败:", url);
				return null;
			}
			const detailPageHtml = await gmHttp.get(detailPageUrl);
			const $detailPageDom = utils.htmlTo$dom(String(detailPageHtml));
			const imgUrl = $detailPageDom.find("a:contains('CLICK HERE')").attr("href") || $detailPageDom.find("img[src*='_s.jpg']").attr("src");
			if (!imgUrl) {
				clog.error("JavStore, 解析预览图失败:", url);
				return null;
			}
			return String(imgUrl).replace(".th", "");
		}
		addImg(title, imgUrl) {
			if (imgUrl) {
				$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenShotImage, {
					src: imgUrl,
					alt: title
				})));
				$(".screen-container").on("click", (event) => {
					event.stopPropagation();
					event.preventDefault();
					window.showImageViewer(event.currentTarget);
				});
			}
		}
		showErrorFallback(carNum, error) {
			clog.error("获取缩略图失败:", error instanceof Error ? error.message.substring(0, 100) : String(error));
			$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenShotFallback, { carNum }))).off("click", ".retry-link").off("click", ".check-link").on("click", ".retry-link", async (e) => {
				e.stopPropagation();
				e.preventDefault();
				$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenReloading, {})));
				try {
					const imgUrl = await this.getScreenshot(carNum);
					this.addImg("缩略图", imgUrl);
				} catch (err) {
					this.showErrorFallback(carNum, err);
				}
			}).on("click", ".check-link", async (e) => {
				e.stopPropagation();
				e.preventDefault();
				window.open(`https://javstore.net/search?q=${encodeURIComponent(carNum)}`, "_blank", "noopener,noreferrer");
			});
		}
	};
	function MagnetHubContainer() {
		return (0, import_jsx_runtime.jsx)("div", { className: "magnet-container" });
	}
	function MagnetHubTabs() {
		return (0, import_jsx_runtime.jsx)("div", { className: "magnet-tabs" });
	}
	function MagnetHubResults() {
		return (0, import_jsx_runtime.jsx)("div", { className: "magnet-results" });
	}
	function MagnetHubLoading({ engineName, keyword }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "magnet-loading",
			children: [
				"正在从 ",
				engineName,
				" 搜索 \"",
				keyword,
				"\"..."
			]
		});
	}
	function MagnetHubError({ message }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "magnet-error",
			children: message
		});
	}
	function MagnetHubTab({ engineId, engineName }) {
		return (0, import_jsx_runtime.jsx)("div", {
			className: "magnet-tab",
			"data-engine": engineId,
			children: engineName
		});
	}
	function MagnetHubTargetLink({ href }) {
		return (0, import_jsx_runtime.jsx)("a", {
			style: {
				marginRight: "20px",
				marginTop: "3px"
			},
			id: "targetBox",
			href,
			target: "_blank",
			children: "原网页"
		});
	}
	function MagnetResultCard({ magnet, title, size, date }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "magnet-result",
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					className: "magnet-title",
					children: (0, import_jsx_runtime.jsx)("a", {
						href: magnet,
						children: title
					})
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "magnet-info",
					children: [(0, import_jsx_runtime.jsxs)("span", { children: ["大小: ", size] }), (0, import_jsx_runtime.jsxs)("span", { children: ["日期: ", date] })]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "magnet-copy",
					children: (0, import_jsx_runtime.jsx)("button", {
						className: "magnet-hub-btn copy-btn",
						"data-magnet": magnet,
						children: "复制链接"
					})
				})
			]
		});
	}
	var magnet_hub_plugin_default = "/*\n * 磁链聚合 MagnetHubPlugin 样式。\n *\n * 原脚本位于 MagnetHubPlugin.initCss() 返回的 `<style>` 字符串内(经\n * PluginManager.processCss → utils.insertStyle 注入 head),此处提取为\n * 独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入\n * (insertStyle 检测无 <style> 标签时自动包裹)。注入目标始终为 head\n * (全局作用域),与原实现一致。CSS 规则内容与原字符串一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n.magnet-container { margin: 20px auto; width: 100%; font-family: Arial, sans-serif; }\n.magnet-tabs { display: flex; border-bottom: 1px solid #ddd; margin-bottom: 15px; justify-content: space-between; }\n.magnet-tab { padding: 5px 12px; cursor: pointer; border: 1px solid transparent; border-bottom: none; margin-right: 5px; background: #f5f5f5; border-radius: 5px 5px 0 0; }\n.magnet-tab.active { background: #fff; border-color: #ddd; border-bottom: 1px solid #fff; margin-bottom: -1px; font-weight: bold; }\n.magnet-tab:hover:not(.active) { background: #e9e9e9; }\n.magnet-results { min-height: 200px; }\n.magnet-result { padding: 15px; border-bottom: 1px solid #eee; position: relative; }\n.magnet-result:hover { background-color: #f9f9f9; }\n.magnet-title { font-weight: bold; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: 80px; }\n.magnet-info { display: flex; justify-content: space-between; color: #666; font-size: 13px; }\n.magnet-copy { position: absolute; right: 15px; top: 15px; }\n.magnet-hub-btn { padding: 4px 10px; margin-left: 4px; cursor: pointer; border: 1px solid #ddd; border-radius: 3px; background: #f5f5f5; }\n.magnet-hub-btn.copied { background: #4caf50; color: #fff; }\n.magnet-loading, .magnet-error { padding: 20px; text-align: center; color: #666; }\n.magnet-error { color: #c00; }\n";
	var MagnetHubPlugin = class extends BasePlugin {
		currentEngine = null;
		_searchSeq = 0;
		searchEngines = [
			{
				name: "U9A9",
				id: "u9a9",
				url: "https://u9a9.com/?type=2&search={keyword}",
				targetPage: "https://u9a9.com/?type=2&search={keyword}",
				parseHtml: this.parseU3C3
			},
			{
				name: "U3C3",
				id: "u3c3",
				url: "https://u3c3.com/?search2=a8lr16lo&search={keyword}",
				targetPage: "https://u3c3.com/?search2=a8lr16lo&search={keyword}",
				parseHtml: this.parseU3C3
			},
			{
				name: "Sukebei",
				id: "Sukebei",
				url: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}",
				targetPage: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}",
				parseHtml: this.parseSukebei
			}
		];
		getName() {
			return "MagnetHubPlugin";
		}
		async initCss() {
			return magnet_hub_plugin_default;
		}
		createMagnetHub(keyword) {
			if (!featureFlags.magnetHubPlugin) return $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: "磁力聚合未启用" })));
			keyword = keyword.replace("FC2-", "");
			const $container = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubContainer, {})));
			const $tabs = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTabs, {})));
			const key = "jhs_magnetHub_selectedEngine";
			const savedEngineId = localStorage.getItem(key);
			let defaultEngineIndex = 0;
			const $tabBox = $(document.createElement("div")).css("display", "flex");
			this.searchEngines.forEach((engine, index) => {
				const $tab = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTab, {
					engineId: engine.id,
					engineName: engine.name
				})));
				if (savedEngineId && engine.id === savedEngineId) {
					$tab.addClass("active");
					this.currentEngine = engine;
					defaultEngineIndex = index;
				} else if (index === 0 && !savedEngineId) {
					$tab.addClass("active");
					this.currentEngine = engine;
				}
				$tabBox.append($tab);
			});
			$tabs.append($tabBox);
			const engine = this.currentEngine || this.searchEngines[0];
			$tabs.append(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTargetLink, { href: engine.targetPage.replace("{keyword}", encodeURIComponent(keyword)) })));
			$container.append($tabs);
			const $resultsContainer = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubResults, {})));
			$container.append($resultsContainer);
			$container.on("click", ".magnet-tab", (e) => {
				const engineId = $(e.target).data("engine");
				this.currentEngine = this.searchEngines.find((eng) => eng.id === engineId) || null;
				if (this.currentEngine) {
					$("#targetBox").attr("href", this.currentEngine.targetPage.replace("{keyword}", encodeURIComponent(keyword)));
					localStorage.setItem(key, engineId);
					$container.find(".magnet-tab").removeClass("active");
					$(e.target).addClass("active");
					this.searchEngine($resultsContainer, this.currentEngine, keyword);
				}
			});
			this.searchEngine($resultsContainer, this.currentEngine || this.searchEngines[defaultEngineIndex], keyword);
			return $container;
		}
		searchEngine($container, engine, keyword) {
			const seq = ++this._searchSeq;
			$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubLoading, {
				engineName: engine.name,
				keyword
			})));
			const cacheKey = `${engine.name}_${keyword}`;
			const cached = sessionStorage.getItem(cacheKey);
			if (cached) try {
				this.displayResults($container, JSON.parse(cached), engine.name);
				return;
			} catch {}
			const url = engine.url.replace("{keyword}", encodeURIComponent(keyword));
			if (engine.parseHtml) GM_xmlhttpRequest({
				method: "GET",
				url,
				timeout: 15e3,
				onload: (response) => {
					try {
						const results = engine.parseHtml.call(this, response.responseText, keyword);
						if (results.length > 0) sessionStorage.setItem(cacheKey, JSON.stringify(results));
						if (seq !== this._searchSeq) return;
						this.displayResults($container, results, engine.name);
					} catch (e) {
						$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `解析 ${engine.name} 结果失败: ${e instanceof Error ? e.message : String(e)}` })));
					}
				},
				onerror: (error) => {
					if (seq !== this._searchSeq) return;
					$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `从 ${engine.name} 获取数据失败: ${error.statusText || "网络错误"}` })));
				},
				ontimeout: () => {
					if (seq !== this._searchSeq) return;
					$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `从 ${engine.name} 获取数据超时` })));
				}
			});
		}
		displayResults($container, results, _engineName) {
			$container.empty();
			if (results.length === 0) {
				$container.append(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: "没有找到相关结果" })));
				return;
			}
			results.forEach((result) => {
				const $result = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetResultCard, {
					magnet: result.magnet,
					title: result.title,
					size: result.size || "未知",
					date: result.date || "未知"
				})));
				$container.append($result);
			});
			$container.on("click", ".copy-btn", function() {
				const $btn = $(this);
				const magnet = $btn.data("magnet");
				const showCopied = () => {
					const originalText = $btn.text();
					$btn.addClass("copied").text("已复制");
					setTimeout(() => {
						$btn.removeClass("copied").text(originalText);
					}, 2e3);
				};
				if (navigator.clipboard) navigator.clipboard.writeText(magnet).then(showCopied).catch(() => {
					utils.copyToClipboard("磁链", magnet);
					showCopied();
				});
				else {
					utils.copyToClipboard("磁链", magnet);
					showCopied();
				}
			});
		}
		parseU3C3(html, keyword) {
			const $dom = utils.htmlTo$dom(html);
			const results = [];
			$dom.find(".torrent-list tbody tr").each((_i, el) => {
				const $el = $(el);
				if ($el.text().includes("置顶")) return;
				const title = $el.find("td:nth-child(2) a").attr("title") || $el.find("td:nth-child(2) a").text().trim();
				if (!title.toLowerCase().includes(keyword.toLowerCase())) return;
				const magnet = $el.find("td:nth-child(3) a[href^='magnet:']").attr("href");
				const size = $el.find("td:nth-child(4)").text().trim();
				const date = $el.find("td:nth-child(5)").text().trim();
				if (magnet) results.push({
					title,
					magnet,
					size,
					date
				});
			});
			return results;
		}
		parseSukebei(html, keyword) {
			return this.parseU3C3(html, keyword);
		}
	};
	function ImageRecognitionDialog() {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: { padding: "20px" },
			children: [(0, import_jsx_runtime.jsxs)("div", {
				id: "upload-area",
				children: [
					(0, import_jsx_runtime.jsxs)("div", {
						style: {
							color: "#555",
							marginBottom: "15px"
						},
						children: [(0, import_jsx_runtime.jsx)("p", { children: "拖拽图片到此处 或 点击按钮选择图片" }), (0, import_jsx_runtime.jsx)("p", { children: "也可以直接 Ctrl+V 粘贴图片 / 图片URL" })]
					}),
					(0, import_jsx_runtime.jsx)("button", {
						id: "select-image-btn",
						children: "选择图片"
					}),
					(0, import_jsx_runtime.jsx)("input", {
						type: "file",
						style: { display: "none" },
						id: "image-file",
						accept: "image/*"
					})
				]
			}), (0, import_jsx_runtime.jsxs)("div", {
				id: "preview-area",
				style: {
					marginBottom: "20px",
					textAlign: "center",
					display: "none"
				},
				children: [
					(0, import_jsx_runtime.jsx)("img", {
						id: "preview-image",
						alt: "",
						src: "",
						style: {
							maxWidth: "100%",
							maxHeight: "300px",
							borderRadius: "4px"
						}
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						style: {
							marginTop: "15px",
							display: "flex",
							justifyContent: "center",
							gap: "10px"
						},
						id: "action-btns",
						children: [(0, import_jsx_runtime.jsx)("button", {
							id: "handle-btn",
							children: "搜索图片"
						}), (0, import_jsx_runtime.jsx)("button", {
							id: "cancel-btn",
							children: "取消"
						})]
					}),
					(0, import_jsx_runtime.jsxs)("div", {
						id: "search-results",
						style: { display: "none" },
						children: [(0, import_jsx_runtime.jsxs)("p", {
							style: { margin: "20px auto" },
							children: ["请选择识图网站:", (0, import_jsx_runtime.jsx)("a", {
								id: "openAll",
								style: { cursor: "pointer" },
								children: "全部打开"
							})]
						}), (0, import_jsx_runtime.jsx)("div", {
							className: "search-img-site-btns-container",
							id: "search-img-site-btns-container"
						})]
					})
				]
			})]
		});
	}
	function ImageRecognitionHint() {
		return (0, import_jsx_runtime.jsx)("p", {
			style: { color: "#c00" },
			children: "图片尚未获得公网 URL,请重试搜索"
		});
	}
	function ImageRecognitionSiteButton({ href, icon, name }) {
		return (0, import_jsx_runtime.jsxs)("a", {
			className: "search-img-site-btn",
			href,
			target: "_blank",
			children: [(0, import_jsx_runtime.jsx)("img", {
				src: icon,
				alt: ""
			}), (0, import_jsx_runtime.jsx)("span", { children: name })]
		});
	}
	var image_recognition_plugin_default = "/*\n * 以图识图 ImageRecognitionPlugin 样式。\n *\n * 原脚本位于 ImageRecognitionPlugin.initCss() 返回的 `\\3c style>` 字符串内\n * (经 PluginManager.processCss → utils.insertStyle 注入 head),此处提取\n * 为独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入\n * (insertStyle 检测无 \\3c style> 标签时自动包裹)。注入目标始终为 head\n * (全局作用域),与原实现一致。CSS 规则内容与原字符串一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n#upload-area {\n    border: 2px dashed #85af68; border-radius: 8px; padding: 40px;\n    text-align: center; margin-bottom: 20px; transition: border-color 0.3s, background-color 0.3s;\n    background-color: #f9f9f9;\n}\n#upload-area:hover { border-color: #76b947; background-color: #f0f0f0; }\n#upload-area.highlight { border-color: #2196F3; background-color: #e3f2fd; }\n\n#select-image-btn {\n    background-color: #4CAF50; color: white; border: none;\n    padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px;\n}\n\n#handle-btn, #cancel-btn {\n    padding: 8px 16px; border-radius: 4px; cursor: pointer;\n    font-size: 14px; border: none;\n}\n#handle-btn { background-color: #2196F3; color: white; }\n#cancel-btn { background-color: #f44336; color: white; }\n\n.search-img-site-btns-container {\n    display: flex; flex-wrap: wrap; gap: 10px; margin-top: 15px;\n}\n\n.search-img-site-btn {\n    display: flex; align-items: center; padding: 8px 12px;\n    background-color: #f5f5f5; border-radius: 4px; text-decoration: none;\n    color: #333; font-size: 14px; border: 1px solid #ddd;\n}\n.search-img-site-btn img { width: 16px; height: 16px; margin-right: 6px; }\n";
	var ImageRecognitionPlugin = class extends BasePlugin {
		siteList = [
			{
				name: "Google旧版",
				url: "https://www.google.com/searchbyimage?image_url={placeholder}&client=firefox-b-d",
				ico: "https://www.google.com/favicon.ico"
			},
			{
				name: "Google",
				url: "https://lens.google.com/uploadbyurl?url={placeholder}",
				ico: "https://www.google.com/favicon.ico"
			},
			{
				name: "Yandex",
				url: "https://yandex.ru/images/search?rpt=imageview&url={placeholder}",
				ico: "https://yandex.ru/favicon.ico"
			}
		];
		isUploading = false;
		currentImageUrl = null;
		getName() {
			return "ImageRecognitionPlugin";
		}
		async initCss() {
			return image_recognition_plugin_default;
		}
		open(onOpenFun) {
			if (!featureFlags.imageRecognitionPlugin) return;
			layer.open({
				type: 1,
				title: "以图识图",
				content: jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionDialog, {})),
				area: utils.isMobile() ? utils.getResponsiveArea() : ["40%", "80%"],
				success: async () => {
					this.initEventListeners();
					onOpenFun?.();
				},
				end: () => {
					$(document).off("paste.searchImg");
				}
			});
		}
		initEventListeners() {
			const $uploadArea = $("#upload-area");
			const $fileInput = $("#image-file");
			const $selectBtn = $("#select-image-btn");
			const $previewArea = $("#preview-area");
			const $previewImage = $("#preview-image");
			const $searchImage = $("#handle-btn");
			const $cancelBtn = $("#cancel-btn");
			const $searchResults = $("#search-results");
			const $siteBtnsContainer = $("#search-img-site-btns-container");
			$uploadArea.on("dragover", (e) => {
				e.preventDefault();
				$uploadArea.addClass("highlight");
			}).on("dragleave", () => $uploadArea.removeClass("highlight")).on("drop", (e) => {
				e.preventDefault();
				$uploadArea.removeClass("highlight");
				const files = e.originalEvent?.dataTransfer?.files;
				if (files?.[0]) this.handleImageFile(files[0]);
			});
			$selectBtn.on("click", () => $fileInput.trigger("click"));
			$fileInput.on("change", (e) => {
				const files = e.target.files;
				if (files?.[0]) this.handleImageFile(files[0]);
			});
			$(document).on("paste.searchImg", async (e) => {
				const clipData = e.originalEvent?.clipboardData;
				const items = clipData?.items;
				if (!items) return;
				for (let i = 0; i < items.length; i++) if (items[i].type.indexOf("image") !== -1) {
					const file = items[i].getAsFile();
					if (file) this.handleImageFile(file);
					return;
				}
				const text = clipData?.getData("text");
				if (text && /^https?:\/\//.test(text)) {
					this.currentImageUrl = text;
					$previewImage.attr("src", text);
					$uploadArea.hide();
					$previewArea.show();
				}
			});
			$searchImage.on("click", async () => {
				if (!this.currentImageUrl) {
					show.error("请先选择图片");
					return;
				}
				await this.searchByImage(this.currentImageUrl);
				$searchResults.show();
				this.renderSiteButtons($siteBtnsContainer);
			});
			$cancelBtn.on("click", () => {
				this.currentImageUrl = null;
				$previewArea.hide();
				$uploadArea.show();
				$searchResults.hide();
				$fileInput.val("");
			});
			$("#openAll").on("click", () => {
				this.siteList.forEach((site) => {
					if (this.currentImageUrl) window.open(site.url.replace("{placeholder}", encodeURIComponent(this.currentImageUrl)), "_blank");
				});
			});
		}
		handleImageFile(file) {
			const reader = new FileReader();
			reader.onload = (e) => {
				const dataUrl = e.target.result;
				this.currentImageUrl = dataUrl;
				$("#preview-image").attr("src", dataUrl);
				$("#upload-area").hide();
				$("#preview-area").show();
				this.searchByImage(dataUrl).then(() => {
					$("#search-results").show();
					this.renderSiteButtons($("#search-img-site-btns-container"));
				});
			};
			reader.readAsDataURL(file);
		}
		async searchByImage(imageSrc) {
			if (this.isUploading) return;
			if (/^https?:\/\//.test(imageSrc) && !imageSrc.startsWith("data:")) {
				this.currentImageUrl = imageSrc;
				return;
			}
			this.isUploading = true;
			try {
				const base64 = imageSrc.replace(/^data:image\/\w+;base64,/, "");
				const resp = await new Promise((resolve, reject) => {
					GM_xmlhttpRequest({
						method: "POST",
						url: "https://api.imgur.com/3/image",
						headers: {
							Authorization: "Client-ID d70305e7c3ac5c6",
							"Content-Type": "application/json"
						},
						data: JSON.stringify({
							image: base64,
							type: "base64"
						}),
						onload: (r) => {
							try {
								resolve(JSON.parse(r.responseText));
							} catch (err) {
								reject(err);
							}
						},
						onerror: reject
					});
				});
				if (resp?.data?.link) this.currentImageUrl = resp.data.link;
				else show.error("图片上传失败");
			} catch (e) {
				clog.error(e);
				show.error("图片上传失败: " + (e instanceof Error ? e.message : String(e)));
			} finally {
				this.isUploading = false;
			}
		}
		renderSiteButtons($container) {
			$container.empty();
			if (!this.currentImageUrl || this.currentImageUrl.startsWith("data:")) {
				$container.append(jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionHint, {})));
				return;
			}
			this.siteList.forEach((site) => {
				const href = site.url.replace("{placeholder}", encodeURIComponent(this.currentImageUrl));
				$container.append(jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionSiteButton, {
					href,
					icon: site.ico,
					name: site.name
				})));
			});
		}
	};
	var wrapperStyle = { marginBottom: "15px" };
	var searchRowStyle = {
		display: "flex",
		gap: "8px",
		flexWrap: "wrap",
		marginBottom: "10px"
	};
	var keywordInputStyle = { maxWidth: "280px" };
	var sortSelectStyle = { height: "2.5em" };
	var paginationStyle = {
		marginTop: "15px",
		textAlign: "center"
	};
	function Fc2BrowsePage({ keyword }) {
		return (0, import_jsx_runtime.jsxs)("div", {
			style: wrapperStyle,
			children: [
				(0, import_jsx_runtime.jsx)("h2", {
					className: "title is-4",
					children: "123Av FC2 浏览"
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: searchRowStyle,
					children: [
						(0, import_jsx_runtime.jsx)("input", {
							id: "fc2-123av-keyword",
							className: "input",
							style: keywordInputStyle,
							placeholder: "搜索关键词",
							value: keyword || ""
						}),
						(0, import_jsx_runtime.jsx)("button", {
							id: "fc2-123av-search",
							className: "button is-info",
							children: "搜索"
						}),
						(0, import_jsx_runtime.jsxs)("select", {
							id: "fc2-123av-sort",
							className: "select",
							style: sortSelectStyle,
							children: [
								(0, import_jsx_runtime.jsx)("option", {
									value: "release_date",
									children: "发布日期"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "recent_update",
									children: "最近更新"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "trending",
									children: "热门"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "most_viewed_today",
									children: "今天最多观看"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "most_viewed_week",
									children: "本周最多观看"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "most_viewed_month",
									children: "本月最多观看"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "most_viewed",
									children: "最多观看"
								}),
								(0, import_jsx_runtime.jsx)("option", {
									value: "most_favourited",
									children: "最受欢迎"
								})
							]
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					id: "fc2-123av-list",
					className: "movie-list h cols-4 vcols-8"
				}),
				(0, import_jsx_runtime.jsx)("div", {
					id: "fc2-123av-pagination",
					style: paginationStyle
				})
			]
		});
	}
	function hookPage(plugin) {
		document.title = "123Av FC2";
		const $box = plugin.$contentBox;
		if (!$box?.length) return;
		$box.html(jsxToString((0, import_jsx_runtime.jsx)(Fc2BrowsePage, { keyword: plugin.keyword ?? void 0 })));
		$("#fc2-123av-sort").val(plugin.sortVal);
		$("#fc2-123av-search").on("click", () => {
			plugin.keyword = String($("#fc2-123av-keyword").val() || "") || null;
			plugin.currentPage = 1;
			plugin.handleQuery();
		});
		$("#fc2-123av-sort").on("change", () => {
			plugin.sortVal = String($("#fc2-123av-sort").val() || "release_date");
			plugin.currentPage = 1;
			plugin.handleQuery();
		});
	}
	function renderPagination(plugin) {
		const $pg = $("#fc2-123av-pagination");
		if (!plugin.maxPage || plugin.maxPage <= 1) {
			$pg.empty();
			return;
		}
		let html = "";
		const cur = plugin.currentPage;
		const max = plugin.maxPage;
		const pages = [];
		for (let i = 1; i <= max; i++) if (i === 1 || i === max || i >= cur - 2 && i <= cur + 2) pages.push(i);
		let last = 0;
		for (const p of pages) {
			if (last && p - last > 1) html += jsxToString((0, import_jsx_runtime.jsx)("span", {
				style: { margin: "0 4px" },
				children: "…"
			}));
			html += jsxToString((0, import_jsx_runtime.jsx)("a", {
				className: `button is-small ${p === cur ? "is-primary" : ""}`,
				"data-page": p,
				style: { margin: "2px" },
				children: p
			}));
			last = p;
		}
		$pg.html(html);
		$pg.find("a[data-page]").on("click", (e) => {
			e.preventDefault();
			plugin.currentPage = parseInt($(e.currentTarget).attr("data-page") || "1", 10);
			plugin.handleQuery();
		});
	}
	async function handleQuery(plugin) {
		const seq = ++plugin._querySeq;
		const base = await plugin.getBaseUrl();
		const fetchPage = async (page) => {
			let url;
			if (plugin.keyword) url = `${base}/search?keyword=${encodeURIComponent(plugin.keyword)}&page=${page}&sort=${plugin.sortVal}`;
			else url = `${base}/makers/fc2?page=${page}&sort=${plugin.sortVal}`;
			const html = await gmHttp.get(url);
			return utils.htmlTo$dom(String(html));
		};
		try {
			show.info("正在加载 123Av FC2...");
			const pageA = (plugin.currentPage - 1) * 2 + 1;
			const pageB = pageA + 1;
			const $dom1 = await fetchPage(pageA);
			let movies = parseCards(plugin, $dom1);
			try {
				const $dom2 = await fetchPage(pageB);
				movies = movies.concat(parseCards(plugin, $dom2));
			} catch {}
			if (seq !== plugin._querySeq) return;
			const lastPageText = $dom1.find(".pagination .page-link").last().text() || $dom1.find(".pagination a").last().text();
			plugin.maxPage = Math.max(1, Math.ceil((parseInt(lastPageText, 10) || 1) / 2));
			const html = movies.map((movie) => jsxToString((0, import_jsx_runtime.jsx)(HitShowMovieItem, { movie }))).join("");
			$("#fc2-123av-list").html(html || jsxToString((0, import_jsx_runtime.jsx)("p", { children: "无结果" })));
			renderPagination(plugin);
			$("#fc2-123av-list .item a.box").on("click", (e) => {
				e.preventDefault();
				const href = $(e.currentTarget).attr("href") || "";
				const carNum = $(e.currentTarget).find("strong").text().trim() || href.split("/").pop() || "";
				plugin.open123AvFc2Dialog(carNum, href.startsWith("http") ? href : base + href);
			});
		} catch (e) {
			clog.error(e);
			show.error("加载 123Av FC2 失败: " + (e instanceof Error ? e.message : String(e)));
		}
	}
	function parseCards(_plugin, $dom) {
		const list = [];
		$dom.find(".card, .movie-list .item, .box").each((_i, el) => {
			const $el = $(el);
			const $a = $el.find("a").first();
			const href = $a.attr("href") || "";
			const title = $a.attr("title") || $el.find(".video-title, .title, h3").text().trim() || "";
			const number = $el.find("strong").text().trim() || (title.match(/FC2[-_]?PPV[-_]?(\d+)/i) ? "FC2-" + title.match(/FC2[-_]?PPV[-_]?(\d+)/i)[1] : title.split(" ")[0]);
			const cover = $el.find("img").attr("data-src") || $el.find("img").attr("src") || "";
			const id = href.split("/").filter(Boolean).pop() || number;
			if (number || href) list.push({
				id,
				number: number.startsWith("FC2") ? number : `FC2-${number}`,
				origin_title: title,
				cover_url: cover,
				release_date: $el.find(".meta, .date").text().trim() || "",
				has_cnsub: false,
				magnets_count: 0,
				new_magnets: false,
				_href: href
			});
		});
		return list;
	}
	var containerStyle = { padding: "15px" };
	var panelInfoStyle = { marginTop: "15px" };
	var toolbarStyle = { margin: "20px 0" };
	var galleryStyle = { marginTop: "10px" };
	var btnBaseStyle = {
		color: "#fff",
		padding: "6px 12px"
	};
	var filterBtnStyle = {
		...btnBaseStyle,
		backgroundColor: "#de3333",
		marginRight: "6px"
	};
	var favoriteBtnStyle = {
		...btnBaseStyle,
		backgroundColor: "#25b1dc",
		marginRight: "6px"
	};
	var hasDownBtnStyle = {
		...btnBaseStyle,
		backgroundColor: "#7bc73b",
		marginRight: "6px"
	};
	var hasWatchBtnStyle = {
		...btnBaseStyle,
		backgroundColor: "#d7a80c",
		marginRight: "6px"
	};
	var magnetSearchBtnStyle = {
		...btnBaseStyle,
		background: "linear-gradient(to right,#f58c01,#54a11d)"
	};
	function Fc2123avDetailDialog() {
		return (0, import_jsx_runtime.jsxs)("div", {
			className: "movie-detail-container",
			style: containerStyle,
			children: [
				(0, import_jsx_runtime.jsx)("div", {
					className: "movie-info-container",
					children: (0, import_jsx_runtime.jsx)("div", {
						className: "search-loading",
						children: "加载中..."
					})
				}),
				(0, import_jsx_runtime.jsx)("div", {
					className: "movie-panel-info",
					style: panelInfoStyle,
					children: (0, import_jsx_runtime.jsx)("strong", { children: "第三方资源: " })
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					style: toolbarStyle,
					children: [
						(0, import_jsx_runtime.jsx)("a", {
							id: "filterBtn",
							className: "jhs-toolbar-menu-btn",
							style: filterBtnStyle,
							children: "🚫 屏蔽"
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "favoriteBtn",
							className: "jhs-toolbar-menu-btn",
							style: favoriteBtnStyle,
							children: "⭐ 收藏"
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "hasDownBtn",
							className: "jhs-toolbar-menu-btn",
							style: hasDownBtnStyle,
							children: "📥️ 已下载"
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "hasWatchBtn",
							className: "jhs-toolbar-menu-btn",
							style: hasWatchBtnStyle,
							children: "🔍 已观看"
						}),
						(0, import_jsx_runtime.jsx)("a", {
							id: "magnetSearchBtn",
							className: "jhs-toolbar-menu-btn",
							style: magnetSearchBtnStyle,
							children: "磁力搜索"
						})
					]
				}),
				(0, import_jsx_runtime.jsx)("div", {
					id: "magnets-content",
					className: "magnet-links"
				}),
				(0, import_jsx_runtime.jsxs)("div", {
					className: "movie-gallery",
					style: galleryStyle,
					children: [(0, import_jsx_runtime.jsx)("strong", { children: "剧照: " }), (0, import_jsx_runtime.jsx)("div", { className: "image-list" })]
				})
			]
		});
	}
	var posterWrapStyle = { marginTop: "8px" };
	var posterImgStyle = { maxWidth: "320px" };
	var actorsStyle = { marginTop: "8px" };
	var actressTagStyle = { margin: "2px" };
	var hiddenStyle = { display: "none" };
	function Fc2123avMovieInfo({ info, carNum, actresses }) {
		return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
			(0, import_jsx_runtime.jsx)("div", {
				className: "movie-title",
				children: (0, import_jsx_runtime.jsx)("strong", { children: info.title || carNum })
			}),
			(0, import_jsx_runtime.jsxs)("div", { children: ["番号: ", info.id || carNum] }),
			(0, import_jsx_runtime.jsxs)("div", { children: ["发布日: ", info.releaseDate || ""] }),
			info.poster ? (0, import_jsx_runtime.jsx)("div", {
				style: posterWrapStyle,
				children: (0, import_jsx_runtime.jsx)("img", {
					src: info.poster,
					style: posterImgStyle
				})
			}) : "",
			(0, import_jsx_runtime.jsxs)("div", {
				className: "movie-actors",
				style: actorsStyle,
				children: [(0, import_jsx_runtime.jsx)("strong", { children: "主演: " }), actresses.length > 0 ? actresses.map((actress) => (0, import_jsx_runtime.jsx)("span", {
					className: "tag",
					style: actressTagStyle,
					children: actress
				})) : "未知"]
			}),
			(0, import_jsx_runtime.jsx)("div", {
				id: "data-releaseDate",
				style: hiddenStyle,
				children: info.releaseDate || ""
			})
		] });
	}
	var HAS_DOWN_STATUS = "hasDown";
	async function open123AvFc2Dialog(plugin, carNum, href) {
		const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(Fc2123avDetailDialog, {}));
		layer.open({
			type: 1,
			title: carNum,
			content: dialogHtml,
			area: utils.getResponsiveArea(["80%", "90%"]),
			success: async () => {
				await loadData(plugin, carNum, href);
				handleLongImg(plugin, carNum.replace(/^FC2-/, ""));
				$("#filterBtn").on("click", () => saveAction(plugin, carNum, href, FILTER_ACTION));
				$("#favoriteBtn").on("click", () => saveAction(plugin, carNum, href, FAVORITE_ACTION));
				$("#hasDownBtn").on("click", () => saveAction(plugin, carNum, href, HAS_DOWN_STATUS));
				$("#hasWatchBtn").on("click", () => saveAction(plugin, carNum, href, HAS_WATCH_ACTION));
				$("#magnetSearchBtn").on("click", () => {
					const magnetHub = plugin.getBean("MagnetHubPlugin");
					if (magnetHub) $("#magnets-content").empty().append(magnetHub.createMagnetHub(carNum));
				});
			}
		});
	}
	async function saveAction(_plugin, carNum, url, actionType) {
		await storageManager.saveCar({
			carNum,
			url,
			names: void 0,
			actionType,
			publishTime: $("#data-releaseDate").text() || ""
		});
		show.ok("操作成功");
		refresh();
	}
	async function loadData(plugin, carNum, href) {
		try {
			const info = await get123AvVideoInfo(plugin, href);
			const fc2Num = carNum.replace(/^FC2-?(PPV-?)?/i, "");
			const actressInfo = await getActressInfo(plugin, fc2Num).catch(() => null);
			const imgList = await getImgList(plugin, fc2Num).catch(() => []);
			const actresses = actressInfo?.actors?.length ? actressInfo.actors : [];
			const imagesHtml = imgList.map((src) => jsxToString((0, import_jsx_runtime.jsx)("a", {
				className: "tile-item",
				href: src,
				target: "_blank",
				children: (0, import_jsx_runtime.jsx)("img", {
					src,
					style: {
						maxWidth: "150px",
						margin: "4px"
					},
					loading: "lazy"
				})
			}))).join("");
			$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(Fc2123avMovieInfo, {
				info,
				carNum,
				actresses
			})));
			$(".movie-gallery .image-list").html(imagesHtml);
			plugin.getBean("TranslatePlugin")?.translate?.(carNum, false)?.then?.();
		} catch (e) {
			clog.error(e);
			$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: e instanceof Error ? e.message : String(e) })));
		}
	}
	function handleLongImg(plugin, carNum) {
		utils.loopDetector(() => $(".movie-gallery .image-list").length > 0, async () => {
			$(".movie-gallery .image-list").prepend(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "150px" })));
			const screenShot = plugin.getBean("ScreenShotPlugin");
			if (!screenShot) return;
			try {
				const screenshotUrl = await screenShot.getScreenshot(carNum);
				if (screenshotUrl) screenShot.addImg("缩略图", screenshotUrl);
			} catch {}
		});
	}
	async function get123AvVideoInfo(plugin, href) {
		const fullHref = href.startsWith("http") ? href : await plugin.getBaseUrl() + href;
		const html = await gmHttp.get(fullHref);
		const $dom = utils.htmlTo$dom(String(html));
		const title = $dom.find("h1").first().text().trim() || $dom.find(".title").first().text().trim() || "";
		const poster = $dom.find("video").attr("poster") || $dom.find(".detail-img img, .cover img").attr("src") || "";
		const releaseDate = $dom.find(".detail-item:contains(\"発売日\"), .detail-item:contains(\"发布\")").text().replace(/.*?[::]/, "").trim() || $dom.find("time").text().trim() || "";
		return {
			id: $dom.find(".detail-item:contains(\"品番\"), .detail-item:contains(\"番号\")").text().replace(/.*?[::]/, "").trim() || title.split(" ")[0] || "",
			releaseDate,
			title,
			poster
		};
	}
	async function getActressInfo(_plugin, fc2Num) {
		try {
			const html = await gmHttp.get(`https://fc2ppvdb.com/articles/${fc2Num}`);
			const $dom = utils.htmlTo$dom(String(html));
			const actors = [];
			$dom.find("a[href*=\"/actress\"], .actress a, .actors a").each((_i, el) => {
				const name = $(el).text().trim();
				if (name) actors.push(name);
			});
			return { actors };
		} catch {
			return null;
		}
	}
	async function getImgList(_plugin, fc2Num) {
		try {
			const html = await gmHttp.get(`https://adult.contents.fc2.com/article/${fc2Num}/`);
			const $dom = utils.htmlTo$dom(String(html));
			const imgs = [];
			$dom.find("img").each((_i, el) => {
				const src = $(el).attr("src") || $(el).attr("data-src") || "";
				if (src && /sample|preview|main/i.test(src)) imgs.push(src);
			});
			return imgs.slice(0, 20);
		} catch {
			return [];
		}
	}
	var Fc2By123AvPlugin = class extends BasePlugin {
		$contentBox = null;
		urlParams = new URLSearchParams(window.location.search);
		sortVal = "release_date";
		currentPage = 1;
		maxPage = null;
		keyword = null;
		_querySeq = 0;
		constructor() {
			super();
			this.sortVal = this.urlParams.get("sort") || "release_date";
			this.currentPage = parseInt(this.urlParams.get("page") || "", 10) || 1;
			this.keyword = this.urlParams.get("keyword") || null;
		}
		getName() {
			return "Fc2By123AvPlugin";
		}
		async getBaseUrl() {
			return "https://123av.com".replace(/\/$/, "") + "/ja";
		}
		async handle() {
			if (!featureFlags.fc2By123AvPlugin) return;
			this.injectNavEntry();
			if (window.location.href.includes("/advanced_search?type=100")) {
				this.$contentBox = $(".section .container");
				this.hookPage();
				await this.handleQuery();
			}
		}
		injectNavEntry() {
			const href = "/advanced_search?type=100&released_start=2099-09";
			if ($("#navbar-item-123av-fc2").length === 0) {
				const $fc2Nav = $("#navbar-menu-hero a.navbar-item").filter((_i, el) => {
					const text = $(el).text().trim();
					const link = $(el).attr("href") || "";
					return text === "FC2" || link.includes("advanced_search?type=3");
				}).first();
				if ($fc2Nav.length) $fc2Nav.after(jsxToString((0, import_jsx_runtime.jsx)("a", {
					className: "navbar-item",
					id: "navbar-item-123av-fc2",
					href,
					children: "123Av-Fc2"
				})));
				else $("#navbar-menu-hero .navbar-start").first().append(jsxToString((0, import_jsx_runtime.jsx)("a", {
					className: "navbar-item",
					id: "navbar-item-123av-fc2",
					href,
					children: "123Av-Fc2"
				})));
			}
			if ($(".tabs li:contains(\"FC2\")").length && !$(".tabs a[href*=\"advanced_search?type=100\"]").length) $(".tabs li:contains(\"FC2\")").after(jsxToString((0, import_jsx_runtime.jsx)("li", {
				id: "tab-123av-fc2",
				children: (0, import_jsx_runtime.jsx)("a", {
					href,
					children: (0, import_jsx_runtime.jsx)("span", { children: "123Av-Fc2" })
				})
			})));
		}
		hookPage() {
			hookPage(this);
		}
		renderPagination() {
			renderPagination(this);
		}
		async handleQuery() {
			await handleQuery(this);
		}
		parseCards($dom) {
			return parseCards(this, $dom);
		}
		async open123AvFc2Dialog(carNum, href) {
			await open123AvFc2Dialog(this, carNum, href);
		}
		async saveAction(carNum, url, actionType) {
			await saveAction(this, carNum, url, actionType);
		}
		async loadData(carNum, href) {
			await loadData(this, carNum, href);
		}
		handleLongImg(carNum) {
			handleLongImg(this, carNum);
		}
		async get123AvVideoInfo(href) {
			return get123AvVideoInfo(this, href);
		}
		async getActressInfo(fc2Num) {
			return getActressInfo(this, fc2Num);
		}
		async getImgList(fc2Num) {
			return getImgList(this, fc2Num);
		}
	};
	var loading_default = ".loading-container {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    background-color: rgb(0, 0, 0, 0.1);\n    z-index: var(--jhs-z-top);\n}\n\n.loading-animation {\n    position: relative;\n    width: 60px;\n    height: 12px;\n    background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);\n    border-radius: 6px;\n    animation: loading-animate 1.8s ease-in-out infinite;\n    box-shadow: 0 4px 12px rgb(0, 0, 0, 0.1);\n}\n\n.loading-animation::before,\n.loading-animation::after {\n    position: absolute;\n    display: block;\n    content: '';\n    animation: loading-animate 1.8s ease-in-out infinite;\n    height: 12px;\n    border-radius: 6px;\n    box-shadow: 0 4px 12px rgb(0, 0, 0, 0.1);\n}\n\n.loading-animation::before {\n    top: -20px;\n    left: 10px;\n    width: 40px;\n    background: linear-gradient(90deg, #ff758c 0%, #ff7eb3 100%);\n}\n\n.loading-animation::after {\n    bottom: -20px;\n    width: 35px;\n    background: linear-gradient(90deg, #ff9a9e 0%, #fad0c4 100%);\n}\n\n@keyframes loading-animate {\n    0% {\n        transform: translateX(40px);\n    }\n\n    50% {\n        transform: translateX(-30px);\n    }\n\n    100% {\n        transform: translateX(40px);\n    }\n}\n";
	var viewer_default = ".viewer-canvas {\n    overflow: auto !important;\n}\n\n.viewer-close {\n    background: rgb(255, 0, 0, 0.6) !important;\n}\n\n.viewer-close:hover {\n    background: rgb(255, 0, 0, 0.8) !important;\n}\n";
	var logger_default = ".console-logger-container {\n    position: fixed;\n    bottom: 0;\n    right: 0;\n    z-index: var(--jhs-z-top);\n    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-end;\n    width: fit-content;\n}\n\n.console-logger-toggle {\n    width: 40px;\n    height: 30px;\n    background: #2c3e50;\n    border-radius: 120px 10px 0 0;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    box-shadow: -2px 0 5px rgb(0, 0, 0, 0.1);\n    transition: background-color 0.3s ease;\n    color: white;\n    font-size: 16px;\n}\n\n.console-logger-toggle:hover {\n    background: #34495e;\n}\n\n.console-logger-toggle::after {\n    content: '▼';\n    transition: transform 0.3s ease;\n}\n\n.console-logger-toggle.collapsed::after {\n    content: '▲';\n}\n\n.console-logger-window {\n    width: 400px;\n    height: 400px;\n    background: white;\n    border-radius: 10px 0 10px 10px;\n    box-shadow: 0 5px 15px rgb(0, 0, 0, 0.1);\n    display: flex;\n    flex-direction: column;\n    overflow: hidden;\n    transform: translateY(0);\n    opacity: 1;\n\n    /* 简化过渡属性 */\n    transition:\n        width 0.3s ease,\n        height 0.3s ease,\n        opacity 0.3s ease,\n        transform 0.3s ease;\n}\n\n.console-logger-window.maximized {\n    width: 600px !important;\n    height: 85vh !important;\n    border-radius: 10px 0 0 10px; /* 调整圆角以匹配右下角 */\n}\n\n.console-logger-window.collapsed {\n    height: 0 !important;\n    min-height: 0 !important;\n    opacity: 0;\n}\n\n.console-logger-header {\n    background: #2c3e50;\n    color: white;\n    padding: 12px 15px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    flex-shrink: 0;\n}\n\n.console-logger-title {\n    font-weight: 600;\n    font-size: 16px;\n}\n\n.console-logger-controls {\n    display: flex;\n    gap: 10px;\n}\n\n.console-logger-controls button {\n    background: transparent;\n    border: 1px solid rgb(255, 255, 255, 0.3);\n    padding: 5px 10px;\n    font-size: 12px;\n    color: white;\n    border-radius: 4px;\n    cursor: pointer;\n    transition: background 0.3s;\n}\n\n.console-logger-controls button:hover {\n    background: rgb(255, 255, 255, 0.1);\n}\n\n/* 新增的按钮样式 */\n.console-logger-maximize-toggle {\n    line-height: 1;\n    font-size: 14px !important; /* 使箭头看起来更大 */\n    padding: 5px 8px !important;\n}\n\n.console-logger-maximize-toggle::before {\n    content: '⇱'; /* Unicode symbol for maximized */\n}\n\n.console-logger-maximize-toggle.active::before {\n    content: '⇲'; /* Unicode symbol for minimized */\n}\n\n.console-logger-filters {\n    display: flex;\n    align-items: center;\n    gap: 5px;\n    padding: 10px;\n    background: #f8f9fa;\n    border-bottom: 1px solid #e9ecef;\n    flex-shrink: 0;\n    overflow-x: hidden;\n}\n\n/* 新增: 过滤器按钮组的容器,负责滚动 */\n.console-logger-filter-group {\n    display: flex;\n    gap: 5px;\n    overflow-x: auto; /* 允许过滤器按钮滚动 */\n    flex-grow: 1; /* 占据剩余空间 */\n    padding-right: 10px; /* 避免滚动条影响按钮 */\n}\n\n.console-logger-filter {\n    padding: 5px 10px;\n    font-size: 12px;\n    border-radius: 15px;\n    background: #ecf0f1;\n    color: #7f8c8d;\n    border: 1px solid #ddd;\n    cursor: pointer;\n    transition: background-color 0.3s, color 0.3s, border-color 0.3s;\n    white-space: nowrap;\n    flex-shrink: 0; /* 确保不被压缩 */\n}\n\n.console-logger-filter.active {\n    background: #3498db;\n    color: white;\n    border-color: #3498db;\n}\n\n/* 新增: 滚动到底部按钮的样式 (位于 filtersContainer 内部右侧) */\n.console-logger-scroll-to-bottom {\n    background: #3498db;\n    border: none;\n    padding: 5px 10px;\n    font-size: 12px;\n    color: white;\n    border-radius: 4px;\n    cursor: pointer;\n    transition: background 0.3s;\n    line-height: 1;\n    height: fit-content;\n    white-space: nowrap;\n    margin-left: auto; /* 将按钮推到最右侧 */\n    flex-shrink: 0; /* 确保不被压缩 */\n}\n\n.console-logger-scroll-to-bottom:hover {\n    background: #2980b9;\n}\n\n.console-logger-content {\n    flex: 1;\n    overflow-y: auto;\n    padding: 10px;\n    background: #fff;\n    overflow-wrap: break-word;\n    text-align: left;\n}\n\n.console-logger-entry {\n    padding: 8px 10px;\n    margin-bottom: 3px;\n    border-radius: 4px;\n    font-size: 12px;\n    line-height: 1.4;\n\n    /* animation: consoleFadeIn 0.3s ease; */\n    border-left: 3px solid transparent;\n}\n\n@keyframes consoleFadeIn {\n    from {\n        opacity: 0;\n        transform: translateY(5px);\n    }\n\n    to {\n        opacity: 1;\n        transform: translateY(0);\n    }\n}\n\n.console-logger-timestamp {\n    color: #7f8c8d;\n    font-size: 11px;\n    margin-right: 2px;\n}\n\n@media (width <= 768px) {\n    .console-logger-container {\n        right: 10px;\n        bottom: 10px;\n    }\n\n    .console-logger-window {\n        width: calc(100vw - 20px);\n        height: 300px;\n    }\n}\n\n.console-logger-message[data-type='json'] {\n    white-space: pre-wrap;\n}\n";
	var javdb_site_default = "/* __HIDENAV2__ */\n\n.navbar {\n    z-index: 12345679 !important;\n    padding: 0 0;\n}\n\n.navbar-link:not(.is-arrowless) {\n    padding-right: 33px;\n}\n\n.sub-header,\n    /*#search-bar-container, !*搜索框*!*/\n    #footer,\n    /*.search-recent-keywords, !*搜索框底部热搜词条*!*/\n    .app-desktop-banner,\n    div[data-controller=\"movie-tab\"] .tabs,\n    h3.main-title,\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(3), /* 相关清单*/\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(2), /* 短评按钮*/\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(1), /*磁力面板 按钮*/\n    .top-meta,\n    .float-buttons {\n    display: none !important;\n}\n\ndiv.tabs.no-bottom,\n.tabs ul {\n    border-bottom: none !important;\n}\n\n/* 视频列表项 相对相对 方便标签绝对定位 */\n.movie-list .item {\n    position: relative !important;\n}\n\n.video-title {\n    white-space: normal !important;\n    height: 80px; /* 固定高度 容器就不会出现高低不一 */\n    display: -webkit-box; /* 必须设置,使接下来的属性生效 */\n    -webkit-box-orient: vertical; /* 垂直方向堆叠行 */\n    -webkit-line-clamp: 3; /* 设置文本最多显示的行数 */\n    line-clamp: 3;\n}\n\n/* 列表页顶部分类自适应 */\n.main-tabs,\n.tabs {\n    overflow-x: hidden;\n    flex-wrap: wrap;\n    justify-content: flex-start;\n}\n\n.main-tabs ul,\n.tabs ul {\n    flex-wrap: wrap;\n    flex-grow: 0;\n}\n\n/* 二级工具栏 大小封面,可播放,含磁链... */\n.toolbar {\n    display: flex;\n}\n";
	var common_toolbar_default = "/* 全局通用样式 */\n.fr-btn {\n    float: right;\n    margin-left: 4px !important;\n}\n\n.menu-box {\n    position: fixed;\n    right: 10px;\n    top: 50%;\n    transform: translateY(-50%);\n    display: flex;\n    flex-direction: column;\n    z-index: 1000;\n    gap: 6px;\n}\n\n.jhs-toolbar-menu-btn {\n    display: inline-block;\n    min-width: 80px;\n    padding: 7px 12px;\n    border-radius: 4px;\n    color: white !important;\n    text-decoration: none;\n    font-weight: bold;\n    font-size: 12px;\n    text-align: center;\n    cursor: pointer;\n    transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;\n    box-shadow: 0 2px 5px rgb(0, 0, 0, 0.1);\n    text-shadow: 0 1px 1px rgb(0, 0, 0, 0.2);\n    border: none;\n    line-height: 1.3;\n    margin: 0;\n}\n\n.jhs-toolbar-menu-btn:hover {\n    transform: translateY(-1px);\n    box-shadow: 0 3px 6px rgb(0, 0, 0, 0.15);\n    opacity: 0.9;\n}\n\n.jhs-toolbar-menu-btn:active {\n    transform: translateY(0);\n    box-shadow: 0 1px 2px rgb(0, 0, 0, 0.1);\n}\n\n.do-hide {\n    display: none !important;\n}\n\n.main-tab-btn {\n    border-bottom: none !important;\n    border-radius: 3px !important;\n    height: 30px;\n    margin-left: 5px !important;\n}\n\n.jhs-icon {\n    width: 16px;\n    height: 16px;\n}\n\n/* 表格内按钮溢出,防止被隐藏 */\n.tabulator .tabulator-row .action-cell-dropdown {\n    overflow: visible !important;\n}\n\n/* 去除行内鼠标小手 */\n.tabulator .tabulator-row.tabulator-selectable:hover {\n    cursor: default !important;\n}\n\n/* 排序小箭头颜色 */\n.tabulator .tabulator-col.tabulator-sortable[aria-sort='ascending'] .tabulator-arrow {\n    border-bottom-color: #337ab7 !important;\n}\n\n.tabulator .tabulator-col.tabulator-sortable[aria-sort='descending'] .tabulator-arrow {\n    border-top-color: #337ab7 !important;\n}\n\n/* 针对折叠行的容器或内容进行样式修改 */\n.tabulator-responsive-collapse {\n    border-top: none !important;\n}\n\n.tabulator-responsive-collapse table {\n    margin-left: 50px !important;\n}\n\n.tabulator-cell {\n    height: auto !important;\n}\n\n/* 列允许换行,去除省略号 */\n.tabulator .tabulator-cell {\n    white-space: normal !important;\n    text-overflow: clip !important;\n}\n\n.tabulator-tableholder {\n    overflow-x: hidden !important;\n}\n\n/*\n * Toastify 宽度修复:toastify-js 源 CSS 中 .toastify-center 有两行\n *   max-width: fit-content;\n *   max-width: -moz-fit-content;\n * lightningcss 压缩时删除了标准 fit-content 只留 -moz-fit-content(Firefox\n * 前缀),Chrome 不认 -moz- 前缀致 max-width 失效,toast 退回 .toastify 的\n * calc(50% - 20px) 变成超宽(~937px)。此处用 !important 强制 fit-content\n * 覆盖,恢复 toast 自适应内容宽度。详见 doc/32-css-layout-fix.md。\n */\n.toastify-center {\n    max-width: fit-content !important;\n}\n\n/* __SCROLLBAR__ */\n";
	var a_normal_buttons_default = ".a-normal, /* 白色 */\n    .a-primary, /* 浅蓝色 */\n    .a-success, /* 浅绿色 */\n    .a-danger, /* 浅粉色 */\n    .a-warning, /* 浅橙色 */\n    .a-info /* 灰色 */ {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    padding: 6px 14px;\n    margin-right: 10px;\n    border-radius: 6px;\n    text-decoration: none;\n    font-size: 13px;\n    font-weight: 500;\n    transition: background-color 0.2s ease;\n    cursor: pointer;\n    border: 1px solid rgb(0, 0, 0, 0.08);\n    white-space: nowrap;\n}\n\n.a-primary {\n    background: #e0f2fe;\n    color: #0369a1;\n    border-color: #bae6fd;\n}\n\n.a-primary:hover {\n    background: #bae6fd;\n}\n\n.a-success {\n    background: #dcfce7;\n    color: #166534;\n    border-color: #bbf7d0;\n}\n\n.a-success:hover {\n    background: #bbf7d0;\n}\n\n.a-danger {\n    background: #fee2e2;\n    color: #b91c1c;\n    border-color: #fecaca;\n}\n\n.a-danger:hover {\n    background: #fecaca;\n}\n\n.a-warning {\n    background: #ffedd5;\n    color: #9a3412;\n    border-color: #fed7aa;\n}\n\n.a-warning:hover {\n    background: #fed7aa;\n}\n\n.a-info {\n    background: #e2e8f0;\n    color: #334155;\n    border-color: #cbd5e1;\n}\n\n.a-info:hover {\n    background: #cbd5e1;\n}\n\n.a-normal {\n    background: transparent;\n    color: #64748b;\n    border-color: #cbd5e1;\n}\n\n.a-normal:hover {\n    background: #f8fafc;\n}\n";
	var accessibility_default = "/* 无障碍基础样式(doc/145 批次 5 基础) */\n\n/* focus-visible:键盘导航时显示焦点环,鼠标点击时不显示 */\n:focus-visible {\n    outline: 2px solid #5d87c2;\n    outline-offset: 2px;\n}\n\n/* 交互元素的焦点样式 */\nbutton:focus-visible,\na:focus-visible,\ninput:focus-visible,\nselect:focus-visible,\ntextarea:focus-visible,\n[tabindex]:focus-visible {\n    outline: 2px solid #5d87c2;\n    outline-offset: 2px;\n}\n\n/* 减少动态效果:尊重用户系统偏好 */\n@media (prefers-reduced-motion: reduce) {\n    *,\n    *::before,\n    *::after {\n        animation-duration: 0.01ms !important;\n        animation-iteration-count: 1 !important;\n        transition-duration: 0.01ms !important;\n        scroll-behavior: auto !important;\n    }\n}\n";
	var design_tokens_default = "/* JavDB Power Tools 设计令牌(doc/147 批次 5 基础) */\n\n/* 作用域:.jhs-ui 或全局 :root,供所有插件 CSS 引用 */\n\n:root {\n    /* 主色 */\n    --jhs-color-primary: #5d87c2;\n    --jhs-color-primary-hover: #4a73b0;\n    --jhs-color-primary-light: #e8eef6;\n\n    /* 语义色 */\n    --jhs-color-success: #64bb69;\n    --jhs-color-warning: #d7a80c;\n    --jhs-color-error: #de3333;\n    --jhs-color-info: #25b1dc;\n\n    /* 中性色 */\n    --jhs-color-text: #363636;\n    --jhs-color-text-secondary: #6c757d;\n    --jhs-color-text-muted: #999;\n    --jhs-color-border: #e2e8f0;\n    --jhs-color-bg: #fff;\n    --jhs-color-bg-secondary: #fbfcfd;\n    --jhs-color-bg-hover: #f5f5f5;\n\n    /* 间距 */\n    --jhs-space-xs: 4px;\n    --jhs-space-sm: 8px;\n    --jhs-space-md: 12px;\n    --jhs-space-lg: 16px;\n    --jhs-space-xl: 20px;\n    --jhs-space-2xl: 24px;\n\n    /* 圆角 */\n    --jhs-radius-sm: 4px;\n    --jhs-radius-md: 6px;\n    --jhs-radius-lg: 8px;\n    --jhs-radius-full: 9999px;\n\n    /* 字体 */\n    --jhs-font-size-xs: 11px;\n    --jhs-font-size-sm: 12px;\n    --jhs-font-size-md: 13px;\n    --jhs-font-size-lg: 14px;\n    --jhs-font-size-xl: 16px;\n\n    /* 阴影 */\n    --jhs-shadow-sm: 0 1px 3px rgb(0, 0, 0, 0.08);\n    --jhs-shadow-md: 0 2px 8px rgb(0, 0, 0, 0.12);\n    --jhs-shadow-lg: 0 4px 16px rgb(0, 0, 0, 0.16);\n\n    /* z-index 层级(layer.js z-index 无上限递增,above-layer 层需 genuinely large 常量) */\n    --jhs-z-dropdown: 100;\n    --jhs-z-sticky: 200;\n    --jhs-z-page: 9999;\n    --jhs-z-top: 999999999;\n\n    /* 过渡 */\n    --jhs-transition-fast: 150ms ease;\n    --jhs-transition-normal: 250ms ease;\n\n    /* 触控目标最小尺寸 */\n    --jhs-touch-target: 44px;\n}\n";
	var filter_chip_base_default = "/**\n * 筛选栏 + 芯片共享基础样式 —— status-tag-filter / video-lists-tag / preload-status-badge\n * 三个插件的筛选栏布局与芯片基础样式逐字节相同,此处以分组选择器统一,\n * 各插件 CSS 仅保留 active 配色与 no-tag 变体。\n *\n * 注意:missav-status-filter 不在此共享基础内——missav 是 Tailwind nord 暗色主题,\n * 浅色基础样式与其风格冲突(doc/223),由 missav-status-filter.css 独立定义暗色样式。\n *\n * 注入方式:main.tsx 顶层 injectCss(始终注入,与 design-tokens.css 同级)。\n */\n\n/* ---------- 筛选栏容器 ---------- */\n.status-tag-filter-bar,\n.jhs-vlt-filter-bar,\n.preload-filter-bar {\n    display: flex;\n    align-items: flex-start;\n    gap: 8px;\n    padding: 8px 0 12px 0;\n    border-bottom: 1px solid #e9ecef;\n}\n\n/* ---------- 筛选栏标签 ---------- */\n.status-tag-filter-label,\n.jhs-vlt-filter-label,\n.preload-filter-label {\n    flex-shrink: 0;\n    font-size: 0.8em;\n    font-weight: 600;\n    color: #6c757d;\n    line-height: 1.8;\n    margin-right: 4px;\n}\n\n/* ---------- 芯片容器 ---------- */\n.status-tag-filter-chips,\n.jhs-vlt-filter-chips,\n.preload-filter-chips {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n}\n\n/* ---------- 芯片基础 ---------- */\n.status-tag-filter-chip,\n.jhs-vlt-filter-chip,\n.preload-filter-chip {\n    display: inline-block;\n    padding: 0.25em 0.7em;\n    font-size: 0.75em;\n    font-weight: 600;\n    line-height: 1.5;\n    border-radius: 50rem;\n    cursor: pointer;\n    user-select: none;\n    border: 1.5px solid #dee2e6;\n    background: #fff;\n    color: #495057;\n    transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;\n}\n\n/* ---------- 芯片悬停 ---------- */\n.status-tag-filter-chip:hover,\n.jhs-vlt-filter-chip:hover,\n.preload-filter-chip:hover {\n    border-color: #adb5bd;\n    background: #f8f9fa;\n}";
	var javdbHideNavCss = "";
	if (window.location.href.includes("hideNav=1")) javdbHideNavCss = "\n        .main-nav,#search-bar-container {\n            display: none !important;\n        }\n        \n        html {\n            padding-top:0px!important;\n        }\n    ";
	var javdbSiteCss = javdb_site_default.replace("/*__HIDENAV2__*/", javdbHideNavCss);
	function generateScrollbarCss() {
		const scrollbarSelectors = [
			".jhs-scrollbar",
			".content-panel",
			".tabulator-tableholder",
			".has-navbar-fixed-top",
			".layui-layer-content"
		];
		const appendPseudo = (selectors, pseudo) => selectors.map((selector) => `${selector}${pseudo}`).join(",");
		return `\n    ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar")}{width:6px;height:6px;}\n    ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-track")}{background:#f1f1f1;border-radius:10px;}\n    ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-thumb")}{background:#888;border-radius:10px;}\n    ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-thumb:hover")}{background:#555;}\n    `.trim().replace(/\n/g, "");
	}
	var commonToolbarCss = common_toolbar_default.replace("/*__SCROLLBAR__*/", generateScrollbarCss());
	if (isJavdbSite) injectCss(javdbSiteCss);
	injectCss(a_normal_buttons_default);
	injectCss(commonToolbarCss);
	injectCss(accessibility_default);
	injectCss(design_tokens_default);
	injectCss(filter_chip_base_default);
	window.utils = new CommonUtil();
	window.gmHttp = new GmHttp();
	window.storageManager = new StorageManager();
	window.loadGfriends = loadGfriends;
	window.filetreeDb = filetreeDb;
	window.WebDavClient = WebDavClient;
	var refreshChannel = new BroadcastChannel("channel-refresh");
	window.refresh = function() {
		refreshChannel.postMessage({ type: "refresh" });
	};
	window.cleanCache_filter_actor_actress_car_list = function() {
		refreshChannel.postMessage({ type: "cleanCache_filter_actor_actress_car_list" });
	};
	window.clean_cacheSettingObj = function() {
		refreshChannel.postMessage({ type: "clean_cacheSettingObj" });
	};
	injectCss(loading_default);
	window.loading = createLoading;
	window.show = show$1;
	(function() {
		function resetOverflow(delay = 10) {
			setTimeout(() => {
				const shadeCount = document.querySelectorAll(".layui-layer-shade").length;
				document.documentElement.style.overflow = shadeCount > 0 ? "hidden" : "";
			}, delay);
		}
		injectCss(viewer_default);
		window.showImageViewer = function(src, alt = "") {
			let container;
			let isTemporary = false;
			if (typeof src == "string" || src instanceof String) {
				container = $(jsxToString((0, import_jsx_runtime.jsx)(TemporaryImageContainer, {
					src: String(src),
					alt
				}))).appendTo("body");
				isTemporary = true;
			} else container = $(src);
			const viewerRef = { current: null };
			const viewerOptions = VIEWER_CONFIG({
				container,
				isTemporary,
				resetOverflow,
				viewerRef
			});
			viewerRef.current = new Viewer(container[0], viewerOptions);
			viewerRef.current.show();
		};
	})();
	(async function() {
		injectCss(logger_default);
		try {
			if (unsafeWindow.parent.clog && typeof unsafeWindow.parent.clog.log == "function") window.clog = unsafeWindow.parent.clog;
			else window.clog = new Logger();
		} catch (err) {
			console.error("创建日志控制台出现异常", err);
			window.clog = new Logger();
		}
		(function() {
			const logger = window.clog || console;
			window.addEventListener("error", function(event) {
				const filename = event.filename;
				const message = event.message;
				if (!filename.includes("javdb")) logger.error(`[全局 Error 异常捕获] ${message} 来源: ${filename}`);
			});
			window.addEventListener("unhandledrejection", function(event) {
				const reason = event.reason;
				const reasonMessage = (reason == null ? void 0 : reason.message) ?? "";
				if (reasonMessage.includes("play()")) return;
				if (reasonMessage.includes("The element has no supported sources")) {
					show$1.error("播放失败, 请检查是否已对节点分流?");
					logger.error("播放失败, 请检查是否已对节点分流?");
					return;
				}
				if (reasonMessage.includes("<span>1005</span>") && reasonMessage.includes("fc2ppvdb")) return;
				const logMessage = `[全局 Promise 异常捕获] ${reason.message || reason}`;
				logger.error(logMessage, reason);
				event.preventDefault();
			});
		})();
		document.addEventListener("mousedown", (event) => {
			const clog = window.clog;
			if (!clog.isInitialized || !clog.container) return;
			const target = event.target;
			const loggerSelectors = [
				".console-logger-container",
				".layui-layer-shade",
				".loading-container"
			].join(",");
			if (target.closest(loggerSelectors)) clog.highZIndex();
			else clog.lowZIndex();
		});
	})();
	setupTooltip();
	window.encryptCredential = encryptCredential;
	window.decryptCredential = decryptCredential;
	setupLayerWrapper();
	var pluginManager = (function() {
		const manager = new PluginManager();
		if (isJavdbSite) {
			manager.register(ListPagePlugin);
			manager.register(AutoPagePlugin);
			manager.register(FoldCategoryPlugin);
			manager.register(ListPageButtonPlugin);
			manager.register(HistoryPlugin);
			manager.register(SettingPlugin);
			manager.register(NavBarPlugin);
			manager.register(HitShowPlugin);
			manager.register(Top250Plugin);
			manager.register(DetailPagePlugin);
			manager.register(ReviewPlugin);
			manager.register(DetailPageButtonPlugin);
			manager.register(HighlightMagnetPlugin);
			manager.register(PreviewVideoPlugin);
			manager.register(ActressInfoPlugin);
			manager.register(OtherSitePlugin);
			manager.register(WantAndWatchedVideosPlugin);
			manager.register(RelatedPlugin);
			manager.register(BlacklistPlugin);
			manager.register(FavoriteActressesPlugin);
			manager.register(NewVideoPlugin);
			manager.register(RatingDisplayPlugin);
			manager.register(Fc2Plugin);
			manager.register(KeyPageTurningPlugin);
			manager.register(ModMyListOpenWayPlugin);
			manager.register(PageSortPlugin);
			manager.register(StatusTagFilterPlugin);
			manager.register(ListWaterfallPlugin);
			manager.register(ListReadingStatusPlugin);
			manager.register(ModalListDisablerPlugin);
			manager.register(VideoListsTagPlugin);
			manager.register(CarListReaderPlugin);
			manager.register(VisitHistoryPlugin);
			manager.register(ListReflowPlugin);
			if (featureFlags.translatePlugin) manager.register(TranslatePlugin, "translatePlugin");
			if (featureFlags.screenShotPlugin) manager.register(ScreenShotPlugin, "screenShotPlugin");
			if (featureFlags.magnetHubPlugin) manager.register(MagnetHubPlugin, "magnetHubPlugin");
			if (featureFlags.imageRecognitionPlugin) manager.register(ImageRecognitionPlugin, "imageRecognitionPlugin");
			if (featureFlags.fc2By123AvPlugin) manager.register(Fc2By123AvPlugin, "fc2By123AvPlugin");
		}
		if (isMissavSite) {
			manager.register(MissavStatusTagPlugin);
			manager.register(MissavQuickCopyPlugin);
			manager.register(MissavAutoPagePlugin);
			manager.register(MissavStatusFilterPlugin);
			manager.register(MissavVideoPreviewPlugin);
		}
		return manager;
	})();
	(async function() {
		const ctx = initPageContext();
		window.isDetailPage = ctx.isDetailPage;
		window.isListPage = ctx.isListPage;
		window.isFc2Page = ctx.isFc2Page;
		await pluginManager.processCss();
		if (isJavdbSite) {
			await storageManager.runMigrations();
			try {
				await persistentDataStore.initialize();
				await migratePersistentData(persistentDataStore);
			} catch (error) {
				let migrationPhase;
				try {
					migrationPhase = (await persistentDataStore.getMeta("persistent-data-v1"))?.phase;
				} catch {
					migrationPhase = void 0;
				}
				if (migrationPhase === "verified" || migrationPhase === "cleanup-pending" || migrationPhase === "complete") persistentDataStore.activate();
				else persistentDataStore.deactivate();
				window.clog.error("大型持久数据迁移失败,将保留可用的持久数据路径", error);
			}
		}
		storageRevision.init();
		storageRevision.onRemoteChange(() => {
			storageManager.clearCarListCache();
			storageManager.clearSettingCache();
			storageManager.clearFilterActorActressCarListCache();
		});
		if (isJavdbSite && /(^|;)\s*locale\s*=\s*en\s*($|;)/i.test(document.cookie)) show$1.error("请切换到中文语言下才可正常使用本脚本", { duration: -1 });
		await pluginManager.processPlugins();
		if (isJavdbSite) {
			const settingPlugin = pluginManager.getBean("SettingPlugin");
			if (settingPlugin) settingPlugin.autoBackup().catch((err) => window.clog.error("自动备份失败", err));
		}
		registerDiagnosticsMenu(pluginManager);
		window.addEventListener("beforeunload", () => pluginManager.destroyAll());
	})();
})($, layer, Tabulator, Toastify, localforage, Viewer, md5);