Commit Graph

290 Commits

Author SHA1 Message Date
Brandon Thomas 6304956628 Merge pull request #835 from pravdomil/claude/cmd-arrow-frame-nav-3a2fc1
Shortcuts: Cmd+Left / Cmd+Right Arrow step a frame
2026-10-12 03:23:12 -04:00
Brandon Thomas 30f7ce8ae5 Merge pull request #826 from luniiya/feat/theme-favorites
UI: favorite themes and appearance shortcuts
2026-10-12 03:23:06 -04:00
Brandon Thomas 03a27f6eab Merge pull request #824 from redonion23/fix/753-layer-viewer-zoom
Layer panel: zoom and pan; View zoom acts on the focused viewer; Roto Brush hint (#753)
2026-10-12 03:23:03 -04:00
Brandon Thomas d1e88ee52b Merge pull request #807 from cdinhoo/fix/nonblocking-expression-diagnostics
Performance: reduce UI stalls while dragging layers
2026-10-12 03:23:02 -04:00
Brandon Thomas 225bbad013 Merge pull request #787 from vangel76/gap-floating-panel-windows
#486: move a panel into its own native window (multiple monitors)

# Conflicts:
#	crates/ui-egui/src/i18n/ui.rs
#	docs/gaps.md
2026-10-12 03:22:53 -04:00
Brandon Thomas 9ebabccbec Merge pull request #775 from vangel76/script-766-mouse-events
#766: ScriptUI mouse events reach script listeners

# Conflicts:
#	crates/engine/src/scriptui.rs
#	crates/script/src/lib.rs
#	docs/scripting-parity.md
2026-10-12 03:20:52 -04:00
Brandon Thomas be1d9928e0 Merge pull request #774 from vangel76/gap-proportional-scrubbing
#770: Proportional Scrubbing of Timeline values (Ctrl+Alt / Cmd+Opt)

# Conflicts:
#	docs/ui-parity.md
2026-10-12 03:20:51 -04:00
Brandon Thomas 78b32b61d4 Merge pull request #769 from vangel76/gap-mxf-import
#767: import MXF (OP1a / OP-Atom) footage

# Conflicts:
#	crates/ui-egui/src/menus.rs
#	docs/gaps.md
2026-10-12 03:20:05 -04:00
Brandon Thomas 7f9aa28c03 fmt: Brightness & Contrast test (#702 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:12:04 -04:00
Brandon Thomas 15387619ee Merge pull request #780 from vangel76/gap-project-panel-a11y
#776: Project panel arrow-key navigation and accessible names
2026-10-12 03:03:44 -04:00
Brandon Thomas bcb9ee1fa5 Merge pull request #764 from vangel76/gap-transition-presets
#757: Transitions animation presets in Effects & Presets
2026-10-12 03:03:31 -04:00
Brandon Thomas c72e47b7dc Merge pull request #761 from vangel76/gap-variable-font-filter
#741: font menu filters to variable fonts
2026-10-12 03:03:28 -04:00
Brandon Thomas b14096c335 Merge pull request #744 from vangel76/fix-732-toolbar-fill-type
#732: toolbar Fill/Stroke Options change the selected shape layers' kind
2026-10-12 03:03:20 -04:00
Brandon Thomas 25dcdcd2d8 Merge pull request #740 from vangel76/fix-303-mesh-warp-grid
#303: Mesh Warp's grid can be dragged in the Composition viewer
2026-10-12 03:03:16 -04:00
Brandon Thomas 8176c86043 Merge pull request #739 from vangel76
# Conflicts:
#	docs/gaps.md
2026-10-12 03:03:10 -04:00
Brandon Thomas 4aa2c6ba8b Merge pull request #734 from vangel76/fix-678-psd-drop-dialog
#678: dropped Photoshop files show the Import Photoshop File dialog
2026-10-12 03:02:56 -04:00
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
pravdomil 88bf89ea5d Shortcuts: Cmd+Left / Cmd+Right Arrow step a frame
Previous / Next Frame also answer to Cmd+Left / Cmd+Right Arrow, and Back / Forward
10 Frames to Cmd+Shift+Left / Right, next to the Page keys (After Effects' second
default keys). A command spec can now list several default shortcuts
(`alt_shortcuts`, `cmd!(…, ["PageDown", "Cmd+ArrowRight"], …)`); menus and tooltips
keep showing the first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:26:27 +02:00
Luniiya e1bcaab0a0 UI: favorite themes and appearance shortcuts
Port of storytold/photocraft#2445. Each theme in the appearance picker gets a
star at the right edge of its row. Starred themes are saved with the
preferences and listed in a right-click menu on the header's appearance button,
next to Appearance Settings, so switching between favorites takes one click.
2026-10-11 15:46:18 +02:00
cdinhoo be6134e8ad Performance: reduce UI stalls while dragging layers 2026-10-11 15:49:21 +03:00
redonion23 afdb958199 Layer panel: zoom and pan; View zoom acts on the focused viewer; Roto Brush hint in the Composition panel (#753)
- The Layer panel keeps its own magnification and pan (`ui.layer_zoom`,
  `ui.layer_pan`) with the Composition panel's magnification menu in its
  footer; the wheel / pinch zooms around the pointer, the Hand tool,
  Space-drag and a middle-drag pan, and the Zoom tool clicks zoom in
  (Alt: out). Navigation never paints or makes Roto Brush strokes.
- View > Zoom In / Zoom Out / Fit / Actual Size act on the Layer panel
  while it is focused, else on the Composition viewer as before.
- A Roto Brush click or drag in the Composition panel says the brush
  works in the Layer panel; double-clicking a layer there with a Roto
  Brush tool opens it in the Layer panel (as After Effects).
- Opening another layer resets the Layer panel to Fit.

Tests: crates/ui-egui/tests/ui_layer_panel_zoom.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:41:40 +02: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
vangel76 e7ada5bd16 #486: move a panel into its own native window (multiple monitors)
Problem: floating panels lived inside the main window only, so on a
two-screen setup no panel could go onto the second monitor (#486).

Fix: panel menu > Move Panel to New Window (and the agent/control command
`window.panelWindow {panel, window?}`) undocks the panel if needed and shows
its floating group in a native egui viewport the user can drag to any screen;
Move Panel to Main Window or closing the window brings it back. The window's
place and size are kept in the workspace (sanitised against hostile values)
and restored on the next start. Browser and test builds, which embed
viewports, show it as an in-app window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:10:12 +02:00
vangel76 884cb9987b #776: test that app shortcuts still run after a mouse click on a row
A mouse click gives a Project row no keyboard focus, and the arrow keys
only move keyboard focus when a row already had it, so Delete /
Backspace, Cmd+D and Enter keep working for mouse users, also after an
arrow key moved the selection. Pin that down with a headless test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:58:03 +02:00
vangel76 132bd21078 #776: Project panel arrow-key navigation and accessible names
Problem: with the Project panel focused the arrow keys did nothing (After
Effects selects the previous / next item, extends with Shift and opens /
closes folders with Right / Left). The custom-drawn rows, sort headers,
twirls, label swatches, proxy switches and footer buttons reached
AccessKit with no name or role, the empty list area and each row's name
cell were unnamed extra Tab stops, a Tab-focused row showed no focus
ring, and the arrows then moved egui's spatial focus instead.

Fix: Up / Down select the previous / next row (Shift extends from the
anchor), Right opens and Left closes the selected folder or selects the
parent; the moved-to row scrolls into view. A row with keyboard focus
keeps the arrows, focus follows the selection and the panel takes focus.
Rows are AccessKit tree items named "<name>, <type>" with selected,
level and expanded state; headers and row / footer buttons are named
buttons; one Tab stop per row and a focus ring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:53:48 +02:00
vangel76 1c06a793a5 #766: deliver ScriptUI mouse events to script listeners
Problem: ScriptUI controls never sent mouse events to scripts.
addEventListener("mousedown" | "mouseup" | "mousemove" | "mouseover" |
"mouseout" | "click") listeners never ran, so custom-drawn controls
(onDraw + mouse, as DuScriptUI builds them) could not react to the pointer.
docs/plugins.md listed this as missing.

Fix: scriptui.js builds a ScriptUI MouseEvent (clientX/clientY relative to
the target, button, detail = click count, screenX/Y, modifier keys,
getModifierState, stopPropagation, preventDefault) and delivers it in the
capture, target and bubble phases. addEventListener takes the capture flag.
The parent walk is bounded, so a script-made parent cycle cannot hang it.
Each published control lists the mouse events a listener wants
(Widget::mouse, inherited from containers). The new scriptui.mouse
command dispatches one event with clamped, finite values and rejects
unknown types and disabled controls. The egui ScriptUI view hit-tests the
pointer over script windows and docked panels, skipping covered layers and
windows blocked by a modal dialog. It sends mouseover/mouseout,
mousemove, mousedown/mouseup with click counts, and a click carrying the
pointer details in place of the plain click, so a checkbox still toggles
once. Handlers run under the existing script limits; a throwing listener
reports an error and the window keeps working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:39:23 +02:00
vangel76 ea0414e280 #770: Proportional Scrubbing of Timeline values (Ctrl+Alt / Cmd+Opt)
Problem: After Effects 26.2 spreads a Ctrl+Alt (Cmd+Opt) hot-text scrub
over the selected layers by selection order (first unchanged, last the
whole change, the ones between even shares). Our multi-layer scrub
(#491) always moved every selected layer by the same change.

Fix: prop.set takes `proportional` (with `selected` + `offset`); each
participating layer, the scrubbed one included, gets index/(n-1) of the
change in selection order, in one undo step; the first layer is left
alone (no key added). The Timeline value editor sends it while Ctrl+Alt
(Cmd+Opt) is held; that chord no longer fine-scrubs or unlinks Scale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:39:06 +02:00
vangel76 f0bd916543 #767: import MXF (OP1a / OP-Atom) footage
Problem: the Media Browser lists .mxf files, but File > Import's dialog
filtered them out and media::is_importable refused them, although the
pinned FilmCraft codecs behind crates/media already demux MXF (AVC,
MPEG-2 / XDCAM / D-10, VC-3 / DNxHD, ProRes, PCM audio) by content.

Fix: add "mxf" to VIDEO_EXTENSIONS and to the File > Import and Set
Proxy dialog filters. Tests check MPEG-2 4:2:2 long-GOP + 24-bit PCM and
DNxHD 36 MXF against ffmpeg frames (>35 dB, measured 64-67 dB), audio
level, and that the Import dialog offers MXF. AVI stays open: FilmCraft
has no AVI demuxer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:30:51 +02:00
vangel76 81bec748ec #757: Transitions animation presets in Effects & Presets
Problem: EffectCraft shipped only 8 original text-animator presets against
After Effects' 621; there was no Transitions category (ranked gap #7).

Fix: an original preset library, crates/engine/presets/transitions.json
(23 presets on our own Transition effects and layer opacity: fades,
directional and diagonal wipes, clock and fan reveals, blinds and slats,
pixel/mosaic dissolves, card flips, CC grid, radial scale, line sweep and
jaws wipes; In and Out). New commands anim.applyBuiltinPreset (adds the
effects with values and keyframes from the current time, one undo step,
reports any path that doesn't fit) and anim.builtinPresets. Effects &
Presets lists them under * Animation Presets > Transitions; double-click
applies. Sidecar and ATTRIBUTION.md entry; gaps/effects-parity updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:24:10 +02:00
vangel76 7afe723260 #741: font menu filters to variable fonts
Problem: After Effects 26.3's font menu can show only variable fonts.
EffectCraft's font menus (Character panel, Properties ▸ Text) had no such
filter, and text.fonts / MCP list_fonts didn't say which families are
variable.

Fix: the font scan marks a face variable when its table directory has an
fvar table declaring axes (header read only, no extra file loading). The
font menu gets a "Variable fonts only" checkbox above the results; it
combines with the typed search, keeps the keyboard in the search field and
stays set while the app runs. text.fonts reports `variable` per family and
takes `{variable: true}`; list_fonts passes it through. Catalog rows for
ja, zh-Hans, zh-Hant and uk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:14:50 +02:00
vangel76 0ae960102c #732: toolbar Fill/Stroke Options change the selected shape layers' kind
Problem: with shape layers selected, the Tools bar's Fill Options / Stroke
Options (None, Solid Color, Linear Gradient, Radial Gradient, and the
swatch's Alt-click cycle) only changed the defaults for new shapes.
shape.fillStroke had no kind, so a selected layer's fill or stroke could
not be removed or turned into a gradient from the toolbar; it had to be
edited in the Timeline. The swatch also kept showing the tool's kind, not
the layer's.

Fix: shape.fillStroke takes fillType / strokeType. None switches the
layers' Fills and Gradient Fills (or Strokes) off; another kind switches
them on and turns them into that kind, keeping the uid and the
properties both kinds share (blend mode, opacity, width, dashes...), and
gives a group with paths and no such item one (gradients span the
group's paths). It reports the first layer's fillType / strokeType, and
the toolbar shows them. The kind picks in the options popup and the
Alt-click cycle apply to the selected shape layers, one undo step each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:49:48 +02:00
vangel76 c733277263 #303: Mesh Warp's grid can be dragged in the Composition viewer
Problem: Mesh Warp keeps its Distortion Mesh in a hidden text parameter,
and the viewer only draws handles for point parameters (Bezier Warp's),
so with Mesh Warp selected there was no grid and nothing to drag: the
effect could not warp anything from the UI.

Fix: with a Mesh Warp effect (or one of its parameters) selected and
layer controls shown, the viewer draws the (rows+1)x(columns+1) grid
over the layer in effect space, with a handle on every vertex
(automation id viewer.meshWarp.<fx>.<row>.<col>). Dragging a vertex
writes its offset to the `mesh` parameter through prop.set, one undo
step per drag; a mesh that does not match Rows/Columns starts from the
undistorted grid, as it renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:43:38 +02:00
vangel76 d58253cc5a #738: Edit ▸ Paste pastes SVG markup as a shape layer
Problem: After Effects 26.3 pastes copied vector artwork as shape layers. Edit ▸ Paste only
knew the app's own clipboard, so SVG markup on the system clipboard was ignored; turning SVG
into shapes needed an imported footage file and Create Shapes from Vector Layer.

Fix: a new `edit.pasteSvg {svg, name?, comp?}` command builds a shape layer from SVG text with
the same conversion as Create Shapes from Vector Layer (paths, fills, strokes, gradients, clip
masks), above the selected layer with the SVG canvas centred in the comp, as one undo step;
oversized, non-SVG or shapeless text is rejected. In the UI, Ctrl/Cmd+V (and the native Edit ▸
Paste) run it when the system clipboard holds SVG. Copying in the app already replaces the
system clipboard with a note, so SVG there is newer than the app's clipboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:43:18 +02:00
vangel76 f9db3afd34 #678: dropped Photoshop files show the Import Photoshop File dialog
Problem: dropping a PSD/PSB file on the window imported it straight away.
The drop handler always passed `importAs` (Settings > Import > Default Drag
Import As) to `file.import`, and the Import Photoshop File form only opens
when `importAs` is missing, so the dialog that File > Import > File...
shows never appeared for drops.

Fix: a drop that includes a Photoshop file leaves `importAs` out, so the
form asks (other drops keep passing the preference). For dropped files the
form's Import Kind starts at Default Drag Import As; File > Import keeps
starting at Composition. The engine already falls back to the preference
for `drag` imports without `importAs`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:28:18 +02: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. Francis 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
Claude 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