diff --git a/src/components/DiffCard.tsx b/src/components/DiffCard.tsx index 841b804..24f6a58 100644 --- a/src/components/DiffCard.tsx +++ b/src/components/DiffCard.tsx @@ -9,10 +9,20 @@ export type DiffData = CanonicalDiffData; export interface DiffCardProps { /** The diff data to visualize. */ diff: DiffData; - /** Called when the user accepts the change. */ - onAccept?: (diffId: string) => void; - /** Called when the user rejects the change. */ - onReject?: (diffId: string) => void; + /** + * Called when the user accepts the change. + * + * Receives the pending-action id (`diff.actionId`, falling back to + * the deprecated `diff.diffId` for older payloads). + */ + onAccept?: (actionId: string) => void; + /** + * Called when the user rejects the change. + * + * Receives the pending-action id (`diff.actionId`, falling back to + * the deprecated `diff.diffId` for older payloads). + */ + onReject?: (actionId: string) => void; /** Whether the accept/reject action is in progress. */ loading?: boolean; /** Additional CSS class name. */ @@ -32,13 +42,14 @@ type DiffCardStatus = 'pending' | 'accepted' | 'rejected'; * ```tsx * apiFetch({ path: `/resolve/${id}`, method: 'POST', data: { action: 'accept' } })} - * onReject={(id) => apiFetch({ path: `/resolve/${id}`, method: 'POST', data: { action: 'reject' } })} + * onAccept={(id) => apiFetch({ path: '/actions/resolve', method: 'POST', data: { action_id: id, decision: 'accepted' } })} + * onReject={(id) => apiFetch({ path: '/actions/resolve', method: 'POST', data: { action_id: id, decision: 'rejected' } })} * /> * ``` */ @@ -58,14 +69,19 @@ export function DiffCard({ className, ].filter(Boolean).join(' '); + // Prefer `actionId` (canonical since v0.11). Fall back to `diffId` + // for payloads produced by older parsers or consumers that still + // construct `CanonicalDiffData` by hand without setting `actionId`. + const resolvedId = diff.actionId || diff.diffId; + const handleAccept = () => { setStatus('accepted'); - onAccept?.(diff.diffId); + onAccept?.(resolvedId); }; const handleReject = () => { setStatus('rejected'); - onReject?.(diff.diffId); + onReject?.(resolvedId); }; const diffHtml = renderDiff(diff); diff --git a/src/diff.ts b/src/diff.ts index 3457101..29c30ec 100644 --- a/src/diff.ts +++ b/src/diff.ts @@ -14,6 +14,25 @@ export interface CanonicalDiffEditorData { } export interface CanonicalDiffData { + /** + * Pending-action id the backend assigned when staging this preview. + * + * This is the canonical field as of v0.11 — it aligns with the + * unified "pending action" primitive on the server side (any tool + * invocation can be staged + previewed + resolved through the same + * /actions/resolve endpoint, not just content diffs). + */ + actionId: string; + /** + * Back-compat alias for `actionId`. + * + * Always populated with the same value as `actionId`. Kept so + * consumers that wired against the v0.7–v0.10 `diffId` field keep + * working through one more major version. New code should read + * `actionId`. + * + * @deprecated Use `actionId`. + */ diffId: string; diffType: CanonicalDiffType; originalContent: string; @@ -74,13 +93,23 @@ export function parseCanonicalDiff( value: unknown ): CanonicalDiffData | null { const container = isRecord( value.data ) ? value.data : value; const rawDiff = isRecord( container.diff ) ? container.diff : container; - const diffId = typeof rawDiff.diffId === 'string' - ? rawDiff.diffId - : typeof rawDiff.diff_id === 'string' - ? rawDiff.diff_id - : typeof container.diff_id === 'string' - ? container.diff_id - : ''; + // Resolve the pending-action id. Prefer the canonical `actionId` + // (server unified on pending-action vocabulary in mid-2026) and + // fall back to the historical `diffId` / `diff_id` shapes so + // older backends and stored payloads keep rendering. + const resolvedId = typeof rawDiff.actionId === 'string' + ? rawDiff.actionId + : typeof rawDiff.action_id === 'string' + ? rawDiff.action_id + : typeof container.action_id === 'string' + ? container.action_id + : typeof rawDiff.diffId === 'string' + ? rawDiff.diffId + : typeof rawDiff.diff_id === 'string' + ? rawDiff.diff_id + : typeof container.diff_id === 'string' + ? container.diff_id + : ''; const diffType = rawDiff.diffType === 'replace' || rawDiff.diffType === 'insert' ? rawDiff.diffType @@ -102,7 +131,7 @@ export function parseCanonicalDiff( value: unknown ): CanonicalDiffData | null { ? rawDiff.replacement_content : ''; - if ( ! diffId && ! originalContent && ! replacementContent ) { + if ( ! resolvedId && ! originalContent && ! replacementContent ) { return null; } @@ -129,7 +158,8 @@ export function parseCanonicalDiff( value: unknown ): CanonicalDiffData | null { : undefined; return { - diffId, + actionId: resolvedId, + diffId: resolvedId, diffType, originalContent, replacementContent,