Commit Graph
1189 Commits
Author SHA1 Message Date
tylerjenningsw c7715a3f2d Footage: a proxy in use that can't be decoded says why (#1701)
The picture comes from an item's proxy while Use Proxy is on, but the
Project panel, the Viewer, project.summary and footage.info asked only
about the item's own file: a proxy that couldn't be decoded (an AVI with
an unsupported codec, say) left the layer empty with no reason given.
They now ask FootageSource::item_problem, which gives the proxy's reason,
said to be the proxy's, for footage and compositions alike.
2026-10-17 14:02:18 -04:00
tylerjenningsw e26b4af5c0 Media Browser: list movies by the importer's list (#1701) 2026-10-17 11:35:05 -04:00
Moein Saharkhiz 8e59ccdb3a Fix #1231: keep autosave completion tied to its originating project (#1330) 2026-10-17 08:58:56 -06:00
MarekSpodBiedry b8d52cd8c4 Paste path operators below the shapes they act on (#1681)
Pasting Trim Paths, Wiggle Paths or a paint into a shape layer with nothing
selected put it on top of Contents, where it acts on no shapes. It now goes
to the bottom, as Add places it. Also fixes #1629.
2026-10-17 14:37:42 +02:00
arnav-hiwarkarandClaude Opus 5.5 c28daa479b #1714: test several comps, negative and malformed saved times
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:35:12 +05:30
arnav-hiwarkarandClaude Opus 5.5 ea67c46880 #1714: doc comments name the saved current times
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:40:44 +05:30
arnav-hiwarkarandClaude Opus 5.5 b9172fec2f #1714: reopen each comp at its saved current time
Layer times were already saved and read back as exact ticks; the values in
the issue are 34f, 315f and 3033f at 59.94 fps. The viewer was black because
the playhead wasn't saved: a reopened comp started at 0, before its layers.
The project file's view (`activeComp`, `openComps`) now also records each
comp's current time (`compTimes`), restored on open within the comp's frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:32:14 +05:30
MarekSpodBiedry 7146006c15 Duplicate the selected text animator or selector instead of the whole text layer (#1665) 2026-10-17 07:50:03 +02:00
MarekSpodBiedry 8f74c3790e Replace non-ASCII characters in EXR layer names so ProEXR export no longer panics (#1702) 2026-10-17 07:46:32 +02:00
Ayberk A. 107cedb84c fix(effects): starred effects survive a restart (#1692)
Effects & Presets kept its favourites in `UiState::effects_favorites`,
which nothing writes out (only the workspace layout is saved), so the
stars were gone the next time the app opened. They now live in the
settings (`Prefs::effects_favorites`, saved when a star is clicked) like
the recent fonts and presets, survive Reset All like them, and a
hand-edited list loses empty and repeated ids on load.
2026-10-17 00:40:04 +03:00
aiongg 816ca95800 Footage: follow stored footage paths only on this computer or the project's share
Footage paths stored in a project are resolved through craft-docpath against
the project file when it is opened or imported: relative paths are taken from
the project's folder, and an item whose path is on another computer, names a
device or is a non-file URL is marked missing without being looked at. The
footage check, the changed-on-disk scan, Reload Footage, Relink, Edit Original
and Reveal in Finder resolve the path again before touching the file, and the
Project panel, Metadata panel and waveforms read no file details of missing
footage.

Lottie image assets are resolved against the Lottie file; one on another
computer is skipped with a warning. Files a 3D model names are read only when
local or on the model's own share.

Reveal in Finder refuses a folder that would open as an app or other bundle.

Files imported or relinked in this session are opened as given.
2026-10-16 17:43:58 +00:00
aiongg 1064911fc4 Edit: Edit Original opens only regular files with a footage media extension
A folder or bundle, a link to another kind of file, or a file whose extension
the footage importer doesn't take is refused with a message instead of being
handed to the system's default app.
2026-10-16 16:14:02 +00:00
aiongg 299223f112 Templates: Import Template writes embedded media only into its item folders
Each 'media/<item>/<file>' entry is written to '<template> Media/<item>/<file>'
only when <item> is all digits and <file> is a plain file name (not empty, '.'
or '..', no separators, and on Windows no drive, stream or device name), and
the written path is checked to sit in an item folder of '<template> Media'.
read_stored skips entries with absolute, drive or '..' names.
2026-10-16 16:14:02 +00:00
detthandClaude Sonnet 5.5 2823abc40c fix(engine): report the keys keys.delete removed, not the keys selected
keys.delete returned the number of selected keys even when some of them
no longer existed, so a delete that removed nothing still reported success.
It now returns the number of keys actually removed, and a key that is gone
or listed twice is not counted.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JaA7tHy731qtvscjulrcfz
2026-10-16 13:55:29 +00:00
Hamza 0c39995d0f Convert a shape layer's parametric path to a bezier path (#932)
`path.convertToBezier` only cleared `roto_bezier` on mask groups, so on a shape
layer it did nothing at all: a Rectangle, Ellipse or Polystar path stayed
parametric and its vertices could not be edited or animated. In After Effects
the command replaces such a path with an editable Path 1 of the same geometry.

The geometry is not re-derived. `parametric_path` calls the same
`effectcraft_path::{rect, ellipse, polystar}` functions `render::shapes::collect`
calls, with the same defaults for a property a group doesn't carry, so the path
that comes out is the shape that was on screen; `from_kurbo` and
`build::shape_path` then make an ordinary Path 1.

Masks are unchanged: shape items take precedence when selected, and with none
selected the command falls through to the old mask behaviour. Path Direction is
carried over rather than reset, since reversing a path is a separate command,
and the new path is left selected so its vertices can be edited straight away.

The parametric properties are read at the current time and the result is a
static path: a Size or Roundness animation cannot survive becoming a vertex
list, in After Effects either. Undo restores the parametric shape.
2026-10-16 18:13:08 +05:00
BFlat 3e2793af49 Merge pull request #1318 from DWAA1660/fix/1250-mask-tangent-lengths
Masks: addVertex and setVertex fill in missing tangents of a loaded path (#1250)
2026-10-16 19:03:32 +07:00
BFlat 5219fa549b Merge pull request #1321 from sanyamsingh001/fix/time-to-frames-duration-r48
Honor duration rounding in timeToFrames
2026-10-16 19:02:47 +07:00
BFlat fa5eff7210 Merge pull request #1332 from Themolx/null-visibility-switch
Null layers: the Video switch hides them in the Composition viewer, as in After Effects
2026-10-16 19:02:24 +07:00
BFlat 8e96fdd0fc Merge pull request #1351 from DWAA1660/fix/1246-script-confirmed-flag
Scripts: a script can't confirm executing a file for itself (#1246)
2026-10-16 19:01:15 +07:00
midasmatterhorn 30e02b06ca project: output_size clamps the scale, not the sides, and every renderer uses it
Clamping each side on its own broke the aspect ratio (a 30000x15000 comp
at 4x came out 30000x30000). The effective scale min(s, MAX_SIZE/w,
MAX_SIZE/h) keeps both sides within the comp limit, and the export
pipeline and the locked-aspect width derivation render at that same
scale, so nothing requests a frame larger than output_size reports.
2026-10-16 14:46:24 +03:00
bflatasticandClaude Opus 5.5 c0b505b41d Masks: deleting vertices fills in missing tangents first, like add and set (#1250)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:40:14 +07:00
BFlat 5ad71a24e6 Merge pull request #1294 from vbasky/fix/1068-loop-import
Engine: sequence Interpret Footage loop regression coverage (#1068)
2026-10-16 18:13:22 +07:00
BFlat 9eec6ea705 Merge pull request #1344 from dotCooCoo/fix/find-missing-fonts-postscript-names
Find Missing Fonts: fonts found by their PostScript or full name are no longer listed
2026-10-16 18:12:47 +07:00
BFlat eac13d1cf1 Merge pull request #948 from Themolx/solo-outside-active-range
Solo: exclude other layers even when the soloed layer is outside its time range
2026-10-16 18:10:06 +07:00
BFlat 41037328a8 Merge pull request #1433 from Hamza0101/render-queue-bare-project-path
Render Queue: resolve a bare project filename against the working directory
2026-10-16 18:09:06 +07:00
BFlat d6a9ccfff7 Merge pull request #1435 from marcoarnulfo/fix/webm-metadata-frame-count
Import: item.metadata counts a WebM's last frame when its duration is not a whole millisecond
2026-10-16 18:08:44 +07:00
bflatasticandClaude Opus 5.5 86dff3ce78 Footage: relinking a moved project keeps Windows separators and UNC shares
project_dir rebuilt the folder with `/` and dropped a UNC share's leading
`\`, so a project opened from \server\share\Job looked for its moved
footage under the relative path server/share/Job and never found it. It now
keeps the path's own separator and its leading `/` or `\`.

The relink tests built mixed-separator paths (`…\Job/p.ecproj`) and so failed
on Windows; they now use native paths and check the separator is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 17:13:40 +07:00
BFlat 8449853d0a Merge pull request #1259 from arnav-hiwarkar/perf/media-browser-listing
perf(media-browser): reuse listings and cache filename sort keys
2026-10-16 17:03:29 +07:00
BFlat 7dc488b79d Merge pull request #1222 from arnav-hiwarkar/perf/preset-panel-cache
perf(presets): reuse cached text and user preset data
2026-10-16 17:03:05 +07:00
BFlat c044e7a872 Merge pull request #1543 from arnav-hiwarkar/fix/1536-ai-retain-layer-sizes
Import: Composition - Retain Layer Sizes for PDF / Illustrator files (#1536)
2026-10-16 17:02:43 +07:00
arnav-hiwarkarandClaude Opus 5.5 c4c079d4f4 fix(engine): copy only the selected keyframes across properties (#1552)
Clicking keyframes on Position, Rotation and Scale highlights those
properties, and Copy then added every key of a highlighted property that
had none selected, so deselected keys came back on paste. The keyframe
copy branch also left a stale value clipboard that Paste preferred over
the new keys.

Copy now takes whole animations only when no key is selected or the
Transform group is selected, and every copy branch clears the clipboards.

Also collapses a nested `if` in the STL importer into a match guard,
which Clippy 1.95 flags under -D warnings (no behaviour change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 20:57:50 +05:30
Gert Pellin a3c3b8530c Text: a variable font's named instances are styles of its family (#1563)
The font scan read only whether a face has an fvar table, so a variable
family offered one style (its default instance), fontStyle = "Black"
picked that face with synthetic bold, and an instance's PostScript name
(Figtree-Black, what After Effects scripts use) found no face at all.

The scan now reads the named instances (style, axis values, PostScript
name, or the one Adobe Technical Note #5902 derives). They are styles of
the family in text.fonts and the font menus, a style, PostScript name or
full name selects the instance, and layout uses its axis values as the
base variations; axes set on the text still win.
2026-10-15 15:30:59 +02:00
arnav-hiwarkarandClaude Opus 5.5 2d216151c6 Import: Composition - Retain Layer Sizes for PDF / Illustrator files (#1536)
The PDF / Illustrator import dialog now offers Composition - Retain Layer
Sizes. Each file layer's footage is cropped to its artwork bounds (fills,
stroke outlines, images, clips) and positioned to keep its place on the
page. Decoding, continuous rasterisation, shape conversion and reload share
one crop (effectcraft_pdf::layer_doc_with_sizes), so they agree.

Also collapses a match guard in the STL reader for current stable clippy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:33:50 +05:30
DWAA1660andClaude Opus 5.5 2c08f675c2 fix(engine): preserve edits when applying background analyses
Merge each offloaded property-group result against the snapshot the job started from. Preserve disjoint live edits, group identity and child changes; refuse conflicting results without changing the document or undo/redo history.

Record an apply rejection on its originating job so completion logs and waiting control commands report failure. Keep concurrent jobs' outcomes separate and retain the correct completion position when the bounded log rolls over.

Add actual-worker regressions for conflicts, disjoint edits, concurrent analyses and undo/redo, plus control completion/error regressions.

Fixes #1249

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:15:40 -06:00
DWAA1660andClaude Opus 5.5 c280506ba0 fix(config): isolate atomic writes and preserve long filenames
Create each atomic write through its own exclusively created temporary sibling and keep its open file handle until writing finishes. Concurrent writers publish complete files without truncating or following an existing temporary entry; failed writes or renames clean up their own staging file.

Keep the temporary basename bounded independently of the destination name so previously supported long project filenames still save. Retain the concurrent-writer and symlink regressions and add a 251-byte filename overwrite regression that checks exact bytes and staging cleanup.

Fixes #1233

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:51:09 -06:00
vangel76andClaude Sonnet 5.5 f2b0998390 #1510: Behaviors animation presets (22 original, expression-driven)
Problem: the built-in animation preset library had no Behaviors category
(gap 7: 54 presets against After Effects' 621).

Fix: 22 original Behaviors presets in crates/engine/presets/behaviors.json:
Soft Wiggle, Shake, Float, Sway, Orbit, Figure Eight, Bouncing Ball, Drift
Right, Drift Diagonal, Pendulum, Wobble, Spin Slow, Spin Fast, Pulse,
Heartbeat, Breathe, Rubber Band, Jitter Scale, Blink, Strobe, Flicker, Fade
Pulse. Each puts a time-based expression on one transform property or
opacity (no effects), built on the property's own value so it keeps running
for the layer's whole duration. Listed under * Animation Presets > Behaviors.
Sidecar, ATTRIBUTION.md, gaps.md row 7 and parity docs updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:46:38 +02:00
DWAA1660andClaude Opus 5.5 642eed0802 Properties/Timeline/Effect Controls: a typed component edits only that component of the other selected layers (#1434)
With several layers selected, typing a value into one dimension (say Z
Position) sent the edited layer's whole value with selected: true, and
prop.set gave every selected layer that same value: they all took the first
layer's X and Y and stacked on top of it. Scrubbing was fine (it sends an
offset).

prop.set takes dims: with selected and a typed value, the other selected
layers get only those components and keep the rest of their own value (Position
with Separate Dimensions included). The three panels send the components the
edit changed (both for linked Scale). Without dims, agents still set the whole
value on every selected layer.

Fixes #1434

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kwMcw2pYQ5fSjKa3ZPnh
2026-10-14 14:39:26 -06:00
Davit MatchakhelidzeandClaude Opus 5.5 fb51427ec0 i18n: Georgian (ka) interface language
Adds Georgian (ქართული, code ka) to Settings > General > Language, with
Match System for ka / ka-GE / ka_GE.UTF-8 locales:

- i18n.rs: the menu catalog, the same 628 (command, English label) keys as
  the other languages, plus the Undo/Redo entries, the workspace and 3D
  view shortcut submenus, the Reveal labels on Windows and Linux and the
  macOS Services items.
- i18n/ui.rs: GEORGIAN, every row of the Chinese panel catalogs (687), and
  GEORGIAN_PANELS, 195 rows for text the panels send through tr() that no
  catalog has yet (form dialogs, drop messages, screen-reader names,
  context menus, Project and Timeline column headers). The tests hold
  them to the same rules (unique, same placeholders, used by a converted
  panel) and keep them from shadowing a Chinese row; prefs_live.rs joins
  the converted files.
- i18n/effects.rs: Georgian names for all 307 effects and 21 categories;
  the CC series and brand names stay in English.
- Properties > Text Animation: the Add Animator and Presets buttons grow
  with their translated labels (English keeps 116 / 84 px).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-14 22:50:28 +04:00
Marco Arnulfo cc29d90943 Import: item.metadata counts a WebM's last frame when its duration is not a whole millisecond (fixes #1429) 2026-10-14 14:22:15 +02:00
Hamza 449e9edd59 Render Queue: resolve a bare project filename against the working directory
`resolve_output` takes the project's folder from `Path::new(path).parent()`.
For a project saved under a bare filename that is the empty path, which the
following `filter` discarded, so a saved project fell through to the
unsaved-project default and relative outputs went to ~/Movies (the user profile
folder on Windows) instead of next to the project.

    --project proj.ecproj      ~/Movies/clip.mp4
    --project ./proj.ecproj    ./clip.mp4

Both spellings name a project in the working directory, so both now resolve
there, as the doc comment on `resolve_output` already describes.

Closes #1427
2026-10-14 17:21:58 +05:00
Marco Arnulfo 69d17d89af Keyframes: a path pasted onto Position becomes a motion path (fixes #1425) 2026-10-14 13:30:45 +02:00
tylerjenningsw f2cdad9edb Expressions: pick-whip references to effect properties by index (refs #1072)
In After Effects, holding Alt while dropping the expression pick whip
refers to an effect's property by its index, effect("Color Control")(1),
instead of its name, ("Color").

prop.pickWhip takes byIndex: the effect keeps its name and each
property inside it is selected by its one-based place among its group's
children (nested groups too), which is what an expression's (1) reads.
Other properties, dimensions ([1]), insertion at the cursor and match
names (Compact English off) are as before. An index still reads a
renamed property, but another one if properties are reordered.

Tests: pick_whip_by_index_selects_effect_properties_by_their_index
(engine, the references); effectcraft-host pick_whip (by index and by
name read the same values, nested and per dimension).
2026-10-13 21:57:19 -04:00
DWAA1660andClaude Opus 5.5 b26a340a7e Project: comp_contains walks precomps with a visited set, not recursion (#1237)
Project::comp_contains recursed through precomp layers with no visited
set or depth limit. A damaged project with a precomp cycle that doesn't
pass the queried comp (A and B nesting each other, C unrelated) recursed
until the stack ran out, which aborts the process ("stack overflow,
aborting"): it can't be caught as a command error, so unsaved work is
lost. layer.addItem checks it before adding a precomp, and the renderer
calls it for every nested comp and its audio. A long chain used the
stack the same way, and comps shared by many paths were walked once per
path (exponential for nested diamonds).

It is now an explicit stack with a visited set: same answers (a comp
contains itself; a target reached through a cycle is found), each comp
visited once, no recursion. The renderer already skips a precomp that
contains the comp being rendered, so a cyclic project now renders
instead of crashing; nothing is repaired on load.

Tests: project comp_contains_ends_on_cycles_long_chains_and_shared_
precomps (cycle with an unrelated target, self-nesting, a target through
a cycle, 40 levels of diamonds, a 20 000-comp chain); engine
a_project_with_a_precomp_cycle_opens_adds_and_renders_without_running_
out_of_stack (file.open, layer.addItem both ways, render). On main the
engine test aborts with a stack overflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:26:42 -06:00
DWAA1660andClaude Opus 5.5 7a9ca5810a Scripts: a script can't confirm executing a file for itself (#1246)
With Warn User When Executing Files on, file.executeFile asks the
frontend first, and the frontend's answer re-runs it with
`confirmed: true`. That flag was taken from the parameters whoever
called, so a script could pass it itself (app.run('file.executeFile',
{path, confirmed: true}), also inside engine.batch) and the file opened
without the question, as long as the user had allowed file and network
access.

`confirmed` now counts only when the command isn't running for a script
(Session::running_for_script, which #590 uses for the same reason and
which stays set through nested batches). A script's request always gets
the question; the user's answer from the frontend's form still opens
the file; with the warning off nothing changes. The permission gate
itself is unchanged.

Tests: engine a_script_cannot_confirm_executing_a_file_for_itself
(execute_for_script directly and through engine.batch; the frontend's
confirmation; warning off) and the same through real JavaScript in the
script crate (app.run, app.run('engine.batch'), File.execute()). Both
fail on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:47:51 -06:00
Kristian MyramandClaude Sonnet 5.5 7e6d76406b Merge upstream main into fix/1010-orientation-followup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 22:03:15 +02:00
Kristian MyramandClaude Sonnet 5.5 dd36eed8e9 Merge upstream main into fix/1063-text-animator-stacked
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:56:33 +02:00
Kristian MyramandClaude Sonnet 5.5 9714fdac78 Merge upstream main into fix/1284-testing-feedback
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:38:41 +02:00
Claude f9c3547e57 #1284: keep Rove Across Time covered through edits to other keys
The report says a roving key goes back to its original time after other keys are edited. Editing
the last key's value and then its time keeps the roving key at its share of the path, as the
Rove Across Time description says, so this adds a regression test for both edits. It passes on
this branch; the reported sequence is not reproduced yet, so the report still needs the exact
steps.

Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
2026-10-13 19:28:22 +00:00
Claude 84589dc288 #1284: double-clicking a shape tool adds a comp-sized shape layer
Double-click a shape tool in the toolbar to add a shape layer the size of the composition, at
its centre, for backgrounds, mattes and overlays, as After Effects does. It uses layer.newShape
with the tool's kind, so the undo label and the command are the same as drawing one.

Covered by tests::a_comp_sized_shape_layer_sits_on_the_comp_centre; the double-click itself is
not covered by an automated test.

Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
2026-10-13 19:28:22 +00: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