ChatGPT实时渲染(MaynorAI)

为ChatGPT添加代码实时预览功能,支持多种编程语言的实时渲染,提供类似Claude的代码预览体验

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

/* ==UserStyle== */
@name           ChatGPT实时渲染(MaynorAI)
@namespace      github.com/maynor/chatgpt-preview
@version        1.3.0
@description    为ChatGPT添加代码实时预览功能,支持多种编程语言的实时渲染,提供类似Claude的代码预览体验
@author         Maynor
@preprocessor   default
@var           checkbox enablePreview "Enable Preview" 1
@var           select   previewStyle "Preview Style" {
  "default": "Default",
  "minimal": "Minimal",
  "full": "Full Width"
}
@license        GPL-2.0-only
@homepageURL    https://github.com/maynor/chatgpt-preview
@supportURL     https://github.com/maynor/chatgpt-preview/issues
==/UserStyle== */

@-moz-document domain("chat.openai.com"), domain("chatgpt.com"), domain("chatgpt-plus.top"), domain("maynor1024.live") {
    /* Your CSS code here */
}
(function () {
    "use strict";
    // 使用 MutationObserver 监听 DOM 变化
    const observer = new MutationObserver(debounce(xuanranHTML, 500));
    const createIframeObserver = new MutationObserver(
        debounce(createIframe, 500)
    );

    // 观察目标节点的变化
    observer.observe(document.body, { childList: true, subtree: true });
    createIframeObserver.observe(document.body, {
        childList: true,
        subtree: true,
    });

    // 首次调用渲染
    window.addEventListener("load", () => {
        setTimeout(xuanranHTML, 1000);
        // 创建一个iframe
        setTimeout(createIframe, 1000);
    });
})();

