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>
Tab in a value being typed (hot text) dropped the keyboard: egui moved
the focus to the next widget, and the inline editors asked for the
focus again every frame, which also reset what keys a field keeps for
itself.
As in After Effects, Tab now commits the value and types in the next
value field of the same panel (the next dimension, then the next
property's), Shift+Tab the previous one. The hot-text fields of each
panel register in drawing order; the dock tags each panel's Ui so the
order stays within it. `keep_focus` asks for the focus only when the
field doesn't have it.
Tests: tab_commits_a_value_and_moves_to_the_next (ui_inline_edit:
Timeline and Effect Controls).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The inline expression field took its height from the saved
expression, so lines typed into it ran over the rows below until the
expression was committed.
Its height now comes from the text being typed (up to 8 lines, as
before), and its bottom edge drags it to any height in whole lines, as
in After Effects; the dragged height is kept per property in the
Timeline state (`expr_lines`).
Tests: the_expression_field_grows_while_editing_and_drags_taller
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
A pick whip dragged to the top or bottom of the Timeline's layers left
them where they were, so a property out of view couldn't be picked.
While a pick whip (expression or parent) is held within a row of the
top or bottom edge of the layers, or past it, the layers scroll that
way, faster the deeper, as in After Effects.
Tests: the_pick_whip_scrolls_the_timeline_at_its_edges
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Time Display Style could only be changed in Project Settings. As in
After Effects, Ctrl-click (Cmd-click on macOS) on the current-time
display of the Timeline or the Composition panel now toggles it between
Timecode and Frames (Feet + Frames goes to Timecode). It runs
`file.projectSettings {timeDisplay}`, so it is one undo step.
Under the Timeline's display the other style shows now (frames under
timecode, timecode under frames), as in After Effects; it always showed
frames.
Tests: ctrl_click_on_the_time_display_toggles_timecode_and_frames
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every bounding-box handle in the Composition viewer showed the
north-west / south-east resize cursor, so two corners and all four
edges showed the wrong direction.
Each handle now takes the cursor along the direction it scales, worked
out from the on-screen box: a corner points between its two edges (the
box's diagonal whatever its proportions), an edge handle across its
edge. The cursors turn with the layer's rotation, as in After Effects.
Tests: handle_cursors_follow_each_handle_and_the_layer_rotation
(ui_viewer: a wide box at 0°, 90° and 45°).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merges land faster than the hour-long FreeBSD job, so every main run was
cancelled and main went unverified. Only PR pushes cancel now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cause: pipeline::Cx rendered with Backend::Auto, which picks the CPU or the
GPU compositor for each top-level frame from measured frame times
(render::AutoPick: warm-up on both sides, a re-probe every 48 frames). The
two compositors differ slightly, so frames came out differently depending
on which side the timings favoured in that run, and two GPU exports of the
same frames differed.
Fix: the job renders every frame on the project's renderer, as After
Effects renders a job with one renderer: Backend::Gpu with Mercury GPU
Acceleration (frames the GPU declines still render on the CPU, as they do
in every GPU render), Backend::Cpu with Mercury Software Only. The viewer
and previews keep Auto. Docs (architecture, parity, web) say so.
Tests: render_settings::a_job_renders_on_one_compositor exports ten frames
with a stand-in GPU compositor that keeps Auto history and draws solid
green: with Mercury GPU Acceleration every frame is green and two runs are
identical (with Auto the warm-up sent frames to the CPU: it fails), with
Software Only the stand-in is never asked. The web worker GPU job test's
comment no longer describes Auto.
Diagnosis by @pat-the-wizard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cause: pipeline::Cx::render_at built the job's Renderer without a layer
cache, so every layer of every frame rendered from scratch (source, masks,
effects, styles), and the GPU compositor, whose uploads are keyed by the
cached buffers, uploaded every layer again on every frame.
Fix: each job (Cx) owns a LayerCache, the cache type the viewer uses, with
its default budget (1 GiB, the default Settings > Memory & CPU layer cache),
shared by the job's parallel frames and dropped with the job. With deferred
GPU readbacks it holds the accelerator's miss gate, as the job workers'
caches do. Cx::renderer builds the job's renderer (options, accelerator,
cache) for render_at and the test.
Tests: pipeline::tests::layers_render_once_per_job exports ten frames of a
solid animated only in Transform: the cache misses of the first frame are
the only ones (it fails without the cache: no layer is cached), and every
frame equals the frame rendered without the cache.
Diagnosis by @pat-the-wizard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#402 made Effect Controls' rows compact and aligned, as After Effects
draws them, and also added thin separator lines between parameter rows
in Effect Controls and the Timeline, with a Settings > Appearance > Show
Parameter Row Separators preference. After Effects has neither, so the
lines, the preference and the Tokens field go; the compact rows, the
alignment and the automation ids stay.
Saved settings with the old key load as before (unknown keys are
ignored).
Test: ui_parameter_spacing keeps the row geometry, hit target and
nesting tests; the separator tests go with the feature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Ease Presets panel (#254) is modelled on a third-party panel, so,
as with third-party tools in After Effects, it isn't core any more: it
is `extensions/scriptui-panels/Ease Presets.jsx`, a ScriptUI panel
bundled as an optional extension (Window ▸ Ease Presets.jsx), written
only against the public scripting API. Clicking a preset eases every
pair of neighbouring selected keyframes in one undo step (per
dimension, along the motion path for spatial properties, the keys'
other sides as they play); the fields edit the working curve, which the
panel draws; From Keys, Save, Rename and Delete as before. The twelve
built-in curves are unchanged.
Scripting API fixes it needed (After Effects parity):
- `app.settings` lasts between runs (`script_settings.json` in the
settings folder; `script.settings.get` / `script.settings.save`).
- Per-key methods (`setTemporalEaseAtKey`, `setInterpolationTypeAtKey`,
`removeKey`, the continuous / auto-Bezier / roving setters) no longer
change the user's key selection (they selected the key they edited).
- `keyInTemporalEase` / `keyOutTemporalEase` report linear and
auto-Bezier sides as they play, `keyIn/OutSpatialTangent` the
tangents the motion path plays.
- `selectedProperties` includes the properties of selected keyframes.
- `isInterpolationTypeValid` is false for Linear / Bezier on values that
only hold.
Removed from core: Window ▸ Ease Presets, the `keys.easePreset.*`
commands, `engine::ease_presets` and the `ease_presets.json` store,
`keyframe::EaseCurve` (nothing else used it) and the egui panel. User
presets saved by the v0.6.0 panel move into the panel's app.settings
section the first time settings load (the old file is left as is).
Docs: plugins.md (bundled extensions), parity.md, gaps.md G8, agents.md.
Test: app_settings_last_between_runs,
per_key_methods_keep_the_key_selection_and_report_eases_as_they_play,
spatial_tangents_are_reported_as_the_path_plays (each fails before),
the_ease_presets_panel_eases_every_selected_pair_in_one_undo_step,
a_typed_curve_scales_to_each_dimension_and_follows_motion_paths,
user_presets_save_rename_delete_and_persist,
presets_saved_by_the_old_core_panel_carry_over (script, through the
panel's controls), ease_presets_panel_eases_the_selected_keys_from_its_list
(UI: the panel docks and a click on a preset eases the keys).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EffectCraft's extension points compared with After Effects' (public
user guide and Scripting Guide) are now in docs/plugins.md, which
becomes "Plug-ins and extensions": scripts, ScriptUI panels and effect
plug-ins, with a short guide and an example panel for writing your own.
ScriptUI panels already came from `Scripts/ScriptUI Panels/` in the
settings folder, were listed at the bottom of the Window menu and
docked as tabs. What was missing: After Effects reopens the panels
that were open when it quit. Now the open panels are remembered in
`scriptui_panels.json` in the settings folder (a panel is added when
it opens and dropped when its tab or its script closes it; quitting
leaves the list), and `window.restoreScriptPanels` opens them again;
the desktop and web apps run it at start-up. A panel whose script is
gone or fails is reported and forgotten, and the others still open.
Where a panel was docked isn't kept, as workspace layouts aren't saved
between launches.
Gaps recorded in plugins.md and gaps.md G8: the Startup and Shutdown
script folders, keyboard shortcuts for scripts, ExtendScript's
`#include` / `#target` directives and `.jsxbin`.
Test: open_panels_open_again_at_the_next_launch (fails before: no
restore command), a_panel_dropped_in_the_folder_is_listed_and_opens_as_a_panel
(script), panels_open_at_quit_dock_again_at_launch (UI: the restored
panel docks with its controls; closing the tab forgets it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Tools bar's Snapping options menu (#265) offered per-feature
toggles (Edges, Corners, Centers, Anchor Points, Mask and Shape Path
Points) that After Effects' Snapping menu doesn't have. It now has the
two After Effects has:
- Snap Edges Extended (as before).
- Snap to Features in Collapsed Compositions and Text Layers: the
visible layers inside a precomp layer with Collapse Transformations
on are snap targets too, mapped into the outer comp (points always,
edge lines while the precomp layer is unrotated); a snap highlights
the precomp layer. Nested collapsed precomps are followed eight deep.
Every layer feature snaps again (corners, edge midpoints, centre,
anchor point, mask and shape path points); the snapping engine itself
is unchanged. `view.snappingOptions` takes `edgesExtended` and
`collapsedFeatures` (and `toggle` of either); the old keys are refused.
Test: collapsed_precomp_features_snap (fails before: no such option),
snapping_options_command_sets_and_toggles (the per-feature keys are
refused), snapping_options_pick_the_targets (AE behaviour kept: edges
extended or not, hidden layers, guides only), and the Snapping menu UI
test (only the two options are listed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Effects: effect points on shape layers start at the layer centre (#227)
# Conflicts:
# crates/ui-egui/src/panels/puppet_tool.rs
# crates/ui-egui/src/panels/viewer.rs
#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>
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 重置).
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>