Patchpass V1
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import type { Change } from "../api/types";
|
||||
|
||||
function DiffView({ content }: { content: string }) {
|
||||
const lines = content.split("\n");
|
||||
return (
|
||||
<pre className="overflow-x-auto rounded-lg border border-border bg-bg p-3 text-xs leading-relaxed">
|
||||
<code>
|
||||
{lines.map((line, i) => {
|
||||
let cls = "";
|
||||
if (line.startsWith("+++") || line.startsWith("---")) cls = "diff-meta";
|
||||
else if (line.startsWith("@@")) cls = "diff-hunk";
|
||||
else if (line.startsWith("+")) cls = "diff-add";
|
||||
else if (line.startsWith("-")) cls = "diff-del";
|
||||
else if (line.startsWith("diff ") || line.startsWith("index ")) cls = "diff-meta";
|
||||
return (
|
||||
<div key={i} className={`whitespace-pre px-1 ${cls}`}>
|
||||
{line || " "}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
||||
function fmtValue(v: unknown): string {
|
||||
if (v === null || v === undefined) return "∅";
|
||||
if (typeof v === "object") return JSON.stringify(v);
|
||||
return String(v);
|
||||
}
|
||||
|
||||
function percentDelta(before: unknown, after: unknown): string | null {
|
||||
const b = Number(before);
|
||||
const a = Number(after);
|
||||
if (!Number.isFinite(b) || !Number.isFinite(a) || b === 0) return null;
|
||||
const pct = Math.round(((a - b) / Math.abs(b)) * 100);
|
||||
if (pct === 0) return null;
|
||||
return `${pct > 0 ? "+" : ""}${pct}%`;
|
||||
}
|
||||
|
||||
function BeforeAfter({ before, after, contentType }: { before: unknown; after: unknown; contentType?: string | null }) {
|
||||
const delta =
|
||||
contentType === "integer" || contentType === "number" ? percentDelta(before, after) : null;
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-2 text-sm">
|
||||
<code className="diff-del rounded px-2 py-0.5 font-mono">{fmtValue(before)}</code>
|
||||
<span className="text-faint">→</span>
|
||||
<code className="diff-add rounded px-2 py-0.5 font-mono">{fmtValue(after)}</code>
|
||||
{delta && <span className="rounded bg-primary-dim/40 px-2 py-0.5 text-xs text-primary">{delta}</span>}
|
||||
{contentType && <span className="text-xs text-faint">({contentType})</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function typeChip(label: string, color: string) {
|
||||
return <span className={`rounded px-2 py-0.5 text-xs font-medium ${color}`}>{label}</span>;
|
||||
}
|
||||
|
||||
export function ChangeCard({ change, index }: { change: Change; index: number }) {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-surface-raised/50 p-4">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<span className="text-xs font-mono text-faint">#{index + 1}</span>
|
||||
{change.type === "unified_diff" && (
|
||||
<>
|
||||
{typeChip("diff", "bg-primary-dim/40 text-primary")}
|
||||
<code className="text-sm text-text">{change.path}</code>
|
||||
</>
|
||||
)}
|
||||
{change.type === "config" && (
|
||||
<>
|
||||
{typeChip("config", "bg-accent/15 text-accent")}
|
||||
<code className="text-sm text-text">{change.path}</code>
|
||||
</>
|
||||
)}
|
||||
{change.type === "custom" && (
|
||||
<>
|
||||
{typeChip("custom", "bg-changes/15 text-changes")}
|
||||
<span className="text-sm text-text">{change.label}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{change.type === "unified_diff" && <DiffView content={change.content} />}
|
||||
{change.type === "config" && (
|
||||
<BeforeAfter before={change.before} after={change.after} contentType={change.content_type} />
|
||||
)}
|
||||
{change.type === "custom" && <BeforeAfter before={change.before} after={change.after} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChangeList({ changes }: { changes: Change[] }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{changes.map((c, i) => (
|
||||
<ChangeCard key={i} change={c} index={i} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user