diff --git a/surfsense_web/components/chat/ChatMessages.tsx b/surfsense_web/components/chat/ChatMessages.tsx index 473564ddf..20aa07815 100644 --- a/surfsense_web/components/chat/ChatMessages.tsx +++ b/surfsense_web/components/chat/ChatMessages.tsx @@ -12,6 +12,9 @@ import ChatSourcesDisplay from "@/components/chat/ChatSources"; import { CitationDisplay } from "@/components/chat/ChatCitation"; import { ChatFurtherQuestions } from "@/components/chat/ChatFurtherQuestions"; import { AnimatedEmptyState } from "@/components/chat/AnimatedEmptyState"; +import { languageRenderers } from "@/components/chat/CodeBlock"; + + export function ChatMessagesUI() { const { messages } = useChatUI(); @@ -63,6 +66,7 @@ function ChatMessageUI({
@@ -73,7 +77,9 @@ function ChatMessageUI({
) : ( - + )} diff --git a/surfsense_web/components/chat/CodeBlock.tsx b/surfsense_web/components/chat/CodeBlock.tsx new file mode 100644 index 000000000..f5474007e --- /dev/null +++ b/surfsense_web/components/chat/CodeBlock.tsx @@ -0,0 +1,153 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { + oneLight, + oneDark, +} from "react-syntax-highlighter/dist/cjs/styles/prism"; +import { Check, Copy } from "lucide-react"; +import { useTheme } from "next-themes"; + +// Code block component with syntax highlighting and copy functionality +export const CodeBlock = ({ + children, + language, +}: { + children: string; + language: string; +}) => { + const [copied, setCopied] = useState(false); + const { resolvedTheme, theme } = useTheme(); + const [mounted, setMounted] = useState(false); + + // Prevent hydration issues + useEffect(() => { + setMounted(true); + }, []); + + const handleCopy = async () => { + await navigator.clipboard.writeText(children); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + // Choose theme based on current system/user preference + const isDarkTheme = mounted && (resolvedTheme === "dark" || theme === "dark"); + const syntaxTheme = isDarkTheme ? oneDark : oneLight; + + return ( +
+
+ +
+ {mounted ? ( + + {children} + + ) : ( +
+
+            
+              {children}
+            
+          
+
+ )} +
+ ); +}; + +// Create language renderer function +const createLanguageRenderer = (lang: string) => + ({ code }: { code: string }) => ( + {code} + ); + +// Define language renderers for common programming languages +export const languageRenderers = { + "javascript": createLanguageRenderer("javascript"), + "typescript": createLanguageRenderer("typescript"), + "python": createLanguageRenderer("python"), + "java": createLanguageRenderer("java"), + "csharp": createLanguageRenderer("csharp"), + "cpp": createLanguageRenderer("cpp"), + "c": createLanguageRenderer("c"), + "php": createLanguageRenderer("php"), + "ruby": createLanguageRenderer("ruby"), + "go": createLanguageRenderer("go"), + "rust": createLanguageRenderer("rust"), + "swift": createLanguageRenderer("swift"), + "kotlin": createLanguageRenderer("kotlin"), + "scala": createLanguageRenderer("scala"), + "sql": createLanguageRenderer("sql"), + "json": createLanguageRenderer("json"), + "xml": createLanguageRenderer("xml"), + "yaml": createLanguageRenderer("yaml"), + "bash": createLanguageRenderer("bash"), + "shell": createLanguageRenderer("shell"), + "powershell": createLanguageRenderer("powershell"), + "dockerfile": createLanguageRenderer("dockerfile"), + "html": createLanguageRenderer("html"), + "css": createLanguageRenderer("css"), + "scss": createLanguageRenderer("scss"), + "less": createLanguageRenderer("less"), + "markdown": createLanguageRenderer("markdown"), + "text": createLanguageRenderer("text"), +}; \ No newline at end of file diff --git a/surfsense_web/components/chat/index.ts b/surfsense_web/components/chat/index.ts index 55ab716c0..812bf805b 100644 --- a/surfsense_web/components/chat/index.ts +++ b/surfsense_web/components/chat/index.ts @@ -4,4 +4,5 @@ export * from './ConnectorComponents'; export * from './Citation'; export * from './SourceUtils'; export * from './ScrollUtils'; +export * from './CodeBlock'; export * from './types'; \ No newline at end of file