diff --git a/web/src/components/MemoContent/CodeBlock.tsx b/web/src/components/MemoContent/CodeBlock.tsx new file mode 100644 index 000000000..e22272590 --- /dev/null +++ b/web/src/components/MemoContent/CodeBlock.tsx @@ -0,0 +1,55 @@ +import { CheckIcon, CopyIcon } from "lucide-react"; +import { useState } from "react"; +import { cn } from "@/lib/utils"; + +interface PreProps { + children?: React.ReactNode; + className?: string; +} + +export const CodeBlock = ({ children, className, ...props }: PreProps) => { + const [copied, setCopied] = useState(false); + + // Extract the code element and its props + const codeElement = children as React.ReactElement; + const codeClassName = codeElement?.props?.className || ""; + const codeContent = String(codeElement?.props?.children || "").replace(/\n$/, ""); + + // Extract language from className (format: language-xxx) + const match = /language-(\w+)/.exec(codeClassName); + const language = match ? match[1] : ""; + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(codeContent); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error("Failed to copy code:", err); + } + }; + + return ( +
++ ); +}; diff --git a/web/src/components/MemoContent/index.tsx b/web/src/components/MemoContent/index.tsx index 508cb44a5..e1b510ce2 100644 --- a/web/src/components/MemoContent/index.tsx +++ b/web/src/components/MemoContent/index.tsx @@ -10,6 +10,7 @@ import { useTranslate } from "@/utils/i18n"; import { remarkPreserveType } from "@/utils/remark-plugins/remark-preserve-type"; import { remarkTag } from "@/utils/remark-plugins/remark-tag"; import { isSuperUser } from "@/utils/user"; +import { CodeBlock } from "./CodeBlock"; import { createConditionalComponent, isTagNode, isTaskListItemNode } from "./ConditionalComponent"; import { MemoContentContext } from "./MemoContentContext"; import { Tag } from "./Tag"; @@ -102,6 +103,12 @@ const MemoContent = observer((props: Props) => { // Conditionally render custom components based on AST node type input: createConditionalComponent(TaskListItem, "input", isTaskListItemNode), span: createConditionalComponent(Tag, "span", isTagNode), + pre: CodeBlock, + a: ({ href, children, ...props }) => ( + + {children} + + ), }} > {content} diff --git a/web/src/components/MemoView.tsx b/web/src/components/MemoView.tsx index 79e5fc6fe..165863e66 100644 --- a/web/src/components/MemoView.tsx +++ b/web/src/components/MemoView.tsx @@ -84,6 +84,13 @@ const MemoView: React.FC+ {language} + +++ {children} ++