mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-23 04:02:49 -05:00
The Ease Presets panel (#254) is modelled on a third-party panel, so, as with third-party tools in After Effects, it isn't core any more: it is `extensions/scriptui-panels/Ease Presets.jsx`, a ScriptUI panel bundled as an optional extension (Window ▸ Ease Presets.jsx), written only against the public scripting API. Clicking a preset eases every pair of neighbouring selected keyframes in one undo step (per dimension, along the motion path for spatial properties, the keys' other sides as they play); the fields edit the working curve, which the panel draws; From Keys, Save, Rename and Delete as before. The twelve built-in curves are unchanged. Scripting API fixes it needed (After Effects parity): - `app.settings` lasts between runs (`script_settings.json` in the settings folder; `script.settings.get` / `script.settings.save`). - Per-key methods (`setTemporalEaseAtKey`, `setInterpolationTypeAtKey`, `removeKey`, the continuous / auto-Bezier / roving setters) no longer change the user's key selection (they selected the key they edited). - `keyInTemporalEase` / `keyOutTemporalEase` report linear and auto-Bezier sides as they play, `keyIn/OutSpatialTangent` the tangents the motion path plays. - `selectedProperties` includes the properties of selected keyframes. - `isInterpolationTypeValid` is false for Linear / Bezier on values that only hold. Removed from core: Window ▸ Ease Presets, the `keys.easePreset.*` commands, `engine::ease_presets` and the `ease_presets.json` store, `keyframe::EaseCurve` (nothing else used it) and the egui panel. User presets saved by the v0.6.0 panel move into the panel's app.settings section the first time settings load (the old file is left as is). Docs: plugins.md (bundled extensions), parity.md, gaps.md G8, agents.md. Test: app_settings_last_between_runs, per_key_methods_keep_the_key_selection_and_report_eases_as_they_play, spatial_tangents_are_reported_as_the_path_plays (each fails before), the_ease_presets_panel_eases_every_selected_pair_in_one_undo_step, a_typed_curve_scales_to_each_dimension_and_follows_motion_paths, user_presets_save_rename_delete_and_persist, presets_saved_by_the_old_core_panel_carry_over (script, through the panel's controls), ease_presets_panel_eases_the_selected_keys_from_its_list (UI: the panel docks and a click on a preset eases the keys). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
4.7 KiB
Rust
93 lines
4.7 KiB
Rust
//! # effectcraft-script (L4)
|
|
//!
|
|
//! Scripting with an After Effects-style object model, on the boa JavaScript engine (the one the
|
|
//! expressions use). Scripts written for After Effects' documented scripting API mostly run
|
|
//! unchanged:
|
|
//!
|
|
//! ```text
|
|
//! app.beginUndoGroup("Build");
|
|
//! var comp = app.project.items.addComp("Main", 1920, 1080, 1, 10, 30);
|
|
//! var solid = comp.layers.addSolid([1, 0, 0], "Red", 1920, 1080, 1);
|
|
//! solid.property("ADBE Transform Group").property("ADBE Position").setValueAtTime(0, [0, 540]);
|
|
//! solid.Effects.addProperty("ADBE Gaussian Blur 2").property("Blurriness").setValue(20);
|
|
//! app.endUndoGroup();
|
|
//! ```
|
|
//!
|
|
//! * **Object model** (`prelude.js`): `app` (project, open/newProject, undo groups,
|
|
//! `executeCommand`/`findMenuCommandId`, `scheduleTask`, `settings` kept in the settings
|
|
//! folder), `Project`, `ItemCollection`,
|
|
//! `CompItem`/`FootageItem`/`FolderItem`, `LayerCollection`, `AVLayer`/`TextLayer`/
|
|
//! `ShapeLayer`/`CameraLayer`/`LightLayer`, `PropertyGroup`/`Property` (values, keyframes,
|
|
//! eases, expressions), `MaskPropertyGroup`, `TextDocument`, `Shape`, `KeyframeEase`,
|
|
//! `MarkerValue`, `RenderQueue`/`RenderQueueItem`/`OutputModule`, `ImportOptions`, `File`/
|
|
//! `Folder`, `$`, `alert`/`writeLn`, and the enums (`BlendingMode`, `KeyframeInterpolationType`,
|
|
//! `TrackMatteType`, `LightType`, `ParagraphJustification`, `PropertyValueType`…).
|
|
//! * **Edits are engine commands**: every mutating call runs a command through
|
|
//! [`Session::execute`](effectcraft_engine::Session::execute), so it is undoable, journaled
|
|
//! and identical to the UI's action; `app.beginUndoGroup`/`endUndoGroup` fold everything in
|
|
//! between into one undo step.
|
|
//! * **Match names** ([`matchnames`]): After Effects' documented match names for our properties
|
|
//! and effects (`ADBE Transform Group`, `ADBE Position`, `ADBE Gaussian Blur 2`…).
|
|
//! * **Security**: scripts can import/open/save projects and render, but `File` reads are limited
|
|
//! to the project's folder and writes (and the network) are off unless Preferences ▸ Scripting
|
|
//! & Expressions ▸ Allow Scripts to Write Files and Access Network is on.
|
|
//! * **ScriptUI** (`scriptui.js`, `ui.rs`): `Window` (dialog / palette / window), `Panel`,
|
|
//! `Group`, `Button`, `StaticText`, `EditText`, `Checkbox`, `RadioButton`, `Slider`,
|
|
//! `Progressbar`, `DropDownList`, `ListBox`, `TabbedPanel`/`Tab`, with `add()`, `orientation`,
|
|
//! `alignChildren`, `alignment`, `margins`, `spacing`, `preferredSize`, `onClick` /
|
|
//! `onChange` / `onChanging` / `onClose`, `show()` / `close()` and `layout.layout()`. Windows
|
|
//! are published to [`Session::script_ui`](effectcraft_engine::Session::script_ui) for
|
|
//! frontends and agents (`scriptui.*` commands); modal dialogs block `show()` until they close.
|
|
//! Scripts in the ScriptUI Panels folder run with `this` = a dockable `Panel`. Resource
|
|
//! strings (`new Window("dialog { ok: Button { text: 'OK' } }")`, `add("group { … }")`) build
|
|
//! whole trees; `onDraw` handlers paint with `ScriptUIGraphics` (`newPen`/`newBrush`/`rectPath`/
|
|
//! `ellipsePath`/`moveTo`/`lineTo`/`fillPath`/`strokePath`/`drawString`/`measureString`) into a
|
|
//! serde draw list frontends paint; edit texts and sliders fire `onChanging` live.
|
|
//! * **Sockets** (`socket.rs`): `Socket` (`open`/`listen`/`poll`/`read`/`readln`/`write`/`close`)
|
|
//! over TCP, behind the same network preference; on the web `open` returns false.
|
|
//!
|
|
//! Entry points: [`install`] sets [`Session::script`](effectcraft_engine::Session::script), which
|
|
//! the `script.run` command, File ▸ Scripts ▸ Run Script File… (`.jsx`/`.js`), the Script Console
|
|
//! panel, `effectcraft-cli script` and the MCP `run_script` tool use.
|
|
|
|
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
|
#![recursion_limit = "256"]
|
|
|
|
pub mod matchnames;
|
|
mod model;
|
|
mod runtime;
|
|
mod socket;
|
|
mod ui;
|
|
|
|
use effectcraft_engine::{ScriptRequest, Session};
|
|
|
|
pub use runtime::{Outcome, ScriptError, run};
|
|
pub use ui::dispatch_ui;
|
|
|
|
/// The [`effectcraft_engine::ScriptRunner`] this crate provides.
|
|
pub fn runner(s: &mut Session, req: &ScriptRequest) -> serde_json::Value {
|
|
run(s, req).to_json()
|
|
}
|
|
|
|
/// Enable scripting (and ScriptUI) on a session.
|
|
pub fn install(s: &mut Session) {
|
|
s.script = Some(runner);
|
|
s.script_ui.dispatch = Some(dispatch_ui);
|
|
}
|
|
|
|
/// Run `code` as a one-off script named `name`.
|
|
pub fn run_code(s: &mut Session, code: &str, name: &str) -> Outcome {
|
|
run(s, &ScriptRequest { code, name, console: false })
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
#[cfg(test)]
|
|
mod tests_ease_presets;
|
|
#[cfg(test)]
|
|
mod tests_panels;
|
|
#[cfg(test)]
|
|
mod tests_ui;
|
|
#[cfg(test)]
|
|
mod tests_ui_more;
|