danbooru / rule34 image tag extractor

Extract image tags/entries from the respective website, including author, character, character attribution (copyright), and filter out censored annotations

Before you install, Sleazy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script will inject ads on the sites you visit. The author of this script explains: 其他浏览器插件的广告

This script earns commission for the author, for example by rewriting links or providing coupon codes to include a referral or affiliate code. The author of this script explains: AI 网站推广

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name            danbooru / rule34 图片标签提取
// @name:en         danbooru / rule34 image tag extractor
// @name:zh         danbooru / rule34 图片标签提取
// @name:ja         danbooru / rule34 画像のタグ抽出
// @namespace       oninashi
// @version         1.0.251120
// @author          oninashi
// @description     从相应网站提取图片标签/词条,包括作者、角色、角色归属(版权),并过滤打码标注
// @description:en  Extract image tags/entries from the respective website, including author, character, character attribution (copyright), and filter out censored annotations
// @description:ja  対応するウェブサイトから画像のタグ/エントリを抽出し、作者、キャラクター、キャラクターの帰属(著作権)、および検閲された注釈をフィルタリングします
// @description:zh  从相应网站提取图片标签/词条,包括作者、角色、角色归属(版权),并过滤打码标注
// @license         MIT
// @homepage        https://baraag.net/@wd92asd
// @supportURL      https://baraag.net/@wd92asd
// @match           https://danbooru.donmai.us/*
// @match           https://rule34.xxx/*
// @require         https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js
// @grant           GM_addStyle
// @antifeature     ads            其他浏览器插件的广告
// @antifeature     referral-link  AI 网站推广
// ==/UserScript==

