style(message): user message use black color

This commit is contained in:
kangfenmao 2024-11-01 17:43:21 +08:00
parent 3791556b13
commit 8ea73e14c9
6 changed files with 83 additions and 17 deletions

View File

@ -7,7 +7,7 @@ import { fetchChatCompletion } from '@renderer/services/ApiService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { estimateMessageUsage } from '@renderer/services/TokenService'
import { Message, Topic } from '@renderer/types'
import { runAsyncFunction } from '@renderer/utils'
import { classNames, runAsyncFunction } from '@renderer/utils'
import { Divider } from 'antd'
import { Dispatch, FC, memo, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -133,11 +133,13 @@ const MessageItem: FC<Props> = ({
return (
<MessageContainer
key={message.id}
className="message"
className={classNames({
message: true,
'message-assistant': isAssistantMessage,
'message-user': !isAssistantMessage
})}
ref={messageContainerRef}
style={{
alignItems: isAssistantMessage ? 'start' : 'end'
}}>
style={{ alignItems: isAssistantMessage ? 'start' : 'end' }}>
<MessageHeader message={message} assistant={assistant} model={model} />
<MessageContentContainer
style={{
@ -175,6 +177,19 @@ const MessageContainer = styled.div`
&.message-highlight {
background-color: var(--color-primary-mute);
}
// reverse color for user message
&.message-user {
.markdown,
.anticon,
.iconfont,
.message-tokens {
color: var(--color-text);
filter: invert(1);
}
.message-action-button:hover {
background-color: var(--color-white-soft);
}
}
.menubar {
opacity: 0;
transition: opacity 0.2s ease;

View File

@ -42,6 +42,8 @@ const MessageContentLoading = styled.div`
flex-direction: row;
align-items: center;
height: 32px;
margin-top: -5px;
margin-bottom: 5px;
`
export default React.memo(MessageContent)

View File

@ -96,27 +96,27 @@ const MessageMenubar: FC<Props> = (props) => {
<MenusBar className={`menubar ${isLastMessage && 'show'}`}>
{message.role === 'user' && (
<Tooltip title="Edit" mouseEnterDelay={0.8}>
<ActionButton onClick={onEdit}>
<ActionButton className="message-action-button" onClick={onEdit}>
<EditOutlined />
</ActionButton>
</Tooltip>
)}
<Tooltip title={t('common.copy')} mouseEnterDelay={0.8}>
<ActionButton onClick={onCopy}>
<ActionButton className="message-action-button" onClick={onCopy}>
{!copied && <i className="iconfont icon-copy"></i>}
{copied && <CheckOutlined style={{ color: 'var(--color-primary)' }} />}
</ActionButton>
</Tooltip>
{canRegenerate && (
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
<ActionButton onClick={onSelectModel}>
<ActionButton className="message-action-button" onClick={onSelectModel}>
<SyncOutlined />
</ActionButton>
</Tooltip>
)}
{isAssistantMessage && (
<Tooltip title={t('chat.message.new.branch')} mouseEnterDelay={0.8}>
<ActionButton onClick={onNewBranch}>
<ActionButton className="message-action-button" onClick={onNewBranch}>
<ForkOutlined />
</ActionButton>
</Tooltip>
@ -127,14 +127,14 @@ const MessageMenubar: FC<Props> = (props) => {
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
onConfirm={() => onDeleteMessage?.(message)}>
<Tooltip title={t('common.delete')} mouseEnterDelay={1}>
<ActionButton>
<ActionButton className="message-action-button">
<DeleteOutlined />
</ActionButton>
</Tooltip>
</Popconfirm>
{!isUserMessage && (
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>
<ActionButton>
<ActionButton className="message-action-button">
<MenuOutlined />
</ActionButton>
</Dropdown>

View File

@ -15,7 +15,11 @@ const MessgeTokens: React.FC<{ message: Message; isLastMessage: boolean }> = ({
}
if (message.role === 'user') {
return <MessageMetadata onClick={locateMessage}>Tokens: {message?.usage?.total_tokens}</MessageMetadata>
return (
<MessageMetadata className="message-tokens" onClick={locateMessage}>
Tokens: {message?.usage?.total_tokens}
</MessageMetadata>
)
}
if (isLastMessage && generating) {
@ -24,7 +28,7 @@ const MessgeTokens: React.FC<{ message: Message; isLastMessage: boolean }> = ({
if (message.role === 'assistant') {
return (
<MessageMetadata onClick={locateMessage}>
<MessageMetadata className="message-tokens" onClick={locateMessage}>
Tokens: {message?.usage?.total_tokens} | {message?.usage?.prompt_tokens} | {message?.usage?.completion_tokens}
</MessageMetadata>
)

View File

@ -4,6 +4,8 @@ import html2canvas from 'html2canvas'
// @ts-ignore next-line`
import { v4 as uuidv4 } from 'uuid'
import { classNames } from './style'
export const runAsyncFunction = async (fn: () => void) => {
await fn()
}
@ -330,10 +332,6 @@ export function formatFileSize(file: FileType) {
return (size / 1024).toFixed(2) + ' KB'
}
export function classNames(...classes: Array<string | boolean | undefined | null>) {
return classes.filter(Boolean).join(' ')
}
export function sortByEnglishFirst(a: string, b: string) {
const isAEnglish = /^[a-zA-Z]/.test(a)
const isBEnglish = /^[a-zA-Z]/.test(b)
@ -341,3 +339,5 @@ export function sortByEnglishFirst(a: string, b: string) {
if (!isAEnglish && isBEnglish) return 1
return a.localeCompare(b)
}
export { classNames }

View File

@ -0,0 +1,45 @@
type ClassValue = string | number | boolean | undefined | null | ClassDictionary | ClassArray
interface ClassDictionary {
[id: string]: any
}
interface ClassArray extends Array<ClassValue> {}
// Example:
// classNames('foo', 'bar'); // => 'foo bar'
// classNames('foo', { bar: true }); // => 'foo bar'
// classNames({ foo: true, bar: false }); // => 'foo'
// classNames(['foo', 'bar']); // => 'foo bar'
// classNames('foo', null, 'bar'); // => 'foo bar'
// classNames({ message: true, 'message-assistant': true }); // => 'message message-assistant'
/**
* class
* @param args
* @returns
*/
export function classNames(...args: ClassValue[]): string {
const classes: string[] = []
args.forEach((arg) => {
if (!arg) return
if (typeof arg === 'string' || typeof arg === 'number') {
classes.push(arg.toString())
} else if (Array.isArray(arg)) {
const inner = classNames(...arg)
if (inner) {
classes.push(inner)
}
} else if (typeof arg === 'object') {
Object.entries(arg).forEach(([key, value]) => {
if (value) {
classes.push(key)
}
})
}
})
return classes.filter(Boolean).join(' ')
}