reform the message box with bubble style

This commit is contained in:
ecwu
2024-07-16 23:01:18 +08:00
parent 0ae53ff954
commit 4bf3e02962
6 changed files with 81 additions and 57 deletions

View File

@@ -722,7 +722,7 @@ export default function ChatBOX(props: {
/> />
))} ))}
{showGenerating && ( {showGenerating && (
<p className="p-2 my-2 animate-pulse dark:text-white message-content"> <p className="p-2 my-2 animate-pulse message-content">
{generatingMessage || Tr("Generating...")} {generatingMessage || Tr("Generating...")}
... ...
</p> </p>

View File

@@ -6,7 +6,7 @@ const logprobToColor = (logprob: number) => {
// 绿色的RGB值为(0, 255, 0)红色的RGB值为(255, 0, 0) // 绿色的RGB值为(0, 255, 0)红色的RGB值为(255, 0, 0)
const red = Math.round(255 * (1 - percent / 100)); const red = Math.round(255 * (1 - percent / 100));
const green = Math.round(255 * (percent / 100)); const green = Math.round(255 * (percent / 100));
const color = `rgb(${red}, ${green}, 0)`; const color = `rgba(${red}, ${green}, 0, 0.5)`;
return color; return color;
}; };

View File

@@ -51,17 +51,26 @@ export default function Message(props: Props) {
setChatStore({ ...chatStore }); setChatStore({ ...chatStore });
}} }}
> >
🗑 Delete
</button> </button>
); );
const CopiedHint = () => ( const CopiedHint = () => (
<span <div role="alert" class="alert">
className={ <svg
"bg-purple-400 p-1 rounded shadow-md absolute z-20 left-1/2 top-3/4 transform -translate-x-1/2 -translate-y-1/2" xmlns="http://www.w3.org/2000/svg"
} fill="none"
> viewBox="0 0 24 24"
{Tr("Message copied to clipboard!")} class="stroke-info h-6 w-6 shrink-0"
</span> >
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>{Tr("Message copied to clipboard!")}</span>
</div>
); );
const copyToClipboard = (text: string) => { const copyToClipboard = (text: string) => {
@@ -78,7 +87,7 @@ export default function Message(props: Props) {
copyToClipboard(textToCopy); copyToClipboard(textToCopy);
}} }}
> >
📋 Copy
</button> </button>
</> </>
); );
@@ -105,51 +114,62 @@ export default function Message(props: Props) {
> >
<div> <div>
<div <div
className={`w-fit p-2 rounded my-2 ${ className={`chat min-w-16 w-fit p-2 my-2 ${
chat.role === "assistant" chat.role === "assistant" ? "chat-start" : "chat-end"
? "bg-white dark:bg-gray-700 dark:text-white"
: "bg-green-400"
} ${chat.hide ? "opacity-50" : ""}`} } ${chat.hide ? "opacity-50" : ""}`}
> >
{chat.hide ? ( <div
<MessageHide chat={chat} /> className={`chat-bubble ${
) : typeof chat.content !== "string" ? ( chat.role === "assistant"
<MessageDetail chat={chat} renderMarkdown={renderMarkdown} /> ? "chat-bubble-secondary"
) : chat.tool_calls ? ( : "chat-bubble-primary"
<MessageToolCall chat={chat} copyToClipboard={copyToClipboard} /> }`}
) : chat.role === "tool" ? ( >
<MessageToolResp chat={chat} copyToClipboard={copyToClipboard} /> {chat.hide ? (
) : renderMarkdown ? ( <MessageHide chat={chat} />
// @ts-ignore ) : typeof chat.content !== "string" ? (
<Markdown markdown={getMessageText(chat)} /> <MessageDetail chat={chat} renderMarkdown={renderMarkdown} />
) : ( ) : chat.tool_calls ? (
<div className="message-content"> <MessageToolCall
{ chat={chat}
// only show when content is string or list of message copyToClipboard={copyToClipboard}
// this check is used to avoid rendering tool call />
chat.content && ) : chat.role === "tool" ? (
(chat.logprobs && renderColor <MessageToolResp
? chat.logprobs.content chat={chat}
.filter((c) => c.token) copyToClipboard={copyToClipboard}
.map((c) => ( />
<div ) : renderMarkdown ? (
style={{ // @ts-ignore
color: logprobToColor(c.logprob), <Markdown markdown={getMessageText(chat)} />
display: "inline", ) : (
}} <div className="message-content">
> {
{c.token} // only show when content is string or list of message
</div> // this check is used to avoid rendering tool call
)) chat.content &&
: getMessageText(chat)) (chat.logprobs && renderColor
} ? chat.logprobs.content
</div> .filter((c) => c.token)
)} .map((c) => (
<hr className="mt-2" /> <div
<TTSPlay chat={chat} /> style={{
<div className="w-full flex justify-between"> backgroundColor: logprobToColor(c.logprob),
display: "inline",
}}
>
{c.token}
</div>
))
: getMessageText(chat))
}
</div>
)}
</div>
<div class="chat-footer opacity-50 flex gap-x-2">
<TTSPlay chat={chat} />
<DeleteIcon /> <DeleteIcon />
<button onClick={() => setShowEdit(true)}>🖋</button> <button onClick={() => setShowEdit(true)}>Edit</button>
{chatStore.tts_api && chatStore.tts_key && ( {chatStore.tts_api && chatStore.tts_key && (
<TTSButton <TTSButton
chatStore={chatStore} chatStore={chatStore}

View File

@@ -13,7 +13,7 @@ export function MessageDetail({ chat, renderMarkdown }: Props) {
{chat.content.map((mdt) => {chat.content.map((mdt) =>
mdt.type === "text" ? ( mdt.type === "text" ? (
chat.hide ? ( chat.hide ? (
mdt.text?.split("\n")[0].slice(0, 16) + "... (deleted)" mdt.text?.split("\n")[0].slice(0, 16) + " ..."
) : renderMarkdown ? ( ) : renderMarkdown ? (
// @ts-ignore // @ts-ignore
<Markdown markdown={mdt.text} /> <Markdown markdown={mdt.text} />

View File

@@ -6,7 +6,5 @@ interface Props {
} }
export function MessageHide({ chat }: Props) { export function MessageHide({ chat }: Props) {
return ( return <div>{getMessageText(chat).split("\n")[0].slice(0, 18)} ...</div>;
<div>{getMessageText(chat).split("\n")[0].slice(0, 18)} ... (deleted)</div>
);
} }

View File

@@ -491,6 +491,12 @@ export default (props: {
<select data-choose-theme class="select select-bordered"> <select data-choose-theme class="select select-bordered">
<option value="light">Light</option> <option value="light">Light</option>
<option value="dark">Dark</option> <option value="dark">Dark</option>
<option value="cupcake">Cupcake</option>
<option value="halloween">Halloween</option>
<option value="wireframe">Wireframe</option>
<option value="sunset">Sunset</option>
<option value="lemonade">Lemonade</option>
<option value="luxury">Luxury</option>
<option value="cyberpunk">Cyberpunk</option> <option value="cyberpunk">Cyberpunk</option>
<option value="black">Black</option> <option value="black">Black</option>
</select> </select>