Commit Graph
2206 Commits
Author SHA1 Message Date
Kristian MyramandClaude Haiku 5.5 fdc515f5be #1063: find an undefined name's first use from the failing frame on
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>
2026-10-13 21:58:52 +02:00
tylerjenningsw a37357f082 Docs: plugins.md notes that Ease Presets skips roving keyframes (#1329) 2026-10-13 15:04:19 -04:00
tylerjenningsw 4dddb34cba Ease Presets: tests for Out alone through undo and redo, and a single key on a curved path (#1329)
Out on an auto-Bezier key leaves its in side playing the same values,
and undo and redo restore each state. On a curved Position path with
unequal segment lengths and durations, In sets the key's speed from the
incoming path length, playback arrives faster than a constant speed, and
the out side plays as before.
2026-10-13 15:04:19 -04:00
tylerjenningsw 9b74dba2a8 Ease Presets: skip roving keyframes and say so, rather than reporting them eased (#1329)
A roving key passes speed straight through: the engine keeps both its
sides linear whenever the keys are edited (retime_roving), so an ease set
on one never plays. The panel still counted it as eased, so a selected
roving key showed "Eased 1 keyframe(s)" and stayed linear. The previous
pair-only panel did the same to a pair holding a roving key.

Selected roving keys are now left out of the work and counted. The
status names how many were skipped and that turning off Roving lets them
ease; when nothing else was selected, nothing changes and no undo step
opens. Fixed keys next to a roving key still ease.
2026-10-13 15:04:19 -04:00
ThemolxandClaude Opus 5.5 f62af4ca73 Guide layers: hidden in nested comps, and in the Timeline context menu, as in After Effects
Guide layers render only in their own comp's viewer: a nested renderer (precomp, collapsed
precomp in 2D, Classic 3D and Advanced 3D, comps read by effects, aux passes, the GPU walk's
resident precomps) now runs with guides off (Renderer::nested_opts), so a precomp's guide
layers no longer show in the parent comp's viewer or in a "Current Settings" render.

The Timeline layer and Composition viewer right-click menus (shared layer_menu) gain a
Guide Layer item (a plain toggle, no check mark) acting on the selection, and guide layers show a guide mark
before their name in the Timeline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:44:40 +02:00
ThemolxandClaude Opus 5.5 03b7a958e5 Solo: exclude other layers even when the soloed layer is outside its time range
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>
2026-10-13 20:30:26 +02:00
tylerjenningsw c0f919a858 Docs: plugins.md describes the Ease Presets In, Out and In-Out sides (#1329) 2026-10-13 14:28:50 -04:00
tylerjenningsw af486dc44a Ease Presets: tests for easing single keyframes In, Out and In-Out (#1329)
One middle key between unequal segments in each mode, with its
neighbours, times, values and selection unchanged and one undo restoring
it; first and last keys, keys that aren't neighbours and keys of several
properties; no undo step when nothing can ease; a Hold and an
auto-Bezier side kept as they play; per-dimension and motion-path
speeds on Scale and Position; and the pair's outer sides under In-Out
and Out. All but the unchanged API tests fail on main.
2026-10-13 14:28:50 -04:00
tylerjenningsw 3a953859f2 Ease Presets: ease the incoming, outgoing or both sides of selected keyframes (#1329)
The panel eased only pairs of neighbouring selected keyframes, so a single
selected key (or keys that aren't neighbours) changed nothing.

An Ease selector (In, Out, In-Out; In-Out by default) now chooses the
sides of each selected key to ease. In eases a key's incoming side with
the curve's in side, Out its outgoing side with the curve's out side. A
side is eased when it has a neighbouring key, selected or not; the
neighbour is left as it is. Each side's speeds scale to the segment on
that side, so a key between a +100/s and a -40/s segment gets each its
own. The side not eased keeps playing as it did (easeSide, unchanged).

Sides are measured before any key changes, and no undo step opens when
no selected key has a segment on the chosen side; the status says which
neighbour is missing. A selected pair in In-Out still gets the whole
curve; its outer sides now ease too when they face an unselected key.
From Keys still reads the curve of a selected pair.
2026-10-13 14:28:49 -04:00
Moein Saharkhiz e30c093654 #1303: reject corrupt and incomplete benchmark evidence 2026-10-13 14:13:54 -04:00
ThemolxandClaude Opus 5.5 a15a7a4818 Null layers: the Video switch hides them in the Composition viewer, as in After Effects
Every layer gets the Video switch (eye) in the Timeline (nulls, cameras and lights
were excluded by an is_av() check). A selected layer whose eye is off no longer
draws its box, handles, anchor or paths in the viewer. Alt-click on an eye and
Layer > Switches > Hide Other Video now take nulls along.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:04:34 +02:00
sanyamsingh001andCodex 36ebe2eada Honor duration rounding in timeToFrames
Round nonzero duration frame counts away from zero while retaining
absolute-time rounding, default argument selection and numeric coercion.

Cover fractional durations, absolute controls, defaults and coercion.
Sort expected paths in the existing footage test for temporary-directory portability.

Co-authored-by: Codex <codex@openai.com>
2026-10-14 03:21:23 +10:00
Lloyd NicholsonandClaude Opus 5.5 d309d6f1d0 Effects & Presets: drag animation presets onto a layer
Preset rows (text, built-in and user .ecpreset) were click-only, so dragging
one onto a layer did nothing. They now start a DragPayload::Preset drag with
a ghost, and the Timeline, Composition viewer and Effect Controls apply the
preset to the layer it is dropped on, as they already do for effects and as
After Effects does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:04:33 +02:00
DWAA1660andClaude Opus 5.5 76a3e7eefe Masks: addVertex and setVertex fill in missing tangents of a loaded path (#1250)
A path's vertices and its two tangent arrays deserialize independently,
and nothing on load makes them agree. mask.new pads them, but a path
read from a project file may carry fewer tangents than vertices.
mask.addVertex checked its index against the vertices only and then
inserted into both tangent arrays, and mask.setVertex indexed the
tangent arrays after the same check, so either command panicked on
such a path (Vec insert/index out of bounds).

ShapePath::fit_tangents gives both arrays one entry per vertex (missing
tangents become corners, extras are dropped), and edit_path_counted
calls it on the value it edits, so every mask/shape-path vertex command
works on a consistent path. Other keyframes of an animated path were
already padded by apply_count_op when the vertex count changes.

Tests: two regression tests save a project, cut every mask path's
tangent arrays to one entry (static value and keyframes), reopen it
through Project::from_json and run setVertex and addVertex. Both panic
at mask.rs:209 without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:04:02 -06:00
zitann2020andDeepSeek V4.1 Flash 5569aad8b5 i18n(ja): four labels After Effects contradicts
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
2026-10-14 01:57:37 +09:00
Ian-ChegeandClaude Opus 5.5 5270eab095 Masks: Mask Expansion past a curve's radius grows a solid mask instead of a ring
Expansion was a fill plus a stroke 2·|expansion| wide, blended per pixel. Once the
expansion passed the shape's curvature radius the stroke's inner edge inverted: a
20 px circle expanded by 60 left a ring with the layer showing through the middle,
+600 cut only the original dot, and negative values left islands. The outline is
now offset with Offset Paths' geometry (round joins) and filled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:15:30 +03:00
sanyamsingh001andCodex e52ce29579 Preserve CLI template inputs when output is a hard link
Compare device and inode on Linux alongside existing path checks. Cover
template and values aliases with input-preservation and diagnostic checks.
Sort the existing footage test's expected paths for temporary-directory portability.

Co-authored-by: Codex <codex@openai.com>
2026-10-14 02:14:42 +10:00
MarekSpodBiedry 9050b1818e Scripting: add the missing app.preferences.saveToDisk() so imported scripts like Duik get past startup (#1263) 2026-10-13 18:02:11 +02:00
zitann2020andDeepSeek V4.1 Flash 2ff7639219 i18n(ja): the resolution labels agree with the panel catalog and After Effects
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
2026-10-14 00:57:21 +09:00
Vikram Bhaskaran d15c82b163 Liquify: expose Distortion Mesh Offset in Effect Controls (#1194)
After Effects' Liquify shows Distortion ▸ Offset (x, y), and the renderer
already applied distortionMeshOffset — sampling the mesh at layer
coordinates minus the offset — but the parameter was declared hidden, so
users could neither see nor edit it, and #1194 read as 'the effect does
not work and the Offset option is missing'.

It is now a visible 2D Point control (still defaulting to 0,0, still
scriptable by its old path). Regression test: the spec exposes it as a
Point, a [20,0] offset moves the displaced-region centroid right by the
offset (the render differs, and the left of the shifted mesh is back to
untouched pixels), docs/effects.md refreshed via UPDATE_DOCS.
2026-10-14 02:46:26 +11:00
Vikram Bhaskaran bde6eb57f3 UI: Spacebar previews the demo opened from the start screen (#1059)
The reported flow — launch to the start screen, Open Demo Project, press
Space — does not reproduce on main: the demo comp activates, the start
screen closes, and playback starts. The behaviour already held (the
#279 menu-focus and #204 start-screen work fixed the neighbouring bugs),
so this lands as a regression test:
space_previews_after_opening_the_demo_from_home walks the exact flow
headlessly and asserts playing=true.

cargo test -p effectcraft-ui-egui --test ui_menus: 8 passed.
2026-10-14 02:20:07 +11:00
Vikram Bhaskaran f12d75e37a Engine: Interpret Footage loop coverage for sequences (#1068)
Looped *video* footage had a regression (#1045); a grouped PNG sequence
going through the same path — interpret a fresh placeholder to Loop 15,
drag it onto the timeline — did not. The behaviour is already correct on
main (addItem sizes a sequence layer from looped_duration, the pool's
frame_index wraps per cycle), so this is coverage, not a change:
interpret_footage_loop_extends_sequence_layers asserts a 15-loop of a
0.4 s sequence lands a 6 s layer.

cargo test -p effectcraft-engine --lib interpret_footage: 4 passed.
2026-10-14 02:15:32 +11:00
Vikram Bhaskaran 4ddf5ffd6f CC Particle World: motion blur smears particles across the shutter (#1195)
With the comp's and the layer's motion-blur switches on, the renderer
hands effects the shutter (angle, phase, samples) — CC Particle World
now uses it: eight to sixty-four sprite fields sampled across the shutter
are averaged into the transfer, so fast particles draw the motion trails
After Effects shows instead of one sharp frozen field.

The GPU particle pass defers to the CPU while the shutter is open
(catalog::gpu_supported), keeping the oracle identical on both backends.

Tests: sim::particle_world_motion_blur_trails (blurred differs from
sharp, lights more pixels, keeps alpha energy) and a catalog assertion
for the CPU gate; effects 450 and gpu 212 lib tests pass; fmt and
clippy -D warnings clean.
2026-10-14 02:01:30 +11:00
dotCooCoo 1cc315708e Find Missing Fonts: fonts found by their PostScript or full name are no longer listed
File > Dependencies > Find Missing Fonts listed a text layer whenever
its font matched no family name, while text layout finds a font by its
family, PostScript or full name (effectcraft_text::resolve). For a
layer set to the PostScript name Inter-Bold, EffectCraft draws the text
in Inter Bold, and text.fontFeatures reports the layer's font as not
missing. Find Missing Fonts listed that layer and a layer set to the
full name Inter SemiBold.

find_missing now lists a layer when effectcraft_text::resolve, which
text layout uses for each run, reports the layer's font as missing.

fonts_named_by_their_postscript_or_full_name_are_not_missing adds text
layers in Noto Serif, Inter-Bold, Inter SemiBold and a font that no face
has, and checks that Find Missing Fonts lists only the last one.
2026-10-13 07:43:21 -07:00
zitann2020 9ab33d9758 i18n(ja): the first rows of the Japanese effect catalog (#821) 2026-10-13 23:33:38 +09:00
Vikram Bhaskaran 48738093b5 Shapes: Merge Paths keeps paint that belongs to a group above it (#1174)
A Merge Paths item appended to a shape layer's Contents cleared every draw
collected so far, including fills and strokes inside groups above it, so
the layer's shapes vanished from the render. AE's rule is per group: only
paint items in the merge's own group above it are dropped. Draws now
record which contents level created them; the merge drops only its own
group's paint, and the group-bubbled draws keep rendering.

Adds a regression test (Merge Shapes and Add modes) that renders nothing
without the fix.
2026-10-14 00:02:12 +11:00
Vikram Bhaskaran 2720ae5fe2 Wave Warp: cover the Top/Bottom edge pinning paths end to end (#1181)
The pinning regression only exercised All Edges (and Left Edge in the CPU
unit test), so the reported 'does not work with Top/Bottom Edge' had no
coverage to catch or clear it. This holds the pinned first/last row in a
CPU test at Direction 90, and adds CPU/GPU parity cases for Top (4),
Bottom (6), diagonal directions, and single-edge pinning on shape layers.
CPU pinning verified correct on main: the guards in dst_wave_pin/wave_pin
agree; the gap was that neither edge path was tested.
2026-10-13 23:40:32 +11:00
Jannik Wappler dbc595b0d6 #1220: Hide layer bounds during path editing 2026-10-13 15:38:06 +03:00
vangel76andClaude Sonnet 5.5 89ae151c03 #1221: two path-format test failures on Windows
Problem: on Windows two tests failed on main although the app is fine; they assumed
macOS/Linux path formats.

Fix: the CLI test strips the verbatim path prefix that canonicalize adds on Windows (a
child's current_dir() never reports it); the host sink test uses a drive-letter path on
Windows (a path rooted without a drive is joined to the home drive by the engine, the test
expected the current drive). Test code only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 14:25:42 +02:00
arnav-hiwarkar bb1a0e5209 perf(media-browser): reuse listings and cache filename sort keys 2026-10-13 17:42:07 +05:30
pravdomilandClaude Opus 5.5 07168cbf22 Settings: the page list's rows touch, with no gap between them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:07:19 +02:00
arnav-hiwarkar da06216475 perf(presets): reuse cached text and user preset data 2026-10-13 16:48:19 +05:30
Moein Saharkhiz 2ddbf20466 #1070: refresh layered composition sources on import 2026-10-13 06:16:07 -04:00
Kristian Myram 037a21d5a6 #1200: allow TGA proxies and add import regressions 2026-10-13 10:09:51 +00:00
Kristian Myram 058da6f3b0 CI: use a match guard in the STL reader 2026-10-13 10:09:51 +00:00
arnav-hiwarkar b63efeb0d3 perf(project): read file sizes once per sort 2026-10-13 15:12:32 +05:30
Brandon ThomasandClaude Opus 5.5 549e9d8251 Footage: the missing-footage toast names the real menu item, Relink Missing Footage… (#1106)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 05:34:22 -04:00
Brandon Thomas 7a9213611e Merge pull request #1106 from eliasx45/upstream/relink-moved-footage
# Conflicts:
#	docs/footage.md
2026-10-13 05:34:14 -04:00
Brandon ThomasandClaude Opus 5.5 91ee7bf1b3 cargo fmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 05:23:52 -04:00
Brandon ThomasandClaude Opus 5.5 6c799f3c3b Batch fixes: tests and catalogs that meet each other once the batch lands together
- Keyboard Shortcuts: Enter is not Delete while the delete confirmation is open (#1113 with #1019).
- Speed graph drag test compares proportions, since the Graph Editor now fits its selection (#1182 with #1109).
- Spanish panel test no longer demands the Chinese catalogs' exact rows (#996 with #1189, #1019).
- Object Matte gets its Simplified Chinese effect-name row (#1199 with #792).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 05:23:30 -04:00
Brandon ThomasandClaude Opus 5.5 3ac283eee4 Review fixes for the landed batch
- Cryptomatte (#1183): planes shorter than the image read as empty instead of panicking.
- layer.addMarker (#1180): a comp marker no longer replaces one at the same time.
- Desktop (#1005): ignore macOS's -psn_ argument instead of refusing to launch.
- Doc comments put back on their own items (#1153, #1037).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:47:52 -04:00
Brandon ThomasandClaude Opus 5.5 900b921dbf PSD: the optimisation tests use the reference decoder once #1137 and #1139 land together
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:47:52 -04:00
Brandon ThomasandClaude Opus 5.5 eb2b734059 PSD: take the integration branch's adapted non-finite test and psd_import formatting (#1149)
The six PSD PRs merged one by one leave #1131's test calling samples()/to_rgb(), which #1139
replaced; vangel76's integration draft #1149 has the adapted version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:47:27 -04:00
vangel76andClaude Sonnet 5.5 02c412b835 PSD: release_bytes takes the buffer by value (clippy ptr_arg)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 04:47:27 -04:00
vangel76andClaude Sonnet 5.5 5f4b3aee86 PSD integration test: deep groups + NaN text, damaged layer section, huge header under a small budget, on a 2 MiB stack
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 04:47:27 -04:00
Brandon ThomasandClaude Opus 5.5 f2d481ce20 Tracker: the 1080p timing test takes the fastest step, so a loaded FreeBSD VM doesn't fail it
The nightly FreeBSD run measured 119 ms/frame against a 100 ms limit: the mean included
Tracker::new and any scheduling stalls on the two-core VM. The best single step with a
250 ms bound still catches an order-of-magnitude regression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:47:27 -04:00
Brandon ThomasandClaude Opus 5.5 c5bb5f4397 docs: move #1104's project import notes from a stray parity-log.md into footage.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:35:32 -04:00
Brandon ThomasandClaude Opus 5.5 1b4ea03fd2 i18n: a group's translated effect name also draws when it isn't being renamed (#1199 with #1075)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:35:32 -04:00
Brandon Thomas acb77a8c85 Merge pull request #1210 from arnav-hiwarkar/feat/copy-presets-folder-path 2026-10-13 04:35:21 -04:00
Brandon Thomas bb4972a886 Merge pull request #1160 from nanox333/fix/1063-levels-alpha-name
# Conflicts:
#	crates/script/src/tests.rs
2026-10-13 04:35:20 -04:00