-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcomponent.tsx
More file actions
140 lines (130 loc) · 4.49 KB
/
Copy pathcomponent.tsx
File metadata and controls
140 lines (130 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"use client"
import { useState } from "react"
import { cn } from "@agent-patterns/core"
interface CodeBlockProps {
code: string
language?: string
filename?: string
showLineNumbers?: boolean
highlightLines?: number[]
startLineNumber?: number
copyable?: boolean
collapsible?: boolean
maxHeight?: string
className?: string
}
export function CodeBlock({
code,
language = "typescript",
filename,
showLineNumbers = true,
highlightLines = [],
startLineNumber = 1,
copyable = true,
collapsible = false,
maxHeight,
className,
}: CodeBlockProps) {
const [copied, setCopied] = useState(false)
const [collapsed, setCollapsed] = useState(false)
const lines = code.split("\n")
const handleCopy = async () => {
await navigator.clipboard.writeText(code)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
const languageColors = {
typescript: "text-blue-400",
javascript: "text-yellow-400",
python: "text-green-400",
rust: "text-orange-400",
go: "text-cyan-400",
java: "text-red-400",
default: "text-purple-400",
}
const languageColor = (languageColors as any)[language] || languageColors.default
return (
<div className={cn("rounded-lg border border-border bg-card overflow-hidden", className)}>
{/* Header */}
{(filename || copyable || collapsible) && (
<div className="flex items-center justify-between border-b border-border bg-muted px-4 py-2">
<div className="flex items-center gap-2">
{filename && (
<span className="text-sm font-medium text-foreground">{filename}</span>
)}
<span className={cn("text-xs", languageColor)}>{language}</span>
</div>
<div className="flex items-center gap-2">
{collapsible && (
<button
onClick={() => setCollapsed(!collapsed)}
className="rounded px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-accent-foreground"
>
{collapsed ? "Expand" : "Collapse"}
</button>
)}
{copyable && (
<button
onClick={handleCopy}
className="rounded px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-accent-foreground"
>
{copied ? (
<span className="flex items-center gap-1">
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
Copied!
</span>
) : (
<span className="flex items-center gap-1">
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
Copy
</span>
)}
</button>
)}
</div>
</div>
)}
{/* Code Content */}
{!collapsed && (
<div
className="overflow-x-auto overflow-y-auto"
style={{ maxHeight: maxHeight || "600px" }}
>
<pre className="p-4 text-sm">
<code>
{lines.map((line, index) => {
const lineNumber = startLineNumber + index
const isHighlighted = highlightLines.includes(lineNumber)
return (
<div
key={index}
className={cn(
"flex",
isHighlighted && "bg-blue-500/10 border-l-2 border-l-blue-500 -ml-4 pl-3.5"
)}
>
{showLineNumbers && (
<span className="mr-4 inline-block w-8 select-none text-right text-muted-foreground">
{lineNumber}
</span>
)}
<span className="flex-1 text-foreground">{line || " "}</span>
</div>
)
})}
</code>
</pre>
</div>
)}
</div>
)
}