Commit Graph

1455 Commits

Author SHA1 Message Date
Brandon Thomas 2c199d55f8 Merge pull request #384 from storytold/feat/297-image-sequences
Import: image sequences with a Sequence option, ranges, folders and missing frames (#297)

# Conflicts:
#	docs/parity.md
2026-10-09 09:31:51 -04:00
Brandon Thomas 14417c0400 Merge pull request #354 from thanhcong2k7/fix/ui-dock-focus-outline-zorder
UI: Draw panel focus outline after panel body to prevent clipping
2026-10-09 09:31:43 -04:00
Brandon Thomas 3d1b87f0d4 Merge pull request #345 from thanhcong2k7/fix/ui-commit-text-on-tool-switch
UI: Commit text edit session when switching away from Type tool
2026-10-09 09:31:42 -04:00
Brandon Thomas e52ff9ab95 Merge pull request #342 from HEOJUNFO/fix-313-single-pin-jump
Puppet: the first pin no longer turns the layer about it (#313)
2026-10-09 09:31:41 -04:00
Brandon Thomas d5beb4b3c4 Merge pull request #344 from MindlessRuff/fix/exr-over-range
fix(export): keep values above 1.0 in EXR output (#339)
2026-10-09 09:31:40 -04:00
Brandon Thomas f87f3b3e1a Merge pull request #357 from HEOJUNFO/fix-317-aiff-float
Export: AIFF 32-bit float writes AIFF-C fl32 instead of 24-bit (#317)
2026-10-09 09:31:38 -04:00
Brandon Thomas b6ba84d828 Merge pull request #353 from cjcwwww/i18n/zh-hans-terminology
i18n: match the Simplified Chinese labels to the installed 26.5.0 UI
2026-10-09 09:28:40 -04:00
Brandon Thomas a990432962 Merge pull request #312 from FeiLiuEM/font/cjk-locale
text, ui: the CJK fallback fonts follow the interface language
2026-10-09 09:28:38 -04:00
Brandon Thomas 64d1965648 Merge pull request #319 from thanhcong2k7/main
fix: rewrite Audio Spectrum to match After Effects linear frequency/amplitude model
2026-10-09 09:28:36 -04:00
Brandon Thomas 3df56ce9d9 MCP: describe_command and execute_command accept `id` again (#365 follow-up)
#365 rejects arguments missing from a tool's schema; both tools read `id`
as an alias of `command`, so declare it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:25:25 -04:00
Brandon Thomas 7b41793ab4 Merge pull request #365 from storybored379/feat/mcp-conventions
MCP: core tools, resources, render progress and cancellation
2026-10-09 09:25:25 -04:00
Brandon Thomas 754e998989 Merge pull request #374 from storytold/fix/368-tflite-budgets
Fix #368: tflite rejects models whose scratch or work exceeds a budget
2026-10-09 09:25:24 -04:00
Brandon Thomas a3cfc3e014 Merge pull request #388 from KazW/fix/mediapipe-crop-sampling
segment: sample crops like MediaPipe's ImageToTensor; no overflow in crop
2026-10-09 09:25:23 -04:00
Brandon Thomas d3684216e4 Merge pull request #373 from storytold/fix/367-nms-degenerate
Fix #367: weighted_nms terminates on NaN or zero-area boxes
2026-10-09 09:25:22 -04:00
Brandon Thomas 68d94b8031
Merge pull request #281 from XusBadia/dmg-background
macOS DMG: branded window background and icon layout
2026-10-09 09:20:44 -04:00
WaWu 1eecb40a6a i18n: match the Simplified Chinese labels to the installed 26.5.0 UI
Checked every submenu of Adobe After Effects 26.5.0 (Build 89), Simplified Chinese, against
the catalog and took the wording the application shows. 81 of the 618 labels change; keys,
order, count and the Traditional Chinese catalog are untouched.

Three groups:

- 61 labels where #309 and this branch disagreed and the application matches this branch -
  including the five blend modes whose Chinese names differ between After Effects and
  Photoshop (屏幕, 发光度, 较深的颜色, 较浅的颜色, 纯色混合), 导入参考... / 导出参考...
  (the application omits the 线), and 类型... for the Type settings page.
- 11 labels where both catalogs were wrong and this takes the application's wording:
  向基本图形添加属性, 移除所有的文本动画器, 显示关键帧的属性, 显示动画的属性,
  将合成裁剪到工作区, 裁切合成至选定图层边界, 基于 3D 视图生成摄像机,
  创建立体 3D 设备, 使用摄像机控制光线, 使用显示色彩管理, 淡灰色.
- 9 labels of spelling and punctuation: 沿路径进行空值追踪 (a stray '1'), 启用逐字 3D 化,
  裁剪合成到目标区域, 中灰(默认) with full-width parentheses.

Left for the maintainers: Material (the application says 素材, but it also calls Footage 素材),
the 导出 prefix on the Premiere export row (the application repeats it but drops 'Pro'), and the
verb in Reset 3D View (the application is inconsistent between 重设 and 重置).
2026-10-09 21:20:41 +08:00
cdinhoo b6cf4a8a60 UI: Compact and align effect parameter rows 2026-10-09 16:09:13 +03:00
sanyamsingh001 6d99f68330 G1: preserve GPU Posterize threshold parity
Use an explicit FMA for the final quotient correction at Posterize
boundaries. Align the kinetic motion-blur QA comparison with export
settings and credit sanyamsingh001.

Co-authored-by: Codex <codex@openai.com>
2026-10-09 23:03:05 +10:00
Dmytro Matviichuk ceda5bd607
i18n: add Ukrainian menu translation (#395) 2026-10-09 14:46:20 +02:00
FeiLiuEM 5344f304c3 text, ui: borrow the app at the test call sites, and drop a redundant lifetime
Two things are one level deeper than the first pass at this review:

- Nine of the call sites that pass the resolved language are inside test functions, where `app`
  is owned, so `crate::i18n::language(app)` needs `&app`. `cargo check --all-targets` (which
  compiles the test modules as well) is what reported them; a check without it does not, which is
  how they survived the first round.
- `clippy -D warnings` rejects the explicit `'static` on `const KANA` / `const HAN`: a const
  already has one.

On this branch `cargo fmt --all --check`, `cargo check -p effectcraft-ui-egui --all-targets`,
`cargo clippy -p effectcraft-ui-egui --all-targets -- -D warnings` and
`cargo test -p effectcraft-ui-egui` (40 targets, 113 lib tests) are clean.
2026-10-09 20:34:03 +08:00
Brandon Thomas 29c4d3ad15 Revert #200 (text always continuously rasterised): breaks kinetic_typography
The QA test's time-remapped precomp no longer matches the precomp rendered
directly (crates/automation/tests/qa/kinetic.rs:78); FreeBSD CI has been red
since. Reverting until the PR is reworked against that test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:22:58 -04:00
FeiLiuEM 55ba002acf Text: the variable-font test picks an axis that moves the advance
`style_variations_change_advances_and_outlines` picked `wdth`, else `wght`, else the first axis
with a range, and then asserted that the advance moves. That assumes the chosen axis changes
advances, which a monospaced family does not: Google Sans Code - the first variable face on a
machine where it is installed, and the one this check met on Linux - moves its outlines along
`wght` but keeps its advances fixed by design, so the test failed with "wght 300..800: advances
780 vs 780" while `measure` and the font were both right.

The test now tries the axes in order and uses the first one whose advance actually moves (Google
Sans Code answers on its own MONO axis, Bahnschrift on `wdth`), and skips with a message on a
machine whose variable faces never move an advance - the same shape as the "no variable font
installed" skip next to it. The outline assertion keeps its teeth: the axis is picked for moving
the advance, but whether the outline follows it is still checked.

On Linux with Google Sans Code and Bahnschrift installed: `cargo test -p effectcraft-text`
reported 43 passed / 1 failed before and 44 passed after. `cargo fmt --all --check` and
`cargo clippy -p effectcraft-text --all-targets -- -D warnings` are clean.
2026-10-09 20:21:05 +08:00
FeiLiuEM 7df9b981cd panels: the render queue, effects and the remaining panels read their text from the catalog
Render queue settings, templates and progress, the effect and preset panels, Essential Graphics,
precomposing, the tutorials, the credits and the info/scope readouts now go through `crate::i18n`.
Units (`px`, `%`, `mm`, `s`) and the automation ids and labels stay English on purpose: they are
read by scripts and tests, not by people.
2026-10-09 20:17:05 +08:00
FeiLiuEM a15a436647 panels: the timeline, viewer, project and layer panels read their text from the catalog
Timeline switches and column headers, the viewer's tools and guides, the Project panel's menus,
tooltips and the layer, properties, graph, marker, tracker and camera-tracker panels now go through
`crate::i18n`.
2026-10-09 20:17:05 +08:00
FeiLiuEM add2e977f6 panels: dialogs, settings and forms read their text from the catalog
The dialogs, Settings pages, unsaved-work and template prompts, the script console and the media
panels now go through `crate::i18n`, so their buttons, field names and hints follow
Settings ▸ General ▸ Language.
2026-10-09 20:17:05 +08:00
FeiLiuEM cceaba0452 i18n: a catalog for the interface below the menu bar
The menu bar has had a catalog since #229 (keyed by `(command, English label)`); everything below
it - panel labels, buttons, field names, tooltips, the sentences a panel draws itself - was written
straight into the code and stayed English in every language (#333).

This adds `crates/ui-egui/src/i18n/ui.rs`:

- Rows are keyed by the English source string, so a panel wraps a literal it already has instead of
  inventing an id, and a row that is missing falls back to English rather than to an empty label.
- `tr("Rename")` translates a plain string; `tr_args("Proxy Use: {}", &[&quality])` fills the values
  of a row that carries them. `tr_args` writes into its own buffer, so it cannot panic on a mismatch,
  and a test keeps every row's placeholder count equal to its English source.
- The language in force is set once per frame from the resolved interface language - the same value
  the CJK fonts are installed for - and kept in a thread-local, so the panels do not have to thread
  it through every function. The render threads never draw interface text.
- Simplified and Traditional Chinese are complete (384 strings); the Japanese rows are still open,
  and the catalog takes them without any other change.

Tests: the two Chinese catalogs must list the same source strings, no empty or duplicated rows, every
row must be asked for by a converted panel, and a converted panel that gains a hard-coded string in a
common call shape fails the build.
2026-10-09 20:17:05 +08:00
bflatastic efd348335f 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:52:19 +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
FeiLiuEM ad3da3c8de text, ui: follow a language change, and resolve the language before the fonts
Review follow-ups on #312:

- `crates/ui-egui/src/theme.rs`'s `japanese_font_tests` still called `install` with two
  arguments, so `cargo test` did not compile. Every call site now passes a language.
- The call sites passed the stored `general.language` preference. With the default `system`
  that is `""`, which handed the CJK choice back to the environment locale — one macOS and
  Windows usually do not set. They now pass `crate::i18n::language(app)`, the resolved
  language, so Match System draws Chinese on a Chinese system.
- Changing Settings ▸ General ▸ Language only reached the fonts on the first frame.
  `EffectcraftApp` now remembers the language its fonts were installed for and installs them
  again when it changes, so the switch applies without a restart.
- Documented that `fonts::set_cjk_locale` is process-global: the Han preference therefore
  applies to composition text as well as the interface chrome (docs/preferences.md and the
  function's own comment).

Rebased onto main after #309 landed — `header.rs` gained an `install` call site in the
meantime, and the catalogs now resolve through `i18n::language`.
2026-10-09 19:28:42 +08:00
FeiLiuEM a4d1e8d0b3 text, ui: the CJK fallback fonts follow the interface language
- `effectcraft_text::fonts::set_cjk_locale` lets the host say whose face should draw Han
  ideographs instead of leaving it to the locale environment, and drops the fallback cache when
  it changes. `han_order` reads that request first; the tag → order rule moves into the pure
  `han_order_for`, so it can be tested without touching process state.
- `theme::install` now takes the stored `general.language` value and registers one CJK system
  fallback per script in every font family, the interface's own script first: kana for Japanese,
  a Han character for `zh-hans` / `zh-hant`, then the other script so names in it still render.
  A face is registered at most once, and a machine with no CJK font installs nothing (the web
  build installs nothing, as before).
- Before this the probe was always `あ`, so Han text was drawn by the Japanese face whenever the
  operating system's locale was not Chinese: a Chinese interface, and Chinese file, layer or
  template names on any locale, both got Japanese glyph forms.
- Tests: the tag → order rule (`zh`, `zh_Hans`, `zh-Hant-TW`, `ko`, `ja`, `en`, empty), and the
  registration order plus once-only registration for a Chinese, a Japanese and an English
  interface.
2026-10-09 19:27:47 +08:00
bflatastic 50c9549529 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:00:07 +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
Kaz Walker 5304437629 segment: sample crops like MediaPipe's ImageToTensor; no overflow in crop (#385, #386)
Fixes #385, Fixes #386.

crop now maps the square's corners to the output corners, reads the source position as a
pixel-centre index, rounds to a whole 0-255 value, and takes a Border (Zero for the detector,
Replicate for the mesh), matching MediaPipe's CPU ImageToTensor. The frame size product is
checked, so an absurd size gives an lo-filled tensor instead of an overflow panic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 04:38:07 -06: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