|
@@ -1,5 +1,6 @@
|
|
|
<script setup lang="jsx">
|
|
|
import { computed, ref, inject, watchEffect } from 'vue';
|
|
|
+import { NCollapse, NCollapseItem } from 'naive-ui';
|
|
|
import MarkdownIt from 'markdown-it';
|
|
|
import hljs from 'highlight.js';
|
|
|
import mila from 'markdown-it-link-attributes';
|
|
@@ -11,7 +12,8 @@ import markdownItSup from 'markdown-it-sup';
|
|
|
import anchor from 'markdown-it-anchor';
|
|
|
import toc from 'markdown-it-toc-done-right';
|
|
|
|
|
|
-const updateCatalog = inject('updateCatalog');
|
|
|
+const updateCatalog = inject('updateCatalog', null);
|
|
|
+const thinkStr = ref('');
|
|
|
|
|
|
const props = defineProps({
|
|
|
content: {
|
|
@@ -29,6 +31,32 @@ const highlightBlock = (str, lang) => {
|
|
|
`
|
|
|
}
|
|
|
|
|
|
+let buffer = '';
|
|
|
+
|
|
|
+const handleThinkContent = (content) => {
|
|
|
+ thinkStr.value = content
|
|
|
+ console.log('Extracted think content:', content);
|
|
|
+};
|
|
|
+
|
|
|
+const processStreamData = (chunk) => {
|
|
|
+ // 将新接收到的数据追加到缓冲区
|
|
|
+ buffer += chunk;
|
|
|
+
|
|
|
+ // 使用非贪婪匹配尽可能快地找到<think>...</think>的内容
|
|
|
+ const regex = /<think>(.*?)<\/think>/gs;
|
|
|
+
|
|
|
+ let match;
|
|
|
+ while ((match = regex.exec(buffer)) !== null) {
|
|
|
+ const thinkContent = match[1];
|
|
|
+ // 假设你想对<think>内的内容做特殊处理
|
|
|
+ handleThinkContent(thinkContent);
|
|
|
+
|
|
|
+ // 移除已处理的部分
|
|
|
+ buffer = buffer.slice(match.index + match[0].length);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
const mdi = new MarkdownIt({
|
|
|
html: true,
|
|
|
linkify: true,
|
|
@@ -72,9 +100,9 @@ mdi.use(toc, {
|
|
|
callback: (_, ast) => {
|
|
|
setTimeout(_ => {
|
|
|
const domExists = document.querySelector('.table-of-contents');
|
|
|
- if ( updateCatalog ) {
|
|
|
- if ( domExists ) {
|
|
|
- const { c:content } = ast;
|
|
|
+ if (updateCatalog) {
|
|
|
+ if (domExists) {
|
|
|
+ const { c: content } = ast;
|
|
|
updateCatalog(content);
|
|
|
} else {
|
|
|
updateCatalog([]);
|
|
@@ -90,24 +118,7 @@ const transformText = (text) => {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-mdi.renderer.rules.echart_block = (tokens, idx, options, env, self) => {
|
|
|
- const token = tokens[idx];
|
|
|
- const optionsString = token.content.trim();
|
|
|
- try {
|
|
|
- const optionsObj = JSON.parse(optionsString);
|
|
|
-
|
|
|
- echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
|
|
|
-
|
|
|
- return `<div id="chart-${idx}"></div>`;
|
|
|
- } catch (e) {
|
|
|
- console.error('Invalid ECharts options:', optionsString);
|
|
|
- return '';
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLine, silent) {
|
|
|
- const startStr = '@@@echarts_b';
|
|
|
- const endStr = '@@@echarts_e';
|
|
|
+const extractRuler = (startStr, endStr, state, startLine, endLine) => {
|
|
|
|
|
|
if (startLine + 1 < state.lineMax && state.src.slice(state.bMarks[startLine] + state.tShift[startLine], state.eMarks[startLine]) === startStr) {
|
|
|
|
|
@@ -135,8 +146,52 @@ mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLi
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
+}
|
|
|
+
|
|
|
+// echart
|
|
|
+mdi.renderer.rules.echart_block = (tokens, idx, options, env, self) => {
|
|
|
+ const token = tokens[idx];
|
|
|
+ const optionsString = token.content.trim();
|
|
|
+ try {
|
|
|
+ const optionsObj = JSON.parse(optionsString);
|
|
|
+
|
|
|
+ echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
|
|
|
+
|
|
|
+ return `<div id="chart-${idx}"></div>`;
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Invalid ECharts options:', optionsString);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLine) {
|
|
|
+ const startStr = '@@@echarts_b';
|
|
|
+ const endStr = '@@@echarts_e';
|
|
|
+ return extractRuler(startStr, endStr, state, startLine, endLine);
|
|
|
});
|
|
|
|
|
|
+// think tag
|
|
|
+// mdi.renderer.rules.think_tag = (tokens, idx, options, env, self) => {
|
|
|
+// const token = tokens[idx];
|
|
|
+// const optionsString = token.content.trim();
|
|
|
+// try {
|
|
|
+// // const optionsObj = JSON.parse(optionsString);
|
|
|
+// // echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
|
|
|
+// thinkStr.value = optionsString;
|
|
|
+
|
|
|
+// return `<div class="testAA">${optionsString}</div>`;
|
|
|
+// } catch (e) {
|
|
|
+// console.error('Invalid:', e);
|
|
|
+// return '';
|
|
|
+// }
|
|
|
+// };
|
|
|
+
|
|
|
+// mdi.block.ruler.after('fence', 'think_tag', function (state, startLine, endLine) {
|
|
|
+// const startStr = '<think>';
|
|
|
+// const endStr = '</think>';
|
|
|
+// return extractRuler(startStr, endStr, state, startLine, endLine);
|
|
|
+// });
|
|
|
+
|
|
|
const updateCharts = () => {
|
|
|
Object.keys(echartOptions).forEach((key) => {
|
|
|
if (echartInstance[key]) return;
|
|
@@ -171,8 +226,42 @@ function splitStringByChartDiv(str) {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+const processedValue = (value) => {
|
|
|
+ return value.replace(/<think>([\s\S]*?)<\/think>/g, (_, content) => {
|
|
|
+ const lines = content.trim().split('\n');
|
|
|
+ const quotedLines = lines.map((line) => `> ${line}`).join('\n');
|
|
|
+ return quotedLines;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const thinkParser = (content) => {
|
|
|
+ const regex = /<think>(.*?)<\/think>/gs;
|
|
|
+ const thinkTagRegex = /<think>/;
|
|
|
+ const match = regex.exec(content);
|
|
|
+ if ( match !== null ) {
|
|
|
+ return match[1];
|
|
|
+ }
|
|
|
+ if (thinkTagRegex.test("<think>")) {
|
|
|
+ return content.slice(7);
|
|
|
+ }
|
|
|
+ return content;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
watchEffect(() => {
|
|
|
- const value = mdi.render(props.content);
|
|
|
+ const { content } = props;
|
|
|
+
|
|
|
+ // if ( content.includes('<think>') ) {
|
|
|
+ // if ( ) {
|
|
|
+
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // processStreamData(content);
|
|
|
+
|
|
|
+ thinkStr.value = thinkParser(content);
|
|
|
+
|
|
|
+ const value = mdi.render(processedValue(content));
|
|
|
|
|
|
const result = splitStringByChartDiv(value)
|
|
|
|
|
@@ -181,6 +270,8 @@ watchEffect(() => {
|
|
|
updateCharts();
|
|
|
})
|
|
|
|
|
|
+
|
|
|
+
|
|
|
// 原本的逻辑,暂时不用
|
|
|
const text = computed(() => {
|
|
|
let value = props.content ?? ""
|
|
@@ -195,6 +286,11 @@ const text = computed(() => {
|
|
|
|
|
|
<template>
|
|
|
<div class="markdown-body text-[15px] break-all" v-if="content">
|
|
|
+ <n-collapse arrow-placement="right" display-directive="show" class="collapse-wrapper">
|
|
|
+ <n-collapse-item title="已深度思考(用时22秒)" name="1">
|
|
|
+ <div class="think-container">{{ thinkStr }}</div>
|
|
|
+ </n-collapse-item>
|
|
|
+ </n-collapse>
|
|
|
<div v-for="item, index in markdownText" :key="index" v-html="item"></div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -207,6 +303,25 @@ const text = computed(() => {
|
|
|
.markdown-body {
|
|
|
word-break: break-word;
|
|
|
|
|
|
+ .collapse-wrapper {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .n-collapse-item__header-main {
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgba(0, 0, 0, 0.6) !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .think-container {
|
|
|
+ border-left: 2px solid rgba(0, 0, 0, 0.08);
|
|
|
+ padding: 4px 0px 4px 12px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 26px;
|
|
|
+ color: rgba(0, 0, 0, 0.6);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
.custom-table-wrapper {
|
|
|
width: 760px;
|
|
|
padding: 10px 10px 4px 10px;
|