Re-engraving is a rescue path for the handful of systems a scan cannot
deliver, so the window is an editing surface rather than an automation
project. Three full-width rows - the scanned system, the render, the
form - because a system is wide and short and the job is comparing one
against the other bar by bar. The render is shown scaled to the scan's
staff height, which is what export does anyway, so it previews the real
thing.
A form rather than a text box. Key and time are slice-level, clef,
notes and lyrics per voice: every staff in a system carries the same key
signature, and Kaipaava proves it across five-staff and two-staff
systems alike. Notes and lyrics stay raw LilyPond, so slurs, dynamics,
tuplets and the laissezVibrer/repeatTie idiom for ties crossing into the
next slice all work untouched.
Notes are entered in \relative mode, referenced to the middle of each
clef's staff, so a part needs no octave marks at all in the common case.
The time signature is used for spacing and bar checks but not printed:
the printed score repeats the key at every system and the time only at
the first, so a re-engraved middle slice showing one would stand out.
Seeded from what can be known reliably. Voice count comes from counting
staves in the slice; key, time and clefs are inherited from the song,
because the slices being re-engraved are the illegible ones and reading
a key signature off them is exactly the measurement that fails. After
the first replacement in a song only the notes need typing.
Staff counting needed two corrections against the corpus: compare gaps
against line spacing rather than staff height, since adjacent staves can
sit closer together than one staff is tall; and require five lines in a
group, since Engel's 'uh______' lyric extenders are long horizontal runs
too and each counted as a staff. Kaipaava now reads 2,2,2,2,5 on page 1,
Ketun 6, Engel 4.
Also in this change:
- Title is required for export, every other metadata field optional,
enforced in bundle.write so the CLI and the editor both get it. Tempo
added; noteman already has a free-form column for it.
- The panel is a splitter rather than a fixed width, sections collapse
under bold grey disclosure headers, and it scrolls.
- A re-engraved slice is washed amber with an ENGRAVED badge, and
markers get badges too. Thin coloured text was invisible against a
scan.
Closes#31Closes#32Closes#33Closes#34
Markers are stored per (page, slot), parallel to the discard flags, so
adding or removing a cut keeps them aligned with their slices. On a
split they stay with the upper half: a marker sits on a printed symbol
and nothing can say which side that symbol landed on, so predictable
beats clever.
Jump targets are chosen by clicking the slice rather than from the
thumbnail strip the plan called for. Less code, and it reads the score
instead of a list of thumbnails - which is what you want when hunting
for the Coda sign. Any page; PageUp/PageDown while picking.
Export resolves (page, slot) to the bundle's array index, the only
cross-reference the format has. A jump whose target was discarded or
re-cut away is dropped rather than exported dangling, since noteman
would have nothing to resolve it to.
tests/test_markers.py covers the enum size - that is the coupling
between two repos - along with cut-edit alignment, index resolution,
the dangling-target drop, and round-trips through both the project file
and a real bundle.
Closes#28Closes#29Closes#30
Opening an exported song starts a fresh session from detection instead
of resuming: cuts, discards and metadata do not carry over, so a re-cut
never inherits decisions that have already shipped. --resume overrides
it on edit, export and project.
This reverses what was agreed in planning and written into docs/spec.md
and CONTEXT.md, which promised resume-across-sessions and re-export.
Both are corrected. The cost is deliberate and worth stating: changing
the width cap or adding the SVG renderer later now means re-cutting each
song by hand rather than regenerating every bundle from its project
file.
Export records the flag in bundle.write, so no caller can forget it.
Also removed --refit and the Auto-fit buttons, which were added without
being asked for and whose only purpose - migrating projects made before
the content rectangle was proposed - disappears once exported projects
start fresh. Reset now restores detection's proposal rather than the
whole page: clearing to full width would undo the thing the rectangle
exists for, so one button covers it.
open_project() replaces four copies of load-or-detect across the CLI
and the editor.
Both scan problems reported from testing came from the same place: the
content rectangle defaulted to the whole page, so the mechanism meant
to handle margin junk never engaged. Ketun joululaulu has a vertical
scan streak down the right margin and Engel a shadow, and because trim
is tight and per slice, either one sets that slice's width, which sets
the song's widest slice, which scales the whole song down.
Detection can propose it. Staff lines are long horizontal runs; scan
shadows, spine darkening and glass streaks are vertical, so a wide flat
opening keeps one and erases the other. Three corrections were needed
against the corpus:
- Search only rows inside detected systems. A horizontal artefact above
or below the music is itself a long horizontal run reaching the paper
edge, which put Engel's left bound at 0.
- Take a percentile of the staff-line extents, not the maximum. Where
an artefact touches a staff line the two merge into one component: on
Ketun p2 the merged line ends at 1575px against 1544px on the clean
page.
- Take the left bound from the brackets too. A bracket sits left of
every staff line, so a staff-line bound crops it off — visible
immediately when comparing exported slices.
Anchors are now a dataclass carrying their left edge rather than a
(top, bottom) tuple.
Engel now drops 12-14% of page width and its music fills 1920px instead
of leaving the shadow's dead space; Ketun drops 12%.
Existing projects keep their saved rectangle; the editor's new Auto-fit
and Auto-fit all buttons re-propose it without disturbing cuts.
QGraphicsView for the viewport, with cuts and the content rectangle
manipulated by hit-testing in the view rather than as movable items —
the geometry is normalised, so what the screen shows and what the
renderer uses are the same numbers at a different zoom.
Cuts are edited as polylines: double-click adds one, drag moves it,
Ctrl-click inserts a vertex, right-click deletes a vertex or the whole
cut. That is how a straight cut becomes the stepped cut Engel needs.
Slice boundaries are drawn, not just cut lines, so a trim anomaly is
visible before export rather than after. Discarded slices are shaded.
Pages preview at 1800px regardless of source resolution, cached per
page, because re-reading a 4959x7017 vector render on every slider move
is unusable.
Autosave is debounced at 800ms and also fires on close.
Closes#12Closes#14Closes#15Closes#16Closes#17Closes#18Closes#19Closes#20Closes#26