🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)

- Ran: bun run eslint:fix && bun run lint:fix
- Inserted AGPL license header via eslint-plugin-header
- Enforced no-multiple-empty-lines and other lint rules
- Formatted code using Prettier v3 (@so1ve/prettier-config)
- No functional changes; formatting-only baseline across JS/JSX files
This commit is contained in:
t0ng7u
2025-08-30 21:15:10 +08:00
parent 41cf516ec5
commit 0d57b1acd4
274 changed files with 11025 additions and 7659 deletions

View File

@@ -18,18 +18,10 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useRef, useEffect } from 'react';
import {
Typography,
TextArea,
Button,
} from '@douyinfe/semi-ui';
import { Typography, TextArea, Button } from '@douyinfe/semi-ui';
import MarkdownRenderer from '../common/markdown/MarkdownRenderer';
import ThinkingContent from './ThinkingContent';
import {
Loader2,
Check,
X,
} from 'lucide-react';
import { Loader2, Check, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
const MessageContent = ({
@@ -41,13 +33,14 @@ const MessageContent = ({
onEditSave,
onEditCancel,
editValue,
onEditValueChange
onEditValueChange,
}) => {
const { t } = useTranslation();
const previousContentLengthRef = useRef(0);
const lastContentRef = useRef('');
const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete';
const isThinkingStatus =
message.status === 'loading' || message.status === 'incomplete';
useEffect(() => {
if (!isThinkingStatus) {
@@ -60,10 +53,11 @@ const MessageContent = ({
let errorText;
if (Array.isArray(message.content)) {
const textContent = message.content.find(item => item.type === 'text');
errorText = textContent && textContent.text && typeof textContent.text === 'string'
? textContent.text
: t('请求发生错误');
const textContent = message.content.find((item) => item.type === 'text');
errorText =
textContent && textContent.text && typeof textContent.text === 'string'
? textContent.text
: t('请求发生错误');
} else if (typeof message.content === 'string') {
errorText = message.content;
} else {
@@ -72,21 +66,21 @@ const MessageContent = ({
return (
<div className={`${className}`}>
<Typography.Text className="text-white">
{errorText}
</Typography.Text>
<Typography.Text className='text-white'>{errorText}</Typography.Text>
</div>
);
}
let currentExtractedThinkingContent = null;
let currentDisplayableFinalContent = "";
let currentDisplayableFinalContent = '';
let thinkingSource = null;
const getTextContent = (content) => {
if (Array.isArray(content)) {
const textItem = content.find(item => item.type === 'text');
return textItem && textItem.text && typeof textItem.text === 'string' ? textItem.text : '';
const textItem = content.find((item) => item.type === 'text');
return textItem && textItem.text && typeof textItem.text === 'string'
? textItem.text
: '';
} else if (typeof content === 'string') {
return content;
}
@@ -97,7 +91,7 @@ const MessageContent = ({
if (message.role === 'assistant') {
let baseContentForDisplay = getTextContent(message.content);
let combinedThinkingContent = "";
let combinedThinkingContent = '';
if (message.reasoningContent) {
combinedThinkingContent = message.reasoningContent;
@@ -112,7 +106,9 @@ const MessageContent = ({
let lastIndex = 0;
while ((match = thinkTagRegex.exec(baseContentForDisplay)) !== null) {
replyParts.push(baseContentForDisplay.substring(lastIndex, match.index));
replyParts.push(
baseContentForDisplay.substring(lastIndex, match.index),
);
thoughtsFromPairedTags.push(match[1]);
lastIndex = match.index + match[0].length;
}
@@ -125,7 +121,9 @@ const MessageContent = ({
} else {
combinedThinkingContent = pairedThoughtsStr;
}
thinkingSource = thinkingSource ? thinkingSource + ' & <think> tags' : '<think> tags';
thinkingSource = thinkingSource
? thinkingSource + ' & <think> tags'
: '<think> tags';
}
baseContentForDisplay = replyParts.join('');
@@ -134,37 +132,55 @@ const MessageContent = ({
if (isThinkingStatus) {
const lastOpenThinkIndex = baseContentForDisplay.lastIndexOf('<think>');
if (lastOpenThinkIndex !== -1) {
const fragmentAfterLastOpen = baseContentForDisplay.substring(lastOpenThinkIndex);
const fragmentAfterLastOpen =
baseContentForDisplay.substring(lastOpenThinkIndex);
if (!fragmentAfterLastOpen.includes('</think>')) {
const unclosedThought = fragmentAfterLastOpen.substring('<think>'.length).trim();
const unclosedThought = fragmentAfterLastOpen
.substring('<think>'.length)
.trim();
if (unclosedThought) {
if (combinedThinkingContent) {
combinedThinkingContent += '\n\n---\n\n' + unclosedThought;
} else {
combinedThinkingContent = unclosedThought;
}
thinkingSource = thinkingSource ? thinkingSource + ' + streaming <think>' : 'streaming <think>';
thinkingSource = thinkingSource
? thinkingSource + ' + streaming <think>'
: 'streaming <think>';
}
baseContentForDisplay = baseContentForDisplay.substring(0, lastOpenThinkIndex);
baseContentForDisplay = baseContentForDisplay.substring(
0,
lastOpenThinkIndex,
);
}
}
}
currentExtractedThinkingContent = combinedThinkingContent || null;
currentDisplayableFinalContent = baseContentForDisplay.replace(/<\/?think>/g, '').trim();
currentDisplayableFinalContent = baseContentForDisplay
.replace(/<\/?think>/g, '')
.trim();
}
const finalExtractedThinkingContent = currentExtractedThinkingContent;
const finalDisplayableFinalContent = currentDisplayableFinalContent;
if (message.role === 'assistant' &&
if (
message.role === 'assistant' &&
isThinkingStatus &&
!finalExtractedThinkingContent &&
(!finalDisplayableFinalContent || finalDisplayableFinalContent.trim() === '')) {
(!finalDisplayableFinalContent ||
finalDisplayableFinalContent.trim() === '')
) {
return (
<div className={`${className} flex items-center gap-2 sm:gap-4 bg-gradient-to-r from-purple-50 to-indigo-50`}>
<div className="w-5 h-5 rounded-full bg-gradient-to-br from-purple-500 to-indigo-600 flex items-center justify-center shadow-lg">
<Loader2 className="animate-spin text-white" size={styleState.isMobile ? 16 : 20} />
<div
className={`${className} flex items-center gap-2 sm:gap-4 bg-gradient-to-r from-purple-50 to-indigo-50`}
>
<div className='w-5 h-5 rounded-full bg-gradient-to-br from-purple-500 to-indigo-600 flex items-center justify-center shadow-lg'>
<Loader2
className='animate-spin text-white'
size={styleState.isMobile ? 16 : 20}
/>
</div>
</div>
);
@@ -173,12 +189,17 @@ const MessageContent = ({
return (
<div className={className}>
{message.role === 'system' && (
<div className="mb-2 sm:mb-4">
<div className="flex items-center gap-2 p-2 sm:p-3 bg-gradient-to-r from-amber-50 to-orange-50 rounded-lg" style={{ border: '1px solid var(--semi-color-border)' }}>
<div className="w-4 h-4 sm:w-5 sm:h-5 rounded-full bg-gradient-to-br from-amber-500 to-orange-600 flex items-center justify-center shadow-sm">
<Typography.Text className="text-white text-xs font-bold">S</Typography.Text>
<div className='mb-2 sm:mb-4'>
<div
className='flex items-center gap-2 p-2 sm:p-3 bg-gradient-to-r from-amber-50 to-orange-50 rounded-lg'
style={{ border: '1px solid var(--semi-color-border)' }}
>
<div className='w-4 h-4 sm:w-5 sm:h-5 rounded-full bg-gradient-to-br from-amber-500 to-orange-600 flex items-center justify-center shadow-sm'>
<Typography.Text className='text-white text-xs font-bold'>
S
</Typography.Text>
</div>
<Typography.Text className="text-amber-700 text-xs sm:text-sm font-medium">
<Typography.Text className='text-amber-700 text-xs sm:text-sm font-medium'>
{t('系统消息')}
</Typography.Text>
</div>
@@ -196,7 +217,7 @@ const MessageContent = ({
)}
{isEditing ? (
<div className="space-y-3">
<div className='space-y-3'>
<TextArea
value={editValue}
onChange={(value) => onEditValueChange(value)}
@@ -207,27 +228,27 @@ const MessageContent = ({
fontSize: styleState.isMobile ? '14px' : '15px',
lineHeight: '1.6',
}}
className="!border-blue-200 focus:!border-blue-400 !bg-blue-50/50"
className='!border-blue-200 focus:!border-blue-400 !bg-blue-50/50'
/>
<div className="flex items-center gap-2 w-full">
<div className='flex items-center gap-2 w-full'>
<Button
size="small"
type="danger"
theme="light"
size='small'
type='danger'
theme='light'
icon={<X size={14} />}
onClick={onEditCancel}
className="flex-1"
className='flex-1'
>
{t('取消')}
</Button>
<Button
size="small"
type="warning"
theme="solid"
size='small'
type='warning'
theme='solid'
icon={<Check size={14} />}
onClick={onEditSave}
disabled={!editValue || editValue.trim() === ''}
className="flex-1"
className='flex-1'
>
{t('保存')}
</Button>
@@ -236,19 +257,23 @@ const MessageContent = ({
) : (
(() => {
if (Array.isArray(message.content)) {
const textContent = message.content.find(item => item.type === 'text');
const imageContents = message.content.filter(item => item.type === 'image_url');
const textContent = message.content.find(
(item) => item.type === 'text',
);
const imageContents = message.content.filter(
(item) => item.type === 'image_url',
);
return (
<div>
{imageContents.length > 0 && (
<div className="mb-3 space-y-2">
<div className='mb-3 space-y-2'>
{imageContents.map((imgItem, index) => (
<div key={index} className="max-w-sm">
<div key={index} className='max-w-sm'>
<img
src={imgItem.image_url.url}
alt={`用户上传的图片 ${index + 1}`}
className="rounded-lg max-w-full h-auto shadow-sm border"
className='rounded-lg max-w-full h-auto shadow-sm border'
style={{ maxHeight: '300px' }}
onError={(e) => {
e.target.style.display = 'none';
@@ -256,7 +281,7 @@ const MessageContent = ({
}}
/>
<div
className="text-red-500 text-sm p-2 bg-red-50 rounded-lg border border-red-200"
className='text-red-500 text-sm p-2 bg-red-50 rounded-lg border border-red-200'
style={{ display: 'none' }}
>
图片加载失败: {imgItem.image_url.url}
@@ -266,28 +291,42 @@ const MessageContent = ({
</div>
)}
{textContent && textContent.text && typeof textContent.text === 'string' && textContent.text.trim() !== '' && (
<div className={`prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm ${message.role === 'user' ? 'user-message' : ''}`}>
<MarkdownRenderer
content={textContent.text}
className={message.role === 'user' ? 'user-message' : ''}
animated={false}
previousContentLength={0}
/>
</div>
)}
{textContent &&
textContent.text &&
typeof textContent.text === 'string' &&
textContent.text.trim() !== '' && (
<div
className={`prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm ${message.role === 'user' ? 'user-message' : ''}`}
>
<MarkdownRenderer
content={textContent.text}
className={
message.role === 'user' ? 'user-message' : ''
}
animated={false}
previousContentLength={0}
/>
</div>
)}
</div>
);
}
if (typeof message.content === 'string') {
if (message.role === 'assistant') {
if (finalDisplayableFinalContent && finalDisplayableFinalContent.trim() !== '') {
if (
finalDisplayableFinalContent &&
finalDisplayableFinalContent.trim() !== ''
) {
// 获取上一次的内容长度
let prevLength = 0;
if (isThinkingStatus && lastContentRef.current) {
// 只有当前内容包含上一次内容时,才使用上一次的长度
if (finalDisplayableFinalContent.startsWith(lastContentRef.current)) {
if (
finalDisplayableFinalContent.startsWith(
lastContentRef.current,
)
) {
prevLength = lastContentRef.current.length;
}
}
@@ -298,10 +337,10 @@ const MessageContent = ({
}
return (
<div className="prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm">
<div className='prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm'>
<MarkdownRenderer
content={finalDisplayableFinalContent}
className=""
className=''
animated={isThinkingStatus}
previousContentLength={prevLength}
/>
@@ -310,7 +349,9 @@ const MessageContent = ({
}
} else {
return (
<div className={`prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm ${message.role === 'user' ? 'user-message' : ''}`}>
<div
className={`prose prose-xs sm:prose-sm prose-gray max-w-none overflow-x-auto text-xs sm:text-sm ${message.role === 'user' ? 'user-message' : ''}`}
>
<MarkdownRenderer
content={message.content}
className={message.role === 'user' ? 'user-message' : ''}
@@ -329,4 +370,4 @@ const MessageContent = ({
);
};
export default MessageContent;
export default MessageContent;