NitroType Perfect Nitros with SFB Highlighting (QWERTY, DVORAK, COLEMAK)

Highlights the largest words, single-finger bigrams (SFBs), and custom words/phrases for QWERTY, DVORAK, or COLEMAK layouts. Adds custom color coding for hand-specific characters like 'b' and 'y'.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         NitroType Perfect Nitros with SFB Highlighting (QWERTY, DVORAK, COLEMAK)
// @namespace    https://greasyfork.org/users/1331131-tensorflow-dvorak
// @version      2.5.1
// @description  Highlights the largest words, single-finger bigrams (SFBs), and custom words/phrases for QWERTY, DVORAK, or COLEMAK layouts. Adds custom color coding for hand-specific characters like 'b' and 'y'.
// @author       Ray Adams/Nate Dogg, Modified by TensorFlow - Dvorak
// @match        https://www.nitrotype.com/race
// @match        https://www.nitrotype.com/race/*
// @run-at       document-end
// @grant        none
// @license      MIT
// ==/UserScript==

(() => {
    // CHANGE THIS to 'QWERTY', 'DVORAK' or 'COLEMAK'
    const keyboardLayout = 'QWERTY';

    const options = {
        highlightColor: '#1b1c25',
        wordHighlightColor: '#5b048a',
        singleFingerBigramColor: '#403dae',
        redForRightHand: 'red',
        blueForLeftHand: 'blue',
        intervalMs: 100
    };

    // Custom wordlist for highlighting (add words or phrases you aim to type differently here)
    const customWords = new Set(['number', "you're"]);

    // SFBs for different keyboard layouts (remove bigrams you don't want to work on)
    const SFBs = new Map([
        ['QWERTY', ['ed', 'de', 'fr', 'rf', 'gt', 'tg', 'bv', 'vb', 'ju', 'uj', 'ki', 'ik', 'nm', 'mn', 'nu', 'un']],
        ['DVORAK', ['pu', 'up', 'ui', 'iu', 'pi', 'ip', 'je', 'ej']],
        ['COLEMAK', ['']]
    ]);

    // Define different layouts.
    const layoutKeys = {
        QWERTY: {
            leftHand: 'qwertasdfgzxcvb',
            rightHand: 'yuiophjklmn',
            targetChars: ['b'] // 'b' is typed by either hand and will be color coded based on which hand should be used. Red = right Blue = Left.
        },
        DVORAK: {
            leftHand: 'aoeuqjkxiyp',
            rightHand: 'dhtsnfgcrlbmwvz',
            targetChars: ['x']
        },
        COLEMAK: {
            leftHand: 'qwfpbjluyarst',
            rightHand: 'neiohjkxvmzcdg',
            targetChars: ['b']
        }
    };

    const client = () => {
        const dashLetters = document.querySelector('.dash-letter');
        if (dashLetters) {
            clearInterval(intervalId);

            // Get all words from the race
            const wordList = [...document.getElementsByClassName('dash-word')].map(word => word.textContent.replace(/\s/g, ''));

            // Find the largest words
            const maxLength = Math.max(...wordList.map(word => word.length));
            const largestWords = wordList.filter(word => word.length === maxLength);

            // Highlight largest words and bigrams
            wordList.forEach((word, index) => {
                const wordElement = document.getElementsByClassName('dash-word')[index];

                // Highlight largest words
                if (largestWords.includes(word)) {
                    wordElement.style.backgroundColor = options.highlightColor;
                }

                // Highlight custom words/phrases
                highlightCustomWords(wordElement);

                // Highlight single-finger bigrams
                highlightSingleFingerBigrams(wordElement);

                // Highlight specific target chars if followed by the same hand's character
                highlightTargetCharsWithSameHand(wordElement, keyboardLayout);
            });
        }
    };

    const highlightCustomWords = (wordElement) => {
        const text = wordElement.textContent.toLowerCase();

        // Loop over each word or phrase in the custom wordlist
        customWords.forEach(word => {
            const startIndex = text.indexOf(word.toLowerCase());
            if (startIndex !== -1) {
                for (let i = 0; i < word.length; i++) {
                    wordElement.querySelector(`.dash-letter:nth-child(${startIndex + i + 1})`).style.color = options.wordHighlightColor;
                }
            }
        });
    };

    const highlightSingleFingerBigrams = (wordElement) => {
        const text = wordElement.textContent;

        // Loop over the text and highlight single-finger bigrams
        for (let i = 0; i < text.length - 1; i++) {
            const bigram = text[i] + text[i + 1];
            if (SFBs.get(keyboardLayout).includes(bigram)) {
                wordElement.querySelector(`.dash-letter:nth-child(${i + 1})`).style.color = options.singleFingerBigramColor;
                wordElement.querySelector(`.dash-letter:nth-child(${i + 2})`).style.color = options.singleFingerBigramColor;
                i++;
            }
        }
    };

    const highlightTargetCharsWithSameHand = (wordElement, layout) => {
        const { leftHand, rightHand, targetChars } = layoutKeys[layout];
        const text = wordElement.textContent.toLowerCase();

        // Check if target characters are followed by the same hand
        for (let i = 0; i < text.length - 1; i++) {
            const currentChar = text[i];
            const nextChar = text[i + 1];

            if (targetChars.includes(currentChar)) {
                if (leftHand.includes(currentChar) && leftHand.includes(nextChar)) {
                    // Highlight target char to suggest typing with the right hand
                    wordElement.querySelector(`.dash-letter:nth-child(${i + 1})`).style.color = options.redForRightHand;
                } else if (rightHand.includes(currentChar) && rightHand.includes(nextChar)) {
                    // Highlight target char to suggest typing with the left hand
                    wordElement.querySelector(`.dash-letter:nth-child(${i + 1})`).style.color = options.blueForLeftHand;
                }
            }
        }
    };

    const intervalId = setInterval(client, options.intervalMs);
})();