Three places built file URLs by string concatenation: Edit Original
(`file://{path}`), the Reveal commands via folder_url (`file://{dir}`) and
File.execute (`file://` with only spaces replaced by %20). On Windows that
produced `file://C:\Users\...`, and any path containing `#`, `%`, `?` or
non-ASCII characters named the wrong file (or none).
Add layer_menu::file_url: backslashes become `/`, drive paths get the third
slash (`file:///C:/...`), and bytes outside the plain path characters are
percent-encoded (UTF-8 for non-ASCII). folder_url, edit_original and
execute_file use it.
Tests: file_urls_are_well_formed, folder_url_names_the_containing_folder,
edit_original_opens_an_encoded_file_url, execute_file_opens_an_encoded_file_url.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Masks are applied before effects, but the noise was composited with the
Opacity alpha alone, so a masked solid filled the whole layer. The result
is now multiplied by the original alpha on the CPU and on both GPU paths
(kernels.wgsl for the default settings, fx_noise.wgsl for the rest).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Dragging the in or out edge of a selected layer's bar now trims every
selected layer by the same delta from its own in/out point, as in After
Effects. layer.timing gains relative inDelta/outDelta params (seconds,
frame-snapped), clamped to the comp start/end and to one frame of layer;
the drag stays one undo step via the merge key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in After Effects, the Preset menu in Composition Settings / New Composition gets a Save
button (asks for the name in a Choose Name window; an existing name of yours is replaced) and a
Delete button for the selected preset of yours. Your presets keep frame size, pixel aspect and
frame rate, are listed above the built-in ones and live in the settings (`compPresets`), so they
survive restarts and Reset All. Commands: `comp.savePreset`, `comp.deletePreset`, `comp.presets`.
A hand-edited settings file loses only its invalid presets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicking a text layer in the Timeline (its name or its bar) edits its text in the
Composition viewer with the whole text selected, as in After Effects; it used to open the rename
field (Enter still renames). The layer bar had no double-click at all: it now opens a precomp,
opens footage and solids in the Layer panel and edits text, like the name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Go to Time dialog's Frame field sends a float (5.0); time.set read
`frame` with as_i64, got None and failed with "need time, frame or
timecode". Read it as any JSON number and round.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unsaved Changes: Enter is Save, Cmd+Backspace is Don't Save.
Revert Project: Cmd+R is Revert (the button is now red).
Delete Items: Cmd+D is Delete.
Crash recovery: Enter is Open Auto-Save.
Marker: Enter is OK, Cmd+Backspace is Delete (from a focused text field too).
The macOS menu bar hands Cmd+D and Cmd+R back to the open prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Alt-click on a layer's Video, Audio, Solo or Lock switch sets that layer
apart: the other layers go off (or locked), and the next Alt-click brings
them back. Backed by layer.setSwitch {others: true}, one undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A launch from the Dock, the Start menu or a launcher has no terminal, and
without RUST_LOG nothing reached standard error anyway, so start-up and
GPU warnings were lost unless Help > Enable Logging was already on.
The logger now writes EffectCraft's info records and everyone's warnings
to standard error and to Logs/effectcraft.log in the settings folder.
Each launch keeps the previous two runs' logs; the file is capped at
16 MiB. RUST_LOG still overrides the filter. Help > Reveal Logging File
shows this run's log unless Enable Logging is on.
Ported from storytold/pdfcraft#239.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Magnification ▸ Fit up to 100% stored the magnification of the moment it
was picked, so the comp kept that size when the panel was maximized or
resized. It is now a fit mode like Fit, capped at 100%.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Selection tool's arrow is a concave outline, but icon fills go through
egui's convex-only polygon fill, which fanned slivers across its notches.
The arrow, and the likewise concave Speaker and Sparkle glyphs, are now drawn
as overlapping convex pieces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fanning_out_reads_stop_at_a_budget expects the run-count error, but under heavy load
the 5 s wall-clock budget could fire first (different error). Add a test-only
per-thread time limit and lift it in that test. Wall-clock assertions in the other
new tests are now only generous hang guards (>=10x), the fuzzer's hang deadline is
24x MAX_TIME and the perf bound is 10x looser.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Two follow-ups to ending text editing on a click in another panel.
A press anywhere in a panel group focused the group's shown panel, even
a press on another of its tabs. Opening Properties or Character behind
Effects & Presets (as in Undocked Panels) therefore ended the edit on
the press, before the tab's click focused the panel chosen, and later
formatting went to the whole layer instead of the selected characters
(an empty Type-tool layer was even removed). A press on another tab now
waits for that tab's click.
The click away is resolved after the frame's panels, so keys that came
after it in the same frame's input were still typed into the text. The
text keyboard handler now takes the frame's keys in order up to a press
outside the viewer: keys before the click are typed, keys after it are
neither typed nor run as shortcuts, and from the next frame they are
the app's.
Tests: opening_a_formatting_tab_keeps_the_text_selection (a real click
on the Properties tab keeps the edit and its selection; Faux Bold then
styles only "Hello") and keys_after_a_click_away_in_the_same_frame_skip_
the_text ("A", a click on a Timeline row, then "x", Space and Delete in
one frame: only "A" is typed, nothing is played or deleted; Space in the
next frame plays). Both fail on the previous commit.
With Fill or Stroke set to Linear or Radial Gradient, clicking its
toolbar swatch opened Fill / Stroke Options, so the gradient's colours
couldn't be edited from the toolbar as in After Effects (reported on
Discord, EffectCraft 0.8.0 on Windows 11). The swatch now opens the
Gradient Editor (prop.editGradient) on the selected shape layer's first
Gradient Fill or Gradient Stroke Colors, in contents order through nested
groups; OK sets that property. With no shape layer selected, or none
with that gradient, the swatch opens the options as before (the kind new
shapes get: the toolbar keeps no gradient stops of its own yet). The
words still open the options, a Solid Color swatch the colour picker,
and Alt-click still steps through the kinds.
Tests (ui_toolbar_gradient): a Linear Fill swatch opens the editor on the
layer's Gradient Fill Colors and OK saves an added stop; a Radial Stroke
swatch edits the Gradient Stroke, not the fill; without a selected
gradient the options open; the words, Solid swatches and Alt-click are
unchanged. The two gradient-swatch tests fail without this change.
Shape layers are always continuously rasterised, as in After Effects and
as text layers already are here: the contents are drawn at the layer's
effective scale (own transform and parents) whether or not the
Continuously Rasterize switch is on, so scaled-up shapes stay sharp.
Fixes#751
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>