Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: styling message action toolbar #737

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion web/screens/Chat/ChatItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 7,10 @@ import SimpleTextMessage from '../SimpleTextMessage'
type Ref = HTMLDivElement

const ChatItem = forwardRef<Ref, ThreadMessage>((message, ref) => (
<div ref={ref} className="py-4 even:bg-secondary dark:even:bg-secondary/20">
<div
ref={ref}
className="relative py-4 first:pb-14 even:bg-secondary dark:even:bg-secondary/20"
>
<SimpleTextMessage {...message} />
</div>
))
Expand Down
118 changes: 62 additions & 56 deletions web/screens/Chat/MessageToolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,63 35,69 @@ const MessageToolbar = ({ message }: { message: ThreadMessage }) => {
}
return (
<div className="flex flex-row items-center">
{message.status === MessageStatus.Pending && (
<StopCircle
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
size={20}
onClick={() => stopInference()}
/>
)}
{message.status !== MessageStatus.Pending &&
message.id === messages[0]?.id && (
<RefreshCcw
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
size={20}
onClick={() => {
const messageRequest: MessageRequest = {
id: message.id ?? '',
messages: messages
.slice(1, messages.length)
.reverse()
.map((e) => {
return {
content: e.content,
role: e.role,
} as ChatCompletionMessage
}),
threadId: message.threadId ?? '',
}
if (message.role === ChatCompletionRole.Assistant) {
deleteAMessage(message.id ?? '')
}
events.emit(EventName.OnNewMessageRequest, messageRequest)
}}
/>
<div className="flex overflow-hidden rounded-md border border-border bg-background/20">
{message.status === MessageStatus.Pending && (
<div
className="cursor-pointer border-r border-border px-2 py-2 hover:bg-background/80"
onClick={() => stopInference()}
>
<StopCircle size={14} />
</div>
)}
<ClipboardCopy
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
size={20}
onClick={() => {
navigator.clipboard.writeText(message.content ?? '')
toaster({
title: 'Copied to clipboard',
})
}}
/>
<Trash2Icon
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
size={20}
onClick={async () => {
deleteAMessage(message.id ?? '')
if (thread)
await pluginManager
.get<ConversationalPlugin>(PluginType.Conversational)
?.saveConversation({
...thread,
messages: messages.filter((e) => e.id !== message.id),
})
}}
/>
{message.status !== MessageStatus.Pending &&
message.id === messages[0]?.id && (
<div
className="cursor-pointer border-r border-border px-2 py-2 hover:bg-background/80"
onClick={() => {
const messageRequest: MessageRequest = {
id: message.id ?? '',
messages: messages
.slice(1, messages.length)
.reverse()
.map((e) => {
return {
content: e.content,
role: e.role,
} as ChatCompletionMessage
}),
threadId: message.threadId ?? '',
}
if (message.role === ChatCompletionRole.Assistant) {
deleteAMessage(message.id ?? '')
}
events.emit(EventName.OnNewMessageRequest, messageRequest)
}}
>
<RefreshCcw size={14} />
</div>
)}
<div
className="cursor-pointer border-r border-border px-2 py-2 hover:bg-background/80"
onClick={() => {
navigator.clipboard.writeText(message.content ?? '')
toaster({
title: 'Copied to clipboard',
})
}}
>
<ClipboardCopy size={14} />
</div>
<div
className="cursor-pointer px-2 py-2 hover:bg-background/80"
onClick={async () => {
deleteAMessage(message.id ?? '')
if (thread)
await pluginManager
.get<ConversationalPlugin>(PluginType.Conversational)
?.saveConversation({
...thread,
messages: messages.filter((e) => e.id !== message.id),
})
}}
>
<Trash2Icon size={14} />
</div>
</div>
</div>
)
}
Expand Down
21 changes: 16 additions & 5 deletions web/screens/Chat/SimpleTextMessage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 5,7 @@ import { ChatCompletionRole, MessageStatus, ThreadMessage } from '@janhq/core'

import hljs from 'highlight.js'

import { useAtomValue } from 'jotai'
import { Marked } from 'marked'

import { markedHighlight } from 'marked-highlight'
Expand All @@ -21,6 22,8 @@ import { displayDate } from '@/utils/datetime'

import MessageToolbar from '../MessageToolbar'

import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'

const marked = new Marked(
markedHighlight({
langPrefix: 'hljs',
Expand Down Expand Up @@ -55,6 58,7 @@ const SimpleTextMessage: React.FC<ThreadMessage> = (props) => {

const [lastTimestamp, setLastTimestamp] = useState<number | undefined>()
const [tokenSpeed, setTokenSpeed] = useState(0)
const messages = useAtomValue(getCurrentChatMessagesAtom)

useEffect(() => {
if (props.status === MessageStatus.Ready || !experimentalFeatureEnabed) {
Expand All @@ -73,22 77,29 @@ const SimpleTextMessage: React.FC<ThreadMessage> = (props) => {

setTokenSpeed(averageTokenSpeed)
setTokenCount(totalTokenCount)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.content])

return (
<div className="group mx-auto rounded-xl px-4 lg:w-3/4">
<div className="group relative mx-auto rounded-xl px-4 lg:w-3/4">
<div
className={twMerge(
'mb-1 flex items-center justify-start gap-2',
'mb-2 flex items-center justify-start gap-x-2',
!isUser && 'mt-2'
)}
>
{!isUser && !isSystem && <LogoMark width={20} />}
<div className="text-sm font-extrabold capitalize">{props.role}</div>
<p className="text-xs font-medium">{displayDate(props.createdAt)}</p>

{experimentalFeatureEnabed && (
<div className="hidden cursor-pointer group-hover:flex">
<div
className={twMerge(
'absolute right-0 cursor-pointer transition-all',
messages[0].id === props.id
? 'absolute -bottom-10 left-4'
: 'hidden group-hover:flex'
)}
>
<MessageToolbar message={props} />
</div>
)}
Expand All @@ -113,7 124,7 @@ const SimpleTextMessage: React.FC<ThreadMessage> = (props) => {
</div>
{experimentalFeatureEnabed &&
(props.status === MessageStatus.Pending || tokenSpeed > 0) && (
<p className="mt-1 text-xs font-medium text-white">
<p className="mt-2 text-xs font-medium text-foreground">
Token Speed: {Number(tokenSpeed).toFixed(2)}/s
</p>
)}
Expand Down
Loading