Nitro Monkey | NT Theme

Custom Nitro Type Theme w/ Font-Size, Height Sliders, and Cursor Customization

目前为 2024-10-20 提交的版本。查看 最新版本

// ==UserScript==
// @name         Nitro Monkey | NT Theme
// @namespace    https://greasyfork.org/users/1331131-tensorflow-dvorak
// @version      2024-10-20
// @description  Custom Nitro Type Theme w/ Font-Size, Height Sliders, and Cursor Customization
// @author       TensorFlow - Dvorak
// @match        *://*.nitrotype.com/race
// @match        *://*.nitrotype.com/race/*
// @license      MIT
// @grant        none
// ==/UserScript==

(function () {
  const dynamicStyle = document.createElement("style");
  document.head.appendChild(dynamicStyle);

  let currentCursorType = localStorage.getItem("cursorType") || "line";
  let currentCursorSpeed = localStorage.getItem("cursorSpeed") || "medium";

  function updateStyles() {
    dynamicStyle.innerHTML = `
      ${generateCursorStyle(currentCursorType, currentCursorSpeed)}
      ${generateFontSizeStyle(localStorage.getItem("dashFontSize") || "40")}
    `;
  }

  function generateCursorStyle(cursorType, cursorSpeed) {
    let cursorSize = "2px";
    let cursorHeight = "1.2em";
    let animationDuration = "1s";
    let cursorTopOffset = "0.2em";
    let cursorColor = "#00FF7F";
    let cursorTransform = "";

    if (cursorType === "block") {
      cursorSize = "0.7em";
      cursorHeight = "1.1em";
      cursorTopOffset = "0";
      cursorColor = "#0075ff42";
      cursorTransform = "translateY(0.3em)";
    } else if (cursorType === "line") {
      cursorSize = "2px";
      cursorHeight = "1.2em";
      cursorColor = "#0075ff";
    } else {
      cursorSize = "0";
    }

    if (cursorSpeed === "slow") {
      animationDuration = "1.5s";
    } else if (cursorSpeed === "fast") {
      animationDuration = "0.5s";
    }

    return `
      .dash-letter {
        color: #acaaff;
      }
      .dash-letter.is-waiting {
        position: relative;
        color: #acaaff;
        background-color: #1c99f400;
      }
      .dash-letter.is-waiting::after {
        content: '';
        display: inline-block;
        width: ${cursorSize};
        height: ${cursorHeight};
        background-color: ${cursorColor};
        animation: blink ${animationDuration} step-end infinite;
        position: absolute;
        top: ${cursorTopOffset};
        left: 0;
        transform: ${cursorTransform};
      }
      .dash-letter.is-incorrect {
        color: red;
        background: #ffffff00;
        position: relative;
      }
      .dash-letter.is-incorrect::after {
        content: '';
        display: inline-block;
        width: ${cursorSize};
        height: ${cursorHeight};
        background-color: rgba(255, 0, 0, 0.5);
        animation: blink ${animationDuration} step-end infinite;
        position: absolute;
        top: ${cursorTopOffset};
        left: 0;
        transform: ${cursorTransform};
      }
      @keyframes blink {
        50% { opacity: 0; }
      }
    `;
  }

  function generateFontSizeStyle(fontSize) {
    return `
      #root {
        background-color: #060516;
      }
      .dash-copy {
        font-size: ${fontSize}px !important;
      }
      .dash-copyContainer {
        background: linear-gradient(to bottom, rgba(6, 5, 22, 0.9) 65%, rgba(6, 5, 22, 0.87) 70%, #060516 100%);
        border-radius: 5px;
        box-shadow: 0 1px 10px rgba(2, 2, 2, 0.14);
        flex: 1;
        overflow: hidden;
        padding: 15px;
        width: 100%;
        display: flex;
      }
      .dash-side, .dash-actions, .dash-nitros {
        display: none;
      }
      .dash:before {
        height: min-content;
      }
      .structure-footer {
        display: flex;
        padding-top: 2rem;
      }
      .race-results {
        background-color: #060516;
      }
      .raceResults--default {
        background: #060516;
      }
      .raceResults-rewards {
        background: #0c0b18;
      }
      .raceResults-dailyChallenges {
        background: #0c0b18;
      }
      .g-b--7of12 {
        background: #060516;
      }
      .footer-nav {
        background: #0c0b18;
      }
      .nav-list {
        background: #0c0b18;
      }
      .nav {
        background: #0c0b18;
        border-bottom: 1px solid #14141b;
      }
      .btn--primary {
        background: #403dae;
      }
      .btn--primary:hover {
        background: #8a1bdf;
      }
      .btn--secondary {
        background: #5b048a;
      }
      .btn--secondary:hover {
        background: #8d11d0;
      }
      .gridTable--raceResults .gridTable-cell {
        background: #0c0b18;
      }
      .gridTable-cell {
        background: #0c0b18;
      }
      .dashShield-layer {
        display: none;
      }
      .dash-center {
        background: #06051687;
      }
      .nt-stats-right-section {
        background: #060516;
      }
      .nt-stats-daily-challenges {
        background: #060516;
      }
      .nt-stats-body {
        background: #0c0b18;
      }
    `;
  }

  const dashElement = document.querySelector(".dash");
  const container = document.querySelector(".structure-content div");

  if (dashElement) {
    const displayContainer = document.createElement("div");
    displayContainer.style.display = "flex";
    displayContainer.style.gap = "2rem";
    displayContainer.style.marginTop = "10px";
    displayContainer.style.fontSize = "20px";
    displayContainer.style.color = "#6864f6";
    displayContainer.style.background = "rgba(6, 5, 22, 0.8)";
    displayContainer.style.padding = "10px";
    displayContainer.style.borderRadius = "5px";
    displayContainer.style.zIndex = "9999";
    displayContainer.innerHTML = `
      <div>WPM: 0 <span id="targetWPMValue" style="margin-left: 10px;">Target WPM: 100</span></div>
      <div>
        <label for="targetWPM" style="margin-right: 10px;">Target WPM:</label>
        <button id="decreaseWPM" style="margin-right: 5px;">-</button>
        <input type="number" id="targetWPM" min="50" max="200" value="100" readonly>
        <button id="increaseWPM" style="margin-left: 5px;">+</button>
      </div>
      <div>Accuracy: 0%</div>
      <div>
        <label for="cursorType" style="margin-right: 10px;">Cursor Type:</label>
        <button id="cursorTypeButton">${
          currentCursorType.charAt(0).toUpperCase() + currentCursorType.slice(1)
        }</button>
      </div>
      <div>
        <label for="cursorSpeed" style="margin-right: 10px;">Cursor Speed:</label>
        <button id="cursorSpeedButton">${
          currentCursorSpeed.charAt(0).toUpperCase() +
          currentCursorSpeed.slice(1)
        }</button>
      </div>
    `;

    container.appendChild(displayContainer);

    const wpmDisplay = displayContainer.querySelector("div:nth-child(1)");
    const targetWPMValueDisplay =
      displayContainer.querySelector("#targetWPMValue");
    const accuracyDisplay = displayContainer.querySelector("div:nth-child(3)");
    const targetWPMInput = displayContainer.querySelector("#targetWPM");
    const increaseWPMButton = displayContainer.querySelector("#increaseWPM");
    const decreaseWPMButton = displayContainer.querySelector("#decreaseWPM");
    const cursorTypeButton =
      displayContainer.querySelector("#cursorTypeButton");
    const cursorSpeedButton =
      displayContainer.querySelector("#cursorSpeedButton");

    const savedTargetWPM = localStorage.getItem("targetWPM") || "100";
    targetWPMInput.value = savedTargetWPM;
    targetWPMValueDisplay.textContent = `Target WPM: ${savedTargetWPM}`;

    function updateTargetWPM(value) {
      const targetWPM = Math.max(50, Math.min(200, parseInt(value, 10)));
      targetWPMInput.value = targetWPM;
      targetWPMValueDisplay.textContent = `Target WPM: ${targetWPM}`;
      localStorage.setItem("targetWPM", targetWPM);
    }

    increaseWPMButton.addEventListener("click", function () {
      updateTargetWPM(parseInt(targetWPMInput.value, 10) + 5);
    });

    decreaseWPMButton.addEventListener("click", function () {
      updateTargetWPM(parseInt(targetWPMInput.value, 10) - 5);
    });

    const cursorTypes = ["none", "block", "line"];
    const cursorSpeeds = ["slow", "medium", "fast"];

    cursorTypeButton.addEventListener("click", function () {
      let currentIndex = cursorTypes.indexOf(currentCursorType);
      currentCursorType = cursorTypes[(currentIndex + 1) % cursorTypes.length];
      cursorTypeButton.textContent =
        currentCursorType.charAt(0).toUpperCase() + currentCursorType.slice(1);
      localStorage.setItem("cursorType", currentCursorType);
      updateStyles();
    });

    cursorSpeedButton.addEventListener("click", function () {
      let currentIndex = cursorSpeeds.indexOf(currentCursorSpeed);
      currentCursorSpeed =
        cursorSpeeds[(currentIndex + 1) % cursorSpeeds.length];
      cursorSpeedButton.textContent =
        currentCursorSpeed.charAt(0).toUpperCase() +
        currentCursorSpeed.slice(1);
      localStorage.setItem("cursorSpeed", currentCursorSpeed);
      updateStyles();
    });

    const fetchStats = () => {
      const wpmElement = document.querySelector(
        ".dash-metrics .list-item:nth-child(1) .g-b--8of12 .h4"
      );
      const accuracyElement = document.querySelector(
        ".dash-metrics .list-item:nth-child(2) .g-b--8of12 .h4"
      );
      const targetWPM = parseInt(localStorage.getItem("targetWPM"), 10) || 100;

      const green = "#00FF7F";
      const yellow = "#FFD700";
      const red = "red";

      if (wpmElement) {
        const wpmValue = parseInt(wpmElement.textContent, 10) || 0;

        if (wpmValue >= targetWPM - 5 && wpmValue <= targetWPM + 5) {
          wpmDisplay.style.color = green;
        } else if (wpmValue < targetWPM - 10) {
          wpmDisplay.style.color = red;
        } else if (wpmValue < targetWPM - 5) {
          wpmDisplay.style.color = yellow;
        } else if (wpmValue > targetWPM + 5) {
          wpmDisplay.style.color = "blue";
        }

        wpmDisplay.innerHTML = `WPM: ${wpmValue} <span id="targetWPMValue" style="margin-left: 10px;">Target WPM: ${targetWPM}</span>`;
      }

      if (accuracyElement) {
        const accuracyValue = parseFloat(accuracyElement.textContent) || 0;

        if (accuracyValue < 94) {
          accuracyDisplay.style.color = red;
        } else if (accuracyValue >= 94 && accuracyValue < 96) {
          accuracyDisplay.style.color = yellow;
        } else {
          accuracyDisplay.style.color = green;
        }
        accuracyDisplay.textContent = `Accuracy: ${accuracyValue}%`;
      }
    };

    // Height slider
    const heightLabel = document.createElement("label");
    heightLabel.textContent = "Adjust Height:";
    heightLabel.style.color = "#00FF7F";
    heightLabel.style.display = "block";
    heightLabel.style.marginTop = "10px";

    const heightSlider = document.createElement("input");
    heightSlider.type = "range";
    heightSlider.min = "100";
    heightSlider.max = "1000";
    const savedHeight = localStorage.getItem("dashHeight") || "500";
    dashElement.style.height = `${savedHeight}px`;
    heightSlider.value = savedHeight;

    heightSlider.style.width = "100%";
    heightSlider.style.marginTop = "5px";
    heightSlider.style.cursor = "pointer";

    container.appendChild(heightLabel);
    container.appendChild(heightSlider);

    heightSlider.addEventListener("input", function () {
      const heightValue = heightSlider.value;
      dashElement.style.height = `${heightValue}px`;
      localStorage.setItem("dashHeight", heightValue);
    });

    // Font size slider
    const fontSizeLabel = document.createElement("label");
    fontSizeLabel.textContent = "Adjust Font Size:";
    fontSizeLabel.style.color = "#00FF7F";
    fontSizeLabel.style.display = "block";
    fontSizeLabel.style.marginTop = "10px";

    const fontSizeSlider = document.createElement("input");
    fontSizeSlider.type = "range";
    fontSizeSlider.min = "20";
    fontSizeSlider.max = "80";
    fontSizeSlider.value = localStorage.getItem("dashFontSize") || "40";

    fontSizeSlider.style.width = "100%";
    fontSizeSlider.style.marginTop = "5px";
    fontSizeSlider.style.cursor = "pointer";

    container.appendChild(fontSizeLabel);
    container.appendChild(fontSizeSlider);

    fontSizeSlider.addEventListener("input", function () {
      const newFontSize = fontSizeSlider.value;
      localStorage.setItem("dashFontSize", newFontSize);
      updateStyles();
    });

    setInterval(fetchStats, 100);

    updateStyles();
  }

  setInterval(() => {
    const experimentDiv = document.querySelector('.experiment');
    const lastSlider = container.querySelector('input[type="range"]:last-of-type');
    if (experimentDiv && lastSlider) {
      const experimentParent = experimentDiv.parentElement;
      if (experimentParent !== lastSlider.parentElement) {
        lastSlider.parentElement.appendChild(experimentDiv);
      }
    }
  }, 500);

  // Retain scroll position
  window.addEventListener('beforeunload', () => {
    localStorage.setItem('scrollPosition', window.scrollY);
  });

  window.addEventListener('load', () => {
    setTimeout(() => {
      const scrollPosition = localStorage.getItem('scrollPosition');
      if (scrollPosition) {
        console.log('Restoring scroll position');
        window.scrollTo(0, parseInt(scrollPosition, 10));
      }
    }, 1000);
  });
})();