src/auth.ts
+3-1
12
12
export function getToken() {
13
-
return localStorage.token;
13
+
const t = cookies.get("session");
14
+
if (!t) throw new Error("no session");
15
+
return t;
14
16
}
file-diff.tsx
styles.css
prompt.md
Collapse
Reset
Copy Code
Download
Run
1
"use client";
2
3
import React from "react";
4
import { CodeXml } from "lucide-react";
5
import { cn } from "@/lib/utils";
6
7
export interface FileDiffProps {
8
filename?: string;
9
addedCount?: number;
10
removedCount?: number;
11
lines?: {
12
type: "unchanged" | "added" | "removed";
13
oldLine?: number;
14
newLine?: number;
15
content: React.ReactNode;
16
}[];
17
className?: string;
18
}
19
20
const Keyword = ({ children }: { children: React.ReactNode }) => (
21
<span className="text-[#0ea5e9] dark:text-[#60a5fa]">{children}</span>
22
);
23
24
const StringLit = ({ children }: { children: React.ReactNode }) => (
25
<span className="text-[#10b981] dark:text-[#4ade80]">{children}</span>
26
);
27
28
const DEFAULT_LINES = [
29
{
30
type: "unchanged" as const,
31
oldLine: 12,
32
newLine: 12,
33
content: " export function getToken() {"
34
},
35
{
36
type: "removed" as const,
37
oldLine: 13,
38
content: <><Keyword>return</Keyword> localStorage.token;</>
39
},
40
{
41
type: "added" as const,
42
newLine: 13,
43
content: <><Keyword>const</Keyword> t = cookies.get(<StringLit>"session"</StringLit>);</>
44
},
45
{
46
type: "added" as const,
47
newLine: 14,
48
content: <><Keyword>if</Keyword> (!t) <Keyword>throw new</Keyword> Error(<StringLit>"no session"</StringLit>);</>
49
},
50
{
51
type: "added" as const,
52
newLine: 15,
53
content: <><Keyword>return</Keyword> t;</>
54
},
55
{
56
type: "unchanged" as const,
57
oldLine: 14,
58
newLine: 16,
59
content: " }"
60
}
61
];
62
63
export function FileDiff({
64
filename = "src/auth.ts",
65
addedCount = 3,
66
removedCount = 1,
67
lines = DEFAULT_LINES,
68
className
69
}: FileDiffProps) {
70
return (
71
<div className={cn("w-full max-w-[calc(100vw-32px)] md:max-w-[500px] rounded-2xl border border-black/5 dark:border-white/5 hover:border-black/10 dark:hover:border-white/20 bg-gradient-to-b from-white from-30% to-[#EBE9EB] dark:from-[#1C1C1E] dark:to-[#0A0A0C] overflow-hidden font-mono text-[12px] sm:text-[13px] shadow-[0_4px_24px_rgba(0,0,0,0.04)] dark:shadow-[0_4px_24px_rgba(0,0,0,0.4)] hover:shadow-[0_12px_40px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_12px_40px_rgba(0,0,0,0.6)] transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)]", className)}>
72
{/* Header */}
73
<div className="flex items-center justify-between px-3.5 sm:px-4 py-[10px] border-b border-black/5 dark:border-white/5 bg-black/[0.02] dark:bg-white/[0.02]">
74
<div className="flex items-center gap-2 sm:gap-2.5 text-zinc-600 dark:text-[#F5F5F7]">
75
<CodeXml className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-zinc-500 dark:text-[#86868B]" />
76
<span className="font-medium tracking-tight text-[12px] sm:text-[13px] truncate">{filename}</span>
77
</div>
78
<div className="flex items-center gap-2 sm:gap-2.5 text-[12px] sm:text-[13px] font-medium font-mono shrink-0">
79
<span className="text-emerald-600 dark:text-[#4ade80]">+{addedCount}</span>
80
<span className="text-red-600 dark:text-[#f87171]">-{removedCount}</span>
81
</div>
82
</div>
83
84
{/* Code Body */}
85
<div className="flex flex-col py-2.5 sm:py-3 overflow-x-auto scrollbar-none [-webkit-overflow-scrolling:touch]">
86
{lines.map((line, i) => (
87
<div
88
key={i}
89
className={cn(
90
"flex relative group text-[12px] sm:text-[13px] leading-[1.7] min-w-full w-max md:w-auto",
91
line.type === "added" && "bg-emerald-500/[0.08] dark:bg-[#4ade80]/[0.08]",
92
line.type === "removed" && "bg-red-500/[0.08] dark:bg-[#f87171]/[0.08]",
93
line.type === "unchanged" && "hover:bg-zinc-50 dark:hover:bg-white/[0.02]"
94
)}
95
>
96
{/* Left Border Decoration */}
97
{line.type === "added" && (
98
<div className="absolute left-0 top-0 bottom-0 w-[2.5px] bg-emerald-500 dark:bg-[#4ade80]" />
99
)}
100
{line.type === "removed" && (
101
<div
102
className="absolute left-0 top-0 bottom-0 w-[2.5px]"
103
style={{
104
background: 'repeating-linear-gradient(-45deg, #f87171, #f87171 2px, transparent 2px, transparent 4px)',
105
backgroundColor: 'rgba(248, 113, 113, 0.15)'
106
}}
107
/>
108
)}
109
110
{/* Line Numbers */}
111
<div className="w-[48px] sm:w-[60px] flex-shrink-0 flex text-zinc-400 dark:text-[#86868B]/50 select-none border-r border-black/5 dark:border-white/5">
112
<div className="w-1/2 text-right pr-1.5 sm:pr-2">{line.oldLine || '\u00A0'}</div>
113
<div className="w-1/2 text-right pr-2 sm:pr-2.5">{line.newLine || '\u00A0'}</div>
114
</div>
115
116
{/* Operator Column */}
117
<div className={cn(
118
"w-[20px] sm:w-[24px] flex-shrink-0 text-center select-none",
119
line.type === "added" ? "text-emerald-600 dark:text-[#4ade80]" :
120
line.type === "removed" ? "text-red-600 dark:text-[#f87171]" : "text-transparent"
121
)}>
122
{line.type === "added" ? "+" : line.type === "removed" ? "-" : " "}
123
</div>
124
125
{/* Code Content */}
126
<div className="flex-1 whitespace-pre pl-1 pr-4 text-zinc-900 dark:text-[#F5F5F7]">
127
{line.content}
128
</div>
129
</div>
130
))}
131
</div>
132
</div>
133
);
134
}
135

