文件
mynode/packages/app-desktop/gui/ChatPanel/ChatMessageItem.tsx
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

85 行
2.3 KiB
TypeScript

import * as React from 'react';
import { _ } from '@joplin/lib/locale';
import { AiChatMessage } from '../../app.reducer';
import InlineMarkdownDisplay from '../InlineMarkdownDisplay';
import { ChatMessage, ChatRole, ChatToolMessage } from '@joplin/lib/services/ai/types';
const toolResultSummary = (actions: ChatMessage[]) => {
const toolResults = actions.filter(action => action.role === ChatRole.Tool);
const editSummary = () => {
let applied = 0;
let missed = 0;
let lastEdit: ChatToolMessage|null = null;
for (const result of toolResults) {
if (!result.isEdit) continue;
if (result.isError) {
missed ++;
} else {
applied ++;
lastEdit = result;
}
}
if (applied + missed === 0) return '';
if (missed === 0 && applied > 1) return _('%d edit(s) applied.', applied);
if (missed === 0 && lastEdit) return lastEdit.userDescription;
return _('%d edit(s) applied, %d could not be placed automatically.', applied, missed);
};
return [
...toolResults
.filter(result => !result.isEdit)
.map(result => result.userDescription),
editSummary(),
]
.filter(item => !!item)
.join('\n');
};
const hasToolError = (actions: ChatMessage[]) => {
return actions.some(action => action.role === ChatRole.Tool && action.isError);
};
interface Props {
message: AiChatMessage;
}
const ChatMessageItem: React.FC<Props> = ({ message }) => {
if (message.role === 'separator') {
return <div className='separator'>{message.text}</div>;
}
if (message.role === 'error') {
return <div className='error'>{message.text}</div>;
}
const summary = message.role === 'assistant' ? toolResultSummary(message.raw) : '';
// Always show something in the message box:
const textContent = !message.text && !summary ? _('(no message)') : message.text;
const renderMarkdown = message.role === 'assistant';
const content = renderMarkdown
? <InlineMarkdownDisplay
className='content'
markdown={textContent}
allowLinks={false} />
: <div className='content'>{textContent}</div>;
return (
<div className={`chat-message turn -${message.role}`}>
{content}
{summary && (
<div className='meta'>
{hasToolError(message.raw)
? <span className='warning'>{summary}</span>
: <span>{summary}</span>}
</div>
)}
</div>
);
};
export default ChatMessageItem;