diff --git a/src/components/chat-view/Markdown/MarkdownWithIcon.tsx b/src/components/chat-view/Markdown/MarkdownWithIcon.tsx index 78c7084..9419aa6 100644 --- a/src/components/chat-view/Markdown/MarkdownWithIcon.tsx +++ b/src/components/chat-view/Markdown/MarkdownWithIcon.tsx @@ -1,13 +1,13 @@ import * as Tooltip from '@radix-ui/react-tooltip'; import { Check, CircleCheckBig, CircleHelp, CopyIcon, FilePlus2 } from 'lucide-react'; import { ReactNode, useState } from 'react'; -import ReactMarkdown from 'react-markdown'; -import rehypeRaw from 'rehype-raw'; import { useApp } from 'src/contexts/AppContext'; import { t } from '../../../lang/helpers' -function CopyButton({ message }: { message: string }) { +import RawMarkdownBlock from './RawMarkdownBlock' + +export function CopyButton({ message }: { message: string }) { const [copied, setCopied] = useState(false) const handleCopy = async () => { @@ -43,7 +43,7 @@ function CopyButton({ message }: { message: string }) { ) } -function CreateNewFileButton({ message }: { message: string }) { +export function CreateNewFileButton({ message }: { message: string }) { const app = useApp() const [created, setCreated] = useState(false) @@ -138,12 +138,10 @@ const MarkdownWithIcons = ({ <>
{iconName && renderIcon()} {renderTitle()} - - {markdownContent} - + />
{markdownContent && finish && iconName === "attempt_completion" &&
diff --git a/src/components/chat-view/Markdown/MermaidBlock.tsx b/src/components/chat-view/Markdown/MermaidBlock.tsx index 1936747..36259df 100644 --- a/src/components/chat-view/Markdown/MermaidBlock.tsx +++ b/src/components/chat-view/Markdown/MermaidBlock.tsx @@ -1,3 +1,4 @@ +import { CopyIcon } from "lucide-react" import mermaid from "mermaid" import { memo, useEffect, useRef, useState } from "react" import styled from "styled-components" @@ -76,6 +77,42 @@ interface MermaidBlockProps { code: string } +interface MermaidToolbarProps { + code: string; +} + +function MermaidToolbar({ code }: MermaidToolbarProps) { + const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard() + + const handleCopy = (e: React.MouseEvent) => { + e.stopPropagation() + // We wrap the code in a markdown block for easy pasting + copyWithFeedback("```mermaid\n" + code + "\n```") + } + + return ( + + + + + + ) +} + +interface MermaidButtonProps { + code: string + children: React.ReactNode +} + +function MermaidButton({ code, children }: MermaidButtonProps) { + return ( + + {children} + + + ) +} + function MermaidBlock({ code }: MermaidBlockProps) { const containerRef = useRef(null) const [isLoading, setIsLoading] = useState(false) @@ -259,15 +296,78 @@ function MermaidBlock({ code }: MermaidBlockProps) { )} ) : ( - + + + )} ) } -const MermaidBlockContainer = styled.div` +const MermaidWrapper = styled.div` position: relative; margin: 8px 0; + + &:hover .mermaid-toolbar { + opacity: 1; + } + + .mermaid-toolbar-btn { + display: flex; + align-items: center; + justify-content: center; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; + color: var(--text-muted); + padding: 0 !important; + margin: 0 !important; + width: 24px !important; + height: 24px !important; + + &:hover { + background-color: var(--background-modifier-hover) !important; + } + } +` + +const ToolbarContainer = styled.div` + position: absolute; + top: 8px; + right: 8px; + z-index: 10; + opacity: 0; + transition: opacity 0.2s ease-in-out; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-border); + border-radius: 6px; + padding: 2px; + display: flex; + align-items: center; + + &:hover { + opacity: 1; /* Keep it visible when hovering over the toolbar itself */ + } +` + +const ToolbarButton = styled.button` + padding: 4px; + color: var(--text-muted); + background: transparent; + border: none; + cursor: pointer; + display: flex; + align-items: center; + border-radius: 4px; + + &:hover { + color: var(--text-normal); + background-color: var(--background-modifier-hover); + } +` + +const MermaidBlockContainer = styled.div` + position: relative; ` const LoadingMessage = styled.div` diff --git a/src/components/chat-view/ReactMarkdown.tsx b/src/components/chat-view/ReactMarkdown.tsx index 0b85207..ac47cfd 100644 --- a/src/components/chat-view/ReactMarkdown.tsx +++ b/src/components/chat-view/ReactMarkdown.tsx @@ -31,8 +31,8 @@ function ReactMarkdown({ applyStatus: ApplyStatus onApply: (toolArgs: ToolArgs) => void children: string - }) { - +}) { + const blocks: ParsedMsgBlock[] = useMemo( () => parseMsgBlocks(children), [children],