profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
revision-diff.tsx 3,398 bytes
1 'use client';
2
3 import { diffWords } from 'diff';
4 import { ArrowRight, GitCommitHorizontal, ShieldCheck } from 'lucide-react';
5 import { useMemo, useState } from 'react';
6 import { RichText } from '@/components/debate/rich-text';
7 import { Badge } from '@/components/ui/badge';
8 import { Button } from '@/components/ui/button';
9 import type { RevisionChangelog } from '@/core/types';
10 import { participantColor, participantTag } from '@/lib/model-visuals';
11
12 interface RevisionDiffProps {
13 index: number;
14 displayName: string;
15 previous: string;
16 next: string;
17 changelog: RevisionChangelog;
18 }
19
20 export function RevisionDiff({ index, displayName, previous, next, changelog }: RevisionDiffProps) {
21 const [showDiff, setShowDiff] = useState(true);
22 const parts = useMemo(() => diffWords(previous, next), [previous, next]);
23 const hasTextChange = parts.some((p) => p.added || p.removed);
24
25 return (
26 <div className="rounded-xl border bg-card">
27 <div className="flex flex-wrap items-center gap-2 border-b p-3">
28 <span
29 className="flex h-6 w-6 items-center justify-center rounded-md text-xs font-bold text-white"
30 style={{ backgroundColor: participantColor(index) }}
31 >
32 {participantTag(index)}
33 </span>
34 <span className="text-sm font-semibold">{displayName}</span>
35 {changelog.changed ? (
36 <Badge variant="default" className="gap-1">
37 <GitCommitHorizontal className="h-3 w-3" /> Revised
38 </Badge>
39 ) : (
40 <Badge variant="secondary" className="gap-1">
41 <ShieldCheck className="h-3 w-3" /> Defended original
42 </Badge>
43 )}
44 {hasTextChange && (
45 <Button variant="ghost" size="sm" className="ml-auto" onClick={() => setShowDiff((v) => !v)}>
46 {showDiff ? 'Show final text' : 'Show diff'}
47 </Button>
48 )}
49 </div>
50
51 <div className="space-y-3 p-4">
52 <div className="rounded-lg bg-muted/50 p-3 text-sm">
53 <div className="mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
54 <ArrowRight className="h-3.5 w-3.5" /> What changed & why
55 </div>
56 <p className="text-foreground/90">{changelog.summary || '-'}</p>
57 {changelog.bullets.length > 0 && (
58 <ul className="mt-2 list-inside list-disc space-y-0.5 text-xs text-muted-foreground">
59 {changelog.bullets.map((b, i) => (
60 <li key={i}>{b}</li>
61 ))}
62 </ul>
63 )}
64 </div>
65
66 {showDiff && hasTextChange ? (
67 <div className="prose-debate whitespace-pre-wrap rounded-lg border p-3 text-sm leading-relaxed">
68 {parts.map((part, i) =>
69 part.added ? (
70 <span key={i} className="rounded bg-emerald-500/20 text-emerald-700 dark:text-emerald-300">
71 {part.value}
72 </span>
73 ) : part.removed ? (
74 <span key={i} className="rounded bg-red-500/15 text-red-600 line-through dark:text-red-400">
75 {part.value}
76 </span>
77 ) : (
78 <span key={i} className="text-foreground/80">
79 {part.value}
80 </span>
81 ),
82 )}
83 </div>
84 ) : (
85 <RichText text={next} />
86 )}
87 </div>
88 </div>
89 );
90 }
91