mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-05 00:04:50 +08:00
feat: 优化对话记录 diff
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { DiffEditor } from '@monaco-editor/react';
|
||||
|
||||
interface DiffProps {
|
||||
@@ -14,6 +14,30 @@ const Diff: React.FC<DiffProps> = ({
|
||||
language = 'javascript',
|
||||
height = 400,
|
||||
}) => {
|
||||
const editorRef = useRef<any>(null);
|
||||
const monacoRef = useRef<any>(null);
|
||||
|
||||
// 卸载时主动 dispose
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (editorRef.current && monacoRef.current) {
|
||||
const editor = editorRef.current;
|
||||
// DiffEditor getModel() 返回 [original, modified]
|
||||
const models = editor.getModel ? editor.getModel() : [];
|
||||
if (models && Array.isArray(models)) {
|
||||
models.forEach(
|
||||
(model: any) => model && model.dispose && model.dispose()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleMount = (editor: any, monaco: any) => {
|
||||
editorRef.current = editor;
|
||||
monacoRef.current = monaco;
|
||||
};
|
||||
|
||||
// 处理高度和宽度样式
|
||||
const boxHeight = typeof height === 'number' ? `${height}px` : height;
|
||||
const boxWidth = 1000; // 默认宽度800px
|
||||
@@ -27,25 +51,19 @@ const Diff: React.FC<DiffProps> = ({
|
||||
height='100%'
|
||||
width='100%'
|
||||
language={language}
|
||||
original={original}
|
||||
modified={modified}
|
||||
original={original || ''}
|
||||
modified={modified || ''}
|
||||
theme='vs-dark'
|
||||
onMount={handleMount}
|
||||
options={{
|
||||
readOnly: true,
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
wordWrap: 'off',
|
||||
lineNumbers: 'on',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
horizontal: 'hidden',
|
||||
handleMouseWheel: false,
|
||||
alwaysConsumeMouseWheel: false,
|
||||
useShadows: false,
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
guides: {
|
||||
indentation: true,
|
||||
|
||||
@@ -43,13 +43,80 @@ export const toolNames = [
|
||||
// 去掉下划线的标签名,用于Markdown渲染
|
||||
export const toolTagNames = toolNames.map((name) => name.replace(/_/g, ''));
|
||||
|
||||
type ToolInfo = any;
|
||||
// 支持多组 diff 分隔符,容错处理
|
||||
function parseAndMergeDiffs(diffText: string) {
|
||||
const diffBlocks: { search: string; replace: string }[] = [];
|
||||
const lines = diffText.split('\n');
|
||||
let inDiff = false;
|
||||
let inSearch = false;
|
||||
let inReplace = false;
|
||||
let searchBuffer: string[] = [];
|
||||
let replaceBuffer: string[] = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (/^<+ *SEARCH/.test(line)) {
|
||||
inDiff = true;
|
||||
inSearch = true;
|
||||
inReplace = false;
|
||||
searchBuffer = [];
|
||||
replaceBuffer = [];
|
||||
continue;
|
||||
}
|
||||
if (/^====+$/.test(line)) {
|
||||
if (inDiff && inSearch) {
|
||||
inSearch = false;
|
||||
inReplace = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (/^>+ *REPLACE/.test(line)) {
|
||||
if (inDiff && inReplace) {
|
||||
diffBlocks.push({
|
||||
search: searchBuffer.join('\n'),
|
||||
replace: replaceBuffer.join('\n'),
|
||||
});
|
||||
inDiff = false;
|
||||
inReplace = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (inDiff) {
|
||||
if (inSearch) {
|
||||
searchBuffer.push(line);
|
||||
} else if (inReplace) {
|
||||
replaceBuffer.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 容错:如果最后一组没有正常结束
|
||||
if (inDiff) {
|
||||
diffBlocks.push({
|
||||
search: searchBuffer.join('\n'),
|
||||
replace: replaceBuffer.join('\n'),
|
||||
});
|
||||
}
|
||||
|
||||
const mergedSearch = diffBlocks.map((b) => b.search).join('\n');
|
||||
const mergedReplace = diffBlocks.map((b) => b.replace).join('\n');
|
||||
|
||||
return { mergedSearch, mergedReplace, diffBlocks };
|
||||
}
|
||||
|
||||
// 预处理 markdown,提取所有 <diff> 内容,生成 diffMap
|
||||
function preprocessMarkdown(mdContent: string) {
|
||||
let diffIndex = 0;
|
||||
const diffMap: Record<string, string> = {};
|
||||
const newMd = mdContent.replace(
|
||||
// 自动补全未闭合的 </diff>
|
||||
let fixedMd = mdContent;
|
||||
const openDiffCount = (fixedMd.match(/<diff>/g) || []).length;
|
||||
const closeDiffCount = (fixedMd.match(/<\/diff>/g) || []).length;
|
||||
if (openDiffCount > closeDiffCount) {
|
||||
// 补全缺失的 </diff>
|
||||
for (let i = 0; i < openDiffCount - closeDiffCount; i++) {
|
||||
fixedMd += '</diff>';
|
||||
}
|
||||
}
|
||||
const newMd = fixedMd.replace(
|
||||
/<diff>([\s\S]*?)<\/diff>/g,
|
||||
(_, diffContent) => {
|
||||
const id = `diff-${diffIndex++}`;
|
||||
@@ -63,22 +130,11 @@ function preprocessMarkdown(mdContent: string) {
|
||||
const MarkDown = ({
|
||||
loading = false,
|
||||
content,
|
||||
showToolInfo = {},
|
||||
setShowToolInfo,
|
||||
setCurrentToolId,
|
||||
handleSearchAbort,
|
||||
}: {
|
||||
loading?: boolean;
|
||||
content: string;
|
||||
showToolInfo: Record<string, ToolInfo>;
|
||||
setShowToolInfo: (value: Record<string, ToolInfo>) => void;
|
||||
setCurrentToolId?: (value: string) => void;
|
||||
handleSearchAbort?: () => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [diffContent, setDiffContent] = useState([]);
|
||||
const [showThink, setShowThink] = useState(false);
|
||||
const editorRef = useRef<any>(null);
|
||||
|
||||
// 删除 content 中 <thinking> 和 <execute_command> 标签,并保留标签中的内容
|
||||
const deleteTags = (content: string) => {
|
||||
@@ -176,25 +232,22 @@ const MarkDown = ({
|
||||
// 去掉 user-content- 前缀
|
||||
const id = node?.properties?.id?.replace(/^user-content-/, '');
|
||||
const rawDiff = id ? diffMap[id] : '';
|
||||
// 解析 rawDiff 为 original 和 modified
|
||||
let original = '',
|
||||
modified = '';
|
||||
if (rawDiff) {
|
||||
const match = rawDiff.match(
|
||||
/<{2,} *SEARCH([\s\S]*?)={2,}([\s\S]*?)>{2,} *REPLACE/
|
||||
);
|
||||
if (match) {
|
||||
// 清理行号标记和分隔线
|
||||
const cleanDiff = (str: string) =>
|
||||
str
|
||||
.replace(/:start_line:\d+\n?[-=]+/g, '')
|
||||
.replace(/^-{2,}\n?/gm, '')
|
||||
.replace(/^={2,}\n?/gm, '')
|
||||
.replace(/^\s+|\s+$/g, '');
|
||||
original = cleanDiff(match[1].trim());
|
||||
modified = cleanDiff(match[2].trim());
|
||||
}
|
||||
const { mergedSearch, mergedReplace } =
|
||||
parseAndMergeDiffs(rawDiff);
|
||||
// 清理行号标记和分隔线
|
||||
const cleanDiff = (str: string) =>
|
||||
str
|
||||
.replace(/:start_line:\d+\n?[-=]+/g, '')
|
||||
.replace(/^-{2,}\n?/gm, '')
|
||||
.replace(/^={2,}\n?/gm, '')
|
||||
.replace(/^\n+|\n+$/g, '');
|
||||
original = cleanDiff(mergedSearch);
|
||||
modified = cleanDiff(mergedReplace);
|
||||
}
|
||||
|
||||
return (
|
||||
<Diff
|
||||
original={original}
|
||||
|
||||
@@ -294,13 +294,6 @@ const AuthPage = () => {
|
||||
// 渲染登录表单
|
||||
const renderLoginForm = () => (
|
||||
<>
|
||||
<LogoContainer>
|
||||
<LogoImage src={Logo} alt='Monkey Code Logo' />
|
||||
<LogoTitle variant='h4' gutterBottom>
|
||||
Monkey Code
|
||||
</LogoTitle>
|
||||
</LogoContainer>
|
||||
|
||||
<Box component='form' onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={12}>{renderUsernameField()}</Grid>
|
||||
@@ -322,6 +315,12 @@ const AuthPage = () => {
|
||||
return (
|
||||
<StyledContainer id='box'>
|
||||
<StyledPaper elevation={3}>
|
||||
<LogoContainer>
|
||||
<LogoImage src={Logo} alt='Monkey Code Logo' />
|
||||
<LogoTitle variant='h4' gutterBottom>
|
||||
Monkey Code
|
||||
</LogoTitle>
|
||||
</LogoContainer>
|
||||
{!loginSetting.disable_password_login && renderLoginForm()}
|
||||
{loginSetting.enable_dingtalk_oauth && dingdingLogin()}
|
||||
</StyledPaper>
|
||||
|
||||
@@ -125,13 +125,6 @@ const ChatDetailModal = ({
|
||||
lineNumbers: 'on',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
horizontal: 'hidden',
|
||||
handleMouseWheel: false,
|
||||
alwaysConsumeMouseWheel: false,
|
||||
useShadows: false,
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
guides: {
|
||||
indentation: true,
|
||||
|
||||
@@ -65,7 +65,7 @@ const Completion = () => {
|
||||
const [filterLang, setFilterLang] = useState('');
|
||||
const [filterAccept, setFilterAccept] = useState<
|
||||
'accepted' | 'unaccepted' | ''
|
||||
>('');
|
||||
>('accepted');
|
||||
|
||||
const { data: userOptions = { users: [] } } = useRequest(() =>
|
||||
getListUser({
|
||||
|
||||
@@ -85,6 +85,7 @@ const lightTheme = createTheme(
|
||||
borderWidth: '1px !important',
|
||||
},
|
||||
borderRadius: '10px !important',
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user