function createIframe() {
    // 判断是否已经创建 dynamicContentIframe
    if (document.getElementById("dynamicContentIframe")) {
        console.log("已经创建 dynamicContentIframe");
        return;
    }

    // 创建一个基于main标签的兄弟iframe元素
    const mainElement = document.querySelector("main");
    // mainElement.style.display = "flex";
    mainElement.style.overflow = "hidden";
    if (mainElement) {
        const iframe = document.createElement("iframe");
        iframe.id = "dynamicContentIframe"; // 添加id以便动态修改内容
        iframe.style.display = "relative";
        iframe.style.width = "100%";
        iframe.style.height = "100%";
        iframe.style.backgroundColor = "#FFFDF6"; // 添加奶白色背景
        // sandbox="allow-scripts"
        iframe.sandbox = "allow-scripts";
        iframe.srcdoc = "<html><body></body></html>";
        // 创建切换显示代码块的div
        const toggleCodeButton = document.createElement("div");
        toggleCodeButton.id = "toggleCodeButton";
        toggleCodeButton.style.position = "absolute";
        toggleCodeButton.style.top = "10px";
        toggleCodeButton.style.right = "40px";
        toggleCodeButton.style.cursor = "pointer";
        toggleCodeButton.innerHTML = `
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M4 6H16M4 10H16M4 14H16" stroke="black" stroke-width="2" stroke-linecap="round"/>
            </svg>
        `;
        toggleCodeButton.onclick = () => {
            // document.getElementById("dynamicContentIframe").contentWindow.document.body.innerHTML = "123";
            document.getElementById("codeContainer").style.display =
                document.getElementById("codeContainer").style.display ===
                "none"
                    ? "block"
                    : "none";
        };

        // 创建缩小按钮
        const minimizeButton = document.createElement("div");
        minimizeButton.id = "minimizeButton";
        minimizeButton.style.position = "absolute";
        minimizeButton.style.top = "10px";
        minimizeButton.style.right = "10px";
        minimizeButton.style.cursor = "pointer";
        minimizeButton.innerHTML = `
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M4 10H16" stroke="black" stroke-width="2" stroke-linecap="round"/>
            </svg>
        `;
        minimizeButton.onclick = () => {
            iframe.style.display =
                iframe.style.display === "none" ? "block" : "none";
            document.querySelector("main").style.display =
                document.querySelector("main").style.display === "block"
                    ? "flex"
                    : "block";
            document
                .querySelector(
                    "body > div.relative.flex.h-full.w-full.overflow-hidden.transition-colors.z-0 > div.flex-shrink-0.overflow-x-hidden.bg-token-sidebar-surface-primary.max-md\\:\\!w-0 > div > div > div > nav > div.flex.justify-between.flex.h-\\[60px\\].items-center.md\\:h-header-height > span > button"
                )
                .click();
        };
        //  toggleCodeButton 和 minimizeButton 添加到一个div下
        const buttonContainer = document.createElement("div");
        buttonContainer.id = "buttonContainer";
        buttonContainer.style.position = "relative";
        buttonContainer.style.top = "10px";
        buttonContainer.style.right = "10px";
        buttonContainer.appendChild(minimizeButton);
        buttonContainer.appendChild(toggleCodeButton);

        // 直接将 iframe 和按钮添加到 main 元素下
        mainElement.appendChild(iframe);
        mainElement.appendChild(buttonContainer);

        // 设置 main 元素为相对定位,以便正确定位最小化按钮
        mainElement.style.position = "relative";

        // mainElement.insertBefore(container, mainElement.nextSibling);
        // 创建一个用户存放显示代码的div
        const codeContainer = document.createElement("div");
        codeContainer.id = "codeContainer";
        codeContainer.style.width = "100%";
        codeContainer.style.height = "100%";
        codeContainer.style.zIndex = "1000";
        // codeContainer.style.backgroundColor = "#FFFDF6"; // 添加奶白色背景
        codeContainer.style.display = "block";
        codeContainer.style.overflow = "hidden";
        codeContainer.style.overflowY = "scroll";
        codeContainer.style.display = "none";
        mainElement.appendChild(codeContainer);
    } else {
        console.error("Main element not found");
    }
    // 显示
    document.getElementById("dynamicContentIframe").style.display = "block";
}
function xuanranHTML() {
    const codes = document.querySelectorAll(".overflow-y-auto.p-4 code");

    codes.forEach((codeElement) => {
        if (codeElement.classList.contains("processed")) {
            return;
        }

        codeElement.classList.add("processed");

        // 获取代码块类型
        const codeType = codeElement.parentNode.parentNode.children[0].innerText.toLowerCase();

        // 检查代码内容是否包含 SVG 标签
        const isSVGContent = codeElement.textContent.trim().startsWith('<svg');

        // 根据不同类型进行渲染
        switch(codeType) {
            case 'html':
                codeElement.parentNode.parentNode.style.display = "none";
                renderSmallWindow(codeElement);
                break;
            case 'svg':
            case 'xml':  // 添加对 xml 类型的支持
                if (isSVGContent) {  // 确认内容是 SVG
                    renderSVG(codeElement);
                } else {
                    console.log("不渲染: 非SVG的XML内容");
                }
                break;
            case 'mermaid':
                renderMermaid(codeElement);
                break;
            case 'pptx':
                renderPPTX(codeElement);
                break;
            default:
                console.log("不渲染: " + codeType);
        }
    });
}
function renderSmallWindow(codeElement) {
    // 创建组件容器
    const componentContainer = document.createElement("div");
    componentContainer.style.display = "flex";
    componentContainer.style.alignItems = "center";
    componentContainer.style.border = "1px solid #e5e7eb";
    componentContainer.style.borderRadius = "8px";
    componentContainer.style.padding = "10px";
    componentContainer.style.backgroundColor = "#f9fafb";
    componentContainer.style.marginBottom = "20px";
    componentContainer.style.cursor = "pointer";

    // 创建图标容器
    const iconContainer = document.createElement("div");
    iconContainer.style.borderRight = "1px solid #e5e7eb";
    iconContainer.style.paddingRight = "10px";

    // 创建 SVG 图标
    const svgIcon = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "svg"
    );
    svgIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svgIcon.setAttribute("style", "height: 24px; width: 24px; color: #6b7280;");
    svgIcon.setAttribute("fill", "none");
    svgIcon.setAttribute("viewBox", "0 0 24 24");
    svgIcon.setAttribute("stroke", "currentColor");
    svgIcon.setAttribute("stroke-width", "2");

    const pathElement = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "path"
    );
    pathElement.setAttribute("stroke-linecap", "round");
    pathElement.setAttribute("stroke-linejoin", "round");
    pathElement.setAttribute(
        "d",
        "M16 8c0-1.104-.9-2-2-2H6c-1.1 0-2 .896-2 2v8c0 1.104.9 2 2 2h8c1.1 0 2-.896 2-2V8zm-4 4h.01m-3 0h.01M7 12h.01m0 0h.01M12 7v.01M8 7v.01M7 7v.01M12 8v.01m-5 0v.01m-1 0v.01"
    );

    svgIcon.appendChild(pathElement);
    iconContainer.appendChild(svgIcon);

    // 创建文本容器
    const textContainer = document.createElement("div");
    textContainer.style.marginLeft = "10px";

    // 创建标题
    const title = document.createElement("h3");
    title.textContent = codeElement.parentNode.parentNode.children[0].innerText;
    title.style.margin = "0";
    title.style.fontSize = "16px";
    title.style.fontWeight = "600";
    title.style.color = "#374151";

    // 创建描述
    const description = document.createElement("p");
    description.textContent = "预览页面 / 刷新渲染";
    description.style.margin = "0";
    description.style.fontSize = "14px";
    description.style.color = "#6b7280";

    textContainer.appendChild(title);
    textContainer.appendChild(description);

    // 组装组件
    componentContainer.appendChild(iconContainer);
    componentContainer.appendChild(textContainer);

    // 添加点击事件,切换代码显示状态
    componentContainer.addEventListener("click", function () {
        const codeParent = codeElement.parentNode.parentNode;
        const mainElement = document.querySelector("main");
        // codeParent.style.display = codeParent.style.display === 'none' ? 'block' : 'none';
        // 隐藏展开左侧历史记录
        if (mainElement.style.display != "flex") {
            mainElement.style.display = "flex";
        }
        if (
            document.querySelector(
                "body > div.relative.flex.h-full.w-full.overflow-hidden.transition-colors.z-0 > div.flex-shrink-0.overflow-x-hidden.bg-token-sidebar-surface-primary.max-md\\:\\!w-0"
            ).style.width != "0px"
        ) {
            document
                .querySelector(
                    "body > div.relative.flex.h-full.w-full.overflow-hidden.transition-colors.z-0 > div.flex-shrink-0.overflow-x-hidden.bg-token-sidebar-surface-primary.max-md\\:\\!w-0 > div > div > div > nav > div.flex.justify-between.flex.h-\\[60px\\].items-center.md\\:h-header-height > span > button"
                )
                .click();
            // dynamicContentIframe
            document.getElementById("dynamicContentIframe").style.display =
                "block";
        }

        // iframe 赋值
        renderIframeContent(
            document.getElementById("dynamicContentIframe"),
            codeElement.textContent
        );
        // 拦截script标签

        // 设置代码容器代码
        // document.getElementById("codeContainer").innerHTML = codeElement.parentNode.parentNode.innerHTML;
        const codeContainer = document.getElementById("codeContainer");
        codeContainer.innerHTML = "";
        const clonedContent = codeElement.parentNode.parentNode.cloneNode(true);

        codeContainer.appendChild(clonedContent);
        codeContainer.childNodes[0].style.display = "contents";

        // 查找并处理按钮
        const buttons = clonedContent.querySelectorAll("button");
        buttons.forEach((button, index) => {
            console.log(button);
            if (button.className == "flex gap-1 items-center py-1") {
                button.addEventListener("click", function (event) {
                    button.innerText = " success √ ";
                    setTimeout(() => {
                        button.innerText = "Copy code";
                    }, 1000);
                });
            }
            button.addEventListener("click", function (event) {
                event.stopPropagation(); // 阻止事件冒泡到父元素
                // 找到原始按钮并模拟点击
                const originalButtons =
                    codeElement.parentNode.parentNode.querySelectorAll(
                        "button"
                    );
                if (originalButtons[index]) {
                    originalButtons[index].click();
                }
            });
        });

        // 添加事件委托到 codeContainer(用于处理代码元素的点击)
        codeContainer.addEventListener("click", function (event) {
            const clickedElement = event.target.closest("code");
            if (clickedElement) {
                // 模拟原始代码元素的点击行为
                const originalCodeElement = codeElement.closest("code");
                if (originalCodeElement) {
                    originalCodeElement.click();
                }
            }
        });
    });

    // 将组件插入到代码元素之前
    codeElement.parentNode.parentNode.insertAdjacentElement(
        "beforebegin",
        componentContainer
    );
    return componentContainer;
}
function renderIframeContent(iframe, content) {
    // console.log(__remixContext.state.loaderData.root.cspScriptNonce);
    // Add nonce to script tags in content
    const nonce = __remixContext.state.loaderData.root.cspScriptNonce;
    content = content.replace(/<script/g, `<script nonce="${nonce}"`);
    iframe.srcdoc = content;
    return;
    // 获取 iframe 内部的 document 对象
    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

    // 将 code ��的内容设置为 iframe 的内容
    iframeDoc.open();

    if (true) {
        // 刷新特效,true为有感刷新,!true为无痕刷新
        // iframeDoc.write(`loading`);
        // 生成一个随机的nonce值
        const nonce = generateNonce();

        // 在content中添加nonce
        // content = content.replace(/<script/g, `<script nonce="${nonce}"`);
        // 设置meta头替换
        // content = content.replace(/<meta/g, `<meta nonce="${nonce}"`);
        // let doms = new DOMParser().parseFromString(content, "text/html");

        // Remove script tags from content
        // content = content.replace(
        //     /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        //     ""
        // );
        // 插入
        iframeDoc.write(content);
        doms.querySelectorAll("script").forEach((script) => {
            return;
            console.log("script");
            const newScript = iframeDoc.createElement("script");
            newScript.textContent = script.textContent;
            newScript.nonce = nonce;
            // iframeDoc.documentElement.appendChild(newScript);
            // 创建一个blob URL来加载脚本
            const blob = new Blob([script.textContent], {
                type: "application/javascript",
            });
            const scriptUrl = URL.createObjectURL(blob);
            newScript.src = scriptUrl;
            script.onload = () => {
                console.log("加载完成");
                URL.revokeObjectURL(scriptUrl);
            };
            iframeDoc.documentElement.appendChild(newScript);
        });

        // setTimeout(() => iframeDoc.write(content), 100);
    } else {
        iframeDoc.write(content);
    }
    iframeDoc.close();
}

