Under load its thread pulled samples before the test read the clock, so
'from the first frame' saw frame 1 and a_started_preview_follows_its_sound flaked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProRes renders had no colr atom, so the stream reported no primaries,
transfer or matrix. Write an nclc atom, as FilmCraft does for MOV, with
the codes of what the render path makes: the project's output space when
it has a working space, BT.709 otherwise.
sRGB output stays at transfer 1, which is still open on #340 and #464.
The frames don't change, so the pinned ProRes bytes move by exactly the
atom's 18 bytes.
The craft-ecproj-format pin moves to storytold/craft-libs#409's new
head, where Head::read is the one reader and saved_by() is removed.
effectcraft-project's saved_by reads savedBy with Head::read, and from
ReadError::TooNew for a project with a newer schema.
saved_by now returns None for text that is not a JSON object with a u32
schema. EffectCraft calls it only on files Project::from_json opened,
so opening and importing projects are unchanged.
effectcraft-project writes and checks the envelope of .ecproj files with
craft-ecproj-format from craft-libs: File (savedBy, then the saved view
and the project) with to_json and to_json_compact for Save Compact
Project Files, check_schema and its message, SCHEMA_VERSION and
saved_by, which stay re-exported under their names. Project, SavedView
and the schema 1 conversion stay here.
Saved files are byte-identical. saved_by now reads only a JSON object,
so a project written as a JSON array no longer reports a savedBy.
A `trailer` marker inside an unreferenced stream followed by `(` and more
than 4 KiB of text made the lexer stop at the window's end, so a later
`/Encrypt` name anywhere in the file refused a plain file. The unbounded
scan read a string there and opened it. The past-the-window fallback now
applies only when no object starts in the window or the first one opens
a dictionary (`<<`, including one split by the window's end).
Regression test with `trailer(` and `trailer[` in a stream and a later
Encrypt name.
The pre-scan window could end inside a trailer key, so a decoy like
`/Encrypted` cut right after `/Encrypt` was taken for the Encrypt key
and an unencrypted file was refused. A window that ends mid-token now
ends before that token instead, and the past-the-window fallback reads
the name whole (a real `/Encrypt` cut as `/Encryp|t` is still refused).
Tests for that boundary, for a trailer with a long /ID, and for `#`
without two hex digits staying as written in a name.
- shortcuts::pick chooses the binding for a key press: narrowest scope, then a
physical-key binding, then the first. When two other panels (or two active
contexts) claim the chord at the same rank, neither runs and the status bar
says which panels own it.
- Physical and logical bindings compete by scope rank in one pass, so a
narrower logical binding beats a physical one of an unrelated panel.
- An additional viewer (PanelKind::Viewer) ranks as the Composition panel.
- Two contexts on the same keys are reported as a conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fallback searched for the literal bytes `/Encrypt`, so a long trailer
with the key written as `/Encr#79pt` (or any other #-escaped spelling) got
past it. It now finds the last name token that decodes to `Encrypt`, with
the lexer's own name decoding, and checks whether that token ends past the
window. It is still one linear pass per file. Longer names that only start
with `Encrypt` (`/Encrypted`) no longer count.
VmHWM is per process; the other tests in the binary ran on parallel threads
and their allocations failed the 64 MiB bound intermittently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 16384x1 output stacks every grid row onto one pixel row, so a sane warp
visits ~100x its pixels; allow each triangle about two grid cells (CodeRabbit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty layers list cleared the composition's markers too (CodeRabbit);
the composition-marker menu uses comp.deleteAllMarkers instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>