|
|
from markdown_it import MarkdownIt |
|
|
import html |
|
|
import re |
|
|
|
|
|
def get_style_css(style_name): |
|
|
""" |
|
|
根据选择的样式名称获取对应的CSS样式文件 |
|
|
|
|
|
Args: |
|
|
style_name (str): 样式名称,可选值为"Default"、"MBE"、"Glassmorphism"、"Apple" |
|
|
|
|
|
Returns: |
|
|
str: CSS样式内容 |
|
|
""" |
|
|
|
|
|
if style_name == "Default": |
|
|
return open("assets/demo.css", "r").read() |
|
|
elif style_name == "1": |
|
|
return open("assets/demo.1.css", "r").read() |
|
|
elif style_name == "MBE": |
|
|
return open("assets/demo_mbe.css", "r").read() |
|
|
elif style_name == "Glassmorphism": |
|
|
return open("assets/demo_glassmorphism.css", "r").read() |
|
|
elif style_name == "Apple": |
|
|
return open("assets/demo_apple.css", "r").read() |
|
|
elif style_name == "Paper": |
|
|
return open("assets/demo_paper.css", "r").read() |
|
|
else: |
|
|
return open("assets/demo.css", "r").read() |
|
|
|
|
|
def decorate_writing(writing_result, style="Default"): |
|
|
if not writing_result: |
|
|
return writing_result |
|
|
|
|
|
cite_pattern = r'<qwen:cite\s+url=["\']([^"\']+)["\'](?:\s+[^>]*)?>(.*?)</qwen:cite>' |
|
|
takeaway_pattern = r'<qwen:takeaway(?:\s+class=["\'](?P<class>[^"\']+)["\'])?>(?P<content>[^<]*)</qwen:takeaway>' |
|
|
citation_map = {} |
|
|
|
|
|
def replace_cite(match): |
|
|
nonlocal citation_map |
|
|
urls = match.group(1).split(',') |
|
|
content = match.group(2) |
|
|
citation_html = [] |
|
|
|
|
|
for url in urls: |
|
|
if url not in citation_map: |
|
|
citation_map[url] = len(citation_map) + 1 |
|
|
current_index = citation_map[url] |
|
|
citation_html.append((f'<a href="{url}" title="点击查看引用来源: {url}">{current_index}</a>', current_index)) |
|
|
|
|
|
citation_html = sorted(citation_html, key=lambda x: x[1]) |
|
|
citation_html = ', '.join([x[0] for x in citation_html]) |
|
|
|
|
|
cite_html = f'{content}<sup class="citation">[{citation_html}]</sup>' |
|
|
return cite_html |
|
|
|
|
|
decorated_result = re.sub(cite_pattern, replace_cite, writing_result, flags=re.S) |
|
|
|
|
|
def replace_takeaway(match): |
|
|
class_attr = match.group('class') |
|
|
content = match.group('content') |
|
|
|
|
|
if class_attr: |
|
|
return f'<div class="takeaway {class_attr}">{content}</div>' |
|
|
else: |
|
|
return f'<div class="takeaway">{content}</div>' |
|
|
|
|
|
decorated_result = re.sub(takeaway_pattern, replace_takeaway, decorated_result, flags=re.S) |
|
|
|
|
|
mermaid_pattern = r'```mermaid\n(.*?)\n```' |
|
|
def decorate_mermaid(match): |
|
|
return f""" |
|
|
<pre class="mermaid"> |
|
|
{match.group(1)} |
|
|
</pre> |
|
|
""" |
|
|
decorated_result = re.sub(mermaid_pattern, decorate_mermaid, decorated_result, flags=re.S) |
|
|
|
|
|
echarts_pattern = r'```echarts\n(.*?)\n```' |
|
|
echarts_index = 0 |
|
|
|
|
|
def replace_echarts(match): |
|
|
""" |
|
|
将echarts代码块转换为HTML和JavaScript |
|
|
|
|
|
Args: |
|
|
match: 正则表达式匹配对象 |
|
|
|
|
|
Returns: |
|
|
str: 包含HTML和JavaScript的echarts图表代码 |
|
|
""" |
|
|
nonlocal echarts_index |
|
|
echarts_code = match.group(1) |
|
|
echarts_id = f'echarts-container-{echarts_index}' |
|
|
echarts_index += 1 |
|
|
|
|
|
replace_code = f""" |
|
|
<div class="echarts-container loading" id="{echarts_id}">Echarts Rendering...</div> |
|
|
<script> |
|
|
var chartDom = document.getElementById('{echarts_id}'); |
|
|
var myChart = echarts.init(chartDom); |
|
|
var option; |
|
|
option = {echarts_code}; |
|
|
myChart.setOption(option); |
|
|
chartDom.classList.remove('loading'); |
|
|
</script> |
|
|
""" |
|
|
return replace_code |
|
|
|
|
|
decorated_result = re.sub(echarts_pattern, replace_echarts, decorated_result, flags=re.S) |
|
|
|
|
|
md = MarkdownIt() |
|
|
body = md.render(decorated_result) |
|
|
|
|
|
selected_css = get_style_css(style) |
|
|
|
|
|
html_content = """ |
|
|
<html> |
|
|
<head> |
|
|
<!-- KaTeX for mathematical formulas --> |
|
|
<link rel="stylesheet" href="https://s4.zstatic.net/npm/katex@0.16.0/dist/katex.min.css"> |
|
|
<script src="https://s4.zstatic.net/npm/katex@0.16.0/dist/katex.min.js"></script> |
|
|
<script src="https://s4.zstatic.net/npm/katex@0.16.0/dist/contrib/auto-render.min.js"></script> |
|
|
<script src="https://s4.zstatic.net/npm/echarts@5.6.0/dist/echarts.min.js"></script> |
|
|
<style> |
|
|
""" + selected_css + """ |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="generated-content"> |
|
|
""" + body + """</div> |
|
|
<script type="module"> |
|
|
import mermaid from 'https://unpkg.com/mermaid@11.6.0/dist/mermaid.esm.min.mjs'; |
|
|
</script> |
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
renderMathInElement(document.body); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
|
|
|
escaped_html_content = html.escape(html_content) |
|
|
|
|
|
|
|
|
iframe_style = "width: 100%; height: 1024px; transform-origin: top left; border-color: lightgrey; border-width: 1px; border-radius: 10px;" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iframe_content = f'<iframe id="ai-ui-iframe" loading="eager" importance="high" pointer-events="none" style="{iframe_style}" srcdoc="{escaped_html_content}"></iframe>' |
|
|
|
|
|
|
|
|
iframe_content = re.sub(r'\n\s*\n', '\n', iframe_content) |
|
|
return iframe_content |
|
|
|