Commit Graph
838 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 ec892ef145 Media: footage audio at another sample rate no longer stutters (#274)
AAC audio in an MP4/MOV whose sample rate differs from the mix rate
(a 44.1 kHz clip in the 48 kHz preview or export, or the reverse)
stuttered and sounded low-pitched in previews and in exported movies.

Cause: FilmCraft's MP4 reader, asked for audio at a rate other than
the file's, applied the edit list's priming offset (1024 samples for
AAC) twice when it resampled: each read started about 1024 source
samples early and extrapolated a ramp over its first ~940 samples
(peaks near 100 against a 0.5 tone). Export and the preview mix ask
for audio a frame or a device buffer at a time, so the ramp repeated
dozens of times a second. Same-rate reads take FilmCraft's copy path
and were exact. FilmCraft fixed this upstream in 5bf2203, 87 commits
past the pinned revision.

Fix: MediaPool reads footage audio at the source's own rate and
resamples it itself (linear, by absolute sample position, so a range
comes out the same however it is split into reads), for every
container.

Test: resampled_audio_read_in_frame_pieces_matches_ffmpeg (44.1 kHz
AAC read at 48 kHz and 48 kHz at 44.1 kHz in 29.97 fps frame pieces,
against ffmpeg's decode: max error 44 before, 0.002 after) and
movies_carry_resampled_footage_audio (a 29.97 fps comp with 44.1 kHz
AAC footage exported to MP4/AAC and MOV/PCM, decoded by ffmpeg: max
error 8.7 before, 0.03 / 0.002 after).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 22:16:35 +07:00
BFlat 929a9c0f01 Merge pull request #278 from storytold/fix/259-fractal-soft-linear
Effects: Fractal Noise's Soft Linear has no lattice creases (#259)
2026-10-08 21:19:54 +07:00
BFlat e283b25efc Merge pull request #296 from storytold/fix/model-import-alpha
Import: a 3D model doesn't ask how to interpret its alpha (#264)
2026-10-08 21:19:24 +07:00
bflatasticandClaude Opus 5.5 69dc1b26fb Import: a 3D model doesn't ask how to interpret its alpha (#264)
Importing a glTF / OBJ model opened Interpret Footage: the probe
reports straight alpha and the file has no alpha label, so with
Settings ▸ Import ▸ Interpret Unlabeled Alpha As at Ask User (the
default) the model was listed as unlabeled. Only image footage
(stills, sequences, video) can have unlabeled alpha; a model renders
its own, and audio and data have none.

Test: importing_a_model_does_not_ask_about_alpha (engine).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:18:52 +07:00
Brandon ThomasandClaude Opus 5.5 6943872cf6 Bump version to 0.6.0 (from 0.5.0) for release
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v0.6.0
2026-10-08 10:02:02 -04:00
BFlat 6ffbfe7cea Merge pull request #292 from storytold/fix/229-wasm-warning
i18n: the browser build's Match System uses supported() too (#229)
2026-10-08 21:01:21 +07:00
bflatasticandClaude Opus 5.5 646a6842cd i18n: the browser build's Match System uses supported() too (#229)
`supported` was only called on desktop builds, so the wasm32 check
warned that it was never used. The browser build, which has no system
locale here, now resolves Match System through supported(None)
(English) instead of a literal.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:00:55 +07:00
bflatasticandClaude Opus 5.5 07c66e0be3 Test: Fractal Noise's Soft Linear shows no lattice (#259)
Two measurements on Turbulent Basic: with one 16 px layer whose lattice
lines run through pixel centres, the slope across the lines relative to
the average slope (0.019 with the quintic fade, 0.75 now; must be over
0.5); and at the issue's settings, the gradient along the lattice axes
against the diagonals (1.21 before, 1.03 now; must be under 1.15).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:00:42 +07:00
bflatasticandClaude Opus 5.5 fe0d11c5cc Effects: Fractal Noise's Soft Linear has no lattice creases (#259)
Soft Linear was value noise with a quintic fade between two lattice
values per axis. That fade has zero slope across every lattice line and
at every lattice point, so the noise's contours met the lattice at right
angles and flattened around its points. The turbulent fold (1 - |s|) and
high contrast turned that into a visible square grid at each layer's cell
size (Turbulent Basic, Contrast 140, Brightness -35: gradients 1.3x as
strong along the axes as along the diagonals; Spline 1.1x).

Soft Linear is now linear interpolation softened by a one-cell box
filter, i.e. the uniform quadratic B-spline over the 3 x 3 lattice values
around the nearest one (evolution still fades between two slices): smooth
everywhere, with half the neighbours' difference as its slope on lattice
lines, and cheaper than Spline (9 taps instead of 16). Averaging
lowers the noise's spread, so a fixed gain about mid-grey restores the
quintic fade's variance and existing projects keep their contrast and
brightness (issue settings: mean 0.356 -> 0.362, std 0.147 -> 0.152,
axis bias 1.28 -> 1.05).

Only Fractal / Turbulent Noise's Soft Linear changes: generate's
value_noise and its other callers are untouched. noise3 now shares
generate's lattice hash and fade instead of copying them. The GPU
kernels (pointwise Fractal Noise and fxn_fractal) call the same
soft_linear_noise. No golden hashes pin these cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:00:42 +07:00
BFlat 1ab26e1119 Merge pull request #286 from storytold/fix/264-model-layers-cpu
Engine: adding a 3D model switches a Classic 3D comp to Advanced 3D (#264)
2026-10-08 20:59:53 +07:00
0f2814de8c README Downloads: where to get it first, then builds with Windows before macOS (#291)
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-08 09:50:59 -04:00
e2a5c99a26 Release: FreeBSD x86_64, Flatpak x86_64 + aarch64, AppImage zsync updates; README Downloads (#287)
- release.yml: `freebsd` job (FreeBSD 14.3 VM, the freebsd.yml packages) running the new
  packaging/freebsd/package.sh; `flatpak` matrix (x86_64 on ubuntu-24.04, aarch64 on
  ubuntu-24.04-arm) repackaging the Linux tarball with packaging/linux/flatpak-bundle.sh and the
  new ai.storyteller.effectcraft.bundle.yml; both added to the draft release's needs.
- AppImages embed gh-releases-zsync update information and ship a .zsync beside them; the Linux
  job installs zsync and checks both.
- version, Linux and web jobs drop `environment: release` (they read no secrets), so a dispatch on
  a branch is a full dry run of every unsigned job; the draft release still needs the signed jobs.
- Flatpak finish-args documented and shared by the bundle and Flathub manifests (lint checks they
  match); audio, GPU, portals, xdg-pictures/videos/documents, network for the optional model
  downloads. The Flathub manifest now also installs effectcraft-cli.
- packaging-lint runs the FreeBSD layout dry run.
- README: Downloads section (every file a release ships) and nav entry; docs/releasing.md updated.

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-08 09:48:00 -04:00
bflatasticandClaude Opus 5.5 3cc9520685 Test: an imported GLB shows in a new composition in software (#264)
The host test writes a binary glTF cube (built in the test), imports it
with the real media layer, makes New Comp from Selection and renders
with no accelerator (the CPU path an Intel HD Graphics 5500 most likely
gets): the comp is Advanced 3D and the red cube covers part of the
frame. Before the fix the comp stayed Classic 3D and drew nothing. The
engine test checks the switch, its single undo step, the toasts, that a
second model doesn't switch again, and primitives.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:45:07 +07:00
bflatasticandClaude Opus 5.5 6d20cc2151 Engine: adding a 3D model switches a Classic 3D comp to Advanced 3D (#264)
Imported GLB/glTF models were invisible in the Composition viewer while
text showed. Only the Advanced 3D renderer draws model and primitive
layers, and new compositions (and New Comp from Selection) are Classic
3D by default, but adding a model layer left the comp's renderer alone,
so the model drew nothing on every backend. It wasn't the reporter's
GPU: on this machine the viewer showed nothing for a GLB in a new comp
with the GPU compositor (Vulkan) and with Mercury Software Only, and
the model in both once the comp used Advanced 3D.

As After Effects documents ("Advanced 3D gets automatically selected
when you add a 3D model to the composition"), layer.newModel /
layer.addItem of a model and layer.newPrimitive now switch a Classic
3D comp to Advanced 3D inside the same edit (one undo step, Geometry
Options added to its text and shape layers like Composition Settings
does) and toast that the comp changed renderer. Switching a comp that
has model layers back to Classic 3D toasts why they disappear.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:45:07 +07:00
BFlat b0fa9f1943 Merge pull request #282 from storytold/fix/270-273-import-puppet-keys
Puppet pin keys show in the Timeline; imports show an Importing card (#273, #270)
2026-10-08 20:44:26 +07:00
bflatasticandClaude Opus 5.5 3318cb0da1 Import: dropped and picked files show an Importing card (#270)
Files dropped on the window or picked in the Import dialog were probed
one after the other on the UI thread: the app froze while a video was
read, nothing said what was being imported or how many files were
left, and only the last new item was selected. People ended up
importing the same file again and again.

file.import takes `background`: the files are probed in a background
job (Window > Progress, jobs.list / jobs.cancel) that names the file
being read and how many there are ("b.png (2 of 3)"); then they are
added in one undo step, all selected (the Project panel scrolls to
them, a file already in the project arrives again as in After
Effects) and a toast reports the count ("Imported 3 items", with the
first error). Drops and the Import dialog use it, and an Importing
card at the bottom left shows the job with its progress bar and
Cancel. Agents and scripts still import at once by default. The
toast registers an automation id.

Test: background_imports_report_progress_and_select_what_arrived
(engine), dropped_files_show_import_progress_then_the_items_they_made
(ui_drag_drop).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:05:07 +07:00
bflatasticandClaude Opus 5.5 285297ed93 Timeline: Puppet pin keyframes show in the Timeline (#273)
Moving a puppet pin keys its Position, but the Timeline didn't show
those keys where people look for them. U revealed every pin's Position
as one more flat "Position" row under the layer, indistinguishable from
the layer's own Position and from each other, and the reveal was a
snapshot: a pin placed or moved after U (or with the layer twirled open
and Effects closed) got keys that no row showed.

As in After Effects, revealed properties inside an effect now show
under the effect and its groups (Puppet > Mesh 1 > Deform > Puppet
Pin 1 > Position), twirled open by the reveal and closable like any
group; Transform and mask properties stay as they were. Placing, moving
or recording a pin in the viewer keeps its Position in view: on a
twirled-open layer its groups open, and a U reveal takes it in. A
collapsed layer stays collapsed. The keys select, move and delete like
any others.

Test: puppet_pin_keys_show_under_their_pins_in_the_timeline,
placing_a_pin_opens_its_groups_on_a_twirled_open_layer (ui_viewer).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:05:07 +07:00
BFlat fbc4080f53 Merge pull request #277 from storytold/fix/269-271-272-ui
UI: long menus scroll, panels get scroll bars, Learn closes when leaving it (#269, #271, #272)
2026-10-08 19:15:07 +07:00
Brandon ThomasandClaude Opus 5.5 348fa2a09a Bump version to 0.5.0 (from 0.4.0) for release
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 08:13:07 -04:00
bflatasticandClaude Opus 5.5 02c75fcaf1 Workspaces: leaving Learn closes its tutorials (#272)
The Learn workspace opens the Home screen on its Learn tab, but no other workspace closed it, so
after Learn the tutorials stayed over Default, Review and the rest until a project was created or
Home was toggled. It wasn't about clicking quickly: any switch away from Learn kept them. Leaving
Learn for another workspace now closes the Home screen it opened.

Test: ui_panels leaving_the_learn_workspace_closes_its_tutorials (clicks through the workspace
tabs: the Home screen and the tutorials show only on Learn).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:40:05 +07:00
bflatasticandClaude Opus 5.5 11f3736025 Panels: every scrolling panel has a scroll bar to drag (#271)
Most panels lay out their own rows and scrolled only with the mouse wheel: Effect Controls,
Effects & Presets, the Media Browser, Ease Presets and the Timeline's layer rows had no scroll
bar, Properties and Preview a thin indicator that couldn't be dragged, and Render Queue, History,
Markers, Character, Paragraph and Info didn't scroll at all, so a panel shorter than its content
cut it off. Without a wheel or a working touchpad there was no way down. The Project panel had
the only real scroll bars, hand-rolled, and its vertical one was hit-tested under the rows, so it
only dragged where no row was.

- widgets::scroll_bar: the Project panel's bars generalised (horizontal or vertical, drag the
  thumb or press the track to move it there, automation id with "scroll/max"); the Project
  panel's two bars and the Timeline's column bar use it, drawn over the rows.
- widgets::PanelScroll: the wheel offset, its clamping and the bar for a panel's own rows, used by
  Effect Controls, Properties, Effects & Presets, Media Browser, Ease Presets, Preview, Render
  Queue, History (which still follows the current state), Markers, Character, Paragraph and Info.
  The Timeline's layer rows get a bar on the right edge.
- Effect Controls: an animated vector's values (a puppet pin's Position) ran under the keyframe
  navigator in a narrow panel. They now end before it, moving left over the name, which is cut
  short.

Test: ui_scrolling panels_taller_than_their_space_have_a_scroll_bar_to_drag (eleven panels in a
short window: a press on the track and a thumb drag scroll them),
effect_values_stay_clear_of_the_keyframe_navigator.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:40:05 +07:00
bflatasticandClaude Opus 5.5 6d2ef5a830 Menus: a long dropdown scrolls instead of running off the window (#269)
The menu bar's own menus (Edit has 30 entries) had no scroll area, only their submenus did, so
on a short screen Edit ran past the bottom of the window. The font menu was capped at 420
points and never moved up, the Preview panel's and the viewer's popup lists neither scrolled nor
moved, and the Timeline's Blending Mode and Expression Language submenus didn't scroll. egui's
scroll bars also only appeared while the pointer was over the list, so a list could only be
scrolled with a wheel or touchpad.

- widgets::popup_list: the one popup list behind popup_menu, the font menu, the Preview panel's
  dropdowns and the viewer's popups: as tall as the window allows, scrolling beyond, moved up to
  fit the window.
- widgets::menu_scroll: the same for menus and submenus (the menu bar's, Blending Mode, the
  Expression Language menu, the viewer's Select menu).
- Scroll bars always show (solid scroll style), so they can be dragged.
- A first window that doesn't fit the screen is maximized: the 1680 x 1020 default on a smaller
  screen is only shrunk to the monitor's size, so with its title bar it ran under the taskbar and
  cut menus off at the bottom of the screen even when they fitted the window.

Test: ui_scrolling a_menu_taller_than_the_window_scrolls (Edit in a 520-point window scrolls
its last entry into view and it takes the click); overflows_screen unit test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:40:05 +07:00
BFlat 52ab38fcff Merge pull request #276 from storytold/fix/262-gpu-why
Render: render.backend says why it renders on the CPU; mcp --gpu documented (#262)
2026-10-08 18:09:33 +07:00
bflatasticandClaude Opus 5.5 4eb69e7577 Render: render.backend says why it renders on the CPU; mcp --gpu documented (#262)
get_project / render.backend reported "gpuAcceleration": true with
"active": "cpu" and no reason, which read as a broken GPU. The
headless MCP server only attaches the GPU compositor when started with
--gpu, which docs/agents.md didn't mention, and a failed --gpu said
only "no usable GPU adapter" (the adapter's error went to a log no
one sees). The session now keeps the host's reason there is no GPU
compositor (accel_note): the CLI's "headless: renders on the CPU
unless started with --gpu", or the desktop app's compositor setup or
retirement error. The status reports it as `why` whenever it renders
on the CPU (or that the project's renderer is Software Only). --gpu
failures include the adapter's error (Gpu::try_headless).
docs/agents.md documents `mcp --gpu`.

Test: render_backend_says_why_it_renders_on_the_cpu (engine).
Checked with effectcraft-cli mcp and mcp --gpu.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:09:01 +07:00
BFlat 0d3b5ed286 Merge pull request #267 from storytold/feat/254-ease-presets
Keyframes: ease presets apply saved curves to keyframe pairs (#254)
2026-10-08 17:44:02 +07:00
bflatasticandClaude Opus 5.5 db5adfa62a Docs: parity.md and gaps.md G8 describe ease presets (#254)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:25:00 +07:00
bflatasticandClaude Opus 5.5 1c22615553 UI: Window > Ease Presets panel draws, edits and applies ease curves (#254)
A dockable panel (our own design) for the keys.easePreset commands. A
value graph shows the working curve with its two handles, which drag
to edit it; Apply eases the selected keyframes with it and From Keys
loads the curve between the first selected pair. Every preset is a
thumbnail drawn in code; a click loads it and applies it to the
selected keys. Save Current keeps the working curve under the name in
the field, and Rename and Delete act on the selected user preset.
Wide panels put the presets beside the graph; narrow ones below it,
the graph shrinking to leave a row of presets, scrolled with the wheel.

The panel state (working curve, selection, name) is serde in the UI
state, and every control registers an automation id (easePresets.*).
widgets::text_field is the search field without its magnifier.

Test: clicking_an_ease_preset_eases_the_selected_keys_and_handles_edit_the_curve
(ui_keyframes); every_panel_registers_automation_ids_for_its_widgets
covers the new panel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:25:00 +07:00
bflatasticandClaude Opus 5.5 0717f12246 Keyframes: ease presets apply a saved curve to keyframe pairs (#254)
keys.easePreset.apply {preset | curve} eases every pair of neighbouring
selected keyframes of each selected property in one undo step: the out
side of the earlier key and the in side of the later one. A curve is
given as {outInfluence, outSpeed, inInfluence, inSpeed} or as handles
[x1, y1, x2, y2]. keys.easePreset.capture reads the curve between the
first selected pair; keys.easePreset.save {name, curve?} keeps it (or a
given curve), and list, rename and delete manage the user presets.

Twelve built-in curves are our own numbers (Linear, Smooth, Ease In-Out
Soft / plain / Strong, Accelerate and Decelerate with Strong variants,
Expo In-Out / Accelerate / Decelerate) and can't be renamed, deleted or
overwritten. User presets live in the config store (ease_presets.json),
loaded with the other settings. A file that isn't JSON gives no user
presets, and entries that don't parse, have no usable name or repeat a
name are skipped and logged. A failed write leaves the presets as they
were.

Test: a_preset_eases_every_selected_pair_in_one_undo_step,
a_curve_scales_to_each_dimension_and_follows_motion_paths,
apply_needs_neighbouring_keys_and_a_known_curve,
user_presets_save_rename_delete_and_persist,
a_corrupt_presets_file_loads_what_it_can (effectcraft-engine).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:25:00 +07:00
bflatasticandClaude Opus 5.5 cc594a7385 Keyframes: ease curves measure and apply the ease of a keyframe pair (#254)
An EaseCurve is the temporal ease of one segment independent of its
duration and values: the out side of the first key and the in side of
the second, as influence (%) and speed relative to the segment's
average speed (1 = linear, 0 = at rest). Its handles in the normalised
value graph are cubic-bezier points. apply() eases a pair of keys per
dimension, along the motion path for spatial values and as overall
progress for paths and gradients, the way Easy Ease shapes them; an
Auto Bezier key keeps its other side as it plays. measure() reads the
curve a segment plays back. Non-finite numbers are rejected and
influences and speeds clamped.

Test: handles_round_trip_and_reject_non_finite_numbers,
applied_curve_scales_to_each_dimension_and_measures_back,
spatial_curve_follows_the_motion_path,
linear_and_auto_bezier_sides_measure_as_they_play (effectcraft-keyframe).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:25:00 +07:00
BFlat 6fa047c2ee Merge pull request #265 from storytold/fix/253-snapping
Snapping: layers, scale handles and the work area snap as in After Effects (#253, #252)
2026-10-08 17:24:10 +07:00
bflatasticandClaude Opus 5.5 25e5d0f9c0 Viewer: wrap the snap() doc comment (#253)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:14:18 +07:00
bflatasticandClaude Opus 5.5 631c069557 Viewer: scale handles snap to the comp's and other layers' edges (#252)
Scaling a layer by a bounding-box handle didn't snap, so it couldn't
be scaled exactly to the comp. With snapping on (Cmd/Ctrl toggles it
during the drag), the dragged handle now snaps like a dragged layer's
feature: to the comp's edges, corners and centre and to other layers'
edges, corners, centres and anchor points, as the Snapping options
allow. A corner snaps both axes, an edge its own axis; with Shift the
corner slides along its line from the anchor point to the nearer
snapped edge, so the proportions stay and the layer lands on, say, the
comp's size.

Test: handle_drags_snap_to_the_comp_edges.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:14:05 +07:00
bflatasticandClaude Opus 5.5 0ac5f9fb9b Timeline: Shift-dragging the work area snaps to the current time (#252)
Shift-dragging the work area's begin or end handle now snaps it to the
current-time indicator, keyframes, layer in and out points and markers
within 8 px, with the snap candidates the CTI and keyframe drags use.
The work area bar itself drags too, as in After Effects: it moves the
work area and keeps its length, and with Shift the nearer end snaps.

Test: shift_dragging_the_work_area_snaps_to_the_current_time.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:14:05 +07:00
bflatasticandClaude Opus 5.5 41be200749 Viewer: Snapping options pick which layer features snap (#253)
Dragging a layer already snapped its feature nearest the pointer to
other layers' edges, corners, centres, anchor points and path points
and to the comp's edges and centre, and Cmd/Ctrl held during the drag
already turned snapping on or off against the checkbox. What was
missing were After Effects' Snapping options beside the Tools bar's
checkbox. The new menu turns Snap Edges Extended off (a layer's edges
then snap only along the layer, not across the comp) and each feature
family (edges, corners, centres, anchor points, mask and shape path
points) on or off, for the dragged layer's snap features and for the
targets alike. Agents set them with view.snappingOptions.

Layers whose eye is off (or that a solo hides) are no longer snap
targets, mask and shape path points of the dragged layer snap too, and
while snapping is on the feature that snaps is boxed during the drag.

Test: layer_drag_snaps_to_other_layers_edges_ctrl_toggles_and_options_apply
(UI), snapping_options_pick_the_targets and
snapping_options_command_sets_and_toggles (engine).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:14:05 +07:00
BFlat c486130a9b Merge pull request #255 from storytold/fix/252-key-selection-props
Timeline: a selected key selects its property and layer (#252)
2026-10-08 16:20:15 +07:00
bflatasticandClaude Opus 5.5 8d5e4ae0ef Timeline: a selected key selects its property and layer (#252)
Clicking (or marquee-selecting) keyframes in the Timeline selected only
the keys. Their property and layer stayed unselected, so the Graph
Editor, which shows the selected properties or else the animated
properties of the selected layers, came up empty until the property
was selected separately. As in After Effects, the Timeline's key
selection now selects the keys' properties and layers too (Shift adds
to them). keys.select takes this as `selectProperties`; the Graph
Editor's own key clicks leave what it shows alone.

Test: a_key_clicked_in_the_timeline_shows_in_the_graph_editor
(ui_keyframes).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 16:19:53 +07:00
BFlat 72a2d47131 Merge pull request #249 from storytold/docs/248-py-aep-g4
Docs: gaps.md G4 lists py-aep as a candidate for .aep import (#248)
2026-10-08 14:40:58 +07:00
bflatasticandClaude Opus 5.5 9fc4407a37 Docs: gaps.md G4 lists py-aep as a candidate for .aep import (#248)
A user suggested py-aep (MIT, Python), which reads and writes .aep
files. G4 now records it for the clean-room decision, with the two
ways it could be used: as an external converter, or ported, which
means reading another implementation of the format.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 14:40:30 +07:00
BFlat 55defcb167 Merge pull request #247 from storytold/fix/243-gl-fallback
App: a launch whose window never drew makes the next use OpenGL (#243)
2026-10-08 13:59:08 +07:00
bflatasticandClaude Opus 5.5 d554724aa4 App: a launch whose window never drew makes the next use OpenGL (#243)
On some older Intel integrated GPUs (UHD 600, UHD with 2020 drivers)
the graphics driver crashes EffectCraft as its window's DirectX 12 or
Vulkan device is created (exit code 0xC0000005): the process
disappears with no message, and only WGPU_BACKEND=gl helped. A driver
crash can't be caught, so each launch now leaves a marker in the
settings folder until its window draws. A launch that finds the
previous one's marker switches the new Settings ▸ Startup & Repair ▸
Window Graphics (Automatic / OpenGL) to OpenGL and says so in the
status bar. A window that fails to open says the next launch will try
OpenGL. WGPU_BACKEND still wins; macOS has no OpenGL backend, so it
never switches there.

Tests: a_launch_that_never_drew_switches_the_next_to_opengl (app),
window_graphics_is_validated (engine). Checked end to end with a
leftover marker: the app opened on OpenGL with the notice and cleared
the marker.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:57:21 +07:00
bflatasticandClaude Opus 5.5 fdb655e78e GPU: the compositor declines OpenGL adapters at once (#243)
On OpenGL the compositor translated its compute kernels for tens of
seconds (22 s on an RTX 4090) before one failed validation and it fell
back to CPU compositing, all before the window's first frame. It now
declines GL adapters up front, as it declines adapters without compute
shaders. OpenGL is what Window Graphics falls back to for drivers that
crash with DirectX 12 or Vulkan.

Test: opengl_adapters_get_cpu_compositing_at_once (gpu tests_init;
skips without an OpenGL adapter).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:57:08 +07:00
BFlat 4d5765f158 Merge pull request #233 from ulanch/fix-variable-font-axis-test
Fix variable-font animator test axis selection and endpoint delta
2026-10-08 13:38:03 +07:00
BFlat 06aa138c94 Merge pull request #245 from storytold/fix/232-golden-libm-variant
Effects: the sim golden test accepts macOS 27's libm hash for Mr. Mercury (#232)
2026-10-08 13:36:30 +07:00
bflatasticandClaude Opus 5.5 e61f0e5560 Effects: the sim golden test accepts macOS 27's libm hash for Mr. Mercury (#232)
On macOS 27.2 (rustc 1.98) one of the 404 pinned simulation hashes,
CC Mr. Mercury at non-default settings, 32 bpc, t = 2.2 s, came out
different. The reporter rendered every case at the commit that pinned
the hashes and at current main on that machine, and the frames were
byte-identical. So the code didn't change: the platform's libm did
(the hashes are exact f32 bits through sin, cos and powf). The test
now also accepts listed per-case libm variants, with that hash as the
first, and every other case stays exact. Its docs say how to tell a
libm difference from a code change.

Test: libm_variants_are_accepted_for_their_case_only (sim_golden; runs
on every platform).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:36:09 +07:00
BFlat f4319c1917 Merge pull request #244 from storytold/fix/234-startup-logging
Logging: RUST_LOG prints to stderr, and start-up logs its milestones (#234)
2026-10-08 13:32:44 +07:00
bflatasticandClaude Opus 5.5 468ed0b297 Logging: RUST_LOG prints to stderr, and start-up logs its milestones (#234)
The logger kept warnings in memory and wrote to a file only after Help
▸ Enable Logging, and it ignored RUST_LOG. An app that never opens its
window (macOS 27 on Apple Silicon, #234) therefore left no trace. Now
RUST_LOG (a level, or env_logger-style `target=level` directives)
also prints what it lets through to stderr, including eframe, winit
and wgpu. The desktop app logs its start-up milestones: opening the
window, the graphics adapter, the app created, the first frame drawn,
the window being asked to close, and the window closed.

Test: rust_log_filters_by_level_and_target (engine logging). Checked
by running the app with RUST_LOG=info and quitting it over the control
channel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:32:16 +07:00
BFlat 5a7119d5fa Merge pull request #242 from storytold/docs/gaps-reports-8-oct
Docs: gaps.md records the user reports of 8 October
2026-10-08 13:11:12 +07:00
bflatasticandClaude Opus 5.5 6a8d339316 Docs: gaps.md records the user reports of 8 October
The reports of 8 October (#225-#227, #229-#231), their fixes (#235,
#237-#241), what remains of #227 (multi-monitor support, the Project
panel's empty-area menu, effect points on shape and text layers) and
the regression tests for each.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:10:57 +07:00
BFlat 49af6472bc Merge pull request #240 from storytold/fix/227-shape-contents
Shapes: draw into, copy, paste and duplicate shape contents (#227)
2026-10-08 13:09:50 +07:00
bflatasticandClaude Opus 5.5 25c01aa5a0 Docs: gaps.md lists the regression tests for the #227 shape fixes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:03:46 +07:00