Commit Graph
1335 Commits
Author SHA1 Message Date
Elias 4e751ad241 file.importProject: import an EffectCraft project into the open project
File > Import > EffectCraft Project (and .ecproj/.ecprojx given to File > Import)
merges the project into a folder named after the file, with every id shifted past
the open project's, in one undo step. Relative footage resolves next to the file;
the result lists comps, root comps, primaryComp and missing media.

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

(cherry picked from commit 19c653fccc95133fb35e978a7f7f20a18c0e8c89)

(cherry picked from commit f4d36d11b0ccdfc56371a3bbd8d43d3b8281ee1b)
(cherry picked from commit c08135b2de8429693e723d065584465c459a61f9)
2026-10-11 12:29:57 +00:00
Claude a5b7e18cfd Properties: long row labels push their values right instead of overlapping
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rc1XNFw6vDovyoYpwHj9Py
(cherry picked from commit 5e86b07f0eedc02e7d23b1aca7c811f678da9e8b)

(cherry picked from commit e10c7e62a63a282e695d982d86e78554059fb1ff)
(cherry picked from commit 99ae078e3ac666936f71208aaab7f3deb7ab1a75)
2026-10-11 12:29:57 +00:00
Claude afe2999655 Properties: Layer Contents section for shape layers and a Roundness row for solids
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rc1XNFw6vDovyoYpwHj9Py
(cherry picked from commit 33d849fb6c57cf1e7de76ed91a4fab41f9121fc8)

# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
#	crates/ui-egui/tests/ui/main.rs

(cherry picked from commit a593488c211864735b3a6e8f087159235ed8184b)
(cherry picked from commit 8457f32cc14a8b32d2e562c4972a71d02b573b79)
2026-10-11 12:29:57 +00:00
Claude d773cec982 Properties: layer.roundCorners command (solid → rounded shape layer, shape rect roundness), roundness on new shapes, Layer › Create › Round Corners, MCP test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rc1XNFw6vDovyoYpwHj9Py
(cherry picked from commit f3cd3cc7b3b8d6c3ce88a3c60d3612aa50dc3b3c)

# Conflicts:
#	crates/engine/src/commands/mod.rs

(cherry picked from commit cec4b6633045617064a6857d4a0c3aea1ae8408f)
(cherry picked from commit 63da1f2779b57d5409310d001eae83821ca8f03a)
2026-10-11 12:29:57 +00:00
Elias 4bf9bb273e UI: let Type tool edit selected overlapping text while respecting visibility
(cherry picked from commit 9fd5b1e5618a6ad19638494b480460e9527debde)

(cherry picked from commit 812e34fd7834e828bebd3f79440f831d188a1a99)
(cherry picked from commit 283adb1a30e6b51ab6603488e57b9997a0ca83f0)
2026-10-11 12:29:57 +00:00
Elias 88e40f444a UI: reproduce overlapping duplicated text editing with pointer and Type tool
(cherry picked from commit 0acb03da5f0d09d726047013e931e5eeff5504ce)

(cherry picked from commit 66427c0149c5f2fd46931945a4f11a9530c12269)
(cherry picked from commit d5fadffc1ac9f072a985a918cd6a62e9763bc2bf)
2026-10-11 12:29:57 +00:00
Elias 9f9398458c Timeline: a click on empty track area deselects layers (as in After Effects)
(cherry picked from commit 9d6352faea454ebcd1a575056c4d5f0bafcc80ea)

(cherry picked from commit d6110a23e9c459eb4c677b439d811b7438b1a999)
(cherry picked from commit f46595d119d253c9298eb045ad521420a155b422)
2026-10-11 12:29:56 +00:00
Elias ad0492b056 Timeline: trimming a selected layer's in/out point trims every selected layer (as in After Effects)
(cherry picked from commit 6af5aa81bf3b66d272f15f018f065f3324faa9fb)

(cherry picked from commit 5423c78e4a58789c26df4f4db46e6f27fb458d51)
(cherry picked from commit 8863abad1571815719e59715955c6a0402b5b67e)
2026-10-11 12:29:56 +00:00
Elias 7731283797 timeline: trim zones straddle layer bar ends (AE), trim wins over slip
The In/Out trim zones were 6 pt strips inside the bar only: at or just outside an end the pointer got the default cursor, and on footage/precomp layers just outside the end it hit the slip zone, which shows the same arrow but slips the source instead of trimming. Zones now reach 4 pt outside and up to 5 pt inside each end (short bars keep their middle third as body), registered after the slip zones so trimming wins there. The trim zones show the system resize cursor (upstream port: the fork also offers an owner-local cursor, left out here). Test: ui_timeline_trim.

