open-prompt / src /components /prompts /remix-diff-view.tsx
GitHub Action
Automated sync to Hugging Face
bcce530
"use client"
import { useMemo } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { cn } from "@/lib/utils"
interface RemixDiffViewProps {
originalPrompt: string
remixedPrompt: string
originalTitle?: string
remixedTitle?: string
}
interface DiffLine {
type: 'unchanged' | 'added' | 'removed'
content: string
lineNumber: { original?: number; remix?: number }
}
export function RemixDiffView({
originalPrompt,
remixedPrompt,
originalTitle = "Original",
remixedTitle = "Remix"
}: RemixDiffViewProps) {
// Calculate diff
const diffLines = useMemo(() => {
const originalLines = originalPrompt.split('\n')
const remixLines = remixedPrompt.split('\n')
const result: DiffLine[] = []
// Simple line-by-line diff
let origIdx = 0
let remixIdx = 0
while (origIdx < originalLines.length || remixIdx < remixLines.length) {
const origLine = originalLines[origIdx]
const remixLine = remixLines[remixIdx]
if (origLine === remixLine) {
result.push({
type: 'unchanged',
content: origLine || '',
lineNumber: { original: origIdx + 1, remix: remixIdx + 1 }
})
origIdx++
remixIdx++
} else if (origIdx < originalLines.length && !remixLines.slice(remixIdx).includes(origLine)) {
result.push({
type: 'removed',
content: origLine,
lineNumber: { original: origIdx + 1 }
})
origIdx++
} else if (remixIdx < remixLines.length && !originalLines.slice(origIdx).includes(remixLine)) {
result.push({
type: 'added',
content: remixLine,
lineNumber: { remix: remixIdx + 1 }
})
remixIdx++
} else {
// Lines are different but exist in both
result.push({
type: 'removed',
content: origLine || '',
lineNumber: { original: origIdx + 1 }
})
result.push({
type: 'added',
content: remixLine || '',
lineNumber: { remix: remixIdx + 1 }
})
origIdx++
remixIdx++
}
}
return result
}, [originalPrompt, remixedPrompt])
// Count changes
const stats = useMemo(() => {
const added = diffLines.filter(l => l.type === 'added').length
const removed = diffLines.filter(l => l.type === 'removed').length
const unchanged = diffLines.filter(l => l.type === 'unchanged').length
return { added, removed, unchanged }
}, [diffLines])
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="text-lg">Prompt Diff</CardTitle>
<div className="flex items-center gap-2">
<Badge variant="outline" className="text-green-600 border-green-600">
+{stats.added} added
</Badge>
<Badge variant="outline" className="text-red-600 border-red-600">
-{stats.removed} removed
</Badge>
</div>
</div>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span className="flex items-center gap-1">
<span className="h-3 w-3 rounded bg-red-500/20" /> {originalTitle}
</span>
<span className="flex items-center gap-1">
<span className="h-3 w-3 rounded bg-green-500/20" /> {remixedTitle}
</span>
</div>
</CardHeader>
<CardContent>
<div className="font-mono text-sm rounded-lg border overflow-hidden">
{diffLines.map((line, index) => (
<div
key={index}
className={cn(
"flex",
line.type === 'added' && "bg-green-500/10",
line.type === 'removed' && "bg-red-500/10",
)}
>
{/* Line numbers */}
<div className="flex-shrink-0 w-20 px-2 py-1 text-muted-foreground text-xs border-r bg-muted/50 flex">
<span className="w-8 text-right">
{line.lineNumber.original || ''}
</span>
<span className="w-4 text-center">/</span>
<span className="w-8">
{line.lineNumber.remix || ''}
</span>
</div>
{/* Change indicator */}
<div className="flex-shrink-0 w-6 flex items-center justify-center border-r">
{line.type === 'added' && (
<span className="text-green-600 font-bold">+</span>
)}
{line.type === 'removed' && (
<span className="text-red-600 font-bold">-</span>
)}
</div>
{/* Content */}
<div className={cn(
"flex-1 px-3 py-1 whitespace-pre-wrap break-all",
line.type === 'added' && "text-green-700 dark:text-green-400",
line.type === 'removed' && "text-red-700 dark:text-red-400",
)}>
{line.content || '\u00A0'}
</div>
</div>
))}
</div>
</CardContent>
</Card>
)
}