cover page: share header bar, drop redundant title, dotfill barèmes
Salem Lahlou
421df9a
- Extract the tabularx that draws the header into _header_tabularx; the
longtable wraps it in \multicolumn and the cover page now uses it
standalone, so the cover carries the same page-number/school/exam-type/
teacher branding as every other page.
- Drop the cover-page title rendering entirely (it duplicated header
exam_info like "DEVOIR SURVEILLÉ 3" / "Fiche d'exercices"). Keep
subtitle as an escape hatch.
- Restructure bullets in both the tool schema and skill.md: each item is
now {description, bareme}. Renderer emits "description \dotfill (bareme)"
so the barème lands flush-right on the same line via a dotted leader,
matching the original exam look and no longer wrapping awkwardly.
feat: optional cover/intro page
Salem Lahlou
59a0f20
- skill.md + tool schema: add intro_page field; Claude only fills it when
photo 0 is a standalone cover (no questions), otherwise leaves it unset.
- render_tex(include_intro=True) emits a titlepage-style block before the
longtable: \Huge title, \large info table, bulleted exercise summary,
underlined footer, linespread 1.5, then \newpage.
- New intro_toggle checkbox: hidden by default, reveals itself when the
extraction came back with an intro_page. Flipping it triggers
handle_intro_toggle which re-renders the current data without calling
Claude and adds a version entry.
- All existing handlers now pass include_intro through so the setting is
preserved across header changes, modifications, and version restores.
fix: \textit{} breaks longtable on \begin{cases}; use {\itshape ...} instead
Salem Lahlou
6485822
\textit parses its argument via \check@nocorr@, which chokes on
\begin{...} environments inside the argument. One piecewise function in a
row's content was enough to trigger a cascade ("Forbidden control sequence",
"Extra alignment tab has been changed to \cr", "Missing $ inserted"),
collapsing the longtable into flat text with no header bar, no borders,
and mangled spacing. {\itshape ...} is a font-shape declaration: no
argument parsing, visually identical, safe around math environments.
- subprocess.run(text=True) assumed UTF-8 but pdflatex writes some
latin-1 bytes under texlive-lang-french; use errors='replace' on both
pdflatex and pdftoppm calls so a stray 0xe9 no longer kills the handler.
- new _downscale_to_tempfile caps uploaded images at 2200px longest side
before they reach _image_block/annotate_bboxes, so the verify step
stays under Anthropic's 5 MB/image limit instead of silently giving up.
- defensive try/except around compile_pdf in handle_upload so any future
crash shows a French message in the Status box instead of a bare toast.
4 features: PDF input, header layout, changelog, version history
Salem Lahlou+1: Claude Opus 4.6 (1M context)
529c3e3
1. PDF input: _prepare_images() converts PDF uploads to per-page PNGs
via pdftoppm before sending to Claude. file_types now includes .pdf.
2. Header layout: teacher name moved to right column with date
(was in the exam_info column). School centered alone in col 2.
3. Modification changelog: submit_exam tool has a new "changelog" field.
call_claude_modify asks Claude to describe what it changed. The
changelog appears in the status box and in the version label.
4. Version history: versions_state (hidden JSON) + Dropdown. Each
generation/modification/header-apply appends a version. Users can
select any previous version from the dropdown to restore it.
MAX_KEPT_WORKDIRS raised to 20 to accommodate history.
Tested locally: 3-image upload + modification + version check,
1-image bac page. All pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
header: school centered alone, teacher above doc type
Salem Lahlou+1: Claude Opus 4.6 (1M context)
cf55457
- School column: just the school name, centered via tabularx X
- Exam-info column: teacher name on line 1, doc type on line 2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: align header/body widths + repeat full header on all pages
Salem Lahlou+1: Claude Opus 4.6 (1M context)
ba8b920
1. Body longtable column width formula was off by 1 tabcolsep + 1
arrayrulewidth, making the body wider than the header tabularx.
Fixed: -4\tabcolsep-3\arrayrulewidth (was -3\tabcolsep-2).
2. Continuation pages now show the full header (school, type, date)
instead of just the page number. Both \endfirsthead and \endhead
render the same content; only \bumppage auto-increments.
Tested locally: 3-image exam renders 2 pages with aligned borders
and full header on both pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: header override before compile + no phantom figures
Salem Lahlou+1: Claude Opus 4.6 (1M context)
a63e387
1. Move user_header override BEFORE compile_pdf in the verify FIX path.
Previously the compile ran with Claude's header, and the override
happened after — so the PDF always showed the original image header.
2. Add skill rule: figures are ONLY for visually existing content on the
page. "Tracer la courbe" is a question asking the student to draw,
NOT a figure to embed. Claude was hallucinating blank boxes for
drawing instructions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three changes:
1. Header override preserved across verify FIX iterations. Previously
Claude's corrections replaced data["header"] with the original image
header, losing the user's school/teacher/date/type. Now user_header
is stored once and re-applied after every FIX.
2. New "Appliquer l'en-tête" button: re-renders the current document
with updated header field values. No API call — just render_tex +
compile. Users can change the 4 header fields and click this button
anytime after the initial generation.
3. Replace generator-based handle_upload with a regular function using
gr.Progress(). Eliminates the broken icons (empty Gallery/File
placeholders during loading) and shows a native Gradio progress bar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
editable header fields with defaults
Salem Lahlou+1: Claude Opus 4.6 (1M context)
0106253
Four text fields at the top of the UI (school, teacher, date, doc type)
with defaults:
- Groupe Scolaire Charles Péguy
- Mme Lahbabi
- 01/05/2026
- Fiche d'exercices
These override whatever Claude extracts from the photos. The user can
edit them before clicking Generate. The header in the rendered PDF
always reflects these field values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
auth: password-only login (no username field)
Salem Lahlou+1: Claude Opus 4.6 (1M context)
97e440a
Use Gradio's auth callback so any username works — only the password
is checked against APP_PASSWORD. Login prompt is in French.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
add password protection via APP_PASSWORD env var
Salem Lahlou+1: Claude Opus 4.6 (1M context)
77d7ebe
Gradio's built-in auth gate: if APP_PASSWORD is set (as a HF Space
secret), the landing page asks for username "user" + that password.
If unset, the app runs without auth (backwards compatible).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: auto page numbering 1/N on every page
Salem Lahlou+1: Claude Opus 4.6 (1M context)
79281a8
The top-left cell now always shows \bumppage (auto-incremented counter
+ \pageref{LastPage} from the lastpage package) instead of whatever
Claude extracted from the original page. This ensures page 1 always
shows "1/N" and subsequent pages show "2/N", "3/N", etc., with N
computed automatically after the second pdflatex pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
structured data architecture: template + tool use
Salem Lahlou+1: Claude Opus 4.6 (1M context)
3dbadba
Replace free-form LaTeX output with structured JSON data extraction.
Claude extracts {header, sections, figures} via submit_exam tool use;
a deterministic Python renderer (render_tex) produces the .tex from a
fixed template. Claude never writes LaTeX structure anymore.
Changes:
- skill.md: 350→84 lines. Now describes the JSON schema + extraction
rules only. No LaTeX template, no style rules, no longtable boilerplate.
- app.py: new render_tex function (~80 lines) owns all formatting.
_sanitize_text handles Unicode→LaTeX + newline→\newline in one pass.
submit_exam / submit_verdict tools replace submit_latex / old verdict.
Modifications work on JSON data (not LaTeX): Claude edits fields,
renderer re-produces the .tex deterministically.
- Verify loop now converges (first "Vérifié OK" observed — 72s/3 rounds
on test.jpeg, vs always hitting the 4-iteration limit before).
Tested:
- 3-image Al Jabr exam: 4 sections, per-exercise barème, 0 figures ✓
- 1-image bac page: 2 sections,…
fix: stale preview-*.png leaking page count after modification
Salem Lahlou+1: Claude Opus 4.6 (1M context)
e8475dd
When a modification reduced the page count, the gallery still showed
the old (larger) number of pages. Two interacting bugs:
1. compile_pdf, in modification mode, used to copy ALL *.png from the
previous workdir into the new one — including the preview-N.png
files that pdftoppm leaves behind from the previous render. We now
exclude preview-* from that copy and only carry the figure PNGs.
2. render_pdf_pages didn't clean up stale preview-*.png in the workdir
before running pdftoppm. If the new PDF had fewer pages than the
old one, pdftoppm would overwrite preview-1..N but leave the higher-
numbered stale ones, and the final glob would return both the new
pages and the leftovers. We now wipe preview-*.png first.
Either fix alone resolves the bug; both make the workdir state robust
against future ways to introduce stale previews.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
figure-aware pipeline with iterative self-verification
Salem Lahlou+1: Claude Opus 4.6 (1M context)
8d1e819
Major rewrite of the conversion + verification flow:
- Anthropic tool use (submit_latex, submit_verdict) replaces fragile
text parsing. Forces structured output and validates schemas.
- Figures: Claude declares each figure on the page in the figures[]
array (id, image_index, bbox in fractional coordinates). The pipeline
generates a blank white PNG with a black border, sized to the bbox
aspect ratio, and embeds it via \includegraphics. The end user prints
the PDF, draws the figure by hand inside the empty box, and photocopies
the result for students.
- Iterative self-verification: handle_upload is now a generator. After
the first compile, the loop sends the original photos (with current
bboxes drawn as red rectangles), the generated blank boxes, the
rendered PDF and the current LaTeX back to Claude. Claude submits
either OK or a corrected LaTeX (and optionally corrected bboxes).
Loop caps: 4 corrections, 120 s wall budget, 1 figure-bbox refinement
to avoid dri…
generic UI text and scrub README/app.py
Salem Lahlou+1: Claude Opus 4.6 (1M context)
611537e
Rename to "photos -> latex", drop domain-specific examples and labels
in the public-facing files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
initial deploy
Salem Lahlou+1: Claude Opus 4.6 (1M context)
dc1c844
Gradio app that converts photos of Moroccan math exams to LaTeX/PDF via
Claude Sonnet vision, with French-language modification support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>