Drop the byte-count tooltip, the "—" marker and their strings and UI test,
and the bench wrapper script: they are outside this PR's scope and After
Effects leaves the Size cell blank when the size is unknown. The size sort
and its tests are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load PDF document objects through the pinned craft-pdf parser and convert them into EffectCraft's existing drawing model with bounded traversal, recovery and retained data. Preserve generation numbers, stream limits, encrypted-document refusal and reported repairs.
Retain app-owned drawing, EPS, fonts and content/image decoding. Assert early excessive-nesting rejection without partial art, and cover tolerant arrays on native and WASM.
Validated with the22-stage local matrix and exact-head FreeBSD, Windows ARM64 and packaging checks.
Co-authored-by: Codex <noreply@openai.com>
Clicking keyframes on Position, Rotation and Scale highlights those
properties, and Copy then added every key of a highlighted property that
had none selected, so deselected keys came back on paste. The keyframe
copy branch also left a stale value clipboard that Paste preferred over
the new keys.
Copy now takes whole animations only when no key is selected or the
Transform group is selected, and every copy branch clears the clipboards.
Also collapses a nested `if` in the STL importer into a match guard,
which Clippy 1.95 flags under -D warnings (no behaviour change).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The roi quad is stored raw from the control channel (crop.useRoi passes
serde_json's [f64;4] straight through, unlike the sibling edge fields,
which are clamped to plus/minus 20 000) and nothing repairs it on load.
Crop::edges converted it with round() as i32 and subtracted raw: an x of
-1e12 saturates to i32::MIN and w as i32 - l overflows in debug builds,
wrapping to a wrong-but-bounded crop in release.
Clamp each converted edge to the same plus/minus 20 000 the sibling
fields use and subtract with saturating_sub in edges() and frame_size().
Regression test: hostile_roi_cannot_overflow_crop_math — panics at
crates/project/src/render_queue.rs:774 on main; with the fix the hostile
quad degrades to a 1x1 frame and the sane-ROI behavior is unchanged.
inline_len computed (w * n * bpc).div_ceil(8) * h with Width/Height taken
straight from the content stream and no size validation — unlike decode()
and mask_image, which bound their geometry first. /W 5000000000000000000
overflows the multiply in debug and test builds; in release it wraps to a
small length and the caller slices the wrong bytes, misparsing the image
and shifting the EI resync.
Chain the arithmetic through checked_mul and return None, which the
caller already routes to the EI-scan fallback. Regression test:
inline_image_with_absurd_dimensions_falls_back_to_ei_scan — panics at
crates/pdf/src/image.rs:85 on main, passes with the chain.
Entries whose layer buffer was freed were pruned only once the cache went over its budget
(1.5 GiB), so an animated layer, which makes a new buffer every frame, left a dead 4K texture
per frame allocated until the cache filled up.
Retry only explicitly read-only control queries after connection failures. Report ambiguous edit outcomes without resending mutations, reconnect on the next explicit call, and reject request-ID exhaustion without overflow.
Add loopback regressions for lost and malformed replies, read-only retries, playback queries versus edits, and unknown methods.
Fixes#1247
Use checked division and remainder for EPS integer operators so i64::MIN with a divisor truncating to -1 cannot panic. Preserve the existing zero-divisor fallback and normal truncation behavior.
Add public EPS parse regressions covering extreme, fractional and ordinary operands.
PR #1223
The PDF / Illustrator import dialog now offers Composition - Retain Layer
Sizes. Each file layer's footage is cropped to its artwork bounds (fills,
stroke outlines, images, clips) and positioned to keep its place on the
page. Decoding, continuous rasterisation, shape conversion and reload share
one crop (effectcraft_pdf::layer_doc_with_sizes), so they agree.
Also collapses a match guard in the STL reader for current stable clippy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge each offloaded property-group result against the snapshot the job started from. Preserve disjoint live edits, group identity and child changes; refuse conflicting results without changing the document or undo/redo history.
Record an apply rejection on its originating job so completion logs and waiting control commands report failure. Keep concurrent jobs' outcomes separate and retain the correct completion position when the bounded log rolls over.
Add actual-worker regressions for conflicts, disjoint edits, concurrent analyses and undo/redo, plus control completion/error regressions.
Fixes#1249
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Create each atomic write through its own exclusively created temporary sibling and keep its open file handle until writing finishes. Concurrent writers publish complete files without truncating or following an existing temporary entry; failed writes or renames clean up their own staging file.
Keep the temporary basename bounded independently of the destination name so previously supported long project filenames still save. Retain the concurrent-writer and symlink regressions and add a 251-byte filename overwrite regression that checks exact bytes and staging cleanup.
Fixes#1233
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EffectCraft's RPM requires Fedora package names: `libxkbcommon`, `(vulkan-loader or libglvnd-egl)`,
`alsa-lib`. openSUSE has no `libxkbcommon`, `vulkan-loader` or `libglvnd-egl` package, so zypper
refuses it with `nothing provides 'libxkbcommon'` (exit 4, which the ArtCraft Launcher shows as
"error 4"; storytold/craft-launcher#341).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two German menu labels described a different command:
- `layer.timeReverse` was "Ebenenreihenfolge umkehren", which reads as reversing
the stacking order rather than time -- now "Ebene zeitlich umkehren", matching
`keys.timeReverse`;
- `layer.style.convertToEditable` was "In editierbare Formate umwandeln", and
"Formate" are formats, not layer styles -- now "In bearbeitbare Stile
umwandeln".
Where the same English term had more than one German rendering, the one already
in the majority wins, so this settles rather than re-translates:
- "Expression" is "Ausdruck" in seven entries and "Expressions" in the two
`layer.expressions` ones, which now read "Ausdrücke aktivieren/deaktivieren";
- "Reveal" is "anzeigen" in eleven of twelve entries, so `layer.revealInFinder`
joins `file.revealInFinder` at "Im Finder anzeigen" -- in every other
catalogue both commands already share one string;
- "Track" as a verb is "verfolgen" in the other four entries, so `track.mask`
is "Maske verfolgen" rather than "Maske tracken";
- one spelling of "nach vorne", matching the neighbouring arrange entry.
German also had no platform variants for the file manager. The engine puts the
platform's name into the label before the catalogue sees it, so on Windows the
source is "Reveal in Explorer" and on Linux "Reveal in File Manager"; only
Ukrainian and Czech special-cased those, and a German UI showed the English
string. Added, with a test mirroring `czech_…` and `ukrainian_…`.
Two of the reported six are left alone: "Essential Graphics" is a 3-3 split and
a panel's proper name, and "Null" partly mirrors the English, which itself says
both "Null Object" and "Nulls". Both are translator's choices with no majority
to follow, not corrections.
`resolve_output` takes the project's folder from `Path::new(path).parent()`.
For a project saved under a bare filename that is the empty path, which the
following `filter` discarded, so a saved project fell through to the
unsaved-project default and relative outputs went to ~/Movies (the user profile
folder on Windows) instead of next to the project.
--project proj.ecproj ~/Movies/clip.mp4
--project ./proj.ecproj ./clip.mp4
Both spellings name a project in the working directory, so both now resolve
there, as the doc comment on `resolve_output` already describes.
Closes#1427
Follow up on #1210: remove the fixed menu index from automation and click handling, and move the preset-folder documentation into the Effects & Presets section.
A layer selected by another layer's effect (Displacement Map, Blend...) had
its structure, values, matrix and time hashed into the owner's key, but not
the comp camera/lights its own scene-reading effects (Caustics, CC Particle
World...) see. The owner's key included the scene only when the owner itself
ran such an effect, and a cached owner never runs the selected layer's
effects, so moving the camera or a light reused stale owner pixels.
Dependencies::layer now hashes comp_scene for every selected (or nested)
layer that reads it, with the same policy as owner keys.
Fixes#1252
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kwMcw2pYQ5fSjKa3ZPnh
effectcraft-cli read its command line with std::env::args, which panics
on an argument that is not valid Unicode (a Latin-1 file name on Linux,
for example). `effectcraft-cli $'\xff'` or
`effectcraft-cli render-frame --out $'caf\xe9.png'` printed a panic from
std's env.rs and exited 101.
It now reads std::env::args_os and refuses such an argument as a usage
error (exit 2) that names it, before anything runs, like an unknown
option. A lossy copy is not used: every argument is a string here, and
`caf\u{fffd}.png` would read or write another file than the one named.
FilmCraft moved its CLI to args_os too (storytold/filmcraft#850).
Tests: a unit test for the conversion and a process test that runs the
CLI with a non-Unicode `--out` path (both unix-only). Before the fix the
process test fails (exit 101, not 2).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
makehybrid -hfs gave every file in the image a com.apple.FinderInfo, so the app in the
DMG, and a copy installed from it, failed codesign --verify --strict --deep ("resource
fork, Finder information, or similar detritus not allowed"), though Gatekeeper accepts
it. Build the image as VectorCraft (#1085) and PdfCraft (#823) do now: hdiutil create
-srcfolder with a few tries for CI's "Resource busy", and verify the mounted app with
--strict --deep so the regression can't ship again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ReferenceError's first use was searched across the whole file, so a mention
earlier in a comment, a string or another function could report the wrong line.
The search now starts at the innermost script frame's line, and covers the whole
file only when there is no frame. Follow-up to storytold/effectcraft#1161.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
In After Effects solo is not time-dependent: while any AV layer is soloed,
non-soloed layers are excluded at every time. The renderer, nested/collapsed
3D paths, engine viewer helpers and viewer hit tests only counted solo layers
active at the current time, so outside the soloed layer's in-out range the
whole comp reappeared. Drop the time condition (audio already ignored it).
Update the #66 viewer regression test to the AE behaviour and add a render
test for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>