'commit'
This commit is contained in:
@@ -164,39 +164,53 @@
|
|||||||
<script src="js/element-plus.zh-cn.min.js"></script>
|
<script src="js/element-plus.zh-cn.min.js"></script>
|
||||||
<script src="js/axios.min.js"></script>
|
<script src="js/axios.min.js"></script>
|
||||||
<!-- Markdown & LaTeX Support -->
|
<!-- Markdown & LaTeX Support -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/github-markdown-css@5.5.1/github-markdown-dark.css">
|
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/github-markdown-css@5.5.1/github-markdown-dark.css">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/katex@0.16.9/dist/katex.min.css">
|
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||||||
<script src="https://unpkg.com/katex@0.16.9/dist/katex.min.js"></script>
|
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||||||
<script src="https://unpkg.com/marked@12.0.0/lib/marked.umd.js"></script>
|
<script src="https://gcore.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* 适配对话框中的 Markdown 样式 */
|
|
||||||
.markdown-body {
|
.markdown-body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: 980px;
|
max-width: 980px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 15px;
|
padding: 24px;
|
||||||
background-color: transparent !important;
|
background-color: #0f172a !important;
|
||||||
color: #cbd5e1 !important;
|
color: #cbd5e1 !important;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
/* 深度覆盖:确保列表和加粗在任何情况下都可见 */
|
||||||
|
.markdown-body ul {
|
||||||
|
list-style-type: disc !important;
|
||||||
|
padding-left: 2em !important;
|
||||||
|
margin-top: 0.5em !important;
|
||||||
|
margin-bottom: 0.5em !important;
|
||||||
|
}
|
||||||
|
.markdown-body li {
|
||||||
|
display: list-item !important;
|
||||||
|
margin-bottom: 0.25em !important;
|
||||||
|
}
|
||||||
|
.markdown-body strong {
|
||||||
|
font-weight: bold !important;
|
||||||
|
color: #f1f5f9 !important;
|
||||||
|
}
|
||||||
|
.markdown-body p {
|
||||||
|
margin-bottom: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
.el-dialog {
|
.el-dialog {
|
||||||
background-color: #1e293b !important;
|
background-color: #1e293b !important;
|
||||||
border: 1px solid #334155;
|
border: 1px solid #334155;
|
||||||
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
.el-dialog__title {
|
.el-dialog__title {
|
||||||
color: #f1f5f9 !important;
|
color: #f1f5f9 !important;
|
||||||
}
|
}
|
||||||
.el-dialog__body {
|
.el-dialog__body {
|
||||||
color: #cbd5e1 !important;
|
background-color: #0f172a !important;
|
||||||
}
|
padding: 0 !important;
|
||||||
.cursor-blink {
|
|
||||||
color: #3b82f6;
|
|
||||||
font-weight: bold;
|
|
||||||
animation: blink 1s step-end infinite;
|
|
||||||
}
|
|
||||||
@keyframes blink {
|
|
||||||
from, to { opacity: 1; }
|
|
||||||
50% { opacity: 0; }
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="js/douyin.js"></script>
|
<script src="js/douyin.js"></script>
|
||||||
|
|||||||
@@ -1,5 +1,75 @@
|
|||||||
const { createApp, ref, computed, onMounted } = Vue;
|
const { createApp, ref, computed, onMounted } = Vue;
|
||||||
|
|
||||||
|
// 1. 全局渲染器 - 纯手写的高鲁棒性解析器,不再依赖 marked
|
||||||
|
const globalRenderMarkdown = (text) => {
|
||||||
|
if (!text) return '';
|
||||||
|
|
||||||
|
// 0. 预处理:去除 AI 可能返回的代码块标记
|
||||||
|
let cleanText = text.trim();
|
||||||
|
// 去除开头的 ```markdown 或 ```
|
||||||
|
cleanText = cleanText.replace(/^```(markdown)?\s*/i, '');
|
||||||
|
// 去除结尾的 ```
|
||||||
|
cleanText = cleanText.replace(/```\s*$/, '');
|
||||||
|
|
||||||
|
// 1. 预处理:按行分割
|
||||||
|
const lines = cleanText.split('\n');
|
||||||
|
let html = '';
|
||||||
|
let inList = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
let line = lines[i];
|
||||||
|
// 关键:去除行首尾空白,解决缩进导致的解析失败
|
||||||
|
let trimmed = line.trim();
|
||||||
|
|
||||||
|
// 空行处理
|
||||||
|
if (!trimmed) {
|
||||||
|
if (inList) { html += '</ul>\n'; inList = false; }
|
||||||
|
continue; // 忽略空行,或者可以加 <br>
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 内联格式处理(加粗、代码、链接)
|
||||||
|
// 加粗 **text** -> <strong>text</strong>
|
||||||
|
trimmed = trimmed.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||||
|
// 代码 `text` -> <code>text</code>
|
||||||
|
trimmed = trimmed.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||||
|
|
||||||
|
// 3. 块级元素处理
|
||||||
|
// 标题 ### Title
|
||||||
|
if (trimmed.startsWith('###')) {
|
||||||
|
if (inList) { html += '</ul>\n'; inList = false; }
|
||||||
|
html += `<h3>${trimmed.replace(/^###\s*/, '')}</h3>`;
|
||||||
|
}
|
||||||
|
else if (trimmed.startsWith('##')) {
|
||||||
|
if (inList) { html += '</ul>\n'; inList = false; }
|
||||||
|
html += `<h2>${trimmed.replace(/^##\s*/, '')}</h2>`;
|
||||||
|
}
|
||||||
|
else if (trimmed.startsWith('#')) {
|
||||||
|
if (inList) { html += '</ul>\n'; inList = false; }
|
||||||
|
html += `<h1>${trimmed.replace(/^#\s*/, '')}</h1>`;
|
||||||
|
}
|
||||||
|
// 列表 - Item 或 * Item
|
||||||
|
else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
|
||||||
|
if (!inList) { html += '<ul>\n'; inList = true; }
|
||||||
|
html += `<li>${trimmed.substring(2)}</li>`;
|
||||||
|
}
|
||||||
|
// 数字列表 1. Item
|
||||||
|
else if (/^\d+\.\s/.test(trimmed)) {
|
||||||
|
// 简单起见,数字列表也用 ul,或者你可以维护一个 ordered list 状态
|
||||||
|
if (!inList) { html += '<ul>\n'; inList = true; }
|
||||||
|
html += `<li>${trimmed.replace(/^\d+\.\s/, '')}</li>`;
|
||||||
|
}
|
||||||
|
// 普通段落
|
||||||
|
else {
|
||||||
|
if (inList) { html += '</ul>\n'; inList = false; }
|
||||||
|
html += `<p>${trimmed}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inList) { html += '</ul>\n'; }
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
createApp({
|
createApp({
|
||||||
setup() {
|
setup() {
|
||||||
const apiBase = ref(window.location.origin || "http://localhost:8000");
|
const apiBase = ref(window.location.origin || "http://localhost:8000");
|
||||||
@@ -15,96 +85,26 @@ createApp({
|
|||||||
const summaryLoading = ref(false);
|
const summaryLoading = ref(false);
|
||||||
const summaryText = ref('');
|
const summaryText = ref('');
|
||||||
|
|
||||||
// 降级用的简易 Markdown 解析器
|
// 使用全局渲染器
|
||||||
const simpleMarkdown = (text) => {
|
|
||||||
if (!text) return '';
|
|
||||||
let lines = text.split('\n');
|
|
||||||
let html = '';
|
|
||||||
let inList = false;
|
|
||||||
const parseInline = (str) => {
|
|
||||||
return str
|
|
||||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
||||||
.replace(/`(.*?)`/g, '<code style="background:rgba(148, 163, 184, 0.1); padding:2px 4px; border-radius:4px;">$1</code>');
|
|
||||||
};
|
|
||||||
for (let line of lines) {
|
|
||||||
let trimmed = line.trim();
|
|
||||||
if (!trimmed) {
|
|
||||||
if (inList) { html += '</ul>'; inList = false; }
|
|
||||||
html += '<br>';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (trimmed.startsWith('### ')) {
|
|
||||||
if (inList) { html += '</ul>'; inList = false; }
|
|
||||||
html += `<h3>${parseInline(trimmed.substring(4))}</h3>`;
|
|
||||||
} else if (trimmed.startsWith('- ') || /^\d+\./.test(trimmed)) {
|
|
||||||
if (!inList) { html += '<ul>'; inList = true; }
|
|
||||||
let content = trimmed.replace(/^(- |\d+\. )/, '');
|
|
||||||
html += `<li>${parseInline(content)}</li>`;
|
|
||||||
} else {
|
|
||||||
if (inList) { html += '</ul>'; inList = false; }
|
|
||||||
html += `<p>${parseInline(trimmed)}</p>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inList) html += '</ul>';
|
|
||||||
return html;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure Marked
|
|
||||||
if (typeof marked !== 'undefined') {
|
|
||||||
marked.use({
|
|
||||||
gfm: true,
|
|
||||||
breaks: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 增强的 Markdown & LaTeX 解析器
|
|
||||||
const renderMarkdownAndLatex = (text) => {
|
|
||||||
if (!text) return '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
let processedText = text;
|
|
||||||
|
|
||||||
// 1. 处理 LaTeX
|
|
||||||
if (typeof katex !== 'undefined') {
|
|
||||||
// 处理块级 LaTeX: $$ ... $$
|
|
||||||
processedText = processedText.replace(/\$\$\s*([\s\S]*?)\s*\$\$/g, (match, formula) => {
|
|
||||||
try {
|
|
||||||
return '<div class="katex-block">' + katex.renderToString(formula, { displayMode: true, throwOnError: false }) + '</div>';
|
|
||||||
} catch (e) { return match; }
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理行内 LaTeX: $ ... $
|
|
||||||
processedText = processedText.replace(/\$([^\$\n]+?)\$/g, (match, formula) => {
|
|
||||||
try {
|
|
||||||
return katex.renderToString(formula, { displayMode: false, throwOnError: false });
|
|
||||||
} catch (e) { return match; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 使用 marked 解析 Markdown
|
|
||||||
if (typeof marked !== 'undefined') {
|
|
||||||
// marked v12+ 使用 marked.parse
|
|
||||||
return marked.parse(processedText);
|
|
||||||
} else {
|
|
||||||
// 降级使用 simpleMarkdown
|
|
||||||
return simpleMarkdown(processedText);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Markdown rendering error:', e);
|
|
||||||
// 最后的兜底:如果 marked 报错,尝试用 simpleMarkdown
|
|
||||||
try {
|
|
||||||
return simpleMarkdown(text);
|
|
||||||
} catch (e2) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderedSummary = computed(() => {
|
const renderedSummary = computed(() => {
|
||||||
if (!summaryText.value) return '';
|
if (!summaryText.value) return '';
|
||||||
return renderMarkdownAndLatex(summaryText.value);
|
const html = globalRenderMarkdown(summaryText.value);
|
||||||
|
return html;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 这里的配置仍然保留,作为双重保险
|
||||||
|
if (typeof marked !== 'undefined') {
|
||||||
|
const m = (typeof marked.marked === 'function') ? marked.marked : marked;
|
||||||
|
if (m.setOptions) {
|
||||||
|
m.setOptions({
|
||||||
|
gfm: true,
|
||||||
|
breaks: true,
|
||||||
|
mangle: false,
|
||||||
|
headerIds: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const startParsing = async () => {
|
const startParsing = async () => {
|
||||||
if (!shareText.value.trim()) return;
|
if (!shareText.value.trim()) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user