import { createTwoFilesPatch } from "diff"; import { djb2Hash } from "./hash"; import { t } from "@/lib/i18n"; /** * Generate a unified diff between original and modified content * @param original - Original file content * @param modified - Modified file content * @param filename - File name (used in diff header) * @returns Unified diff string */ export function generateUnifiedDiff(original: string, modified: string, filename: string): string { // createTwoFilesPatch generates a unified diff with headers // Parameters: oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options // Note: oldHeader and newHeader should be empty strings or valid timestamps // The go-diff library on the backend expects standard unified diff format const diff = createTwoFilesPatch( filename, filename, original, modified, "", // Empty oldHeader - backend doesn't need timestamps "", // Empty newHeader - backend doesn't need timestamps { context: 3 }, // Number of context lines ); return diff; } /** * Calculate hash of content for change detection. * Uses Web Crypto API (SHA-256) in secure contexts (HTTPS, localhost). * Falls back to djb2 in non-secure contexts (HTTP) — sufficient for detecting * content changes, not for security purposes. * @param content - Content to hash * @returns Hex-encoded hash string */ export async function calculateHash(content: string): Promise { // crypto.subtle is only available in secure contexts (HTTPS, localhost) if (typeof crypto !== "undefined" && crypto.subtle) { try { const encoder = new TextEncoder(); const data = encoder.encode(content); const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); } catch { // Fall through to djb2 } } return djb2Hash(content); } /** * Format diff stats for display * @param additions - Number of additions * @param deletions - Number of deletions * @returns Formatted string like "+5 -3" */ export function formatDiffStats(additions: number, deletions: number): string { const parts: string[] = []; if (additions > 0) { parts.push(`+${additions}`); } if (deletions > 0) { parts.push(`-${deletions}`); } return parts.join(" ") || t("task:noChanges"); }