Wire together the per-page pipeline: OCR -> LLM redaction detection ->
guided OpenCV search -> font detection with masking -> pixel alignment.
Includes font caching per line and progress callbacks.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Adds a new detect_font_masked() function that masks redaction boxes to
white (255) before running pixel-based font detection on the full line
crop. This replaces the fragile approach of cropping just the left/right
portion of a line, which lost signal from narrow crops and coordinate
mismatches. The Dice-based scoring naturally ignores masked (white)
regions since they contain no ink pixels.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add find_redaction_in_region() that searches for a black rectangle
within a specific page region identified by the LLM. Uses a lower
minimum area threshold (100px vs 500px) since the LLM provides
spatial guidance, preventing merged redactions and false positives.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Introduce llm_detect.py that sends OCR lines to Claude Haiku to identify
redaction locations via broken text patterns (garbled chars, brackets,
pipes). The LLM returns boundary words which are mapped back to pixel
positions using OCR character data. Includes comprehensive tests covering
single/multiple redactions, line start/end edge cases, and error handling.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Instead of using a neighboring line (which may have a different font/size,
e.g. email header vs body text), crop the non-redacted left or right
portion of the actual redacted line for font detection. Falls back to
clean neighbor lines only when neither side has enough text (>=50px).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace OCR-based offset calculation with pixel alignment that slides
rendered text across the page image to find the exact position with
maximum Dice overlap. Also fix char filtering to use center-point
(c.x + c.w/2) instead of right-edge, recovering characters like "nt"
in "Sent" that partially overlap the redaction box.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Redaction boxes corrupt OCR text and add spurious dark pixels that
break pixel-based font scoring. Now uses wider crop padding (3x)
and prefers the nearest non-overlapping line for font detection.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace word-width MAE scoring with pixel-overlap Dice scoring in both
_full_search() and _fine_search(). Both functions now accept a line_crop
parameter and use _score_font_line_pixel() instead of _score_font_line().
Comparisons are flipped (higher is better). The size range is now derived
from the OCR line height instead of the fixed SIZE_RANGE constant, and
uses step=1 since pixel scoring has sharp peaks at the exact font size.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add _score_font_line_pixel() which renders candidate text and compares
pixels against the actual page image using Dice coefficient overlap.
This captures glyph shape differences that the old word-width MAE
approach cannot distinguish (e.g. serif vs sans-serif).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Centralize 21 repeated color values into :root variables.
Makes theme changes trivial and improves readability.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Reorganize the monolithic 1,436-line frontend into 9 focused modules:
types, state, dom, canvas, viewport, associates, popover, solver, main.
No build step — browsers load ES modules natively. JSDoc @typedef
annotations provide type safety via @ts-check + jsconfig.json.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Without flex properties, #viewer-section collapsed to content
height (just the controls bar), giving the workspace 0 height
and causing zoom to compute as 0%.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Remove unused detect_redactions import from app.py
- Add de-duplication check in dblclick handler to prevent
creating duplicate redaction entries for the same dark region
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Shift+drag now adjusts gap width (was ctrl+shift+drag).
Manual redaction marking via shift+drag is removed;
users now double-click to spot-detect redactions.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Extract inline analysis logic from the analyze endpoint into a reusable
_run_analysis() helper function, then add a new /api/redaction/spot
endpoint that chains spot_redaction detection with analysis in one call.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Uses OpenCV connected components to find a dark region at a specific
click point, complementing the existing detect_redactions() which
auto-detects all redaction bars via morphological operations.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Adds green segment overlay, floating font toolbar, bottom text edit
bar, and backend offset guess to the redaction-centric UI.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Hold Shift and drag on the canvas to draw a custom redaction
rectangle. Creates an unanalyzed redaction entry that triggers
on-demand OCR when clicked.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace line list with redaction list, add canvas-native redaction
overlays with hit-testing, popover with on-demand analysis and
solver integration. Remove old font/segment/overlay controls.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
When a user clicks a redaction, this endpoint crops the surrounding line,
runs Tesseract OCR, detects the font, and returns text segments (before/after
the redaction), gap width, font info, and character-level bounding boxes.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace upfront OCR + font detection + overlay rendering with lightweight
OpenCV-based redaction detection. Upload now just rasterizes and detects
black rectangles, making it significantly faster. Remove the overlay
endpoint and update the data endpoint to return redaction bboxes.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Shift from upfront OCR to redaction-centric workflow with OpenCV
detection, canvas-native clickable overlays, on-demand analysis,
and per-redaction popover solver UI.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Fetches from 3 data sources (HuggingFace parquet, rhowardstone entities,
Epstein Exposed API) plus 8 court-verified addresses. Merges, deduplicates,
validates, and writes 644 unique emails to unredact/data/emails.txt.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Victim names are stored as a flat set of lowercased name variants
with no person details — the V badge appears but is not clickable,
protecting victim identities. A result can show both T1/T2/T3 and V
badges simultaneously.
- 15 known victims from public court records → 43 name variants
- Enabler/perpetrator categories promoted to T2
- Purple V badge with pointer-events disabled
- Victim matching uses prefix/suffix combination like associate matching
Co-Authored-By: Claude Opus 4.6 <[email protected]>
When matching solver results against associates, now tries:
- gap text alone ("oe")
- prefix + gap ("joe" if prefix="j")
- gap + suffix
- prefix + gap + suffix
Composite matches (prefix+gap) get a +3 score boost since the
surrounding context confirms the match. Deduplicates by person_id,
keeping the highest-scoring match.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Badge is now a button — clicking it opens a popup listing all
matching associates with their tier description (flight logs /
inner circle / named in files), category, and match type.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Sort results strictly by tier (T1 > T2 > T3 > none), then by score
within the same tier. Previously a T3 full-name match could outrank
a T1 first-name match.
- Boost associate score by +2 when the person's name matches the
user's filter prefix/suffix, surfacing likely candidates higher.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Lazy-loads associates.json from data dir and serves it at /api/associates,
with fallback to empty dicts if file is missing.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
7 tasks covering build script, API endpoint, frontend matching/sorting/badges,
and end-to-end testing. TDD approach with exact file paths and code.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Defines tiered prioritization of known Epstein associates in solver
results using rhowardstone/Epstein-research-data as the data source.
Associates are matched by full name, first/last, nicknames, and aliases
with frontend-side sorting and badge display.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Measures a known word's width in font context, then verifies the solver
recovers that word from its width. Proves the width table and solver
work correctly end-to-end.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add a solve panel UI that lets users attempt to recover redacted text
by finding strings whose rendered width matches the redaction gap.
Includes charset selection, length bounds, tolerance slider, and
dictionary/enumerate/both mode controls. Results stream via SSE and
can be clicked to insert into the gap.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add POST /api/solve endpoint that streams results via Server-Sent Events,
supporting both enumerate (brute-force) and dictionary solve modes. Add
dictionary CRUD endpoints (POST/GET/DELETE /api/dictionary) and solve
cancellation (DELETE /api/solve/{id}). Move static files mount to end of
app.py so API routes take priority.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add solve_dictionary() for linear-scan matching of dictionary entries
against a target pixel width, and DictionaryStore for managing named
word/name lists. Reuses SolveResult from the solver module.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Distribute branch-and-bound search across CPU cores by generating
depth-2 prefixes and farming each subtree to a worker process.
Includes on_progress callback and automatic prefix depth selection.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace static overlay with client-side canvas rendering. Left panel
shows clickable OCR lines with font controls (dropdown + size slider).
Right panel renders green text overlay on the document via canvas.
Fonts loaded from backend TTF files using the FontFace API.
New endpoints: GET /api/fonts, GET /api/font/{id}
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Instead of detecting one font per page, detect the font for each line
independently. The previous line's font is used as a prior — if it
scores within 15% of the best candidate, it's kept for consistency.
This handles documents with mixed fonts (bold headers, regular body,
different sections).
Also: redirect / to the static UI, fix data endpoint to return
per-line font info.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Word-by-word rendering hides font detection errors by repositioning
each word at the OCR'd spot. Line-by-line rendering lets the font's
own character spacing determine where each character falls — any
drift reveals that the font/size is wrong. The drift IS the feedback
mechanism.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Render each word at its OCR'd position instead of whole lines,
so word spacing matches the original document exactly
- Add y-offset correction using glyph bounding box
- Add fine-grained font size search (±3 around coarse best, step 1)
- Add alignment quality test (48.9% hit rate, >30% threshold)
- Add scipy to dev dependencies
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Adds POST /api/upload (runs full pipeline: rasterize, OCR, font detect,
overlay), GET endpoints for original/overlay images and OCR data JSON.
Also adds python-multipart, anyio, and pytest-anyio dependencies.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add render_overlay() that composites semi-transparent green text on
the original document image using the detected font. This is the
visual proof mechanism for font detection accuracy. Also adds numpy
to project dependencies for test pixel analysis.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add rasterize_pdf() that converts PDF pages to PIL images using
pdf2image. Supports configurable DPI and page range selection.
Includes tests verifying multi-page output, high-DPI dimensions,
and single-page extraction.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
9 tasks covering: project scaffolding, PDF rasterization, OCR with
character bounding boxes, font detection, green overlay renderer,
FastAPI backend, web UI, alignment calibration, and E2E smoke test.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
AI-powered redaction analysis tool for Epstein case files.
Image-first pipeline: OCR, font detection, redaction detection,
constraint solving, and Claude API guessing with visual overlay
verification.
Co-Authored-By: Claude Opus 4.6 <[email protected]>