function debounce(func, wait) {
    let timeout;
    return function () {
        const context = this,
            args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(context, args), wait);
    };
}
function generateNonce() {
    return (
        Math.random().toString(36).substring(2, 15) +
        Math.random().toString(36).substring(2, 15)
    );
}

// 添加新的渲染函数
function renderSVG(codeElement) {
    // 创建组件容器
    const componentContainer = document.createElement("div");
    componentContainer.style.display = "flex";
    componentContainer.style.alignItems = "center";
    componentContainer.style.border = "1px solid #e5e7eb";
    componentContainer.style.borderRadius = "8px";
    componentContainer.style.padding = "10px";
    componentContainer.style.backgroundColor = "#f9fafb";
    componentContainer.style.marginBottom = "20px";
    componentContainer.style.cursor = "pointer";

    // 创建图标容器
    const iconContainer = document.createElement("div");
    iconContainer.style.borderRight = "1px solid #e5e7eb";
    iconContainer.style.paddingRight = "10px";

    // 创建 SVG 图标
    const svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svgIcon.setAttribute("style", "height: 24px; width: 24px; color: #6b7280;");
    svgIcon.setAttribute("fill", "none");
    svgIcon.setAttribute("viewBox", "0 0 24 24");
    svgIcon.setAttribute("stroke", "currentColor");
    svgIcon.setAttribute("stroke-width", "2");

    const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
    pathElement.setAttribute("stroke-linecap", "round");
    pathElement.setAttribute("stroke-linejoin", "round");
    pathElement.setAttribute("d", "M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2zm0 0V5");
    svgIcon.appendChild(pathElement);
    iconContainer.appendChild(svgIcon);

    // 创建文本容器
    const textContainer = document.createElement("div");
    textContainer.style.marginLeft = "10px";

    // 创建标题
    const title = document.createElement("h3");
    title.textContent = "SVG Preview";
    title.style.margin = "0";
    title.style.fontSize = "16px";
    title.style.fontWeight = "600";
    title.style.color = "#374151";

    // 创建描述
    const description = document.createElement("p");
    description.textContent = "点击切换显示/隐藏";
    description.style.margin = "0";
    description.style.fontSize = "14px";
    description.style.color = "#6b7280";

    textContainer.appendChild(title);
    textContainer.appendChild(description);

    // 组装组件
    componentContainer.appendChild(iconContainer);
    componentContainer.appendChild(textContainer);

    // 创建 SVG 预览容器
    const svgPreviewContainer = document.createElement("div");
    svgPreviewContainer.style.margin = "10px 0";
    svgPreviewContainer.style.padding = "20px";
    svgPreviewContainer.style.backgroundColor = "#fff";
    svgPreviewContainer.style.border = "1px solid #e5e7eb";
    svgPreviewContainer.style.borderRadius = "8px";
    svgPreviewContainer.style.display = "none";
    svgPreviewContainer.innerHTML = codeElement.textContent;

    // 创建代码容器
    const codeContainer = document.createElement("div");
    codeContainer.style.margin = "10px 0";
    codeContainer.style.display = "none";
    const clonedContent = codeElement.parentNode.parentNode.cloneNode(true);
    codeContainer.appendChild(clonedContent);
    codeContainer.childNodes[0].style.display = "block";

    // 添加点击事件
    componentContainer.addEventListener("click", function() {
        svgPreviewContainer.style.display = svgPreviewContainer.style.display === "none" ? "block" : "none";
        codeContainer.style.display = codeContainer.style.display === "none" ? "block" : "none";
    });

    // 将组件插入到代码元素的位置
    const parent = codeElement.parentNode.parentNode;
    parent.style.display = "none";
    parent.insertAdjacentElement("beforebegin", componentContainer);
    componentContainer.insertAdjacentElement("afterend", svgPreviewContainer);
    svgPreviewContainer.insertAdjacentElement("afterend", codeContainer);

    return componentContainer;
}

