ChatGPT GDScript Syntax Highlighter

Highlights GDScript code in the ChatGPT web interface using Prism.js.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         ChatGPT GDScript Syntax Highlighter
// @name:zh-CN   ChatGPT GDScript 代码高亮
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Highlights GDScript code in the ChatGPT web interface using Prism.js.
// @description:zh-CN 为 ChatGPT 网页版的 GDScript 代码片段提供语法高亮,自动适配暗色/亮色主题,基于 Prism.js。
// @author       Anonymous
// @match        https://chatgpt.com/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-gdscript.min.js
// @grant        GM_addStyle
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    if (typeof Prism === 'undefined') {
        console.error('❌ [GDScript Highlighter] 核心库 Prism.js 未加载。');
        return;
    }

    // 1. 创建 TrustedHTML 策略 (应对潜在的 CSP 限制)
    let ttPolicy;
    if (window.trustedTypes && window.trustedTypes.createPolicy) {
        try {
            const policyName = 'gdscript-policy-gpt-' + Math.random().toString(36).substring(2, 9);
            ttPolicy = window.trustedTypes.createPolicy(policyName, {
                createHTML: (string) => string
            });
        } catch (e) {
            console.error('❌ [GDScript Highlighter] TrustedTypes 策略创建失败:', e);
        }
    }

    // 2. 实时检测 ChatGPT 网页的实际主题模式
    function updateThemeMode() {
        const textColor = window.getComputedStyle(document.body).color;
        const match = textColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
        let isDarkMode = true;

        if (match) {
            const r = parseInt(match[1], 10);
            const g = parseInt(match[2], 10);
            const b = parseInt(match[3], 10);
            // 通过 RGB 计算视觉亮度 (Luma)
            const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
            isDarkMode = luma > 128; // 亮度大于 128 (文字发白),即为暗色背景
        }
        document.documentElement.setAttribute('data-gds-theme', isDarkMode ? 'dark' : 'light');
    }

    const themeObserver = new MutationObserver((mutations) => {
        for (let m of mutations) {
            if (m.type === 'attributes' && (m.attributeName === 'class' || m.attributeName === 'style' || m.attributeName === 'data-theme')) {
                updateThemeMode();
                break;
            }
        }
    });
    // ChatGPT 的主题通常挂载在 html class 上(例如 "light" 或 "dark")
    themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'style', 'data-theme'] });
    themeObserver.observe(document.body, { attributes: true, attributeFilter: ['class', 'style'] });
    updateThemeMode();

    // 3. 注入统一的高亮样式 (兼容 ChatGPT 的 div 容器)
    GM_addStyle(`
        .gdscript-injected {
            background: transparent !important;
            text-shadow: none !important;
            font-family: inherit !important;
            tab-size: 4 !important;
        }

        /* ====== 浅色主题 ====== */
        html[data-gds-theme="light"] .gdscript-injected { color: #24292e !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.comment { color: #6a737d !important; font-style: italic !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.punctuation { color: #24292e !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.keyword { color: #d73a49 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.function { color: #6f42c1 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.string { color: #032f62 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.number { color: #005cc5 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.class-name,
        html[data-gds-theme="light"] .gdscript-injected .token.builtin { color: #e36209 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.operator { color: #d73a49 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.boolean,
        html[data-gds-theme="light"] .gdscript-injected .token.property,
        html[data-gds-theme="light"] .gdscript-injected .token.constant { color: #005cc5 !important; }
        html[data-gds-theme="light"] .gdscript-injected .token.variable { color: #e36209 !important; }

        /* ====== 深色主题 ====== */
        html[data-gds-theme="dark"] .gdscript-injected { color: #d4d4d4 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.comment { color: #6a9955 !important; font-style: italic !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.punctuation { color: #d4d4d4 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.keyword { color: #569cd6 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.function { color: #dcdcaa !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.string { color: #ce9178 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.number { color: #b5cea8 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.class-name,
        html[data-gds-theme="dark"] .gdscript-injected .token.builtin { color: #4ec9b0 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.operator { color: #d4d4d4 !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.boolean,
        html[data-gds-theme="dark"] .gdscript-injected .token.property,
        html[data-gds-theme="dark"] .gdscript-injected .token.variable { color: #9cdcfe !important; }
        html[data-gds-theme="dark"] .gdscript-injected .token.constant { color: #4fc1ff !important; }
    `);

    // 4. 执行 GDScript 识别与渲染 (适配 ChatGPT 结构)
    function highlightGDScript() {
        const pres = document.querySelectorAll('pre');

        pres.forEach(pre => {
            // 查找顶部标题栏区域,ChatGPT 通常在这里标明语言
            const header = pre.querySelector('.bg-token-bg-elevated-secondary');

            // 验证是否包含 gdscript 标识
            if (header && header.textContent.toLowerCase().includes('gdscript')) {
                // ChatGPT 代码正文放置在 .cm-content 内
                const contentDiv = pre.querySelector('.cm-content');
                if (contentDiv) {
                    // 使用 innerText 可以自然提取出带 \n 的纯文本
                    const rawText = contentDiv.innerText;

                    // 避免打断流式输出
                    if (contentDiv.dataset.rawText === rawText) return;
                    contentDiv.dataset.rawText = rawText;

                    contentDiv.classList.add('gdscript-injected', 'language-gdscript');

                    // 利用 Prism.js 生成标准高亮 HTML
                    const highlightedHTML = Prism.highlight(rawText, Prism.languages.gdscript, 'gdscript');

                    // 核心适配:将 \n 转换回 <br>,以兼容 ChatGPT 的 CodeMirror div 结构
                    const finalHTML = highlightedHTML.replace(/\n/g, '<br>');

                    if (ttPolicy) {
                        contentDiv.innerHTML = ttPolicy.createHTML(finalHTML);
                    } else {
                        contentDiv.innerHTML = finalHTML;
                    }
                }
            }
        });
    }

    // 5. 监听页面变化,适配流式输出
    let debounceTimer = null;
    const domObserver = new MutationObserver(() => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(highlightGDScript, 300);
    });

    domObserver.observe(document.body, { childList: true, subtree: true, characterData: true });

    // 初始化执行
    setTimeout(highlightGDScript, 500);

})();