diff --git a/surfsense_web/components/copy-button.tsx b/surfsense_web/components/copy-button.tsx new file mode 100644 index 0000000..fd42ca6 --- /dev/null +++ b/surfsense_web/components/copy-button.tsx @@ -0,0 +1,33 @@ +"use client"; +import { useState } from "react"; +import type { RefObject } from "react"; +import { Button } from "./ui/button"; +import { Copy, CopyCheck } from "lucide-react"; + +export default function CopyButton({ + ref, +}: { + ref: RefObject; +}) { + const [copy, setCopy] = useState(false); + + const handleClick = () => { + if (ref.current) { + const text = ref.current.innerText; + navigator.clipboard.writeText(text); + + setCopy(true); + setTimeout(() => { + setCopy(false); + }, 500); + } + }; + + return ( +
+ +
+ ); +} diff --git a/surfsense_web/components/markdown-viewer.tsx b/surfsense_web/components/markdown-viewer.tsx index f4bebf9..2e75e77 100644 --- a/surfsense_web/components/markdown-viewer.tsx +++ b/surfsense_web/components/markdown-viewer.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState, useEffect } from "react"; +import React, { useMemo, useState, useEffect, useRef } from "react"; import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import rehypeSanitize from "rehype-sanitize"; @@ -7,267 +7,350 @@ import { cn } from "@/lib/utils"; import { Citation } from "./chat/Citation"; import { Source } from "./chat/types"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { oneLight, oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; +import { + oneLight, + oneDark, +} from "react-syntax-highlighter/dist/cjs/styles/prism"; import { Check, Copy } from "lucide-react"; import { useTheme } from "next-themes"; +import CopyButton from "./copy-button"; interface MarkdownViewerProps { - content: string; - className?: string; - getCitationSource?: (id: number) => Source | null; + content: string; + className?: string; + getCitationSource?: (id: number) => Source | null; + type?: "user" | "ai"; } -export function MarkdownViewer({ content, className, getCitationSource }: MarkdownViewerProps) { - // Memoize the markdown components to prevent unnecessary re-renders - const components = useMemo(() => { - return { - // Define custom components for markdown elements - p: ({node, children, ...props}: any) => { - // If there's no getCitationSource function, just render normally - if (!getCitationSource) { - return

{children}

; - } - - // Process citations within paragraph content - return

{processCitationsInReactChildren(children, getCitationSource)}

; - }, - a: ({node, children, ...props}: any) => { - // Process citations within link content if needed - const processedChildren = getCitationSource - ? processCitationsInReactChildren(children, getCitationSource) - : children; - return {processedChildren}; - }, - li: ({node, children, ...props}: any) => { - // Process citations within list item content - const processedChildren = getCitationSource - ? processCitationsInReactChildren(children, getCitationSource) - : children; - return
  • {processedChildren}
  • ; - }, - ul: ({node, ...props}: any) =>