From be1bb388924f4422058099dcb0debdd1c857d36a Mon Sep 17 00:00:00 2001
From: kamtschatka <sschatka@gmail.com>
Date: Sun, 9 Jun 2024 15:30:56 +0200
Subject: feature(web): Add syntax highlighting to code blocks and a quick copy
 button. Fixes #195 (#197)

* Any plans to support copy to clipboard (markdown code) for notes? #195
added a button to copy the markdown and added code highlighting

* Any plans to support copy to clipboard (markdown code) for notes? #195
Changed the copy-button to a generic one
added a safeguard and a message to the copy button if copying is not possible

* Some code cleanups

---------

Co-authored-by: kamtschatka <simon.schatka@gmx.at>
Co-authored-by: MohamedBassem <me@mbassem.com>
---
 apps/web/components/ui/markdown-component.tsx | 58 +++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 apps/web/components/ui/markdown-component.tsx

(limited to 'apps/web/components/ui/markdown-component.tsx')

diff --git a/apps/web/components/ui/markdown-component.tsx b/apps/web/components/ui/markdown-component.tsx
new file mode 100644
index 00000000..f92cb3a3
--- /dev/null
+++ b/apps/web/components/ui/markdown-component.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+import CopyBtn from "@/components/ui/copy-button";
+import { cn } from "@/lib/utils";
+import Markdown from "react-markdown";
+import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
+import { dracula } from "react-syntax-highlighter/dist/cjs/styles/prism";
+
+function PreWithCopyBtn({ className, ...props }: React.ComponentProps<"pre">) {
+  const ref = React.useRef<HTMLPreElement>(null);
+  return (
+    <span className="group relative">
+      <CopyBtn
+        className="absolute right-1 top-1 m-1 hidden text-white group-hover:block"
+        getStringToCopy={() => {
+          return ref.current?.textContent ?? "";
+        }}
+      />
+      <pre ref={ref} className={cn(className, "")} {...props} />
+    </span>
+  );
+}
+
+export function MarkdownComponent({
+  children: markdown,
+}: {
+  children: string;
+}) {
+  return (
+    <Markdown
+      className="prose dark:prose-invert"
+      components={{
+        pre({ ...props }) {
+          return <PreWithCopyBtn {...props} />;
+        },
+        code({ className, children, ...props }) {
+          const match = /language-(\w+)/.exec(className ?? "");
+          return match ? (
+            // @ts-expect-error -- Refs are not compatible for some reason
+            <SyntaxHighlighter
+              PreTag="div"
+              language={match[1]}
+              {...props}
+              style={dracula}
+            >
+              {String(children).replace(/\n$/, "")}
+            </SyntaxHighlighter>
+          ) : (
+            <code className={className} {...props}>
+              {children}
+            </code>
+          );
+        },
+      }}
+    >
+      {markdown}
+    </Markdown>
+  );
+}
-- 
cgit v1.2.3-70-g09d2

