An expression nested about 60 brackets deep overflowed the stack of a 2 MiB thread in the
JavaScript parser (release and debug; ~200 levels on 8 MiB), which aborts the whole app: it
can't be caught. The array-maths rewrite also nests one call per arithmetic operator, so a long
sum went the same way, and the rewriter itself recursed per bracket. rewrite() now measures the
nesting (brackets plus chains of prefix operators) before and after rewriting and turns an
expression deeper than MAX_NESTING (32) into a RangeError; check_syntax reports the same error.
Also replaces the unreachable!() in the bracket-tree builder with a let-chain, and denies the
panic lints in the crate.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
boa leaves the value stack of inner call frames behind when an exception
leaves a script (or a function called from Rust) from a nested call. Every
property read throws the pending-host-request abort from inside a prelude
helper, and so does any helper error (`thisComp.layer("Ghost")`), so the
thread's runtime filled up after ~100 evaluations of a pick-whip
expression: from then on every expression on that thread failed with
"RuntimeLimitError: reached the maximum stack size" and silently fell back
to its keyframed value (e.g. a follower layer stops following after ~90
render_frame calls).
Wrap the script and `__finish` in try/catch and rethrow from their own
frame, which unwinds the stack; backtraces (error line numbers) survive.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Layer > Camera > Create Stereo 3D Rig (left/right eye comps with expression-linked eye
cameras, Stereo 3D Controls effect, stereo comp through 3D Glasses), Create Orbit Null,
Create Cameras from 3D Model; Layer > Light > Create Lights from 3D Model
(KHR_lights_punctual), Control Light with Camera, Create Environment Light Background
Layer (equirectangular sky drawn through the camera). glTF cameras and punctual lights
are parsed by effectcraft-model.
Advanced 3D: per-sample motion blur over the comp shutter, and blend modes / track
mattes / Preserve Transparency composited through the 2D path in depth order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Shapes (SHP-2): Stroke Taper (pixels/percent start/end length, width, ease) and
Wave (amount, pixels/cycles, wavelength, phase) rendered as a variable-width
outline; Dash 2/Gap 2/Dash 3/Gap 3 (shape.dashes.add/remove, timeline +/-
buttons); radial gradient Highlight Length/Angle as a focal gradient.
Masks (MSK-1): mask feather points on the Mask Path (mask.featherPoint.*,
Mask Feather tool add/drag), per-point signed radius and tension, rendered
from the signed distance to the path; carried through vertex edits.
Motion blur (LYR-10): Adaptive Sample Limit comp setting; 2D layers take one
sample per pixel of shutter travel between Samples Per Frame and the limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sampleImage renders the sampled layer through the evaluating renderer's footage source
(EvalCtx::footage), cached per thread and frame. File > Import reads .json/.csv/.tsv as data
footage kept in the project. expr.errors collects failing expressions for the error bar;
expr.languageMenu lists the categorised Expression Language menu. Marker keys expose
protectedRegion and cue points; colorDepth, timeToFeetAndFrames, comp.activeCamera.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
TextDoc gains character style runs (font, size, colours, tracking, leading,
baseline shift, h/v scale, tsume, faux styles, caps, super/subscript, kerning
metrics/optical/manual, ligatures) and per-paragraph settings (justification
with last-line modes, indents, space before/after, direction, composer,
hanging punctuation), serde-compatible with single-style documents. Layout
handles mixed runs on a line (shared baseline, largest leading), every-line
composer, editing geometry (carets, hit testing, selection quads) and basic
vertical type. Renderer colours per run. Expressions get the sourceText style
API (style, getStyleAt, createStyle, setters with character ranges).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>