您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Race data
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/514399/1476019/raceData.js
// ==UserScript== // @name Session Info // @namespace https://gf.qytechs.cn/users/1331131-tensorflow-dvorak // @version 2024-10-26 // @description Race data // @match *://*.nitrotype.com/race // @match *://*.nitrotype.com/race/* // @require https://update.gf.qytechs.cn/scripts/501960/1418069/findReact.js // @license MIT // @grant none // ==/UserScript== (function () { "use strict"; let sessionStartTime; let raceTimes = []; let averageWPM = 0; let cardColor = localStorage.getItem("nt_cardColor") || "#1a1a2e"; let buttonColor = localStorage.getItem("nt_buttonColor") || "#5a67d8"; function initializeSessionData() { const persistData = localStorage.getItem("persist:nt"); if (persistData) { const parsedData = JSON.parse(JSON.parse(persistData).user); sessionStartTime = parseInt(localStorage.getItem("sessionStartTime")) || Date.now(); raceTimes = JSON.parse(localStorage.getItem("raceTimes")) || []; averageWPM = parsedData.avgSpeed || 0; if (!localStorage.getItem("sessionStartTime")) { localStorage.setItem("sessionStartTime", sessionStartTime); } return parsedData.sessionRaces || 0; } return 0; } let sessionRaces = initializeSessionData(); function createPostRaceInfo() { const dashElement = document.querySelector(".dash"); const settingsElement = document.querySelector( ".nitro-monkey__settings-container" ); if (dashElement && settingsElement) { const postRaceContainer = document.createElement("div"); postRaceContainer.classList.add("nitro-monkey__pr-container"); postRaceContainer.style.display = "flex"; postRaceContainer.style.justifyContent = "center"; postRaceContainer.style.alignItems = "center"; postRaceContainer.style.padding = "0.5rem"; postRaceContainer.style.backgroundColor = cardColor; postRaceContainer.style.borderRadius = "8px"; postRaceContainer.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.15)"; postRaceContainer.style.width = "auto"; postRaceContainer.style.height = "fit-content"; postRaceContainer.style.color = "#e0e0e0"; postRaceContainer.style.fontSize = "0.9rem"; postRaceContainer.style.marginBottom = "1rem"; postRaceContainer.innerHTML = ` <table style="width: auto; border-collapse: collapse; font-size: 0.9rem;"> <tbody> <tr> <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Session Races:</td> <td id="sessionValue" style="padding: 0.4rem; text-align: right;">${sessionRaces}</td> </tr> <tr> <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Races/hr:</td> <td id="hourlyRaces" style="padding: 0.4rem; text-align: right;">${calculateRacesLastHour()}</td> </tr> <tr> <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Est. Races/hr:</td> <td id="estimatedRaces" style="padding: 0.4rem; text-align: right;">${ localStorage.getItem("estimatedRaces") || 0 }</td> </tr> <tr> <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Average WPM:</td> <td id="averageWPM" style="padding: 0.4rem; text-align: right;">${averageWPM}</td> </tr> <tr> <td colspan="2" style="text-align: center; padding: 0.5rem;"> <button id="resetButton" style="padding: 0.3rem 0.8rem; font-size: 0.85rem; border: none; border-radius: 5px; background-color: ${buttonColor}; color: #1a1a2e; cursor: pointer;">Reset</button> </td> </tr> </tbody> </table> `; settingsElement.insertBefore( postRaceContainer, settingsElement.firstChild ); document .getElementById("resetButton") .addEventListener("click", resetSessionData); return true; } return false; } const getTypedCharacterCount = () => { return document.querySelectorAll(".dash-letter.is-correct.is-typed") ?.length; }; const getTotalCharacters = () => { return document.querySelectorAll(".dash-letter").length - 1; }; function calculateRacesLastHour() { const oneHourAgo = Date.now() - 1000 * 60 * 60; const recentRaces = raceTimes.filter( (raceTime) => raceTime.timestamp >= oneHourAgo ); return recentRaces.length; } function calculateEstimatedRacesPerHour() { if (raceTimes.length === 0) return 0; const avgRaceTimeInHours = raceTimes.reduce((a, b) => a + b.duration, 0) / raceTimes.length / (1000 * 60 * 60); const estimatedRacesPerHour = (1 / avgRaceTimeInHours).toFixed(1); localStorage.setItem("estimatedRaces", estimatedRacesPerHour); return estimatedRacesPerHour; } function resetSessionData() { sessionStartTime = Date.now(); raceTimes = []; document.getElementById("estimatedRaces").textContent = 0; document.getElementById("hourlyRaces").textContent = 0; localStorage.setItem("sessionStartTime", sessionStartTime); localStorage.setItem("raceTimes", JSON.stringify(raceTimes)); localStorage.setItem("estimatedRaces", 0); console.log("Session data reset."); } const updateSessionInfo = () => { const typedCharacters = getTypedCharacterCount(); const totalCharactersInRace = getTotalCharacters(); if ( typedCharacters === totalCharactersInRace && totalCharactersInRace > 0 ) { const currentTime = Date.now(); const persistData = localStorage.getItem("persist:nt"); if (persistData) { const parsedPersistData = JSON.parse(persistData); const user = JSON.parse(parsedPersistData.user); sessionRaces = user.sessionRaces = (user.sessionRaces || 0) + 1; averageWPM = user.avgSpeed || 0; parsedPersistData.user = JSON.stringify(user); localStorage.setItem("persist:nt", JSON.stringify(parsedPersistData)); } const raceDuration = currentTime - (raceTimes.length > 0 ? raceTimes[raceTimes.length - 1].timestamp : sessionStartTime); raceTimes.push({ timestamp: currentTime, duration: raceDuration }); localStorage.setItem("raceTimes", JSON.stringify(raceTimes)); raceTimes = raceTimes.filter( (race) => race.timestamp >= Date.now() - 1000 * 60 * 60 ); document.getElementById("sessionValue").textContent = sessionRaces; document.getElementById("averageWPM").textContent = averageWPM; document.getElementById("hourlyRaces").textContent = calculateRacesLastHour(); const estimatedRaces = calculateEstimatedRacesPerHour(); document.getElementById("estimatedRaces").textContent = estimatedRaces; clearInterval(updateUI); console.log( "Race Complete. Session Races:", sessionRaces, "Average WPM:", averageWPM ); } }; const interval = setInterval(() => { if (createPostRaceInfo()) { clearInterval(interval); } }, 500); const updateUI = setInterval(() => { updateSessionInfo(); }, 500); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址