style(message): user message use black color
This commit is contained in:
parent
3791556b13
commit
8ea73e14c9
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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 }
|
||||
|
||||
45
src/renderer/src/utils/style.ts
Normal file
45
src/renderer/src/utils/style.ts
Normal 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(' ')
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user