Commit Graph
258 Commits
Author SHA1 Message Date
Brandon Thomas 196dd80104 Merge pull request #709 from Ni-zav/fix/684-empty-viewer-project-drop-s20261011T035800Z-cf39
fix(viewer): create a composition when dropping footage onto an empty viewer
2026-10-12 03:02:47 -04:00
Brandon Thomas d7fe6d2e56 Merge pull request #705 from tylerjenningsw/enhance/custom-ui-scale
Settings: allow custom UI scale percentages (#682)
2026-10-12 03:02:46 -04:00
Brandon Thomas 162a2e2e01 Merge pull request #699 from didpublishing/fix/596-gpu-information
#596: GPU Information names the adapter, or why the GPU compositor is off
2026-10-12 03:02:43 -04:00
tylerjenningsw f4c5015b35 Settings: keep action buttons visible at high UI scales
At 200 % in a 1600x1000 window, Previous / Next / Cancel / OK were cut
off. Settings sized its pages from the window height less a fixed 170,
and the shared modal put everything, buttons included, in one more
scroll area, so a footer that didn't fit scrolled out of the dialog.

The modal helper gets a modal_fit variant without that scroll area: it
hands its body the height left under the title. Settings lays out its
buttons first, from the bottom up, and gives the page list and the page
what they leave; both scroll on their own and the buttons stay put. The
pages are a clipped child, so a wide page can't widen the dialog under
the buttons. Other dialogs keep modal as it was.

Tests: settings_buttons_stay_in_view_at_200_percent opens Settings at
200 % in 1600x1000 and checks that each button is inside the window and
painted unclipped (fails before this change: the scroll area clipped
them at the bottom). It scrolls the page to UI Scale without moving
them, then types 150 and clicks Cancel (200 % again, nothing saved) and
OK (150 % saved). The UI Scale test no longer needs a larger window and
clicks Cancel at 200 %.
2026-10-11 11:48:07 -04:00
tylerjenningsw 3df7e8f77c Shapes: a closed colour picker leaves the next Escape to the dialog (#612)
The picker marked every pass it drew, including the one it closed in, so
for one pass after closing the dialog Escape handler still stepped aside
and a quick second Escape did nothing. The mark is now set only while the
picker stays open and cleared in the pass it closes (OK, Cancel, Escape or
a click outside); the picker also consumes the Escape it handles.

Test: the_escape_after_the_picker_closes_cancels_the_editor presses Escape
to close the picker and again in the very next pass, with no settling
frames: the editor cancels. It fails on the previous marking.
2026-10-11 08:29:54 -04:00
Nigel Ihza Firdausy 31370e6144 fix(viewer): create a composition when footage is dropped on empty viewer 2026-10-11 11:02:26 +07:00
tylerjenningsw 42d8120147 Settings: allow custom UI scale percentages (#682)
Settings > Appearance > UI Scale offered only 75, 100, 125, 150, 175 and
200 %, so a size between 75 and 100 wasn't possible. It is now the shared
numeric field (Kind::Int(75, 200, "%")): type or drag any whole
percentage. The engine already stored the scale as a clamped integer and
the UI applies it with set_zoom_factor, so the range, the default, live
preview, Cancel and saved preferences are unchanged; the preset list
goes.

Numeric settings now apply a typed number once it is entered (Enter or
leaving the field), not at every keystroke: typing 85 used to apply the
partial 8, clamped to 75, and resize the whole interface before the 5.
Dragging still applies as it moves.

Tests: the UI Scale test types 85 a key at a time (nothing applies and
the field keeps the keyboard until Enter), Cancel, 50 and 300 clamp to
75 and 200, 90 with OK, and a new app with the saved preferences starts
at 90 %. The GPU Information transaction test types 125.
2026-10-10 23:47:27 -04:00
tylerjenningsw 5c1484b6e7 Shapes: the Gradient Editor uses the shared colour picker; Escape closes only the picker (#612)
The stop colour now uses widgets::srgb_color_button, the After Effects-style
picker every colour input opens (#638): hex field, HSB/RGB fields, OK and
Cancel, sRGB as the project stores colours. The stop keeps its alpha.

The dialog Escape handler only stepped aside for egui popups, and this
picker isn't one, so in a modal dialog Escape with the picker open closed
the dialog too. The picker now marks the pass it was open in
(color_picker::open_recently), and the handler leaves Escape to it: Escape
restores the colour the picker opened with and closes only the picker.

Tests: ui_gradient_editor clicks inside the picker's colour field over the
modal editor, types a hex code, closes the picker by clicking outside it
and saves the stop; a new test checks that Escape and the picker's Cancel
restore the stop's colour and keep the editor and its other edits. That
test fails without the Escape change. ui_color_hex still passes.
2026-10-10 23:43:40 -04:00
Dennis M. FrancisandClaude Sonnet 5.5 a6945de659 #596: GPU Information names the adapter, or why the GPU compositor is off
Settings > Previews > GPU Information was fixed text ("Compositing: CPU, N
threads... Display: egui on wgpu."): it never named a GPU, even while the GPU
compositor was running, so a card looked undetected. The System Report's GPU
line only said whether the compositor started.

sysinfo::gpu_summary builds the graphics lines from the session: the compositor's
adapter, or the reason compositing is on the CPU, and the adapter the window
itself got. The dialog uses it.

This does not explain why a particular card isn't used for compositing; that
needs the reporter's System Report (Help > System Report).

Tests: engine gpu_summary_names_the_adapter_or_says_why_there_is_none and UI
gpu_information_names_the_adapter_and_why_the_gpu_is_off (fails with the old
text). The six engine tests that fail on this Windows machine (relative footage
and render-queue paths) fail on main too.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 19:33:06 -07:00
tylerjenningsw ec1bc8cc99 Shapes: edit gradient color and opacity stops (#612)
The Timeline's "Edit Gradient…" on a gradient property (Gradient Fill /
Gradient Stroke ▸ Colors, Gradient Overlay) only painted its label. It now
opens a Gradient Editor (prop.editGradient): opacity stops above a preview
bar and colour stops below. Click to add a stop (it takes that track's
own value there), drag or type a Location to move it, set its opacity or
colour (egui's colour picker), Delete removes it (never the last of a
track, never while typing). The tracks stay in order while editing and the
selection follows its stop.

The editor works on a draft. OK sets the property once with prop.set at
the composition time it opened, snapped to the frame, with
timeBase: "comp" (offset and stretched layers get the key where it was
opened): one undo step, and nothing at all when the draft is unchanged, so
OK adds no keyframe. Cancel and Escape change nothing; Escape with the
picker open closes only the picker. On an animated gradient only the
keyframe at that time changes; when its stop counts no longer match a
neighbouring keyframe's (gradients only blend with equal counts) the
editor says so. The draft is saved in the typed form, so one colour stop
and each stop's RGBA survive.

prop.editGradient checks its target is a gradient property before the UI
opens anything, so agents get an error for a wrong one. New Gradient
color_at / opacity_at sample one track each (sample() multiplies opacity
in, which would apply it twice to a new stop).

Tests: ui_gradient_value (fails on main: the value had no automation id),
ui_gradient_editor (11), gradient_editor and keyframe unit tests.
2026-10-10 22:22:13 -04:00
skylar taylor-barrick 83ae6218ef UI.9: retain shared docking across upstream workspace changes 2026-10-10 19:25:49 -06:00
ClaudeandDennis M. Francis cb37c2511c App: a lost graphics device tells the user why the window closes and where the recovery copy is (#591)
Ported from #673: a native error dialog names the auto-saved recovery copy before the window
closes; agent runs through the control channel skip it. The UI calls Hooks::device_lost once.

Co-Authored-By: Dennis M. Francis <44986763+didpublishing@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:36:52 +00:00
skylar taylor-barrick 3dba472943 UI.9: share docking transactions and preserve viewer lifecycle
Preserve legacy selection repair, modifier routing, viewer lifecycle and workspace persistence through the shared docking integration. Qualify the complete workspace gate by refreshing the 18 stale Audio Spectrum hashes for the existing linear renderer, with an independent synthetic-tone regression.
2026-10-10 17:14:12 -06:00
Claude 1ac09c7fa3 Issue fixes: #591 GPU allocation and device loss, #553 motion-path vertex tools, #488 rename focus, #511 macOS sheet dialogs, #500 MCP file confinement, #343 desktop shortcut 2026-10-10 23:06:04 +00:00
Claude d6894249fa Inline editors: the whole name is selected only as a rename opens; arrows then move the caret (#488)
widgets::keep_focus (Project panel rename and comment, Render Queue, Essential
Graphics and Timeline fields) selected the whole text whenever it saw its field
without the keyboard focus, not just on the frame the editor opened. Anything
that took the focus for a frame (an input method, another handler) then
re-selected the name, so the arrow keys seemed to do nothing and Backspace erased
the whole name, as reported on Linux. The editor now remembers the pass it was
last shown: it selects all only when it opens and otherwise just takes the
keyboard back with the caret where it was.

The harness does not reproduce the report's Linux behaviour itself (arrows reach
the field there); the tests cover the path that did: a unit test drops the focus
for a frame and checks the caret survives (and that reopening selects all), and a
kittest renames a Project item and a Timeline layer with Left / Right / Up /
Down / End and Backspace, editing part of each name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:54:56 +00:00
Claude 6a3d5c6703 Viewer: the Pen, Add, Delete and Convert Vertex tools edit motion paths, with pen pointers (#553)
Motion-path hits were only handled for the Selection tool, so the Pen slot's
tools did nothing on a layer's motion path, and every Pen slot tool showed the
same crosshair. As in After Effects, on a motion path:
- Add Vertex (and the Pen) add a Position keyframe at the clicked frame dot,
  with the path's value there (prop.addKey), so the path keeps its shape;
- Delete Vertex removes the clicked keyframe (keys.select + keys.delete);
- Convert Vertex switches it between Linear and Auto Bezier spatial
  interpolation (keys.interpolation {spatial}).
motion_path now reports its frame dots for the hit test. Over the viewer the Pen
slot tools draw their own pen glyph (Pen, Add, Delete, Convert, Mask Feather) at
the pointer in place of the system cursor, and the Pen shows Add Vertex's pen over
a mask, shape or motion path it would add a point to.

Test: ui_viewer clicks a two-key motion path with each tool and checks the added
key, its spatial interpolation both ways, its removal, and the pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:54:56 +00:00
Claude 3c063b5075 GPU: a failed allocation reported as an internal error, or a lost device, no longer crashes the app (#591)
On Adreno (Windows ARM64) a failed vkAllocateMemory surfaces as an internal
ERROR_INITIALIZATION_FAILED, not out-of-memory. Gpu::within_memory only scoped
OutOfMemory, and wgpu's DeviceExt::create_buffer_init maps the new buffer at
creation and panics ("Failed to get mapped range") when the allocation failed,
taking down a frame thread. Buffers with initial contents are now created unmapped
and filled through queue.write_buffer (context::buffer_init), so a failed
allocation is only an invalid buffer and an error for the caller's scope, and
within_memory scopes Internal errors too, discarding the frame for the CPU path
as it does for out-of-memory.

The device was then lost, and egui-wgpu panicked on the main thread staging the
next frame's index buffer on the dead device. Once the host reports the
presentation device lost, the app draws nothing more (no mesh to stage), auto-saves
unsaved changes, keeps the crash-recovery marker so the next launch offers them,
and closes its window.

Tests: a device with a small buffer limit gets an over-limit and an unaligned
buffer_init without a panic and sees the error in its scope; the UI harness
reports a device loss and checks the next frame has no shapes, closes the window
and wrote the auto-save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:54:56 +00:00
Claude 9d689b1a61 Community follow-ups: dedup font files from overlapping folders (#665); empty-Timeline drop also accepts compositions, rustfmt (#662)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 22:29:04 +00:00
Claude 0b91b465df Merge pull request #650 from KattKnight/auto-bezier-spatial-tangents
# Conflicts:
#	crates/expr/src/tests.rs
2026-10-10 22:28:40 +00:00
Claude 44e3fe5f6e Merge pull request #662 from Ni-zav/fix/649-empty-timeline-project-drop-s20261010T2145Z-e7a2 2026-10-10 22:28:36 +00:00
Claude e615b06256 Merge branch 'fix/f6': community issue fixes
# Conflicts:
#	crates/gpu/src/tests.rs
2026-10-10 21:59:26 +00:00
Nigel Ihza Firdausy a9f4d92d5a test(timeline): avoid overlapping harness borrows in multi-drop regression 2026-10-11 04:55:57 +07:00
Nigel Ihza Firdausy bd906d3ee9 fix(timeline): create composition when footage is dropped into an empty timeline (#649) 2026-10-11 04:52:24 +07:00
Claude 657f34d40b Layers: with several layers selected, value edits, stopwatches and viewer Scale / Rotate change them all (#355, #491)
With several layers selected, After Effects applies a property edit to the same property of
every selected layer; we changed only the layer whose row was edited (the Properties panel
showed only the first selected layer), so a stopwatch switched on one layer and later moves
of the others had nothing to keyframe (#355), and scaling or rotating a selection changed
one layer (#491).

- Engine: `prop.set`, `prop.toggleAnimation` and `prop.toggleKey` take `selected: true`:
  the change also goes to the same property (by match path, `transform/scale`,
  `effects/<effect>#n/<param>`) of every other selected, unlocked layer whose value has the
  same kind, in one undo step (a scrub keeps folding into it through its `merge` key). With
  `offset: true` (a scrub) each layer changes by the same amount from its own value; a typed
  value sets them all. Position with Separate Dimensions is handled per layer.
- The Timeline, Properties and Effect Controls panels pass `selected` (and `offset` while a
  value is dragged). The Properties panel heads "(N) Selected Objects" (zh-CN / zh-TW / ja /
  uk rows).
- Viewer: a bounding-box handle drag scales every selected layer by the same ratio about its
  own anchor point; the Rotation tool turns them all by the same angle (one undo step).

Tests: engine test (stopwatch, scrub offset, typed value, keyframe button, one undo step,
unselected layers untouched); kittests: the Properties panel with three layers selected
(header, stopwatch on all three undone in one step, a scrub moving all three by the same
amount) and a handle drag scaling two selected layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 21:23:36 +00:00
Claude 56fd5d52b1 Dock: a panel dropped on a stacked panel becomes a tab in its frame, as in After Effects (#302)
A stack (the Default workspace's right column) held one panel per frame. Dropping a panel
onto the middle or the tab strip of a stacked panel inserted a new stacked frame below it,
and dropping on its top or bottom edge split the whole column.

A stacked frame now holds tabs (`StackEntry { panels, active, open, height }`):
- Center / tab strip drops add the panel as a tab of the anchor's frame and show it.
- Top / Bottom drops add a frame of its own above / below the anchor's frame only.
  (Left / Right still split the column: a frame can't hold a side-by-side split.)
- Clicking a frame's shown tab (or its header bar) expands / collapses it; another tab
  shows that panel. Closing a tab keeps the frame; its last tab removes it.
- Saved workspaces from before (`{"panel": …}` per frame) still load.

Tests: dock unit tests (Center / Top / Bottom on a stack, insert_tab, close, old saved
entries); a kittest dragging Effect Controls onto Properties makes a two-tab frame whose
tabs switch the shown panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 21:22:38 +00:00
KattandClaude Opus 5.5 6240ed646e Keyframes: auto-Bezier spatial tangents follow After Effects
Measured against After Effects 26.3 with the original
examples/ae-spatial-tangents.jsx (three paths, ten keys, even and uneven
timing): an auto-Bezier key's out tangent is (next - previous) / 6 and
the in tangent its mirror, whatever the key times; an end key uses its
one neighbour, (neighbour - key) / 6. EffectCraft gave end keys none and
scaled middle tangents by segment length, so paths with auto-Bezier keys
ran up to 28 px from AE's. All ten tangents now match; path samples are
within 0.05 px.

Tests that relied on end keys having no tangent now expect AE's: Spatial
Bezier keeps auto keys' handles (linear keys still get a third), the
motion-path handle is drawn at a sixth, and a straight loopOut path is
compared within 1e-9. docs/fidelity records the method and scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:44:49 +05:30
Claude 1bcfe6912d Timeline: colour properties get an eyedropper after their swatch, as in After Effects (#633)
A shape's Fill and Stroke Color (and every other colour property) in the
Timeline only had a swatch that opens the colour picker; After Effects puts
an eyedropper next to it to sample a colour from the Composition panel.
The Timeline now shows that eyedropper (`timeline.prop.<uid>.eyedropper`),
reusing Effect Controls' viewer pick: click it, then click in the
Composition panel, and the property is set to the sampled colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 21:11:53 +00:00
Claude 8b42f970b6 Merge branch 'fix/f4': community issue fixes 2026-10-10 20:00:37 +00:00
Claude 3effa9a2ea Timeline: the view follows the CTI past its edges when scrubbing and during playback (#468)
The ruler scrub set the time under the pointer and never moved the visible span, so with
the timeline zoomed in a scrub couldn't go past the edge, and a preview ran the CTI out of
view. As in After Effects:

- zoomed in, a scrub held past either end of the ruler scrolls the timeline that way,
  faster the further past, with the CTI riding the edge (repainting while held);
- during playback, when the CTI leaves the visible span the view pages to start at it
  (not past the comp's end).

Tests: a unit test of the paging and edge speed, and a kittest that holds a scrub past the
right end (the view scrolls, the CTI stays at the edge), then past the left end, then plays
from beyond the view (it pages to the CTI). (#468's Shift+F and black-layer items need
more information and aren't part of this.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:54:17 +00:00
Claude 7b38d4d74e Dock: a click anywhere on a stacked panel's header bar expands or collapses it (#301)
In the right-hand panel stack only the tab (the name plus its padding) answered clicks, so
the rest of the header bar did nothing, a small target for short names like Audio. As in
After Effects, the rest of the strip now toggles the panel too and focuses it. A stack group
holds one panel, so the bar is unambiguous; the panel menu keeps its own click. The bar
registers the automation id `panel.header.<Panel>`.

Test: a kittest clicks near the right end of Preview's header bar (collapses), its middle
(expands) and its panel menu button (stays open).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:54:17 +00:00
Claude f440adbb5a Dock: dragging a divider past a panel's minimum no longer crashes the app (#306)
A split's first side was sized with `f32::clamp(20.min(avail), (avail - 20).max(0))`.
`f32::clamp` panics when its lower bound exceeds its upper one, which happened for any
split with 0 < avail < 40 pt. Dragging the viewer/Timeline divider up takes the area above
it down to 40 pt, so the splits nested there (the right column's vertical split and
others) got about 36 pt and the layout panicked; no guard covers egui's update, so the app
closed with no dialog.

The split size now goes through `split_first` / `clamp_ordered`, which never panic: when
the room is too small for both minimums the lower bound wins, and non-finite sizes fall
back to it. The gutter drag uses the same clamp.

Tests: a dock unit test lays out every workspace in rects too short and too narrow for its
minimums (it panicked before) and checks `split_first` on hostile sizes; a kittest drags the
divider above the Timeline 2000 pt up and 3000 pt down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:53:56 +00:00
Claude 1bea11c9f1 Roto Brush: Cmd/Ctrl-drag resizes the brush, and the tool options show its Diameter (#509)
Cause: the Roto Brush and Refine Edge diameters live in the editor state and
the roto.options command sets them, but the UI never did: the Layer panel
only painted strokes and the bottom bar only set the view mode, so the brush
size could not be changed.

Fix, as in After Effects: Cmd-drag (Ctrl-drag on Windows and Linux) in the
Layer panel resizes the active brush (Roto Brush or Refine Edge) without
painting. The circle stays at the press point, its edge follows the pointer,
and the size is shown while dragging. The tools bar shows a Diameter field
(header.roto.diameter) while a Roto Brush tool is active. Both go through
roto.options, and the next stroke uses the new radius. Diameter is in all
four interface catalogs.

Test: roto_brush_ctrl_drag_resizes_the_brush (a 20 px Cmd/Ctrl-drag makes a
40 px brush and paints nothing, the next stroke's radius is half of it, and
the Diameter field is registered).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit b4183c28eea34ba097d7a16153b84fcf7aeea045)
2026-10-10 19:48:25 +00:00
Claude 3ba43b20c7 Effect Controls: file parameters get Choose…, Apply Color LUT asks for its LUT on apply, and text parameters get a field (#514, #473)
Cause: Apply Color LUT's only parameter, `lut`, was ParamUi::Hidden (set only
through commands), so the effect could not be used from the UI. Effect
Controls also had no widget for any ParamUi::Text parameter (only EXtractoR's
channels had one), so Cryptomatte's Selection, the OCIO effects' config file /
file / config names and Colorama's Output Cycle could not be set either.

Fix: a new ParamUi::File { filters } for paths. Effect Controls shows the
file's name (None when empty) and a Choose... button that opens the host's
file dialog with those extensions and sets the path (prop.set, so agents use
the same command). As in After Effects, applying an effect with an empty file
parameter from the UI (Effect menu, Last Effect, drops, Effects & Presets)
opens the file dialog straight away; cancelling keeps the effect without a
file. Apply Color LUT's LUT (.cube) and the OCIO Config File (.ocio) and File
(.cube/.3dl/.csp/.cc/.ccc/.cdl/.spi1d/.spi3d/.spimtx) use it. Text
parameters get a single-line field that sets the value when it loses the
focus (Esc keeps the old text). Saved projects are upgraded on open: a
Hidden or Text parameter whose spec is now a File parameter takes the File
UI and keeps its path (effects::migrate). docs/effects.md regenerated.

This covers #473's first part (the Selection field); picking mattes in the
viewer, the layer popup from the file's metadata, the GPU path and quoted
commas are separate.

Tests: effects hidden_and_text_paths_become_file_parameters (migration); ui
file_and_text_parameters_have_widgets (the LUT is asked for on apply and
inverts red to cyan, the Choose... button picks another LUT, typing into
Cryptomatte's Selection sets it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit 403863f703999d19d3a83d9976f76bdc7577e682)
2026-10-10 19:48:25 +00:00
Claude 40e87709d5 Import: numbered stills import as stills unless the Sequence option is on; dropped folders make sequences (#431)
Cause: SequenceOptions::from_params defaulted `sequence` to true for
file.import, so any numbered still (IMG_0001.jpg) brought its whole numbered
run from its folder, and several picked or dropped stills with a common
prefix became one sequence. Dropped files never asked, and the Import
dialog's "<format> Sequence" checkbox started ticked.

Fix, matching After Effects: file.import takes picked and dropped files as
stills unless `sequence: true` (the Import dialog's Sequence checkbox, which
now starts unticked; Force Alphabetical Order implies it), while the numbered
stills inside a picked or dropped folder still import as sequences
(SequenceOptions::folders), and `sequence: false` (Alt-drag of a folder)
imports a folder's files one by one. Replace Footage, reload, proxies and
render outputs keep detecting their sequence (SequenceOptions::default and
from_params are unchanged), and mediaBrowser.import keeps bringing a numbered
still's run unless `sequence: false`. docs/footage.md and docs/agents.md
describe the new defaults.

Tests: sequence import_takes_stills_unless_asked_for_a_sequence and engine
numbered_stills_import_as_stills_by_default (one dropped still and two picked
IMG_ stills stay stills, a dropped folder makes one sequence, the checkbox
brings the run); the existing sequence tests pass `sequence: true`; ui
import_dialog_offers_the_image_sequence_option (unticked by default: a still;
ticked: the run) and a_dropped_folder_imports_as_one_sequence_with_sequence_interpretation
(two dropped frames: two stills; the folder: one sequence).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit aa3c31db9ab3a774ee2f8ba867e1bafc1fce7aba)
2026-10-10 19:48:25 +00:00
Claude 09b3dbaad9 Merge branch 'fix/f3': community issue fixes
# Conflicts:
#	crates/ui-egui/src/widgets.rs
2026-10-10 19:48:24 +00:00
Claude 33dc3e72fa Merge branch 'fix/f2': community issue fixes 2026-10-10 19:48:18 +00:00
Claude d3e34156e5 Merge branch 'fix/f1': community issue fixes
# Conflicts:
#	crates/ui-egui/src/panels/text_panels.rs
2026-10-10 19:48:18 +00:00
Claude fbe0f7e1ce Cache bars: cached frames show at 1/3 resolution (#595)
The cache bars count the frames of `shown_series`, which rebuilds the
series from the frame key's scale in thousandths (333 -> 0.333). The key's
`opts` hashed the render options with the f64 scale, and the frames had been
rendered at 0.3333..., so the series never matched and the bars stayed empty
at Third (and any resolution that isn't a whole number of thousandths, such
as Custom 1/7).

`opts_hash` now hashes the scale rounded to the key's thousandths, which the
key carries anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:32:18 +00:00
Claude e96638093c Workspaces: saved workspaces last between sessions (#320)
Window > Workspace > Save as New Workspace and Save Changes to this
Workspace kept the layout only in memory (`UiState::saved_workspaces` /
`saved_floating`): nothing wrote them, so they were gone after a restart.

They are now written to `workspaces.json` in the settings store (the config
folder on desktop, OPFS on the web) when a workspace is saved, renamed or
deleted, and read back when the app starts; a file that doesn't read is
ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:32:18 +00:00
Claude 3bde85dfdb Timeline: Ctrl/Cmd+wheel and a trackpad pinch zoom time (#428)
egui turns Ctrl/Cmd+wheel and a pinch into a zoom factor (zoom_delta)
rather than a scroll, and the Timeline only zoomed on Alt+wheel, so
both did nothing over the layers. They now zoom time around the
pointer, like Alt+wheel. (B stays Set Work Area Start as in After
Effects; Split Layer is Ctrl/Cmd+Shift+D.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:48 +00:00
Claude 69bf806ff5 Graph Editor: buttons that don't fit the bar move to a » menu instead of being cut off (#457)
The Graph Editor's bottom bar lays out 17 text buttons in one row
(about 1100 px) on a painter clipped to the graph area, so on a
normal-width Timeline the right-hand ones (keyframe interpolation and
easing) were cut off and unreachable. Buttons that would pass the bar's
right edge now go, in order, to a » menu at its end; picking one there
runs the same action on the next frame. Hidden buttons register as
timeline.graph.more.<id> while the menu is open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:48 +00:00
Claude e152dfd1d1 Essential Graphics: Scale keeps its Constrain Proportions link, on by default (#338)
A Scale property added to Essential Graphics fell into the generic Point
control: two independent fields with no link, so dragging one value
distorted the layer, unlike After Effects, whose Scale control keeps the
chain link. It now draws the link (on by default) with one field per
axis; linked, an edit scales the others in proportion (Alt edits one),
using the Timeline's constrained() (moved to widgets). The link state is
the property's own, shared with the Timeline's chain link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:47 +00:00
Claude 76c9abad50 Viewer: the 3D Renderer menu shows the comp's renderer and switches it (#307)
The Composition panel's bottom bar drew a hard-coded "Classic 3D"
dropdown that ignored the comp's renderer and did nothing when clicked,
so an Advanced 3D comp still read Classic 3D. It now shows the comp's
renderer (translated) and, as in After Effects, its menu switches it
through comp.settings (one undo step); the automation label is the
renderer's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:36 +00:00
Claude bf5e6c6cec #581: the Discord pill fits its label and field text sits on the field's middle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:03 +00:00
Claude e4916e5ca9 #513: clicking off a mask's path, even on its layer, deselects its points
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:03 +00:00
Claude ad7df67602 #360: the Project row menu's New Comp from Selection uses the whole selection
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:03 +00:00
Claude 7a95a0d138 Project: a single unselected folder or data-file drag still reports why; index math saturates (#634 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:51:45 +00:00
Claude 954e89c661 Merge pull request #638 from FrederoxDev/feat/color-hex-input 2026-10-10 18:51:16 +00:00
Claude 1c68166b2e Merge pull request #634 from Ni-zav/fix/project-multiselect-drag-351-s20261010T1753Z-f4c2 2026-10-10 18:51:15 +00:00
Claude 5727754a54 Merge pull request #628 from MarekSpodBiedry/issue-505 2026-10-10 18:51:15 +00:00