Commit Graph
19 Commits
Author SHA1 Message Date
Claude 4de11be339 Time: in Frames style a typed number is a frame count (#610 follow-up)
parse_timecode reads bare digits as timecode pairs, so 00100 went to
frame 30 (1 s) instead of frame 100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:50:32 +00:00
Claude 312ece2b5c Merge pull request #610 from vbasky/fix/530-current-time 2026-10-10 18:49:52 +00:00
MarekSpodBiedry 60a85e97d4 Convert Expression to Keyframes: sort the keys by layer time on a time-reversed layer (#531) 2026-10-10 19:14:28 +02:00
Vikram Bhaskaran 9ae7788f8d Keep the playhead on the frame shown in the current-time field.
Timecode and Frames entries were parsed as absolute counts without the composition Start Timecode or Start Numbering Frames At offset, so committing the displayed text jumped to another frame.
2026-10-11 03:32:01 +11:00
MarekSpodBiedry 56cd3f1bf5 Expressions: Add Expression applies to the selected properties (#476) 2026-10-09 21:13:46 +02:00
bflatasticandClaude Opus 5.5 79bf5a6b59 Timeline: property names have the property menu (#407)
Right-clicking a property's name in the Timeline did nothing, so
Position's dimensions could be separated only from the Graph Editor.

As in After Effects, it now opens the property's menu: Reset, Edit
Value…, Separate Dimensions (Position and, once separated, X / Y / Z
Position, checked), Add / Remove Expression and Add Property to
Essential Graphics. Only Position separates, as in After Effects
(Scale stays one property).

`prop.reset` without `default` used to leave the value as it was: it
now gives the property its own default (an effect parameter's, or a
Transform property's, shared with Layer ▸ Transform ▸ Reset), which
also makes the Properties panel's Reset reset the values. `prop.set
{layer, prop}` without `value` opens the value dialog, like
`keys.set`.

Tests: timeline_property_names_have_the_property_menu
(ui_blank_context_menus), reset_gives_a_property_its_default (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:52:56 +07:00
bflatasticandClaude Opus 5.5 6fadae2818 Timeline: the pick whip picks the dimension it is dropped on (#363)
Dropping the expression pick whip on one of a property's values (the X
of Position) referenced the whole property. As in After Effects, a
drop on one value now picks that dimension: `transform.position[0]`;
a drop on the name still picks the whole property.

`prop.pickWhip` takes `target.dimension`.

The rest of #363 is unchanged: inserting at the cursor landed with
#284, and After Effects' property pick whip in the Parent & Link
column is left for later.

Tests: pick_whip_picks_one_dimension_of_the_target (engine),
the_expression_pick_whip_picks_the_dimension_it_is_dropped_on
(ui_inline_edit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:52:56 +07:00
bflatasticandClaude Opus 5.5 ed5ad56eb5 Viewer: a straight motion path has Bezier handles to curve it (#290)
The Position motion path drew a key's spatial handles only where its
tangent wasn't zero, and auto-Bezier keys have none at the path's ends,
so a two-key path (the usual start) had no handles to pull it into a
curve. Auto-Bezier keys now show a handle on such a side too, pointing
where the path leaves the key, a third of the way to the next key;
dragging it makes the key Bezier with that tangent. Linear keys still
show none.

Keyframe Interpolation > Spatial Bezier copied those zero tangents, so
the keys stayed straight with no handles (and read back as Linear); it
now gives them the same handles. The path keeps its shape and timing.

The handles come from the new keyframe::spatial_handles.

Tests: spatial_bezier_gives_a_straight_path_handles (engine),
a_straight_motion_path_has_handles_to_curve_it (ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:43 +07:00
bflatasticandClaude Opus 5.5 e6fd1461d7 Timeline: the bar past a layer's source is striped; time remapping it ends at the source's end (#290)
A footage or precomp layer dragged out past its source's end holds its
last frame, but its bar looked the same all the way, so the source's
end couldn't be seen. That part of the bar (and any part before the
source's start) is now darkened and striped in the label colour, with
an automation id (timeline.layer.<id>.bar.afterSource / beforeSource);
with time remapping on, the remap decides what shows there, so it isn't.

Enable Time Remapping on such a layer put its end key at the extended
out point; it now ends at the source's end, where the last frame starts
to hold (After Effects). Trimmed and reversed layers keep their keys.

Tests: time_remap_on_an_extended_layer_ends_at_the_source_end (engine),
the_bar_past_the_source_end_is_striped (ui_timeline_switches).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:42 +07:00
bflatasticandClaude Opus 5.5 567a947fd8 Timeline: deleting Time Remap turns time remapping off (#290)
Selecting Time Remap in the Timeline (its name, which selects all its
keys) and pressing Delete removed the keys, and a second Delete then
deleted the whole layer. As in After Effects, Edit > Clear with Time
Remap selected now turns time remapping off (Layer > Time > Enable Time
Remapping off), in one undo step, and the layer stays; one selected
Time Remap key on its own is still deleted as a key.

Test: deleting_time_remap_turns_time_remapping_off (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:12:22 +07:00
bflatasticandClaude Opus 5.5 bf09a4fcad Expressions: the pick whip inserts at the cursor while editing (#284)
Dragging an expression's pick whip always replaced the whole
expression, also while it was being edited (and the editor then wrote
its old text back over the reference). As in After Effects, a pick
whip pressed while the expression is being edited now puts the
reference at the cursor, replacing the selected text, and the editing
goes on with the cursor after the reference. Without an edit in
progress it replaces the expression as before.

`prop.pickWhip` takes the text being edited (`expression`) and the
selection in it (`range`, in characters) to insert there.

Tests: the_expression_pick_whip_inserts_at_the_cursor_while_editing
(ui_inline_edit), pick_whip_inserts_into_the_expression_being_edited
(engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:54 +07:00
bflatasticandClaude Opus 5.5 59c55e8a26 Timeline: dragging over stopwatches sets them all (#284)
As in After Effects, pressing a property's stopwatch toggles it, and
dragging on over the stopwatches above or below gives every property
the pointer passes the state the first one got (animated or not),
whatever its own, in one undo step. Alt+click still adds an
expression.

The gesture is the layer switches' press-and-drag from #238, moved
into `switch_gesture` and shared by both. `prop.toggleAnimation` takes
`merge` so the drag folds into one undo step.

Tests: dragging_over_stopwatches_sets_them_all (ui_timeline_switches),
stopwatch_toggles_with_a_merge_key_are_one_undo_step (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic f9990f7387 M13.20: every Timeline property shortcut — double presses AA (Material Options), PP (paint / Roto Brush / Puppet), SS (selected properties), RR (Time Remap), TT (Mask Opacity), MM (all mask properties; a single M now shows Mask Path), FF (missing effects), EE (expressions) next to LL and UU; Alt+Shift+A/P/S/R/T add or remove a keyframe at the current time on every selected layer in one undo step (keys.toggleTransform: separated X/Y/Z Position, Orientation and X/Y/Z Rotation on 3D layers); Ctrl/Cmd+` twirls the selected layers open or closed (from a reveal, their full tree) 2026-10-04 01:10:33 +07:00
bflatastic 52cf74e997 M13.18: drag layer rows in the Timeline to reorder them — a line shows where they land, the selected layers move together (an unselected row moves alone), one undo step, nothing for a drop in place; layer.arrange {above: layer} for agents 2026-10-04 00:31:59 +07:00
Brandon ThomasandClaude Opus 5.5 702ca1757f M5.7: frame-aligned layer, comp and keyframe times
- 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>
2026-10-01 22:19:29 -04:00
Brandon ThomasandClaude Opus 5.5 0acc106175 M6.4: pen tool masks, mask vertex selection/dragging, tangent handles
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>
2026-10-01 22:12:52 -04:00
Brandon ThomasandClaude Opus 5.5 941e087446 M5.6: inline expression editor, expression pick-whip, parent pick-whip
- 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>
2026-10-01 21:58:42 -04:00
Brandon ThomasandClaude Opus 5.5 1cba07bdfe M5.1: AE timecode/ruler display parity
- 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>
2026-10-01 21:48:47 -04:00
Brandon ThomasandClaude Opus 5.5 951ed1fa3d M5.2–M5.5: keyframe clipboard, easy ease, velocity/interpolation dialogs, roving, time remap/stretch/reverse/freeze, graph editor, separate dimensions, pick-whip
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>
2026-10-01 21:47:29 -04:00