Spaces:
Configuration error
Configuration error
| "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> | |
| ) | |
| } | |