ChatGPT GDScript Syntax Highlighter

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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);

})();