diff options
| author | kamtschatka <sschatka@gmail.com> | 2024-06-09 15:30:56 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-09 14:30:56 +0100 |
| commit | be1bb388924f4422058099dcb0debdd1c857d36a (patch) | |
| tree | bd518e1df0a137f88950b4f7f083da90e8e29cbb /apps/web/components/ui | |
| parent | f7a77533240ec435c8a7b103b6f6be409bf995d8 (diff) | |
| download | karakeep-be1bb388924f4422058099dcb0debdd1c857d36a.tar.zst | |
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>
Diffstat (limited to 'apps/web/components/ui')
| -rw-r--r-- | apps/web/components/ui/copy-button.tsx | 37 | ||||
| -rw-r--r-- | apps/web/components/ui/markdown-component.tsx | 58 |
2 files changed, 95 insertions, 0 deletions
diff --git a/apps/web/components/ui/copy-button.tsx b/apps/web/components/ui/copy-button.tsx new file mode 100644 index 00000000..a51ce902 --- /dev/null +++ b/apps/web/components/ui/copy-button.tsx @@ -0,0 +1,37 @@ +import React, { useEffect } from "react";
+import { Check, Copy } from "lucide-react";
+
+export default function CopyBtn({
+ className,
+ getStringToCopy,
+}: {
+ className?: string;
+ getStringToCopy: () => string;
+}) {
+ const [copyOk, setCopyOk] = React.useState(false);
+ const [disabled, setDisabled] = React.useState(false);
+ useEffect(() => {
+ if (!navigator || !navigator.clipboard) {
+ setDisabled(true);
+ }
+ });
+
+ const handleClick = async () => {
+ await navigator.clipboard.writeText(getStringToCopy());
+ setCopyOk(true);
+ setTimeout(() => {
+ setCopyOk(false);
+ }, 2000);
+ };
+
+ return (
+ <button
+ className={className}
+ onClick={handleClick}
+ disabled={disabled}
+ title={disabled ? "Copying is only available over https" : undefined}
+ >
+ {copyOk ? <Check /> : <Copy />}
+ </button>
+ );
+}
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>
+ );
+}
|
