dzmm-helper

自用辅助脚本,只适用于电脑浏览器

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

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

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

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

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

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

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

Advertisement:

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

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

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

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

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

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

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

Advertisement:

// ==UserScript==
// @name         dzmm-helper
// @namespace    oninashi
// @version      1.5.4
// @author       oninashi
// @description  自用辅助脚本,只适用于电脑浏览器
// @license      MIT
// @icon         https://www.dzmm.ai/favicon.ico
// @match        https://www.dzmm.ai/*
// @match        https://www.dzmm.io/*
// @match        https://www.duskpine.top/*
// @match        https://www.aiaptx.com/*
// @match        https://www.fendal.top/*
// @match        https://www.aikda.com/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// ==/UserScript==

(function() {
  "use strict";

  const d = new Set(); const importCSS = async (e) => { d.has(e) || (d.add(e), ((t) => { typeof GM_addStyle == "function" ? GM_addStyle(t) : (document.head || document.documentElement).appendChild(document.createElement("style")).append(t); })(e)); };

  var _GM_getValue = (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  var _GM_openInTab = (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)();
  var _GM_setValue = (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  var _GM_xmlhttpRequest = (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  const lsObj = {
    setItem: (key, value) => {
      localStorage.setItem(key, JSON.stringify(value));
    },
    getItem: (key, def = "") => {
      const item = localStorage.getItem(key);
      if (item) {
        return JSON.parse(item);
      }
      return def;
    },
  };
  const _locHref = () => location.href;
  const $n = (selector) => {
    return document.querySelector(selector);
  };
  const $na = (selector) => {
    return document.querySelectorAll(selector);
  };
  const gm_config_def = {
    isDef: true,
    every_config: {
      baseURL: "http://127.0.0.1:8083",
      name: "name",
      password: "password",
      switchOn: false,
    },
  };
  const gob = {
    $body: document.body,
    gm_config: null,
    pageObserver: null,
    staticTimestamp: Math.floor(Date.now() / 1e3),
    get latestTimestamp() {
      return Math.floor(Date.now() / 1e3);
    },
    get locHref() {
      return _locHref();
    },
    get bolWidthChanged() {
      return $n("div.width-adjusted") !== null;
    },
    get $$header() {
      return $na("header");
    },
    init() {
      this.gm_config = _GM_getValue("config", gm_config_def);
      if (this.gm_config.isDef) {
        _GM_setValue("config", Object.assign({}, gm_config_def, { isDef: false }));
      }
    },
    _checkImgUrl: (url) => url.startsWith("/api/draw/image/"),
    _getUUID: (url) => {
      const parts = decodeURIComponent(url).split("/");
      return parts[parts.length - 1].split("?")[0];
    },
    _getImgUrl: (url) => {
      const uuid = gob._getUUID(url);
      const rslUrl = `${location.origin}/draw/${uuid}`;
      return rslUrl;
    },
    _markImg: ($img, classNames, delay = 1e3) => {
      setTimeout(() => {
        $img.classList.add(...classNames, "dzmm-image");
        if ($img.classList.contains("opacity-0")) {
          gob._markImg($img, classNames, delay);
          return;
        }
      }, delay);
    },
    _getImages: () => {
      const $$imgs = $na("div.group img");
      const $$Images = [];
      $$imgs.forEach(($img) => {
        const src = $img.getAttribute("src");
        if (src && gob._checkImgUrl(src)) {
          $img.dataset.uuid = gob._getUUID(src);
          $$Images.push($img);
        }
      });
      return $$Images;
    },
    _getMultiToolBar() {
      let $button = Array.from($na("button")).find(($btn) => $btn.textContent === "退出选择");
      if ($button) {
        return $button.parentNode;
      }
      const $div = $n("div.border-b");
      if (!$div) {
        return null;
      }
      $button = Array.from($div.querySelectorAll("button")).find(($btn) => $btn.textContent === "取消");
      if ($button) {
        return $div;
      }
      return null;
    },
    _isHistoryPage() {
      return this.locHref.includes("draw/generate/history");
    },
    _isHistoryGallery() {
      return this.locHref.includes("draw/generate/history?sub=gallery");
    },
    _isGeneratePage() {
      return this.locHref.endsWith("draw/generate/create");
    },
    _isDrawPage() {
      return /\/draw\/[a-f0-9-]{36}$/.test(this.locHref);
    },
    _isCharEdt() {
      return this.locHref.includes("/studio/character-creation");
    },
    _cleanClickEvents($el) {
      $el.onclick = (e) => {
        e.stopPropagation();
      };
      $el.oncontextmenu = (e) => {
        e.stopPropagation();
      };
    },
    _delayRun(fn, delay = 1e3) {
      setTimeout(() => {
        fn();
      }, delay);
    },
    disablePageObserve() {
      if (this.pageObserver) {
        this.pageObserver.disconnect();
        this.pageObserver = null;
      }
    },
    enablePageObserve(callback) {
      if (this.pageObserver) {
        return;
      }
      this.pageObserver = new MutationObserver((mr, mo) => callback(mr, mo));
      this.pageObserver.observe(this.$body, {
        childList: true,
        subtree: true,
      });
    },
  };
  gob.init();
  const LS_PUBLIC = "dzmm_listPublic";
  const storePublic = {
    galleries: {},
    load() {
      const galleries = lsObj.getItem(LS_PUBLIC, {});
      if (Object.keys(galleries).length > 0) {
        this.galleries = galleries;
      }
    },
    save() {
      lsObj.setItem(LS_PUBLIC, this.galleries);
    },
    parseGalleries(res) {
      const data = JSON.parse(res);
      if (data.result?.data?.json) {
        const galleries = data.result.data.json;
        if (Array.isArray(galleries)) {
          galleries.forEach((gallery) => {
            const { id, title, images } = gallery;
            this.galleries[id] = { id, title, images };
          });
          this.save();
        }
      }
    },
    addGallery(gallery) {
      this.galleries[gallery.id] = { id: gallery.id, title: gallery.title, images: gallery.images };
      this.save();
    },
    isPublic($img) {
      const uuid = $img.getAttribute("data-uuid");
      if (!uuid) return;
      Object.values(this.galleries).forEach((gallery) => {
        gallery.images.forEach((image) => {
          if (image.endsWith(uuid)) {
            gob._markImg($img, ["public"]);
          }
        });
      });
    },
  };
  storePublic.load();
  const gmWindow = _unsafeWindow ?? window;
  const reFetchHandler = (requestUrl, response) => {
    const interceptConfigs = [
      {
        pattern: /\/api\/trpc\/user.getGalleries\?input=/,
        handler: async (text) => {
          storePublic.parseGalleries(text);
        },
      },
    ];
    interceptConfigs.forEach(async (config) => {
      if (config.pattern.test(requestUrl)) {
        try {
          const text = await response.clone().text();
          await config.handler(text);
        }
        catch (err) {
          console.error("拦截处理出错,回退原始 response:", err);
        }
      }
    });
  };
  const originalFetch = gmWindow.fetch.bind(gmWindow);
  const reFetch = async (input, init) => {
    const response = await originalFetch(input, init);
    const requestUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
    reFetchHandler(requestUrl, response);
    return response;
  };
  gmWindow.fetch = reFetch;
  const styleCss = "div.group:has(.dzmm-image){--orange: #f97316;--green: #10b981;border-width:2px}div.group:has(.submitted){border-top-color:var(--green)}div.group:has(.cur-downloaded){border-right-color:var(--green)}div.group:has(.public){border-bottom-color:var(--green)}div.group:has(.downloaded){border-left-color:var(--green)}div.group:has(.un-downloaded){border-left-color:hsl(var(--primary));border-top-color:hsl(var(--primary));border-right-color:hsl(var(--primary))}div.group:has(.dzmm-art):hover .dzmm-art{flex-wrap:wrap!important}div.group:has(.dzmm-art):hover .dzmm-art>span{min-width:48%}.dzmm-art-input.short{width:6.3rem}.z-55{z-index:55}.z-60{z-index:60}.z-65{z-index:65}.z-75{z-index:75}.dzmm-drop-panel{position:fixed;top:0;right:0;bottom:0;height:100vh;width:25vw;background:color-mix(in srgb,hsl(var(--card)) 85%,#000 15%);border-left:1px solid hsl(var(--border));box-shadow:-12px 0 24px #00000040;display:flex;flex-direction:column;pointer-events:auto;transition:all .22s ease}.dzmm-drop-panel-zone{flex:1;border-bottom:1px dashed hsl(var(--border));display:flex;flex-direction:column;justify-content:center;align-items:center;gap:6px}.dzmm-drop-panel-zone h4{margin:0;font-size:16px;line-height:1.2}.dzmm-drop-panel-zone p{margin:0;font-size:13px;opacity:.8}.dzmm-drop-panel.mini{bottom:auto;width:0;height:0}.dzmm-drop-panel.mini .dzmm-drop-panel-zone{display:none}";
  importCSS(styleCss);
  const fnAddElementToNav = (newElement) => {
    const $nav = $n("nav.h-full div.hidden");
    if (!$n(".dzmm-nav-divider")) {
      const $span = document.createElement("span");
      $span.classList.add("px-1", "py-2", "text-muted-foreground");
      $span.classList.add("dzmm-nav-divider");
      $span.textContent = "|";
      $nav?.insertAdjacentElement("beforeend", $span);
    }
    newElement.classList.add("px-3", "py-2", "text-muted-foreground", "hover:text-foreground");
    if ($nav) {
      $nav?.insertAdjacentElement("beforeend", newElement);
      $nav.classList.add("dzmm-nav-added");
    }
  };
  const fnAddGalleryLink = () => {
    const clsName = "dzmm-gallery-link";
    if ($n(`.${clsName}`)) return;
    const $画廊链接 = document.createElement("a");
    $画廊链接.classList.add(clsName);
    $画廊链接.href = "/draw";
    $画廊链接.textContent = "画廊";
    fnAddElementToNav($画廊链接);
  };
  const fnAddGenerateLink = (target = "nav") => {
    const clsName = "dzmm-generate-link" + target;
    if ($n(`.${clsName}`)) return;
    const $创作链接 = document.createElement("a");
    $创作链接.classList.add(clsName);
    $创作链接.href = "/draw/generate/create";
    $创作链接.textContent = "创作";
    fnAddElementToNav($创作链接);
  };
  const fnAddHistoryLink = () => {
    const clsName = "dzmm-history-link";
    if ($n(`.${clsName}`)) return;
    const $历史链接 = document.createElement("a");
    $历史链接.classList.add(clsName);
    $历史链接.href = "/draw/generate/history";
    $历史链接.textContent = "我的作品";
    fnAddElementToNav($历史链接);
  };
  const fnReplaceButtonWithLink = ($container, btnText, href) => {
    if (!$container) return;
    const done = $container.dataset.done === "true";
    if (done) return;
    $container.dataset.done = "true";
    const $btn = Array.from($container.querySelectorAll("button")).find((btn) => btn.textContent === btnText);
    if (!$btn) return;
    const $link = document.createElement("a");
    $link.href = href;
    $link.className = $btn.className;
    $link.innerHTML = $btn.innerHTML;
    $link.target = "_blank";
    $btn.replaceWith($link);
  };
  const fnConvertButtonToLink = () => {
    let $container = $n("div.relative.items-center.overflow-x-auto");
    fnReplaceButtonWithLink($container, "画廊", "/draw");
    $container = $n(".mb-4 .max-w-full");
    fnReplaceButtonWithLink($container, "画廊", "/draw/generate/history?sub=gallery");
  };
  const LS_ART = "dzmm_listArt";
  const storeArt = {
    arts: [],
    load() {
      const gmArts = _GM_getValue(LS_ART, []);
      const lsArts = lsObj.getItem(LS_ART, []);
      const arts = [...new Set([...gmArts, ...lsArts])];
      if (arts.length > 0) {
        this.arts = arts;
      }
    },
    save() {
      lsObj.setItem(LS_ART, this.arts);
      _GM_setValue(LS_ART, this.arts);
    },
    add(artName) {
      if (!this.arts.includes(artName)) {
        this.arts.push(artName);
        this.save();
      }
    },
    remove(artName) {
      const idx = this.arts.indexOf(artName);
      if (idx !== -1) {
        this.arts.splice(idx, 1);
        this.save();
      }
    },
    artsCheck(text, cb) {
      const lowerText = text.toLowerCase();
      const found = this.arts.filter((artName) => lowerText.includes(artName.toLowerCase()));
      cb(found);
    },
  };
  storeArt.load();
  class HttpRequest {
    constructor() {
    }

    get(url, headers = {}) {
      return this.request({
        method: "GET",
        url,
        headers,
      });
    }

    post(url, data = {}, headers = {}) {
      const formData = new FormData();
      for (const key in data) {
        formData.append(key, data[key]);
      }
      return this.request({
        method: "POST",
        url,
        data: formData,
        headers,
      });
    }

    request(options) {
      return new Promise((resolve, reject) => {
        const requestOptions = Object.assign({}, options);
        requestOptions.onload = function(res) {
          resolve(res);
        };
        requestOptions.onerror = function(error) {
          reject(error);
        };
        _GM_xmlhttpRequest(requestOptions);
      });
    }
  }
  const http = new HttpRequest();
  const DOWNLOAD_URL_TEMPLATE = "{baseURL}/?search={uuid}&json=1&path_column=1";
  const LS_DL = "dzmm_listDownload";
  const storeDownload = {
    dls: [],
    get switchOn() {
      return !!gob.gm_config?.every_config?.switchOn;
    },
    load() {
      const lsDls = lsObj.getItem(LS_DL, []);
      if (lsDls.length > 0) {
        this.dls = lsDls;
      }
    },
    lsCacheCheck(dlInfo) {
      const lsTime = dlInfo.lsTime;
      const now = gob.staticTimestamp;
      const isValid = now - lsTime < 3600 * 4;
      return isValid;
    },
    lsWatchCheck(dlInfo) {
      const now = gob.staticTimestamp;
      if (dlInfo && dlInfo.watchUntil && now < dlInfo.watchUntil) {
        return true;
      }
      return false;
    },
    lsWatchSet(uuid, watchDuration = 180) {
      const dlInfo = this.lsGet(uuid);
      if (dlInfo) {
        const now = gob.staticTimestamp;
        dlInfo.watchUntil = now + watchDuration;
        this.lsSet(dlInfo);
      }
    },
    lsDLCheck(uuid) {
      const dlInfo = this.lsGet(uuid);
      if (dlInfo) {
        return dlInfo.downloaded;
      }
      return false;
    },
    lsGet(uuid) {
      const dlInfo = this.dls.find((dl) => dl.uuid === uuid) || null;
      if (dlInfo && !this.lsCacheCheck(dlInfo)) {
        return null;
      }
      return dlInfo;
    },
    lsSet(dlInfo) {
      const existingIndex = this.dls.findIndex((dl) => dl.uuid === dlInfo.uuid);
      if (existingIndex >= 0) {
        this.dls[existingIndex] = dlInfo;
      }
      else {
        this.dls.push(dlInfo);
      }
      lsObj.setItem(LS_DL, this.dls);
    },
    lsDel(uuid) {
      this.dls = this.dls.filter((dl) => dl.uuid !== uuid);
      lsObj.setItem(LS_DL, this.dls);
    },
    isDL($img, forceCheck = false) {
      const uuid = $img.dataset.uuid;
      const dlInfo = this.lsGet(uuid);
      if (dlInfo === null || this.lsWatchCheck(dlInfo) || forceCheck) {
        $img.classList.remove("downloaded", "un-downloaded");
        const payload = {
          uuid,
          lsTime: gob.staticTimestamp,
          downloaded: false,
        };
        if (this.lsWatchCheck(dlInfo)) {
          payload.watchUntil = dlInfo.watchUntil;
        }
        if (storeDownload.switchOn) {
          storeDownload.checkDownload(uuid).then((data) => {
            if (data.totalResults > 0) {
              payload.downloaded = true;
            }
          }).finally(() => {
            storeDownload.lsSet(payload);
            gob._markImg($img, payload.downloaded ? ["downloaded"] : ["un-downloaded"]);
          });
        }
      }
      else {
        gob._markImg($img, dlInfo.downloaded ? ["downloaded"] : ["un-downloaded"]);
      }
    },
    getDefaultResult() {
      return {
        totalResults: 0,
        results: [],
      };
    },
    getQueryUrl(uuid) {
      const baseURL = gob.gm_config?.every_config?.baseURL?.trim();
      if (!baseURL) {
        return "";
      }
      return DOWNLOAD_URL_TEMPLATE.replace("{baseURL}", baseURL).replace("{uuid}", encodeURIComponent(uuid));
    },
    getHeaders() {
      const name = gob.gm_config?.every_config?.name?.trim() || "";
      const password = gob.gm_config?.every_config?.password?.trim() || "";
      if (!name && !password) {
        return {};
      }
      return {
        Authorization: "Basic " + btoa(`${name}:${password}`),
      };
    },
    async checkDownload(uuid) {
      const tplRlt = this.getDefaultResult();
      const url = this.getQueryUrl(uuid);
      if (!url) {
        console.error("everything API 查询失败,未配置 baseURL");
        return tplRlt;
      }
      try {
        const res = await http.get(url, this.getHeaders());
        if (res.status === 200) {
          const data = JSON.parse(res.responseText);
          return data || tplRlt;
        }
        console.error("everything API 查询失败,状态码", res.status);
      }
      catch (error) {
        console.error("查询 everything API 失败", error);
      }
      return tplRlt;
    },
  };
  storeDownload.load();
  const insertPreviewContainer = (clsName) => {
    let $fixedDiv = $n(`.${clsName}`);
    if (!$fixedDiv) {
      $fixedDiv = document.createElement("div");
      $fixedDiv.className = `rounded-lg bg-card fixed bottom-1 z-75 p-1.5 ${clsName} hidden`;
      $fixedDiv.innerHTML = "<div class=\"image-preview h-full\"></div>";
      document.body.appendChild($fixedDiv);
    }
    $fixedDiv.style.maxWidth = "40vw";
    return $fixedDiv;
  };
  const fnShowImgIndex = ($img, act = "show") => {
    if (!$img) {
      return;
    }
    const i = parseInt($img.dataset.index);
    const recordIndex = $img.dataset.recordIndex;
    const clsName = `dzmm-img-index-${i}`;
    const $imgIndex = $n(`.${clsName}`);
    if (act === "show") {
      if ($imgIndex) return;
      const $span = document.createElement("span");
      $span.className = "absolute bottom-1 right-1 text-xs bg-black/50 rounded-full px-1 py-1 transition-opacity";
      $span.classList.add(clsName);
      $span.textContent = String(i + 1) + (recordIndex ? ` (${recordIndex})` : "");
      $img.insertAdjacentElement("afterend", $span);
    }
    else {
      if ($imgIndex) {
        $imgIndex.classList.add("opacity-0");
        setTimeout(() => {
          $imgIndex.remove();
        }, 500);
      }
    }
  };
  const fnFindBadge = ($el) => {
    const $elParent = $el.parentElement;
    if (!$elParent) return null;
    const $greenBg = $elParent.querySelector(".text-green-700");
    if ($greenBg) {
      return $greenBg;
    }
    else {
      return fnFindBadge($elParent);
    }
  };
  const fnBadgeHandle = ($img) => {
    const $greenBg = fnFindBadge($img);
    if ($greenBg) {
      $greenBg.classList.add("dark:text-orange-400", "text-orange-700");
      $greenBg.classList.remove("dark:text-green-400", "text-green-700");
      gob._cleanClickEvents($greenBg);
    }
    const detUrl = gob._getImgUrl($img.src);
    if ($greenBg && detUrl) {
      const tmpHtml = $greenBg.innerHTML;
      const tmpCls = $greenBg.className;
      $greenBg.className = "";
      $greenBg.innerHTML = `<a href="${detUrl}" target="_blank" class="${tmpCls}">${tmpHtml}</a>`;
    }
  };
  const fnPromptHandle = ($img, isList = false) => {
    let textPrompt = "";
    if (isList) {
      const $itemCenter = $img.closest(".items-stretch");
      const $promptWrap = $itemCenter?.querySelector(".self-stretch");
      if ($promptWrap) {
        const $prompt = $promptWrap.querySelector(".text-sm.line-clamp-2");
        $prompt?.classList.remove("line-clamp-2");
        $prompt?.classList.add("dzmm-prompt");
        const actBar = $promptWrap.querySelector("div.mt-1.justify-between");
        if (actBar) {
          actBar.classList.remove("justify-between", "items-end");
          actBar.classList.add("items-center");
        }
        $promptWrap.classList.add("cursor-default");
        $promptWrap.onclick = (e) => {
          if (e.target.tagName === "BUTTON") return;
          e.stopPropagation();
          e.preventDefault();
        };
      }
    }
    else {
      const $divGroup = $img.closest("div.group");
      const $promptWrap = $divGroup?.querySelector("div.px-3");
      if ($promptWrap && $img.dataset.index) {
        gob._cleanClickEvents($promptWrap);
        const $prompt = $promptWrap.querySelector("p.text-sm");
        $prompt?.classList.add("dzmm-prompt");
        textPrompt = $prompt?.textContent?.trim() || "";
        storeArt.artsCheck(textPrompt, (found) => {
          if (found.length > 0) {
            let $artFound;
            if ($promptWrap.querySelector(".text-xs")) {
              $artFound = $promptWrap.querySelector(".flex-nowrap");
              $artFound?.classList.add("dzmm-art");
            }
            else {
              $prompt?.insertAdjacentHTML("afterend", "<p class=\"dzmm-art flex gap-1\" data-art=\"found\"></p>");
              $artFound = $promptWrap.querySelector(".dzmm-art");
            }
            found.forEach((art) => {
              $artFound?.insertAdjacentHTML("beforeend", `<span class="text-xs px-2 py-0.5 rounded-full dark:bg-pink-900/40 dark:text-pink-200" data-art="${art}">${art}</span>`);
            });
          }
        });
      }
      if ($divGroup) {
        $divGroup.style.aspectRatio = "0.75/1.2";
      }
    }
  };
  const fnCountUndownloaded = (imgs) => {
    const $navUL = $n("nav[role=\"navigation\"] ul");
    if (!$navUL) return;
    let count = 0;
    imgs.forEach(($img) => {
      if ($img && $img.classList.contains("un-downloaded")) {
        count++;
      }
    });
    let $countSpan = $navUL.querySelector(".dzmm-undownload-count");
    if (!$countSpan) {
      $countSpan = document.createElement("li");
      $countSpan.className = "dzmm-undownload-count text-sm text-muted-foreground px-2";
      $navUL.appendChild($countSpan);
    }
    count === 0 ? $countSpan.classList.add("hidden") : $countSpan.classList.remove("hidden");
    const lstCount = $countSpan.dataset.count || "0";
    if (lstCount === String(count)) return;
    $countSpan.dataset.count = String(count);
    $countSpan.textContent = `未下载: ${count}`;
  };
  class PreviewEngine {
    getImages;
    $fixedDiv;
    clsName = "";
    isList = false;
    imageHandlers = [];
    finalizeHandlers = [];
    hasRegisteredDefaults = false;
    init({ getImages, clsName }) {
      this.getImages = getImages;
      this.$fixedDiv = insertPreviewContainer(clsName);
      this.clsName = clsName;
      this.isList = $na(".space-y-3").length > 0;
      this.regDefaults();
      return this;
    }

    regImage(apply, name = "image-handler") {
      this.imageHandlers.push({ name, apply });
      return this;
    }

    regFinalize(apply, name = "finalize-handler") {
      this.finalizeHandlers.push({ name, apply });
      return this;
    }

    regDefaults() {
      if (this.hasRegisteredDefaults) return this;
      this.regImage(($img, i) => {
        $img.dataset.index = i.toString();
      }, "set-index");
      this.regImage(($img) => {
        storePublic.isPublic($img);
      }, "public-mark");
      storeDownload.switchOn && this.regImage(($img) => {
        storeDownload.isDL($img);
      }, "download-mark");
      this.regImage(($img, _i, ctx) => {
        fnPromptHandle($img, ctx.isListMode);
      }, "prompt-enhance");
      this.regImage(($img, _i, ctx) => {
        if (!ctx.isListMode) {
          $img.draggable = true;
        }
      }, "set-draggable");
      this.regImage(($img) => {
        fnBadgeHandle($img);
      }, "badge-enhance");
      this.regImage(($img, _i, ctx) => {
        if ($img.dataset.event === "set") return;
        $img.dataset.event = "set";
        $img.addEventListener("mouseenter", (e) => {
          clearTimeout(ctx.hideTimer);
          fnShowImgIndex($img);
          const $preview = $n(`.${ctx.clsName} .image-preview`);
          $preview.innerHTML = `<img src="${$img.src}" class="h-full" />`;
          ctx.$fixedDiv?.appendChild($preview);
          ctx.$fixedDiv.classList.remove("hidden");
          const isLeft = ctx.isListMode || e.clientX < window.innerWidth / 2;
          if (isLeft) {
            ctx.$fixedDiv.classList.add("right-1");
            ctx.$fixedDiv.classList.remove("left-1");
          }
          else {
            ctx.$fixedDiv.classList.add("left-1");
            ctx.$fixedDiv.classList.remove("right-1");
          }
          const aspectRatio = $img.naturalWidth / $img.naturalHeight;
          if (aspectRatio >= 1) {
            ctx.$fixedDiv.classList.remove("top-1");
            $preview.querySelector("img")?.classList.remove("h-full");
          }
          else {
            ctx.$fixedDiv.classList.add("top-1");
            $preview.querySelector("img")?.classList.add("h-full");
          }
        });
        $img.addEventListener("mouseout", () => {
          ctx.removePreview();
          setTimeout(() => {
            fnShowImgIndex($img, "hide");
          }, 1e3);
        });
      }, "hover-preview");
      this.regFinalize((ctx) => {
        if (!storeDownload.switchOn) return;
        gob._delayRun(() => {
          fnCountUndownloaded(ctx.imgs);
        }, 1700);
      }, "undownload-count");
      this.regFinalize((ctx) => {
        if (ctx.$fixedDiv.dataset.event === "set") return;
        ctx.$fixedDiv.addEventListener("mousemove", () => {
          clearTimeout(ctx.hideTimer);
        });
        ctx.$fixedDiv.addEventListener("mouseleave", () => {
          ctx.removePreview();
        });
        ctx.$fixedDiv.dataset.event = "set";
      }, "bind-fixed-div-events");
      this.hasRegisteredDefaults = true;
      return this;
    }

    run() {
      if (!this.getImages || !this.$fixedDiv || !this.clsName) {
        return this;
      }
      const imgs = Array.from(this.getImages());
      if (imgs.length === 0) return this;
      const ctx = {
        imgs,
        $fixedDiv: this.$fixedDiv,
        clsName: this.clsName,
        isListMode: this.isList,
        hideTimer: void 0,
        removePreview: () => {
          ctx.hideTimer = setTimeout(() => {
            ctx.$fixedDiv.classList.add("hidden");
          }, 50);
        },
      };
      imgs.forEach(($img, i) => {
        this.imageHandlers.forEach((handler) => handler.apply($img, i, ctx));
      });
      this.finalizeHandlers.forEach((handler) => handler.apply(ctx));
      return this;
    }
  }
  const fnAddImagePreview = () => {
    const isTargetPage = (gob._isHistoryPage() || gob._isGeneratePage()) && !gob._isHistoryGallery();
    if (!isTargetPage) {
      return;
    }
    const clsName = "dzmm-fixed-preview";
    const runPreview = () => {
      const previewEngine = new PreviewEngine();
      previewEngine.init({
        getImages: gob._getImages,
        clsName,
      }).run();
    };
    runPreview();
  };
  const 自动获取数量 = 48;
  const oDL = {
    markDL: ($img) => {
      gob._markImg($img, ["downloaded"]);
    },
    removeDL: ($img) => {
      gob._markImg($img, ["un-downloaded"]);
      $img.classList.remove("downloaded");
    },
    isDL: ($img) => {
      const uuid = $img.dataset.uuid;
      return $img.classList.contains("downloaded") || storeDownload.lsDLCheck(uuid);
    },
  };
  const fnGetPickBtn = ($img) => {
    const rlt = {
      $pickBtn: null,
      isPicked: false,
    };
    const $group = $img.closest("div.group");
    if (!$group) {
      return rlt;
    }
    const $btn = $group.querySelector("div.right-2.z-10 div");
    if ($btn) {
      rlt.$pickBtn = $btn;
      rlt.isPicked = $btn.classList.contains("bg-primary");
    }
    return rlt;
  };
  const fnClearPick = () => {
    const $$imgs = gob._getImages();
    $$imgs.forEach(($img) => {
      const { $pickBtn, isPicked } = fnGetPickBtn($img);
      if ($pickBtn && isPicked) {
        $pickBtn.click();
      }
    });
  };
  const fnGetPickImages = () => {
    const $$imgs = gob._getImages();
    const $$pickImgs = [];
    const $toolBar = gob._getMultiToolBar();
    if ($toolBar) {
      $$imgs.forEach(($img) => {
        const { $pickBtn, isPicked } = fnGetPickBtn($img);
        if ($pickBtn && isPicked) {
          $$pickImgs.push($img);
        }
      });
    }
    else {
      for (let i = 0; i < Math.min(自动获取数量, $$imgs.length); i++) {
        $$pickImgs.push($$imgs[i]);
      }
    }
    return $$pickImgs;
  };
  const _getImagesWrapper = (isEvenClick) => {
    const $$imgs = gob._getImages();
    let $$imgsFiltered = [];
    $$imgsFiltered = $$imgsFiltered.length > 0 ? $$imgsFiltered : $$imgs.filter(($img) => !oDL.isDL($img));
    if (isEvenClick && $$imgsFiltered.length > 0) {
      return $$imgsFiltered;
    }
    return $$imgs;
  };
  const fnAutoPick = ($toolBar, callback) => {
    const clickCount = parseInt($toolBar.dataset.dzmmClickCount || "0", 10);
    $toolBar.dataset.dzmmClickCount = (clickCount + 1).toString();
    const $$imgs = _getImagesWrapper(clickCount % 2 === 0);
    let pickCount = 0;
    const pickStart = parseInt($toolBar.dataset.dzmmPickStart || gob.$body.dataset.dzmmPickStart || "0", 10);
    const uuids = [];
    $$imgs.forEach(($img, index) => {
      const { $pickBtn, isPicked } = fnGetPickBtn($img);
      if (!$pickBtn) {
        return;
      }
      if (index >= pickStart && pickCount < 自动获取数量) {
        if (!isPicked) {
          $pickBtn.click();
        }
        pickCount++;
        const uuid = $img.dataset.uuid;
        uuid && uuids.push(uuid);
      }
      else {
        if (isPicked) {
          $pickBtn.click();
        }
      }
      if (pickCount === 1) {
        $img.scrollIntoView({ behavior: "smooth", block: "center" });
      }
    });
    uuids.length > 0 && storeDownload.switchOn && storeDownload.checkDownload(uuids.join("|")).then((data) => {
      for (let i = 0; i < uuids.length; i++) {
        const uuid = uuids[i];
        const $img = $n(`img[data-uuid="${uuid}"]`);
        if (!$img) {
          console.log(`UUID ${uuid} 对应的图片元素未找到,无法标记下载状态`);
          continue;
        }
        const rlt = data.results.find((item) => item.name.includes(uuid));
        let watchDuration = 0;
        if (rlt) {
          watchDuration = 30;
          oDL.markDL($img);
        }
        else {
          watchDuration = 180;
          oDL.removeDL($img);
        }
        storeDownload.lsWatchSet(uuid, watchDuration);
      }
    });
    let nextStart = pickStart + 自动获取数量;
    if (nextStart >= $$imgs.length) {
      nextStart = 0;
    }
    $toolBar.dataset.dzmmPickStart = nextStart.toString();
    gob.$body.dataset.dzmmPickStart = pickStart.toString();
    if (callback) {
      callback();
    }
  };
  const fnCopyUUIDs = () => {
    const $autoPickBtn = $n("button.dzmm-auto-pick-btn");
    if ($autoPickBtn) {
      const originalText = $autoPickBtn.textContent;
      $autoPickBtn.textContent = "UUID 已复制";
      setTimeout(() => {
        $autoPickBtn.textContent = originalText;
      }, 2e3);
    }
    setTimeout(async () => {
      const $$pickImgs = fnGetPickImages();
      const uuids = $$pickImgs.map(($img) => $img.dataset.uuid || "").sort((a, b) => a.localeCompare(b)).join("|");
      await navigator.clipboard.writeText(uuids);
    }, 500);
  };
  const fnAddBatchBtn = () => {
    const $toolBar = gob._getMultiToolBar();
    if (!$toolBar || $toolBar.dataset.dzmmPickInit === "1") {
      return;
    }
    const $downloadBtn = Array.from($toolBar.querySelectorAll("button")).find((btn) => btn.innerText.includes("下载"));
    if (!$downloadBtn) {
      return;
    }
    const $copyUUIDBtn = document.createElement("button");
    $copyUUIDBtn.className = $downloadBtn.className;
    $copyUUIDBtn.classList.add("dzmm-copy-uuid-btn");
    $copyUUIDBtn.innerText = "复制 UUID";
    $copyUUIDBtn.onclick = () => {
      fnCopyUUIDs();
    };
    $downloadBtn.insertAdjacentElement("beforebegin", $copyUUIDBtn);
    const $autoPickBtn = document.createElement("button");
    $autoPickBtn.className = $downloadBtn.className;
    $autoPickBtn.classList.add("dzmm-auto-pick-btn");
    $autoPickBtn.innerText = "批量选择";
    $autoPickBtn.onclick = () => {
      fnClearPick();
      gob._delayRun(() => {
        fnAutoPick($toolBar, fnCopyUUIDs);
      }, 300);
    };
    $downloadBtn.insertAdjacentElement("beforebegin", $autoPickBtn);
    $toolBar.dataset.dzmmPickInit = "1";
  };
  const fnAddStoreArt = () => {
    if (!gob._isHistoryPage() && !gob._isGeneratePage() && !gob._isDrawPage()) {
      return;
    }
    const clsName = "dzmm-art-input";
    if ($n(`.${clsName}`)) return;
    let $subNav = $n("div.relative.items-center.overflow-x-auto");
    if (!$subNav) {
      const $h3 = $n("div.items-center > h3.font-medium");
      if (!$h3) return;
      $subNav = document.createElement("div");
      $subNav.classList.add("flex-1", "flex", "gap-1", "ml-1");
      $h3.insertAdjacentElement("afterend", $subNav);
    }
    if (!$subNav) return;
    const $nav = $n(".dzmm-nav-added");
    const $btn = document.createElement("button");
    $btn.classList.add(clsName);
    $btn.classList.add("dzmm-store-art-btn", "px-2", "py-1.5", "text-sm", "text-muted-foreground", "hover:text-foreground", "rounded-md", "border", "border-input", "hover:bg-accent");
    $btn.textContent = "保存画师";
    const $input = document.createElement("input");
    $input.type = "text";
    $input.placeholder = "输入画师名称并回车";
    $input.classList.add("dzmm-art-input", "text-sm", "bg-muted", "rounded-full", "w-0", "transition-[width]", "duration-500", "ease-swift");
    $subNav.insertAdjacentElement("beforeend", $input);
    $subNav.insertAdjacentElement("beforeend", $btn);
    const observer = new IntersectionObserver((changes) => {
      changes.forEach((change) => {
        change.intersectionRatio <= 0 ? $nav?.insertAdjacentElement("beforeend", $input) : $btn?.insertAdjacentElement("beforebegin", $input);
        change.intersectionRatio <= 0 && _ToggleInput("short");
      });
    });
    observer.observe($btn);
    const fnSubmitArt = () => {
      const artName = $input.value.trim().replace(/\s+/g, "_").toLocaleLowerCase();
      if (!artName) {
        $input.placeholder = "画师名称不能为空";
        return;
      }
      storeArt.add(artName);
      const btnText = $btn.textContent || "保存画师";
      $btn.textContent = "已保存: " + artName;
      gob._delayRun(() => {
        $btn.textContent = btnText;
      }, 2e3);
      $input.value = "";
    };
    let t = null;
    const _ToggleInput = (to, focus = false) => {
      if (to === "show" || to === "short") {
        if (t) {
          clearTimeout(t);
          t = null;
        }
        $input.classList.add("w-48", "px-2", "py-1.5");
      }
      if (to === "show") {
        $input.classList.remove("short");
        focus && $input.focus();
      }
      if (to === "short") {
        t = setTimeout(() => {
          $input.classList.add("short");
          $input.blur();
        }, 1e3);
      }
    };
    [$n("header"), $subNav.parentElement].forEach(($parent) => {
      $parent?.addEventListener("mouseenter", () => {
        if ($parent.querySelector(`.${clsName}`)) {
          _ToggleInput("show", true);
        }
      });
      $parent?.addEventListener("mouseleave", () => {
        if ($parent.querySelector(`.${clsName}`)) {
          _ToggleInput("short");
        }
      });
    });
    $btn.addEventListener("click", () => {
      fnSubmitArt();
    });
    $input.addEventListener("keydown", (e) => {
      if (e.key === "Enter") {
        fnSubmitArt();
      }
    });
  };
  const fnParseSoloGallery = () => {
    if (!gob.locHref.includes("/gallery/")) {
      return;
    }
    const galleryId = gob.locHref.split("/gallery/")[1].split("?")[0];
    const $myGallerySpan = Array.from($na(".justify-between .text-xs")).find((el) => el.textContent?.includes("我的画廊"));
    if (!$myGallerySpan || $myGallerySpan.dataset.uuid === galleryId) {
      return;
    }
    $myGallerySpan.dataset.uuid = galleryId;
    const images = Array.from($na(".swiper-slide img")).map(($img) => {
      const src = $img.getAttribute("src");
      return src || "";
    });
    storePublic.addGallery({
      id: galleryId,
      title: "unknown",
      images,
    });
  };
  const fnAddPromptExt2 = ($h3Parent, $p) => {
    const $copyBtn = $h3Parent?.querySelector("button.rounded");
    if (!$copyBtn) return;
    const uuid = gob._getUUID(gob.locHref);
    let $dlBtn = $h3Parent?.querySelector(".dzmm-download-prompt-btn");
    if (!$dlBtn) {
      $dlBtn = document.createElement("button");
      $dlBtn.classList.add("dzmm-download-prompt-btn", "p-1.5", "rounded", "hover:bg-muted", "transition-all", "duration-200");
      $dlBtn.textContent = "下载提示词";
      $copyBtn.insertAdjacentElement("beforebegin", $dlBtn);
    }
    $dlBtn.onclick = () => {
      const promptText = $p.textContent?.trim() || "";
      if (!promptText) return;
      const blob = new Blob([promptText], { type: "text/plain;charset=utf-8" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `${uuid}.txt`;
      a.click();
      URL.revokeObjectURL(url);
    };
  };
  const fnAddPromptExt = () => {
    const $h3提示词 = Array.from($na("h3")).find((el) => {
      return el.textContent?.includes("绘画提示词");
    });
    if (!$h3提示词) return;
    const $h3Parent = $h3提示词.parentElement;
    const $p = $h3Parent?.nextElementSibling;
    const fnChangePrompt = ($p2) => {
      $p2.classList.add("dzmm-sys-prompt");
      $p2.classList.add("hidden");
      const $$a = $p2.querySelectorAll("a");
      let promptText = "";
      if ($$a.length > 0) {
        $$a.forEach(($a) => {
          const linkText = $a.textContent?.trim() || "";
          promptText += linkText + ", ";
        });
      }
      else {
        promptText = $p2.textContent?.trim() || "";
      }
      let $newPromptP = $n(".dzmm-new-prompt");
      if ($newPromptP) {
        $newPromptP.textContent = promptText;
      }
      else {
        $newPromptP = document.createElement("p");
        $newPromptP.textContent = promptText;
        $newPromptP.classList.add("dzmm-new-prompt", "rounded-md", "bg-muted/60", "p-3", "leading-relaxed", "text-foreground/80");
        $h3Parent?.insertAdjacentElement("afterend", $newPromptP);
      }
      fnAddPromptExt2($h3Parent, $newPromptP);
    };
    if ($p && $p.tagName === "DIV") {
      fnChangePrompt($p);
      const observer = new MutationObserver(() => {
        fnChangePrompt($p);
      });
      observer.observe($p, { childList: true, subtree: true });
    }
  };
  const dropPanel = {
    PANEL_ID: "dzmm-drop-panel",
    $panel: null,
    $dragImg: null,
    t: null,
    hasImageFiles(event) {
      const files = event.dataTransfer?.files;
      if (!files || files.length === 0) {
        return false;
      }
      return Array.from(files).some((file) => file.type.startsWith("image/"));
    },
    ensure() {
      if (this.$panel) {
        return this.$panel;
      }
      let $panel = document.getElementById(this.PANEL_ID);
      $panel = document.createElement("div");
      $panel.className = "dzmm-drop-panel rounded-md z-55 mini";
      $panel.id = this.PANEL_ID;
      $panel.innerHTML = `
    <div class="dzmm-drop-panel-zone" data-zone="top">
        <h4>管理在线图片</h4>
        <p>根据 UUID 查看图片记录</p>
    </div>
    <div class="dzmm-drop-panel-zone" data-zone="bottom">
        <h4>页面内的图片处理</h4>
        <p>检查图片是否下载(需要配置 everything API)</p>
    </div>
`;
      document.body.appendChild($panel);
      this.$panel = $panel;
      return $panel;
    },
    getDropZone(event) {
      const $panel = this.$panel;
      if (!$panel) {
        return "top";
      }
      const rect = $panel.getBoundingClientRect();
      const isTop = event.clientY < rect.top + rect.height / 2;
      return isTop ? "top" : "bottom";
    },
  };
  const fnAddDropPanel = () => {
    if (document.body.dataset.dzmmDropPanelInit === "1") {
      return;
    }
    const $panel = dropPanel.ensure();
    document.body.addEventListener("dragstart", (event) => {
      const $target = event.target;
      dropPanel.$dragImg = $target instanceof HTMLImageElement ? $target : null;
    });
    document.body.addEventListener("dragend", () => {
      dropPanel.$dragImg = null;
    });
    const togglePanel = (to, delay = 300) => {
      dropPanel.t && clearTimeout(dropPanel.t);
      const bolToMini = to === "hide";
      $panel.classList.toggle("mini", bolToMini);
      dropPanel.t = setTimeout(() => {
        togglePanel("hide");
      }, delay);
      setTimeout(() => {
        $panel.dataset.state = to;
      }, delay);
    };
    document.body.addEventListener("dragenter", (event) => {
      event.preventDefault();
      togglePanel("show");
    });
    $panel.addEventListener("dragover", (event) => {
      event.preventDefault();
      togglePanel("show", 5e3);
    });
    $panel.addEventListener("dragleave", (event) => {
      if ($panel.dataset.state !== "show") {
        return;
      }
      const nextTarget = event.relatedTarget;
      if (!nextTarget || !$panel.contains(nextTarget)) {
        togglePanel("hide");
      }
    });
    $panel.addEventListener("drop", (event) => {
      event.preventDefault();
      const zone = dropPanel.getDropZone(event);
      if (zone === "top") {
        if (!dropPanel.hasImageFiles(event)) {
          return;
        }
        const files = Array.from(event.dataTransfer?.files || []).filter((file) => file.type.startsWith("image/"));
        const host = location.origin;
        const name = files[0].name.replace(/^.+@|\.png/g, "");
        _GM_openInTab(host + "/draw/" + name, false);
      }
      if (storeDownload.switchOn && zone === "bottom") {
        const $img = dropPanel.$dragImg;
        if ($img && $img.dataset.uuid) {
          storeDownload.isDL($img, true);
        }
      }
      dropPanel.$dragImg = null;
      togglePanel("hide");
    });
    document.body.dataset.dzmmDropPanelInit = "1";
  };
  const fnAddNavLinks = () => {
    fnAddGalleryLink();
    fnAddGenerateLink();
    fnAddHistoryLink();
    fnConvertButtonToLink();
    gob._delayRun(() => {
      if (gob.$$header.length >= 2) {
        Array.from(gob.$$header).forEach(($header) => {
          if (!$header.querySelector(".dzmm-nav-added")) {
            $header.remove();
          }
        });
      }
    });
  };
  const fnSetContainer = () => {
    if (gob.bolWidthChanged) {
      return;
    }
    setTimeout(() => {
      const $container = $n(".min-h-screen div.container");
      if (!$container) {
        return;
      }
      $container.className = "w-full space-y-2 flex-1 container max-w-6xl mx-auto p-4";
      const $grid = $container.querySelector(".grid.md\\:grid-cols-3");
      if (!$grid) {
        return;
      }
      $grid?.classList.add("lg:grid-cols-6", "width-adjusted");
    }, 1e3);
  };
  const fnInitLitePage = () => {
    fnAddNavLinks();
    fnSetContainer();
    fnAddImagePreview();
    fnAddStoreArt();
    fnAddPromptExt();
    fnAddBatchBtn();
    fnAddDropPanel();
    fnParseSoloGallery();
  };
  const mountLitePage = () => {
    console.log("lite page mounted");
    gob.enablePageObserve(() => {
      fnInitLitePage();
    });
  };
  mountLitePage();
})();