Commit Graph
1772 Commits
Author SHA1 Message Date
Brandon Thomas e02c60c08a Merge pull request #1131 from vangel76/fix-psd-nonfinite-and-panic-paths
# Conflicts:
#	crates/psd/src/tests.rs
2026-10-13 04:28:31 -04:00
Brandon Thomas 5c0ac48f31 Merge pull request #1123 from nanox333/fix/808-source-point-convert
# Conflicts:
#	crates/script/src/tests.rs
2026-10-13 04:28:30 -04:00
Brandon Thomas 7da7358504 Merge pull request #1102 from vangel76/feat-gap7-presets-backgrounds
# Conflicts:
#	crates/ui-egui/tests/ui_panels.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 04:28:30 -04:00
Brandon Thomas 39ef24af3a Merge pull request #1093 from moecr7/codex/fix-1063-text-line-breaks 2026-10-13 04:28:28 -04:00
Brandon Thomas 2a1a98006c Merge pull request #1086 from vangel76/fix-1070-stale-footage-cache 2026-10-13 04:28:27 -04:00
Brandon Thomas cbe710220a Merge pull request #1066 from zitann2020/i18n/ja-panels 2026-10-13 04:28:26 -04:00
Brandon Thomas 0e424b45f1 Merge pull request #1050 from pravdomil/claude/bind-existing-shortcuts 2026-10-13 04:28:25 -04:00
Brandon Thomas 61a6d95e51 Merge pull request #1030 from vbasky/fix/990-u-reveal-timeremap 2026-10-13 04:28:24 -04:00
Brandon Thomas c3644e59d4 Merge pull request #1019 from pravdomil/claude/confirm-unguarded-deletes
# Conflicts:
#	crates/ui-egui/src/panels/shortcut_editor.rs
2026-10-13 04:28:24 -04:00
Brandon Thomas 3484f91c3f Merge pull request #1009 from marcoarnulfo/fix/portable-mode 2026-10-13 04:28:22 -04:00
Brandon Thomas ad83f9ae1a Merge pull request #1114 from vangel76/docs-gaps-audit-2026-10-12
# Conflicts:
#	docs/ui-parity.md
2026-10-13 04:28:22 -04:00
Brandon Thomas 7293148c3c Merge pull request #1189 from marcoarnulfo/fix/settings-zh-hans 2026-10-13 04:27:33 -04:00
Brandon Thomas 7955ae81ce Merge pull request #1168 from nanox333/fix/marker-frame-identity 2026-10-13 04:27:33 -04:00
Brandon Thomas b77018ea8f Merge pull request #1161 from nanox333/fix/1063-undefined-name-location 2026-10-13 04:27:32 -04:00
Brandon Thomas d9f300f940 Merge pull request #1152 from elZanco/layer-menu-effects-submenu 2026-10-13 04:27:31 -04:00
Brandon Thomas 96a29bb2f0 Merge pull request #1129 from vangel76/fix-psd-nested-group-depth 2026-10-13 04:27:30 -04:00
Brandon Thomas a62d03130a Merge pull request #1122 from nanox333/fix/809-layer-source 2026-10-13 04:27:29 -04:00
Brandon Thomas 1b3c88dcc5 Merge pull request #1113 from pravdomil/claude/keyboard-shortcuts-overflow-e1e74e 2026-10-13 04:27:29 -04:00
Brandon Thomas 396231a9f8 Merge pull request #1101 from pravdomil/claude/form-dialog-fit 2026-10-13 04:27:28 -04:00
Brandon Thomas 936b9842d0 Merge pull request #1084 from webdevtodayjason/fix/svg-finite-sizes 2026-10-13 04:27:27 -04:00
Brandon Thomas 7f584f9ba8 Merge pull request #1065 from marcoarnulfo/fix/project-panel-comp-settings 2026-10-13 04:27:26 -04:00
Brandon Thomas 85bc2d220d Merge pull request #1048 from vbasky/fix/994-effect-param-interp 2026-10-13 04:27:25 -04:00
Brandon Thomas 69f416e3b0 Merge pull request #1038 from vbasky/fix/1010-camera-parent 2026-10-13 04:27:25 -04:00
Brandon Thomas c069a72928 Merge pull request #1029 from vbasky/fix/956-scale-unlink 2026-10-13 04:27:24 -04:00
Brandon Thomas 59bf40a5da Merge pull request #1018 from pravdomil/claude/gpu-dialog-enter-ok-209420 2026-10-13 04:27:23 -04:00
Brandon Thomas dcd46ca57d Merge pull request #1008 from marcoarnulfo/fix/render-toast-reason 2026-10-13 04:27:23 -04:00
redonion23andClaude Opus 5.5 004984d853 Scripting: footage proxies, replaceWithSequence and mainSource.conformFrameRate (#813)
FootageItem had no replaceWithSequence, setProxy, setProxyWithSequence or
setProxyToNone, useProxy and proxySource were fixed stubs, and mainSource
was a new object on every read with no conformFrameRate. They now run the
existing commands (file.replaceFootage, file.setProxy, file.useProxy,
file.setProxyNone, file.interpretFootage); the item info reports the proxy
and the conform/native frame rates, and mainSource is one live
FootageSource per FootageItem. replaceSource is not covered: it needs an
engine command that changes a layer's source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 05:59:10 +02:00
tylerjenningsw de0f573620 Layer Styles: the controls scroll both ways above the buttons (#1062)
Keeping OK and Cancel in view took away the outer scroll area that also
scrolled the dialog sideways, and the controls only scrolled up and
down. In a narrow window (960x600 at 200 % UI Scale leaves the controls
about 190 points next to the 200-point list) they ran past the dialog's
right edge with no way to reach them.

The controls now scroll both ways in the room left under their heading,
with no minimum scrolled size, so a small budget can't hide the end of
them; the list scrolls in its height the same way. Each control also
registers dialog.layerStyle.value.<style>/<path> next to its label's id.

Tests:
- layer_style_controls_stay_reachable_in_a_small_window: in a 960x600
  window at 200 %, and opened large then narrowed, Noise is scrolled to,
  painted whole and dragged; fails before (it lies past the window).
- layer_style_buttons_stay_in_view now checks both buttons at 100 % and
  200 %, each case in a fresh app with a real edit: OK keeps it, Cancel
  puts the project and its undo history back as before the dialog.
2026-10-12 22:42:53 -04:00
Marco Arnulfo 26b2451836 i18n: the Settings pages reach the Chinese catalogs (fixes #1178) 2026-10-13 04:41:07 +02:00
Marco Arnulfo 3aaefab270 UI: the interface symbols test checks the drawn glyph, not has_glyph
epaint's has_glyph compares face keys, so a glyph drawn by the replacement
face reads as missing. With no face in the stack covering egui's `◻`, Inter's
`?` becomes the replacement and Inter's `•` fails the check, which happens
once another test (the #1141 one) has loaded a system face without `◻`: the
fallback tries loaded faces first. Compare the drawn glyph with the
replacement glyph instead, as assert_drawn already does.
2026-10-13 04:17:16 +02:00
Marco Arnulfo 3db5aebb33 Graph editor: speed graph handles keep dragging once their key leaves the plot (fixes #1171) 2026-10-13 03:47:24 +02:00
tylerjenningsw bf911d18bc Layer Styles: OK and Cancel stay in view (#1062)
The Layer Style dialog's OK and Cancel came after the style list and
the style's controls in one scroll area, and at its usual size they
were laid out past the dialog's bottom edge: clipped, and a click on
them did nothing until the dialog was scrolled.

The dialog now uses above_footer: OK and Cancel sit at its bottom right
and the list and the controls scroll above them, also at 200 % UI
Scale.

Test: layer_style_buttons_stay_in_view (both buttons painted whole and
clicked, at 100 % and 200 %; fails before: Cancel isn't painted).
2026-10-12 21:24:43 -04:00
tylerjenningsw bcfb25ca93 Dialogs: share the buttons-first layout Settings uses
Settings keeps its buttons in view by laying them out first, at the
bottom of its body, and giving its pages the height left above them,
clipped to it. That layout is now dialogs::above_footer, so other
dialogs can keep their buttons in view the same way. Settings looks and
works as before.
2026-10-12 21:24:41 -04:00
redonion23andClaude Opus 5.5 f816cd5e38 Scripting: a stroke's Dashes use After Effects' match names (#815)
Dashes reported its children as `dash`, `gap`, `offset`, so
`property("ADBE Vector Stroke Dash 1")` was null and addProperty threw.
They now report ADBE Vector Stroke Dash 1-3 / Gap 1-3 / Offset.
addProperty with one of those names returns the existing property
(EffectCraft's Dashes always holds Dash 1, Gap 1 and Offset) or adds the
Dash/Gap 2 or 3 pair through shape.dashes.add. Lookups by the old ids
still work.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 03:04:12 +02:00
Kristian MyramandClaude Haiku 5.5 98796a29b8 #1063: the gradient ramp matches the CPU on the GPU, including transparent regions
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 02:48:46 +02:00
Kristian MyramandClaude Haiku 5.5 63e5068f7c #1169: blend the two samples around each time when a layer is stretched
Stretched layers copied the nearest source sample for each output sample, which adds error: at
48 kHz the worst error on a 440 Hz tone was 0.025, and 0.15 at 8 kHz. They now blend the two
source samples either side. Layers at 100% and 50% speed keep their exact samples.

The render tests cover a plain precomp, which mixes the same audio as its layers, and the stretch
error against the exact tone.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 02:36:16 +02:00
Kristian MyramandClaude Haiku 5.5 863a6aff6c #1167: frames: keep the viewer frames when only plain markers change
Adding a marker replaced the comp, so every cached frame stopped matching and the preview had to
re-render before its audio could carry on. Plain markers change no pixels; protected time markers
with a duration still do (Responsive Design). Markers count in full again when a project uses
expressions, which can read them.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 02:20:31 +02:00
Kristian MyramandClaude Haiku 5.5 9e8112499c #1167: test: a marker edit keeps the viewer frames (fails on main; reproduces the audio pause)
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 02:20:31 +02:00
Kristian MyramandClaude Haiku 5.5 5530560fb8 #1063: find a light's Casts Shadows option by its After Effects name
A script asks a light's options for "ADBE Casts Shadows", which returned null. The light's option
is named "Casts Shadows" here, so the lookup never matched. The name is now an alias for the
castsShadows property, and a regression test checks the lookup returns the property.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:53:35 +02:00
Kristian MyramandClaude Haiku 5.5 a26f1a3749 #551: drop the Shift+Space default from the preview parity gaps
Shift+Spacebar now plays every frame by default, so it is no longer a gap in docs/ui-parity.md.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:46:34 +02:00
Kristian MyramandClaude Haiku 5.5 64945f2ef9 #1063: report an undefined name at its first use inside a function wrapper
Inside a function wrapper such as (function () { ... })(), boa places a ReferenceError at the start of
the function, so a script's error was reported at the wrapper's first line rather than the line that
failed. The first use of the undefined name in the code is now the position, and boa's frames remain
the fallback. Thrown errors and object-model errors already report their own lines. A regression
test covers a thrown error, an undefined name, and an object-model error inside a wrapper.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:44:14 +02:00
Kristian MyramandClaude Haiku 5.5 8eece41c7e #1063: keep the Gradient Ramp inside the layer's alpha
The Gradient Ramp wrote alpha 1.0 to every pixel, so a shape layer's transparent area filled up with
the ramp and the layer's bounds showed instead of the shape. The ramp now uses the pixel's own alpha
on the CPU and in the GPU kernel, as the #939 fix does for the noise effects. A regression test
checks that transparent pixels stay transparent at two Blend With Original values.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:37:03 +02:00
redonion23andClaude Opus 5.5 b2cf845624 Scripting: key calls fetch only the key they need, so removeKey stays fast (#814)
Every key call (removeKey, keyTime, keyValue, setInterpolationTypeAtKey…)
looked its key up through `__keys()`, which serialised the whole key list,
eases and tangents included, on each call. Removing keys one by one was
therefore quadratic: about 100 ms per removeKey on a 2,000-key property.
The `keys` query now takes an optional `index` and returns just that key
with the count; `__key` uses it. removeKey on 2,000 keys drops from
103.7 ms to 0.4 ms (release, Windows 11 aarch64).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 01:35:49 +02:00
Kristian MyramandClaude Haiku 5.5 be99fecfdc #1010: recover a static Orientation's Y angle without losing precision near gimbal lock
uniform_parent_pose took the Y angle from asin, which loses precision within about 1e-6 degrees of
+-90. The Y angle now comes from atan2 on cos(Y), as the X and Z angles already do. The gimbal
branch and the principal-range output are unchanged.

A new test parents a static Orientation with Y 1e-6 degrees off gimbal lock under a uniform
parent. Its largest corner displacement was 3.3e-8 px before this change and 1.5e-14 px after.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:33:51 +02:00
tylerjenningsw bfb7507565 Timeline: a Quick Offset of layer bars ends on release off screen (#1012)
The layer bar Quick Offset was followed and ended in its row's code, and
rows out of view aren't drawn. Released with its row scrolled away, it
stayed active: the next drag of that bar, even a plain one, ran as a
Quick Offset from the old origin, into the old undo step.

Like the key drag, it now follows the pointer below the rows and ends
when the button is released, wherever the bar's row is.

Test: a_quick_offset_released_off_screen_ends (release with the row
scrolled away; a plain drag after it moves both layers together as its
own undo step; fails before).
2026-10-12 18:49:51 -04:00
tylerjenningsw e1c9c33fb1 Keyframes: a refused key drag step doesn't count towards the drag (#1012)
keys.quickOffset and keys.move stored the drag's new total before the
edit ran. When the edit refused the step (keys.quickOffset with a key
landing past the end of time), the project stayed as it was but the
total kept the refused delta, and with incremental steps (merge without
fromStart) the next step moved by it too.

The drag state is now stored only after the edit applies.

Test: a_refused_step_does_not_count_towards_the_drag (+1 s, a refused
step, -1 s: the keys end where they started; fails before: the last key
ends near the end of time).
2026-10-12 18:49:48 -04:00
tylerjenningsw f72b8993ef Keyframes: moving keys keeps subframe spacing (#1012)
keys.move and the nudges snapped each key's destination to the nearest
frame, so moving keys a Quick Offset had staggered onto subframes put
them back on frames: four keys at 0, 1/3, 2/3 and 1 frame, moved one
frame, came out at 1, 1, 2 and 2. Layer bars kept theirs, because
layer.timing snaps the move, not the times.

The move is now rounded to whole frames and every key translated by it.
layer_shift turns a comp-time move into a layer-time move through the
layer's start and stretch (reversed too) without a round trip through
comp time; keys.quickOffset uses it as well.

Test: moving_staggered_keys_keeps_their_subframe_spacing (Quick Offset,
then keys.move and keys.nudgeForward; fails before: 1/3 frame lands on
a frame).
2026-10-12 18:49:34 -04:00
redonion23andClaude Opus 5.5 a69c7d7cd2 Timeline: Reset keeps an animated property's keyframes and keys the default (#1078)
prop.reset cleared every keyframe before setting the default value. As in
After Effects (and as effect.reset already does for effect parameters), an
animated property now keeps its keyframes and gets the default as a key at
the current time; a static property just takes the default. The expression
is still removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 23:52:44 +02:00
tylerjenningsw e2d50ec926 Timeline: Shift ranges and key boxes stagger in comp order (#1012)
In After Effects a marquee selection staggers in the comp's layer order,
not in the direction the box was drawn. A Shift-click range of layer
bars and a box of keys already come out top layer first; this test makes
both with the pointer (the range from the bottom layer up, the box from
the bottom right to the top left) and checks the Quick Offset that
follows: the top layer stays and the bottom one moves the whole delta.
2026-10-12 17:45:30 -04:00
storybored379andOpenAI Codex 5d9bab268d M6.7: reject Roto Brush span edits past trimmed strokes
Validate saved stroke frames against the current layer limits before clamping a span edit. Keep project and undo state intact when trimming has left strokes outside the layer.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-12 14:40:10 -07:00