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