Skip to content

Commit 8a12006

Browse files
committed
Add copy to markdown button
1 parent 652d50d commit 8a12006

2 files changed

Lines changed: 84 additions & 3 deletions

File tree

web/app/docs/[slug]/page.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { notFound } from "next/navigation";
22
import Link from "next/link";
33
import { getDocPage, getAllDocPages } from "@/lib/content/loader";
44
import MarkdownContent from "@/components/MarkdownContent";
5+
import CopyToMarkdownButton from "@/components/CopyToMarkdownButton";
56
import GettingStartedGuides from "@/components/GettingStartedGuides";
67
import type { Metadata } from "next";
78

@@ -92,9 +93,15 @@ export default async function DocPage({ params }: DocPageProps) {
9293
<span className="text-black">{doc.metadata.title}</span>
9394
</nav>
9495

95-
<h1 className="text-4xl md:text-5xl font-bold mb-4">
96-
{doc.metadata.title}
97-
</h1>
96+
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-4">
97+
<h1 className="text-4xl md:text-5xl font-bold">
98+
{doc.metadata.title}
99+
</h1>
100+
<CopyToMarkdownButton
101+
content={doc.content}
102+
title={doc.metadata.title}
103+
/>
104+
</div>
98105

99106
{doc.metadata.description && (
100107
<p className="text-xl text-gray-600 mb-12">
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
5+
interface CopyToMarkdownButtonProps {
6+
content: string;
7+
title: string;
8+
}
9+
10+
export default function CopyToMarkdownButton({
11+
content,
12+
title,
13+
}: CopyToMarkdownButtonProps) {
14+
const [copied, setCopied] = useState(false);
15+
16+
const handleCopy = async () => {
17+
try {
18+
// Create a more comprehensive markdown output including title
19+
const fullContent = `# ${title}\n\n${content}`;
20+
await navigator.clipboard.writeText(fullContent);
21+
setCopied(true);
22+
setTimeout(() => setCopied(false), 2000);
23+
} catch (err) {
24+
console.error("Failed to copy text: ", err);
25+
}
26+
};
27+
28+
return (
29+
<button
30+
onClick={handleCopy}
31+
className="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-gray-600 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 hover:text-black transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-1"
32+
aria-label="Copy to Markdown"
33+
>
34+
{copied ? (
35+
<>
36+
<svg
37+
xmlns="http://www.w3.org/2000/svg"
38+
width="16"
39+
height="16"
40+
viewBox="0 0 24 24"
41+
fill="none"
42+
stroke="currentColor"
43+
strokeWidth="2"
44+
strokeLinecap="round"
45+
strokeLinejoin="round"
46+
className="w-4 h-4 text-green-600"
47+
>
48+
<polyline points="20 6 9 17 4 12" />
49+
</svg>
50+
<span className="text-green-600">Copied!</span>
51+
</>
52+
) : (
53+
<>
54+
<svg
55+
xmlns="http://www.w3.org/2000/svg"
56+
width="16"
57+
height="16"
58+
viewBox="0 0 24 24"
59+
fill="none"
60+
stroke="currentColor"
61+
strokeWidth="2"
62+
strokeLinecap="round"
63+
strokeLinejoin="round"
64+
className="w-4 h-4"
65+
>
66+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
67+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
68+
</svg>
69+
<span>Copy for AI</span>
70+
</>
71+
)}
72+
</button>
73+
);
74+
}

0 commit comments

Comments
 (0)