function renderMermaid(codeElement) {
    // 创建组件容器
    const componentContainer = document.createElement("div");
    componentContainer.style.display = "flex";
    componentContainer.style.alignItems = "center";
    componentContainer.style.border = "1px solid #e5e7eb";
    componentContainer.style.borderRadius = "8px";
    componentContainer.style.padding = "10px";
    componentContainer.style.backgroundColor = "#f9fafb";
    componentContainer.style.marginBottom = "20px";
    componentContainer.style.cursor = "pointer";

    // 创建图标容器
    const iconContainer = document.createElement("div");
    iconContainer.style.borderRight = "1px solid #e5e7eb";
    iconContainer.style.paddingRight = "10px";

    // 创建 SVG 图标
    const svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svgIcon.setAttribute("style", "height: 24px; width: 24px; color: #6b7280;");
    svgIcon.setAttribute("fill", "none");
    svgIcon.setAttribute("viewBox", "0 0 24 24");
    svgIcon.setAttribute("stroke", "currentColor");
    svgIcon.setAttribute("stroke-width", "2");

    const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
    pathElement.setAttribute("stroke-linecap", "round");
    pathElement.setAttribute("stroke-linejoin", "round");
    pathElement.setAttribute("d", "M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z");
    svgIcon.appendChild(pathElement);
    iconContainer.appendChild(svgIcon);

    // 创建文本容器
    const textContainer = document.createElement("div");
    textContainer.style.marginLeft = "10px";

    // 创建标题
    const title = document.createElement("h3");
    title.textContent = "Mermaid Diagram";
    title.style.margin = "0";
    title.style.fontSize = "16px";
    title.style.fontWeight = "600";
    title.style.color = "#374151";

    // 创建描述
    const description = document.createElement("p");
    description.textContent = "点击切换显示/隐藏";
    description.style.margin = "0";
    description.style.fontSize = "14px";
    description.style.color = "#6b7280";

    textContainer.appendChild(title);
    textContainer.appendChild(description);

    // 组装组件
    componentContainer.appendChild(iconContainer);
    componentContainer.appendChild(textContainer);

    // 创建 Mermaid 预览容器
    const mermaidPreviewContainer = document.createElement("div");
    mermaidPreviewContainer.style.margin = "10px 0";
    mermaidPreviewContainer.style.padding = "20px";
    mermaidPreviewContainer.style.backgroundColor = "#fff";
    mermaidPreviewContainer.style.border = "1px solid #e5e7eb";
    mermaidPreviewContainer.style.borderRadius = "8px";
    mermaidPreviewContainer.style.display = "none";

    // 创建 Mermaid 图表容器
    const mermaidContainer = document.createElement("div");
    mermaidContainer.className = "mermaid";
    mermaidContainer.textContent = codeElement.textContent;
    mermaidPreviewContainer.appendChild(mermaidContainer);

    // 创建代码容器
    const codeContainer = document.createElement("div");
    codeContainer.style.margin = "10px 0";
    codeContainer.style.display = "none";
    const clonedContent = codeElement.parentNode.parentNode.cloneNode(true);
    codeContainer.appendChild(clonedContent);
    codeContainer.childNodes[0].style.display = "block";

    // 添加点击事件
    componentContainer.addEventListener("click", function() {
        const newDisplayState = mermaidPreviewContainer.style.display === "none" ? "block" : "none";
        mermaidPreviewContainer.style.display = newDisplayState;
        codeContainer.style.display = newDisplayState;

        // 如果是显示状态,确保 Mermaid 图表被渲染
        if (newDisplayState === "block") {
            if (typeof mermaid === 'undefined') {
                // 动态加载 mermaid
                const script = document.createElement('script');
                script.src = 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js';
                script.onload = () => {
                    mermaid.initialize({ startOnLoad: true });
                    mermaid.init(undefined, mermaidContainer);
                };
                document.head.appendChild(script);
            } else {
                mermaid.init(undefined, mermaidContainer);
            }
        }
    });

    // 将组件插入到代码元素的位置
    const parent = codeElement.parentNode.parentNode;
    parent.style.display = "none";
    parent.insertAdjacentElement("beforebegin", componentContainer);
    componentContainer.insertAdjacentElement("afterend", mermaidPreviewContainer);
    mermaidPreviewContainer.insertAdjacentElement("afterend", codeContainer);

    return componentContainer;
}

