| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
|
|
| |
| |
| |
| |
| |
| function initMermaid() { |
| if (typeof mermaid !== 'undefined') { |
| mermaid.initialize({ |
| startOnLoad: false, |
| theme: 'default', |
| securityLevel: 'loose', |
| fontFamily: 'Inter, sans-serif' |
| }); |
| } |
| } |
|
|
| |
| function initMarked() { |
| if (typeof marked === 'undefined') return; |
|
|
| const renderer = new marked.Renderer(); |
|
|
| |
| renderer.code = function(code, language, isEscaped) { |
| |
| if (language === 'mermaid') { |
| return `<pre class="mermaid-diagram"><code class="mermaid">${code}</code></pre>`; |
| } |
|
|
| |
| let highlightedCode = code; |
| const langClass = language ? `language-${language}` : ''; |
| |
| if (language && typeof hljs !== 'undefined' && hljs.getLanguage(language)) { |
| try { |
| highlightedCode = hljs.highlight(code, { language: language, ignoreIllegals: true }).value; |
| } catch (e) { |
| highlightedCode = hljs.highlightAuto(code).value; |
| } |
| } else if (typeof hljs !== 'undefined') { |
| highlightedCode = hljs.highlightAuto(code).value; |
| } |
|
|
| |
| const copyIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`; |
| |
| |
| return `<pre class="code-block-wrapper"><button class="code-copy-btn" title="Copy code">${copyIcon}<span>Copy</span></button><code class="hljs ${langClass}">${highlightedCode}</code></pre>`; |
| }; |
|
|
| |
| marked.use({ |
| renderer: renderer, |
| gfm: true, |
| breaks: true, |
| pedantic: false, |
| mangle: false, |
| headerIds: false |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function preprocessMath(text) { |
| const mathBlocks = {}; |
| let counter = 0; |
|
|
| function createPlaceholder(match) { |
| const id = `@@MATHBLOCK_${counter++}@@`; |
| mathBlocks[id] = match; |
| return id; |
| } |
|
|
| |
| text = text.replace(/\$\$([\s\S]+?)\$\$/g, createPlaceholder); |
| |
| |
| text = text.replace(/\\\[([\s\S]+?)\\\]/g, createPlaceholder); |
| |
| |
| text = text.replace(/(?<!\$)\$(?!\$)((?:[^$\\]|\\.)+?)\$(?!\$)/g, createPlaceholder); |
| |
| |
| text = text.replace(/\\\(([\s\S]+?)\\\)/g, createPlaceholder); |
|
|
| return { text, mathBlocks }; |
| } |
|
|
| function postprocessMath(html, mathBlocks) { |
| |
| for (const [id, mathStr] of Object.entries(mathBlocks)) { |
| html = html.split(id).join(mathStr); |
| } |
| return html; |
| } |
|
|
| |
| |
| |
| |
| function processThinkingBlocks(text) { |
| |
| let normalizedText = text |
| .replace(/<\|channel\|>thought\s*<\|channel\|>/gi, "<think>\n") |
| .replace(/<\|channel\|>answer\s*<\|channel\|>/gi, "\n</think>\n") |
| .replace(/<\|im_start\|>thought/gi, "<think>\n") |
| .replace(/<\|im_end\|>/gi, "\n</think>\n"); |
|
|
| const thinkRegex = /<think>([\s\S]*?)(?:<\/think>|$)/i; |
| const thinkMatch = normalizedText.match(thinkRegex); |
| |
| let thinkHtml = ''; |
| let mainText = normalizedText; |
|
|
| if (thinkMatch) { |
| const thinkContent = thinkMatch[1].trim(); |
| |
| const parsedThinkContent = typeof marked !== 'undefined' ? marked.parse(thinkContent) : thinkContent; |
| |
| thinkHtml = `\n <details class="qwen-think-box" open>\n <summary>\n <svg class="arrow" style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <polyline points="9 18 15 12 9 6"></polyline>\n </svg> \n Thinking Process\n </summary>\n <div class="qwen-think-content">${parsedThinkContent}</div>\n </details>`; |
| |
| |
| mainText = normalizedText.replace(thinkRegex, '').trim(); |
| } |
|
|
| return { thinkHtml, mainText }; |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| async function triggerMathRendering(container) { |
| const texElements = container.querySelectorAll('.tex2jax_process'); |
| if (texElements.length === 0) return; |
| |
| |
| if (typeof katex !== 'undefined') { |
| texElements.forEach(el => { |
| try { |
| |
| renderMathInElement(el, { |
| delimiters: [ |
| {left: '$$', right: '$$', display: true}, |
| {left: '\\[', right: '\\]', display: true}, |
| {left: '$', right: '$', display: false}, |
| {left: '\\(', right: '\\)', display: false} |
| ], |
| throwOnError: false, |
| output: 'html', |
| trust: true |
| }); |
| } catch (err) { |
| console.warn('KaTeX rendering warning:', err); |
| } |
| }); |
| } |
| |
| |
| if (window.MathJax && MathJax.typesetPromise) { |
| try { |
| await MathJax.typesetPromise([container]); |
| } catch (err) { |
| console.warn('MathJax Rendering Warning:', err); |
| } |
| } |
| } |
|
|
| async function triggerMathJax(container) { |
| |
| await triggerMathRendering(container); |
| } |
|
|
| async function triggerMermaid(container) { |
| if (typeof mermaid === 'undefined') return; |
| |
| const diagrams = container.querySelectorAll('.mermaid'); |
| if (diagrams.length === 0) return; |
|
|
| try { |
| |
| await mermaid.run({ nodes: diagrams }); |
| } catch (err) { |
| console.warn('Mermaid Rendering Warning:', err); |
| |
| diagrams.forEach(d => { |
| if (!d.querySelector('svg')) { |
| d.innerHTML = `<span style="color:var(--brand-danger);font-size:12px;">⚠️ Invalid Mermaid Syntax</span>`; |
| } |
| }); |
| } |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| async function parseAndRender(fullText, isProcessing, container) { |
| if (!container) return; |
|
|
| |
| const { thinkHtml, mainText } = processThinkingBlocks(fullText); |
|
|
| let finalHtml = thinkHtml; |
|
|
| |
| if (mainText) { |
| |
| const { text: safeText, mathBlocks } = preprocessMath(mainText); |
| |
| |
| let parsedHtml = ''; |
| if (typeof marked !== 'undefined') { |
| parsedHtml = marked.parse(safeText); |
| } else { |
| parsedHtml = safeText.replace(/\n/g, '<br>'); |
| } |
| |
| |
| parsedHtml = postprocessMath(parsedHtml, mathBlocks); |
| |
| |
| finalHtml += `<div class="tex2jax_process">${parsedHtml}</div>`; |
| } |
|
|
| |
| if (isProcessing) { |
| finalHtml += `<span class="blinking-cursor"></span>`; |
| } |
| |
| |
| container.innerHTML = finalHtml; |
|
|
| |
| |
| |
| if (!isProcessing) { |
| |
| setTimeout(async () => { |
| await triggerMathJax(container); |
| await triggerMermaid(container); |
| }, 20); |
| } else { |
| |
| |
| |
| setTimeout(() => { |
| triggerMathJax(container); |
| }, 30); |
| } |
| } |
|
|
| |
| |
| |
|
|
| function init() { |
| initMermaid(); |
| initMarked(); |
| console.log('[MarkdownRenderer] Engine initialized successfully.'); |
| } |
|
|
| |
| window.MarkdownRenderer = { |
| parseAndRender: parseAndRender, |
| init: init |
| }; |
|
|
| |
| init(); |
|
|
| })(); |
|
|