diff options
| author | Mahesh Sanikommu <[email protected]> | 2025-10-18 17:36:30 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-10-18 17:36:30 -0700 |
| commit | 5a001c639aebfeb3b7109d1dcfe03317f5f24d39 (patch) | |
| tree | 32d1a258de4d601b2625507410cab18528e92f38 /apps | |
| parent | Merge pull request #491 from vorahardik7/fix/graph-view (diff) | |
| parent | removed unneccasry comments (diff) | |
| download | supermemory-5a001c639aebfeb3b7109d1dcfe03317f5f24d39.tar.xz supermemory-5a001c639aebfeb3b7109d1dcfe03317f5f24d39.zip | |
Merge pull request #472 from Mikethebot44/feature/show-memory-content-markdown
Add markdown rendering support to memory content display
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/web/components/memories-utils/html-content-renderer.tsx | 161 |
1 files changed, 152 insertions, 9 deletions
diff --git a/apps/web/components/memories-utils/html-content-renderer.tsx b/apps/web/components/memories-utils/html-content-renderer.tsx index 6231f5eb..3caf3448 100644 --- a/apps/web/components/memories-utils/html-content-renderer.tsx +++ b/apps/web/components/memories-utils/html-content-renderer.tsx @@ -1,5 +1,7 @@ import { memo, useMemo } from "react" import DOMPurify from "dompurify" +import ReactMarkdown from "react-markdown" +import type { Components } from "react-markdown" interface HTMLContentRendererProps { content: string @@ -23,19 +25,64 @@ const isHTMLContent = (content: string): boolean => { export const HTMLContentRenderer = memo( ({ content, className = "" }: HTMLContentRendererProps) => { - const { isHTML, processedContent } = useMemo(() => { + const { isHTML, isMarkdown, processedContent } = useMemo(() => { const contentIsHTML = isHTMLContent(content) if (contentIsHTML) { return { isHTML: true, + isMarkdown: false, processedContent: DOMPurify.sanitize(content), } } + let processed = content + + if (content.includes("\n$ ")) { + processed = content.replace(/^\$ (.*$)/gm, "```bash\n$ $1\n```") + } + + if ( + content.trim().startsWith("{") && + content.includes('"') && + content.includes(":") + ) { + const lines = content.split("\n") + let inJsonBlock = false + const jsonLines: string[] = [] + const otherLines: string[] = [] + + for (const line of lines) { + if (line.trim() === "{" || line.trim() === "[") { + inJsonBlock = true + } + + if (inJsonBlock) { + jsonLines.push(line) + if (line.trim() === "}" || line.trim() === "]") { + inJsonBlock = false + } + } else { + otherLines.push(line) + } + } + + if (jsonLines.length > 0 && jsonLines.join("\n").trim()) { + const jsonBlock = jsonLines.join("\n") + const otherContent = otherLines.join("\n") + processed = + otherContent + + (otherContent ? "\n\n" : "") + + "```json\n" + + jsonBlock + + "\n```" + } + } + return { isHTML: false, - processedContent: content, + isMarkdown: true, + processedContent: processed, } }, [content]) @@ -49,13 +96,109 @@ export const HTMLContentRenderer = memo( ) } - return ( - <p - className={`text-sm leading-relaxed whitespace-pre-wrap text-foreground ${className}`} - > - {processedContent} - </p> - ) + if (isMarkdown) { + try { + const components: Components = { + h1: ({ children }) => ( + <h1 className="text-foreground text-lg font-semibold mb-1.5"> + {children} + </h1> + ), + h2: ({ children }) => ( + <h2 className="text-foreground text-base font-semibold mb-1.5"> + {children} + </h2> + ), + h3: ({ children }) => ( + <h3 className="text-foreground text-sm font-semibold mb-1"> + {children} + </h3> + ), + h4: ({ children }) => ( + <h4 className="text-foreground text-sm font-medium mb-1"> + {children} + </h4> + ), + h5: ({ children }) => ( + <h5 className="text-foreground text-sm font-medium mb-1"> + {children} + </h5> + ), + h6: ({ children }) => ( + <h6 className="text-foreground text-sm font-medium mb-1"> + {children} + </h6> + ), + p: ({ children }) => ( + <p className="text-foreground text-sm leading-relaxed mb-1.5"> + {children} + </p> + ), + strong: ({ children }) => ( + <strong className="text-foreground font-semibold"> + {children} + </strong> + ), + em: ({ children }) => ( + <em className="text-foreground italic">{children}</em> + ), + code: ({ children, className }) => ( + <code + className={`text-foreground bg-muted px-1.5 py-0.5 rounded text-xs font-mono ${className || ""}`} + > + {children} + </code> + ), + pre: ({ children }) => ( + <pre className="text-foreground bg-muted border border-border p-2 rounded text-xs overflow-x-auto mb-2 whitespace-pre font-mono leading-tight"> + {children} + </pre> + ), + blockquote: ({ children }) => ( + <blockquote className="text-muted-foreground border-l-4 border-muted-foreground pl-3 italic mb-2"> + {children} + </blockquote> + ), + a: ({ children, href }) => ( + <a + href={href} + className="text-primary hover:text-primary/80 underline" + target="_blank" + rel="noopener noreferrer" + > + {children} + </a> + ), + ul: ({ children }) => ( + <ul className="text-foreground text-sm mb-2 ml-4 list-disc"> + {children} + </ul> + ), + ol: ({ children }) => ( + <ol className="text-foreground text-sm mb-2 ml-4 list-decimal"> + {children} + </ol> + ), + li: ({ children }) => <li className="mb-1">{children}</li>, + } + + return ( + <div className={`${className} bg-background`}> + <ReactMarkdown components={components}> + {processedContent} + </ReactMarkdown> + </div> + ) + } catch { + return ( + <p + className={`text-sm leading-relaxed whitespace-pre-wrap text-foreground ${className}`} + > + {processedContent} + </p> + ) + } + } }, ) |