From 4ca94199caf3fd479715141956e0417b690d8acf Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 1 Oct 2026 06:49:22 +0000
Subject: [PATCH] Harden HTML output and add advanced diff options
Co-authored-by: teaphile <141439252+teaphile@users.noreply.github.com>
---
src/polydiff/cli.py | 15 ++++++++++--
src/polydiff/core/plugin_base.py | 1 +
src/polydiff/plugins/image_diff.py | 4 +--
src/polydiff/plugins/pdf_diff.py | 8 +++---
src/polydiff/plugins/xlsx_diff.py | 39 +++++++++++++++++++++---------
tests/test_pdf_diff.py | 20 +++++++++++++++
tests/test_xlsx_diff.py | 31 ++++++++++++++++++++++++
7 files changed, 98 insertions(+), 20 deletions(-)
diff --git a/src/polydiff/cli.py b/src/polydiff/cli.py
index 5b9fc44..f7ff03d 100644
--- a/src/polydiff/cli.py
+++ b/src/polydiff/cli.py
@@ -13,8 +13,6 @@
from .core.git_integration import (
install_polydiff,
parse_diff_driver_args,
- setup_git_config,
- write_gitattributes,
)
from .core.plugin_base import DiffOptions
from .core.registry import registry
@@ -60,6 +58,17 @@ def diff(
True, "--color/--no-color",
help="Enable/disable colored output"
),
+ context_lines: int = typer.Option(
+ 3, "--context-lines", "-c",
+ min=0,
+ help="Context lines for text diffs (e.g., PDF text changes)"
+ ),
+ similarity_threshold: float = typer.Option(
+ 0.99, "--similarity-threshold",
+ min=0.0,
+ max=1.0,
+ help="Threshold below which files are considered changed"
+ ),
):
"""Compare two files directly (works with or without git)."""
# Validate files exist
@@ -85,6 +94,8 @@ def diff(
options = DiffOptions(
output_format=format,
output_path=output,
+ context_lines=context_lines,
+ similarity_threshold=similarity_threshold,
color=color,
)
diff --git a/src/polydiff/core/plugin_base.py b/src/polydiff/core/plugin_base.py
index 5d2577e..f4952df 100644
--- a/src/polydiff/core/plugin_base.py
+++ b/src/polydiff/core/plugin_base.py
@@ -13,6 +13,7 @@ class DiffOptions:
output_format: str = "terminal" # "terminal" | "html" | "json" | "image"
output_path: Optional[Path] = None
context_lines: int = 3 # for text-layer diffs inside plugins (PDF text, etc.)
+ similarity_threshold: float = 0.99 # changed if similarity < threshold
color: bool = True
diff --git a/src/polydiff/plugins/image_diff.py b/src/polydiff/plugins/image_diff.py
index 8b79a1e..30c1573 100644
--- a/src/polydiff/plugins/image_diff.py
+++ b/src/polydiff/plugins/image_diff.py
@@ -3,7 +3,6 @@
import base64
import io
from pathlib import Path
-from typing import Optional
from PIL import Image
@@ -13,7 +12,6 @@
compute_ssim,
create_diff_overlay,
create_side_by_side,
- downsample_image,
)
@@ -58,7 +56,7 @@ def diff(self, path_a: Path, path_b: Path, options: DiffOptions) -> DiffResult:
side_by_side = create_side_by_side(image_a, image_b, diff_overlay)
# Determine if changed
- changed = similarity < 0.99 # Allow tiny floating point differences
+ changed = similarity < options.similarity_threshold
# Build summary
summary_parts = []
diff --git a/src/polydiff/plugins/pdf_diff.py b/src/polydiff/plugins/pdf_diff.py
index 599b2bf..4d55211 100644
--- a/src/polydiff/plugins/pdf_diff.py
+++ b/src/polydiff/plugins/pdf_diff.py
@@ -1,14 +1,14 @@
"""PDF diff plugin for polydiff."""
import difflib
+from html import escape
from pathlib import Path
-from typing import Optional
import fitz # PyMuPDF
from PIL import Image
from ..core.plugin_base import DiffOptions, DiffPlugin, DiffResult
-from ..utils.image_ops import compute_ssim, downsample_image
+from ..utils.image_ops import compute_ssim
class PdfDiffPlugin(DiffPlugin):
@@ -90,7 +90,7 @@ def diff(self, path_a: Path, path_b: Path, options: DiffOptions) -> DiffResult:
else:
avg_similarity = 0.0
- changed = avg_similarity < 0.99 or pages_added or pages_removed or any(
+ changed = avg_similarity < options.similarity_threshold or pages_added or pages_removed or any(
p["text_changed"] for p in page_results
)
@@ -266,7 +266,7 @@ def _build_html_output(
html_parts.extend([
' View text diff
',
- f' {page["text_diff"]}',
+ f' {escape(page["text_diff"])}',
'