diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index 4499e3fdc..2a8144a5b 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -29,7 +29,7 @@ permissions: jobs: windows-arm64: # Immutable supporting workflow revision until the new install contract lands upstream. - uses: storytold/craft-common-actions/.github/workflows/windows-arm64.yml@8bb4e5ab95443171104ad88113fe1ef9b0e5ce3f + uses: storytold/craft-common-actions/.github/workflows/windows-arm64.yml@fa5ef18780cf5e7d5d3b8de068de56cffc83cac5 with: app-name: effectcraft app-dir: EffectCraft diff --git a/Cargo.lock b/Cargo.lock index 21adef411..629fc65e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1623,6 +1623,7 @@ name = "effectcraft-media" version = "0.18.0" dependencies = [ "craft-docpath", + "effectcraft-color", "effectcraft-model", "effectcraft-pdf", "effectcraft-project", diff --git a/README.md b/README.md index c1f895176..12970b8ba 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@
A free, open-source compositor: compositions, layers, - keyframes, 306 effects, layer styles, expressions, 3D cameras and lights, and a render queue, native on macOS, + keyframes, 308 effects, layer styles, expressions, 3D cameras and lights, and a render queue, native on macOS, Windows and Linux, and in the browser. Young, moving fast, and already usable.
@@ -84,9 +84,10 @@ a few places where it matters to us: without a plugin. - **A project file you can read.** Projects are versioned JSON (`.ecproj`), so they diff cleanly in version control. -- **Everything is scriptable.** Every menu item, timeline drag and property edit goes through one - command registry, so the same actions are reachable from a command line, a JSON control - channel and an MCP server for agents. +- **Everything is scriptable.** Menu actions, timeline edits and property changes are exposed as + commands. Engine commands are available through the CLI, JSON control channel and MCP; + UI-only commands use the frontend dispatcher and can be invoked through the control channel + or an attached MCP bridge. - **No FFmpeg.** Video and audio decoding and encoding are pure Rust: FilmCraft's codecs and EffectCraft's own VP9, AV1, HEVC and Opus encoders. @@ -126,7 +127,7 @@ or into the Timeline, between layers and at the time you point to. ## Effects -306 built-in effects across its categories, each with its parameter names, order and +308 built-in effects across its categories, each with its parameter names, order and defaults: blur and sharpen, channel, color correction (Curves, Levels, Hue/Saturation, a full color-grading panel…), distort (Warp, Bulge, Turbulent Displace, CC Power Pin…), generate (Fractal Noise, Gradient Ramp, Stroke, Write-on, Audio Spectrum…), keying, matte, noise and grain, perspective, @@ -173,8 +174,10 @@ cannot express; File ▸ Import ▸ Lottie… opens one as a new composition. ## Built for agents -Everything you can do from a menu is a command with an id, and agents can reach every one of -them: +Engine commands are available through the CLI, JSON control channel and MCP. UI-only commands +need the running app's frontend dispatcher: complete menu access requires the app's control +channel or an attached MCP bridge. Headless MCP sessions expose supported engine commands, +but not UI-only commands or bridge-only `ui_*` tools. - **MCP server:** `effectcraft-cli mcp` speaks the Model Context Protocol over stdio, headless or bridged to the running app (`--bridge 9877`). Tools cover commands, the project and property @@ -228,7 +231,7 @@ tests, layering, asset attribution and the WebAssembly build). See [CONTRIBUTING ## Where it stands EffectCraft is young: its first commit was on 1 October 2026. A comprehensive feature set -exists, including 306 effects, but breadth is not the same as full maturity across every edge +exists, including 308 effects, but breadth is not the same as full maturity across every edge case. Today it's a good place to try things and to tell us what breaks. It isn't yet recommended for production client work. In particular: diff --git a/crates/effects/src/audio_fx.rs b/crates/effects/src/audio_fx.rs index cd9153d66..2aab70309 100644 --- a/crates/effects/src/audio_fx.rs +++ b/crates/effects/src/audio_fx.rs @@ -206,6 +206,12 @@ pub fn reverses(id: &str) -> bool { id == "ec.audio.backwards" } +/// Does each output sample depend only on the input sample at the same time (no state), so the +/// mixer may run the effect in short pieces with parameters animated between them? +pub fn per_sample(id: &str) -> bool { + matches!(id, "ec.audio.stereomixer" | "ec.audio.backwards") +} + /// Seconds of input history the effect needs before a block to reach the state it would have /// had when processing continuously. pub fn preroll(id: &str, params: &Params) -> f64 { diff --git a/crates/effects/src/catalog.rs b/crates/effects/src/catalog.rs index 80a937b22..85be739ed 100644 --- a/crates/effects/src/catalog.rs +++ b/crates/effects/src/catalog.rs @@ -182,6 +182,20 @@ mod tests { assert!(have == want, "docs/effects.md is stale: UPDATE_DOCS=1 cargo test -p effectcraft-effects --lib effects_doc_is_current"); } + /// The README quotes the number of effects (#2565): every count it gives is the registry's. + #[test] + fn readme_effect_counts_are_current() { + let readme = std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/../../README.md")).unwrap_or_default(); + let words: Vec<&str> = readme.split_whitespace().collect(); + let counts: Vec<&str> = words + .windows(3) + .filter(|w| w[0].parse::Result