530 Commits
Author SHA1 Message Date
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 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 Thomas acb77a8c85 Merge pull request #1210 from arnav-hiwarkar/feat/copy-presets-folder-path 2026-10-13 04:35:21 -04:00
Brandon Thomas 66c08de572 Merge pull request #1039 from moecr7/codex/fix-989-effect-paste-timing
# Conflicts:
#	contributors/people.toml
#	docs/gaps.md
2026-10-13 04:35:19 -04:00
Jannik WapplerandClaude Opus 5.5 1765dc53df Tint: Swap Colors (#963) (#1060)
Squash of #1060 by @RavoxX, without the docs/images screenshots (kept out of the repo).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:33:43 -04:00
Brandon Thomas 32792ff172 Merge pull request #1199 from cjcwwww/i18n/effect-names
# Conflicts:
#	crates/ui-egui/src/panels/timeline.rs
#	docs/gaps.md
#	docs/localization-parity.md
2026-10-13 04:33:20 -04:00
Brandon Thomas 430c968e52 Merge pull request #933 from Themolx/replace-with-precomp
# Conflicts:
#	crates/engine/src/tests_project_items.rs
2026-10-13 04:31:42 -04:00
Brandon Thomas 4574fb62bc Merge pull request #792 from vangel76/gap-object-matte
# Conflicts:
#	crates/ui-egui/src/i18n/ui.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 04:30:49 -04:00
Brandon Thomas 287c35dae7 Merge pull request #1109 from eliasx45/upstream/azerty-graph-zoom
# Conflicts:
#	crates/engine/src/commands/time.rs
#	crates/ui-egui/src/panels/timeline.rs
2026-10-13 04:29:47 -04:00
Brandon Thomas 6e47aa3f00 Merge pull request #1176 from tylerjenningsw/fix/report-workspace-save-errors 2026-10-13 04:29:16 -04:00
Brandon Thomas c720a1d30b Merge pull request #1005 from Nael-Nathanael/fix/desktop-args-997
# Conflicts:
#	contributors/people.toml
2026-10-13 04:29:10 -04:00
Brandon Thomas 3aac824976 Merge pull request #1175 from storybored379/fix/roto-trim-span
# Conflicts:
#	ROADMAP.md
2026-10-13 04:29:08 -04:00
Brandon Thomas a985dfb36b Merge pull request #1098 from vangel76/feat-gap21-percent-guides-effect-labels
# Conflicts:
#	crates/engine/src/tests_menu_cmds.rs
#	docs/effects-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-13 04:29:06 -04:00
Brandon Thomas 37b681ac12 Merge pull request #1104 from eliasx45/upstream/import-project 2026-10-13 04:28:59 -04:00
Brandon Thomas bd74443238 Merge pull request #1110 from eliasx45/upstream/render-bench 2026-10-13 04:28:58 -04:00
Brandon Thomas 1d8fe7a8a0 Merge pull request #1158 from nanox333/fix/551-shift-space-every-frame
# Conflicts:
#	docs/ui-parity.md
2026-10-13 04:28:55 -04:00
Brandon Thomas f0fa92ceef Merge pull request #1146 from redonion23/fix/1078-reset-keeps-keyframes 2026-10-13 04:28:54 -04:00
Brandon Thomas 4f57d5d73d Merge pull request #1144 from tylerjenningsw/enhance/quick-offset
# Conflicts:
#	crates/ui-egui/src/i18n/ui.rs
2026-10-13 04:28:43 -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 0e424b45f1 Merge pull request #1050 from pravdomil/claude/bind-existing-shortcuts 2026-10-13 04:28:25 -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
arnav-hiwarkar c7692aa697 #971: copy the presets folder path in Effects & Presets 2026-10-13 13:27:09 +05:30
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 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
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
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 <noreply@anthropic.com>
2026-10-13 01:46:34 +02: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 <noreply@anthropic.com>
2026-10-12 23:52:44 +02: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 <noreply@openai.com>
2026-10-12 14:40:10 -07:00
tylerjenningsw 94b4d0ca34 Timeline: Quick Offset layers and keyframes in selection order (#1012)
Ctrl/Cmd+Alt dragging a selected layer bar or a selected key now runs a
Quick Offset (layer.quickOffset / keys.quickOffset with fromStart, one
undo step). The mode is chosen when the drag starts and needs at least
two unlocked layers; the last selected layer moves by whole frames with
the pointer. A readout by the pointer shows the Total Offset and the
offset Per Layer, translated.

A plain drag still moves the selection together, Alt alone still scales
a key group in time (it now leaves Ctrl/Cmd+Alt to Quick Offset), and
Ctrl/Cmd+Alt+click still toggles Hold. The key drag's readout is drawn
by a shared drag_tip. Keys also register timeline.keyAt.<uid>.<ticks>,
exact where keys on subframes can share a frame.

Tests (ui_quick_offset): staggering layer bars in selection order and
keys per layer, one undo each; plain drags and Alt scaling unchanged; the
readout while dragging.
2026-10-12 17:37:34 -04:00
tylerjenningsw 28e9e5229e Quick Offset: layer.quickOffset and keys.quickOffset commands (#1012)
After Effects 25.4's Quick Offset staggers selected layers, or each
layer's selected keyframes, in the order they were selected: the first
stays, the last moves by the drag and the ones between by equal steps,
landing on subframes.

layer.quickOffset {delta, layers?} moves each layer's start, in and out
point by total * i / (n - 1), worked out once in ticks with integer
arithmetic, so 1 frame over 4 layers gives 0, 1/3, 2/3 and 1 frame
exactly. keys.quickOffset {delta} does the same per layer for the
selected keys, the layers in the order their first key was selected;
every selected key of a layer, across its properties, moves by that
layer's comp-time offset through its start and stretch (reversed too),
keeping values, interpolation, easing and labels. Locked layers are
skipped and at least two layers are needed. With merge and fromStart,
delta is the whole drag and each step starts again from before it, so
the drag is one undo step and a key passed over survives. A delta or a
destination out of the range of times is an error, not a clamped move.

Docs: agents.md; architecture.md notes Quick Offset as the exception to
times snapping to frames.
2026-10-12 17:37:31 -04:00
vangel76andClaude Sonnet 5.5 19e4fc7c60 docs: re-audit gaps.md and parity docs against main (2026-10-12)
Open-issue count, crash list, closed-issue references, localization
catalog counts, export formats and #include status checked against
upstream/main 470ea7a3. Rows 7, 14 and 21 of gaps.md untouched (pending
in PRs #1102, #792, #1098).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:36:26 +02:00
vangel76andClaude Sonnet 5.5 51371939ad Gap 7: Backgrounds animation presets (#1096)
Problem: the built-in animation preset library had no Backgrounds category
(gap 7: 31 presets against After Effects' 621).

Fix: 23 original Backgrounds presets in crates/engine/presets/backgrounds.json,
built from our own generator, noise, colour and stylize effects: Soft Gradient
Drift, Aurora Veil, Slow Fog, Cloud Bank, Dark Smoke, Ember Haze, Magma Flow,
Ice Flow, Pastel Plasma, Cell Drift, Crystal Facets, Hex Mosaic, Mirrored Silk,
Water Caustics, Blueprint Grid, Checker Scroll, Scrolling Stripes, Pulse Rings,
Bokeh Drift, Spotlight Floor, Dawn Sky, Sweeping Light, Grainy Wash.

The preset format gains `expr` (an expression per property, so backgrounds keep
moving for the whole layer) and point parameters written as fractions of the
layer size. Effects & Presets lists them under * Animation Presets > Backgrounds.
Tests: catalogue uniqueness and categories, apply/undo/redo of every preset
(engine), render of every preset with the expression engine: finite,
non-flat, deterministic, moving (host), panel listing and apply (ui-egui).
Sidecar, ATTRIBUTION.md, gaps.md row 7 and effects-parity.md updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:13:05 +02:00
vangel76andClaude Sonnet 5.5 f11792827e Gap 21: percentage guides and effect colour labels (#1095)
Guides can store a percent of the comp so they follow comp-size changes
(View > Add Percentage Guide, Convert Guides to Percentage / Pixels); an
effect instance has an optional colour label (effect.setLabel, Effect Controls
header bar and Label menu). Both are serde-defaulted, so old projects load.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:07:36 +02:00
Moein Saharkhiz a27f9f54b9 #1063: render carriage-return text as separate paragraphs 2026-10-12 14:47:58 -04:00
Elias 0553180092 shortcuts: physical-key bindings for AZERTY layouts; Go to Work Area Start/End
Shortcut presets accept Physical+<key> (egui US-position names) so layouts that report the
same logical key for two physical keys (AZERTY's - and )) can bind them separately; physical
bindings win for that press, and a focused panel's binding wins over an application command on
the same chord. ui.key accepts the same notation. Adds time.workAreaStart / time.workAreaEnd.

(cherry picked from commit 209543e9e0ba32f809f9ff333abb8b8ec4125cea)

(cherry picked from commit f8301a9e3e0b60e200e8a233455398741658481e)
(cherry picked from commit 4d002fabf992934e1bb73a0c32bc9f3fd3ae5447)

(cherry picked from commit 5d466c5955ee934d5347c194ec7d15fb5f58bba4)
2026-10-12 19:21:40 +01:00
Elias 376f5757fc file.importProject: import an EffectCraft project into the open project
File > Import > EffectCraft Project (and .ecproj/.ecprojx given to File > Import)
merges the project into a folder named after the file, with every id shifted past
the open project's, in one undo step. Relative footage resolves next to the file;
the result lists comps, root comps, primaryComp and missing media.

offset_ids now shifts tracker Motion Targets and saturates instead of overflowing;
fix_next_id saturates too, so a damaged file can't overflow on open or import.
Menu entry translated in all ten menu catalogues.

Rebased onto current upstream main for the split of #858 (test module list in
engine lib.rs; es/pt-br/ru/fr/de/cs menu translations).

(cherry picked from commit 19c653fccc95133fb35e978a7f7f20a18c0e8c89)
(cherry picked from commit 4e751ad2)

(cherry picked from commit ceed706cc3b5f3755769738a1ddb3d965a94ab6b)
2026-10-12 19:19:21 +01:00
Elias e23b926efc G5: cargo xtask bench: render benchmark of generated scenarios (peak memory, CPU, wall) with results.jsonl and --compare
(cherry picked from commit 40b232d02069925e9c72677f52ee6f356495a736)

(cherry picked from commit a2d89bda11b7217c83c98ab681b8f9b4a334786d)
(cherry picked from commit 63951595222207b6bfd0ecde72f991628c6f12d9)

(cherry picked from commit 20c2bb0dcb)
2026-10-12 19:18:32 +01:00
Claude 2cb5d199ea G4: relink footage that moved
footage.relink {item, path} relinks one item and every other missing item that moved with it (one undo
step); Replace Footage on a missing item does the same, as in After Effects. File > Dependencies >
Relink Missing Footage in Folder... (footage.relinkFolder) finds missing files by name in a folder;
footage.missing lists them. The footage check after open finds files that moved along with the
project's folder, without marking the project modified. Paths compare by parts, so Windows and macOS
projects relink either way.

Checks: engine relink:: / footage_check:: (13 passed with replay::), host relink:: (1 passed),
clippy -D warnings (engine, host, ui-egui, script, cli, xtask). Not run: ui-egui i18n tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEytG1Qum4TJrMTLKjgwDa

Upstream port: the folder search (footage.relinkFolder) is left out, since upstream's file.relinkFootage already scans a folder; the module is relink_moved.rs next to upstream's relink.rs.

(cherry picked from commit ea7923535fb81693ed95aa0fe768b850b347b2a9)

(cherry picked from commit d15e853a720304c1032e21974d84e1501c6edfc3)
(cherry picked from commit 0b8a328dda1fb727488e912a13f94a2ef4147d03)

(cherry picked from commit 9d85bfde72)
2026-10-12 19:17:35 +01:00
vangel76andClaude Sonnet 5.5 4acea86d4f Merge upstream/main into gap-object-matte
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 19:03:42 +02:00
Marco Arnulfo 7304f4c672 Project panel: Composition Settings… in a comp's right-click menu (fixes #1058)
First in the menu, as in After Effects, and for the right-clicked comp rather than the one in the viewer: the dialog keeps the comp it edits and OK passes it to comp.settings. comp.settings with only {"comp": id} opens the dialog on that comp.
2026-10-12 18:54:56 +02:00
pravdomilandClaude Opus 5.5 92e3939ca7 Fast Previews and Show Channel: one command per mode, with default keys
Both were a single command taking the mode as a parameter, with no menu entry,
so nothing could carry a default key. Each mode is now its own command:
view.fastPreviews.off / adaptive / draft / fastDraft / wireframe (Cmd+Alt+1-5)
and view.channel.red / green / blue / alpha (Alt+1-4), redColorized /
greenColorized / blueColorized (Alt+Shift+1-3) and rgbStraight (Alt+Shift+4).
A channel's key again returns to RGB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:34:56 +02:00
Moein Saharkhiz 55ef2d05db #989: align pasted effect keyframes with the playhead 2026-10-12 10:26:35 -04:00
ThemolxandClaude Opus 5.5 fcf56415dc Project panel: Replace with Precomposition (used N times)
Right-clicking a footage item or solid in the Project panel offers
"Replace with Precomposition (used N times)": the item goes into a new
composition of its size, pixel aspect and frame rate, in the same folder, and
every layer in every composition that used it switches to that composition,
keeping timing, transforms, effects and masks. One undo step. Agents run it as
`file.replaceWithPrecomp`.

The Project panel's details header also shows how often the selected item is
used ("Name ▾, used 3 times"); the ▾ lists and opens the comps that use it.
`Project::item_users` and the `project.usedIn` query back both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:14:43 +02:00
Marco Arnulfo e2188c436c Portable mode: a portable.txt beside the exe keeps settings in EffectCraftData (#993) 2026-10-12 13:42:01 +02:00
RealtaNuaandClaude Opus 5.5 649ba2e8cf Desktop: an unknown option or a bad --control port stops the launch (#997)
The desktop app imported any argument it didn't know as a file (writing it to Recent Footage)
and dropped a missing or non-numeric --control value, starting without a control server.
parse_args now returns an error for an unknown option, a --control value that isn't a port from
1 to 65535, or a bad EFFECTCRAFT_CONTROL_PORT (checked only when --control isn't given); main
prints it and exits with status 1 before anything starts. --control=<port> works, and -- ends
the options so a file named -x can still be opened.

Tests: parse_args unit tests, and tests/args.rs runs the binary with its own settings folder
and checks the exit, the message and that nothing was written.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 17:40:15 +07:00
Brandon Thomas af671f6997 Merge pull request #987 from Losminek/cs-ui
i18n: add Czech (cs) interface language

# Conflicts:
#	crates/engine/src/prefs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ui.rs
#	docs/localization-parity.md
2026-10-12 05:22:10 -04:00
Brandon Thomas 617412b555 Merge pull request #888 from vangel76/fix-882-viewer-pixel-exact
#882: pixel-exact Composition viewer at 100 % and above

# Conflicts:
#	crates/ui-egui/src/menus.rs
2026-10-12 04:22:01 -04:00