1109 Commits
Author SHA1 Message Date
SusieeTheFemboyandGLM-5.3-Flash a3a72d48aa Transform: Fit to Comp sizes shape and text layers by their painted bounds (#2198)
layer.transform's fit / fitWidth / fitHeight ops took the layer's size
from render::source_size, which returns (0, 0) for layers without a
source item — shapes and text — so the ops matched nothing and silently
did nothing, for the shortcut, the menu item and the command alike.

When the source size is empty and a fit op runs, the size falls back to
the layer's painted content bounds at the current time, and the anchor
goes to the bounds' centre: content bounds live in layer space, where a
shape is centred on the origin and text sits above its baseline, so
(w/2, h/2) would fill the frame but land off-centre. Reset and the
other ops keep the source size, so their defaults are unchanged.

The regression test asserts what the viewer shows — the painted bounds
mapped to comp space span [0, 0, 640, 360] after fit, and fitWidth
spans the width centred vertically — for a rect shape and a text layer.

Co-Authored-By: GLM-5.3-Flash <noreply@z.ai>
2026-10-29 05:22:57 +01:00
SusieeTheFemboyandGLM-5.3-Flash 60a356ea14 Edit: duplicating during a text edit ends the edit (#2226)
Edit > Duplicate replaces the layer selection with the copies, but an
open in-canvas text edit stayed on the original layer: typing kept
editing a layer that was no longer selected. When the duplicate leaves
the edited layer deselected, end the edit the way #2096's Deselect All
does (text_edit::end_edit), in the command every caller — menu, shortcut,
MCP — dispatches through.

Co-Authored-By: GLM-5.3-Flash <noreply@z.ai>
2026-10-29 05:22:56 +01:00
BFlat bd4c68fcf8 Merge pull request #2291 from dotCooCoo/feat/recent-script-files
Scripts: Recent Script Files lists the last ten scripts and Cmd+Alt+Shift+D reruns the newest
2026-10-29 10:10:03 +07:00
BFlat 0a3142bcc8 Merge pull request #2517 from MatejKozlovsky/crypto-pick-probe
Cryptomatte pick: keep a hover probe, built off the UI thread (#473)
2026-10-29 10:09:49 +07:00
BFlat 226b954777 Merge pull request #2553 from arnav-hiwarkar/perf/2551-disk-cache-bounded
Disk cache: 20 GB default and a free-space reserve, so it no longer fills the disk (#2551)
2026-10-29 10:09:24 +07:00
BFlat 168c556d10 Merge pull request #2548 from Silvertree2010/fix/h3262-align-video-to-data-timestamp-pa
Fix Align Video to Data panic on multi-byte char in timestamp
2026-10-29 10:09:11 +07:00
Eduardo EspinosaandClaude Opus 5.5 e1d66a3c5c perf: share unchanged layers between undo states
Every undoable edit keeps the previous project for undo, so the comp it touches
is shared and Arc::make_mut deep-copies it: every layer and every keyframe. An
edit therefore costs time in proportion to the comp's size, and a script that
sets many keyframes in a big comp goes quadratic (a 1,600-layer build with
~100k sampled keys took ~45 minutes).

Comp::layers is now a Layers collection backed by Vec<Arc<Layer>>: copying the
stack copies pointers, and only the layer an edit changes is copied. The API
mirrors Vec<Layer>; serialized form is unchanged (a list of layers).
Comp::layer_mut and the iter_mut().filter/find call sites use find_mut /
iter_mut_where so they copy only the layers they actually change.

- prop.addKey in a 400-layer comp: 1.36 ms -> 0.16 ms per key (flat in comp size)
- the 1,600-layer script build: ~45 min -> 47 s, frames pixel-identical
- regression tests: an edit leaves the other layers shared with undo; layers
  round-trip as a plain list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkpGcKfWyCudfSsQNiWAaG
2026-10-28 22:20:23 -04:00
tylerjenningsw b9808d5a24 Engine: a typed prop.set measures each layer's follow change where it keys (#2485) 2026-10-28 20:28:10 -04:00
dotCooCoo ff717aa309 Merge main and retain Cineon Settings in the extracted modules
Resolve the Interpret Footage form against main's split into
interpret_footage_form and project_settings_form, keeping the Cineon
Settings fields and the pointUnits readback. Restore the Arc, rayon
prelude and cineon_frame imports that main's explicit use lists dropped.
2026-10-28 16:57:56 -07:00
tylerjenningsw a90b58a885 Engine: a typed prop.set can move follow components by each layer's own change (#2485) 2026-10-28 19:54:09 -04:00
dotCooCoo 0702cd23ee Merge main 2026-10-28 15:31:46 -07:00
dotCooCoo 6e8acedc0d Merge main into feat/recent-script-files 2026-10-28 14:23:49 -07:00
arnav-hiwarkarandClaude Opus 5.5 4171b6c0db Disk cache: negative free space from df counts as none (#2551)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:53:53 +05:30
arnav-hiwarkarandClaude Opus 5.5 8ab492d8cd Disk cache: apply the free-space reading where it's taken, evict off the UI thread at open (#2551)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:45:48 +05:30
arnav-hiwarkarandClaude Opus 5.5 74ea0be88f Disk cache: a failed free-space reading clears the last one (#2551)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:29:21 +05:30
dotCooCoo 509e250c4f Scripts: leave a script out of Recent Script Files when its path can't be made absolute
Services::full_path now returns None when the working directory can't be
read, instead of the relative path. file.runScript still runs the script
but does not add it to the list, so a later working directory can't make
the entry open a different file.
2026-10-28 09:37:46 -07:00
arnav-hiwarkarandClaude Opus 5.5 52dda004a5 Disk cache: evict before writing, reset the disk reading when the folder changes (#2551)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:55:33 +05:30
dotCooCoo b8b1a3a6a2 Run the File > Scripts entry when a panel shares its name
File > Scripts lists a bundled script even when an installed ScriptUI
panel has the same name, but running it by name opened the panel. A
script now runs before a panel of the same name, and it enters Recent
Script Files.
2026-10-28 06:33:44 -07:00
arnav-hiwarkarandClaude Opus 5.5 6efeae3793 Disk cache: 20 GB default, a free-space reserve and cheaper eviction (#2551)
- Default disk cache size 20 GB (was 100 GB); settings version 3 migrates the stored old
  default and keeps a size the user chose.
- The cache keeps max(2 GB, 5% of the disk) free: sysinfo::disk_space (df -Pk / PowerShell
  DriveInfo) is read in the background from memory_tick and caps the limit
  (DiskCache::set_disk_space); cache.diskStats reports limitBytes.
- Eviction goes down to 90% of the limit, so the LRU sort runs once per tenth of the limit
  written instead of on every write at the limit.
- Failed writes are counted (cache.diskStats failed) and the first is logged.

Also fixes #2063.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:27:31 +05:30
dotCooCoo 32f058ea05 Merge main into feat/wiggle-paths-spatial-phase 2026-10-28 05:39:33 -07:00
Matej Kozlovský 4407e82c62 Cryptomatte pick: keep a hover probe, built off the UI thread (#473)
effect.cryptomatteAt rendered the effect's input on every call to learn its pixel grid (up
to ~1 s per hover on a 3D precomp). The aux channels and that grid are now kept in a
CryptoProbe on the Session, keyed by the project revision, layer, effect, frame, scale and
render prefs (nested switches, draft shadows). On footage with nothing animated before the
Cryptomatte effect the frame is the source frame, so a still or a held frame keeps one
probe for every comp time. The effect's own Selection doesn't count: the probe never reads
it, so an animated Selection keeps the probe too.

Session::crypto_probe_ready says whether a query answers from the kept probe and changes
nothing. Session::crypto_probe_job builds it on another thread for the viewer; asking for a
job is starting it. Each started job has a generation: it stores its result only while it
is the newest, a newer job or an edit cancels it, and a panic in the build becomes the
probe's error. A cancelled build checks its generation between its stages (reading the IDs,
rendering the effect's input), so it stops early and lets go of what it read. `probeOnly`
makes cryptomatteAt / pickCryptomatte answer {pending} instead of building on the caller.

A pick re-keys the probe (it only edits the Selection), and undo, redo and history jumps
over picks keep it too (each pick is labelled "Pick Cryptomatte"). Any other edit, footage
changes, Open / New and Session::drop_crypto_probe (the eyedropper disarmed) let it go. The
slot's type is crate-private (the Session field stays public, doc(hidden), so other crates
can still build a Session); crypto_probe_stats and crypto_probe_jobs (jobs run, cancelled
ones included) are doc(hidden), for tests.
2026-10-28 12:29:41 +01:00
NetterSilverandClaude Haiku 5.5 3cfd7e461f Fix Align Video to Data panic on multi-byte char in timestamp
Found while testing crates/engine/src/cmd/align_data.rs:75 (parse_stamp)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 09:09:43 +01:00
Dylan M. Taylor f20d22bf36 Refactor engine for strict Clippy checks 2026-10-28 06:09:21 +00:00
tylerjenningsw c44d25b9ea Clipboard: format the keyframe text imports (#1908) 2026-10-27 19:50:41 -04:00
Anubhav RaiandClaude Opus 5 06a1e6d27b engine: entry names are compared the way a filesystem might (#2123)
Two names that differ only in case are one file on Windows and on macOS as they
come, and Win32 drops a trailing dot or space, so `clip.png` with `CLIP.png`,
and `a.png` with `a.png.`, still landed on each other: ten entries extracted to
nine files on APFS, one footage item holding the other's picture. Collisions are
now looked up by a key with the case folded and those characters off the end,
while the name written stays as the entry asked.

Found by CodeRabbit on the pull request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-28 00:32:34 +05:30
dotCooCoo e0dc5eb2ff Merge main and retain Cineon Settings in the extracted modules 2026-10-27 11:41:43 -07:00
dotCooCoo 5e32a2a39c Merge main and preserve Startup script tasks across project changes 2026-10-27 11:41:43 -07:00
dotCooCoo bb4f976384 Merge main and retain the Wiggle Transform command schema 2026-10-27 11:41:42 -07:00
Anubhav RaiandClaude Opus 5 a89fbdab8f engine: two template footage entries can't become one file (#2123)
The fallback name was never checked against the names already taken, so an
entry called exactly what the fallback produces took the same file: `media/1/a:b.png`,
`media/2/a_b.png` and `media/3/2-a_b.png` extracted to five files for six
entries, the third overwriting the second, and both items' footage pointing at
it. Deduplication now runs on the name actually written and counts up until it
is free.

The collision predates making the names safe (two entries named `a_b.png` and
`2-a_b.png` collide on their own), but sanitising gives more ways in, so it
belongs with it.

Found by CodeRabbit on the pull request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-28 00:05:19 +05:30
tylerjenningsw ef825f2947 Merge remote-tracking branch 'origin/main' into enhance/pickwhip-index-refs 2026-10-27 14:13:51 -04:00
tylerjenningsw b2cace1320 Roto Brush: test that a stroke corrects earlier ones through the commands (#1646)
Green, Alt (red) over part of the object, then green there again, as the
Layer panel sends them through roto.stroke: the render follows the last
stroke, and Undo / Redo step between the two results.
2026-10-27 14:13:40 -04:00
tylerjenningsw ecc3e673bc Merge remote-tracking branch 'origin/main' into enhance/keyframe-text-clipboard 2026-10-27 14:13:38 -04:00
tylerjenningsw b1c0a2394d Merge remote-tracking branch 'origin/main' into enhance/batch-parent-matte 2026-10-27 14:13:37 -04:00
tylerjenningsw 74b309e6cf Merge remote-tracking branch 'origin/main' into enhance/avi-import
# Conflicts:
#	crates/project/src/lib.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/menus.rs
2026-10-27 14:13:31 -04:00
dotCooCoo 8ee9bc144b Keep failed scripts and selected panels out of recent script history 2026-10-27 11:06:19 -07:00
Anubhav RaiandClaude Opus 5 14b345c333 engine: a template's footage entry can't name a file outside the folder (#2123)
EffectCraft writes its own templates as `media/<item id>/<file>` entries, and
extraction trusted both halves. `base_name` splits on `/` and `\`, but the item
id was taken with `split('/')` alone, and it is pasted in front of a base name
another item already used. So a second `media/..\..\..\Startup\x/clip.mp4`
entry became `<dir>/..\..\..\Startup\x-clip.mp4`, which on Windows is three
folders up, with the attacker choosing how the name ends.

The id is now used only when it is one, and the name goes through
`safe_file_name`: separators, drive colons and NUL become `_`, and `.` / `..`
become `footage`. Reached from `templates.create` with `path`, from File ▸ New
from Template, and from a template dropped in the user Templates folder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-27 23:34:41 +05:30
Matej Kozlovský 3ad8ba9581 CLI: log warnings to stderr by default
The CLI installed no logger, so GPU warnings (a declined frame, out of
memory, fallback to the CPU) were dropped and an export silently rendered
on the CPU. Install the engine logger with a `warn` default (RUST_LOG
still overrides). Split out of #1415.
2026-10-27 18:52:16 +01:00
tylerjenningsw 13821ab694 Puppet: test editing several selected pins at once (#1751) 2026-10-27 13:44:38 -04:00
tylerjenningsw ca5e2003a7 Puppet: a value edit on one selected pin reaches the other selected pins (#1751) 2026-10-27 13:44:38 -04:00
tylerjenningsw 68e9d9e322 Animation presets: test that saved effect keyframes start at the playhead (#1971) 2026-10-27 13:44:37 -04:00
tylerjenningsw b74b89feb5 Animation presets: effect keyframes in presets saved by earlier versions start at the playhead too (#1971) 2026-10-27 13:44:36 -04:00
tylerjenningsw 337a6c2053 Animation presets: saved effect keyframes start at the playhead (#1971) 2026-10-27 13:44:36 -04:00
dotCooCoo 572ed5d17f Preserve script keyboard shortcuts across menu module changes 2026-10-27 10:25:26 -07:00
dotCooCoo 2c2c51ca0c Preserve recent script files across menu and settings module changes 2026-10-27 10:11:33 -07:00
vangel76andClaude Sonnet 5.5 36ec54f007 Merge upstream/main into warp-skip-discarded-plan
Upstream split lib.rs files into modules and moved engine commands/ to cmd/; this branch's change merges without textual conflicts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:51:52 +01:00
Christopher Linnett c8b07b4b06 Text: duplicate during a text edit leaves no keyframe or mask vertex selected on the original (#2226)
Opening the editor keeps the keyframe and mask-vertex selection as well as the property one,
and Duplicate cleared only the properties. With the copy selected, Clear then deleted a key or
vertex of the original instead of the copy. During an edit the copies now take the selection the
way a click on them does: layer.select's rule, moved into one helper both use. With no edit
open, Duplicate's selection is unchanged.
2026-10-28 00:50:43 +08:00
Christopher Linnett 0e3b894cd9 Text: the text-edit tests count layers with the shared helper; forwarding docs name Duplicate (#2226)
The engine's text-edit tests repeated one layer-count closure in five places; they now use
the layer_count helper the 3D tests already had. The doc of forwardable_during_text_edit and
of the Type-tool test helper still spoke of Deselect All alone, though the editor now forwards
Duplicate too.
2026-10-28 00:30:00 +08:00
Christopher Linnett eeedd6b540 Text: duplicate during a text edit leaves no effect selected on the original (#2226)
Duplicate passes over the property selection an open edit kept and copies the edited layer,
but left that selection in place on the original. With the copy selected, the next Duplicate
copied the original's effect, shape item or text animator, and Clear would have removed it.
Ending the edit through Duplicate now clears that property selection along with it.
2026-10-28 00:26:52 +08:00
Christopher Linnett 9928f6181b Text: duplicate during a text edit copies the layer, not a selected effect (#2226)
Opening the editor keeps the property selection, so Cmd+D while typing duplicated an
effect, shape item or text animator selected before and left the edit open. An open edit
now comes first, as it does for Clear: Duplicate ends it and copies the edited layer. With
no edit open, selected effects and items keep their precedence, and the command's
description says so.
2026-10-28 00:22:35 +08:00
dotCooCoo 87eb51a089 Include alpha interpretation in frame-blend cache keys 2026-10-27 09:22:14 -07:00