(function(vue) {
  "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.appendChild(document.createElement("style")).append(t); })(e)); };

  importCSS(" *,:before,:after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / .5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / .5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.de-helper :is(.my-2){margin-top:.5rem;margin-bottom:.5rem}.de-helper :is(.mb-1){margin-bottom:.25rem}.de-helper :is(.hidden){display:none}.de-helper :is(.h-10){height:2.5rem}.de-helper :is(.w-\\[30\\%\\]){width:30%}.de-helper :is(.w-\\[48\\%\\]){width:48%}.de-helper :is(.w-10){width:2.5rem}.de-helper :is(.w-48){width:12rem}.de-helper :is(.w-full){width:100%}.de-helper :is(.flex){display:flex}.de-helper :is(.flex-col){flex-direction:column}.de-helper :is(.flex-wrap){flex-wrap:wrap}.de-helper :is(.cursor-pointer){cursor:pointer}.de-helper :is(.items-center){align-items:center}.de-helper :is(.justify-center){justify-content:center}.de-helper :is(.gap-1){gap:.25rem}.de-helper :is(.gap-2){gap:.5rem}.de-helper :is(.overflow-clip){overflow:clip}.de-helper :is(.overflow-hidden){overflow:hidden}.de-helper :is(.border){border-width:1px}.de-helper :is(.border-b-1){border-bottom-width:1px}.de-helper :is(.border-blue-500){--un-border-opacity:1;border-color:rgb(59 130 246 / var(--un-border-opacity))}.de-helper :is(.border-transparent){border-color:transparent}.de-helper :is(.border-yellow-400){--un-border-opacity:1;border-color:rgb(250 204 21 / var(--un-border-opacity))}.de-helper :is(.rounded-md){border-radius:.375rem}.de-helper :is(.rounded){border-radius:.25rem}.de-helper :is(.rounded-l-lg){border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.de-helper :is(.bg-blue-500){--un-bg-opacity:1;background-color:rgb(59 130 246 / var(--un-bg-opacity))}.de-helper :is(.bg-white){--un-bg-opacity:1;background-color:rgb(255 255 255 / var(--un-bg-opacity))}.de-helper :is(.hover\\:bg-blue-600:hover){--un-bg-opacity:1;background-color:rgb(37 99 235 / var(--un-bg-opacity))}.de-helper :is(.p-1){padding:.25rem}.de-helper :is(.p-2){padding:.5rem}.de-helper :is(.px-2){padding-left:.5rem;padding-right:.5rem}.de-helper :is(.pb-2px){padding-bottom:2px}.de-helper :is(.font-size-17px){font-size:17px}.de-helper :is(.text-white){--un-text-opacity:1;color:rgb(255 255 255 / var(--un-text-opacity))}.de-helper :is(.font-500){font-weight:500}.de-helper :is(.font-600){font-weight:600}.de-helper :is(.opacity-95){opacity:.95}.de-helper :is(.transition-colors){transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s} ");

  const clothes = [
    [
      ".+_uniform",
      "bikini",
      "bottomless",
      "clothes",
      "clothing",
      "costume",
      "maid",
      "nude",
      "school_uniform",
      "serafuku",
      "topless",
      "suit",
    ],
    [
      ".+_cutout",
      "belt\\b",
      "bow\\b",
      "bracelet",
      "charm",
      "cross",
      "frills",
      "fur_trim",
      "glint",
      "heart",
      "jewelry",
      "lace_trim\\b",
      "lace\\b",
      "ofuda",
      "pasties",
      "piercing",
      "pinstripe_pattern",
      "ribbon",
      "zipper",
    ],
    [
      ".+_cap\\b",
      ".+_eyewear",
      "eyewear_.+",
      "glasses",
      "hat",
      "qingdai_guanmao",
      "veil\\b",
      "headgear",
    ],
    [
      "choker",
      "collar",
      "neckerchief",
      "off_shoulder",
      "scarf",
      "spaghetti_strap",
    ],
    [
      "gloves?",
      "sleeveless",
      "sleeves?",
      "bridal_gauntlets",
    ],
    [
      ".+_hood",
      "apron",
      "dress",
      "jacket",
      "leotard",
      "shirt",
      "swimsuit",
    ],
    [
      "babydoll",
      "bra\\b",
      "cameltoe",
      "g-string",
      "gusset",
      "highleg",
      "lingerie",
      "panties",
      "panty_straps",
      "pelvic_curtain",
      "t-back",
      "thong",
      "trefoil",
      "underwear",
      "wedgie",
      "pasties",
    ],
    [
      ".+_pants",
      "pants",
      "shorts",
      "skirt",
    ],
    [
      "garter_straps",
      "legwear",
      "pantyhose",
      "skindentation",
      "thigh_strap",
      "thighhighs",
    ],
    [
      "high_heels",
      "mary_janes",
      "shoes",
      "slippers",
      "socks",
    ],
  ];
  const flatClothes = {
    name: "服装",
    order: 145,
    break: true,
    items: clothes.flat(),
  };
  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 gob = {
    site: "danbooru",
    vueEls: ["de-app", "de-sidebar"],
    $imgContainer: null,
    $app: null,
    get locHref() {
      return location.href;
    },
    get types() {
      return ["artist", "character", "copyright", "general"];
    },
    $n(selector, context = document) {
      return context.querySelector(selector);
    },
    $na(selector, context = document) {
      return context.querySelectorAll(selector);
    },
    _init() {
      this.site = this.locHref.includes("rule34") ? "rule34" : "danbooru";
    },
    _isPost() {
      if (this.site === "rule34") {
        this.$imgContainer = gob.$n(".flexi > div > img");
        return /^https:\/\/rule34\.xxx\/index\.php\?page=post&s=view&id=\d+/.test(this.locHref);
      }
      else {
        this.$imgContainer = gob.$n("#content .image-container");
        return this.site === "danbooru" && /^https:\/\/danbooru\.donmai\.us\/posts\/.+$/.test(this.locHref);
      }
    },
    _getPostID() {
      if (this.site === "danbooru") {
        const match = this.locHref.match(/^https:\/\/danbooru\.donmai\.us\/posts\/(\d+)/);
        return match ? match[1] : null;
      }
      else if (this.site === "rule34") {
        const match = this.locHref.match(/^https:\/\/rule34\.xxx\/index\.php\?page=post&s=view&id=(\d+)/);
        return match ? match[1] : null;
      }
      return null;
    },
    _tagLink(tag) {
      if (this.site === "danbooru") {
        return `https://danbooru.donmai.us/posts?tags=${tag}`;
      }
      else if (this.site === "rule34") {
        return `https://rule34.xxx/index.php?page=post&s=list&tags=${tag}`;
      }
      return "";
    },
    _setAppStyle() {
      if (!gob.$app || !gob.$imgContainer) return;
      const MIN_MARGIN = 10;
      const rectImg = gob.$imgContainer.getBoundingClientRect();
      const rectApp = gob.$app.getBoundingClientRect();
      const minTop = window.innerHeight < rectApp.height + MIN_MARGIN ? window.innerHeight - rectApp.height - MIN_MARGIN : MIN_MARGIN;
      const appTop = Math.max(rectImg.top, minTop);
      const appLeft = rectImg.right + MIN_MARGIN;
      gob.$app.style.setProperty("--top", `${appTop}px`);
      gob.$app.style.setProperty("--left", `${appLeft}px`);
    },
    _getTags(type = "general") {
      if (this.site === "danbooru" && this._isPost()) {
        let selector = `ul.${type}-tag-list`;
        const tags = this.$n(selector)?.querySelectorAll("li a.search-tag") || [];
        return Array.from(tags).map((tag) => tag.textContent.trim().replace(/\s/g, "_") || "");
      }
      if (this.site === "rule34" && this._isPost()) {
        let selector = `li.tag-type-${type} >a`;
        const $$tags = this.$na(selector) || [];
        const tags = Array.from($$tags).filter((tag) => tag.textContent !== "?");
        return Array.from(tags).map((tag) => tag.textContent.trim().replace(/\s/g, "_") || "");
      }
      return [];
    },
    _replaceSynonyms(tags) {
      const _subReplace = (arg_tags, search, target = "_placeholder_") => {
        return arg_tags.map((tag) => tag.replace(search, target));
      };
      const list = {
        fingernails: ["nails"],
      };
      for (const key in list) {
        if (!Object.hasOwn(list, key)) continue;
        tags = _subReplace(tags, key, "_placeholder_");
        const synonyms = list[key];
        for (const synonym of synonyms) {
          tags = _subReplace(tags, synonym, key);
        }
        tags = _subReplace(tags, "_placeholder_", key);
      }
      return tags;
    },
    _terGroups() {
      return [
        {
          name: "前置",
          items: ["\\dgirl", "\\dboy", "solo|duo", "yuri|hetero", "multiple_girls", "6\\+"],
        },
        {
          name: "镜头视角",
          items: [".+_focus", "pov", "multiple_views", ".+_angle", "\\bfrom_behind", "from_above", "from_below", "from_side", "close-up", "portrait", "looking_.+", "full_body", "upper_body", "cowboy_shot", "upskirt", "straight-on"],
        },
        {
          name: "场景位置",
          items: ["indoors", "outdoors", ".+_background", "sidelighting", "onsen", "blurry", "window", "\\bshadow\\b", "night", "day", "cityscape", "sky"],
        },
        {
          name: "物品家具",
          items: ["\\bbed\\b", "pillow", "bed_sheet", "petals", "phone", "bag", "chandelier"],
          break: true,
        },
        {
          name: "人物整体",
          items: ["sweat", "hairy", "mole\\b", "mature", "jimiko", "mojyo", ".+_skin", "skinned", "plump", "tanlines", "\\btan\\b", ".+_out_of_frame", "scars?\\b", "steaming_body", "curvy", "petite", "skinny"],
        },
        {
          name: "头",
          items: ["\\bhair", ".+_hair", "sidelocks", "hairband", "ponytail", "ahoge", "braid\\b", "horns", "twintails", "halo", "。+_bangs"],
        },
        {
          name: "脸",
          items: ["blush", "mouth", "smile", "eyes", "eyebrows", "earrings", "makeup", "eyelashes", "grin", "teeth", "tongue", "fang", ":.+", "lips", "!+", "@_@", "mole_under_eye", "drooling", "saliva"],
        },
        {
          name: "肩颈",
          items: ["collarbone", "bare_shoulders"],
        },
        {
          name: "胸部",
          items: ["breasts?", "nipples?", "areolae?", "cleavage", "no_bra", "underboob", "sideboob", "backboob"],
        },
        {
          name: "手臂",
          items: ["armpits", "armpit_crease", "armpit_riah", "fingernails", "nail_polish"],
        },
        {
          name: "腰腹",
          items: ["belly", "navel", "\\bstomach"],
        },
        {
          name: "股间",
          items: ["pussy", "pubic_riah", "groin", "fat_mons", "partially_visible_vulva"],
        },
        {
          name: "臀",
          items: ["\\bass", "huge_ass", "anus", "anal_riah", "wide_hips", "hip_bones"],
        },
        {
          name: "腿",
          items: ["thighs", "legs\\b", "kneepits", "thigh_gap", "thick_thighs", "zettai_ryouiki"],
        },
        {
          name: "脚",
          items: ["barefoot", "feet", "soles", "toes"],
          break: true,
        },
        {
          name: "动作",
          items: [".+_pose", "holding", "straddling", "spread_.+", "squatting", "lying", "adjusting_.+", ".+_up", "sitting", "standing", "hands?_on_.+", "on_side", "on_stomach", "groping", "grabbing", "elbow_rest", "on_bed", "on_back", "head_tilt", "arms_behind_.+", "against_.+"],
        },
        {
          name: "sex",
          items: ["sex", "penis", "vagina", ".+_position", "girl_on_top", ".+stimulation", "ejaculation", "\\bcum", "breath", "erection", "suspended_congress", "lifting_person", "prone_bone"],
          break: true,
        },
        {
          name: "其他",
          items: [],
        },
      ].map((group, i) => ({ ...group, order: 10 * i }));
    },
    _groupTags(tags, flat = false) {
      const terGroups = this._terGroups();
      terGroups.push(flatClothes);
      terGroups.sort((a, b) => a.order - b.order);
      const groupedTags = [];
      for (const group of terGroups) {
        groupedTags.push({ name: group.name, items: [], order: group.order, break: group.break || false });
      }
      for (let tag of tags) {
        tag = tag.replace(/(pubic|anal|armpit|body)_hair/g, "$1_riah");
        let found = false;
        for (const group of terGroups) {
          if (group.items.some((item) => {
            try {
              const regex = new RegExp(item);
              return regex.test(tag);
            }
            catch (e) {
              console.log(e);
              return tag.includes(item);
            }
          })) {
            tag = tag.replace(/(pubic|anal|armpit|body)_riah/g, "$1_hair");
            groupedTags.find((g) => g.name === group.name)?.items.push(tag);
            found = true;
            break;
          }
        }
        if (!found) {
          groupedTags.find((g) => g.name === "其他")?.items.push(tag);
        }
      }
      if (flat) {
        const flatTags = [];
        let legChaged = false;
        for (const group of groupedTags) {
          if (group.items.length > 0) {
            flatTags.push(...group.items);
            legChaged = true;
          }
          if (group.break && legChaged) {
            flatTags.push("++");
            legChaged = false;
          }
        }
        if (flatTags[flatTags.length - 1] === "++") {
          flatTags.pop();
        }
        return flatTags;
      }
      return groupedTags;
    },
    _saveSelectedTags(arg_data) {
      const postID = this._getPostID();
      if (!postID) return;
      const key = `selectedTags_${postID}`;
      lsObj.setItem(key, arg_data);
    },
    _loadSelectedTags() {
      const postID = this._getPostID();
      if (!postID) return;
      const key = `selectedTags_${postID}`;
      return lsObj.getItem(key, []);
    },
  };
  const _hoisted_1$1 = { class: "mb-1 flex items-center gap-1" };
  const _hoisted_2 = { class: "de-tags-list flex flex-wrap gap-2 w-full font-size-17px font-500" };
  const _hoisted_3 = ["href", "title"];
  const _hoisted_4 = ["onClick"];
  const _hoisted_5 = {
    key: 0,
    class: "rounded",
  };
  const _hoisted_6 = ["value"];
  const _sfc_main$1 = vue.defineComponent({
    __name: "App",
    setup(__props) {
      const tagsByGroup = vue.ref([]);
      const tagsForShow = vue.computed(() => genShowTags());
      const showType = vue.ref("def");
      const selectedTags = vue.ref([]);
      const showSelected = vue.computed(() => {
        const flatTags = tagsForShow.value.filter((_, index) => selectedTags.value.includes(index));
        return flatTags.map((tag) => tag.value).join(", ");
      });
      const clsTagSpan = vue.ref("");
      const blockTags = ["mosaic_censoring", "censored", "bar_censor", "talisman_pasties", "heart_censor", "patreon_username", "web_address"];
      const getTags = () => {
        const rltTags = [];
        const types = gob.types;
        types.forEach((type) => {
          let groupTags = gob._getTags(type).map((tag) => {
            return type !== "general" ? `${type}: ${tag}` : tag;
          });
          if (type === "general") {
            groupTags = groupTags.filter((tag) => !blockTags.includes(tag));
            groupTags = gob._replaceSynonyms(groupTags);
            groupTags = gob._groupTags(groupTags, true);
          }
          const curTags = {
            category: type,
            values: type !== "general" ? [...groupTags, "||"] : groupTags,
          };
          if (groupTags.length > 0) {
            rltTags.push(curTags);
          }
        });
        return rltTags;
      };
      const genShowTags = () => {
        const showTags = [];
        tagsByGroup.value.forEach((gTag) => {
          const flatTags = gTag.values.map((item) => {
            return {
              category: gTag.category,
              value: item,
            };
          });
          showTags.push(...flatTags);
        });
        const arrClass = [showTags.length >= 60 ? "w-[30%]" : "w-[48%]"];
        arrClass.push("border-b-1", "border-transparent", "pb-2px", "cursor-pointer");
        clsTagSpan.value = arrClass.join(" ");
        return showTags;
      };
      const copyTags = (event) => {
        const $target = event.target;
        $target.textContent = "已复制";
        setTimeout(() => {
          $target.textContent = "一键复制";
        }, 1500);
        let text = tagsForShow.value.map((tag) => tag.value).join(", ").replace(/([|+]{2}),/g, "$1");
        if (selectedTags.value.length > 0 && showType.value === "def") {
          text = showSelected.value;
        }
        navigator.clipboard.writeText(text);
      };
      const switchShowType = () => {
        switch (showType.value) {
          case "def":
            showType.value = "link";
            break;
          default:
            showType.value = "def";
            break;
        }
      };
      const toggleTagSelection = (index) => {
        const curSet = new Set(selectedTags.value);
        if (curSet.has(index)) {
          curSet.delete(index);
        }
        else {
          curSet.add(index);
        }
        selectedTags.value = Array.from(curSet).sort();
        gob._saveSelectedTags(selectedTags.value);
      };
      vue.onMounted(() => {
        tagsByGroup.value = getTags();
        selectedTags.value = gob._loadSelectedTags();
      });
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createElementVNode("p", _hoisted_1$1, [
            vue.createElementVNode("button", {
              class: "p-1 px-2 bg-blue-500 hover:bg-blue-600 text-white rounded transition-colors border-blue-500",
              onClick: _cache[0] || (_cache[0] = ($event) => copyTags($event)),
            }, " 一键复制 "),
            vue.createElementVNode("button", {
              class: "p-1 px-2 bg-blue-500 hover:bg-blue-600 text-white rounded transition-colors border-blue-500",
              onClick: switchShowType,
            }, vue.toDisplayString(showType.value === "def" ? "切至链接模式" : "切至默认模式"), 1),
          ]),
          vue.createElementVNode("div", _hoisted_2, [
            showType.value === "link"
              ? (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 0 }, vue.renderList(tagsForShow.value, (itemTag) => {
                return vue.openBlock(), vue.createElementBlock("a", {
                  key: itemTag.value,
                  class: vue.normalizeClass(["overflow-clip", `${itemTag.category} ${clsTagSpan.value}`]),
                  href: vue.unref(gob)._tagLink(itemTag.value),
                  title: itemTag.value,
                  target: "_blank",
                }, vue.toDisplayString(itemTag.value), 11, _hoisted_3);
              }), 128))
              : (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 1 }, vue.renderList(tagsForShow.value, (itemTag, i) => {
                return vue.openBlock(), vue.createElementBlock("span", {
                  key: itemTag.value,
                  class: vue.normalizeClass(["overflow-clip", `${itemTag.category} ${clsTagSpan.value} ${showType.value === "def" && selectedTags.value.includes(i) ? " border-yellow-400" : ""}`]),
                  onClick: ($event) => showType.value === "def" && toggleTagSelection(i),
                }, vue.toDisplayString(itemTag.value), 11, _hoisted_4);
              }), 128)),
          ]),
          showType.value === "def" && selectedTags.value.length > 0
            ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5, [
              _cache[1] || (_cache[1] = vue.createElementVNode("hr", { class: "border-b-1 my-2" }, null, -1)),
              vue.createElementVNode("textarea", {
                class: "w-full p-2 border rounded",
                value: showSelected.value,
                rows: "5",
                readonly: "",
              }, null, 8, _hoisted_6),
            ]))
            : vue.createCommentVNode("", true),
          _cache[2] || (_cache[2] = vue.createElementVNode("hr", { class: "border-b-1 my-2" }, null, -1)),
          _cache[3] || (_cache[3] = vue.createElementVNode("div", { class: "font-600" }, " 说明:默认模式下,可以点击词条以自行选择 ", -1)),
          _cache[4] || (_cache[4] = vue.createElementVNode("div", { class: "font-600 ads" }, [
            vue.createTextVNode(" 广告:"),
            vue.createElementVNode("a", {
              href: "https://www.dzmm.io?rf=76bdb299",
              target: "_blank",
              title: "dzmm.io",
            }, "dzmm.io"),
            vue.createTextVNode(" ← 可以用 ublock-origin 规则屏蔽这个广告显示 "),
          ], -1)),
        ], 64);
      };
    },
  });
  function mitt(n) {
    return { all: n = n || new Map(), on: function(t, e) {
      var i = n.get(t);
      i ? i.push(e) : n.set(t, [e]);
    }, off: function(t, e) {
      var i = n.get(t);
      i && (e ? i.splice(i.indexOf(e) >>> 0, 1) : n.set(t, []));
    }, emit: function(t, e) {
      var i = n.get(t);
      i && i.slice().map(function(n2) {
        n2(e);
      }), (i = n.get("*")) && i.slice().map(function(n2) {
        n2(t, e);
      });
    } };
  }
  const eventBus = mitt();
  const _hoisted_1 = { class: "flex" };
  const _sfc_main = vue.defineComponent({
    __name: "SideBar",
    setup(__props) {
      const showContent = vue.ref(false);
      const showApp = vue.ref(false);
      const toggleApp = () => {
        const $vue = gob.$n("#" + gob.vueEls[0]);
        if ($vue?.classList.contains("hidden")) {
          $vue?.classList.remove("hidden");
          eventBus.emit("request:data", { act: "showApp" });
          showApp.value = true;
        }
        else {
          $vue?.classList.add("hidden");
          showApp.value = false;
        }
      };
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
          !showContent.value
            ? (vue.openBlock(), vue.createElementBlock("div", {
              key: 0,
              id: "de-sidebar-badge",
              class: "bg-blue-500 text-white w-10 h-10 flex items-center justify-center cursor-pointer rounded-l-lg",
              onMouseenter: _cache[0] || (_cache[0] = ($event) => showContent.value = true),
            }, " D ", 32))
            : vue.createCommentVNode("", true),
          vue.createElementVNode("div", {
            class: vue.normalizeClass(["bg-white opacity-95 rounded-md p-1", showContent.value ? "w-48 flex flex-col" : "hidden"]),
            onMouseenter: _cache[1] || (_cache[1] = ($event) => showContent.value = true),
            onMouseleave: _cache[2] || (_cache[2] = ($event) => showContent.value = false),
          }, [
            vue.createElementVNode("button", {
              class: "p-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors",
              onClick: toggleApp,
            }, vue.toDisplayString(showApp.value ? "再次点击关闭" : "显示词条提取"), 1),
          ], 34),
        ]);
      };
    },
  });
  const VueMount = (id) => {
    if (id === "de-app") {
      !gob.$n(`#${id}`) && vue.createApp(_sfc_main$1).mount(
        (() => {
          const app = document.createElement("div");
          app.id = id;
          app.className = "de-helper fixed";
          app.classList.add("hidden");
          document.body.append(app);
          gob.$app = app;
          return app;
        })(),
      );
    }
    else if (id === "de-sidebar") {
      !gob.$n(`#${id}`) && vue.createApp(_sfc_main).mount(
        (() => {
          const sidebar = document.createElement("div");
          sidebar.id = id;
          sidebar.className = "de-helper fixed right-0 top-1/2";
          document.body.append(sidebar);
          return sidebar;
        })(),
      );
    }
  };
  const styleCss = ".de-helper,.de-helper *{box-sizing:border-box;padding:0;margin:0}.de-helper hr{border-color:#777892}#de-app{--app-right: 8rem;background-color:#1e1e2c;border-radius:1rem;color:#d1d1da;padding:1rem;position:fixed;z-index:110;left:var(--left);top:var(--top);right:var(--app-right);border:1px solid #333;transition:top .3s ease-in-out}@media(max-width:1760px){#de-app{--app-right: 6rem}}@media(max-width:1680px){#de-app{--app-right: 2rem}}@media(max-width:1600px){#de-app{top:50%;left:50%;transform:translate(-50%,-50%)}}#de-app .artist{color:var(--artist-tag-color)}#de-app .copyright{color:var(--copyright-tag-color)}#de-app .character{color:var(--character-tag-color)}#de-app .general{color:var(--general-tag-color)}#de-sidebar{position:fixed;z-index:120;right:0;top:50%}@media(min-width:1601px){#de-app.hidden{display:block!important}#de-sidebar{display:none}}";
  importCSS(styleCss);
  const fnInitializePage = () => {
    gob._init();
    if (gob._isPost()) {
      gob.vueEls.forEach((id) => {
        VueMount(id);
      });
      if (gob.$imgContainer) {
        if (gob.$imgContainer.dataset.loaded === "true") return;
        gob.$imgContainer.dataset.loaded = "true";
        gob._setAppStyle();
        window.addEventListener("scrollend", () => {
          gob._setAppStyle();
        });
      }
    }
  };
  (() => {
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", fnInitializePage);
    }
    else {
      fnInitializePage();
    }
  })();
})(Vue);