(cherry picked from commit e2490f801067365addc03858bd555731cb188ad1)

(cherry picked from commit b704fc52c4fdb43816721e95d13f132a11062aa1)
(cherry picked from commit 17faf9cebf9e347534ceab37386e79979da8f786)
2026-10-11 12:29:56 +00:00
Elias 960c0d59a8 ui: timeline view stops at the comp end; a ruler scrub past the edge scrolls with the CTI
Co-Authored-By: Claude <noreply@anthropic.com>

(cherry picked from commit 562bfa51bd6ee86d1f8f5558b1ad466c7e4b4517)

(cherry picked from commit 0779d6944ede8afdff6ab49ea5d2caad19dfbfa3)
(cherry picked from commit 38e224f5c5037b7c79a283ab058582a3ad200339)
2026-10-11 12:29:56 +00:00
Elias 7c639a431c shapes: Reverse Path Direction (⇄) on Path/Rectangle/Ellipse/Polystar rows
- shape.reversePathDirection {layer?, prop?, reversed?}: one undoable step; toggles the
  selected items without prop. The Timeline shows the ⇄ button on shape item rows; Path
  Direction stays hidden as a property row. Trim Paths then runs the other way.
- Scripting: ADBE Vector Shape Direction is settable despite being hidden, with After
  Effects' values (1 Default, 2 normal, 3 Reversed) mapped to ours (0/1) and read back as 1/3.
- AEP import: AE's Reversed (3) imported as 2 (not reversed); now maps to 1.
- Go to Work Area Start/End get AE's defaults Shift+Home / Shift+End.

Upstream port: the Shift+Home / Shift+End work-area bindings and the After Effects-import part are left out (they build on fork work not upstream yet).

(cherry picked from commit 7782ab015a01613c1685cb6dda48d48c3b1db67b)

(cherry picked from commit c21cb5b74430cae267a46fdc835a4cf1ed32fc9a)
(cherry picked from commit f79c41ef0f366d3cc7b4c90ca3faefdd8a6b07fd)
2026-10-11 11:50:21 +00:00
Elias 1663e70532 shapes: toolbar Fill/Stroke kinds, blend and opacity apply to selected shape layers
With shape layers selected, Option-clicking the Fill/Stroke swatch or picking a kind,
blend mode or opacity in Fill/Stroke Options only changed the shape tools' defaults
(shape.toolOptions); the layers kept their paint (e.g. Fill: None left the blue fill).

shape.fillStroke now takes fillType/strokeType (none|solid|linear|radial, or
fill/stroke: false), fillBlend/strokeBlend and fillOpacity/strokeOpacity, applied to
every Fill/Stroke and Gradient Fill/Stroke in the layers' contents (nested groups too),
one undo step:
- None switches the items off (eye off, kept, as After Effects does); the renderer
  already skips disabled items.
- Solid switches them on; Linear/Radial convert Fill <-> Gradient Fill and
  Stroke <-> Gradient Stroke in place, keeping shared properties (uids, blend, opacity,
  width, dashes, taper, wave) and the item number ("Fill 2" -> "Gradient Fill 2").
- Paths with no fill get one for a fill kind; with no stroke one for a stroke kind.
- Reports fillType/strokeType of the first layer (new pub first_kinds).

Header: the Alt-click kind cycle and the Options popup (kinds, blend, opacity) also
call shape.fillStroke on the selected shape layers; the swatches show the selected
layer's kinds.

(cherry picked from commit e28f2bdcc672fa8c1cf7ea771fd34ba8a48ff066)

(cherry picked from commit 4d514362dff9dfd5ae5d168daf119dbf7802e49b)
(cherry picked from commit addd0645c64a40d1611005f333f8686b2e003422)
2026-10-11 11:50:15 +00:00
Elias 8235701fc0 ui: Graph Editor fit goes through the timeline zoom; AZERTY zoom keys in the graph
Co-Authored-By: Claude <noreply@anthropic.com>

(cherry picked from commit 6277a98f3ff6207bd215213af514c5f6fb481532)

