JVCode

Améliore l'affichage du code sur jeuxvideo.com : coloration syntaxique, numéros de ligne, bouton copier, design moderne.

От 20.12.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         JVCode
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Améliore l'affichage du code sur jeuxvideo.com : coloration syntaxique, numéros de ligne, bouton copier, design moderne.
// @author       FaceDePet
// @match        https://www.jeuxvideo.com/forums/*
// @match        https://www.jeuxvideo.com/messages-prives/*
// @icon         https://image.noelshack.com/fichiers/2017/04/1485268586-hackeur-v1.png
// @require      https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js
// @resource     HLJS_CSS https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css
// @license      MIT
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_setClipboard
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // (Highlight.js + Custom UI
    const hljsCss = GM_getResourceText("HLJS_CSS");
    GM_addStyle(hljsCss);

    const customCss = `
        /* Conteneur principal */
        .jv-enhanced-code {
            background-color: #282c34;
            border-radius: 8px;
            margin: 10px 0;
            overflow: hidden;
            font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 13px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.3);
            border: 1px solid #3e4451;
            position: relative;
            max-width: 100%;
            display: flex;
            flex-direction: column;
        }

        /* En-tête */
        .jv-code-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            background-color: #21252b;
            padding: 5px 15px;
            border-bottom: 1px solid #181a1f;
            color: #abb2bf;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            user-select: none;
            flex-shrink: 0;
        }

        .jv-lang-badge {
            font-weight: bold;
            color: #61afef;
        }

        /* Bouton Copier */
        .jv-copy-btn {
            background: transparent;
            border: 1px solid #3e4451;
            color: #abb2bf;
            cursor: pointer;
            padding: 3px 8px;
            border-radius: 4px;
            font-size: 11px;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .jv-copy-btn:hover {
            background-color: #3e4451;
            color: white;
        }
        .jv-copy-btn.copied {
            border-color: #98c379;
            color: #98c379;
        }

        /* Corps du code */
        .jv-code-body {
            display: flex;
            width: 100%;
            min-width: 0;
        }

        /* Colonne des numéros de ligne */
        .jv-line-numbers {
            text-align: right;
            padding: 10px 10px 10px 15px;
            background-color: #282c34;
            color: #495162;
            border-right: 1px solid #3e4451;
            user-select: none;
            line-height: 1.5;
            flex-shrink: 0;
            z-index: 2;
        }

        /* Colonne du code */
        .jv-code-content {
            padding: 10px 15px;
            background-color: #282c34;
            color: #abb2bf;
            line-height: 1.5;
            tab-size: 4;
            flex-grow: 1;
            overflow-x: auto;
            min-width: 0;
        }

        .jv-code-content pre {
            margin: 0 !important;
            padding: 0 !important;
            border: none !important;
            background: none !important;
            display: inline-block;
            min-width: 100%;
        }

        .jv-code-content code.hljs {
            padding: 0;
            background: transparent;
            white-space: pre;
            overflow-x: visible;
        }

        .jv-code-content::-webkit-scrollbar {
            height: 8px;
            background-color: #282c34;
        }
        .jv-code-content::-webkit-scrollbar-thumb {
            background-color: #4b5363;
            border-radius: 4px;
        }
        .jv-code-content::-webkit-scrollbar-corner {
            background-color: #282c34;
        }
    `;
    GM_addStyle(customCss);

    function enhanceCodeBlocks(container = document) {
        const blocks = container.querySelectorAll('pre.pre-jv:not([data-processed="true"])');

        blocks.forEach(pre => {
            const codeElement = pre.querySelector('code.code-jv');
            if (!codeElement) return;

            pre.setAttribute('data-processed', 'true');

            let rawCode = codeElement.textContent;
            // enlève les retours chariots du tout début et toute fin, mais garde l'indentation
            rawCode = rawCode.replace(/^\s*\n/g, '').replace(/\n\s*$/g, '');

            if(rawCode === "") return;

            const highlightResult = hljs.highlightAuto(rawCode);
            const detectedLang = highlightResult.language || 'text';
            const highlightedCode = highlightResult.value;

            const lineCount = rawCode.split(/\r\n|\r|\n/).length;
            let lineNumbersHtml = '';
            for (let i = 1; i <= lineCount; i++) {
                lineNumbersHtml += `${i}\n`;
            }

            const wrapper = document.createElement('div');
            wrapper.className = 'jv-enhanced-code';

            wrapper.innerHTML = `
                <div class="jv-code-header">
                    <span class="jv-lang-badge">${detectedLang}</span>
                    <button class="jv-copy-btn" title="Copier le code">
                        <span>Copier</span>
                    </button>
                </div>
                <div class="jv-code-body">
                    <div class="jv-line-numbers"><pre>${lineNumbersHtml}</pre></div>
                    <div class="jv-code-content"><pre><code class="hljs ${detectedLang}">${highlightedCode}</code></pre></div>
                </div>
            `;

            const copyBtn = wrapper.querySelector('.jv-copy-btn');
            copyBtn.addEventListener('click', () => {
                navigator.clipboard.writeText(rawCode).then(() => {
                    const originalText = copyBtn.innerHTML;
                    copyBtn.innerHTML = '<span>Copié !</span>';
                    copyBtn.classList.add('copied');
                    setTimeout(() => {
                        copyBtn.innerHTML = '<span>Copier</span>';
                        copyBtn.classList.remove('copied');
                    }, 2000);
                }).catch(err => {
                    console.error('Erreur copie :', err);
                });
            });

            pre.style.display = 'none';
            pre.parentNode.insertBefore(wrapper, pre);
        });
    }

    enhanceCodeBlocks();

    // Observer
    const observer = new MutationObserver((mutations) => {
        let needsUpdate = false;
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) {
                        if (node.matches && node.matches('pre.pre-jv') || node.querySelector && node.querySelector('pre.pre-jv')) {
                            needsUpdate = true;
                        }
                        if (node.classList && (node.classList.contains('bloc-message-forum') || node.id === 'bloc-formulaire-forum')) {
                            needsUpdate = true;
                        }
                    }
                });
            }
        });
        if (needsUpdate) enhanceCodeBlocks();
    });

    const targetNode = document.getElementById('page-messages-forum') || document.body;
    observer.observe(targetNode, { childList: true, subtree: true });

})();