Commit Graph
1199 Commits
Author SHA1 Message Date
sanyamsingh001andCodex a7a6295bb0 Issue 257: support explicit keyframe time bases
Co-authored-by: Codex <[email protected]>
2026-10-10 11:12:14 +10:00
Brandon ThomasandClaude Opus 5.5 813c7c4650 Bump version to 0.7.0 (from 0.6.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v0.7.0
2026-10-09 21:02:08 -04:00
Mara Johnson 6f0369b3a8 fix(engine): layer.setText schema requires optional OpenType keys (#520)
- Mark OpenType? and ss01? as optional in params doc
- Fix clippy lint: use as_chunks instead of chunks_exact
2026-10-09 20:57:21 -04:00
Luniiya fe27c375e3 feat: standardise system appearance modes with PhotoCraft 2026-10-10 02:15:26 +02:00
Dylan M. Taylor 675fc55303 chore: bump github actions to fix Node 20 deprecation warnings 2026-10-09 20:08:26 -04:00
SawizzleandClaude Opus 5.5 fbe028717e docs: register the MCP server from an installed release
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:06:42 -07:00
tylerjenningsw 4359899f7a Project: a Cut is read with the modifiers held where it is in the frame (#484)
Cause: clipboard events carry no modifiers, and the Cut and Copy/Paste
mapping read i.modifiers, egui's state after all of the frame's events.
A Shift+Delete with Shift released in the same frame became Ctrl+X (no
deletion), and a Ctrl+X followed in the same frame by Ctrl up and Shift
down became Shift+Delete: the selected items and the layers using them
deleted without the prompt.

Fix: walk the frame's events in order from the modifiers held when it
began (the state the last pass ended with, recorded each pass), so each
Cut, Copy and Paste uses the modifiers at its place in the stream. When
that state isn't known (a pass was skipped), a Cut is never Shift+Delete.
Only Shift alone counts, not Ctrl/Alt+Shift.

Tests: a_cut_is_read_with_the_modifiers_where_it_is_in_the_frame queues
modifier changes and a Cut in one frame (Shift then Cut and Shift up;
Ctrl, Cut, Ctrl up, Shift down; and the mixed orders); the Windows
Shift+Delete helper now sends Shift, Cut and Shift up as one frame. They
fail on the previous mapping and pass now.
2026-10-09 19:55:48 -04:00
mjohnson7cdandClaude Opus 5.5 c195e6c40b fix: CLI --bitrate now applies to VP9 WebM (#440)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 18:50:13 -04:00
sanyamsingh001andCodex e4ca1e919a G2: return to Settings after dismissing GPU Information
Preserve pending preference edits through the existing OK and Cancel actions.

Co-authored-by: Codex <[email protected]>
2026-10-10 08:46:03 +10:00
daijroandClaude Opus 5.5 ddfa566d7d Render: 8 and 16 bpc quantisation rounds with floor in the browser
Cause: quantize_px rounded with `(v + 0.5) as u32 as f32`. Converting back
from u32 is several instructions in V8's wasm code on x86 (no unsigned
conversion), so quantising a 1080p layer took about 15 ms in the browser:
every 8/16 bpc layer is quantised after its source and every effect, and
the CPU compositor quantises the comp after every layer.

Fix: round_half_up uses floor in wasm (one instruction there) and keeps the
integer conversion elsewhere (x86-64's baseline has no floor instruction,
where floor is slower). The values are the same: both round non-negative
values half up. About 1.5x faster quantising in the browser (15 -> 10 ms
per 1080p layer in a wasm micro-benchmark), unchanged on the desktop.

Tests: color::tests::rounding_forms_agree: both forms agree on 0..=levels
in sixteenths (exact halves included) and on 100 001 products, at 8 and
16 bpc.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-09 16:44:49 -05:00
daijroandClaude Opus 5.5 74b151cedc Footage: reduced-resolution renders get movie frames converted at their size
Cause: at Half, Third and Quarter resolution (the viewer's Auto resolution
in most windows) every movie frame was converted from Y'CbCr to f32 RGBA at
full size and then resampled down. In the browser (wasm, one thread) that
was about 8 ms converting a 1080p frame, 6 ms resampling it and 2 ms
zeroing a 33 MB buffer, per layer and frame, for a frame shown at a
quarter or a ninth of its pixels; the footage frame cache held the
full-size frames.

Fix: FootageSource::frame_at_size asks for a frame shown at a given size
(default: the full frame, as before). The renderer asks for it where it
resampled footage (below 3/4 scale; not for proxies, fields or Interpret
Footage's pixel options, which need the full frame). MediaPool makes opaque
4:2:0 movie frames of the footage's own size there: it box-filters them to
half size in Y'CbCr before converting (each 2x2 block of luma, and the
[1 6 1]/8 neighbourhood of its chroma sample, which is the mean of the
upsampled chroma its pixels get), resamples the rest of the way and caches
the result per size. Conversion is linear until colours clip, so this is
the RGB box filter resample starts with, except in blocks where some
colours clip. Other frames convert at full size and resample as before.
In the browser the conversion and resampling take about 4 ms instead of
16. Docs (architecture).

Tests:
- convert::scaled_conversion_matches_resampling_the_converted_frame: half,
  third, quarter and odd sizes of an unclipped 4:2:0 frame are within 1e-5
  of resampling the converted frame; larger sizes, odd frame sizes and
  4:4:4 convert at full size.
- tests_footage_size::reduced_renders_take_footage_resampled_by_the_source:
  a source that resamples frames itself renders the same frames as one
  that doesn't, at 8 and 32 bpc and every resolution, and is asked only
  below 3/4 scale, and not with Invert Alpha.
- footage::h264_frames_at_reduced_sizes (ffmpeg fixture): 1080p H.264
  testsrc2 frames, whose saturated colours clip, at 960x540, 640x360,
  480x270 and 323x182 are 58-62 dB from the resampled full frames, and
  are cached per size.
- convert's ignored 1080p bench also times the conversion at a third.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-09 16:44:49 -05:00
daijroandClaude Opus 5.5 2ccc66e73b Web: the layer and footage caches stay small enough for eight stacked clips
Cause: the browser build kept the desktop's cache budgets (1 GB layer
cache, 1 GB footage frame cache) inside the page's memory, which wasm32
caps at 4 GiB. FilmCraft's decoders keep up to 384 MB of decoded frames
per open movie beside them, so a comp of eight stacked 1080p H.264 clips
grew the page by about 55 MB/s while playing and ran it out of memory
after about 50 s ("unreachable": a failed allocation).

Fix: in the browser Prefs::cache_budgets keeps the Layer Cache within
512 MB and the Footage Frame Cache within 256 MB (WEB_LAYER_CACHE,
WEB_MEDIA_CACHE); smaller settings and the desktop are unchanged, and
the RAM preview keeps its budget. Docs (web, preferences) say so.

Tests: tests_m145::web_cache_budgets_leave_room_for_the_decoders: with
4 GB settings the web budgets are the caps, the desktop's are the
settings, and smaller settings stay as they are.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-09 16:44:15 -05:00
sanyamsingh001andCodex c37b88602f G3: fix frame-rate boundaries and bounded timecode arithmetic
Preserve relative MIN-field cancellation with direct saturating addition and subtraction.

Co-authored-by: Codex <[email protected]>
2026-10-10 06:40:02 +10:00
tylerjenningsw 80a23372df Docs: gaps.md records the #484 Shift+Delete tests 2026-10-09 16:27:50 -04:00
tylerjenningsw a65d70f10e Project: Shift+Delete deletes the selected items without asking (#484)
With the Project panel focused, Delete Project Items Without Confirmation
runs project.delete on the selection: items compositions use go without
the prompt, with their layers, in one undo step. In the other panels it
does nothing, and like every shortcut it doesn't fire while typing or
with a dialog open.

On Windows the windowing layer sends Shift+Delete as Cut (the old Cut
key), which was mapped to Ctrl+X: in the Project panel, which has no
clipboard, a Cut with only Shift held is Shift+Delete now. Elsewhere it
still cuts.

Tests (ui_project_delete): Shift+Delete as a key and as Windows' Cut
deletes a comp, a solid in use, footage and a folder without the prompt
and one undo restores them; a multi-selection with a folder holding a
solid in use goes together; an empty selection deletes nothing; the next
Delete still asks after Shift+Delete or an answered prompt; it does
nothing in the Composition panel, while renaming or with a dialog open;
Shift+Backspace, Alt/Ctrl+Delete and Ctrl/Alt+Shift+Delete don't delete.
The deleting tests fail without this change.
2026-10-09 16:27:48 -04:00
tylerjenningsw 01ba8a85e9 Project: answering the Delete Items prompt confirms that deletion only (#484)
The prompt's Delete set a flag that let the next project.delete skip the
prompt. It now runs project.delete directly (delete_items::delete_confirmed),
so the confirmation goes with that request and can't carry over to a later
deletion. Shift+Delete uses the same path.
2026-10-09 16:27:21 -04:00
tylerjenningsw f59acb5b80 Project: Delete Project Items Without Confirmation is a command, on Shift+Delete (#484)
A frontend command (project.deleteWithoutConfirmation) so the shortcut is
listed and rebindable in Keyboard Shortcuts and runs by id over the
control channel and MCP. The UI runs project.delete for it.
2026-10-09 16:24:53 -04:00
MarekSpodBiedry 6c31247ceb EXR: do not double the part name of Blender 5 per-pass parts (#481) 2026-10-09 21:53:30 +02:00
MarekSpodBiedry 56cd3f1bf5 Expressions: Add Expression applies to the selected properties (#476) 2026-10-09 21:13:46 +02:00
MarekSpodBiedry bca027de6a Scripting: importFile(ImportOptions) imports one file unless sequence is set (#475) 2026-10-09 21:12:22 +02:00
Dmytro MatviichukandCodex 4823c08d9c G9: expand Ukrainian UI coverage (#333)
Translate the 396 current panel catalog rows and route Ukrainian Reveal
labels for Finder, Explorer and File Manager. Enforce catalog parity,
placeholder preservation and platform label coverage; update language docs.
Use a fixed-size OCIO row iterator to satisfy the current Clippy gate.

Co-authored-by: Codex <[email protected]>
2026-10-09 20:54:14 +02:00
sanyamsingh001andCodex cac47626f3 fix(engine): preserve supported 3D parenting compensation (#348)
Convert Position values and spatial tangents into the new parent space.
Preserve supported static Orientation and Scale in constant uniform
parent spaces, and convert spatial Position ease speeds for uniform
transforms. Retain the compensation and Undo/Redo regressions.

Refs #348.

Co-authored-by: Codex <[email protected]>
2026-10-10 04:51:15 +10:00
tylerjenningsw 4aea350f2a Packaging: the installer's Repair puts back a damaged effectcraft-cli.exe (#466)
Cause: Repair used REINSTALLMODE=ecmus. effectcraft-cli.exe has no version
resource, so Windows Installer treats it as unversioned: a changed copy
with a later write time counts as a user edit and is kept, and "c" only
checks files that carry an MSI checksum, which these don't.

Fix: Repair uses "amus", which puts back every file whatever its version
or date.

Tests: test-msi.ps1's stand-in CLI is unversioned like the real one; the
test damages it in place and repairs with the REINSTALLMODE read from
installer-ui.wxs (no longer msiexec /fa after deleting it), then compares
contents. It fails with ecmus (the damaged file stays) and passes now.
2026-10-09 14:49:19 -04:00
tylerjenningsw 462913a483 Packaging: upgrading a custom-folder install from 0.6.0 or earlier keeps its folder (#466)
Cause: the folder search read only the App Paths "Path" value, which this
branch introduced. 0.6.0 and earlier could already be installed elsewhere
with INSTALLFOLDER=..., but have no Path value, so an upgrade went to
Program Files while the wizard said it would stay in the same folder.

Fix: when there is no Path value, a second search reads the App Paths
default value (the full path of effectcraft.exe, written by every
release). A "file" registry search with no FileSearch inside yields the
folder of that file.

Tests: test-msi.ps1 installs a legacy package (the same .wxs without the
Path value) into a custom folder, then upgrades it without naming a
folder; it fails on the previous .wxs (the upgrade lands in Program
Files) and passes now. A further upgrade checks the Path value route.
2026-10-09 14:45:47 -04:00
Ikki NishiharaandClaude Sonnet 5.5 a2dfd6631a i18n: Japanese for the panels, dialogs and tooltips
ui.rs gets a Japanese table with the same 396 rows as the Simplified and
Traditional Chinese ones, so Japanese now translates the interface below
the menu bar too (it covered the menus only). The shape test keeps the
three tables listing the same source strings; a new test checks that
Japanese reads its own rows and fills values in order. The existing
"no rows yet" check uses Ukrainian instead.

ROADMAP.md and docs/preferences.md say which catalogs have panel rows.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-10 03:17:26 +09:00
tylerjenningsw 184df34875 CI: the MSI test reads Apps entries one value at a time (#466)
Get-ItemProperty throws on a malformed Uninstall entry, which aborted the
upgrade check on a real Windows 10 machine. Failures now print their message.
2026-10-09 14:11:53 -04:00
tylerjenningsw 2412173b7e Contributors: tylerjenningsw's display name is tylerjenningsw 2026-10-09 14:05:49 -04:00
tylerjenningsw 6c58eea643 Contributors: credit tylerjenningsw as Tyler Jennings 2026-10-09 14:05:18 -04:00
BFlat 30aaddf7c2 Merge pull request #459 from storytold/fix/394-408-arabic-flac
Arabic, Hebrew, Indic and Thai names; FLAC preview opens once and off the UI thread (#394, #408)
2026-10-10 01:04:24 +07:00
tylerjenningsw 5d58fd4029 Docs: releasing.md describes the MSI install folder and its test (#466) 2026-10-09 14:00:46 -04:00
tylerjenningsw 7dc2b7a381 README Downloads: the Windows installer lets you pick the folder (#466) 2026-10-09 14:00:29 -04:00
tylerjenningsw f2d335b138 CI: the MSI is install-tested with a custom folder, upgrade, repair and uninstall (#466)
test-msi.ps1 builds the MSI around stub binaries, validates it (ICE) and,
silently: installs into a folder with spaces and a non-ASCII name, upgrades
without naming a folder (it must stay there, one entry in Apps), repairs a
deleted file, uninstalls (files and App Paths gone), and installs to the
Program Files default. Run by a windows-latest job in packaging-lint.yml.
2026-10-09 14:00:09 -04:00
tylerjenningsw 9ceda6a8f5 CI: packaging lint checks every Windows .wxs file (#466) 2026-10-09 13:59:46 -04:00
tylerjenningsw b32965f209 Packaging: the Windows installer asks where to install EffectCraft (#466)
A fresh install opens on a destination page (Program Files by default;
Change... browses, with Up and New folder), then a Ready page that shows
the folder before anything is written. An upgrade goes straight to the
Ready page with the installed copy's folder and can't move it; opening the
MSI again offers Repair or Remove. Cancel, finish, files-in-use and error
dialogs are authored too. Silent installs (/qn) show none of it.

The dialogs (installer-ui.wxs) are original, built from Windows Installer's
own controls and events, not the WixUI library, whose MS-RL licence is not
on the allowed list. package.ps1 builds both .wxs files.
2026-10-09 13:58:35 -04:00
bflatasticandClaude Opus 5.5 09a5dc357a Audio: a FLAC opens once and off the UI thread; conforming is bounded (#408)
The freeze and quit during a FLAC preview did not reproduce on current
main (Windows 11): a 4-minute stereo and a 10-minute 24-bit 6-channel
96 kHz FLAC played through a RAM preview with a real-time audio pull,
after trimming and zooming, with no stall, no growing queue and memory
bounded by the RAM preview budget; 150 cpal output start/stop cycles
ran clean. Three defects in that path were found and fixed:

- FilmCraft decodes FLAC, MP3, Ogg and AIFF files whole when they are
  opened (8 s and 1.4 GB for the 10-minute 6-channel file). MediaPool
  opened a file outside its lock, so every caller asking before the
  first open finished decoded the whole file too: a preview's frame
  workers, audio feeder, conform and waveform threads at once (8 reads
  at once: 8 decodes). Each path now opens once; the others wait for it.
- AudioPlayback::start mixed its first 50 ms on the calling (UI)
  thread, so the first read of such a file froze the UI for the whole
  decode (audio-only previews start the sound right away). All mixing
  is on the feeder thread now; the device plays silence until the first
  block arrives, and the clock (frames actually played) waits for it.
- Conforming (Settings > Disk > Conformed Audio Folder, on by default)
  held the whole conformed track in memory before writing it, sized by
  the footage duration from the file's header: a header claiming a
  month of audio aborted the app on a failed 995 GB allocation. The
  conformed file is now written a chunk at a time, and footage longer
  than 24 hours is read from its decoder instead of conformed.

Tests: media concurrent_first_reads_open_a_file_once (8 threads: 8
opens before, 1 now; PoolStats::opened), pool
a_header_claiming_a_month_of_audio_is_not_conformed (aborted before),
ui-egui audio starting_audio_does_not_wait_for_slow_footage (start took
the footage's 2 s before), conformed_flac_audio_reads_back_like_the_
decoder, and the robustness test flac_audio_of_any_shape_reads_in_
device_blocks (2 minutes of 24-bit 6-channel 96 kHz, mono 8 kHz,
11.025 kHz, 192 kHz and 8-channel FLAC read in 1024-frame blocks at
48 kHz through and past the end: level, silence after the end, opened
once). FLAC fixtures come from ffmpeg (skipped without it).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:53:21 +07:00
bflatasticandClaude Opus 5.5 98a6a3a041 UI: Arabic, Hebrew, Indic and Thai names can be typed and are drawn (#394)
Cause: two things kept Arabic out of a layer name on Windows.
- Windows switches the keyboard layout with Alt+Shift. Releasing Shift
  before Alt left Alt held alone, and the in-window menu bar (#279)
  counted the release as an Alt tap: it took the keyboard focus, the
  rename field closed, and the letters typed next were swallowed by the
  menu bar. The Project panel's rename field lost them the same way.
- The UI fonts are Inter plus the CJK system fallbacks, so Arabic,
  Hebrew, Indic and Thai names that did arrive (pasted, or from a
  project) showed as missing-glyph boxes.

Fix:
- An Alt tap only counts when Alt went down with no other modifier
  held, so Alt+Shift in either release order is no tap; a plain Alt tap
  still focuses the menu bar.
- The UI registers installed system faces for Arabic, Hebrew, Devanagari,
  Bengali, Gurmukhi, Gujarati, Tamil, Telugu, Kannada, Malayalam and Thai
  after the CJK ones, the same way (native only; the web build has no
  system fonts). A face covering several scripts is registered once
  (Segoe UI: Arabic and Hebrew; Nirmala UI: every Indic script).
- The text engine's fallback tries each script's plain platform families
  (Segoe UI, Nirmala UI, Leelawadee UI, Geeza Pro, Kohinoor, Noto…)
  before any covering face, so neither the UI nor a text layer gets a
  calligraphic face that merely sorts first.
- The Timeline's rename field registers `timeline.layer.<id>.nameEdit`.

egui 0.36 shapes each run of one face, so Arabic letters join and a word
reads right to left; it has no bidirectional layout, so the words of a
multi-word Arabic name (and mixed Arabic/Latin) still follow each other
left to right.

Tests: ui_inline_edit a_layout_switch_keeps_the_name_field_and_arabic_
names_are_kept (Alt+Shift both ways in the layer rename, Arabic typed and
stored; Hebrew/Devanagari/Thai in the Project panel; a plain Alt tap
still focuses File; failed before: the field closed), theme
installed_script_fallbacks_draw_arabic_hebrew_indic_and_thai_names
(failed before: replacement glyph for Arabic) and
arabic_words_are_joined_and_read_right_to_left, text
other_scripts_fall_back_to_a_plain_system_family and the script
classification.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:53:21 +07:00
BFlat 921f0dbdbe Merge pull request #454 from storytold/fix/397-particle-world-camera
Effects: CC Particle World follows the comp camera (#397)
2026-10-10 00:52:58 +07:00
tylerjenningsw 8b8a57780f Packaging: Windows upgrades and repairs install into the existing folder (#466)
The MSI records its folder as the App Paths "Path" value of effectcraft.exe
and reads it back with a registry search, so an upgrade, a repair or a
silent reinstall goes where the installed copy already is. A folder passed
on the command line (INSTALLFOLDER=...) still wins, and INSTALLFOLDER is
Secure so a folder chosen in the UI reaches the elevated install. v0.6.0 and
earlier have no Path value and upgrade in Program Files, where they are.
2026-10-09 13:51:44 -04:00
Vikram Bhaskaran db6b2e8079 Relink missing footage by scanning a folder for compatible files.
A same-named file is used only when kind, size, codec, and for moving pictures the rate and duration agree. Symlinks are not followed, and duplicate names are left alone.
2026-10-10 04:37:44 +11:00
bflatasticandClaude Opus 5.5 923d978119 Effects: CC Particle World follows the comp camera (#397)
Cause: CC Particle World projected its particles through a fixed camera
(distance 2 layer widths, looking at the layer centre) and never asked the
host for the comp camera, so moving or orbiting a camera layer left the
particles flat on the layer. It also had no Extras > Effect Camera. Separately,
the layer cache key ignored the comp camera and lights, so the effects that
read them (Card Dance / Shatter / Card Wipe Comp Camera, First Comp Light,
Caustics) kept a stale frame when the camera, a light or the layer moved.

Fix: as the Cycore manual describes, Particle World sees its particles through
the comp's active camera layer when there is one (EffectHost::comp_scene now
says whether its camera is a camera layer's, CompScene::camera_layer), through
card3d's comp-camera projection (factored out of Card Dance as comp_proj, with
Proj::view for depth, size scale and screen velocity). Otherwise it uses the
new Extras > Effect Camera: Distance (2), Rotation X / Y / Z (0) and FOV (50)
orbit the world origin; FOV moves the camera so the origin's plane keeps the
scale Distance gives it, so the defaults reproduce the old fixed camera bit for
bit (golden hashes unchanged) and old projects render as before. The Effect
Camera is left out of the simulation cache key, so animating it doesn't
restart the simulation. The plan is shared, so the GPU compositor follows too.
The layer cache folds the comp scene into the key of layers running an effect
in effects::READS_COMP_SCENE (render::comp_scene is shared with the host).

Tests: sim particle_world_follows_the_comp_camera (panning a camera layer moves
the particles, zooming spreads them, the Effect Camera is ignored then, the
default view keeps the Effect Camera, cameras facing away / zero / NaN draw
nothing) and particle_world_effect_camera (missing params = defaults, rotation,
distance and FOV change the view, extremes stay finite, the sim key ignores the
camera); render particle_world_follows_the_comp_camera (a real camera layer
panned 40 px moves the particles left, and the cached render after the move
equals an uncached one: it failed with the old cache key); gpu
particle_world_through_the_comp_camera (CPU vs GPU at 8 / 32 bpc, full and
half resolution). particle_golden / sim_golden unchanged. effects.md
regenerated; parity.md and gaps.md describe the fix.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:35:25 +07:00
BFlat e9032c5ce2 Merge pull request #452 from storytold/fix/363-property-pick-whip
Timeline: every property has a pick whip in the Parent & Link column (#363)
2026-10-10 00:34:54 +07:00
llopjulenandClaude Sonnet 5.5 096cca08b0 Add Spanish (es) UI translation
Add a Spanish catalog for the menus (618 entries), the computed titles and the macOS Services label, and list Español in the Language preference. Match System now maps es, es-ES, es_MX and es-419 to it. Add llopjulen to contributors/people.toml.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HcDWLRR3UfieXAyFa9cq4p
2026-10-09 19:30:26 +02:00
bflatasticandClaude Opus 5.5 67b9b20a07 Timeline: every property has a pick whip in the Parent & Link column (#363)
What: each property row that can take an expression (it has a stopwatch) now
has a property pick whip in the Parent & Link column, as in After Effects
CC 2018 and later (automation id `timeline.prop.<uid>.propertyPickWhip`).
Dragged onto another property's name or one of its values, it runs
`prop.pickWhip`: the reference is added as the property's expression, or
replaces the one it has, and the expression is enabled. A drop on one value
picks that dimension (`position[1]`).

The parent, expression and property pick whips share one widget and one
drop path (rubber band, autoscroll, dimension pick); only the expression
pick whip inserts at the cursor of an expression being edited.

Pick whips (expression and property) now also drop on properties in
Effect Controls: its property rows register as targets while a pick whip
is dragged, with their per-dimension value fields.

Why: the last part of #363. Adobe's docs say the Property Link pick whip
adds the expression without enabling expressions first, and a pick whip
outside an edit replaces the whole expression text. No modifier keys for
the property pick whip are documented, so none are implemented.

Tests: `the_property_pick_whip_links_a_property_to_another` (ui_inline_edit)
drops it on a property's name, on one of Position's values, on a property
with a disabled expression (replaced and enabled), on an Effect Controls
slider and one value of a point, and on itself (no change). It fails
before the change; with Effect Controls' targets removed it fails at the
slider. Looked at the result with the headless snapshot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:28:22 +07:00
BFlat 4b7bd135ec Merge pull request #449 from storytold/fix/410-411-412-exr-ocio
EXR beauty layer, Interpret Footage Preserve RGB, OCIO v2 transforms for Blender configs (#412, #411, #410)
2026-10-10 00:27:39 +07:00
storybored379andClaude Opus 5.5 8ed8a7e220 MCP: return on write errors while stdin is open; document requests during a render
Follow-ups from the #365 review:
- serve() no longer hangs when a reply cannot be written: the stdin reader thread is
  detached instead of scoped, so a failed write returns at once rather than waiting for a
  read that only ends at EOF. serve() now takes a 'static reader (serve_stdio is unchanged).
- A test pins down requests during a render: a second render is refused however it is
  started (blocking, wait: false, inside a batch), and edits apply to the project while the
  job keeps rendering its snapshot.
- docs/mcp.md: isError is true for any JSON result with failed > 0; what requests mean
  mid-render; wait: false renders are polled with renderQueue.list.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-09 10:26:57 -07:00
bflatasticandClaude Opus 5.5 492fa6de0a Effects: OCIO custom configs read Blender's v2 transforms and warn (#410)
Cause: the config reader didn't know ColorSpaceTransform or
ExponentWithLinearTransform and passed colours through them silently.
Blender's Linear Rec.709 -> sRGB therefore stayed in its E-white XYZ
reference space (the blue cast) without the sRGB curve. Display
colorspaces, view transforms, v2 views, looks, AllocationTransform,
BuiltinTransform and .spi1d / .spimtx files were missing too, and
nothing told the user.

Fix (from the OCIO v2 documentation and the CLF "moncurve", not OCIO's
source): ExponentWithLinear, ColorSpace, Look, DisplayView, Allocation
and Builtin transforms (identity, ACES AP0/AP1 matrices, ACEScct /
ACEScc, the sRGB / P3 / Rec.1886 / gamma / PQ displays). RangeTransform
now clamps and takes one-sided ranges. Display colorspaces meet the
scene reference through the default view transform. Added v2 views,
view looks, the looks section and .spi1d / .spi3d / .spimtx files. A
conversion is built once as a flat list of steps (Config::path /
display_path) for both the CPU effect and the GPU colour program (new
moncurve and clamp ops and the ACEScc curve; a program over the
kernel's 255 ops renders on the CPU). Named-transform resolution is
limited to 16 levels and 4096 steps, and YAML nesting to 64 levels, so
hostile or looping configs stop. Transforms that are still unsupported
(Grading Primary / Tone, ACES output, HLG…), unreadable configs and
unknown spaces are now reported: a warning under the effect in Effect
Controls, and the new effect.warning query.

Tests: ocio_config::blender_like_config_converts_linear_rec709_to_srgb
(CST, display colorspaces, v1 / v2 views, allocation, looks, builtin,
range, loops), moncurve_matches_srgb_and_inverts, builtin_styles,
yaml_nesting_is_bounded, ocio::spi_files_parse,
tests_effects::ocio_effect_warns_about_unsupported_config_transforms,
gpu tests_fx_lut::ocio_custom_config_v2 (GPU = CPU).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:57:12 +07:00
bflatasticandClaude Opus 5.5 d6de68b20d Import: Interpret Footage ▸ Preserve RGB keeps the file's values (#411)
Cause: float footage (OpenEXR, float TIFF) was always sRGB-encoded on
decode, and Interpret Footage could not turn that off, so an OCIO effect
on the layer converted values that were already encoded.

Fix: After Effects' Interpret Footage ▸ Color ▸ Preserve RGB
(Footage::preserve_rgb; file.interpretFootage {"preserveRgb": true}; a
checkbox in the Interpret Footage form; Remember / Apply Interpretation
carry it). The decoder keeps float values linear, and the working-space
conversion from the colour profile and Interpret As Linear Light don't
apply, so the file's values enter the composition unconverted. The media
cache key includes the flag, so both interpretations decode separately.

Tests: pool::preserve_rgb_reads_float_exr_values_unconverted,
tests_color::preserve_rgb_footage_is_not_converted (managed, linearized
and not), tests_proxy::guess_alpha_invert_and_linear_light (command,
render, Apply Interpretation).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:57:12 +07:00
bflatasticandClaude Opus 5.5 698ade94ac Import: layered EXRs show the beauty pass, never Cryptomatte (#412)
Cause: a multi-layer OpenEXR without an unnamed RGB layer showed its
first colour layer unless one was named combined / beauty / rgba. A
Blender compositor File Output names the beauty pass "Image", and its
Cryptomatte layers (lowercase r, g, b, a) sort first, so the picture was
CryptoMaterial00.

Fix: the picture is the unnamed RGB layer, else the beauty pass by the
last part of its name (Combined, Beauty, Image, RGBA, RGB, Color), else
the first colour layer that isn't a data pass (Cryptomatte, depth, mist,
normals, vectors, positions, IDs). A layer without R, G and B shows its
own first channel as grey. After Effects shows a file's main RGBA and
leaves the other layers to EXtractoR, which is unchanged.

Tests: exr_channels::layered_exr_shows_the_beauty_pass_not_cryptomatte
(the reported Blender layout, ViewLayer.Combined, no beauty name,
data-only files).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:57:12 +07:00
BFlat 176cddcdad Merge pull request #444 from storytold/fix/415-416-417-text-fidelity
Text and viewer fidelity: glyph seams, overlay fringes, text raster scale, viewer minification (#415, #416, #390, #417)
2026-10-09 23:56:18 +07:00
sanyamsingh001andCodex 4751a462dc #332: render Essential Graphics templates from CLI values
Co-authored-by: Codex <[email protected]>
2026-10-10 02:48:06 +10:00