(cherry picked from commit 646f4055b180d98ccc6ac6898b64ba97f9e2ad7d)
(cherry picked from commit b21932ed7058cec68da01d337ba46521501ebc49)
2026-10-11 11:50:14 +00:00
Elias fe298b29fa ui: Graph Editor fits the selection when it opens
Opening the Graph Editor (header button, menu, Shift+F3, control channel)
frames the graphs on its first frame: Fit Selection to View when keys are
selected, else Fit All Graphs to View. Switching value/speed graph refits
too (different units). A selected key now shows its property's graph even
when its layer isn't selected, and Fit Selection on a lone key also frames
its neighbouring keys.

Co-Authored-By: Claude <noreply@anthropic.com>

(cherry picked from commit a3c95f601e78e7a12f62aba94ee3515b2ade1670)

(cherry picked from commit d6309ffad79b91676d6409d1845de508e485873d)
(cherry picked from commit 78f557433742e443c9b7db34c1dac67e6a2b8e18)
2026-10-11 11:50:14 +00:00
Elias a0336a1355 ui: Graph Editor follows the timeline time zoom; zoom buttons and slider clicks work
The Timeline footer's zoom-out / zoom-in mountain icons were painted but not
interactive, and the zoom slider only reacted to drags (a click on the track did
nothing) and set the zoom without the whole-comp clamp. The icons are now buttons
(timeline.zoomOutButton / timeline.zoomInButton, one = / - step), the slider
jumps on click, and both go through set_zoom, centred on the CTI.

The layer bars and the Graph Editor share one time zoom (TimelineState start /
pps, as in After Effects); regression tests drive = / -, the buttons, the slider
and the navigator over the control channel in both views and check switching
views keeps the zoom.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 2ee0fb656ff6bd16eed6857be10495d9f30e3a64)

(cherry picked from commit f1997d32d4c51b58467a04944889131fd741a22a)
(cherry picked from commit 1c25aeb51d37357b4b37857858db4dbea246b13a)
2026-10-11 11:50:14 +00:00
Claude 0e6a7ae1e0 shortcuts: Go to Work Area Start / End default to Shift+Home / Shift+End, as in After Effects
Split out of 7782ab01 (shapes: Reverse Path Direction …; work area keys) for upstream, where it
belongs with the commands it binds.

(cherry picked from commit 7782ab015a01613c1685cb6dda48d48c3b1db67b)

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

