mybearworld / Cotsu-Tools

// ==UserScript==
// @name        Cotsu-Tools
// @namespace   mybearworld
// @match       *://cotsu.de/*
// @grant       GM.xmlHttpRequest
// @version     1.16.5
// @license     MIT
// @author      mybearworld
// @description Userscript für https://cotsu.de.
// @updateURL   https://openuserjs.org/meta/mybearworld/Cotsu-Tools.meta.js
// @downloadURL https://openuserjs.org/install/mybearworld/Cotsu-Tools.user.js
// ==/UserScript==
"use strict";
(() => {
  // src/lib/isSceneChange.ts
  var isSceneChange = (records) => records.some(
    (record) => record.target instanceof HTMLElement && record.target.id === "gatsby-focus-wrapper" || record.addedNodes.length > 0 && record.addedNodes[0] instanceof HTMLElement && record.addedNodes[0].id === "gatsby-focus-wrapper"
  );

  // src/lib/element.ts
  var element = (a) => {
    if (!(a instanceof HTMLElement)) throw new Error("Not an element");
    return a;
  };
  var text = (a) => {
    if (!(a instanceof Text)) throw new Error("Not an element");
    return a;
  };

  // src/lib/gmfetch.ts
  var gmfetch = (options) => {
    return new Promise((resolve, reject) => {
      GM.xmlHttpRequest({
        ...options,
        onload: (response) => {
          if (response.status < 200 || response.status > 299) {
            reject();
          }
          resolve(response.response);
        },
        onerror: () => {
          reject();
        }
      });
    });
  };

  // src/lib/wadokuInformation.ts
  var parser = new DOMParser();
  var wadokuSearchResultCache = /* @__PURE__ */ new Map();
  var wadokuInformationCache = /* @__PURE__ */ new Map();
  var TILDE = /[~〜]/g;
  var getWadokuInformation = async (kanji, reading, bulk = [kanji]) => {
    const shouldHaveEllipsis = kanji.includes("\u2026");
    kanji = kanji.replace(TILDE, "\u2026");
    reading = reading.replace(TILDE, "");
    bulk = bulk.map((s) => s.replace(TILDE, "\u2026"));
    if (reading.includes("\u30FB")) {
      const results = await Promise.all(
        reading.split("\u30FB").map(
          (individualReading) => getWadokuInformation(kanji, individualReading, bulk)
        )
      );
      const nonNullResult = results.find((result) => result !== null);
      if (nonNullResult === void 0) return null;
      return {
        pitchAccent: results.filter((information) => information !== null).map((information) => information.pitchAccent).join("\u30FB"),
        meaning: nonNullResult.meaning,
        definition: nonNullResult.definition
      };
    }
    const cacheKey = `${kanji}/${reading}`;
    const cachedInformation = wadokuInformationCache.get(cacheKey);
    if (cachedInformation) {
      if (cachedInformation instanceof Promise) {
        return await cachedInformation;
      }
      return cachedInformation;
    }
    let done;
    wadokuInformationCache.set(
      cacheKey,
      new Promise((resolve) => {
        done = resolve;
      })
    );
    const doReturn = (information) => {
      wadokuInformationCache.set(cacheKey, information);
      done(information);
      return information;
    };
    let suboptimalResult = {
      segmentedPitchAccent: null,
      noPitchAccent: null,
      hasAlternateSpellings: null,
      alternateSpelling: null,
      undesirableDefinition: null,
      ellipsis: null
    };
    try {
      const cachedSearchResponseDocument = wadokuSearchResultCache.get(kanji);
      let searchResponseDocument;
      if (cachedSearchResponseDocument) {
        searchResponseDocument = await cachedSearchResponseDocument;
      } else {
        const res = [];
        bulk.forEach((kanji2) => {
          wadokuSearchResultCache.set(
            kanji2,
            new Promise((resolve) => res.push(resolve))
          );
        });
        const searchResponse = await gmfetch({
          // ¡ is a separator character that's not used in an entry and probably
          // won't be at any point.
          url: `https://wadoku.de/search/${bulk.join("\xA1")}`,
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          data: "searchType=JAPANESE&matchType=EXACT"
        });
        searchResponseDocument = parser.parseFromString(
          searchResponse,
          "text/html"
        );
        res.forEach((resolve) => resolve(searchResponseDocument));
      }
      for (const resultLine of searchResponseDocument.querySelectorAll(
        ".resultline"
      )) {
        const readingRow = resultLine.querySelector(".accent") ?? resultLine.querySelector(".reading");
        if (!readingRow || readingRow.textContent.trim().replace(/\uffe8|・|~|…/g, "") !== reading) {
          continue;
        }
        const senses = element(
          resultLine.querySelector(".senses")?.cloneNode(true)
        );
        let isUndesirableDefinition = false;
        const fillElement = (elementToFill, currentNode) => {
          if (currentNode instanceof Text) {
            if (currentNode.textContent === " " && (elementToFill.lastChild?.textContent === " " || elementToFill.lastChild?.textContent === "\xA0")) {
              return;
            }
            elementToFill.append(currentNode.textContent);
            return;
          }
          if (!(currentNode instanceof Element))
            throw new Error("currentElement is not an element");
          if (currentNode.classList.contains("genus") || currentNode.classList.contains("season") || currentNode.matches(".transcr:has(+ .jap)") || currentNode.classList.contains("klammer") && currentNode.classList.contains("global") || currentNode.classList.contains("badge")) {
            return;
          }
          let nextElementToFillClass = null;
          let wrap = null;
          if (currentNode.classList.contains("indexnr")) {
            nextElementToFillClass = "cotsu-tools-definition-index-number";
            wrap = ["[", "]"];
          } else if (currentNode.classList.contains("reg") || currentNode.classList.contains("usage")) {
            nextElementToFillClass = "cotsu-tools-definition-context";
          } else if (currentNode.classList.contains("dom")) {
            if (currentNode.textContent === "Buchtitel" || currentNode.textContent === "weibl. Name" || currentNode.textContent === "m\xE4nnl. Name" || currentNode.textContent === "Familienn." || currentNode.textContent === "Beng\u014D") {
              isUndesirableDefinition = true;
            }
            nextElementToFillClass = "cotsu-tools-definition-context";
          } else if (currentNode.classList.contains("klammer") || currentNode.classList.contains("etym")) {
            nextElementToFillClass = "cotsu-tools-definition-paren";
          } else if (currentNode.classList.contains("descr")) {
            nextElementToFillClass = "cotsu-tools-definition-paren";
            wrap = ["(", ")"];
          } else if (currentNode.classList.contains("reflink") && !currentNode.classList.contains("other")) {
            if (elementToFill.lastChild instanceof Text && elementToFill.lastChild.textContent === "; ") {
              elementToFill.lastChild.remove();
            }
            return;
          }
          let nextElementToFill = elementToFill;
          if (nextElementToFillClass) {
            nextElementToFill = document.createElement("span");
            nextElementToFill.classList.add(nextElementToFillClass);
            elementToFill.append(nextElementToFill);
          }
          if (wrap) nextElementToFill.append(wrap[0]);
          for (const child of currentNode.childNodes) {
            fillElement(nextElementToFill, child);
          }
          if (wrap) nextElementToFill.append(wrap[1]);
        };
        const definition = document.createElement("div");
        fillElement(definition, senses);
        const information = {
          meaning: "",
          definition,
          pitchAccent: readingRow.classList.contains("accent") ? readingRow.innerHTML : null
        };
        let sense = element(resultLine.querySelector(".sense:not(.master)"));
        sense = sense.querySelector(".prior1") ?? sense;
        for (const childNode of sense.childNodes) {
          if (childNode instanceof HTMLElement) {
            if (childNode.classList.contains("rel")) {
              break;
            } else if (childNode.classList.contains("token")) {
              information.meaning += childNode.firstChild?.textContent;
            } else if (childNode.classList.contains("def")) {
              information.meaning += childNode.textContent;
            }
          } else if (childNode.nodeType === Node.TEXT_NODE) {
            information.meaning += childNode.textContent;
          }
        }
        information.meaning = information.meaning.trim().replace(/\.$/, "");
        const orth = element(resultLine.querySelector(".orth")).textContent.split(
          "\uFF1B"
        );
        if (isUndesirableDefinition) {
          suboptimalResult.undesirableDefinition = information;
        } else if (orth[0] !== kanji) {
          suboptimalResult.alternateSpelling ??= information;
        } else if (!shouldHaveEllipsis && information.pitchAccent?.includes("\u2026")) {
          suboptimalResult.ellipsis ??= information;
        } else if (information.pitchAccent?.includes("\uFF65")) {
          suboptimalResult.segmentedPitchAccent ??= information;
        } else if (information.pitchAccent === null) {
          suboptimalResult.noPitchAccent ??= information;
        } else if (orth.length !== 1) {
          suboptimalResult.hasAlternateSpellings ??= information;
        } else {
          return doReturn(information);
        }
      }
    } catch (e) {
      console.error(e);
      return doReturn(null);
    }
    return doReturn(
      suboptimalResult.hasAlternateSpellings ?? suboptimalResult.alternateSpelling ?? suboptimalResult.ellipsis ?? suboptimalResult.segmentedPitchAccent ?? suboptimalResult.undesirableDefinition ?? suboptimalResult.noPitchAccent
    );
  };
  var pitchAccentElement = (kanji, reading, bulk) => {
    const pitchAccentElement2 = document.createElement("span");
    pitchAccentElement2.classList.add(
      "cotsu-tools-pitch-accent",
      "cotsu-tools-pitch-accent-loading"
    );
    pitchAccentElement2.textContent = reading;
    getWadokuInformation(kanji, reading, bulk).then((information) => {
      pitchAccentElement2.classList.remove("cotsu-tools-pitch-accent-loading");
      if (information?.pitchAccent) {
        pitchAccentElement2.innerHTML = information.pitchAccent.replace(/class/g, "data-cotsu-tools-pitch-accent-segment").replace(/…/g, "\u301C");
      } else {
        pitchAccentElement2.append(" (kein Pitch-Accent verf\xFCgbar)");
      }
    });
    return pitchAccentElement2;
  };
  var meaningElement = (kanji, reading, bulk) => {
    const meaningElement2 = document.createElement("span");
    meaningElement2.classList.add("cotsu-tools-meaning");
    meaningElement2.textContent = "l\xE4dt...";
    getWadokuInformation(kanji, reading, bulk).then((information) => {
      if (information?.meaning) {
        meaningElement2.innerHTML = `${information.meaning} (Wadoku)`;
      } else {
        meaningElement2.textContent = "(keine Bedeutung verf\xFCgbar)";
      }
    });
    return meaningElement2;
  };
  var definitionElement = (kanji, reading, options) => {
    const wrapperElement = document.createElement("div");
    wrapperElement.classList.add("cotsu-tools-definition");
    const headerElement = document.createElement("div");
    headerElement.classList.add("cotsu-tools-definition-header");
    headerElement.textContent = "Definition von Wadoku";
    wrapperElement.append(headerElement);
    const definitionElement2 = document.createElement("div");
    if (options?.collapsed) {
      definitionElement2.classList.add("cotsu-tools-definition-collapsed");
    }
    definitionElement2.textContent = "l\xE4dt...";
    wrapperElement.append(definitionElement2);
    getWadokuInformation(kanji, reading, options?.bulk).then((information) => {
      definitionElement2.replaceChildren(
        information?.definition ?? "keine Definition verf\xFCgbar"
      );
      if (options?.collapsed) {
        let currentButton = null;
        let hasClickedButton = false;
        const resizeListener = () => {
          if (definitionElement2.scrollHeight === definitionElement2.clientHeight) {
            if (!currentButton) return;
            currentButton.remove();
            currentButton = null;
          } else {
            if (currentButton || hasClickedButton) return;
            currentButton = document.createElement("button");
            currentButton.textContent = "volle Definition anzeigen";
            currentButton.addEventListener("click", () => {
              definitionElement2.classList.remove(
                "cotsu-tools-definition-collapsed"
              );
              if (currentButton) currentButton.remove();
              currentButton = null;
              hasClickedButton = true;
            });
            headerElement.append(currentButton);
          }
        };
        window.addEventListener("resize", resizeListener);
        resizeListener();
      }
    });
    return wrapperElement;
  };

  // src/lib/settings.ts
  var SETTINGS = {
    "cotsu-tools-katakana-mode": {
      name: "Katakana statt Hiragana bei \xDCbungen nutzen",
      acknowledgeable: false,
      effect: (newSetting) => {
        document.body.classList.toggle("cotsu-tools-katakana-mode", newSetting);
      }
    },
    "cotsu-tools-shuffle-summary": {
      name: "Am Ende einer \xDCbung die Vokabel\xFCbersicht mischen",
      acknowledgeable: false,
      effect: (newSetting) => {
        document.body.classList.toggle("cotsu-tools-shuffle-summary", newSetting);
      }
    },
    "cotsu-tools-hide-question-maturity": {
      name: "Indikator des Fortschritts einer Vokabel w\xE4hrend einer \xDCbung verstecken",
      acknowledgeable: false,
      effect: (newSetting) => {
        document.body.classList.toggle(
          "cotsu-tools-hide-question-maturity",
          newSetting
        );
      }
    },
    "cotsu-tools-cursive-font": {
      name: "Handschriftliche Schriftart f\xFCr japanischen Text verwenden (Yuji Syuku)",
      acknowledgeable: true,
      effect: (newSetting) => {
        document.body.classList.toggle("cotsu-tools-cursive-font", newSetting);
      }
    }
  };
  var SETTING_IDS = Object.keys(SETTINGS);
  var STORAGE_KEY = "cotsu-tools";
  var ACKNOWLEDGED_STORAGE_KEY = "cotsu-tools-acknowledged";
  var loadedSettingsString = localStorage.getItem(STORAGE_KEY);
  var loadedAcknowledgedString = localStorage.getItem(ACKNOWLEDGED_STORAGE_KEY);
  var settings;
  if (loadedSettingsString) {
    settings = JSON.parse(loadedSettingsString);
  } else {
    settings = Object.fromEntries(SETTING_IDS.map((id) => [id, false]));
    localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
  }
  SETTING_IDS.forEach((id) => {
    if (id in settings) {
      SETTINGS[id].effect(settings[id]);
    } else {
      settings[id] = false;
      SETTINGS[id].effect(false);
    }
  });
  var acknowledged;
  if (loadedAcknowledgedString) {
    acknowledged = JSON.parse(loadedAcknowledgedString);
  } else {
    if (loadedSettingsString) {
      acknowledged = SETTING_IDS.filter((id) => !SETTINGS[id].acknowledgeable);
    } else {
      acknowledged = SETTING_IDS;
    }
    localStorage.setItem(ACKNOWLEDGED_STORAGE_KEY, JSON.stringify(acknowledged));
  }
  var getSetting = (id) => settings[id];
  var settingName = (id) => SETTINGS[id].name;
  var setSetting = (id, value) => {
    settings[id] = value;
    SETTINGS[id].effect(value);
    localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
  };
  var doAcknowledgeableSettingsExist = () => SETTING_IDS.some(
    (id) => SETTINGS[id].acknowledgeable && !acknowledged.includes(id)
  );
  var isAcknowledged = (id) => acknowledged.includes(id);
  var acknowledgeSettings = () => {
    acknowledged = SETTING_IDS;
    localStorage.setItem(ACKNOWLEDGED_STORAGE_KEY, JSON.stringify(acknowledged));
  };

  // src/actions/handleSettings.ts
  var createDialog = () => {
    const dialog = document.createElement("dialog");
    dialog.classList.add("cotsu-tools-dialog");
    const closeDialog = () => {
      dialog.close();
      dialog.querySelectorAll(".cotsu-tools-new-badge").forEach((badge) => {
        badge.remove();
      });
    };
    const dialogWrapper = document.createElement("div");
    dialogWrapper.classList.add("cotsu-tools-dialog-wrapper");
    dialog.addEventListener("mousedown", (e) => {
      if (!element(e.target).closest(".cotsu-tools-dialog-wrapper")) {
        closeDialog();
      }
    });
    dialog.append(dialogWrapper);
    const verisonHeader = document.createElement("header");
    const leftHeader = document.createElement("div");
    const h2 = document.createElement("h2");
    h2.textContent = "Cotsu-Tools";
    leftHeader.append(h2);
    const version = document.createElement("span");
    version.textContent = `v${GM.info.script.version}`;
    leftHeader.append(version);
    verisonHeader.append(leftHeader);
    const rightHeader = document.createElement("div");
    const link = document.createElement("a");
    link.classList.add("cotsu-tools-dialog-link");
    link.textContent = "\u2197";
    link.href = "https://openuserjs.org/scripts/mybearworld/Cotsu-Tools";
    link.target = "_blank";
    rightHeader.append(link);
    verisonHeader.append(rightHeader);
    dialogWrapper.append(verisonHeader);
    const settingsHeader = document.createElement("h3");
    settingsHeader.textContent = "Einstellungen";
    dialogWrapper.append(settingsHeader);
    SETTING_IDS.forEach((id) => {
      const label = document.createElement("label");
      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      checkbox.checked = getSetting(id);
      checkbox.addEventListener("input", () => {
        setSetting(id, checkbox.checked);
      });
      label.append(checkbox);
      const description = document.createElement("span");
      description.textContent = settingName(id);
      if (!isAcknowledged(id)) {
        const newBadge = document.createElement("span");
        newBadge.textContent = "Neu";
        newBadge.classList.add(
          "cotsu-tools-new-badge",
          "cotsu-tools-new-badge-dialog"
        );
        description.append(newBadge);
      }
      label.append(description);
      dialogWrapper.append(label);
    });
    const debuggingDetails = document.createElement("details");
    const debuggingSummary = document.createElement("summary");
    const debuggingHeader = document.createElement("h3");
    debuggingHeader.textContent = "Debugging";
    debuggingSummary.append(debuggingHeader);
    debuggingDetails.append(debuggingSummary);
    const debuggingWadokuKanjiInput = document.createElement("input");
    debuggingWadokuKanjiInput.placeholder = "Kanji";
    const debuggingWadokuReadingInput = document.createElement("input");
    debuggingWadokuReadingInput.placeholder = "Lesung";
    const debuggingWadokuButton = document.createElement("button");
    debuggingWadokuButton.textContent = "Wadoku-Informationen erhalten";
    const debuggingWadoku = document.createElement("div");
    debuggingWadokuButton.addEventListener("click", () => {
      debuggingWadoku.innerHTML = "";
      const kanji = debuggingWadokuKanjiInput.value;
      const reading = debuggingWadokuReadingInput.value;
      debuggingWadoku.append(pitchAccentElement(kanji, reading));
      debuggingWadoku.append(definitionElement(kanji, reading));
    });
    debuggingDetails.append(
      debuggingWadokuKanjiInput,
      debuggingWadokuReadingInput,
      debuggingWadokuButton,
      debuggingWadoku
    );
    dialogWrapper.append(debuggingDetails);
    const closeButton = document.createElement("button");
    closeButton.textContent = "Schlie\xDFen";
    closeButton.addEventListener("click", () => {
      closeDialog();
    });
    dialogWrapper.append(closeButton);
    document.body.append(dialog);
    return dialog;
  };
  var handleSettings = async (records) => {
    if (!isSceneChange(records) || document.querySelector("cotsu-tools-button"))
      return;
    const header = document.querySelector(
      "[class^=MainLayout-module--header--] .MuiGrid-root"
    );
    if (!header) return;
    let dialog = document.querySelector(".cotsu-tools-dialog") || createDialog();
    if (!(dialog instanceof HTMLDialogElement))
      throw new Error("Settings aren't a dialog");
    const settingsButton = document.createElement("button");
    settingsButton.classList.add("cotsu-tools-button");
    settingsButton.textContent = "Cotsu-Tools";
    let newBadge = null;
    if (doAcknowledgeableSettingsExist()) {
      newBadge = document.createElement("span");
      newBadge.textContent = "Neu";
      newBadge.classList.add(
        "cotsu-tools-new-badge",
        "cotsu-tools-new-badge-nav-bar"
      );
      settingsButton.append(newBadge);
    }
    settingsButton.addEventListener("click", () => {
      dialog.showModal();
      acknowledgeSettings();
      newBadge?.remove();
    });
    element(header.lastChild).insertAdjacentElement("afterbegin", settingsButton);
  };

  // src/lib/interceptedFetch.ts
  var stats;
  var readingExercise;
  var didntKnowQids = /* @__PURE__ */ new Set();
  var DUMMY_QUESTION_ID = "cotsu-tools-dummy-question";
  var authorization;
  var _fetch = unsafeWindow.fetch;
  var startInterceptingFetch = () => {
    unsafeWindow.fetch = async (url, options) => {
      if (options?.headers && "Authorization" in options?.headers) {
        authorization = options.headers.Authorization;
      }
      let requestBody = options?.body;
      if (typeof requestBody === "string" && url === "https://api.cotsu.de/user.php?r=update-progress") {
        const parsedRequestBody = JSON.parse(requestBody);
        parsedRequestBody.answers = parsedRequestBody.answers.flatMap(
          (answer) => answer.qid === DUMMY_QUESTION_ID ? [] : {
            qid: answer.qid,
            correct: didntKnowQids.has(answer.qid) ? 0 : answer.correct
          }
        );
        requestBody = JSON.stringify(parsedRequestBody);
      }
      const response = await _fetch(url, { ...options, body: requestBody });
      let body = await response.text();
      if (typeof url === "string") {
        if (url === "https://api.cotsu.de/user.php?r=stats") {
          stats = JSON.parse(body);
        } else if (url.startsWith("https://api.cotsu.de/user.php?r=review-reading") || url.startsWith("https://api.cotsu.de/user.php?r=learn-reading")) {
          const parsedBody = JSON.parse(body);
          if (parsedBody.questions.length !== 0) {
            parsedBody.questionCount = parsedBody.questions.length;
            parsedBody.questions.push({
              writing: "",
              reading: "",
              german: "",
              sentence: "Das war's!",
              sentence_german: "",
              qtype: "3",
              qid: DUMMY_QUESTION_ID,
              kanjis: []
            });
            readingExercise = parsedBody;
            didntKnowQids = /* @__PURE__ */ new Set();
          }
          body = JSON.stringify(parsedBody);
        }
      }
      return new Response(body, response);
    };
  };

  // src/lib/kanjiSearch.ts
  var kanjiSearch = async (kanji) => {
    const response = await gmfetch({
      url: `https://api.cotsu.de/user.php?r=search&search=${kanji}`,
      method: "GET",
      headers: {
        Authorization: authorization
      }
    });
    return [
      ...JSON.parse(response).words,
      ...JSON.parse(response).kanjis
    ];
  };

  // src/lib/katakanaToHiragana.ts
  var KATAKANA_TO_HIRAGANA = { "\u30A1": "\u3041", "\u30A2": "\u3042", "\u30A3": "\u3043", "\u30A4": "\u3044", "\u30A5": "\u3045", "\u30A6": "\u3046", "\u30A7": "\u3047", "\u30A8": "\u3048", "\u30A9": "\u3049", "\u30AA": "\u304A", "\u30AB": "\u304B", "\u30AC": "\u304C", "\u30AD": "\u304D", "\u30AE": "\u304E", "\u30AF": "\u304F", "\u30B0": "\u3050", "\u30B1": "\u3051", "\u30B2": "\u3052", "\u30B3": "\u3053", "\u30B4": "\u3054", "\u30B5": "\u3055", "\u30B6": "\u3056", "\u30B7": "\u3057", "\u30B8": "\u3058", "\u30B9": "\u3059", "\u30BA": "\u305A", "\u30BB": "\u305B", "\u30BC": "\u305C", "\u30BD": "\u305D", "\u30BE": "\u305E", "\u30BF": "\u305F", "\u30C0": "\u3060", "\u30C1": "\u3061", "\u30C2": "\u3062", "\u30C3": "\u3063", "\u30C4": "\u3064", "\u30C5": "\u3065", "\u30C6": "\u3066", "\u30C7": "\u3067", "\u30C8": "\u3068", "\u30C9": "\u3069", "\u30CA": "\u306A", "\u30CB": "\u306B", "\u30CC": "\u306C", "\u30CD": "\u306D", "\u30CE": "\u306E", "\u30CF": "\u306F", "\u30D0": "\u3070", "\u30D1": "\u3071", "\u30D2": "\u3072", "\u30D3": "\u3073", "\u30D4": "\u3074", "\u30D5": "\u3075", "\u30D6": "\u3076", "\u30D7": "\u3077", "\u30D8": "\u3078", "\u30D9": "\u3079", "\u30DA": "\u307A", "\u30DB": "\u307B", "\u30DC": "\u307C", "\u30DD": "\u307D", "\u30DE": "\u307E", "\u30DF": "\u307F", "\u30E0": "\u3080", "\u30E1": "\u3081", "\u30E2": "\u3082", "\u30E3": "\u3083", "\u30E4": "\u3084", "\u30E5": "\u3085", "\u30E6": "\u3086", "\u30E7": "\u3087", "\u30E8": "\u3088", "\u30E9": "\u3089", "\u30EA": "\u308A", "\u30EB": "\u308B", "\u30EC": "\u308C", "\u30ED": "\u308D", "\u30EE": "\u308E", "\u30EF": "\u308F", "\u30F0": "\u3090", "\u30F1": "\u3091", "\u30F2": "\u3092", "\u30F3": "\u3093", "\u30F4": "\u3094", "\u30F5": "\u3095", "\u30F6": "\u3096" };
  var katakanaToHiragana = (kana) => [...kana].map(
    (kana2) => kana2 in KATAKANA_TO_HIRAGANA ? KATAKANA_TO_HIRAGANA[kana2] : kana2
  ).join("");

  // src/actions/handleExerciseWords.ts
  var previousReadingExercise = null;
  var handleExerciseWords = async (records) => {
    for (const record of records) {
      handleUpdatedWord(record);
      handleUpdatedCardWord(record);
      handleWrongAnswer(record);
      handleSummary(record);
    }
  };
  var currentQuestionId = () => {
    const studyProgressTextElement = element(
      document.querySelector("[class^=StudyProgress-module--study-progress--] p")
    );
    return Number(studyProgressTextElement.childNodes.item(1)?.textContent) - 1;
  };
  var handleUpdatedWord = (record) => {
    const firstAddedNode = record.addedNodes[0];
    if ((record.type !== "characterData" || record.target.parentElement?.tagName !== "P" || !record.target.parentElement?.parentElement?.className.startsWith(
      "StudyProgress-module--study-progress--"
    )) && (record.type !== "childList" || !(firstAddedNode instanceof HTMLElement) || !firstAddedNode.className.startsWith(
      "StudyProgress-module--study-progress--"
    )))
      return;
    const id = currentQuestionId();
    if (previousReadingExercise !== readingExercise) {
      previousReadingExercise = readingExercise;
    }
    const currentQuestion = readingExercise.questions[id];
    element(
      document.querySelector(
        "[class^=StudyProgress-module--study-progress--] .MuiLinearProgress-bar"
      )
    ).style.transform = `translateX(${-100 * (1 - (id + 1) / readingExercise.questionCount)}%)`;
    if (currentQuestion.qid === DUMMY_QUESTION_ID) {
      const questionContainer = element(
        document.querySelector(
          "[class^=QuestionContainer-module--question-container--]"
        )
      );
      questionContainer.classList.add("cotsu-tools-dummy-question");
      element(
        document.querySelector(
          "[class*=ReadingQuestionCard-module--action-check--]"
        )
      ).textContent = "Zum Ende";
    } else {
      void getWadokuInformation(currentQuestion.writing, currentQuestion.reading);
    }
  };
  var handleUpdatedCardWord = (record) => {
    const firstAddedNode = record.addedNodes[0];
    if ((record.type !== "childList" || !(firstAddedNode instanceof HTMLElement) || !firstAddedNode.className?.startsWith("StudyContainer")) && (record.type !== "characterData" || !record.target.parentElement?.classList?.contains("card-word") || record.target.nextSibling?.textContent !== " \u2192 "))
      return;
    const cardWord = record.type === "childList" ? element(firstAddedNode).querySelector(".card-word") : record.target.parentElement;
    if (!cardWord) return;
    cardWord.parentElement?.classList.remove(
      "cotsu-tools-didnt-know-word-strike"
    );
    cardWord.nextSibling?.remove();
    const cardWordClone = element(cardWord.cloneNode(true));
    cardWordClone.classList.add("cotsu-tools-card-word-clone");
    const hiraganaElement = text(
      [...cardWordClone.childNodes].find(
        (_node, i) => cardWordClone.childNodes[i - 1]?.textContent === " \u2192 "
      )
    );
    const exercise = readingExercise.questions[currentQuestionId() - 1];
    hiraganaElement.textContent = "";
    cardWordClone.append(definitionElement(exercise.writing, exercise.reading));
    hiraganaElement.after(pitchAccentElement(exercise.writing, exercise.reading));
    const wasAnsweredCorrectly = !cardWordClone.querySelector(
      "[class*=MaturityTallies-module--tally-icon_0--]"
    );
    if (wasAnsweredCorrectly) {
      const didntKnowWord = document.createElement("label");
      didntKnowWord.classList.add("cotsu-tools-didnt-know-word");
      didntKnowWord.append("Doch nicht gewusst?");
      const didntKnowWordCheckBox = document.createElement("input");
      didntKnowWordCheckBox.type = "checkbox";
      didntKnowWordCheckBox.addEventListener("input", () => {
        const qid = readingExercise.questions[currentQuestionId() - 1].qid;
        if (didntKnowWordCheckBox.checked) {
          didntKnowQids.add(qid);
        } else {
          didntKnowQids.delete(qid);
        }
        cardWordClone.parentElement?.classList.toggle(
          "cotsu-tools-didnt-know-word-strike",
          didntKnowWordCheckBox.checked
        );
      });
      didntKnowWord.append(didntKnowWordCheckBox);
      cardWordClone.insertAdjacentElement("afterbegin", didntKnowWord);
    }
    cardWord.insertAdjacentElement("afterend", cardWordClone);
    const studyCardContainer = cardWord.parentElement?.parentElement?.parentElement;
    if (!studyCardContainer) return;
    studyCardContainer.querySelectorAll("[class^=KanjiCard-module--row--]").forEach((row) => {
      const previousOtherKanjiElement = row.querySelector(
        ".cotsu-tools-other-kanji"
      );
      if (previousOtherKanjiElement) {
        previousOtherKanjiElement.remove();
      }
      const kanji = element(
        row.querySelector("[class^=KanjiCard-module--kanjiBig--]")
      ).textContent;
      const otherKanjiElement = document.createElement("div");
      otherKanjiElement.className = element(row.lastChild).className;
      otherKanjiElement.classList.add("cotsu-tools-other-kanji");
      const button = document.createElement("button");
      button.classList.add("cotsu-tools-other-kanji-init-button");
      button.textContent = "andere W\xF6rter mit " + kanji;
      button.addEventListener("click", async () => {
        button.textContent = "l\xE4dt...";
        const result = await kanjiSearch(kanji);
        const relevantBulk = [];
        const relevantWords = [];
        result.forEach((word) => {
          if (!word.maturity) return;
          if (word.word === exercise.writing) return;
          relevantBulk.push(word.word);
          relevantWords.push(word);
        });
        button.remove();
        otherKanjiElement.classList.add("cotsu-tools-other-kanji-loaded");
        const heading = document.createElement("div");
        otherKanjiElement.append(heading);
        if (relevantWords.length === 0) {
          heading.textContent = "Du kennst das Kanji noch in keinen anderen W\xF6rtern.";
          return;
        }
        heading.textContent = "Du kennst das Kanji noch in diesen W\xF6rtern:";
        const ul = document.createElement("ul");
        otherKanjiElement.append(ul);
        relevantWords.forEach((word) => {
          const li = document.createElement("li");
          const reading = katakanaToHiragana(word.reading);
          const kanji2 = document.createElement("span");
          kanji2.classList.add("cotsu-tools-other-kanji-kanji");
          kanji2.append(word.word);
          li.append(kanji2);
          const moreButton = document.createElement("button");
          moreButton.classList.add("cotsu-tools-other-kanji-more");
          moreButton.textContent = "?";
          moreButton.addEventListener("click", () => {
            moreButton.remove();
            li.append(
              " (",
              pitchAccentElement(word.word, reading),
              ") ",
              word.word_de ?? meaningElement(word.word, reading)
            );
          });
          li.append(" ", moreButton);
          ul.append(li);
        });
      });
      otherKanjiElement.append(button);
      row.append(otherKanjiElement);
    });
  };
  var handleWrongAnswer = (record) => {
    const firstAddedNode = record.addedNodes[0];
    if (!(firstAddedNode instanceof HTMLElement) || !firstAddedNode.className?.startsWith(
      "ReadingQuestionCard-module--wrong-answer--"
    ))
      return;
    const currentQuestion = readingExercise.questions[currentQuestionId()];
    firstAddedNode.innerHTML = "";
    firstAddedNode.append(
      "Die Antwort ist ",
      pitchAccentElement(currentQuestion.writing, currentQuestion.reading)
    );
  };
  var handleSummary = (record) => {
    const firstAddedNode = record.addedNodes[0];
    if (!(firstAddedNode instanceof HTMLElement) || !firstAddedNode.className.startsWith("SummaryCard-module--summary-card--"))
      return;
    if (readingExercise.questions.length === 0) return;
    element(
      document.querySelector(
        "[class^=QuestionContainer-module--question-container--]"
      )
    ).classList.remove("cotsu-tools-dummy-question");
    const summaryCorrectText = element(
      document.querySelector("[class^=SummaryCard-module--summary-text--] p")
    );
    const summaryCorrectTextMatch = summaryCorrectText.textContent.match(
      /^(\d+) von (\d+) richtig$/
    );
    if (!summaryCorrectTextMatch) throw new Error("No X von Y richtig");
    const [, correctExercises, totalExercises] = summaryCorrectTextMatch;
    summaryCorrectText.textContent = `${Number(correctExercises) - 1} von ${Number(totalExercises) - 1} richtig`;
    const wordSummary = document.createElement("div");
    const incorrectKanjiHeading = document.createElement("h4");
    incorrectKanjiHeading.textContent = "Falsch waren:";
    const incorrectKanjiUnshuffled = document.createElement("div");
    incorrectKanjiUnshuffled.classList.add("cotsu-tools-summary-unshuffled");
    const incorrectKanjiShuffled = document.createElement("div");
    incorrectKanjiShuffled.classList.add("cotsu-tools-summary-shuffled");
    const correctKanjiHeading = document.createElement("h4");
    correctKanjiHeading.textContent = "Richtig waren:";
    const correctKanjiUnshuffled = document.createElement("div");
    correctKanjiUnshuffled.classList.add("cotsu-tools-summary-unshuffled");
    const correctKanjiShuffled = document.createElement("div");
    correctKanjiShuffled.classList.add("cotsu-tools-summary-shuffled");
    wordSummary.append(
      incorrectKanjiHeading,
      incorrectKanjiUnshuffled,
      incorrectKanjiShuffled,
      correctKanjiHeading,
      correctKanjiUnshuffled,
      correctKanjiShuffled
    );
    wordSummary.classList.add("word-summary");
    const wrongKanji = /* @__PURE__ */ new Set();
    const originalSummaryElement = firstAddedNode.querySelector("div:not([class])");
    originalSummaryElement?.childNodes?.forEach((word) => {
      const match = word.textContent?.match(/([^ ]+) → ([^ ]+)/);
      if (!match) throw new Error("No XYZ \u2192 ABC in summary element");
      const [, kanji, reading] = match;
      wrongKanji.add(`${kanji}/${reading}`);
    });
    readingExercise.questions.forEach(({ writing: kanji, reading, qid }) => {
      if (qid === DUMMY_QUESTION_ID) return;
      const spoilerWrap = (toWrap) => {
        const spoilerElement = document.createElement("button");
        spoilerElement.classList.add("cotsu-tools-spoiler");
        spoilerElement.ariaLabel = "L\xF6sung anzeigen";
        const accessibilityWrapper = document.createElement("span");
        accessibilityWrapper.ariaHidden = "true";
        accessibilityWrapper.append(toWrap);
        spoilerElement.append(accessibilityWrapper);
        spoilerElement.addEventListener("click", () => {
          spoilerElement.classList.add("cotsu-tools-spoiler-shown");
          spoilerElement.ariaLabel = null;
          spoilerElement.role = "none";
          spoilerElement.disabled = true;
          accessibilityWrapper.ariaHidden = "false";
        });
        return spoilerElement;
      };
      const isIncorrect = wrongKanji.has(`${kanji}/${reading}`) || didntKnowQids.has(qid);
      const makeRow = () => {
        const row = document.createElement("div");
        const word = document.createElement("span");
        word.lang = "ja";
        word.textContent = kanji;
        const solution = document.createElement("span");
        solution.append(
          pitchAccentElement(kanji, reading),
          " ",
          readingExercise.questions.find(
            (question) => question.writing === kanji && question.reading === reading
          )?.german ?? meaningElement(kanji, reading)
        );
        row.append(word, " \u2192 ", isIncorrect ? spoilerWrap(solution) : solution);
        return row;
      };
      if (isIncorrect) {
        incorrectKanjiUnshuffled.append(makeRow());
        incorrectKanjiShuffled.append(makeRow());
      } else {
        correctKanjiUnshuffled.append(makeRow());
        correctKanjiShuffled.append(makeRow());
      }
    });
    const shuffle = (container) => {
      const children = [...container.children];
      let remaining = children.length;
      while (remaining) {
        const i = Math.floor(Math.random() * remaining);
        remaining--;
        [children[remaining], children[i]] = [children[i], children[remaining]];
      }
      container.innerHTML = "";
      children.forEach((child) => {
        container.append(child);
      });
    };
    shuffle(correctKanjiShuffled);
    shuffle(incorrectKanjiShuffled);
    if (originalSummaryElement) {
      element(document.querySelector("h3")).textContent = "Hier nochmal alle W\xF6rter";
      originalSummaryElement.replaceWith(wordSummary);
    } else {
      const actions = element(
        document.querySelector("[class^=SummaryCard-module--summary-actions--]")
      );
      const h3 = document.createElement("h3");
      h3.textContent = "Hier nochmal alle W\xF6rter";
      actions.append(h3);
      actions.append(wordSummary);
    }
    const summaryText = element(
      document.querySelector("[class^=SummaryCard-module--summary-text--] p")
    );
    const summaryTextMatch = summaryText.textContent.match(/^(\d+)(.*)$/);
    if (!summaryTextMatch) throw new Error("No summaryTextMatch");
    summaryText.textContent = (Number(summaryTextMatch[1]) - didntKnowQids.size).toString() + summaryTextMatch[2];
  };

  // src/lib/levels.ts
  var LEVELS = ["N5", "N4", "N3", "N2", "N1"];
  var LOWERCASE_LEVELS = {
    N5: "n5",
    N4: "n4",
    N3: "n3",
    N2: "n2",
    N1: "n1"
  };

  // src/actions/showMoreStats.ts
  var percent = (a, b) => `${Math.round(a / b * 100)}%`;
  var showMoreStats = async (records) => {
    if (!records.some(
      (record) => record.target instanceof HTMLElement && record.target.className?.includes("MaturityTallies")
    ))
      return;
    const practiceCard = document.querySelector(
      "div[class*=index-module--kanji-read-actions--] .MuiGrid-item:first-of-type  [class*=index-module--action-card-normal--]"
    );
    if (practiceCard && stats.readyForEarlyReview !== "0" && stats.totalLearned !== 0) {
      text(
        practiceCard.querySelector("[class^=index-module--action-card-text--]")?.lastChild
      ).textContent = `Du kannst aber trotzdem noch ${Math.ceil(Number(stats.readyForEarlyReview) / 20)} mal \xFCben.`;
    }
    LEVELS.forEach((level) => {
      const levelStats = stats.progress[LOWERCASE_LEVELS[level]];
      const progressBarElement = element(
        document.querySelector(`.MuiLinearProgress-root[label=${level}]`)?.parentElement
      );
      const levelNameElement = element(
        progressBarElement.previousSibling?.firstChild
      );
      const progressElement = element(progressBarElement.nextSibling?.firstChild);
      levelNameElement.textContent += ` (${levelStats.total})`;
      progressElement.textContent = `${percent(
        levelStats.learning,
        levelStats.total
      )} (${levelStats.learning})`;
      if (levelStats.mature !== 0) {
        const progressContainer = element(progressElement.parentElement);
        const matureProgressContainer = element(
          progressContainer.cloneNode(true)
        );
        const matureProgress = element(matureProgressContainer.firstChild);
        matureProgress.classList.add("cotsu-tools-detail-green");
        matureProgress.textContent = `${percent(
          levelStats.mature,
          levelStats.total
        )} (${levelStats.mature})`;
        element(progressContainer.parentElement).append(matureProgressContainer);
      }
    });
  };

  // src/actions/showExercisesLeftInKanjiLearningTab.ts
  var showExercisesLeftInKanjiLearningTab = async (records) => {
    if (!stats || !isSceneChange(records)) return;
    document.querySelectorAll(".MuiSlider-markLabel").forEach((label) => {
      const level = label.textContent.split(" ")[0];
      if (!LEVELS.includes(level)) return;
      const levelStats = stats.progress[LOWERCASE_LEVELS[level]];
      const br = document.createElement("br");
      if (levelStats.total !== levelStats.learning) {
        label.append(
          br,
          `noch ${Math.ceil((levelStats.total - levelStats.learning) / 20)}`
        );
      }
    });
  };

  // src/actions/fixFontInNewKanjiTab.ts
  var fixFontInNewKanjiTab = async (records) => {
    if (!isSceneChange(records)) return;
    const search = document.querySelector("[class^=suche-module--search-field--");
    if (!search) return;
    search.setAttribute("lang", "ja");
  };

  // src/actions/handleKanjiTab.ts
  var handleKanjiTab = async (records) => {
    const items = [];
    for (const record of records) {
      const firstAddedNode = record.addedNodes[0];
      if (record.target instanceof HTMLElement && record.target.parentElement?.parentElement?.className.startsWith(
        "suche-module--container--"
      ) && record.target.classList.contains("MuiList-root") && firstAddedNode instanceof HTMLElement) {
        const text2 = element(
          firstAddedNode.querySelector(
            ".MuiListItemText-root .MuiTypography-root"
          )
        );
        if (text2.dataset.cotsuToolsAddedWadokuInformation === "true") {
          continue;
        }
        const match = text2.textContent.match(/^(.+?)((.+?)) (.+)?$/);
        if (!match) throw new Error("Unexpected text format");
        const [, kanji, kana, german] = match;
        const reading = katakanaToHiragana(kana);
        items.push({ kanji, reading, german, element: text2 });
      }
    }
    if (items.length === 0) return;
    const bulk = items.map(({ kanji }) => kanji);
    items.forEach(({ element: element2, kanji, reading, german }) => {
      element2.innerHTML = "";
      element2.dataset.cotsuToolsAddedWadokuInformation = "true";
      element2.append(
        kanji,
        "\uFF08",
        pitchAccentElement(kanji, reading, bulk),
        "\uFF09 ",
        german || meaningElement(kanji, reading, bulk)
      );
    });
  };

  // src/lib/style.css
  var style_default = `/* General */
@font-face {
  font-family: "Noto Sans JP All Katakana";
  src: url("https://mybearworld.github.io/japanese-fonts/out/NotoSansJPAllKatakana.ttf");
}
@font-face {
  font-family: "Yuji Syuku";
  src: url("https://mybearworld.github.io/japanese-fonts/src/YujiSyuku-Regular.ttf");
  size-adjust: 120%;
  font-weight: 400;
  unicode-range: U+A5, U+4E00-9FFF, U+30??, U+FF10-FF19, U+FF66-FF9F;
}
@font-face {
  font-family: "Yuji Syuku All Katakana";
  src: url("https://mybearworld.github.io/japanese-fonts/out/YujiSyukuAllKatakana.ttf");
  size-adjust: 120%;
  font-weight: 400;
  unicode-range: U+A5, U+4E00-9FFF, U+30??, U+FF10-FF19, U+FF66-FF9F;
}
input {
  font-weight: 400 !important;
}
body {
  --cotsu-tools-default-font: Noto Sans JP, Noto Sans Japanese, sans-serif;
  --cotsu-tools-font: var(--cotsu-tools-default-font);
}
body.cotsu-tools-cursive-font {
  --cotsu-tools-font: Yuji Syuku, var(--cotsu-tools-default-font);
}
a,
body,
button,
p,
span,
input {
  font-family: var(--cotsu-tools-font) !important;
}

/* Dialog */
[class^="MainLayout-module--header--"] .MuiGrid-container .MuiGrid-item {
  flex-basis: initial;
  max-width: initial;
}
[class^="MainLayout-module--header--"]
  .MuiGrid-container
  .MuiGrid-item:last-child {
  flex-grow: 1;
}
.cotsu-tools-button {
  font: inherit;
  border: none;
  background: none;
  color: #fff;
  padding: 0;
  margin-right: 20px;
  height: 60px;
}
.cotsu-tools-button:hover {
  cursor: pointer;
  color: #ffdc79;
}
body:has(.cotsu-tools-dialog:open) {
  overflow: hidden;
}
.cotsu-tools-new-badge {
  background-color: #ff4e71;
  color: white;
  font-weight: bold;
  padding: 0 0.5rem;
  border-radius: 999px;
}
.cotsu-tools-new-badge.cotsu-tools-new-badge-nav-bar {
  background-color: white;
  color: #ff4e71;
  margin-left: 0.5rem;
}
.cotsu-tools-new-badge.cotsu-tools-new-badge-dialog {
  margin-left: 0.25rem;
}
.cotsu-tools-dialog {
  border: 1px solid #f6f6f6;
  border-radius: 20px;
  width: min(calc(100% - 2rem), 30rem);
  max-width: none;
  box-sizing: border-box;
  padding: 0;
  margin-top: max(auto, 1rem);
  max-height: 100vh;
}
.cotsu-tools-dialog-wrapper {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem;
  max-height: calc(100vh - 4rem);
}
.cotsu-tools-dialog header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.cotsu-tools-dialog header > div {
  display: flex;
  align-items: end;
  gap: 0.5rem;
}
.cotsu-tools-dialog h2 {
  margin: 0;
  font-family: inherit;
}
.cotsu-tools-dialog h3 {
  font-size: 1rem;
  font-weight: 700;
  margin: 0;
  margin-top: 0.5rem;
}
.cotsu-tools-dialog summary {
  margin-top: 0.5rem;
}
.cotsu-tools-dialog summary h3 {
  margin-top: 0;
  display: inline-block;
}
.cotsu-tools-dialog header span {
  font-size: 1rem;
  font-weight: 700;
}
.cotsu-tools-dialog-link {
  font-size: 1.5rem;
  font-weight: 700;
}
.cotsu-tools-dialog label {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
}
.cotsu-tools-dialog input {
  margin: 0;
}
.cotsu-tools-dialog button {
  align-self: end;
}

/* Pitch accent */
.cotsu-tools-pitch-accent {
  display: inline-table;
  border-collapse: collapse;
}
.cotsu-tools-pitch-accent,
.cotsu-tools-pitch-accent span {
  font-weight: 400;
}
.cotsu-tools-pitch-accent span {
  border: 0px solid currentcolor;
  --border-width: 1px;
  display: table-cell;
  padding: 0 3px;
}
.cotsu-tools-pitch-accent.cotsu-tools-pitch-accent-loading {
  letter-spacing: 3px;
  opacity: 0.5;
  margin-top: var(--border-width);
}
.card-header .cotsu-tools-pitch-accent,
.card-header .cotsu-tools-pitch-accent span {
  --border-width: 2px;
  font-weight: 700;
}
.cotsu-tools-pitch-accent span:empty {
  display: none;
}
.cotsu-tools-pitch-accent [data-cotsu-tools-pitch-accent-segment~="divider"] {
  display: none;
}
.cotsu-tools-pitch-accent [data-cotsu-tools-pitch-accent-segment~="b"] {
  border-bottom-width: var(--border-width);
}
.cotsu-tools-pitch-accent [data-cotsu-tools-pitch-accent-segment~="t"] {
  border-top-width: var(--border-width);
}
.cotsu-tools-pitch-accent [data-cotsu-tools-pitch-accent-segment~="l"] {
  border-left-width: var(--border-width);
}
.cotsu-tools-pitch-accent [data-cotsu-tools-pitch-accent-segment~="r"] {
  border-right-width: var(--border-width);
}
div.card-word {
  height: auto;
  min-height: 29px;
  display: none;
}
div.card-word.cotsu-tools-card-word-clone {
  display: block;
}

/* Meaning */
.cotsu-tools-meaning {
  padding: 0;
  border: 0;
  background-color: transparent;
  font-size: inherit;
  font-weight: inherit;
}
.cotsu-tools-meaning,
.cotsu-tools-meaning-summary,
.cotsu-tools-meaning-definition {
  font-family: inherit;
}
.cotsu-tools-meaning-definition {
  display: none;
}
.cotsu-tools-meaning.cotsu-tools-meaning-shown-definition
  .cotsu-tools-meaning-summary {
  display: none;
}
.cotsu-tools-meaning.cotsu-tools-meaning-shown-definition
  .cotsu-tools-meaning-definition {
  display: inline-block;
}

/* Definition */
.cotsu-tools-definition {
  font-weight: 300;
}
.cotsu-tools-definition-header {
  font-weight: 700;
  font-size: 12px;
  line-height: 1.2;
  margin-top: 0.5rem;
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: center;
}
.cotsu-tools-definition-collapsed {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.cotsu-tools-definition-index-number {
  margin-left: 0.3rem;
}
.cotsu-tools-definition-paren {
  color: #555;
}
.cotsu-tools-definition-context {
  font-variant-caps: all-small-caps;
  letter-spacing: 0.02rem;
}

/* Dummy question */
.cotsu-tools-dummy-question
  [class^="ReadingQuestionCard-module--section-action--"] {
  height: auto;
}
.cotsu-tools-dummy-question
  [class*="ReadingQuestionCard-module--action-check--"] {
  margin: 0;
  transition: margin 0s;
}
.cotsu-tools-dummy-question
  [class^="ReadingQuestionCard-module--input-field--"],
.cotsu-tools-dummy-question
  [class^="ReadingQuestionCard-module--newWordHint--"],
.cotsu-tools-dummy-question [class^="StudyProgress-module--study-progress--"] {
  display: none;
}

/* Word summary */
.word-summary div,
.word-summary span {
  font-weight: 400;
}
.word-summary div {
  margin-bottom: 4px;
}
.cotsu-tools-spoiler {
  border-radius: 5px;
  border: 0;
  background-color: currentcolor;
  padding: 0;
  color: #444;
  padding: 1px 5px;
}
.cotsu-tools-spoiler:not(.cotsu-tools-spoiler-shown):hover {
  cursor: pointer;
  color: #555;
}
.cotsu-tools-spoiler.cotsu-tools-spoiler-shown {
  background-color: #eee;
  transition:
    background-color 0.5s,
    color 0.5s;
  user-select: text;
  color: inherit;
}
.cotsu-tools-summary-shuffled {
  display: none;
}
.cotsu-tools-shuffle-summary .cotsu-tools-summary-shuffled {
  display: block;
}
.cotsu-tools-shuffle-summary .cotsu-tools-summary-unshuffled {
  display: none;
}

/* Stats */
div[class^="index-module--action-card-text--"] {
  height: auto;
  min-height: 74px;
}
[class^="MaturityTallies-module--maturity-tallies--"] + h4 + p + div {
  display: table;
  border-spacing: 10px 0;
}
[class^="MaturityTallies-module--maturity-tallies--"] + h4 + p + div > div {
  display: table-row;
}
[class^="MaturityTallies-module--maturity-tallies--"]
  + h4
  + p
  + div
  > div
  > div {
  display: table-cell;
  min-width: 0px !important;
  white-space: nowrap;
}
.cotsu-tools-detail-green {
  color: #40d5ac;
  font-weight: bold;
}

/* Katakana mode */
body {
  --cotsu-tools-kana-font: ;
}
body.cotsu-tools-katakana-mode {
  --cotsu-tools-kana-font:
    Noto Sans JP All Katakana, Noto Sans JP, Noto Sans Japanese, sans-serif;
}
body.cotsu-tools-cursive-font {
  --cotsu-tools-kana-font:
    Yuji Syuku, Noto Sans JP, Noto Sans Japanese, sans-serif;
}
body.cotsu-tools-cursive-font.cotsu-tools-katakana-mode {
  --cotsu-tools-kana-font:
    Yuji Syuku All Katakana, Noto Sans JP, Noto Sans Japanese, sans-serif;
}
[class^="ReadingQuestionCard-module--input-field--"],
.cotsu-tools-pitch-accent,
.cotsu-tools-pitch-accent span,
.commonReading,
.commonReading b {
  font-family: var(--cotsu-tools-kana-font) !important;
}

/* Other words with Kanji */
.cotsu-tools-other-kanji-init-button {
  white-space: nowrap;
}
.cotsu-tools-other-kanji {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: end;
  padding: 0;
  text-align: left;
}
.cotsu-tools-other-kanji li {
  margin-bottom: 0.25rem;
}
.cotsu-tools-other-kanji.cotsu-tools-other-kanji-loaded {
  align-items: start;
}
.cotsu-tools-other-kanji-kanji {
  font-weight: 400;
}
.cotsu-tools-other-kanji-more {
  margin-left: 0.25rem;
}

/* Didn't know word */
.cotsu-tools-didnt-know-word {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  user-select: none;
}
.cotsu-tools-didnt-know-word-strike .card-level-up {
  text-decoration: line-through;
}
.cotsu-tools-didnt-know-word-strike
  [class^="MaturityTallies-module--tally-icon--"] {
  filter: grayscale(1);
  position: relative;
}
.cotsu-tools-didnt-know-word-strike
  [class^="MaturityTallies-module--tally-icon--"]::before {
  position: absolute;
  top: 50%;
  left: -2px;
  rotate: -45deg;
  width: 20px;
  height: 1px;
  display: inline-block;
  background-color: black;
  content: "";
}

/* Hide question maturity */
.cotsu-tools-hide-question-maturity
  [class^="ReadingQuestionCard-module--icons--"] {
  display: none;
}
`;

  // src/lib/style.ts
  var insertCSS = () => {
    const styleElement = document.createElement("style");
    styleElement.innerHTML = style_default;
    document.head.append(styleElement);
  };

  // src/index.ts
  insertCSS();
  startInterceptingFetch();
  new MutationObserver(async (records) => {
    handleSettings(records);
    handleExerciseWords(records);
    showMoreStats(records);
    showExercisesLeftInKanjiLearningTab(records);
    fixFontInNewKanjiTab(records);
    handleKanjiTab(records);
  }).observe(document.body, {
    childList: true,
    subtree: true,
    characterData: true
  });
})();