Commit Graph
3605 Commits
Author SHA1 Message Date
vangel76andClaude Opus 5.5 fb16d5573e Tint: Rec. 601 luminance weights, as After Effects
After Effects 2026 (sRGB working space, not linearised) maps red, green,
blue and 50% grey solids at Tint's defaults to 76, 150, 29 and 128/255:
Rec. 601 weights on the working-space values, not Rec. 709. CPU and GPU
kernel changed; tests pin the four greys within 1/255.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:12:50 +02:00
SBaksa 71aafd6307 Text Range Selector: test the Timeline shows Index-unit values without % (#1814)
Start, End and Offset are read as painted in the Timeline: with % under
Units = Percentage, without it under Units = Index. Fails on the old
suffix rule ("0.0%").
2026-10-20 01:05:15 +11:00
dorfee 80158bb69b ScriptUI: install scheduled task runner in host sessions 2026-10-19 16:45:39 +03:00
SBaksa c17f9eb959 Text Range Selector: Start, End and Offset show as counts with Units = Index (#1814)
With Units set to Index the selector already counts characters (words,
lines), but the timeline kept showing Start, End and Offset as
percentages ("3.0%"), so switching looked like it did nothing. They are
now shown without the % sign while Units is Index
(render::text::in_index_units).
2026-10-19 21:08:16 +11:00
Camille Moine 8756ec1846 Timeline: Preserve Transparency toggles from the clicked row (#1849)
layer.setSwitch with no value toggles relative to the first selected layer in
composition order, so with a mixed selection (A checked, C unchecked) clicking
C's box could leave C unchanged. The checkbox now sends the value it toggles
to, as the switches do.
2026-10-19 10:25:17 +02:00
Camille Moine f72e17a2bb Engine: Video's Alt-click and Hide Other Video take cameras and lights along
The Timeline draws the Video switch (the eye) on every layer's row, nulls,
cameras and lights included, but set_switch_others and hide_other_video
skipped cameras and lights: Alt-clicking the eye of a selected layer neither
set them apart with the selection nor hid them with the rest.

Both now use the rule the eye cell shows: every layer but track mattes.
2026-10-19 10:25:17 +02:00
NetterSilverandClaude Haiku 5.5 7930fb2019 Use frozen Object Matte in sessions without the freezing model
Fixes #1457

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:09:07 +02:00
Camille Moine 4f3fa4d0fb docs: the Timeline row controls act on the selection (#1849)
One bullet in the Timeline section of docs/agents.md and a note on the
ui-parity Timeline row, next to the other multi-selection fixes.
2026-10-19 09:55:40 +02:00
Camille Moine 16d43346c4 Timeline: the label swatch and the Modes column act on the selection (#1849)
The label swatch's menu, the Blending Mode menu and Preserve Transparency
passed the clicked layer alone, so a colour or a mode had to be set one layer
at a time. They now act on the whole selection, with the Modes column edits
going only to the selected layers that have video, as its cells do.

The preserve-transparency checkbox also gets an automation id, like the cells
around it.
2026-10-19 09:55:24 +02:00
Camille Moine bf7aff11f8 Timeline: layer switches act on every selected layer (#1849)
A switch click on one of several selected layers passed that layer alone to
`layer.setSwitch`, so Lock, 3D, Video (and the rest of the A/V Features and
Switches columns) had to be set one layer at a time.

The click now gives the switch to the whole selection, with the layers it
doesn't apply to left out (3D on a camera, Effect on a layer without effects,
Audio on a layer without audio, ...). The targets are picked when the press
happens, so drawing a row costs nothing more than before. A row that isn't
selected still changes that layer alone, pressing and dragging over rows is
unchanged, and Alt-clicking an A/V feature sets the selected layers apart.
2026-10-19 09:54:16 +02:00
NetterSilverandClaude Haiku 5.5 ae5d090aed Hold position between identical auto-Bézier spatial keys
Fixes #1675

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:16:41 +02:00
NetterSilverandClaude Haiku 5.5 26636bfb18 Make Fractal Noise evolution progress linearly
Fixes #1816

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 07:00:02 +00:00
Matej Kozlovský d74cb70938 Cryptomatte pick: snap to the effect's input buffer grid, not the preview scale
At preview scales from 0.75 to 1 footage keeps native pixels, a proxy sets
its own buffer scale at any render scale, a Preserve Resolution precomp is
drawn at full size, and earlier effects can pad or offset the buffer, so
snapping to the preview grid could pick a different ID than the rendered
matte. Use the scale and offset of the buffer the Cryptomatte effect
receives (layer_input, rendered once and then cached).

The pixel centre goes to an aux point through effectcraft_effects::aux_point,
which index_map_at now calls too, so the two round identically (a division
vs the reciprocal picked the other ID on a 104 px proxy of 1920 px footage).

An unknown effect uid errors "no such effect" and a layer without pixels
"this layer has no pixels", as effect.pickColor does.
2026-10-19 08:40:16 +02:00
Matej Kozlovský 76098665b0 Cryptomatte: effect.pickCryptomatte picks the ID under a point into the Selection (#473)
Engine part of viewer picking: replace / add / remove the top-coverage ID at a
layer-space point in one undo step, at the time the Selection was read. The
aux channels come from the render's own path (Renderer::layer_aux, shared with
the effect host; footage at its source time, 3D precomps) through a renderer
set up like Session::render (expressions, render prefs, layer cache). An
optional scale snaps the point to the pixel a preview at that resolution
samples. New effects::crypto_select quotes names and falls back to <hex> for
unnamed, wildcard-looking or ambiguous names. effect.cryptomatteAt is the
read-only hover query.
2026-10-19 06:10:27 +02:00
heojunfoandClaude Opus 5.5 4bc40c7ac3 Text: grow Faux Bold per axis under a non-uniform scale (#1676)
Synthetic bold grows a glyph's outline in its own space, so a
non-uniform character Scale stretches it more along one axis. The patch
and layer bounds reserved it through mean_scale and could clip the
stretched side; they now reserve how far the bold reaches along x and y.

A run of solid fills grows its glyphs together in layer space by one
radius, which only matches when the bold stays round there: a bold glyph
whose matrix stretches or skews is no longer gathered into a run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 11:04:36 +09:00
Vikram Bhaskaran d3fd2c58d7 Key browser preview caches on a file generation, and hash bytes only when that key misses.
Replacing a virtual file bumps a counter the UI lookup reads. The content hash runs on that miss, from a callback, so the frame path does not stat every footage item or hash on every store write.
2026-10-19 13:04:16 +11:00
heojunfoandClaude Opus 5.5 a5f096324d Text: draw Faux Bold and synthetic bold (#1676)
The layout marks a glyph synth_bold when Faux Bold is on, or when a bold
style is asked of a family without a bold face, but the renderer never
read it, so both drew as regular.

PlacedGlyph now carries the bold radius (0.03 em, which brings Inter
Regular's stems to Inter Bold's). A bold fill is the outline's coverage
merged with a round stroke twice that wide, and a bold glyph's stroke is
the band between the outline grown by radius ± half the stroke width, so
it follows the bolder shape. Runs of solid fills only merge glyphs of the
same bold radius, and the patch and layer bounds grow by it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 10:36:16 +09:00
vangel76andClaude Sonnet 5.5 24612b0eaf Merge upstream/main into gap-behaviors-presets
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 02:02:16 +02:00
Davit MatchakhelidzeandClaude Opus 5.5 7999754a13 i18n(ka): Georgian rows for the panel strings main added; drop one it no longer uses
Copy, Paste, Copy Source Path, Guide Layer and Layer Styles join GEORGIAN in the Chinese order;
Edit Value…, Remove Expression and Effects move there from GEORGIAN_PANELS now that the Chinese
catalogs carry them; Freeze on Last Frame is only a menu command now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 03:20:50 +04:00
Davit MatchakhelidzeandClaude Opus 5.5 5a069af3fd Merge i18n-improvements (main merged)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 02:58:18 +04:00
Davit MatchakhelidzeandClaude Opus 5.5 7d03c0a1f6 Merge main; context menus use upstream's context_item (#1308)
Upstream now translates the Timeline, Composition and Effect Controls context menus itself, so
command_label is left for the dock's tab titles and the workspace bar, on menu_catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 02:58:04 +04:00
Kristian MyramandClaude Sonnet 5.5 0c70ee026b WAV markers: the host test covers the bound layers parameter and undo/redo
The end-to-end test called Update Markers From Source only through the
selection. It now also passes the layer as the bound `layers` parameter (what a
script or the menu binding supplies), checks the markers by name, and checks
that the update is one undo step: undo removes the restored marker again and
redo brings it back.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 00:56:32 +02:00
Kristian MyramandClaude Sonnet 5.5 620d340d15 WAV markers: read RF64 files and bound the adtl list
Two findings from the review of #1423.

RF64 (WAVs over 4 GB): a chunk too big for 32 bits has a size field of
0xFFFFFFFF and its real size in the file's `ds64` chunk. The reader took the
field literally, so it stopped at the `data` chunk and never reached the
`cue ` and `_PMX` chunks that follow it. It now resolves such sizes through
`ds64` (the `data` size, and a table of other chunks, capped at 1,024 entries),
with checked arithmetic, and leaves the field as it was when there is no
usable `ds64`.

The `adtl` list: every `labl` record scanned the growing list of names, which
is quadratic in the number of records, and nothing bounded how many were
kept. The cue points are now read first, names and lengths are kept only for
those ids in hash maps, and at most 40,000 `adtl` sub-chunks are looked at.
The duplicate check between the cue and XMP markers uses a set instead of a
scan. What a file yields is unchanged for any file within the bounds: a `labl`
still wins over a `note`, and the first `ltxt` still gives the length.

Tests: an RF64 file whose markers follow a sentinel-sized `data` chunk, sizes
taken from the `ds64` table (and not taken when the table has no entry),
hostile `ds64` sizes and tables and every truncation of an RF64 file, and a
100,000-record `adtl` list that is read in linear time, with the cap applied.
The RF64 tests fail with the previous reader.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 00:56:32 +02:00
iannitramandClaude Opus 5.5 c1d411b249 Tests: serialise disk-cache-opening tests with the roto tests
The Roto/Object Matte segment store is one per process (roto::set_seg_store),
and every Session::configure_disk_cache that opens a cache takes it over. The
roto and disk-cache tests already serialise on hold_roto_cache_test_lock(), but
many other engine tests build a Session with a settings store, which opens the
disk cache at the platform folder and takes the store; once that session drops,
the weak store is dead and object_matte_mattes_persist_in_the_disk_cache sees
no disk writes. It failed 3 of 3 in the full parallel suite here.

configure_disk_cache now takes that lock under cfg(test) before opening a
cache, so every such test runs apart from the roto tests. Re-entry on the same
thread is a no-op. Product code is unchanged: only the app's one session ever
opens a disk cache, so users cannot hit the race.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:51:47 -06:00
Kristian MyramandClaude Sonnet 5.5 79dec68d63 Marker names: a short duration stays visible beside the name box
When a named marker's duration ended inside the name box, the box's fill covered
the duration's end cap and most of its bar, so the duration could not be seen.
The box now steps past the end cap in that case (and stays at the flag when the
bar is longer than the box, or ends before it).

The new test puts a 0.1 s duration on a named marker, takes the timeline's scale
from a second marker, and checks that the cap and the bar are drawn and that the
box starts after the cap; it fails with the previous drawing. The two render
tests now take a lock, because two tests creating a GPU device at once crash
some Vulkan loaders.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 00:35:59 +02:00
sanyamsingh001andCodex 9bcf2e2dfd Use symmetric HexTile cell coordinates on CPU and GPU
Keep the existing regression and tolerance while preserving upstream
WireRemoval and test-service recovery changes.

Co-authored-by: Codex <codex@openai.com>
2026-10-18 21:48:26 +00:00
Higgs32584andClaude Opus 5.5 88f940d54e Export: CPU cap bounds images waiting to encode and survives a GPU stop (#1615)
Review of #1763:
- With EFFECTCRAFT_CPU_INFLIGHT set, each window is the CPU cap instead of
  one frame per thread, so rendered images held before encoding count
  against the cap too (a cap of 2 on 16 threads held up to 16 images).
- Once the GPU stops for good, the GPU cap lapses and the CPU cap alone
  applies, instead of the smaller cap (often the GPU default of 1) holding
  for the rest of the batch.

The caps are now an `Inflight { gpu, cpu }` that `map_frames_while`
re-evaluates per window and chunk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:05:03 -04:00
Kushagra Kumar f7922a9fba test(engine): add undo and redo assertions for queued still frame export 2026-10-19 02:11:05 +05:30
Kushagra Kumar aacf09a5ca fix(render): use pixel units for text animator tracking amount
In After Effects, text animator Tracking Amount adds the value in absolute
pixels between neighbouring characters, independent of font size. EffectCraft
previously scaled tracking by 1/1000 em (matching static Character-panel
tracking), causing tracking reveals to have significantly reduced spread.

Apply Tracking Amount directly in pixels during glyph positioning layout,
update existing tracking tests to reflect pixel units, and add a regression
test verifying font-size independence.

Fixes #1798
2026-10-19 02:07:17 +05:30
Kushagra Kumar 791e6bb5eb fix(engine): preserve alpha channel when saving still PNG frames
Exporting still frames via comp.saveFrameAs previously flattened the
rendered frame against the composition background color, stripping
the alpha channel. Single still frames queued in the Render Queue also
defaulted to an RGB-only PNG sequence output module.

Preserve straight alpha by default in comp.saveFrameAs (while honoring
transparent: false if requested), and set TemplateSlot::Still to default
to 'PNG Sequence with Alpha'.

Fixes #1815
2026-10-19 01:59:48 +05:30
Michael Higgins 14dd6d333d Merge branch 'storytold:main' into fix/1615-cpu-inflight-cap 2026-10-18 15:02:44 -04:00
Dylan M. Taylor 75fce6b2d8 docs: put SonarCloud and CodeRabbit badges on their own line 2026-10-18 14:47:37 -04:00
Dylan M. Taylor ef74d82902 ci: configure Dependabot for Cargo and GitHub Actions
Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
2026-10-18 12:31:27 -06:00
Anubhav-RaiandClaude Opus 5 3b9d243ac5 Shift Channels: After Effects' Take … From menu order, so 10 is Full Off (#1797)
The menu had an extra "Half On" at position 10, where After Effects has
"Full Off", so a project or script written for After Effects set a channel
to 50 % grey instead of turning it off, with no error: the RGB-split recipe
(three copies, each keeping one channel and setting the other two to Full
Off, blended with Add) rendered pastel instead of pure red, green and blue.

After Effects' ten options now come first, in its order, and "Half On" sits
last as an EffectCraft extra, so values 1 to 10 mean what they mean in After
Effects and nothing is lost. Instances saved with the old order keep the
option they chose: upgrade_instance already remaps popup values by label.

The GPU path keeps its own copy of the table, so it is reordered to match;
a GPU-vs-CPU parity case now covers the constant sources at the end of the
menu, where the two tables could drift apart unnoticed. Both paths clamped
the index to a hardcoded 10 and now use SHIFT_ORDER.len() - 1, so the clamp
cannot go stale if the list changes again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-18 23:34:25 +05:30
tylerjenningsw f52ffb63ce Viewer: no proxy notice on a collapsed precomp, which draws its own layers (#1701) 2026-10-18 12:59:24 -04:00
tylerjenningsw 70ad68eb85 Preview: the Extended Viewer startup test doesn't race the first frame (#947)
startup_finishes_when_extended_viewer_frames_are_larger failed on busy
machines. The harness clock moves 1/30 s per step however long a frame
takes, so a slow first frame could use up the startup's limits in
harness time. The test now waits for the first frame in real time
before stepping on, as it is about the wait for the frames ahead, and
checks the fake device was started and not stopped.
2026-10-18 12:58:09 -04:00
tylerjenningsw ef60d3db9d Preview: a slow first frame keeps the opening sound (#947)
Under load the first frame could take longer than STARTUP_WAIT to be
cached. The start then gave up, started the sound on that uncached
frame, and the stall path stopped it in the same step, so the opening
played silently.

STARTUP_WAIT (2 s) now counts from when the first frame is cached, and
only bounds the wait for the frames ahead. A new STARTUP_LIMIT (10 s
from Play) still ends the wait for a first frame that never gets cached.
The sound is never started on a frame that isn't cached: the preview
goes on as a stalled one does, and the sound starts once frames are in.

Tests (ui_audio_scrub): a first frame held uncached for 3 s still gets
its sound from frame 0, and the device keeps playing (fails on the
previous commit). The two give-up tests now check that no sound is
started and dropped.
2026-10-18 12:58:09 -04:00
Dylan M. Taylor 20c03451c0 docs: describe color grading panel, scope SDK plug-in warning, qualify rendering tests, and link parity checklist 2026-10-18 12:51:59 -04:00
tylerjenningsw d2ae0bd503 Merge main into enhance/avi-import 2026-10-18 12:45:36 -04:00
tylerjenningsw dd50e42486 Merge remote-tracking branch 'origin/main' into enhance/pickwhip-index-refs
# Conflicts:
#	crates/ui-egui/tests/ui_inline_edit.rs
2026-10-18 12:38:44 -04:00
Charles BaumerandClaude Opus 5.5 bd961131a4 Media tests: ffmpeg fixtures survive concurrent first runs on Windows
Tests that share a fixture each generate it on a first run and rename
their temp file into place. On Windows the losing rename fails with
PermissionDenied while the winner has the file open. Treat a failed
rename as "another test made it first" when the fixture exists, as
`png_sequence` already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:23:28 +00:00
Charles BaumerandClaude Opus 5.5 4fa31b5e5b Engine tests: clippy passes on Windows (Stills is Unix-only)
`Stills` is only used by the `#[cfg(unix)]` part of
`footage_on_other_computers_is_never_looked_at`. On Windows it was dead
code, so `cargo xtask ci` stopped at clippy (`-D warnings`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:12:55 +00:00
Davit MatchakhelidzeandClaude Opus 5.5 6c6b54a182 Georgian interface text draws from an installed font
Georgian joins the installed-font fallback like Thai, Hebrew and the Indic scripts:
Sylfaen on Windows, SF Georgian on macOS, Noto Sans Georgian or DejaVu Sans on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-18 19:54:25 +04:00
Claude bccbc666a2 #1417: WAV markers show on the audio layer
A WAV's markers were never read, so its audio layer showed none. The importer
now reads the file's cue points (`cue ` chunk named by `LIST/adtl` `labl` and
`note`, sized by `ltxt`) and Adobe's XMP markers (`_PMX` chunk, `xmpDM:markers`
entries as elements or attributes, in frames of the track's `xmpDM:frameRate`).
A marker present in both encodings is listed once. The reader is bounds-checked
and caps the marker count and XMP size.

- `Footage.markers` (optional in the file format) holds them at footage time; a
  layer made from the footage starts with them.
- Layer ▸ Markers ▸ Update Markers From Source also works on footage layers.
- Markers from a file have no label colour.

Tests: reader unit tests (cue, note, ltxt, both XMP forms, both encodings,
truncated and hostile files), a probe test, and an end-to-end test through the
fully wired session. Not checked against a WAV written by Adobe's tools.

Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 17:38:44 +02:00
Kristian MyramandClaude Sonnet 5.5 4124ec6de7 Marker names are drawn in a small outlined box, in theme colours
A named marker drew its name as bare text beside the flag. After Effects puts
it in a small outlined box. The box uses the theme's field fill, field outline
and text tokens, so it follows Dark, Darker and Light instead of a fixed grey.

It applies to every marker with a name, composition markers included; the dimmed
names of nested-comp markers on a precomp layer's bar are unchanged.

Split out of the WAV markers change (#1423), where the review asked for it to be
its own decision. Test: ui_marker_names renders the timeline in Dark and in
Light, samples the box's fill and outline pixels, and checks they equal that
theme's tokens and differ between the themes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 17:38:28 +02:00
Brandon Thomas 22072d9f25 Merge pull request #1317
# Conflicts:
#	crates/ui-egui/src/panels/effects_presets.rs
#	crates/ui-egui/src/panels/timeline.rs
2026-10-18 11:12:44 -04:00
Brandon Thomas 033890fdcf Merge pull request #1802 2026-10-18 11:12:27 -04:00
Dylan M. Taylor 99bdcde092 docs: add SonarCloud and CodeRabbit badges to README 2026-10-18 11:01:31 -04:00
FredApps b0acbbe121 CLI: regression tests for render --progress (#1778) and template media values (#1787)
- render --progress with a piped stderr writes well-formed
  `progress <done> <total> <elapsed> <left|->` lines, one per change, and
  the same render without the flag writes none.
- A template's Media Replacement control takes {"path"|"file"|"media": …},
  is found by name ignoring case, and a path that cannot be imported fails
  with an error naming the control and the path.
2026-10-18 16:46:06 +02:00
Dylan M. Taylor cb70a0cdbe docs: replace Lumetri effect reference with generic color grading 2026-10-18 10:42:59 -04:00