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

Add: option to view plaintext chat message. Issue/2508 #3496

Merged
merged 10 commits into from
Jun 21, 2023
1 change: 1 addition & 0 deletions website/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"no": "No",
"output": "Output",
"parameters": "Parameters",
"plain_text": "Plain Text",
"privacy_policy": "Privacy Policy",
"prompt": "Prompt",
"report_a_bug": "Report a Bug",
Expand Down
16 changes: 14 additions & 2 deletions website/src/components/Chat/ChatMessageEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import { Check, Copy, Edit, RotateCcw, ThumbsUp, X, XCircle } from "lucide-react
import { ThumbsDown } from "lucide-react";
import { useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";
import { forwardRef, KeyboardEvent, memo, ReactNode, useCallback, useMemo, useRef } from "react";
import { forwardRef, KeyboardEvent, memo, ReactNode, useCallback, useMemo, useRef, useState } from "react";
import { InferenceMessage } from "src/types/Chat";

import { MarkdownIcon } from "../icons/Markdown";
import { MarkdownOffIcon } from "../icons/MarkdownOff";
import { BaseMessageEntry } from "../Messages/BaseMessageEntry";
import { BaseMessageEmojiButton } from "../Messages/MessageEmojiButton";
import { MessageInlineEmojiRow } from "../Messages/MessageInlineEmojiRow";
Expand Down Expand Up @@ -92,6 +94,7 @@ export const ChatMessageEntry = memo(function ChatMessageEntry({

const isAssistant = message.role === "assistant";
const [isEditing, setIsEditing] = useBoolean(false);
const [isPlainText, setIsPlainText] = useState<boolean>(false);
const inputRef = useRef<HTMLTextAreaElement>(null);

const handleEditSubmit = useCallback(() => {
Expand Down Expand Up @@ -130,6 +133,7 @@ export const ChatMessageEntry = memo(function ChatMessageEntry({
isAssistant={isAssistant}
usedPlugin={used_plugin}
content={isEditing ? "" : content!}
isPlainText={isPlainText}
>
{!isAssistant && parentId !== null && (
<Box position="absolute" top={{ base: "4", md: 0 }} style={{ insetInlineEnd: `0.5rem` }}>
Expand Down Expand Up @@ -175,6 +179,12 @@ export const ChatMessageEntry = memo(function ChatMessageEntry({
)}
{state === "complete" && (
<>
<BaseMessageEmojiButton
Copy link
Collaborator

@notmd notmd Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you adjust icon base on the isPlainText state? I think using these two icons will make more sense
https://tabler-icons.io/i/markdown-off
https://tabler-icons.io/i/markdown
Just copy the SVG, we don't need to install the lib

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted. have made the relevant changes.

emoji={isPlainText ? MarkdownIcon : MarkdownOffIcon}
onClick={() => setIsPlainText(!isPlainText)}
label={t("plain_text")}
/>

{canRetry && <BaseMessageEmojiButton emoji={RotateCcw} onClick={handleRetry} label={t("retry")} />}
{!hasCopied ? (
<BaseMessageEmojiButton emoji={Copy} onClick={onCopy} label={t("copy")} />
Expand Down Expand Up @@ -213,6 +223,7 @@ type PendingMessageEntryProps = {
id?: string;
"data-id"?: string;
usedPlugin?: object;
isPlainText?: boolean;
};

export const messageEntryContainerProps = {
Expand All @@ -221,7 +232,7 @@ export const messageEntryContainerProps = {
};

export const PendingMessageEntry = forwardRef<HTMLDivElement, PendingMessageEntryProps>(function PendingMessageEntry(
{ content, isAssistant, children, usedPlugin, ...props },
{ content, isAssistant, children, usedPlugin, isPlainText, ...props },
ref
) {
const bgUser = "transparent";
Expand All @@ -245,6 +256,7 @@ export const PendingMessageEntry = forwardRef<HTMLDivElement, PendingMessageEntr
isAssistant={isAssistant}
maxWidth={messageEntryContainerProps.maxWidth}
containerProps={messageEntryContainerProps}
isPlainText={isPlainText}
{...props}
>
{children}
Expand Down
15 changes: 10 additions & 5 deletions website/src/components/Messages/BaseMessageEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ export type BaseMessageEntryProps = StrictOmit<BoxProps, "bg" | "backgroundColor
usedPlugin?: object;
isAssistant?: boolean;
containerProps?: BoxProps;
isPlainText?: boolean;
};

export const BaseMessageEntry = forwardRef<HTMLDivElement, BaseMessageEntryProps>(function BaseMessageEntry(
{ content, avatarProps, children, highlight, usedPlugin, isAssistant, containerProps, ...props },
{ content, avatarProps, children, highlight, usedPlugin, isAssistant, containerProps, isPlainText, ...props },
ref
) {
const bg = useColorModeValue("#DFE8F1", "#42536B");
Expand Down Expand Up @@ -63,10 +64,14 @@ export const BaseMessageEntry = forwardRef<HTMLDivElement, BaseMessageEntryProps
{...props}
_dark={{ outlineColor: { md: colors.dark.active }, ...props._dark }}
>
<Suspense fallback={content}>
{isAssistant ? <PluginUsageDetails usedPlugin={usedPlugin} /> : null}
<RenderedMarkdown markdown={content} disallowedElements={[]}></RenderedMarkdown>
</Suspense>
{!isPlainText ? (
<Suspense fallback={content}>
{isAssistant ? <PluginUsageDetails usedPlugin={usedPlugin} /> : null}
<RenderedMarkdown markdown={content} disallowedElements={[]}></RenderedMarkdown>
</Suspense>
) : (
content
)}
{children}
</Box>
</Flex>
Expand Down
24 changes: 24 additions & 0 deletions website/src/components/icons/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { LucideProps } from "lucide-react";

export const MarkdownIcon = (props: LucideProps) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
className="icon icon-tabler icon-tabler-markdown"
width={24}
height={24}
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"></path>
<path d="M7 15v-6l2 2l2 -2v6"></path>
<path d="M14 13l2 2l2 -2m-2 2v-6"></path>
</svg>
);
};
26 changes: 26 additions & 0 deletions website/src/components/icons/MarkdownOff.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { LucideProps } from "lucide-react";

export const MarkdownOffIcon = (props: LucideProps) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
className="icon icon-tabler icon-tabler-markdown-off"
width={24}
height={24}
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 5h10a2 2 0 0 1 2 2v10"></path>
<path d="M19 19h-14a2 2 0 0 1 -2 -2v-10a2 2 0 0 1 1.85 -2"></path>
<path d="M7 15v-6l2 2l1 -1m1 1v4"></path>
<path d="M17.5 13.5l.5 -.5m-2 -1v-3"></path>
<path d="M3 3l18 18"></path>
</svg>
);
};