1871 Commits
Author SHA1 Message Date
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
arnav-hiwarkar c7692aa697 #971: copy the presets folder path in Effects & Presets 2026-10-13 13:27:09 +05:30
WaWu b743410e72 UI: Quick Apply keeps an IME composition while pinyin is typed
The command palette's search field took the keyboard with `request_focus()`
on every frame it was drawn. egui's `Memory::request_focus` interrupts the
IME composition, and `egui-winit` acts on `should_interrupt_composition`
with `set_ime_allowed(false)` + `(true)` — on Windows that is
`ImmAssociateContextEx(hwnd, 0, IACE_CHILDREN/IACE_DEFAULT)`, which drops the
composition in progress. With Microsoft Pinyin the pre-edit text was
cancelled over and over, so the pinyin never accumulated and nothing could be
committed: Chinese (and Japanese/Korean) could not be typed into Quick Apply
at all, while Latin typing was unaffected.

Ask for the focus only when the field hasn't got it, the same guard the
inline editors use (`widgets::keep_focus`): the palette still takes the
keyboard when it opens, and takes it back when something else takes it away.

Tests: panels::dialogs::tests::the_palette_leaves_a_pinyin_composition_alone
opens the palette, feeds Preedit("n".."nihao") frame by frame and asserts that
the frame never asks the platform to cancel the composition, that the whole
pre-edit text is in the query, and that the field claims the keyboard back
when another widget takes it. Failed before: "typing `n` cancelled the
composition".
2026-10-13 14:51:36 +08:00
Vikram Bhaskaran c46f80faa2 Store each comp's layer selection on its own.
A single selected_layers list kept ids from every comp, so the active comp looked selected whenever another comp was. Opening a comp and running a script against a different comp now park and restore that selection.
2026-10-13 17:46:02 +11:00
Marco Arnulfo 3095217ce8 Track matte on an adjustment layer limits where its effects show (fixes #1201) 2026-10-13 08:42:14 +02:00
Vikram Bhaskaran 8c986a1bec Keep closed comps' layer selections across scripts (#1001)
sanitize_state dropped every selected layer not in the active comp at the
end of each script run, so a layer selected by script in a comp that was not
open was gone by the next script call. Every comp keeps its own selection as
in After Effects; only ids gone from every comp are dropped now. UI and
engine lookups resolve selections per comp and tolerate foreign ids.
2026-10-13 17:39:16 +11:00
WaWu e655159382 i18n: the effect catalog takes a language's tables, with Japanese's slot reserved (#821)
The Japanese data point in #821 (zitann2020) offered to write its rows against whatever mechanism
this PR lands, so both languages share one catalog instead of inventing two. This makes adding a
language a data change and reserves `ja`:

- `Catalog` holds a language's effect rows and its category labels, and `catalog()` resolves a
  language code to one; adding a language is two tables and one arm, with no change to the lookups
  or to any panel.
- `JAPANESE_EFFECTS` and `JAPANESE_CATEGORIES` are declared, keyed by the same effect ids and by
  the registry's English category names, and `ja` resolves to them. Both are empty, so a Japanese
  interface draws exactly what it drew before — the slot changes no behaviour on its own.
- `a_pending_language_only_uses_known_ids` guards the pending tables (Japanese and Traditional
  Chinese) as rows arrive: an id Simplified Chinese does not know, a different English name for one
  of them, an empty translation or a repeated id fails. `languages_without_rows_draw_the_registry_name`
  asserts each pending language has an arm in `catalog()` (a table with no arm would leave a
  translator's rows dead) and is still row-less, so the day one gets rows the test says where its
  coverage moved.
- `only_the_brand_names_stay_in_english` now documents that it pins *Simplified Chinese's* five
  brand names and is deliberately not shared: AE's Japanese interface keeps the whole `CC`, `VR`
  and `OCIO` sets in English while its Chinese one translates them, so repeating the English name
  is a language's choice and not an unfinished row. The row check reflects that (a repeated English
  name is accepted, an empty one is not).

docs/localization-parity.md records the two empty tables and that each language decides for itself
which names stay in English.

Refs #821
2026-10-13 14:13:28 +08:00
WaWu 40a8e50de9 i18n: the effect categories take the Chinese interface's own wording (#821)
Verification against the installed After Effects 2026 (26.5.0.89) Simplified Chinese interface
(the checklist's `07-类别` sheet) finds two of the 21 category names differ from our first
suggestion:

- `Keying` is 抠像, not 键控 — the interface names the folder 抠像 while the effects inside it keep
  键 (颜色差值键, 亮度键, 内部/外部键), so the two do not contradict each other;
- `Obsolete` is 过时, not 已废弃 — the effects in the folder say 旧版 for the legacy ones.

The other nineteen are confirmed as they were. `Color Correction` (颜色校正), `Matte` (遮罩) and
`Noise & Grain` (杂色和颗粒) were the three where the checklist's older machine-translated
suggestion differed; the interface's wording matches what the catalog already had, so those stay.

`CATEGORIES`' doc comment now records where the 21 names come from instead of calling them our
wording.

Refs #821
2026-10-13 13:53:08 +08:00
WaWu 0aa2ea5e12 i18n: an effect's name draws in the interface language, the generated Effect menu included (#821)
The interface below the menu bar is localized (618 fixed menu entries, ~490 panel rows) but an
effect's name still rendered in English everywhere it appeared: the generated Effect menu, Effect
Controls, the Timeline and Effects & Presets.

The registry's English name is the effect's identity: `effect.list`, `effect.apply {"effect": …}`,
scripts, expressions and the property tree's group name all match on it. Only the drawing changes.

`crates/ui-egui/src/i18n/effects.rs` keys the names by effect **id** rather than by the English
string — a repeat is stored as `Gaussian Blur 2` (an English key would miss it) and upstream
renames a display name now and then, while the id is stable. It carries 306 rows for Simplified
Chinese, one per effect in the registry, taken from the installed After Effects 2026 (26.5.0.89)
Simplified Chinese interface, with the `CC` series following Adobe's Chinese documentation (which
names 68 of the 69 that its interface leaves in English). `Cryptomatte`, `EXtractoR`, `IDentifier`,
`Key Light` and `Unmult` stay in English as brand names; `Face Measurements` and `Face Track
Points` are EffectCraft's own effects. The 21 category folder names are in the same file, worded
after the effect names (channel → 通道, noise → 杂色, matte → 遮罩 …).

What draws the catalog now:

- **Effect menu:** the entries and the category submenus. The command and its `{"effect": <id>}`
  params are untouched, so the menu still applies by id.
- **Effect Controls:** the header, its Rename field, the accessible name and the About popup.
- **Timeline:** effect group rows, and the search (a Chinese interface is searchable in Chinese).
- **Effects & Presets:** the rows, the drag ghost, the folder rows and the search.

Kept as they are: the effect id, the property tree's `PropGroup.name`, every automation id and
label, `effect.list`'s `name`/`category`, and the undo label (`Apply Gaussian Blur`). A name the
user typed in Effect Controls stays verbatim, and a repeated instance keeps the index the engine
appends (`Gaussian Blur 2` → `高斯模糊 2`). No other language has rows yet, Traditional Chinese
included, so those draw the registry's English name.

Tests: nine unit tests under `cargo test -p effectcraft-ui-egui --lib i18n` cover the data (every
effect has a row, the row still carries the registry's English name, ids are unique, only the five
brand names repeat their English name, every category has a row, a row names an effect the registry
knows) and the behaviour (the instance index, user renames, English and untranslated languages,
search) plus a scan pinning the drawing sites. `crates/ui-egui/tests/ui_effect_names.rs` checks the
Effect Controls header, the generated Effect menu and that the ids, automation labels and
`effect.list` stay English.

`docs/localization-parity.md` and `docs/gaps.md` no longer record effect names as deliberately
English; effect *parameter* names are still untranslated and stay the next piece.

Refs #821
2026-10-13 13:39:16 +08: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
redonion23andClaude Opus 5.5 a96c80788a Scripting: CompItem.saveFrameToPng writes the frame as a PNG (#816)
After Effects scripts save a comp frame with saveFrameToPng(time, file);
EffectCraft only had the comp.saveFrameAs command, so the method was
undefined. It now runs comp.saveFrameAs at the given comp time, after the
same file-gate check as other script writes (Allow Scripts to Write Files).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 04:32:48 +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
tylerjenningsw 544d46a218 Workspaces: a failed save says a restart may undo the changes (#696) 2026-10-12 22:03:01 -04:00
tylerjenningsw e643457fa3 Commands: an earlier invocation's frontend failure isn't a later command's result (#696) 2026-10-12 22:03:01 -04: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
maxaeexe af1133a1f3 layer.addMarker: add composition marker when no layers are selected 2026-10-13 04:43:53 +03:00
tylerjenningsw ecf485e375 Docs: engine.execute reports a failed workspace save through workspaceStorageError (#696) 2026-10-12 21:35:48 -04:00
tylerjenningsw dbaf559b58 Docs: workspace commands report a save that didn't reach disk (#696) 2026-10-12 21:32:24 -04:00
tylerjenningsw 41b04bf414 Workspaces: tests for a failed, a storage-less and an on-disk workspace save (#696) 2026-10-12 21:32:22 -04:00
tylerjenningsw b9a9a0a414 Workspaces: Save, Save As, Rename and Delete fail when the file isn't written (#696) 2026-10-12 21:32:18 -04:00
tylerjenningsw 73ea427015 Commands: a frontend-only command's own failure is its result (#696) 2026-10-12 21:31:16 -04: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
tylerjenningsw 765915e126 Workspaces: deleting the current workspace saves after switching to Default (#696) 2026-10-12 21:07:39 -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
Marco Arnulfo 56f6612f78 Dialogs: drag a modal dialog by its title to move it off the viewer (fixes #1155) 2026-10-13 02:02:46 +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 5e97eaddba #1063: find a Levels alpha control on the effect by its name
Pro Levels2 keeps its Alpha controls one group down, under Alpha, and a script's
effect.property("Alpha Input White") only searched the effect's direct children, so it returned null.
The lookup now searches the groups nested below the direct children, up to three levels, after the
direct match fails. Direct children keep their priority. A regression test checks the name on the
effect and the same control through the Alpha group.

Co-Authored-By: Claude Haiku 5.5 <[email protected]>
2026-10-13 01:41:22 +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