New crate effectcraft-lottie (L3): export a comp to Lottie JSON / dotLottie
(precomp and base64 image assets; shape, solid, null, precomp, image and text
layers or glyph shapes; transforms with parenting, separated position,
anchor; keyframes with speed/influence converted to normalised i/o tangents,
hold keys, spatial ti/to; all shape items incl. gradients, dashes, trim,
repeater and path operators; masks, track mattes, blend modes, time remap,
timing, markers, optional expressions, a few Lottie effects) with a warnings
list for what Lottie cannot express, and the inverse import. Commands
file.exportLottie / file.importLottie with File > Export / Import entries.
Round-trip tests (values and rendered frames) and hand-written fixtures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Animation ▸ Animate Text (Enable Per-character 3D, every animator property,
Fill/Stroke Color RGB/Hue/Saturation/Brightness/Opacity submenus, Line Anchor,
Character Value), Add Text Selector ▸ Range/Wiggly/Expression (text.addSelector
now supports all kinds) and Text Animation Presets after Browse Presets, in the
engine menu tree. Stubs replaced: text.perCharacter3d, animatorLineAnchor,
animatorCharacterValue, addWigglySelector, addExpressionSelector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also: render uses web_time::Instant in layer styles; renderQueue.add documents
its render-settings/output-module keys so checked execution accepts them (+test).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- apps/effectcraft-web (L6): eframe web runner booting the shared EffectcraftApp with a browser
session: in-memory file table, <input type=file> pickers and drag-and-drop for open/import,
downloads for Save and Render Queue outputs (several files as one stored .zip), and the
window.effectcraft promise API over the control channel. Demo project on start.
- wasm32 runtime fixes: web-time for Instant/SystemTime (render, engine, export, ui-egui);
Frames renders queued jobs on the UI thread (Frames::pump) instead of a thread pool;
Render Queue runs inline; relative outputs resolve under / ; control PNGs inline.
- effectcraft-export: per-job output Sink (files to memory instead of disk); host FileExporter
carries it; test that sink exports touch no file.
- xtask web [--dev] [--serve PORT] (wasm-bindgen 0.2.129, optional wasm-opt, COOP/COEP server);
effectcraft-web in WEB_CRATES. docs/web.md, headless Chrome smoke test, favicon (+attribution).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts; register playback.audio (Composition > Preview > Audio)
as a real frontend command replacing its stub; layer_at uses content_buf.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Convert to Editable Styles / Show All / Remove All / Drop Shadow ... Stroke now
run main's layer.style.* commands; the layer.styles stub is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Timeline: Text group 'Animate:' and animator 'Add:' pop-ups (properties and
Range/Wiggly/Expression selectors), selectors/properties listed directly under
each animator, Path Options ▸ Path lists the layer's masks and hides the
other path options until one is chosen. Properties panel Add Animator lists
every animator property plus Enable Per-character 3D, and a Presets menu.
Animation menu: Animate Text, Add Text Selector, Text Animation Presets.
Expression Selector result replaces the selection (no Mode); selector popups
are non-keyframable like AE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Integrate main (render queue, timeline/keys, time remap, masks/pen, 3D, MCP/CLI).
- Menu tree now runs renderQueue.add, layer.enableTimeRemap, layer.timeReverse,
layer.freezeFrame/OnLastFrame, prop.separateDimensions, mask.setClosed,
layer.cameraSettings, view.3d.* / view.3d.last / view.reset3DView, main's
layer.autoOrient and keyframe velocity/interpolation/time-stretch dialogs;
matching stubs deleted (view.3d.default stays disabled).
- Keyframe clipboard is main's (keys.copy/paste); Paste Reversed Keyframes
reuses it. Animation-menu commands moved to commands/animation.rs.
- Main's spec menu paths/labels aligned with AE (Camera Settings under
Layer > Camera, "..." labels, Freeze On Last Frame).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Range selector complete (units, based on, modes, shapes, smoothness, ease
high/low, randomize), Wiggly and Expression selectors combined by mode;
all AE animator properties incl. line anchor, character offset/value,
skew axis, tracking type, fill/stroke HSB+opacity, blur; More Options
(anchor grouping, fill & stroke order, inter-character blending); Path
Options (text on a mask path); per-character 3D rendered as planes in
the 3D compositor; text animation commands and original presets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Layer Styles (Drop Shadow, Inner Shadow, Outer/Inner Glow, Bevel and Emboss, Satin,
Color/Gradient Overlay, Stroke) and Blending Options (Global Light, Fill Opacity, R/G/B,
Knockout, Blend Interior Styles as Group) as a layerStyles property group with AE names,
order and defaults. Rendered in layer space after effects (exterior passes composite with
their own blend modes below the body), cached per pass, scaled with render resolution.
Commands layer.style.* (menu Layer > Layer Styles), Global Light shared per comp, timeline
eye switches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts with perf (layer cache / Arc<Buf>), export (RenderOpts.view: None
for output), timeline (separate dimensions + one-node POI visibility), mask/pen
viewer gestures (alongside camera tools and view-aware layer matrices), key
dialogs (alongside Camera/Light Settings), render queue + MCP command modules.
3D path tangent honours separated position; DOF blurs a per-frame copy so cached
layer buffers stay shared. New test: cached renders match uncached after camera,
focus, light and 3D view changes. Fix a clone-on-copy lint from main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- layer.autoOrient {mode: off|alongPath|towardsCamera|towardsPointOfInterest}
(Layer > Transform > Auto-Orient…, Cmd+Alt+O); render support for Orient
Along Path (2D and 3D) and Orient Towards Camera (with recursion guard).
- Layers parented to cameras/lights follow their full rotation.
- Draft 3D turns off lights and shadows (and DOF).
- Turning the 3D switch off discards Z values, Orientation and X/Y Rotation.
- Tests for all of the above.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Comp durations (comp.new / comp.settings) are whole frames.
- layer.addItem start/out, layer.timing in/out/start/delta and time-stretch in/out snap to
the nearest comp frame; prop.addKey / prop.set with an explicit time key the nearest frame.
- The demo project builds every time on 29.97 fps frames (Lower Third in = frame 150 =
0:00:05:00, comp = 300 frames).
- Test: demo + commands stay frame-aligned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine (mask.rs, tests in tests_timeline.rs incl. render + undo/redo):
mask.new, mask.addVertex, mask.setVertex, mask.setClosed (Layer > Mask and Shape Path >
Closed), mask.selectVertices, mask.moveVertices (merged drags), mask.deleteVertices
(Delete with vertices selected), mask.remove / mask.removeAll (Layer > Mask). Edits of an
animated Mask Path key the current time.
Viewer: Pen tool on the selected layer - click = corner vertex, drag = Bezier vertex,
click the first vertex to close, Esc/Enter or another tool ends the path; rubber band to
the pointer. Selection tool: click/shift-click mask vertices, drag selected vertices,
drag tangent handles (symmetric; Alt breaks). Vertices register automation ids
viewer.mask.<uid>.vertex.<i>. Mode/inverted/feather/opacity/expansion stay in the
timeline mask rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Properties with an expression get an editor row in the timeline: Expression label,
enable (=) switch, pick whip, syntax-error warning, and a multiline code editor in the
time-graph area (commits on blur or Cmd/Ctrl+Enter via prop.setExpression).
- Expression pick whip: drag onto another property row -> prop.pickWhip (AE reference
expression). Parent pick whip in Parent & Link: drag onto a layer -> layer.setParent.
- Alt-click on a stopwatch starts with the property's own reference (transform.opacity).
- Timeline rows have variable heights; the empty-graph box-select area sits under the rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
engine comp.settings/comp.new: anchor (shifts unparented layers and their
position keys on resize), startTime/startTimecode, renderer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Current time shown as AE does: 0:00:02:15 (hours unpadded; time::format_timecode_ae),
frame count zero-padded to 5 digits (00075 (29.97 fps)).
- Time ruler labels in seconds:frames (00:15f, 01:00f) with frame-based adaptive spacing.
- time.set snaps seconds to the nearest frame (2.5 s at 29.97 -> frame 75).
- Mode / Track Matte columns hidden until Toggle Switches/Modes (F4), as in AE's default.
- Timeline outline pane sized to its columns and clipped so Parent & Link never draws
over the time graph in narrow timelines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine (all commands with undo/redo + tests in engine/src/tests_timeline.rs):
- keys.copy/paste (Edit Copy/Cut/Paste route to keys when keys are selected;
relative timing kept, paste at CTI, cross-property paste of same type),
keys.selectAll (Cmd+Alt+A), keys.nudge* (Alt+Arrow, Alt+Shift+Arrow),
keys.set / keys.setEase (graph editor drags), keys.info (dialog data),
keys.timeReverse; keys.interpolation gains continuous/auto Bezier, spatial
interpolation and roving; keys.velocity takes per-dimension values and
Continuous.
- Roving keys (keyframe::retime_roving): constant speed along the motion path.
- Layer > Time: layer.timeStretch (percent|duration, hold in/current/out),
layer.timeReverse (Cmd+Alt+R), layer.enableTimeRemap (Cmd+Alt+T),
layer.freezeFrame, layer.freezeOnLastFrame; render uses Time Remap as the
source time for precomp/footage layers.
- prop.separateDimensions (X/Y/Z Position; prop.set on Position writes them).
- layer.setParent compensates so the layer stays in place (compensate:false
to opt out).
- prop.pickWhip writes AE-style reference expressions (thisComp.layer(..),
effect(..)(..), mask(..).maskFeather, temp=…;[temp,temp], …[0]).
- prop.setExpression: syntax errors keep the text but disable the expression.
- expr: arrays of properties returned by an expression are resolved.
UI: Keyframe Velocity, Keyframe Interpolation and Time Stretch dialogs;
Graph Editor (value + speed graphs, draggable keys and Bezier/influence
handles, auto-zoom, fit, show selected, separate dimensions, hold/linear/
auto Bezier/Easy Ease buttons); key + layer context menus. Clears the
workspace clippy -D warnings failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- render: LayerCache (content-keyed processed-layer cache: source -> masks ->
effects, keyed by a hash of the layer's non-transform props evaluated at the
frame time, solid item, switches, scale; time folded in for clock-driven
effects / wiggle paths). Static and transform-only layers render once;
edits only re-render the edited layer. Session + UI frame jobs share it.
- effects: TIME_DEPENDENT list + test that every clock-reading effect is listed.
- raster: affine composite fast path (per-row span clipping, unchecked interior
bilinear, integer-translation copies, inlined Normal blend), checked against
composite_warp_reference; box/Gaussian blur without transposes, two
ping-pong buffers; accumulate_warp so motion blur sums into one buffer
instead of one full-frame image per sub-sample.
- ui: viewer frame is urgent and jumps the prefetch queue; stale-revision jobs
are dropped; paused prefetch waits for the viewer frame; "Frame Render Time"
reports viewer frames only; parallel f32 -> Color32 conversion.
- cli: `frame --bench N` (per-layer/per-effect timings, wall + CPU time) and
`frame --bench-play N` (consecutive frames with/without the layer cache).
- tests: cache transparency (scrub, edits of values/keys/effects/masks/
solids/text/timing), hit/miss accounting, time-dependent effects, demo
cached == uncached, fast warp/blur/accumulate == reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Properties panel (panels/properties.rs): Layer Transform with keyframe navigator
or stopwatch, AE value formatting (integers, %, Nx+N° rotation), linked Scale,
Reset; Text (font/style/size/leading/tracking/stroke, fill/stroke toggles);
Paragraph (7 alignments); Text Animation > Add Animator; scrolling.
- Tabs name what they show: Composition <comp>, <comp> timeline, Effect
Controls <layer>, Properties: <layer>.
- Default workspace: full-height right column, Preview over Properties.
- engine: time.go nextKey/prevKey accept prop uid; layer.setText supports all
seven justify modes, applyFill/applyStroke and Auto leading (fixes the
Paragraph 'justify' button mapping to Justify All).
- examples/snapshot: render the real UI headless (egui_kittest + wgpu) with
scripted control-channel steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>