// 新增 PPTX 渲染函数
function renderPPTX(codeElement) {
    // 创建组件容器
    const componentContainer = document.createElement("div");
    componentContainer.style.display = "flex";
    componentContainer.style.alignItems = "center";
    componentContainer.style.border = "1px solid #e5e7eb";
    componentContainer.style.borderRadius = "8px";
    componentContainer.style.padding = "10px";
    componentContainer.style.backgroundColor = "#f9fafb";
    componentContainer.style.marginBottom = "20px";
    componentContainer.style.cursor = "pointer";

    // 创建图标容器
    const iconContainer = document.createElement("div");
    iconContainer.style.borderRight = "1px solid #e5e7eb";
    iconContainer.style.paddingRight = "10px";

    // 创建 PPT 图标
    const svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svgIcon.setAttribute("style", "height: 24px; width: 24px; color: #6b7280;");
    svgIcon.setAttribute("fill", "none");
    svgIcon.setAttribute("viewBox", "0 0 24 24");
    svgIcon.setAttribute("stroke", "currentColor");
    svgIcon.setAttribute("stroke-width", "2");

    const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
    pathElement.setAttribute("stroke-linecap", "round");
    pathElement.setAttribute("stroke-linejoin", "round");
    pathElement.setAttribute("d", "M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm8 3v8m-4-4h8");
    svgIcon.appendChild(pathElement);
    iconContainer.appendChild(svgIcon);

    // 创建文本容器
    const textContainer = document.createElement("div");
    textContainer.style.marginLeft = "10px";

    // 创建标题
    const title = document.createElement("h3");
    title.textContent = "PowerPoint Presentation";
    title.style.margin = "0";
    title.style.fontSize = "16px";
    title.style.fontWeight = "600";
    title.style.color = "#374151";

    // 创建描述
    const description = document.createElement("p");
    description.textContent = "点击切换显示/隐藏";
    description.style.margin = "0";
    description.style.fontSize = "14px";
    description.style.color = "#6b7280";

    textContainer.appendChild(title);
    textContainer.appendChild(description);

    // 组装组件
    componentContainer.appendChild(iconContainer);
    componentContainer.appendChild(textContainer);

    // 创建 PPTX 预览容器
    const pptxPreviewContainer = document.createElement("div");
    pptxPreviewContainer.style.margin = "10px 0";
    pptxPreviewContainer.style.padding = "20px";
    pptxPreviewContainer.style.backgroundColor = "#fff";
    pptxPreviewContainer.style.border = "1px solid #e5e7eb";
    pptxPreviewContainer.style.borderRadius = "8px";
    pptxPreviewContainer.style.display = "none";

    // 创��� PPTX 容器
    const pptxContainer = document.createElement("div");
    pptxContainer.id = "pptx-container-" + Math.random().toString(36).substr(2, 9);
    pptxPreviewContainer.appendChild(pptxContainer);

    // 创建控制按钮
    const controlsContainer = document.createElement("div");
    controlsContainer.style.marginTop = "10px";
    controlsContainer.style.textAlign = "center";

    const prevButton = document.createElement("button");
    prevButton.textContent = "上一页";
    prevButton.style.marginRight = "10px";
    prevButton.style.padding = "5px 10px";

    const nextButton = document.createElement("button");
    nextButton.textContent = "下一页";
    nextButton.style.padding = "5px 10px";

    controlsContainer.appendChild(prevButton);
    controlsContainer.appendChild(nextButton);
    pptxPreviewContainer.appendChild(controlsContainer);

    // 创建代码容器
    const codeContainer = document.createElement("div");
    codeContainer.style.margin = "10px 0";
    codeContainer.style.display = "none";
    const clonedContent = codeElement.parentNode.parentNode.cloneNode(true);
    codeContainer.appendChild(clonedContent);
    codeContainer.childNodes[0].style.display = "block";

    // 添加点击事件
    componentContainer.addEventListener("click", function() {
        const newDisplayState = pptxPreviewContainer.style.display === "none" ? "block" : "none";
        pptxPreviewContainer.style.display = newDisplayState;
        codeContainer.style.display = newDisplayState;

        if (newDisplayState === "block") {
            // 检查是否已加载 pptxjs
            if (typeof jQuery === 'undefined') {
                // 加载 jQuery
                const jqueryScript = document.createElement('script');
                jqueryScript.src = 'https://code.jquery.com/jquery-3.6.0.min.js';
                jqueryScript.onload = () => {
                    // 加载 pptxjs
                    const pptxjsScript = document.createElement('script');
                    pptxjsScript.src = 'https://cdn.jsdelivr.net/gh/meshesha/pptxjs@latest/dist/pptxjs.min.js';
                    pptxjsScript.onload = () => {
                        renderPPTXContent(pptxContainer.id, codeElement.textContent);
                    };
                    document.head.appendChild(pptxjsScript);

                    // 加载 pptxjs CSS
                    const pptxjsCSS = document.createElement('link');
                    pptxjsCSS.rel = 'stylesheet';
                    pptxjsCSS.href = 'https://cdn.jsdelivr.net/gh/meshesha/pptxjs@latest/dist/pptxjs.min.css';
                    document.head.appendChild(pptxjsCSS);
                };
                document.head.appendChild(jqueryScript);
            } else {
                renderPPTXContent(pptxContainer.id, codeElement.textContent);
            }
        }
    });

    // 将组件插入到代码元素的位置
    const parent = codeElement.parentNode.parentNode;
    parent.style.display = "none";
    parent.insertAdjacentElement("beforebegin", componentContainer);
    componentContainer.insertAdjacentElement("afterend", pptxPreviewContainer);
    pptxPreviewContainer.insertAdjacentElement("afterend", codeContainer);

    return componentContainer;
}

