Commit Graph
901 Commits
Author SHA1 Message Date
Christopher Linnett 0558c9427c fix: end an open text edit when Deselect All runs (#2096)
While a text layer was being edited, Edit > Deselect All and its shortcuts
selected every character (or did nothing) instead of ending the edit and
deselecting the layer.

- deselect_all now commits the open edit first, through the same route as
  text.endEdit: a created-but-empty type-tool layer is discarded, anything
  typed is kept, and no extra undo step is introduced.
- While the in-canvas editor holds the keyboard, the editor forwards the
  command's currently assigned shortcut (and any function-key chord) so
  Deselect All reaches the command from the comp; bare-letter bindings stay
  typing, and Select All still selects characters.
- The Project-panel focus redirects (invocation and menu enabled-ness) stand
  down while a text edit is open, so the menu and shortcut end the edit even
  when focus stayed on Project after Layer > New > Text.

Tests: three engine cases (commit + discard semantics), six UI kittest cases
(chords, F2, rebinding, menu enabled-ness, Project focus), RED at base.
2026-10-23 23:23:59 +08:00
heojunfoandClaude Opus 5.5 058fe9a005 Reload a numbered still as its own file (#2074)
Reload Footage probed every item the way File ▸ Import does by default,
so a still such as Photo_2.jpg with Photo_1.jpg beside it was grouped
into a run and measured from the run's first file. The still took
Photo_1's size. Sequence items still probe as a run; everything else
reads its one file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:21:35 +09:00
Marco Arnulfo 952b04322e Project Settings: Frame Count, Start at 0 or Start at 1 (part of #1807)
file.projectSettings takes frameStart (0|1), the Project Settings dialog shows Frame Count, and scripts get app.project.framesCountType and displayStartFrame. The setting was already read by the Frames and Feet + Frames time display; nothing could change it.
2026-10-23 03:07:01 +02:00
Markus Ang 8ac7b3f508 #2135: reject non-ASCII thumbnail text safely 2026-10-22 19:29:42 -04:00
Anubhav-RaiandClaude Opus 5 6e3b5eb3e3 engine: deleting a layer clears the references to it (#2153)
Nothing cleared a `Value::Layer` reference when its layer was deleted, so the id stayed behind:
`env_pixels` resolved it to nothing and rendered no environment at all, while the UI showed "None",
which is documented as "fall back to the comp's Environment Layer". The delete path already
unparents children and drops a track matte that pointed at a deleted layer; a layer reference now
goes the same way, in the same history step, so undo restores the layer and the reference together.

This covers every layer-reference property, not only an Environment light's Source: 73 of them
across the effects (Clone Source, Set Matte, Back Layer, Water Surface, Sky, Ground…). Keyframes
carry the same value kind as the property, as in `essential.rs`, and are fixed too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-23 04:41:11 +05:30
Christopher Linnett d2fae0a144 fix: bound Classic 3D depth-of-field blur and reject non-finite focus distances (#2092)
The camera's Set Focus Distance to Layer command compared only 'd <= 0.0', which NaN passes, and two finite but extreme positions can overflow their difference to infinity; a stored non-finite focus distance serializes as null and leaves the project unsavable. Reject it, fall back to the zoom plane in the camera evaluation, and count a non-finite focus or depth as in focus. The Classic 3D blur radius itself was unbounded: a 0%-scaled or very distant layer scales it by up to 1e9, and the padded blur buffer sized from it aborts the process. Cap each plane's radius to a padded buffer that fits the renderer's 16 Mpx layer budget, don't blur planes whose pixels cover under a thousandth of a comp pixel, saturate the padded sizes, and refuse padded buffers past the project's maximum layer size. Regression tests for each guard.
2026-10-22 21:57:41 +08:00
skylar taylor-barrick b55cb5bc15 Merge pull request #1363 from DWAA1660/fix/1237-comp-contains-cycles
Project: comp_contains walks precomps with a visited set, not recursion (#1237)
2026-10-22 07:52:56 -06:00
Madhav Agarwal 7fe0513894 engine: split layers above the original by default (#2083) 2026-10-22 19:13:36 +05:30
Madhav Agarwal 963f489eab engine: resolve effect match-name paths in prop_ref (#2064) 2026-10-22 18:47:59 +05:30
Madhav Agarwal 3f4ffdc0fc engine: a keyframe on the out point no longer warns (#2084) 2026-10-22 18:41:26 +05:30
tylerjenningsw f14a57fb3e 3D: Layer > New > 3D Primitive... dialog for a primitive's type, name and dimensions (#1734)
OK runs layer.newPrimitive, which switches a Classic 3D comp to Advanced 3D in the same undo step. The dimension fields are the Geometry Options the engine builds for the chosen type.
2026-10-21 13:31:58 -04:00
Nikolai Nyegaard c839ae7598 Settings: list the languages alphabetically by their own name
- English (and Auto where offered) stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones
- The list used the registry order, which is the order the languages were added
2026-10-21 12:01:47 +02:00
tylerjenningsw cfb762b114 Clipboard: effect parameters After Effects numbers or labels otherwise follow it in keyframe text (#1908)
A table of After Effects' index and unit for the effect parameters its keyframe text gives differently from EffectCraft's rules, read from After Effects 2019's text for 230 effects. All 1,530 parameters the two share now match; 949 did before.
2026-10-20 13:41:34 -04:00
tylerjenningsw 5556b58050 Clipboard: effect sliders of -100 to 100 have no unit in keyframe text, as in After Effects (#1908) 2026-10-20 11:12:52 -04:00
tylerjenningsw f9d6c0801b Clipboard: keyframe text for shape contents and camera, light and material options (#1908) 2026-10-20 10:57:52 -04:00
tylerjenningsw c5f028b5cd Clipboard: test Fractal Noise's indices after Evolution Options (#1908) 2026-10-20 10:36:14 -04:00
tylerjenningsw b40c1aff22 Clipboard: keyframe text gives a text layer's source as 1 x 1, as After Effects does (#1908) 2026-10-20 10:26:10 -04:00
tylerjenningsw dac6421ec3 Clipboard: effect properties in twirl-down groups, numbered as After Effects does (#1908) 2026-10-20 10:23:52 -04:00
tylerjenningsw 05f2daa4ca Clipboard: keyframe text for text layer properties, and percent sliders labelled percent (#1908) 2026-10-20 09:57:40 -04:00
tylerjenningsw 6cad56b19d Clipboard: keyframe text for mask properties (#1908) 2026-10-20 09:53:28 -04:00
tylerjenningsw 39142e9e6b Clipboard: keyframe text for effect properties (#1908) 2026-10-20 09:49:25 -04:00
tylerjenningsw 9ee6c5ef62 Clipboard: keyframe text for Time Remap and separated X / Y / Z Position (#1908) 2026-10-20 09:45:51 -04:00
tylerjenningsw 30dc57f790 Clipboard: keyframe text in current After Effects' layout, with a tab after each column (#1908) 2026-10-20 09:42:08 -04:00
dedandClaude Opus 5.5 49ec37ec3f Disk cache: an edit that leaves the footage alone keeps its salt (#1926)
Session::bump took the disk cache's footage salt at every edit, which reads the size and time of
every footage file, each frame of an image sequence included. With three 5000-frame sequences a
caret move or a typed character in a text field took ~0.6 s on the UI thread (9 ms with the disk
cache off). The salt is now taken again only when the footage items changed, or when a project
is opened, footage is reloaded or the disk cache attached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:40:24 +03:00
tylerjenningsw 8ace9aac7f Clipboard: keyframe text starts the layer with a Layer line (#1908) 2026-10-20 09:10:29 -04:00
tylerjenningsw 47093d997a Clipboard: keyframe text has the source's pixel aspect (#1908) 2026-10-20 08:52:55 -04:00
tylerjenningsw 22ddf7bb99 Clipboard: keyframe text for a 2D layer writes the Z it shows (#1908) 2026-10-20 08:51:09 -04:00
tylerjenningsw 6b77e6a1a7 Clipboard: keyframe text leaves out Transform properties the layer doesn't use (#1908) 2026-10-20 08:49:33 -04:00
tylerjenningsw 9313784304 Clipboard: Orientation and Rotation headers as After Effects writes them (#1908) 2026-10-20 08:47:51 -04:00
Camille Moine c7eb48dc3a Layer: reveal layer sources in the Project panel (#1809) 2026-10-20 13:59:01 +02:00
Camille Moine 8d887fd6da Layer: replace a layer's source with Alt-drag and Ctrl+Alt+/ (#991, #813)
Alt/Option-drag a Project item onto a Timeline layer, or press Ctrl+Alt+/ (Cmd+Opt+/) with layers and an item selected, to swap the layer's source. The layer keeps its name, timing, transforms, keyframes, expressions, effects, masks, switches, parenting and track matte; a source with audio adds Audio properties when missing. Precompositions can't be nested inside themselves.

AVLayer.replaceSource now runs the same engine command and closes the replaceSource part of #813.
2026-10-20 12:18:46 +02:00
tylerjenningsw 598f0d51c1 Clipboard: copied keyframes as After Effects keyframe text (#1908)
Copying keys or unanimated properties of one layer records them as After Effects keyframe text: its Transform properties, one row per key with the frame in comp time (start offset and stretch applied) or a frameless row for a static value. EffectCraft's own keyframe clipboard is unchanged.
2026-10-19 22:49:41 -04:00
vangel76andClaude Sonnet 5.5 67ccf44fba Review: behavior preset test also covers redo
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 01:38:45 +02:00
vangel76andClaude Opus 5.5 d57b73d4cb Shape rectangles start at the top-right, as After Effects, so Trim Paths cut the same segment
After Effects draws a Rectangle Path clockwise from its top-right corner, or
with round corners from the end of the top-right arc on the right edge;
Trim Paths measures from there (AE 2026, case 930: 400 x 300 rectangles,
Trim 0-25 % and 70 / 30). EffectCraft built the path with kurbo, starting at
the top-left corner, so trimmed rectangles drew another segment: case 930
was 86 of 5184 grid points off (max 255), now 5 at stroke edges. Start > End
was already drawn swapped, as in AE. Convert to Bezier now gives AE's vertex
order (top-right first); the engine test follows. Lottie rectangles share
the path and now start where lottie players start them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:33:44 +02:00
Camille Moine f72e17a2bb Engine: Video's Alt-click and Hide Other Video take cameras and lights along
The Timeline draws the Video switch (the eye) on every layer's row, nulls,
cameras and lights included, but set_switch_others and hide_other_video
skipped cameras and lights: Alt-clicking the eye of a selected layer neither
set them apart with the selection nor hid them with the rest.

Both now use the rule the eye cell shows: every layer but track mattes.
2026-10-19 10:25:17 +02:00
Matej Kozlovský d74cb70938 Cryptomatte pick: snap to the effect's input buffer grid, not the preview scale
At preview scales from 0.75 to 1 footage keeps native pixels, a proxy sets
its own buffer scale at any render scale, a Preserve Resolution precomp is
drawn at full size, and earlier effects can pad or offset the buffer, so
snapping to the preview grid could pick a different ID than the rendered
matte. Use the scale and offset of the buffer the Cryptomatte effect
receives (layer_input, rendered once and then cached).

The pixel centre goes to an aux point through effectcraft_effects::aux_point,
which index_map_at now calls too, so the two round identically (a division
vs the reciprocal picked the other ID on a 104 px proxy of 1920 px footage).

An unknown effect uid errors "no such effect" and a layer without pixels
"this layer has no pixels", as effect.pickColor does.
2026-10-19 08:40:16 +02:00
Matej Kozlovský 76098665b0 Cryptomatte: effect.pickCryptomatte picks the ID under a point into the Selection (#473)
Engine part of viewer picking: replace / add / remove the top-coverage ID at a
layer-space point in one undo step, at the time the Selection was read. The
aux channels come from the render's own path (Renderer::layer_aux, shared with
the effect host; footage at its source time, 3D precomps) through a renderer
set up like Session::render (expressions, render prefs, layer cache). An
optional scale snaps the point to the pixel a preview at that resolution
samples. New effects::crypto_select quotes names and falls back to <hex> for
unnamed, wildcard-looking or ambiguous names. effect.cryptomatteAt is the
read-only hover query.
2026-10-19 06:10:27 +02:00
Vikram Bhaskaran d3fd2c58d7 Key browser preview caches on a file generation, and hash bytes only when that key misses.
Replacing a virtual file bumps a counter the UI lookup reads. The content hash runs on that miss, from a callback, so the frame path does not stat every footage item or hash on every store write.
2026-10-19 13:04:16 +11:00
vangel76andClaude Sonnet 5.5 24612b0eaf Merge upstream/main into gap-behaviors-presets
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 02:02:16 +02:00
Davit MatchakhelidzeandClaude Opus 5.5 5a069af3fd Merge i18n-improvements (main merged)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 02:58:18 +04:00
iannitramandClaude Opus 5.5 c1d411b249 Tests: serialise disk-cache-opening tests with the roto tests
The Roto/Object Matte segment store is one per process (roto::set_seg_store),
and every Session::configure_disk_cache that opens a cache takes it over. The
roto and disk-cache tests already serialise on hold_roto_cache_test_lock(), but
many other engine tests build a Session with a settings store, which opens the
disk cache at the platform folder and takes the store; once that session drops,
the weak store is dead and object_matte_mattes_persist_in_the_disk_cache sees
no disk writes. It failed 3 of 3 in the full parallel suite here.

configure_disk_cache now takes that lock under cfg(test) before opening a
cache, so every such test runs apart from the roto tests. Re-entry on the same
thread is a no-op. Product code is unchanged: only the app's one session ever
opens a disk cache, so users cannot hit the race.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:51:47 -06:00
Kushagra Kumar f7922a9fba test(engine): add undo and redo assertions for queued still frame export 2026-10-19 02:11:05 +05:30
Kushagra Kumar 791e6bb5eb fix(engine): preserve alpha channel when saving still PNG frames
Exporting still frames via comp.saveFrameAs previously flattened the
rendered frame against the composition background color, stripping
the alpha channel. Single still frames queued in the Render Queue also
defaulted to an RGB-only PNG sequence output module.

Preserve straight alpha by default in comp.saveFrameAs (while honoring
transparent: false if requested), and set TemplateSlot::Still to default
to 'PNG Sequence with Alpha'.

Fixes #1815
2026-10-19 01:59:48 +05:30
tylerjenningsw dd50e42486 Merge remote-tracking branch 'origin/main' into enhance/pickwhip-index-refs
# Conflicts:
#	crates/ui-egui/tests/ui_inline_edit.rs
2026-10-18 12:38:44 -04:00
Charles BaumerandClaude Opus 5.5 4fa31b5e5b Engine tests: clippy passes on Windows (Stills is Unix-only)
`Stills` is only used by the `#[cfg(unix)]` part of
`footage_on_other_computers_is_never_looked_at`. On Windows it was dead
code, so `cargo xtask ci` stopped at clippy (`-D warnings`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:12:55 +00:00
Claude bccbc666a2 #1417: WAV markers show on the audio layer
A WAV's markers were never read, so its audio layer showed none. The importer
now reads the file's cue points (`cue ` chunk named by `LIST/adtl` `labl` and
`note`, sized by `ltxt`) and Adobe's XMP markers (`_PMX` chunk, `xmpDM:markers`
entries as elements or attributes, in frames of the track's `xmpDM:frameRate`).
A marker present in both encodings is listed once. The reader is bounds-checked
and caps the marker count and XMP size.

- `Footage.markers` (optional in the file format) holds them at footage time; a
  layer made from the footage starts with them.
- Layer ▸ Markers ▸ Update Markers From Source also works on footage layers.
- Markers from a file have no label colour.

Tests: reader unit tests (cue, note, ltxt, both XMP forms, both encodings,
truncated and hostile files), a probe test, and an end-to-end test through the
fully wired session. Not checked against a WAV written by Adobe's tools.

Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 17:38:44 +02:00
Brandon Thomas 7804cfd223 Format merged community PRs 2026-10-18 09:37:06 -04:00
Brandon Thomas 10cf6690c4 Merge pull request #1730
# Conflicts:
#	crates/engine/src/commands/prop_groups.rs
2026-10-18 09:36:40 -04:00
Brandon Thomas a594abf4f7 Merge pull request #1652
# Conflicts:
#	docs/gaps.md
2026-10-18 09:35:51 -04:00
Brandon Thomas dfff3f9010 Merge pull request #1785 2026-10-18 09:35:35 -04:00