SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
4.0 KB · 86 lines tsx
Raw Blame History
1import { SignificanceBadge } from '@/components/ui/badges';2import { Note } from '@/components/ui/section';3import { cn } from '@/lib/cn';4import { fmtPct } from '@/lib/format';5import type { BlockDelta, DiffPayload } from '@/lib/types';67/** Block-level diff (added / removed / modified with before → after text), significance meter and reasons. */8export function DiffViewer({ diff, significance, className }: { diff: DiffPayload; significance?: number | null; className?: string }) {9  const total = diff.added.length + diff.removed.length + diff.modified.length;10  return (11    <div className={cn('space-y-5', className)} data-diff-viewer>12      <div className="grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">13        <div>14          <p className="eyebrow">Significance</p>15          <div className="mt-1 flex items-center gap-3">16            <div className="meter flex-1">17              <span style={{ width: `${Math.round((significance ?? 0) * 100)}%`, background: (significance ?? 0) >= 0.65 ? 'var(--warning)' : (significance ?? 0) >= 0.4 ? 'var(--accent)' : 'var(--ink-3)' }} />18            </div>19            <SignificanceBadge value={significance} />20          </div>21          <p className="tnum mt-2 text-xs text-ink-3">22            text delta {fmtPct(diff.text_delta_ratio, 1, true)} · similarity {fmtPct(diff.similarity, 1, true)} · {diff.counts.unchanged !== undefined ? `${diff.counts.unchanged} unchanged blocks` : `${total} changed blocks`}23          </p>24          <div className="mt-2 flex flex-wrap gap-3 text-xs">25            <span className="text-positive">+{diff.added.length} added</span>26            <span className="text-danger">−{diff.removed.length} removed</span>27            <span className="text-warning">~{diff.modified.length} modified</span>28            {diff.moved.length > 0 && <span className="text-ink-3">↕ {diff.moved.length} moved</span>}29          </div>30        </div>31        <div>32          <p className="eyebrow">Why it scored this way</p>33          {diff.reasons.length ? (34            <ul className="mt-1 space-y-0.5 text-sm text-ink-2">35              {diff.reasons.map((r) => (36                <li key={r}>· {r}</li>37              ))}38            </ul>39          ) : (40            <Note className="mt-1">No reasons recorded for this diff.</Note>41          )}42        </div>43      </div>4445      {total === 0 ? (46        <Note>No block-level differences above the noise floor between these versions.</Note>47      ) : (48        <div className="space-y-4">49          {diff.added.length > 0 && <Group label="Added" items={diff.added} kind="added" />}50          {diff.removed.length > 0 && <Group label="Removed" items={diff.removed} kind="removed" />}51          {diff.modified.length > 0 && <Group label="Modified" items={diff.modified} kind="modified" />}52        </div>53      )}54    </div>55  );56}5758function Group({ label, items, kind }: { label: string; items: BlockDelta[]; kind: 'added' | 'removed' | 'modified' }) {59  return (60    <section>61      <p className="eyebrow mb-1.5">62        {label} <span className="tnum normal-case tracking-normal text-ink-3">({items.length})</span>63      </p>64      <ul className="space-y-2">65        {items.map((b) => (66          <li key={`${kind}-${b.key}`} className={cn('diff-block', kind)}>67            <p className="mono mb-1 flex flex-wrap items-center gap-2 text-[10px] text-ink-3">68              <span className="uppercase tracking-wider">{b.kind}</span>69              <span className="truncate">{b.path}</span>70              <span className="ml-auto tnum">w {b.weight.toFixed(2)}{b.similarity !== null ? ` · sim ${b.similarity.toFixed(2)}` : ''}</span>71            </p>72            {kind === 'modified' ? (73              <>74                <p className="diff-before">{b.before}</p>75                <p className="diff-after mt-1">{b.after}</p>76              </>77            ) : (78              <p className={kind === 'removed' ? 'diff-before' : 'diff-after'}>{kind === 'removed' ? b.before : b.after}</p>79            )}80          </li>81        ))}82      </ul>83    </section>84  );85}86