Files
window-axis-innovators-box/javascript/AIaToolbox_dark.html
tzdwindows 7 adf659853d feat(browser): 实现Java字体和主题动态同步到HTML界面
- 添加javaFontsLoaded和javaThemeChanged事件监听机制
- 在BrowserWindow和BrowserWindowJDialog中实现字体信息获取和注入
- 前端HTML文件增加对应的字体应用逻辑和样式更新
- 创建WindowRegistry统一管理窗口主题更新
- 更新README文档说明HTML事件使用方法- 支持Monaco和CodeMirror编辑器的字体动态调整
-优化CEF浏览器与Java UI的字体和主题同步流程
2025-10-05 19:49:53 +08:00

920 lines
29 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>DeepSeek - 智能助手</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 动态加载主题样式 -->
<link id="hljs-light" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/intellij-light.min.css" media="(prefers-color-scheme: light)">
<link id="hljs-dark" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
<!-- KaTeX 核心样式 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<!-- KaTeX 核心库 -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<!-- 自动渲染扩展 -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<style>
:root {
/* 色彩体系 */
--primary-color: #58a6ff;
--primary-hover: #79b8ff;
--bg-gradient: linear-gradient(158deg, #0a0d12 0%, #141920 100%);
--card-bg: rgba(22, 26, 34, 0.98);
--text-primary: #e3e6eb;
--text-secondary: #8b949e;
--border-color: rgba(255, 255, 255, 0.12);
--shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.4);
--code-bg: rgba(255, 255, 255, 0.08);
/* 状态色 */
--success: #3ba776;
--warning: #d29922;
--error: #da3633;
/* 特殊效果 */
--glow-effect: radial-gradient(circle at 50% 0%, rgba(88,166,255,0.15) 0%, transparent 70%);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: var(--bg-gradient);
color: var(--text-primary);
font-family: 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
height: 100vh;
display: flex;
flex-direction: column;
line-height: 1.6;
}
/* 容器样式 */
.container {
max-width: 1200px;
margin: 20px auto;
flex: 1;
width: 95%;
display: flex;
flex-direction: column;
position: relative;
}
.chat-container {
background: transparent !important;
backdrop-filter: none;
border: none;
box-shadow: none;
}
/* 消息区域 */
.messages {
flex: 1;
padding: 24px;
overflow-y: auto;
scroll-behavior: smooth;
display: flex;
flex-direction: column;
gap: 20px;
}
/* 消息气泡 */
.message {
max-width: 85%;
opacity: 0;
animation: messageIn 0.3s ease-out forwards;
position: relative;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.message:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px -6px rgba(0, 0, 0, 0.3);
}
@keyframes messageIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* 用户消息 */
.message.user {
align-self: flex-end;
background: var(--primary-color);
color: #ffffff;
border-radius: 20px 20px 4px 20px;
box-shadow: 0 4px 12px -2px rgba(88, 166, 255, 0.3);
}
/* AI消息 */
.message.ai {
align-self: flex-start;
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 4px 20px 20px 20px;
box-shadow: var(--shadow);
}
.bubble {
padding: 18px 24px;
line-height: 1.8;
position: relative;
transition: all 0.3s ease;
}
/* 流式光标 */
.streaming-cursor {
display: inline-block;
width: 8px;
height: 1em;
background: var(--text-primary);
margin-left: 4px;
vertical-align: middle;
animation: cursorPulse 1.2s ease-in-out infinite;
}
@keyframes cursorPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
/* 输入区域 */
.input-area {
padding: 24px;
background: rgba(22, 26, 34, 0.95);
border-top: 1px solid var(--border-color);
backdrop-filter: blur(8px);
}
.input-wrapper {
display: flex;
gap: 12px;
max-width: 800px;
margin: 0 auto;
position: relative;
}
input {
flex: 1;
padding: 16px 24px;
border: 1px solid var(--border-color);
border-radius: 30px;
background: rgba(255, 255, 255, 0.05);
color: var(--text-primary);
font-size: 16px;
transition: all 0.3s ease;
}
input:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);
border-color: var(--primary-color);
}
/* 按钮样式 */
button {
padding: 16px 32px;
border: none;
border-radius: 30px;
background: var(--primary-color);
color: white;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
position: relative;
overflow: hidden;
}
button:hover {
background: var(--primary-hover);
transform: translateY(-1px);
}
button::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(120deg,
rgba(255,255,255,0.1) 0%,
rgba(255,255,255,0.05) 50%,
rgba(255,255,255,0.1) 100%
);
opacity: 0;
transition: opacity 0.3s;
}
button:hover::after {
opacity: 1;
}
/* 代码块样式 */
pre {
background: var(--code-bg);
border: 1px solid var(--border-color);
padding: 16px;
border-radius: 8px;
overflow-x: auto;
position: relative;
margin: 12px 0;
}
code {
font-family: 'JetBrains Mono', monospace;
font-size: 0.9em;
color: var(--text-primary);
}
/* 复制按钮 */
.copy-btn {
position: absolute;
right: 12px;
top: 12px;
padding: 6px 12px;
border: none;
border-radius: 4px;
background: rgba(255,255,255,0.1);
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s;
font-size: 0.85em;
}
.copy-btn:hover {
background: rgba(255,255,255,0.15);
}
/* 折叠按钮 */
.fold-btn {
position: absolute;
right: 15px;
bottom: 10px;
background: linear-gradient(180deg,
rgba(22,26,34,0) 0%,
rgba(22,26,34,0.95) 60%
);
padding: 6px 12px;
border: none;
color: var(--primary-color);
font-size: 0.85em;
cursor: pointer;
display: none;
z-index: 2;
border-radius: 4px;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
background: var(--card-bg);
}
::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.2);
border-radius: 4px;
}
/* 其他组件 */
.thinking-content {
color: var(--text-secondary);
font-style: italic;
margin-bottom: 12px;
padding: 8px 12px;
background: rgba(255,255,255,0.05);
border-radius: 6px;
}
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.85);
color: white;
padding: 12px 24px;
border-radius: 30px;
font-size: 0.9em;
animation: toastIn 0.3s ease-out;
backdrop-filter: blur(4px);
}
/* 数学公式样式 */
.katex {
font-size: 1.1em;
padding: 0 0.2em;
color: var(--text-primary) !important;
}
.math-block {
margin: 1em 0;
padding: 1em;
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/java.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
<div class="container">
<div class="chat-container">
<div class="messages" id="messages">
<div class="typing-indicator" id="typing">
<div class="dot-flashing"></div>
</div>
</div>
<div class="input-area">
<div class="input-wrapper">
<input type="text"
id="input"
placeholder="输入您的问题..."
autocomplete="off">
<button onclick="sendMessage()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>
</svg>
发送
</button>
</div>
</div>
</div>
</div>
<script>
class EffectSystem {
constructor() {
this.initCanvas();
this.initParticleSystem();
this.initMouseEffects();
this.createStarryBackground();
this.lastMeteorTime = 0;
}
initCanvas() {
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
this.canvas.style.cssText = `
position: fixed;
top: 0;
left: 0;
pointer-events: none;
z-index: 0;
`;
document.body.prepend(this.canvas);
this.resize();
window.addEventListener('resize', () => this.resize());
}
resize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
initParticleSystem() {
this.particles = [];
this.animate();
}
animate() {
this.ctx.fillStyle = 'rgba(0,0,0,0.05)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.particles.forEach((particle, index) => {
particle.update();
particle.draw();
if (particle.alpha <= 0) this.particles.splice(index, 1);
});
requestAnimationFrame(() => this.animate());
}
initMouseEffects() {
// 点击特效
document.addEventListener('click', (e) => {
for(let i = 0; i < 15; i++) {
this.particles.push(new SparkParticle(e.clientX, e.clientY, this.ctx));
}
});
// 流星特效每3秒随机生成
setInterval(() => {
if (Math.random() > 0.7) { // 30%概率生成
this.createMeteor();
}
}, 3000);
}
createMeteor() {
const startX = Math.random() * this.canvas.width;
const meteor = new MeteorParticle(
startX,
-50,
this.ctx,
this.canvas.width,
this.canvas.height
);
this.particles.push(meteor);
}
createStarryBackground() {
// 绘制静态星空
this.ctx.fillStyle = 'rgba(255,255,255,0.8)';
for(let i = 0; i < 150; i++) {
const x = Math.random() * this.canvas.width;
const y = Math.random() * this.canvas.height;
const r = Math.random() * 1.2;
this.ctx.beginPath();
this.ctx.arc(x, y, r, 0, Math.PI * 2);
this.ctx.fill();
}
}
}
class SparkParticle {
constructor(x, y, ctx) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.alpha = 1;
this.velocity = {
x: (Math.random() - 0.5) * 4,
y: (Math.random() - 0.5) * 4
};
this.size = Math.random() * 2 + 1;
this.color = `hsla(${190 + Math.random() * 40}, 70%, 60%, ${this.alpha})`;
}
update() {
this.x += this.velocity.x;
this.y += this.velocity.y;
this.alpha -= 0.02;
this.velocity.x *= 0.95;
this.velocity.y *= 0.95;
}
draw() {
this.ctx.fillStyle = this.color;
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
this.ctx.fill();
}
}
class MeteorParticle {
constructor(x, y, ctx, maxWidth, maxHeight) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.alpha = 1;
this.size = 2;
this.speed = 15;
this.angle = Math.PI / 4 + Math.random() * Math.PI / 2;
this.color = `hsla(180, 100%, 70%, ${this.alpha})`;
this.trail = [];
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
}
update() {
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
// 添加拖尾
this.trail.push({x: this.x, y: this.y});
if(this.trail.length > 15) this.trail.shift();
// 渐隐
this.alpha -= 0.008;
this.color = `hsla(180, 100%, 70%, ${this.alpha})`;
}
draw() {
// 绘制拖尾
this.trail.forEach((pos, i) => {
const alpha = this.alpha * (i / this.trail.length);
const gradient = this.ctx.createLinearGradient(
pos.x, pos.y,
pos.x + Math.cos(this.angle)*20,
pos.y + Math.sin(this.angle)*20
);
gradient.addColorStop(0, `hsla(180, 100%, 70%, ${alpha})`);
gradient.addColorStop(1, `hsla(180, 100%, 70%, 0)`);
this.ctx.strokeStyle = gradient;
this.ctx.lineWidth = this.size;
this.ctx.beginPath();
this.ctx.moveTo(pos.x, pos.y);
this.ctx.lineTo(
pos.x + Math.cos(this.angle)*20,
pos.y + Math.sin(this.angle)*20
);
this.ctx.stroke();
});
}
}
// 初始化特效
//if(document.documentElement.getAttribute('data-theme') === 'dark') {
new EffectSystem();
//}
// 增强毛玻璃效果
document.querySelectorAll('.chat-container, .message.ai .bubble').forEach(el => {
el.style.cssText += `
background: rgba(30, 35, 45, 0.85) !important;
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
border: 1px solid rgba(255,255,255,0.1);
`;
});
// 层级调整
document.querySelector('.container').style.cssText += `
position: relative;
z-index: 1;
`;
// 修改原有createMessageElement函数
function createMessageElement(requestId) {
const element = document.createElement('div');
element.className = 'message ai response';
element.innerHTML = `
<div class="bubble" style="
background: rgba(255,255,255,0.05);
backdrop-filter: blur(8px);
border: 1px solid rgba(255,255,255,0.1);
">
<div class="content"></div>
<div class="streaming-cursor"></div>
<button class="fold-btn" onclick="toggleFold(event)">展开</button>
</div>
`;
messages.insertBefore(element, typing);
return element;
}
// 初始化配置
marked.setOptions({
breaks: true,
highlight: code => hljs.highlightAuto(code).value
});
marked.use({
extensions: []
});
hljs.configure({
languages: ['java', 'python', 'javascript', 'typescript'],
cssSelector: 'pre code',
ignoreUnescapedHTML: true
});
// 监听Java字体加载事件
document.addEventListener('javaFontsLoaded', function(event) {
const fontInfo = event.detail;
console.log('接收到Java字体信息:', fontInfo);
// 应用Java字体到界面
applyJavaFonts(fontInfo);
});
// 应用Java字体的函数
function applyJavaFonts(fontInfo) {
const uiFonts = fontInfo.uiFonts || {};
const defaultFont = fontInfo.defaultFont || uiFonts['Label.font'] || {};
if (defaultFont && defaultFont.family) {
const fontFamily = defaultFont.family;
const fontSize = defaultFont.size || 14;
const fontWeight = defaultFont.bold ? 'bold' : 'normal';
const fontStyle = defaultFont.italic ? 'italic' : 'normal';
// 创建字体样式
const style = document.createElement('style');
style.textContent = `
body, html {
font-family: '${fontFamily}', 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
}
.message.user .bubble {
font-family: '${fontFamily}', 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
}
.message.ai .bubble {
font-family: '${fontFamily}', 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
font-size: ${fontSize}px !important;
}
input, button {
font-family: '${fontFamily}', 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
}
code, pre {
font-family: 'JetBrains Mono', 'Consolas', 'Monaco', 'Courier New', monospace !important;
}
`;
// 添加到文档头
document.head.appendChild(style);
console.log('Java字体已应用到DeepSeek界面:', fontFamily, fontSize + 'px');
}
}
// 如果字体信息已经存在,立即应用
if (typeof window.javaFontInfo !== 'undefined') {
applyJavaFonts(window.javaFontInfo);
}
// CEF通信桥接
window.javaQuery = window.cefQuery ? (request, success, error) => {
window.cefQuery({
request,
onSuccess: success,
onFailure: (code, msg) => error?.(msg)
});
} : console.error;
// 流式响应处理器
const streams = new Map();
window.updateResponse = (requestId, content) => {
if (content === '[end]') {
finalizeStream(requestId);
return;
}
let stream = streams.get(requestId);
if (!stream) {
stream = {
buffer: "", // 主缓冲区
element: createMessageElement(requestId),
cursorTimer: null,
isCompleted: false
};
streams.set(requestId, stream);
startCursorAnimation(requestId);
hideTyping();
}
// 直接累积内容到主缓冲区
stream.buffer += content;
// 实时更新到 DOM无论公式是否闭合
renderContent(requestId);
maintainScroll();
};
function hideTyping() {
document.getElementById('typing').style.display = 'none';
}
function handleThinkingContent(stream, content) {
const parts = content.split('</think>');
if (parts[0]) {
stream.hasThinking = true;
appendThinkingContent(stream, parts[0]);
}
if (parts[1]) {
stream.buffer += parts[1];
}
}
function appendThinkingContent(stream, content) {
const thinkingDiv = document.createElement('div');
thinkingDiv.className = 'thinking-content';
thinkingDiv.textContent = content.replace('<think>', '').trim();
stream.element.querySelector('.content').appendChild(thinkingDiv);
}
function startCursorAnimation(requestId) {
const stream = streams.get(requestId);
if (!stream) return;
stream.cursorTimer = setInterval(() => {
if (stream.isCompleted) {
clearInterval(stream.cursorTimer);
return;
}
const cursor = stream.element.querySelector('.streaming-cursor');
if (cursor) {
cursor.style.opacity = cursor.style.opacity === '1' ? '0.3' : '1';
}
}, 600);
}
function renderContent(requestId) {
const stream = streams.get(requestId);
if (!stream) return;
const contentDiv = stream.element.querySelector('.content');
const rawContent = stream.buffer;
// 使用 Marked 解析内容
const tempDiv = document.createElement('div');
tempDiv.innerHTML = marked.parse(rawContent);
// 高亮代码块
tempDiv.querySelectorAll('pre code').forEach(block => {
hljs.highlightElement(block);
});
// 增量更新 DOM
contentDiv.innerHTML = tempDiv.innerHTML;
// 触发 KaTeX 渲染(兼容未闭合公式)
if (window.renderMathInElement) {
renderMathInElement(contentDiv, {
delimiters: [
{ left: '$$', right: '$$', display: true },
{ left: '$', right: '$', display: false }
],
throwOnError: false, // 忽略错误,允许未闭合公式临时显示
strict: false // 宽松模式,兼容不完整语法
});
}
// 显示流式光标
if (!stream.isCompleted && !contentDiv.querySelector('.streaming-cursor')) {
contentDiv.innerHTML += '<div class="streaming-cursor"></div>';
}
addCopyButtons(contentDiv);
}
///function createMessageElement(requestId) {
/// const element = document.createElement('div');
/// element.className = 'message ai response'; // 添加response类
/// element.innerHTML = `
/// <div class="bubble">
/// <div class="content"></div>
/// <div class="streaming-cursor"></div>
/// <button class="fold-btn" onclick="toggleFold(event)">展开</button>
/// </div>
/// `;
/// messages.insertBefore(element, typing);
/// return element;
///}
function finalizeStream(requestId) {
const stream = streams.get(requestId);
if (stream) {
clearInterval(stream.cursorTimer);
stream.isCompleted = true;
// 移除光标
const cursor = stream.element.querySelector('.streaming-cursor');
if (cursor) cursor.remove();
// 自动折叠逻辑
checkCollapsible(stream.element);
addFoldButton(stream.element);
streams.delete(requestId);
}
}
function checkCollapsible(element) {
const bubble = element.querySelector('.bubble');
const lineHeight = parseInt(getComputedStyle(bubble).lineHeight);
if (bubble.scrollHeight > lineHeight * 5) {
element.classList.add('collapsed');
}
}
function addFoldButton(element) {
const btn = element.querySelector('.fold-btn');
btn.style.display = 'block';
btn.textContent = element.classList.contains('collapsed') ? '展开' : '收起';
}
function toggleFold(event) {
const btn = event.target;
const message = btn.closest('.message');
const beforeHeight = messages.scrollHeight;
message.classList.toggle('collapsed');
btn.textContent = message.classList.contains('collapsed') ? '展开' : '收起';
const heightDiff = messages.scrollHeight - beforeHeight;
messages.scrollTop += heightDiff;
}
function maintainScroll() {
const threshold = 100;
const container = document.getElementById('messages');
const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight <= threshold;
if (isNearBottom) {
requestAnimationFrame(() => {
container.scrollTop = container.scrollHeight;
});
}
}
function addCopyButtons(container) {
container.querySelectorAll('pre').forEach(pre => {
if (!pre.querySelector('.copy-btn')) {
const btn = document.createElement('button');
btn.className = 'copy-btn';
btn.textContent = '复制';
btn.onclick = () => copyCode(pre);
pre.prepend(btn);
}
});
}
function copyCode(pre) {
const code = pre.querySelector('code')?.textContent || '';
navigator.clipboard.writeText(code).then(() => {
showToast('代码已复制');
});
}
function showToast(message) {
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
function sendMessage() {
const input = document.getElementById('input');
const prompt = input.value.trim();
if (!prompt) return;
const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 4)}`;
const userMsg = document.createElement('div');
userMsg.className = 'message user';
userMsg.innerHTML = `
<div class="bubble">${marked.parse(prompt)}</div>
`;
messages.insertBefore(userMsg, typing);
messages.scrollTop = messages.scrollHeight;
showTyping(true);
input.value = '';
window.javaQuery(
`ai-inference:${requestId}:${prompt}`,
response => {
if (response.startsWith("COMPLETED:")) {
finalizeStream(requestId);
}
},
error => showError(requestId, error)
);
}
function showError(requestId, message) {
const stream = streams.get(requestId);
if (stream) {
stream.element.innerHTML = `
<div class="bubble error">
<strong>⚠️ 请求失败:</strong> ${message}
</div>
`;
streams.delete(requestId);
}
showTyping(false);
}
function showTyping(show) {
typing.style.display = show ? 'block' : 'none';
if (show) messages.scrollTop = messages.scrollHeight;
}
document.getElementById('input').addEventListener('keypress', e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
document.addEventListener('DOMContentLoaded', () => {
renderMathInElement(document.body, {
delimiters: [
{ left: '$$', right: '$$', display: true }, // 块级公式
{ left: '$', right: '$', display: false }, // 行内公式
{ left: '\\[', right: '\\]', display: true }, // LaTeX 环境
{ left: '\\(', right: '\\)', display: false }
],
throwOnError: false // 忽略渲染错误
});
});
</script>
</body>
</html>