Skip to content

Commit

Permalink
feat: Implement instant Markdown rendering for chats
Browse files Browse the repository at this point in the history
Now we have add a additional instant Markdown renderer along side of the Live(auto-typing) Markdown Style. Also now added support of Markdown Style for user inserted text.
  • Loading branch information
RajeshTechForge committed Sep 6, 2024
1 parent 8d9a4ee commit 9fcf187
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 34 deletions.
105 changes: 105 additions & 0 deletions frontend/components/LiveMarkdownStyle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useState, useEffect } from "react";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
import { nightOwl } from "react-syntax-highlighter/dist/esm/styles/prism";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { useStopTextGeneration } from "../context/StopTextGeneration";
import styles from "./styles/MarkdownStyle.module.css";

interface LiveMarkdownStyleProps {
text: string;
setButtonsDisabled?: React.Dispatch<React.SetStateAction<boolean>> | null;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const SyntaxHighlight = ({ children, code, ...props }: any) => {
const [isCopied, setIsCopied] = useState(false);

const handleCopy = () => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
};

return (
<>
<SyntaxHighlighter
style={nightOwl}
language="javascript"
showLineNumbers
customStyle={{
padding: "1rem",
borderRadius: "5px 5px 0 0",
fontSize: "1rem",
margin: "0",
}}
{...props}
>
{children}
</SyntaxHighlighter>
<div className={styles.copyBtnContainer}>
<CopyToClipboard text={code} onCopy={handleCopy}>
<button className={styles.copyButton}>
{isCopied ? "Copied!" : "Copy"}
</button>
</CopyToClipboard>
</div>
</>
);
};

// =============================================================== //

export const LiveMarkdownStyle: React.FC<LiveMarkdownStyleProps> = ({
text,
setButtonsDisabled,
}) => {
const { stop, setStop } = useStopTextGeneration();
const [currentText, setCurrentText] = useState("");
const [currentIndex, setCurrentIndex] = useState(0);
const delay = 10;

//
// Handle typing effect for the entire Markdown content
useEffect(() => {
if (currentIndex >= text.length - 5) {
setButtonsDisabled ? setButtonsDisabled(false) : null;
}
if (stop) {
setButtonsDisabled ? setButtonsDisabled(false) : null;
setStop(false);
} else if (currentIndex < text.length) {
const timeout = setTimeout(() => {
setCurrentText((prevText) => prevText + text[currentIndex]);
setCurrentIndex((prevIndex) => prevIndex + 1);
}, delay);
return () => clearTimeout(timeout);
}
}, [currentIndex, delay]);

return (
<div className={styles.markdownText}>
<Markdown
remarkPlugins={[remarkGfm]}
components={{
code({ className, children, ...props }) {
if (className?.includes("language-")) {
return (
<SyntaxHighlight code={children?.toString()}>
{children}
</SyntaxHighlight>
);
}
return (
<code {...props} className={styles.inlineCode}>
{children}
</code>
);
},
}}
>
{currentText}
</Markdown>
</div>
);
};
30 changes: 2 additions & 28 deletions frontend/components/MarkdownStyle.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
import { nightOwl } from "react-syntax-highlighter/dist/esm/styles/prism";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { useStopTextGeneration } from "../context/StopTextGeneration";
import styles from "./styles/MarkdownStyle.module.css";

interface MarkdownStyleProps {
text: string;
setButtonsDisabled?: React.Dispatch<React.SetStateAction<boolean>> | null;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -52,31 +50,7 @@ const SyntaxHighlight = ({ children, code, ...props }: any) => {

export const MarkdownStyle: React.FC<MarkdownStyleProps> = ({
text,
setButtonsDisabled,
}) => {
const { stop, setStop } = useStopTextGeneration();
const [currentText, setCurrentText] = useState("");
const [currentIndex, setCurrentIndex] = useState(0);
const delay = 10;

//
// Handle typing effect for the entire Markdown content
useEffect(() => {
if (currentIndex >= text.length - 5) {
setButtonsDisabled ? setButtonsDisabled(false) : null;
}
if (stop) {
setButtonsDisabled ? setButtonsDisabled(false) : null;
setStop(false);
} else if (currentIndex < text.length) {
const timeout = setTimeout(() => {
setCurrentText((prevText) => prevText + text[currentIndex]);
setCurrentIndex((prevIndex) => prevIndex + 1);
}, delay);
return () => clearTimeout(timeout);
}
}, [currentIndex, delay]);

return (
<div className={styles.markdownText}>
<Markdown
Expand All @@ -98,7 +72,7 @@ export const MarkdownStyle: React.FC<MarkdownStyleProps> = ({
},
}}
>
{currentText}
{text}
</Markdown>
</div>
);
Expand Down
17 changes: 13 additions & 4 deletions frontend/features/msgBubble/AiMsgBubble.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import { LiveMarkdownStyle } from "../../components/LiveMarkdownStyle";
import { MarkdownStyle } from "../../components/MarkdownStyle";
import styles from "./styles/MsgBubble.module.css";

Expand All @@ -10,11 +11,13 @@ import pinacLogo from "/icon/pinac-logo.png";
// ============================================================================ //

interface AiMsgBubbleProps {
live: boolean;
response: string;
setButtonsDisabled?: React.Dispatch<React.SetStateAction<boolean>>;
}

export const AiMsgBubble: React.FC<AiMsgBubbleProps> = ({
live,
response,
setButtonsDisabled,
}) => {
Expand Down Expand Up @@ -66,10 +69,16 @@ export const AiMsgBubble: React.FC<AiMsgBubbleProps> = ({
<div className={styles.msg_name}>PINAC</div>
</div>
<div className={`${styles.msg_text} ${styles.ai_msg}`}>
<MarkdownStyle
text={response}
setButtonsDisabled={setButtonsDisabled? setButtonsDisabled : null}
/>
{live ? (
<LiveMarkdownStyle
text={response}
setButtonsDisabled={
setButtonsDisabled ? setButtonsDisabled : null
}
/>
) : (
<MarkdownStyle text={response} />
)}
</div>
<div className={styles.ai_msg_copy_btn}>
<button
Expand Down
2 changes: 1 addition & 1 deletion frontend/features/msgBubble/SetChatBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const SetChatBubble: React.FC<SetChatBubbleProps> = ({ role, msg }) => {
{role === "user" ? (
<UserMsgBubble response={msg} />
) : (
<AiMsgBubble response={msg} />
<AiMsgBubble live={false} response={msg} />
)}
</>
);
Expand Down
3 changes: 2 additions & 1 deletion frontend/features/msgBubble/UserMsgBubble.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import { MarkdownStyle } from "../../components/MarkdownStyle";
import styles from "./styles/MsgBubble.module.css";

// Icons
Expand Down Expand Up @@ -36,7 +37,7 @@ export const UserMsgBubble: React.FC<UserMsgBubbleProps> = ({ response }) => {
<div className={styles.msg_content}>
<div className={styles.msg_name}>You</div>
<div className={`${styles.msg_text} ${styles.human_msg}`}>
{response}
<MarkdownStyle text={response} />
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const HomePage: React.FC = () => {
"ai",
MessageContent,
<AiMsgBubble
live={true}
response={MessageContent}
setButtonsDisabled={setButtonsDisabled}
key={aiMessageKey}
Expand Down

0 comments on commit 9fcf187

Please sign in to comment.