diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index ba14d310f..06181e775 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -657,6 +657,7 @@ class WebSocketDialogMsg extends AbstractModel $text = $msgData['text'] ?? ''; if (!$text) return ''; if ($msgData['type'] === 'md') { + $text = preg_replace("/:::\s*reasoning[\s\S]*?:::/", "", $text); $text = Base::markdown2html($text); $text = self::previewConvertTaskList($text); } diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js index e8528e829..e34edb352 100755 --- a/resources/assets/js/functions/web.js +++ b/resources/assets/js/functions/web.js @@ -246,6 +246,7 @@ import {convertLocalResourcePath} from "../components/Replace/utils"; return ''; } if (type === 'md') { + text = text.replace(/:::\s*reasoning[\s\S]*?:::/g, ""); text = MarkdownPreview(text); } // diff --git a/resources/assets/js/store/markdown.js b/resources/assets/js/store/markdown.js index 57c3ee2b9..ec115ae30 100644 --- a/resources/assets/js/store/markdown.js +++ b/resources/assets/js/store/markdown.js @@ -68,7 +68,72 @@ const MarkdownPluginUtils = { return value; }, - // 修改初始化插件函数 + // 修改初始化插件函数(推理) + initReasoningPlugin(md) { + md.block.ruler.before('fence', 'reasoning', (state, startLine, endLine, silent) => { + const start = state.bMarks[startLine] + state.tShift[startLine]; + const max = state.eMarks[startLine]; + const firstLine = state.src.slice(start, max).trim(); + + // 检查是否匹配 :::reasoning 开始标记 + const match = firstLine.match(/^:::\s*reasoning(?:\s+(\S+))?$/); + if (!match) { + return false; + } + + if (silent) { + return true; + } + + let nextLine = startLine + 1; + let content = []; + + // 查找结束标记 ::: + while (nextLine < endLine) { + const lineStart = state.bMarks[nextLine] + state.tShift[nextLine]; + const lineMax = state.eMarks[nextLine]; + const currentLine = state.src.slice(lineStart, lineMax); + + if (currentLine.trim() === ':::') { + break; + } + + content.push(state.getLines(nextLine, nextLine + 1, state.tShift[nextLine], true)); + nextLine++; + } + + // 创建外层容器 + let token = state.push('reasoning_open', 'div', 1); + token.attrs = [['class', 'apply-reasoning']]; + + // 创建标签 + token = state.push('reasoning_label_open', 'div', 1); + token.attrs = [['class', 'reasoning-label']]; + token = state.push('text', '', 0); + token.content = $A.L('思考过程'); + state.push('reasoning_label_close', 'div', -1); + + // 创建内容容器 + token = state.push('reasoning_content_open', 'div', 1); + token.attrs = [['class', 'reasoning-content']]; + + // 处理内容 + if (content.length > 0) { + state.md.block.parse(content.join('\n'), state.md, state.env, state.tokens); + } + + // 关闭内容容器 + state.push('reasoning_content_close', 'div', -1); + + // 关闭外层容器 + state.push('reasoning_close', 'div', -1); + + state.line = nextLine + 1; + return true; + }); + }, + + // 修改初始化插件函数(创建任务) initCreateTaskPlugin(md) { md.block.ruler.before('fence', 'create-task', (state, startLine, endLine, silent) => { const start = state.bMarks[startLine] + state.tShift[startLine]; @@ -201,6 +266,7 @@ export function MarkdownConver(text) { }) MarkdownUtils.mdi.use(mila, {attrs: {target: '_blank', rel: 'noopener'}}) MarkdownUtils.mdi.use(mdKatex, {blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000'}) + MarkdownPluginUtils.initReasoningPlugin(MarkdownUtils.mdi); MarkdownPluginUtils.initCreateTaskPlugin(MarkdownUtils.mdi); } return MarkdownUtils.formatMsg(MarkdownUtils.mdi.render(text)) @@ -209,6 +275,7 @@ export function MarkdownConver(text) { export function MarkdownPreview(text) { if (MarkdownUtils.mds === null) { MarkdownUtils.mds = MarkdownIt() + MarkdownPluginUtils.initReasoningPlugin(MarkdownUtils.mds); MarkdownPluginUtils.initCreateTaskPlugin(MarkdownUtils.mds); } return MarkdownUtils.mds.render(text) diff --git a/resources/assets/sass/dark.scss b/resources/assets/sass/dark.scss index 5495ea282..869d67360 100644 --- a/resources/assets/sass/dark.scss +++ b/resources/assets/sass/dark.scss @@ -306,6 +306,12 @@ body.dark-mode-reverse { } } } + + .apply-reasoning { + &:before { + background-color: #4e4e56; + } + } } .dialog-group-info { diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index 32defeb85..fe8605e37 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -2053,6 +2053,34 @@ pointer-events: none; } + .apply-reasoning { + margin: 0 0 12px 0; + padding: 0 0 0 13px; + line-height: 26px; + position: relative; + + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 2px; + background-color: #e1e1e1; + } + + .reasoning-label { + margin-bottom: 4px; + } + + .reasoning-content { + opacity: 0.5; + > p:last-child { + margin-bottom: 0; + } + } + } + .apply-create-task { min-width: 160px; margin-bottom: 16px;