Commit Graph

913 Commits

Author SHA1 Message Date
bflatastic e95780ca14 Import: Interpret Footage has Start Timecode, as in After Effects (#297)
After Effects' Interpret Footage has Start Timecode: Use Source File
Timecode, or Override Start, which changes the timecode the footage
shows and leaves its frames alone. The Start Frame field that trimmed or
padded a sequence's head isn't in After Effects, so it is replaced.

Footage.start_timecode (Override Start, in frames at the footage's
rate; None = Use Source, 0 as file timecode isn't read) relabels source
time: the Footage panel's current time and footage.info's `timecode`
count from it. file.interpretFootage takes `overrideStart` and
`startTimecode` (timecode, read at the footage's rate after any frame
rate change, or a frame number, 0 to 1e9). It applies to every footage
with time, not only sequences, and Reload keeps it. A sequence's frames
always run from its first file's number to its last.

Test: start_timecode_only_relabels_source_time (project),
interpret_footage_start_timecode_relabels_source_time (engine: the
reply, the Footage panel's timecode, frame numbers, Use Source, the
footage's rate, bad values, undo), Reload keeps it, and the Interpret
Footage fields in ui_drag_drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:50:13 +07:00
bflatastic 77af6d1a78 Import: missing sequence frames show a placeholder, as in After Effects (#297)
After Effects shows a missing frame of an image sequence as a
placeholder (colour bars) and has no Hold Previous Frame or Skip choice
in Interpret Footage, so EffectCraft doesn't either.

Interpret Footage loses its Missing Frames field, file.interpretFootage
its missingFrames parameter, and Footage its missing_frames setting. A
gap in a numbered sequence's numbering always shows colour bars. A
sequence imported with Force Alphabetical Order still plays its files
one after another (its numbering doesn't count), as in After Effects;
that is now an internal Footage.alphabetical flag, set on import and
kept by Reload. The Interpret Footage note lists the missing frames.

Projects saved before this have no such field and open with the After
Effects behaviour: a sequence with gaps shows placeholders where it
used to close them up.

Test: project sequence unit tests (placeholders, alphabetical order,
projects saved without the flag), tests_sequence (Start Frame instead
of Hold / Skip), sequence_gaps_show_placeholders (media) and the
Interpret Footage fields in ui_drag_drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:54:00 +07:00
bflatastic 736fa4ac3c Import: image sequences with a Sequence option, ranges, folders and missing frames (#297)
Image sequences were found only by the desktop media prober: one picked
numbered still brought its whole run, with no way to import just that
frame, picking every frame made one sequence item per frame, the browser
couldn't make sequences at all, and a gap in the numbering closed up
silently.

The engine now groups numbered stills itself (engine/src/sequence.rs):
one picked file brings its run from its folder, several picked files of
one run import as that range, a picked or dropped folder imports its
files with each run as a sequence, and Force Alphabetical Order takes
every image of that type in the folder by name. `file.import` takes
`sequence` (default on), `alphabetical` and `frameRate`; Replace Footage,
Reload, proxies and render outputs probe the same way. The media layer
probes one file; the browser lists its file table for the run and
registers every frame with its media pool.

File > Import > File... asks, like After Effects' "PNG Sequence"
checkbox, whether picked numbered stills import as a sequence, with
Force Alphabetical Order and the frame rate. Sequences are named
`shot_[0001-0250].png`.

Interpret Footage gains Missing Frames (Show Placeholder, colour bars as
in After Effects, the new default; Hold Previous Frame; Skip) and Start
Frame (the frame number at the footage's first frame: trims or pads the
head). Saved projects keep Skip, so their sequences play as before.

Form notes keep a readable width and the form fits its rows, so OK stays
in view. docs/footage.md explains importing footage and sequences.

Test: tests_sequence (engine: run, picked range, every frame, Sequence
off, folder, alphabetical, Interpret Footage Missing Frames / Start
Frame, Replace Footage and Reload), project sequence unit tests,
sequence_gaps_show_placeholders_hold_or_close_up (media),
import_dialog_offers_the_image_sequence_option and
dropped_frames_import_as_one_sequence_with_sequence_interpretation (UI).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:53:44 +07:00
BFlat d5f78f3fc0
Merge pull request #391 from storytold/fix/279-275-284-ui
Menu-bar keyboard, comp marker bin, UI Scale and Settings scroll (#279, #275, #284)
2026-10-09 18:44:55 +07:00
bflatastic 65d2c34e54 Menus: the Project panel's footage menu passes the keyboard navigator (#279, #290)
The footage item's context menu from #383 builds its entries with
menu_nodes, which #391 gave the menu bar's keyboard navigator and
depth; like the other context menus it passes a fresh one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:38:39 +07:00
bflatastic 7a9c459e42 Settings: Appearance > UI Scale sizes the whole interface (#284)
Settings > Appearance > UI Scale (appearance.uiScale, 75-200 %, default 100)
scales the whole interface on top of the display's own scaling, live as it is
chosen; Cancel puts it back, OK saves it and a saved scale applies from the
first frame. The Settings dialog's choice rows show a number setting with
choices as its text. Automation id: settings.appearance.uiScale.

egui's own Ctrl+= / Ctrl+- / Ctrl+0 zoom is off now: Ctrl+0 (Window > Project)
also reset the zoom behind the app's back, and the scale is a setting.

Test: engine tests_settings ui_scale_is_a_percentage_from_75_to_200; ui_panels
ui_scale_setting_sizes_the_interface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:34:27 +07:00
bflatastic 8e4d9988af Settings: the dialog no longer scrolls sideways (#284)
The page column had a fixed 590 pt width, a few points more than the dialog had
beside the page list, so the whole Settings dialog scrolled sideways a little.
It takes the rest of the dialog's width now.

Test: ui_scrolling settings_dialog_does_not_scroll_sideways.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:34:27 +07:00
bflatastic 7056801eb2 Timeline: the composition marker bin adds comp markers where they are dropped (#275)
As in After Effects, the right end of the Timeline's time ruler has a
composition marker bin (an original icon drawn in code). Dragging out of it
shows a marker following the pointer along the ruler's marker strip, snapping
like a dragged marker (current time, work area, other markers); releasing over
the time ruler's width adds a composition marker there with comp.addMarker (one
"Add Marker" undo step). Dropped back on the bin, nothing is added. The time
graph ends 18 px before the panel edge to leave the bin a column above the
layers' scroll bar. Automation id: timeline.markerBin.

Numpad * (or * typed with Shift+8), the keymap's Add Marker, now works in the
window too: egui has no key for it, so it comes as text.

The marker glyph and colour are shared helpers now (markers, nested markers,
the dragged marker).

Test: ui_timeline_zoom dragging_out_of_the_marker_bin_adds_a_comp_marker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:34:27 +07:00
bflatastic 6bb9bd8dab Menus: arrow keys and Enter drive the menu bar; shortcuts wait while a menu is open (#279)
The in-window menu bar now works from the keyboard as After Effects' does on
Windows: Alt pressed and released on its own focuses the bar, Left / Right move
between its menus and Down, Enter or Space opens one. In an open menu Up / Down
move the highlight (skipping disabled entries, wrapping around), Right opens the
highlighted submenu and Left closes it (on a plain entry they open the next /
previous menu), Enter or Space chooses the entry and Escape closes the menus.
Hovering an entry moves the highlight there, and a highlighted entry scrolls
into view in long menus.

While a menu, context menu or popup list is open, or the bar has the keyboard,
keys go to the menus only: the app's shortcuts don't run (Space started the
preview behind an open menu) and the panels don't see the keys (Enter on an
entry no longer reaches the Project panel's rename).

Tall menus already scroll inside the window since #277 (ui_scrolling's
a_menu_taller_than_the_window_scrolls). F10 stays View > Switch 3D View > Front,
as in After Effects.

Test: ui_menus (shortcuts_wait_while_a_menu_is_open,
arrow_keys_drive_the_menu_bar, enter_chooses_the_highlighted_entry).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:34:27 +07:00
BFlat 4aaadd0ed3
Merge pull request #387 from storytold/fix/295-extractor-layers
Effects: EXtractoR picks layers and channels of multi-layer EXRs (#295)
2026-10-09 17:58:37 +07:00
bflatastic 27cabcb36b Effects: EXtractoR picks layers and channels of multi-layer EXRs (#295)
EXtractoR's Red, Green, Blue and Alpha are text parameters naming
channels, and Effect Controls drew nothing for text parameters: there
was no way to choose a layer or channel. And a multi-layer OpenEXR file
whose colour is only in named layers (`diffuse.R`, Blender's
`ViewLayer.Combined.R`) didn't import at all: the image decoder wants an
unnamed RGB layer ("does not contain non-deep rgb channels").

- Effect Controls: EXtractoR gets a Layer popup listing the footage's
  OpenEXR layers. Picking one sets red, green, blue and alpha to its
  channels in one undo step, as After Effects' EXtractoR dialog does (a
  one-channel layer such as depth goes in all three colours). Red /
  Green / Blue / Alpha are popups of the layer's own R, G, B, A, every
  channel of the file, and (none). The lists are read only while open,
  so playing an EXR sequence doesn't parse files on the UI thread.
- `layer.channels {layer}` lists a layer's EXR layers and channels and
  what each layer shows in red, green, blue and alpha, for agents.
- Media: an EXR without an unnamed RGB layer imports and shows its
  colour layer (one named combined, beauty or rgba first); its channels
  stay available to EXtractoR and the 3D Channel effects.
- Raster: AuxChannels::layers / layer_rgba and split_channel.

Test: extractor_shows_the_chosen_layer_and_channels_of_a_layered_exr
(host: a layered EXR written with the exr crate imports, layer.channels,
EXtractoR renders spec and depth; it failed to import before),
extractor_offers_the_exr_layers_and_channels (UI: Layer popup, one undo
step, channel popup), layered_exr_without_an_unnamed_rgb_layer_imports
(media).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:32:09 +07:00
BFlat a6c72acf94
Merge pull request #383 from storytold/fix/290-tweaks
Tweaks from #290: label colours, nudging, motion-path handles, Time Remap, masks, context menus
2026-10-09 17:28:27 +07:00
bflatastic a8bf9a5b88 Docs: gaps.md records the #290 fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:21:13 +07:00
bflatastic 1afeb9d5c6 Timeline: right-clicking the work area offers Trim Comp to Work Area, Lift and Extract (#290)
The work area bar had no context menu. Right-clicking it now offers the
work area's commands from the menu bar: Trim Comp to Work Area, Lift
Work Area and Extract Work Area (After Effects).

Test: work_area_context_menu_trims_the_comp (ui_timeline_zoom).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:21:13 +07:00
bflatastic 0f44588bbd Project panel: footage items' menu has Interpret Footage, Replace and Reload Footage (#290)
Right-clicking imported footage offered no way to change its
interpretation. As in After Effects, right-clicking an item now selects
it, and footage items' menu has the File menu's footage commands:
Interpret Footage (Main, Proxy, Remember / Apply Interpretation),
Replace Footage, Reload Footage and Reveal in Explorer / Finder, with
the menu bar's labels, shortcuts and enabled state
(menus::submenu_at / entry_for). The panel's Interpret Footage button,
which only ever said to select footage, opens the dialog for selected
footage.

Test: footage_context_menu_interprets_the_footage (ui_project_rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:21:13 +07:00
bflatastic 2f3a507f8e Labels: label menus show each label's colour (#290)
The label menus listed the labels' names only, so a label couldn't be
picked by its colour. Every place a label is chosen now shows its
colour swatch before its name, as After Effects does: the Timeline's
label swatch (which now opens its menu on a click too, not only a
right-click), the Project panel's label swatch and a new Label submenu
in its item menu, Edit > Label, keyframe Label menus and the marker
dialog. One shared widget (widgets::label_entry / label_popup) draws
them; keyframe labels now show the names set in Settings > Labels.

Tests: label_swatch_opens_the_label_menu (ui_project_rows),
the_label_swatch_menu_shows_the_label_colours (ui_timeline_switches),
edit_label_menu_shows_the_label_colours (ui_panels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:21:12 +07:00
bflatastic 3174652ce4 Viewer: double-clicking a mask point selects the whole mask (#290)
Double-clicking a mask or shape path's point (or segment) put its points
in a free-transform box but left only the clicked point selected, so
once the box was gone a drag moved that one point. As in After Effects,
the double-click now selects all the path's points too: dragging inside
the box, or afterwards any of the points, moves the whole mask.

The test also draws a mask with the Pen, moves a point and undoes: each
undo takes back one action (the move, the closing, the last point).

Test: pen_mask_undoes_step_by_step_and_double_click_moves_the_whole_mask
(ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:43 +07:00
bflatastic 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
bflatastic 5b05a5ae80 Viewer: arrow keys nudge the selected layers 1 pixel at the current magnification (#290)
The arrow keys did nothing to a selected layer, so it couldn't be lined
up precisely. As in After Effects, over the Composition panel or the
Timeline an arrow key moves the selected layers 1 pixel at the viewer's
magnification (half a comp pixel at 200 %, so sub-pixel steps come from
zooming in) and Shift+arrow 10, one undo step per press; holding the
key repeats. Not while typing or with mask points selected.

The move is the new engine command layer.nudge {x, y (comp pixels),
layers?}: through each layer's parents into Position (or X/Y Position),
a key at the current time when Position is animated, locked layers left
alone. It shares the Align panel's move.

Tests: nudge_moves_layers_by_comp_pixels (engine),
arrow_keys_nudge_the_selected_layer_at_the_viewer_magnification
(ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:43 +07:00
bflatastic 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
bflatastic 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
bflatastic 56b374800d Masks: an open mask leaves its layer whole until it is closed (#290)
While a mask was being drawn with the Pen, its open path was filled as
if closed, so the layer went black except inside the points placed so
far, which made rotoscoping by eye impossible. As in After Effects, an
open mask makes no transparency: the layer shows whole until the path
is closed. Effects that take a mask as a path (Stroke and the others)
still get open masks.

Test: open_masks_make_no_transparency (render).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:12:22 +07:00
BFlat 735482d7ed
Merge pull request #381 from storytold/fix/284-timeline-keys-tools
Timeline, keyframes, Graph Editor, Puppet and pick whip fixes from #284
2026-10-09 17:11:47 +07:00
bflatastic 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
bflatastic 02bc2b8f9a Puppet: the tools stay on the rigged layer and pins go on the deformed mesh (#284)
With a Puppet tool, a click off the pins picked the topmost layer under
the pointer by its own bounds, and selected it. So a click where the
deformation had stretched the mesh past the layer's bounds went to the
layer behind (selecting it and giving it a Puppet effect), and any
click on another layer switched to it.

While selected layers have a Puppet effect, a click (or a press that
might start a pin marquee) now works only on them, as in After
Effects: on the layer whose mesh, as deformed now, is under the
pointer (the pin lands on the deformed shape), else the one whose own
art is (a new mesh); elsewhere nothing happens and the selection
stays. Without a rigged layer selected, the tools pick and rig the
layer under the pointer as before.

`puppet::contains` tests a point against a deformed mesh; `puppet.addPin`
uses it too (its own test only held for one triangle winding).

Test: puppet_pins_go_on_the_deformed_mesh_and_the_tool_stays_on_the_rigged_layer
(ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic 065f0c91e7 Viewer: the eyedropper samples under the pointer in a region of interest (#284)
Effect eyedroppers (other than keyers', which pick from the effect's
input) sample the composite the viewer shows; a test covers that on
main. But they, and the Info panel, read the shown frame as if it
covered the whole comp. With a Region of Interest, or the Extended
Viewer of a 3D view, the frame covers only that area, so they read a
pixel somewhere else. They now map the point through the area the
frame covers (`shown_region`, `frame_pixel`).

Tests: the_eyedropper_samples_under_the_pointer_in_a_region_of_interest
(ui_effect_controls), eyedropper_samples_straight_colour (fx_widgets).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic cf7263d6c6 Graph Editor: a handle drag moves the handles of every selected key (#284)
Dragging a Bezier handle in the Graph Editor changed only that key's
ease. As in After Effects, the same side's handles of every selected
key now move with it, by as much as the pointer moved (time and value
or speed alike), in one undo step. Each handle keeps its offset from
where the drag began, so a handle stopped at a limit (100 % influence)
doesn't drag the others on.

Test: a_graph_handle_drag_moves_the_handles_of_every_selected_key
(ui_keyframes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic 04ccf6f906 Timeline: keyframes without a dark outline (#284)
Each half of a keyframe glyph had a 1 px near-black border, so keys
that overlap read as a tangle of borders. Keys are now flat, as in
After Effects: each half is filled and edged in the key's own colour
(the hairline closes the seam between the halves), so overlapping keys
read as one shape. The Light theme's keyframe grey is darker so
unselected keys still stand out on its light time graph without the
border.

Test: keyframes_are_drawn_without_a_dark_outline (icons).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic 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 2eb56cff1c Timeline: a test that the cached frames show in green (#284)
#284 reported no cache indication in the Timeline. Main has one: a
green line under the work area bar for the frames cached at the
viewer's resolution, view and options (blue for disk-cached frames).
This test pins it down: once the viewer's frame has rendered, the
Timeline paints the green line from the start of the work area.

Test: the_timeline_shows_the_cached_frames_in_green (ui_ram_preview).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic 976784b0a6 Timeline: Shift-clicking layer bars selects the range (#284)
Shift-clicking a layer's name selected the layers from the last one
clicked to it, but Shift-clicking a layer bar in the time graph only
added that layer. Bars now select the same range (shared with the
name column: `shift_range`), and a click on a bar becomes where the next
range starts, as a click on a name does.

Test: timeline_shift_click_on_layer_bars_selects_the_range
(ui_workflow_selection).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic f9570d783a Viewer: scrubbing drops the viewer frames still waiting (#284)
Scrubbing asks for the viewer frame under the pointer at every step.
Each newer request only demoted the earlier ones to prefetch, so every
frame the pointer passed was still rendered, oldest first, holding the
frame threads the frame under the pointer needed. A newer viewer frame
of a comp now drops that comp's viewer frames that haven't started
(and releases them, so they can be asked for again). Frames already
rendering finish into the cache (rendering can't be interrupted
mid-frame), and the viewer keeps its last frame until the new one is
in. Prefetch frames and other comps' viewer frames stay queued.

Test: a_newer_viewer_frame_drops_the_ones_still_waiting (frames).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatastic 2419605cc4 Timeline: a press in the time ruler moves the current time (#284)
The ruler moved the current-time indicator only once the pointer had
dragged past egui's drag threshold, or on release for a click. As in
After Effects, the time now jumps to the pointer as soon as the button
goes down in the ruler, then follows the drag.

Test: a_press_in_the_ruler_moves_the_current_time (ui_timeline_zoom).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
BFlat 5742a5c567
Merge pull request #380 from storytold/fix/293-render-names-prores-alpha
Render Queue: output files end in their format's extension once (#293)
2026-10-09 16:59:34 +07:00
bflatastic be926adb92 Render Queue: output files end in their format's extension once (#293)
Renders on macOS came out as `Comp 1.mov.ecproj`, and an image
sequence's every frame as `Comp 1_00001.png.ecproj`; another output
was `Comp 2.mov.mov`.

Cause: the save dialogs for anything but a project went through the
project dialog hook, which filters to `.ecproj`; macOS's save panel
appends the allowed extension to any other name. 0.4.0's Render Queue
Choose File… used it (#192 moved Output To to its own dialog since),
and Save Frame As, Save Current Preview, Export Lottie, Save a Copy As
XML, Collect Files, Watch Folder and the Premiere export form's Browse
still did. Nothing then fixed the name: Output To kept `.ecproj` (no
format claims it), and changing the format replaced only the last
extension (`Comp 2.mov.ecproj` → `Comp 2.mov.mov`). `[compName]` of a
comp named `Comp 2.mov` also doubled it.

Fix: `Hooks::save_dialog` asks the dialog for the default name's own
extension (`pick_save_file`), the project dialog only for projects or
on hosts without another (the web); the desktop dialog has no filter
for a name without an extension (folders). `OutputFormat::with_extension`
makes an output path end in the format's extension exactly once
(project extensions and repeats of the format's dropped, a missing one
added; `logo.png.mov` kept), used by Output To, format changes and
`resolve_output`, so existing projects and templates render to the
right name too. Output To picks the file type from the extension
before a stray `.ecproj`.

Test: output_names_end_in_the_format_extension_once (project),
output_to_ends_in_the_format_extension_once (engine) and
save_dialogs_offer_the_file_extension (ui); the engine and ui tests
fail with the fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:51:12 +07:00
BFlat a62a298f0c
Merge pull request #382 from storytold/fix/274-zero-rate-guard
Media: resampling footage audio guards a zero mix rate (#274)
2026-10-09 16:50:04 +07:00
bflatastic fc5771f00f Media: resampling footage audio guards a zero mix rate (#274)
A requested rate of 0 would divide by zero in the resampler; it now
reads through the plain path, which returns silence for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:43:21 +07:00
Brandon Thomas cae67546dd Media: checked offset and as_chunks_mut in the stereo interleave (#305 follow-up)
clippy 1.99 flags chunks_exact_mut with a constant size; the skip offset
was also an unchecked slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:45:44 -04:00
Jonas Neubauer 22ce74ea0d Fix NVIDIA Dust & Scratches threshold rounding (#211)
Squashed from #211 without its before/after PNG (we don't commit binary images).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:22:05 -04:00
Brandon Thomas 48dc1ec4e5 Merge pull request #200 from EthanSuquet/fix/text-scale-continuous-raster
Render: text layers are always continuously rasterised
2026-10-09 00:21:56 -04:00
Brandon Thomas 7434cfb798 Merge pull request #305 from storytold/fix/274-audio-stutter
Media: footage audio at another sample rate no longer stutters (#274)
2026-10-09 00:21:54 -04:00
Brandon Thomas e4d1a4494f G3: audio scrub test waits on the clock, not a step count (flaky under load)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:17:53 -04:00
Brandon Thomas 742833bfa4 i18n: rustfmt the language table (#309 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:15:20 -04:00
Brandon Thomas 1f5218378c Merge pull request #298 from midasdf/fix/vertical-japanese-punctuation
M9: place Japanese vertical punctuation using font metrics
2026-10-09 00:04:52 -04:00
Brandon Thomas f43b956382 Merge pull request #310 from DWAA1660/fix/285-empty-glyph-fallback
Text: fallback skips a font whose glyph for the character is empty (#285)
2026-10-09 00:04:50 -04:00
Brandon Thomas 2a968c4d0e Merge pull request #309 from FeiLiuEM/i18n/zh-hans
i18n: add Simplified and Traditional Chinese (zh-hans, zh-hant) UI translation
2026-10-09 00:04:49 -04:00
Brandon Thomas 5cb3ee5b8a Docs: headless pitfalls point at mcp --autosave (#266 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:04:47 -04:00
Brandon Thomas 459c8987fe MCP: add the autosave _meta without IndexMut (#280 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:04:47 -04:00
Brandon Thomas 11848da0a1 App: only a held lock means another launch is starting (#283 follow-up)
A filesystem without lock support returned an error from try_lock, which
read as 'held' and disabled crash detection for good.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:04:47 -04:00
Brandon Thomas 5cce817d4d Merge pull request #266 from pat-the-wizard/docs/agent-pitfalls
Docs: pitfalls for agents driving EffectCraft over MCP
2026-10-08 23:51:24 -04:00