(cherry picked from commit b58cfb7434e147535e66582718b8856bd3ce1230)
(cherry picked from commit ff4876054f3cbf9388954602e8fe7288d136c255)
2026-10-11 11:50:14 +00:00
Elias a749de65c5 shortcuts: physical-key bindings for AZERTY layouts; Go to Work Area Start/End
Shortcut presets accept Physical+<key> (egui US-position names) so layouts that report the
same logical key for two physical keys (AZERTY's - and )) can bind them separately; physical
bindings win for that press, and a focused panel's binding wins over an application command on
the same chord. ui.key accepts the same notation. Adds time.workAreaStart / time.workAreaEnd.

(cherry picked from commit 209543e9e0ba32f809f9ff333abb8b8ec4125cea)

(cherry picked from commit f8301a9e3e0b60e200e8a233455398741658481e)
(cherry picked from commit 4d002fabf992934e1bb73a0c32bc9f3fd3ae5447)
2026-10-11 11:50:14 +00:00
Elias f07497dfee Effects: re-pin CC Particle World / Systems II golden hashes
3eb3b99 makes the particles replace their layer as in AE, so their 112 pinned
CPU hashes change intentionally; no other effect changed.

(cherry picked from commit 9900eae773e1cad43a6d1bdcacbadc45a84b063a)

(cherry picked from commit 7aa27c0dd7c24b9f24591bb144a95951dc0ec510)
(cherry picked from commit 73377e8eca5c639715e5f3f623ed53ff329638a0)
2026-10-11 11:50:14 +00:00
Elias 87a733e076 Effects: Time effects see the effects applied before them
Echo, Posterize Time and the other Time effects read the layer at other times with none of
its effects. After Effects renders them with the effects before them: SceneYard "Task
Preparation UI" applies Echo after CC Particle Systems II on black solids, and AE shows the
echoed fireworks over a white page where we echoed the black solids and blacked it out
(Posterize Time after Turbulent Displace, the usual stop-motion setup, depends on the same).
The host already bounds the count to the effects before the one rendering.

Mean abs error vs AE: Task Preparation UI 0.1855 -> 0.0071, Lightbulb Idea 0.0126 -> 0.0102;
the 15 other corpus projects with a Time effect after another effect are pixel-identical.

(cherry picked from commit 46870452db2656c72406f63245e8b1609ceb04d4)

(cherry picked from commit 334d102582e5a64db32e2f3e55836d55ac96bdaa)
(cherry picked from commit 734da3e35dc57c01b73823458bdd87611ac1bf99)
2026-10-11 11:50:14 +00:00
Elias 338421de36 Effects: CC Particle World / Systems II replace their layer with the particles
The particles were composited with the original layer by the Transfer Mode, so the solid an
artist applies them to stayed visible. After Effects 2026 frames show no trace of it:
SceneYard "Burnout matchstick" (a red rectangle around the flame), "Documentary Man Falling"
(a black block over the light), "Task Preparation UI" (black solids under fireworks). The
Transfer Mode now only sets how the particles meet each other.

Mean abs error vs AE: Burnout matchstick 0.0191 -> 0.0130, Documentary Man Falling
0.1177 -> 0.0937 (Task Preparation also needs the next commit).

(cherry picked from commit 3eb3b9958f9cd929dcf060865e604d42cc0f9337)

(cherry picked from commit 729638710e63f4da10711fc5e6a1f7ddc42c37aa)
(cherry picked from commit 083332116c936f1b4863f0bb390d0647f2b567a1)
2026-10-11 11:50:13 +00:00
Elias 8bcc456caa ui: skip timeline ruler labels that would run under the marker bin
The comp-end label was clipped to a stray "1" against the marker bin.

(cherry picked from commit f62afa0364f1bf38f0b0b5eda5ccc07f33174790)

(cherry picked from commit 2e3d08dad7d73a14688bf6d8bfb1712ab51443a2)
(cherry picked from commit 773471c3a39bef51f4763d21d007534cfd95c0b1)
2026-10-11 11:50:13 +00:00
Elias 0f87159928 ui: centre Reset / About... on the effect header row in Effect Controls
The buttons were offset 2pt below the 22pt header's centre, so their text sat
below the effect name's baseline.

(cherry picked from commit acacef9f42e992c535397a55ae03bb3f3684a5e0)

(cherry picked from commit dc9ff1b5aad75c4f4233d89ed33813844e980487)
(cherry picked from commit 1e599b6e1d2e3402a10477ccccd1fd52724dc95c)
2026-10-11 11:50:13 +00:00
Elias 0d03abafc0 ui: elide long Project panel names with an ellipsis
Names were hard-clipped mid-glyph against the label column
("EffectCraft Intrc"); use widgets::text_fit like the other text columns.

(cherry picked from commit 341c924eff265f08e8b04b5a6a99d0d173a13e24)

(cherry picked from commit 60086aa4b5c735cd2307d773b22d8189e211f40f)
(cherry picked from commit c318f8438f60d5ecb8bbebb8857bad1fe6d9834d)
2026-10-11 11:50:13 +00:00
Elias 3de62e02fb ui: centre text and hint vertically in search/text fields
The TextEdit inside widgets::field took its natural height at the top of the
field, so vertical_align(Center) had no room to act and the placeholder sat
~2pt above the magnifier (Timeline, Project, Effects & Presets, Home).

(cherry picked from commit 6806eb4f7c955e87c6f2a5b01a9d80d43dd6ac58)

(cherry picked from commit af85d220bc337d5a3c6fa94b6fa315b0d3b0c09b)
(cherry picked from commit 685b7f2057839da1af9607dc0c93020f062a0946)
2026-10-11 11:50:13 +00:00
Elias ed5c3685e7 SVG: rasterise each shape within its own bounds
Every fill and stroke allocated a coverage mask the size of the whole canvas and painted every
canvas pixel, so a vector file cost shapes × canvas area: SceneYard "Retro Desktop Movie
Popups" spent 556 s per frame on one 4001×2501 Illustrator layer (568 s a frame in all).

Each shape now rasterises and paints only inside its device-space bounding box (+1 px of
anti-aliasing) clipped to the canvas; a stroke uses the bounds of its outline. The frame
renders in 5–7 s (bench, no cache). Pixels match the whole-canvas version except a few
anti-aliased edge pixels (float rounding of the re-based transform). A test checks that every
pixel a shape covers lies inside its rectangle, and pins the output.

(cherry picked from commit 58d369b5044bf03698f0fae6915843c3d400b1d1)

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

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

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

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

(cherry picked from commit ea7923535fb81693ed95aa0fe768b850b347b2a9)

(cherry picked from commit d15e853a720304c1032e21974d84e1501c6edfc3)
(cherry picked from commit 0b8a328dda1fb727488e912a13f94a2ef4147d03)
2026-10-11 11:50:13 +00:00
Elias 574f4958a7 Docs: control-protocol lists app.fxConsole (Effect Console) and its automation ids
(cherry picked from commit 49c8b99863401e8ff80fc915aa894bd7a2319317)

(cherry picked from commit 5e9995cc9a0c7fe597daf4231f11b51b4e35b5d4)
(cherry picked from commit f3b27e8575c9f876a582e91251f84c29a95858ce)
2026-10-11 11:50:13 +00:00
Elias 5f914db98c ui: Effect Console (Ctrl+Space) — search and apply effects and presets at the pointer
A small search bar opens at the pointer (Edit > Effect Console..., app.fxConsole {query?}).
It ranks effects, text presets and user presets by name, word starts, initials (gb = Gaussian
Blur) and letters in order, boosting recent and favourite effects and sinking Obsolete ones.
Enter applies to the selected layers and closes, Shift+Enter applies and stays open, arrows
choose, Escape / Ctrl+Space / a click outside close. With no text it lists recent effects.
Off macOS a Ctrl shortcut now also expects the command flag egui reports with Ctrl.

(cherry picked from commit c88580b11b1cb6940165d1eaaa8635a3a934cd0f)

(cherry picked from commit c508d92a93299f0f5ec7986fc1c6f84aa243ff33)
(cherry picked from commit 36f3d6533de51d70b0255c33b3f94ed2ebf2297b)
2026-10-11 11:50:12 +00:00
Claude 351a4b96c0 G5: cargo xtask bench --fail-over PCT fails on resource regressions
With --compare, exits 1 when a matched run got more than PCT % worse in CPU time, peak memory, max RSS
or instructions retired (wall time follows the load and is never gated).

Checks: xtask fail_over test (1 passed).

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

(cherry picked from commit c34be4d12f3a61f4e386256ae54d75c6bc7f875d)

(cherry picked from commit 6e3eab96493726c551ec1e66174ac08b5591e4c2)
(cherry picked from commit 2e066f4073b300d8bace04419a7d4af3f3242b2e)
2026-10-11 11:50:12 +00:00
Elias 20c2bb0dcb G5: cargo xtask bench: render benchmark of generated scenarios (peak memory, CPU, wall) with results.jsonl and --compare
(cherry picked from commit 40b232d02069925e9c72677f52ee6f356495a736)

(cherry picked from commit a2d89bda11b7217c83c98ab681b8f9b4a334786d)
(cherry picked from commit 63951595222207b6bfd0ecde72f991628c6f12d9)
2026-10-11 11:50:12 +00:00
skylar taylor-barrick 9a7d3e7699 Merge pull request #689 from skylartaylor/codex/ui-suite-integration-20261010
Share docking transactions and preserve viewer lifecycle
2026-10-10 20:10:35 -06:00
skylar taylor-barrick 83ae6218ef UI.9: retain shared docking across upstream workspace changes 2026-10-10 19:25:49 -06:00
Brandon ThomasandClaude Opus 5.5 f953cd2855 Release prep: 0.8.0 and a Linux riscv64 release build (#669)
* Release prep: bump to 0.8.0 and add a Linux riscv64 release build

- release.yml: new `linux-riscv64` job, ported from GridCraft. It cross-compiles on
  ubuntu-24.04 against riscv64 libraries from ports.ubuntu.com (the Linux job's X11/Wayland/GL
  -dev packages plus ALSA), ships effectcraft-<v>-linux-riscv64.tar.gz, and smoke-tests
  effectcraft-cli under qemu-riscv64. The draft-release job now needs it, so its tarball is
  uploaded and covered by SHA256SUMS.txt.
- packaging/linux/package.sh: cross-compiles when CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE are set
  (cargo --target, the cross strip, the EMULATOR smoke test), as GridCraft's does.
- Version 0.7.0 -> 0.8.0 (cargo xtask version set), Cargo.lock refreshed.
- README and docs/releasing.md list the riscv64 tarball.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix FreeBSD CI: pointer_wrap test harness, wasm32 target, clippy as_chunks

- pointer_wrap tests: the harnesses dropped run_ui's FullOutput with the font atlas upload still
  in textures_delta, and epaint 0.36's drop check panics on that in debug builds. Every
  pointer_wrap test failed on every platform; FreeBSD is the only CI that runs them. Clear the
  delta, as the other UI tests do.
- freebsd.yml: install wasm32-unknown-unknown in the VM. Without it the host plug-in test's build
  of examples/plugins/posterize-bands failed (E0463, no std) and the test skipped silently; now
  it builds and runs.
- media/convert.rs: chunks_exact(2) -> as_chunks::<2>() (clippy chunks_exact_to_as_chunks,
  new in Rust 1.99); same pairs, same dropped remainder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
v0.8.0
2026-10-10 21:08:18 -04:00
BFlat 4b58cd2eb7 Merge pull request #676 from storytold/claude/community-pr-review-lwtrlr
Issue fixes: drag render pile-up, CC Smear/Tiler past text bounds, relative footage paths, GIF playback test
2026-10-11 07:16:00 +07:00
Claude ceb116d6d2 Issue fixes: #647 nested frame renders while dragging, #659 CC Smear/Tiler past text bounds, #652 relative footage next to the project, #651 GIF playback test 2026-10-10 23:55:23 +00:00
BFlat 2ec87e921f Merge pull request #675 from storytold/claude/community-pr-review-lwtrlr
Community batch: pointer-wrap test texture deltas, device-lost dialog
2026-10-11 06:55:07 +07:00
Claude f3fe25fd67 Import: test that an animated GIF imported from disk plays (#651)
Animated GIFs came in as one still frame in 0.7.0; bfbdadd3 (after that
release) probes them as movies. This end-to-end test imports a GIF file
through the wired session into a comp and checks the layer shows each of
its frames at its time, the path the viewer and exports share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:50:08 +00:00
Claude 8e14c00eaf Project: footage saved with a relative path is found next to the project file (#652)
file.import stores a path as given, so footage imported by the CLI or an
agent as `clip.mp4` was saved relative, and opening the project looked it
up from the working directory. The CLI, started in the project's folder,
found it; the desktop app, started from the Finder, Explorer or a launcher
in another folder, showed and rendered neither its picture nor its sound,
while the Project panel still listed the saved duration.

On open, a relative footage path that doesn't exist from the working
directory but does next to the project file is resolved there (After
Effects also looks for footage relative to the project). Paths that exist
as they are, and absolute ones, are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:41:43 +00:00
ClaudeandDennis M. Francis cb37c2511c App: a lost graphics device tells the user why the window closes and where the recovery copy is (#591)
Ported from #673: a native error dialog names the auto-saved recovery copy before the window
closes; agent runs through the control channel skip it. The UI calls Hooks::device_lost once.

Co-Authored-By: Dennis M. Francis <44986763+didpublishing@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:36:52 +00:00
Claude 2aefe3e644 Effects: CC Smear and CC Tiler on text and shape layers reach past the content's box (#659)
A text or shape layer's effect bounds are comp-sized (as in After Effects),
but its buffer surrounds only its content, and each effect grows the buffer
itself when it needs room. CC Smear and CC Tiler never did, so a smear was
cut off where the glyphs end and tiles filled only the text's box.

CC Tiler now covers the whole layer rectangle; CC Smear grows by how far it
moves pixels (Reach x From->To), at most to the layer bounds, so footage
layers, whose buffer is their bounds already, are unchanged. CPU and GPU
alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:35:47 +00:00
Claude 7fd1d09088 Merge pull request #671 from vbasky/fix/pointer-wrap-textures-delta 2026-10-10 23:31:17 +00:00
Claude d247b98b95 Viewer: dragging a layer no longer piles up frame renders until memory runs out (#647)
Every viewer request spawns a job on the frame pool. A pool thread blocked
inside a render (waiting on a parallel loop's join) runs other pool work
meanwhile, rayon's injected jobs included: the spawn of the next request
started a whole frame render on top of the waiting one. Dragging makes a
new revision, and a new viewer frame, every UI frame, so renders nested
without bound on every thread, each holding its buffers (a large image's
full-resolution copies), until all memory was used. Holding the layer still
makes no new revisions, which is why only moving it did this.

A spawn now drains the queue, and one that lands inside a render on its
thread returns at once; the render below takes the queued jobs when it is
done. Each pool thread renders one frame at a time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:29:44 +00:00
BFlat 21681e9590 Merge pull request #674 from storytold/claude/community-pr-review-lwtrlr
Issue fixes: GPU device loss, motion-path vertex tools, rename focus, macOS sheet dialogs, MCP file confinement, desktop shortcut
2026-10-11 06:25:56 +07:00
Vikram Bhaskaran da53c828dd Credit Vikram Bhaskaran by real name and display name. 2026-10-11 10:20:10 +11:00
Vikram Bhaskaran e7735f0875 Tests: pointer-wrap frames drain egui's texture deltas instead of dropping them.
The Scrub helper and the real-properties test discarded FullOutput, so the
first pass' font-atlas texture delta panicked on drop in debug builds —
which is how the FreeBSD CI gate failed. Consume the delta the way the
rest of the crate's egui tests do.
2026-10-11 10:20:10 +11:00
skylar taylor-barrick 3dba472943 UI.9: share docking transactions and preserve viewer lifecycle
Preserve legacy selection repair, modifier routing, viewer lifecycle and workspace persistence through the shared docking integration. Qualify the complete workspace gate by refreshing the 18 stale Audio Spectrum hashes for the existing linear renderer, with an independent synthetic-tone regression.
2026-10-10 17:14:12 -06:00
Claude 1ac09c7fa3 Issue fixes: #591 GPU allocation and device loss, #553 motion-path vertex tools, #488 rename focus, #511 macOS sheet dialogs, #500 MCP file confinement, #343 desktop shortcut 2026-10-10 23:06:04 +00:00
BFlat ca89a975c7 Merge pull request #668 from storytold/claude/community-pr-review-lwtrlr
Community batch: font folders, empty-Timeline drop, AE auto-Bezier tangents, clippy 1.95
2026-10-11 06:05:39 +07:00
Claude 8fb4281632 MCP: --automation-read-root / --automation-write-root confine the agent's file access (#500)
`effectcraft-cli mcp` had no way to keep an agent's file access to a folder;
the flags were unknown options. Both now take a folder (repeatable, as in
PhotoCraft): with either given, files are read only under a read or write root
and written only under a write root (none: no writes). Defaults are unchanged.

automation::confine::Confinement checks every engine command the backend runs
(execute_command, command_run, open/save_project, each engine.batch step), in
both headless and bridge mode, before it runs: the file parameters of the
commands that read or write files must be absolute paths inside the roots
(symbolic links resolved through the longest existing ancestor, `..` refused),
as must any other absolute path in a command's parameters. Commands that write
where they aren't told are checked against the target they would write: Save /
Increment and Save the open project's file, renderQueue.render every queued
output (renderQueue.list). Scripts, Execute File, Reveal in Finder, Open Recent
by index, `$N` file-path references in a batch, render_frame/screenshot paths
outside a write root and the bridge's UI-driving tools are refused while
confined. docs/mcp.md describes it.

Test: a confined headless server opens and saves inside its roots and is refused
outside them, for a relative path, `..`, a batch step, a `$N` path, Save to a
read-only root's project, the Render Queue's default output there, a symbolic
link out of the write root, and scripts; the unconfined server still writes
anywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:54:57 +00:00
Claude aaf23aa5f8 macOS: File menu dialogs open as sheets, so a drag over the window can't abort the app (#511)
The file dialogs were blocking rfd::FileDialog calls made inside eframe's frame,
that is inside winit's event handler. On macOS the dialog runs a nested modal
event loop there; a file dragged over the window while it was open reached
winit's handler again, which refuses re-entry with a panic across the
Objective-C boundary, aborting the app (Import with Cmd+I, then a drag).

Hooks gains file_dialog_async: a host that sets it shows a command's file dialog
(Import, Open, Save As, Save Frame As, presets, guides, templates, Collect Files,
Relink...) without blocking, and the command runs with the answer on a later
frame (menus::dialog_answered, drained at the start of each frame); a cancelled
dialog runs nothing. The macOS app implements it with rfd::AsyncFileDialog, a
sheet on the window answered when it closes, outside the frame. Other platforms
keep the blocking dialogs (winit queues their re-entrant events), as do the few
panel buttons that pick a file in place (Settings paths, shortcut import/export,
effect file pickers).

Test: with a non-blocking hook, Save As and Save Frame As return at once without
the blocking dialog, the save happens when the answer arrives from another
thread, and a cancelled dialog writes nothing. The macOS sheet itself needs a
manual check (Cmd+I, drag a file over the window, pick or cancel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:54:56 +00:00