// 渲染 PPTX 内容的辅助函数
function renderPPTXContent(containerId, base64Content) {
    try {
        // 将 base64 转换为 Blob
        const byteCharacters = atob(base64Content);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], { type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' });

        // 使用 pptxjs 渲染
        $("#" + containerId).pptxToHtml({
            pptxFileUrl: URL.createObjectURL(blob),
            slidesScale: "50%",
            slideMode: true,
            keyBoardShortCut: true
        });
    } catch (error) {
        console.error("PPTX rendering failed:", error);
        document.getElementById(containerId).innerHTML = "无法渲染 PPTX 内容,请检查格式是否正确。";
    }
}

function savaAiRecording(askFileName = false, format = 'html') {
    // askFileName为true时弹窗询问文件名
    var fileName = document.title;
    var today = new Date();
    var month = (today.getMonth() + 1).toString().padStart(2, '0');
    var day = today.getDate().toString().padStart(2, '0');
    fileName = `${fileName}-${month}${day}.${format}`;
    fileName = askFileName ? prompt('输入要保存的文件名:', fileName) : fileName;
    var body = document.createElement('body');
    body.innerHTML = document.body.innerHTML;
    // 删除所有script标签
    var ps = body.querySelectorAll('script');
    for (var i = 0; i < ps.length; i++) {
        ps[i].parentNode.removeChild(ps[i]);
    }
    // 删除所有style标签,因为downloadHtml会自动再获取一次
    var ps = body.querySelectorAll('style');
    for (var i = 0; i < ps.length; i++) {
        ps[i].parentNode.removeChild(ps[i]);
    }
    // 删除下边框
    var element = body.querySelector('#__next > div > div > main > div.absolute');
    element && element.remove();
    // 删除侧边框
    var element = body.querySelector('#__next > div > div.hidden');
    element && element.remove();
    // 删除侧边框间隔
    var element = body.querySelector('#__next > div > div');
    if (element) { element.className = ''; }
    // 添加script标签,用于修复一键复制
    var script = document.createElement('script');
    script.innerHTML = copyScript;
    body.appendChild(script);

    if (format === 'html') {
        downloadHtml(body.innerHTML, fileName);
    } else if (format === 'docx') {
        // 使用 docx 库生成 docx 文件
        var doc = new docx.Document();
        doc.addSection({
            children: [
                new docx.Paragraph(body.innerText)
            ]
        });
        docx.Packer.toBlob(doc).then(blob => {
            saveAs(blob, fileName);
        });
    } else if (format === 'md') {
        // 使用 showdown 库将 HTML 转换为 Markdown
        var converter = new showdown.Converter();
        var markdown = converter.makeMarkdown(body.innerHTML);
        var blob = new Blob([markdown], { type: 'text/markdown' });
        saveAs(blob, fileName);
    }
}