Keystroke Visualizer

A lightweight Tampermonkey HUD that displays real-time keypresses with a terminal-inspired aesthetic.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Keystroke Visualizer
// @namespace    http://tampermonkey.net/
// @version      2026-03-12
// @description  A lightweight Tampermonkey HUD that displays real-time keypresses with a terminal-inspired aesthetic.
// @author       Samir
// @match        https://*/*
// @icon         https://www.clipartmax.com/png/middle/152-1524168_open-tab-key-icon-png.png
// @grant        none
// @license      MIT 
// ==/UserScript==

(function() {
    'use strict';

    const display = document.createElement('div');
    display.id = 'tm-keystroke-display';
    display.innerText = 'Press key...';

    const style = document.createElement('style');
    style.innerHTML = `
        #tm-keystroke-display {
            position: fixed;
            top: 50px;
            right: 50px;
            padding: 12px 20px;
            /* Restored original colors */
            background: rgba(0, 0, 0, 0.85);
            color: #00ff00; /* Green */
            border: 1px solid #444; /* Original border */

            font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
            font-size: 19px;
            font-weight: bold;
            border-radius: 8px;
            z-index: 1000000;
            cursor: move;
            user-select: none;
            box-shadow: 0 4px 15px rgba(0,0,0,0.4);
            min-width: 100px;
            text-align: center;
            pointer-events: auto;
            opacity: 1;
        }

        /* Blink animation for repeated keys */
        .key-press-animation {
            animation: key-blink 0.12s ease-out;
        }

        @keyframes key-blink {
            0% {  background: rgba(30, 30, 30, 0.9); scale: 0.8; color: #06402B} /* Dim and slightly lighter bg */
            100% {  background: rgba(0, 0, 0, 0.85); scale: 1; color: #00ff00} /* Back to normal */
        }
    `;
    document.head.appendChild(style);
    document.body.appendChild(display);

    window.addEventListener('keydown', (e) => {
        let keyText = [];
        if (e.metaKey) keyText.push('⌘');
        if (e.ctrlKey) keyText.push('Ctrl');
        if (e.altKey) keyText.push('⌥');
        if (e.shiftKey && e.key !== 'Shift') keyText.push('⇧');

        const mainKey = e.key === ' ' ? 'SPACE' : e.key.toUpperCase();

        if (!['META', 'CONTROL', 'ALT', 'SHIFT'].includes(mainKey)) {
            keyText.push(mainKey);
        }

        display.innerText = keyText.join(' + ');

        display.classList.remove('key-press-animation');
        void display.offsetWidth; // Force reflow
        display.classList.add('key-press-animation');

        clearTimeout(display.timeout);
        display.timeout = setTimeout(() => {
            display.innerText = '...';
            display.classList.remove('key-press-animation');
        }, 1800);
    }, true);

    //draggable
    let isDragging = false;
    let offset = { x: 0, y: 0 };

    display.addEventListener('mousedown', (e) => {
        isDragging = true;
        offset.x = e.clientX - display.getBoundingClientRect().left;
        offset.y = e.clientY - display.getBoundingClientRect().top;
    });

    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        display.style.left = (e.clientX - offset.x) + 'px';
        display.style.top = (e.clientY - offset.y) + 'px';
        display.style.right = 'auto';
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
    });

})();