/** * Canonical refactor impact preview for rename/move dialogs. * Scholarly Dusk vocabulary: rail-style labels, mono paths, amber caution. * * @module src/serve/public/components/RefactorImpactPreview */ import type { FileRefactorPreviewPlan } from "../../../core/file-refactor-contract"; const UNRESOLVED_CLASSIFICATIONS = new Set([ "ambiguous", "unsupported", "malformed", "invalid", ]); function shortDigest(digest: string): string { return `${digest.slice(0, 8)}…${digest.slice(-6)}`; } export interface RefactorImpactPreviewProps { plan: FileRefactorPreviewPlan | null; loading?: boolean; confirmed: boolean; onConfirmedChange: (confirmed: boolean) => void; /** Accessible outcome message after apply (sync-pending, recovery, etc.). */ outcomeMessage?: string | null; outcomeTone?: "warning" | "error" | "success"; } export function RefactorImpactPreview({ plan, loading = false, confirmed, onConfirmedChange, outcomeMessage, outcomeTone = "warning", }: RefactorImpactPreviewProps) { if (loading && !plan) { return (
Computing reference impact…
); } if (!plan) { return null; } const rewriteable = plan.examinedReferences.filter( (ref) => ref.classification === "rewriteable" ); const unresolved = plan.examinedReferences.filter((ref) => UNRESOLVED_CLASSIFICATIONS.has(ref.classification) ); const affectedPaths = plan.affectedDocuments.map((doc) => doc.relPath); const confirmId = `refactor-confirm-${plan.planDigest.slice(0, 12)}`; const outcomeClass = outcomeTone === "error" ? "border-destructive/40 bg-destructive/10 text-destructive" : outcomeTone === "success" ? "border-primary/30 bg-primary/10 text-primary" : "border-amber-500/30 bg-amber-500/10 text-amber-500"; return (
Safety
{plan.canApply ? "Plan is safe to apply with the exact digest below." : `Cannot apply: ${(plan.safety.blockingReasonCodes[0] ?? "blocked").replaceAll("_", " ")}.`}

Digest {shortDigest(plan.planDigest)}

Target

{plan.source.relPath} → {plan.target.relPath}

{plan.safety.rewriteableCount} rewrite {plan.safety.rewriteableCount === 1 ? "" : "s"} ·{" "} {plan.safety.unchangedCount} unchanged · {unresolved.length}{" "} unresolved

{affectedPaths.length > 0 && (
Affected documents
)} {rewriteable.length > 0 && (
Rewrites
)} {unresolved.length > 0 && (
Unresolved / unsupported
)} {plan.safety.warnings.length > 0 && (
{plan.safety.warnings.map((warning) => (
{warning}
))}
)}
onConfirmedChange(event.target.checked)} type="checkbox" />
{outcomeMessage && (
{outcomeMessage}
)}
); }