Prettier Codewars

Polish Codewars with cleaner training pages, better editor typography, responsive fixes, subtle typing effects, and promotion cleanup.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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         Prettier Codewars
// @namespace    https://codewars.com/
// @version      1.2.0
// @description  Polish Codewars with cleaner training pages, better editor typography, responsive fixes, subtle typing effects, and promotion cleanup.
// @author       NihilDigit
// @match        https://www.codewars.com/*
// @match        https://codewars.com/*
// @icon         https://www.codewars.com/favicon.ico
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  const STYLE_ID = "cw-polish-style";
  const HIDDEN_MARK = "data-cw-polish-hidden";
  const SPARKS_ID = "cw-polish-sparks";
  const STORAGE_PREFIX = "prettier-codewars:";

  const defaultConfig = {
    hidePromotions: true,
    useMapleMono: true,
    tuneCodeMirror: true,
    lineWrapping: true,
    autoFormat: true,
    lightweightAutocomplete: true,
    typingSparks: true,
    deleteAnnihilation: true,
    editorFontSize: "15px",
    editorLineHeight: 1.55
  };

  const menuOptions = [
    ["hidePromotions", "Hide promotions"],
    ["useMapleMono", "Maple Mono font"],
    ["tuneCodeMirror", "CodeMirror polish"],
    ["lineWrapping", "Line wrapping"],
    ["autoFormat", "AutoFormat"],
    ["lightweightAutocomplete", "Lightweight autocomplete"],
    ["typingSparks", "Typing sparks"],
    ["deleteAnnihilation", "Delete annihilation"]
  ];

  const C_KEYWORDS = [
    "auto",
    "break",
    "case",
    "char",
    "const",
    "continue",
    "default",
    "do",
    "double",
    "else",
    "enum",
    "extern",
    "float",
    "for",
    "goto",
    "if",
    "inline",
    "int",
    "long",
    "register",
    "restrict",
    "return",
    "short",
    "signed",
    "sizeof",
    "static",
    "struct",
    "switch",
    "typedef",
    "union",
    "unsigned",
    "void",
    "volatile",
    "while"
  ];

  const C_STANDARD_WORDS = [
    "EOF",
    "FILE",
    "NULL",
    "SEEK_CUR",
    "SEEK_END",
    "SEEK_SET",
    "bool",
    "calloc",
    "char",
    "double",
    "errno",
    "exit",
    "fclose",
    "feof",
    "ferror",
    "fflush",
    "fgets",
    "fopen",
    "fprintf",
    "fputc",
    "fputs",
    "fread",
    "free",
    "fscanf",
    "fseek",
    "ftell",
    "fwrite",
    "getchar",
    "int16_t",
    "int32_t",
    "int64_t",
    "int8_t",
    "intptr_t",
    "malloc",
    "max_align_t",
    "memcmp",
    "memcpy",
    "memmove",
    "memset",
    "offsetof",
    "printf",
    "ptrdiff_t",
    "putchar",
    "puts",
    "qsort",
    "realloc",
    "scanf",
    "size_t",
    "snprintf",
    "sprintf",
    "sscanf",
    "stderr",
    "stdin",
    "stdout",
    "strcat",
    "strchr",
    "strcmp",
    "strcpy",
    "strlen",
    "strncmp",
    "strncpy",
    "strrchr",
    "strstr",
    "strtol",
    "strtoul",
    "uint16_t",
    "uint32_t",
    "uint64_t",
    "uint8_t",
    "uintptr_t",
    "va_arg",
    "va_end",
    "va_list",
    "va_start",
    "void"
  ];

  function readSetting(key) {
    const fallback = defaultConfig[key];
    const storageKey = STORAGE_PREFIX + key;

    try {
      if (typeof GM_getValue === "function") {
        return GM_getValue(storageKey, fallback);
      }

      const value = window.localStorage.getItem(storageKey);
      return value === null ? fallback : JSON.parse(value);
    } catch (_error) {
      return fallback;
    }
  }

  function writeSetting(key, value, reload = true) {
    const storageKey = STORAGE_PREFIX + key;

    try {
      if (typeof GM_setValue === "function") {
        GM_setValue(storageKey, value);
      } else {
        window.localStorage.setItem(storageKey, JSON.stringify(value));
      }
    } catch (_error) {
      return;
    }

    if (reload) {
      window.location.reload();
    }
  }

  function readConfig() {
    return Object.fromEntries(Object.keys(defaultConfig).map((key) => [key, readSetting(key)]));
  }

  const config = readConfig();

  const effectColors = {
    sparks: ["#ffd166", "#ff9f1c", "#ff6b35", "#e5383b", "#fff3b0"]
  };

  function buildCss() {
    return `
    ${
      config.hidePromotions
        ? `
    [data-cw-polish-hidden="true"] {
      display: none !important;
    }

    .partner-display,
    .promoted {
      display: none !important;
    }
    `
        : ""
    }

    ${
      config.useMapleMono
        ? `
    @font-face {
      font-family: "Maple Mono Web";
      font-style: normal;
      font-weight: 400;
      font-display: swap;
      src:
        local("Maple Mono NF"),
        local("MapleMono NF"),
        local("Maple Mono Normal NF"),
        url("https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-normal.woff2") format("woff2");
    }

    @font-face {
      font-family: "Maple Mono Web";
      font-style: italic;
      font-weight: 400;
      font-display: swap;
      src:
        local("Maple Mono NF Italic"),
        local("MapleMono NF Italic"),
        local("Maple Mono Normal NF Italic"),
        url("https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-italic.woff2") format("woff2");
    }

    .CodeMirror,
    .CodeMirror pre,
    .CodeMirror code,
    .CodeMirror-line,
    .CodeMirror-line *,
    pre,
    code,
    kbd,
    samp {
      font-family: "Maple Mono Web", "Maple Mono NF", "Maple Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
      font-variant-ligatures: contextual common-ligatures !important;
    }
    `
        : ""
    }

    ${
      config.tuneCodeMirror
        ? `
    .CodeMirror {
      font-size: ${config.editorFontSize} !important;
      line-height: ${config.editorLineHeight} !important;
    }

    .CodeMirror-lines,
    .CodeMirror pre.CodeMirror-line,
    .CodeMirror pre.CodeMirror-line-like {
      line-height: ${config.editorLineHeight} !important;
    }

    .CodeMirror-cursor {
      transition:
        left 80ms ease-out,
        top 80ms ease-out,
        height 80ms ease-out !important;
    }

    .CodeMirror-activeline-background {
      background: rgb(255 255 255 / 5.5%) !important;
    }

    .CodeMirror-hscrollbar {
      display: none !important;
    }

    .CodeMirror-scroll {
      overflow-x: hidden !important;
    }
    `
        : ""
    }

    #description_area .description-content[data-cw-polish-short-overflow="true"] {
      overflow: visible !important;
    }

    #description_area .description.h-full > :not(.description-content) {
      display: none !important;
    }

    #description_area .description.h-full {
      overflow: hidden !important;
    }

    #description_area .description-content.p-4 {
      height: 100% !important;
      overflow: auto !important;
    }

    .CodeMirror-hints {
      z-index: 2147483646 !important;
      border: 1px solid rgb(255 255 255 / 14%) !important;
      border-radius: 6px !important;
      background: rgb(18 20 26 / 96%) !important;
      box-shadow: 0 12px 30px rgb(0 0 0 / 35%) !important;
      color: rgb(238 241 246 / 94%) !important;
      font-family: "Maple Mono Web", "Maple Mono NF", "Maple Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
      font-size: 13px !important;
    }

    .CodeMirror-hint {
      border-radius: 4px !important;
      color: inherit !important;
    }

    li.CodeMirror-hint-active {
      background: rgb(232 98 36 / 92%) !important;
      color: white !important;
    }

    #cw-polish-sparks {
      position: fixed;
      inset: 0;
      z-index: 2147483647;
      pointer-events: none;
      overflow: hidden;
    }

    @media (max-width: 1100px) {
      body.play_view #cc_play_view .game-title .panel > .flex.flex-col.md\\:flex-row {
        flex-direction: column !important;
      }

      body.play_view #cc_play_view .game-title .w-full.md\\:w-5\\/12,
      body.play_view #cc_play_view .game-title .w-full.md\\:w-7\\/12 {
        width: 100% !important;
      }

      body.play_view #cc_play_view .game-title .w-full.md\\:w-7\\/12.pt-4.md\\:pl-4 {
        display: flex !important;
        flex-wrap: wrap !important;
        align-items: stretch !important;
        gap: 8px !important;
        padding-left: 0 !important;
        padding-top: 12px !important;
      }

      body.play_view #cc_play_view .game-title .language-selector,
      body.play_view #cc_play_view .game-title #language_dd,
      body.play_view #cc_play_view .game-title #language_version {
        flex: 1 1 180px !important;
        min-width: 160px !important;
        max-width: none !important;
      }

      body.play_view #cc_play_view .game-title .w-full.md\\:w-7\\/12.pt-4.md\\:pl-4 > a {
        display: flex !important;
        flex: 0 0 auto !important;
      }
    }

    @media (max-width: 1000px) {
      body#users.show_view main .bg-ui-section .flex.flex-col.md\\:flex-row {
        flex-direction: column !important;
        align-items: stretch !important;
      }

      body#users.show_view main .bg-ui-section .flex.flex-col.md\\:flex-row > .w-full.md\\:w-6\\/12 {
        width: 100% !important;
        padding-left: 0 !important;
      }

      body#users.show_view #report .honor-chart-container {
        display: grid !important;
        grid-template-columns: 220px minmax(220px, 1fr) !important;
        align-items: center !important;
        column-gap: 32px !important;
        width: max-content !important;
        max-width: 100% !important;
        margin: 16px auto 0 !important;
      }

      body#users.show_view #report #honor_chart {
        grid-column: 1 !important;
      }

      body#users.show_view #report .honor-chart-center {
        left: 55px !important;
        top: 55px !important;
      }

      body#users.show_view #report .honor-chart-container > .md\\:w-64 {
        position: static !important;
        grid-column: 2 !important;
        width: auto !important;
        height: auto !important;
        overflow: visible !important;
        padding-left: 0 !important;
        margin-top: 0 !important;
      }
    }

    @media (max-width: 720px) {
      body#users.show_view main .bg-ui-section .flex.flex-col.md\\:flex-row {
        flex-direction: column !important;
      }

      body#users.show_view main .bg-ui-section .flex.flex-col.md\\:flex-row > .w-full.md\\:w-6\\/12 {
        width: 100% !important;
      }

      body#users.show_view #report .honor-chart-container {
        grid-template-columns: 1fr !important;
        justify-items: center !important;
        row-gap: 18px !important;
        width: 100% !important;
      }

      body#users.show_view #report .honor-chart-container > .md\\:w-64 {
        grid-column: 1 !important;
      }
    }
  `;
  }

  const adSelectors = [
    "#house_ad_display",
    ".cw-ad",
    ".ads-container",
    "[id*='ad_display' i]",
    "[id*='ad-container' i]",
    "[class*='ad-container' i]",
    "a[href*='/ads/']",
    "a[href*='house_srv']",
    "iframe[src*='ad' i]",
    "ins.adsbygoogle",
    ".partner-display",
    ".promoted",
    ".my-4.flex.flex-col.md\\:flex-row.space-y-4.md\\:space-y-0.md\\:space-x-4",
    ".mt-4.flex.flex-col.md\\:flex-row.space-y-4.md\\:space-y-0.md\\:space-x-4"
  ];

  const classSetBlocklist = [
    ["my-4", "flex", "flex-col", "md:flex-row", "space-y-4", "md:space-y-0", "md:space-x-4"],
    ["mt-4", "flex", "flex-col", "md:flex-row", "space-y-4", "md:space-y-0", "md:space-x-4"],
    ["description-footer", "flex", "flex-row"],
    ["w-256", "max-w-full", "mx-auto", "my-4"],
    ["partner-display"],
    ["promoted"]
  ];

  function injectStyle() {
    let style = document.getElementById(STYLE_ID);
    if (!style) {
      style = document.createElement("style");
      style.id = STYLE_ID;
      (document.head || document.documentElement).append(style);
    }

    style.id = STYLE_ID;
    style.textContent = buildCss();
  }

  function registerSettingsMenu() {
    if (typeof GM_registerMenuCommand !== "function") return;

    menuOptions.forEach(([key, label]) => {
      const state = config[key] ? "On" : "Off";
      GM_registerMenuCommand(`${state} - ${label}`, () => writeSetting(key, !config[key]));
    });

    GM_registerMenuCommand(`Set editor font size (${config.editorFontSize})`, () => {
      const value = window.prompt("Editor font size, for example 15px:", config.editorFontSize);
      if (value && /^\d+(?:\.\d+)?(?:px|rem|em)$/.test(value.trim())) {
        writeSetting("editorFontSize", value.trim());
      }
    });

    GM_registerMenuCommand(`Set editor line height (${config.editorLineHeight})`, () => {
      const value = window.prompt("Editor line height, for example 1.55:", String(config.editorLineHeight));
      const numberValue = Number(value);
      if (Number.isFinite(numberValue) && numberValue >= 1 && numberValue <= 3) {
        writeSetting("editorLineHeight", numberValue);
      }
    });

    GM_registerMenuCommand("Reset Prettier Codewars settings", () => {
      Object.entries(defaultConfig).forEach(([key, value]) => writeSetting(key, value, false));
      window.location.reload();
    });
  }

  function hide(node) {
    if (node && node.nodeType === Node.ELEMENT_NODE) {
      node.setAttribute(HIDDEN_MARK, "true");
    }
  }

  function isProtected(element) {
    return Boolean(
      element.closest("html, body") === element ||
        element.matches("main, #main_header, #trainer, #trainer *") ||
        element.querySelector?.("#trainer")
    );
  }

  function hideSafely(element) {
    if (!element || isProtected(element)) return;
    hide(element);
  }

  function nearestAdContainer(element) {
    return (
      element.closest(
        "#house_ad_display, .ads-container, .cw-ad, aside, article, section:not(#trainer), .panel, div[class*='md:w-'], div[class*='w-full']"
      ) ||
      element
    );
  }

  function removeAds(root = document) {
    if (!config.hidePromotions) return;

    for (const selector of adSelectors) {
      root.querySelectorAll(selector).forEach((element) => hideSafely(nearestAdContainer(element)));
    }

    root.querySelectorAll("div").forEach((element) => {
      if (classSetBlocklist.some((classSet) => classSet.every((name) => element.classList.contains(name)))) {
        hideSafely(element);
      }
    });

  }

  function tuneEditors(root = document) {
    if (!config.tuneCodeMirror) return;

    const mirrors = [];

    if (root.matches?.(".CodeMirror")) {
      mirrors.push(root);
    }

    if (root.querySelectorAll) {
      mirrors.push(...root.querySelectorAll(".CodeMirror"));
    }

    mirrors.forEach((element) => {
      if (element.CodeMirror) {
        tuneEditor(element.CodeMirror);
        scheduleInitialAutoFormat(element.CodeMirror, element);
        if (config.typingSparks || config.deleteAnnihilation) {
          attachEffects(element.CodeMirror);
        }
      }
    });
  }

  function tuneEditor(cm) {
    const optionsKey = [config.lineWrapping, 4, 4, false].join(":");

    if (cm.__cwPolishOptionsKey !== optionsKey) {
      cm.__cwPolishOptionsKey = optionsKey;
      cm.setOption("lineWrapping", config.lineWrapping);
      cm.setOption("indentUnit", 4);
      cm.setOption("tabSize", 4);
      cm.setOption("indentWithTabs", false);
      cm.refresh();
    }

    attachEditorFeatures(cm);
  }

  function scheduleInitialAutoFormat(cm, element) {
    if (!config.autoFormat || cm.__cwPolishInitialFormatted || !isCMode(cm) || !isSolutionEditor(element)) return;
    cm.__cwPolishInitialFormatted = true;

    window.setTimeout(() => {
      if (!cm.getWrapperElement?.().isConnected) return;
      autoFormat(cm);
    }, 200);
  }

  function isSolutionEditor(element) {
    return document.querySelector(".CodeMirror") === element;
  }

  function attachEditorFeatures(cm) {
    if (cm.__cwPolishFeatures) return;
    cm.__cwPolishFeatures = true;

    const keyMap = {
      Tab: (instance) => {
        insertSpaces(instance, 4);
        return true;
      }
    };

    if (config.autoFormat) {
      keyMap["Alt-Shift-F"] = (instance) => {
        autoFormat(instance);
        return true;
      };
    }

    if (config.lightweightAutocomplete && isCMode(cm)) {
      keyMap["Ctrl-Space"] = (instance) => {
        showIdentifierHints(instance, true);
        return true;
      };

      cm.on("inputRead", (instance, change) => {
        if (instance.state.completionActive || !change.text || change.text.length !== 1) return;
        if (!/^[A-Za-z_$]$/.test(change.text[0])) return;

        const prefix = currentPrefix(instance);
        if (prefix.length >= 2) {
          showIdentifierHints(instance, false);
        }
      });
    }

    cm.addKeyMap(keyMap);
  }

  function insertSpaces(cm, count) {
    const spaces = " ".repeat(count);

    if (typeof cm.replaceSelections === "function" && typeof cm.listSelections === "function") {
      cm.replaceSelections(cm.listSelections().map(() => spaces), "end", "+input");
      return;
    }

    cm.replaceSelection(spaces, "end", "+input");
  }

  function autoFormat(cm) {
    if (!cm || typeof cm.indentLine !== "function") return;

    const cursor = cm.getCursor();
    const scroll = cm.getScrollInfo();

    if (isCMode(cm)) {
      const formatted = formatKAndRC(cm.getValue());
      if (formatted !== cm.getValue()) {
        cm.setValue(formatted);
      }
    }

    cm.operation(() => {
      for (let line = cm.firstLine(); line <= cm.lastLine(); line += 1) {
        cm.indentLine(line, "smart");
      }
    });

    cm.setCursor(cursor);
    cm.scrollTo(scroll.left, scroll.top);
  }

  function formatKAndRC(text) {
    const normalized = text.replace(/\t/g, "    ");
    const lines = normalized.split("\n");
    const output = [];

    lines.forEach((line) => {
      const leading = line.match(/^\s*/)[0];
      const trimmed = line.trim();

      if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//") || trimmed.startsWith("*")) {
        output.push(line);
        return;
      }

      if (trimmed === "{" && output.length > 0) {
        const previous = output[output.length - 1];
        const previousTrimmed = previous.trim();

        if (isKAndRBraceLine(previousTrimmed)) {
          output[output.length - 1] = `${previous.replace(/\s*$/, "")} {`;
          return;
        }
      }

      if (/^else\b/.test(trimmed) && output.length > 0 && output[output.length - 1].trim() === "}") {
        output[output.length - 1] = `${output[output.length - 1].replace(/\s*$/, "")} ${trimmed}`;
        return;
      }

      output.push(line.replace(/^(\s*)}\s*else\b/, "$1} else"));
    });

    return output.join("\n");
  }

  function isKAndRBraceLine(beforeBrace) {
    return (
      /^(?:if|for|while|switch)\s*\(.*\)$/.test(beforeBrace) ||
      /^}?\s*else(?:\s+if\s*\(.*\))?$/.test(beforeBrace) ||
      /^do$/.test(beforeBrace) ||
      /^(?:[A-Za-z_]\w*[\w\s*]*\s+)?[A-Za-z_]\w*\s*\([^;]*\)$/.test(beforeBrace) ||
      /^(?:struct|union|enum)\b.*$/.test(beforeBrace)
    );
  }

  function showIdentifierHints(cm, explicit) {
    if (!cm || !isCMode(cm) || typeof cm.showHint !== "function") return;

    const hints = buildIdentifierHints(cm);
    if (!explicit && hints.list.length < 2) return;

    cm.showHint({
      hint: () => hints,
      completeSingle: false,
      closeCharacters: /[\s()[\]{};:>,]/,
      customKeys: {
        Tab: (_editor, handle) => handle.pick(),
        Enter: (_editor, handle) => handle.pick(),
        Esc: (_editor, handle) => handle.close(),
        Up: (_editor, handle) => handle.moveFocus(-1),
        Down: (_editor, handle) => handle.moveFocus(1)
      }
    });
  }

  function buildIdentifierHints(cm) {
    const cursor = cm.getCursor();
    const prefix = currentPrefix(cm);
    const from = { line: cursor.line, ch: cursor.ch - prefix.length };
    const to = { line: cursor.line, ch: cursor.ch };
    const candidates = buildCompletionCandidates(cm, cursor);
    const seen = new Set();

    const list = candidates
      .filter((word) => {
        const normalized = word.toLowerCase();
        if (seen.has(normalized)) return false;
        seen.add(normalized);
        return word.length > 1 && word !== prefix && (!prefix || normalized.startsWith(prefix.toLowerCase()));
      })
      .slice(0, 80);

    return { list, from, to };
  }

  function buildCompletionCandidates(cm, cursor) {
    const text = cm.getValue();
    const cache = cm.__cwPolishCompletionCache;

    if (cache && cache.text === text && cache.line === cursor.line) {
      return cache.candidates;
    }

    const candidates = [
      ...collectLocalIdentifiers(cm, cursor),
      ...collectPreprocessorIdentifiers(text),
      ...C_STANDARD_WORDS,
      ...C_KEYWORDS
    ];

    cm.__cwPolishCompletionCache = {
      candidates,
      text,
      line: cursor.line
    };

    return candidates;
  }

  function collectLocalIdentifiers(cm, cursor) {
    const startLine = findLocalBlockStart(cm, cursor.line);
    const endLine = findLocalBlockEnd(cm, cursor.line);
    const text = cm.getRange({ line: startLine, ch: 0 }, { line: endLine, ch: cm.getLine(endLine).length });
    return collectIdentifiers(text);
  }

  function collectPreprocessorIdentifiers(text) {
    const identifiers = [];
    const definePattern = /^\s*#\s*define\s+([A-Za-z_]\w*)/gm;
    let match;

    while ((match = definePattern.exec(text))) {
      identifiers.push(match[1]);
    }

    return identifiers;
  }

  function findLocalBlockStart(cm, line) {
    let depth = 0;

    for (let index = line; index >= cm.firstLine(); index -= 1) {
      const text = stripCCommentsAndStrings(cm.getLine(index));

      for (let ch = text.length - 1; ch >= 0; ch -= 1) {
        if (text[ch] === "}") depth += 1;
        if (text[ch] === "{") {
          if (depth === 0) return Math.max(cm.firstLine(), index - 1);
          depth -= 1;
        }
      }
    }

    return cm.firstLine();
  }

  function findLocalBlockEnd(cm, line) {
    let depth = 0;

    for (let index = line; index <= cm.lastLine(); index += 1) {
      const text = stripCCommentsAndStrings(cm.getLine(index));

      for (let ch = 0; ch < text.length; ch += 1) {
        if (text[ch] === "{") depth += 1;
        if (text[ch] === "}") {
          if (depth === 0) return index;
          depth -= 1;
          if (depth === 0) return index;
        }
      }
    }

    return cm.lastLine();
  }

  function collectIdentifiers(text) {
    const identifiers = [];
    const wordPattern = /\b[A-Za-z_]\w*\b/g;
    const cleaned = stripCCommentsAndStrings(text);
    let match;

    while ((match = wordPattern.exec(cleaned))) {
      identifiers.push(match[0]);
    }

    return identifiers;
  }

  function stripCCommentsAndStrings(text) {
    return text
      .replace(/\/\*[\s\S]*?\*\//g, " ")
      .replace(/\/\/.*/g, " ")
      .replace(/"(?:\\.|[^"\\])*"/g, " ")
      .replace(/'(?:\\.|[^'\\])*'/g, " ");
  }

  function currentPrefix(cm) {
    const cursor = cm.getCursor();
    const line = cm.getLine(cursor.line).slice(0, cursor.ch);
    const match = line.match(/[A-Za-z_$][\w$]*$/);
    return match ? match[0] : "";
  }

  function isCMode(cm) {
    return languageKey(cm.getOption("mode")) === "c";
  }

  function languageKey(mode) {
    const value = String(mode || "").toLowerCase();

    if (value === "text/x-c" || value === "text/x-csrc" || value === "text/x-chdr") return "c";
    return "";
  }

  function attachRunAutoFormat() {
    if (!config.autoFormat || document.__cwPolishRunAutoFormat) return;
    document.__cwPolishRunAutoFormat = true;

    document.addEventListener(
      "click",
      (event) => {
        if (!event.target.closest?.("#validate_btn, #attempt_btn, #submit_btn")) return;
        const solutionEditor = document.querySelector(".CodeMirror")?.CodeMirror;
        autoFormat(solutionEditor);
      },
      true
    );
  }

  function ensureSparkLayer() {
    let layer = document.getElementById(SPARKS_ID);
    if (!layer) {
      layer = document.createElement("div");
      layer.id = SPARKS_ID;
      document.documentElement.append(layer);
    }

    return layer;
  }

  function sparkAt(x, y, intensity = 1) {
    const layer = ensureSparkLayer();
    const count = Math.min(7, Math.max(3, Math.round(4 * intensity)));

    for (let index = 0; index < count; index += 1) {
      const spark = document.createElement("i");
      const angle = -Math.PI + Math.random() * Math.PI;
      const distance = 18 + Math.random() * 34 * intensity;
      const dx = Math.cos(angle) * distance;
      const dy = Math.sin(angle) * distance - Math.random() * 8;
      const size = 4 + Math.random() * 4;
      const color = effectColors.sparks[Math.floor(Math.random() * effectColors.sparks.length)];
      const rotation = (Math.random() - 0.5) * 160;

      spark.style.cssText = [
        "position:absolute",
        `left:${x}px`,
        `top:${y}px`,
        `width:${size}px`,
        `height:${size}px`,
        `background:${color}`,
        "border-radius:1px",
        `box-shadow:0 0 ${7 + size * 2}px ${color}`,
        `transform:translate(-50%,-50%) rotate(${rotation}deg) scale(1)`,
        "opacity:.9",
        "will-change:transform,opacity"
      ].join(";");

      layer.append(spark);

      spark
        .animate(
          [
            { transform: `translate(-50%, -50%) rotate(${rotation}deg) scale(1)`, opacity: 0.95 },
            {
              transform: `translate(calc(-50% + ${dx}px), calc(-50% + ${dy}px)) rotate(${rotation + 120}deg) scale(.25)`,
              opacity: 0
            }
          ],
          {
            duration: 520 + Math.random() * 260,
            easing: "cubic-bezier(.16, 1, .3, 1)"
          }
        )
        .finished.finally(() => spark.remove());
    }
  }

  function annihilateAt(x, y, intensity = 1) {
    const layer = ensureSparkLayer();
    const driftX = -5 - Math.random() * 8;
    const driftY = -2 + (Math.random() - 0.5) * 6;
    const count = Math.min(5, Math.max(3, Math.round(4 * intensity)));

    for (let index = 0; index < count; index += 1) {
      const voidBit = document.createElement("i");
      const angle = Math.random() * Math.PI * 2;
      const distance = 18 + Math.random() * 32 * intensity;
      const sx = Math.cos(angle) * distance;
      const sy = Math.sin(angle) * distance;
      const endX = driftX * 0.35 + (Math.random() - 0.5) * 1.5;
      const endY = driftY * 0.35 + (Math.random() - 0.5) * 1.5;
      const size = 4 + Math.random() * 5;
      const rotation = (Math.random() - 0.5) * 180;

      voidBit.style.cssText = [
        "position:absolute",
        `left:${x}px`,
        `top:${y}px`,
        `width:${size}px`,
        `height:${size}px`,
        "background:oklch(5% 0.01 265 / .92)",
        "border-radius:2px",
        "box-shadow:0 0 8px oklch(0% 0 0 / .9)",
        `transform:translate(calc(-50% + ${sx}px), calc(-50% + ${sy}px)) rotate(${rotation}deg) scale(1)`,
        "opacity:.86",
        "will-change:transform,opacity,filter"
      ].join(";");

      layer.append(voidBit);

      voidBit
        .animate(
          [
            {
              transform: `translate(calc(-50% + ${sx}px), calc(-50% + ${sy}px)) rotate(${rotation}deg) scale(1)`,
              opacity: 0.86
            },
            {
              transform: `translate(calc(-50% + ${endX}px), calc(-50% + ${endY}px)) rotate(${rotation + 210}deg) scale(.05)`,
              opacity: 0
            }
          ],
          {
            duration: 500 + Math.random() * 140,
            easing: "cubic-bezier(.55, 0, .1, 1)"
          }
        )
        .finished.finally(() => voidBit.remove());
    }
  }

  function attachEffects(cm) {
    if (cm.__cwPolishEffects) return;
    cm.__cwPolishEffects = true;

    let lastSpark = 0;

    cm.on("change", (_instance, change) => {
      if (!change.origin || change.origin === "setValue") return;

      const now = performance.now();
      if (now - lastSpark < 30) return;
      lastSpark = now;

      const cursor = cm.getCursor();
      const pos = cm.cursorCoords(cursor, "window");
      const typed = change.text.join("").length;
      const removed = change.removed ? change.removed.join("").length : 0;
      const x = pos.left + 2;
      const y = pos.top + (pos.bottom - pos.top) / 2;

      if (typed > 0 && config.typingSparks) {
        sparkAt(x, y, Math.min(1.8, 1 + typed / 8));
      } else if (removed > 0 && config.deleteAnnihilation) {
        annihilateAt(x, y, Math.min(1.6, 1 + removed / 8));
      }
    });
  }

  function tuneDescriptionScroll(root = document) {
    const contents = [];

    if (root.matches?.("#description_area .description-content")) {
      contents.push(root);
    }

    if (root.querySelectorAll) {
      contents.push(...root.querySelectorAll("#description_area .description-content"));
    }

    contents.forEach((element) => {
      if (element.scrollHeight - element.clientHeight <= 20) {
        element.setAttribute("data-cw-polish-short-overflow", "true");
      } else {
        element.removeAttribute("data-cw-polish-short-overflow");
      }
    });
  }

  function boot() {
    registerSettingsMenu();
    injectStyle();
    removeAds();
    tuneEditors();
    tuneDescriptionScroll();
    attachRunAutoFormat();

    [100, 300, 800, 1500, 3000].forEach((delay) => {
      window.setTimeout(() => {
        injectStyle();
        removeAds();
        tuneEditors();
        tuneDescriptionScroll();
      }, delay);
    });

    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE) {
            removeAds(node);
            tuneEditors(node);
            tuneDescriptionScroll(node);
          }
        }
      }
    });

    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
    });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", boot, { once: true });
    injectStyle();
  } else {
    boot();
  }
})();