agent-ecosystem/src/renderer/components/team/review/reviewContentPreview.ts
2026-05-17 14:18:54 +03:00

99 lines
3.6 KiB
TypeScript

import type { FileChangeWithContent } from '@shared/types';
import type { FileChangeSummary } from '@shared/types/review';
export type ReviewRejectBlockReason =
| 'missing-on-disk'
| 'content-unavailable'
| 'manual-ledger-review'
| 'baseline-unavailable';
type ReviewContentAvailability = Pick<
FileChangeWithContent,
'contentSource' | 'originalFullContent' | 'modifiedFullContent'
>;
export function hasReviewSnippetText(file: Pick<FileChangeSummary, 'snippets'>): boolean {
return file.snippets.some(
(snippet) => !snippet.isError && (snippet.oldString.length > 0 || snippet.newString.length > 0)
);
}
export function getLastWriteSnippetContent(
file: Pick<FileChangeSummary, 'snippets'>
): string | null {
const writeSnippets = file.snippets.filter(
(snippet) =>
!snippet.isError && (snippet.type === 'write-new' || snippet.type === 'write-update')
);
if (writeSnippets.length === 0) return null;
return writeSnippets[writeSnippets.length - 1]?.newString ?? null;
}
export function getResolvedReviewModifiedContent(
file: Pick<FileChangeSummary, 'snippets'>,
fileContent: Pick<FileChangeWithContent, 'modifiedFullContent'> | null
): string | null {
return fileContent?.modifiedFullContent ?? getLastWriteSnippetContent(file);
}
export function isReviewFileMissingOnDisk(
fileContent: Pick<FileChangeWithContent, 'modifiedFullContent'> | null
): boolean {
return fileContent ? fileContent.modifiedFullContent == null : false;
}
export function isReviewTextContentUnavailable(
file: Pick<FileChangeSummary, 'snippets'>,
fileContent: Pick<FileChangeWithContent, 'contentSource' | 'modifiedFullContent'> | null
): boolean {
return (
fileContent?.contentSource === 'unavailable' &&
getResolvedReviewModifiedContent(file, fileContent) === null
);
}
export function requiresManualLedgerReview(file: Pick<FileChangeSummary, 'snippets'>): boolean {
return file.snippets.some(
(snippet) =>
!!snippet.ledger &&
(!!snippet.ledger.beforeState?.unavailableReason ||
!!snippet.ledger.afterState?.unavailableReason) &&
(snippet.ledger.originalFullContent == null || snippet.ledger.modifiedFullContent == null)
);
}
export function getReviewRejectBlockReason(
file: Pick<FileChangeSummary, 'snippets' | 'isNewFile'>,
fileContent: ReviewContentAvailability | null
): ReviewRejectBlockReason | null {
if (isReviewFileMissingOnDisk(fileContent)) return 'missing-on-disk';
if (isReviewTextContentUnavailable(file, fileContent)) return 'content-unavailable';
if (requiresManualLedgerReview(file)) return 'manual-ledger-review';
if (!fileContent) {
return file.snippets.length > 0 && !hasReviewSnippetText(file) ? 'baseline-unavailable' : null;
}
const modified = getResolvedReviewModifiedContent(file, fileContent);
if (modified == null) return 'baseline-unavailable';
if (file.isNewFile) return fileContent.originalFullContent === '' ? null : 'baseline-unavailable';
return fileContent.originalFullContent == null ? 'baseline-unavailable' : null;
}
export function isReviewRejectable(
file: Pick<FileChangeSummary, 'snippets' | 'isNewFile'>,
fileContent: ReviewContentAvailability | null
): boolean {
return getReviewRejectBlockReason(file, fileContent) === null;
}
export function shouldRenderCurrentDiskContextPreview(
file: Pick<FileChangeSummary, 'snippets' | 'isNewFile'>,
fileContent: ReviewContentAvailability | null
): boolean {
return (
fileContent?.contentSource === 'disk-current' &&
fileContent.modifiedFullContent != null &&
getReviewRejectBlockReason(file, fileContent) === 'baseline-unavailable'
);
}