Commit Graph

832 Commits

Author SHA1 Message Date
Brandon Thomas 6943872cf6 Bump version to 0.6.0 (from 0.5.0) for release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
2026-10-08 21:00:55 +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
Brandon Thomas 0f2814de8c
README Downloads: where to get it first, then builds with Windows before macOS (#291)
Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:50:59 -04:00
Brandon Thomas 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 <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:48:00 -04:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 20:45:07 +07:00
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
2026-10-08 20:05:07 +07:00
bflatastic 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 <noreply@anthropic.com>
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 Thomas 348fa2a09a Bump version to 0.5.0 (from 0.4.0) for release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:13:07 -04:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 18:40:05 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 18:40:05 +07:00
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic db5adfa62a Docs: parity.md and gaps.md G8 describe ease presets (#254)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:25:00 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 17:25:00 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 17:25:00 +07:00
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 25e5d0f9c0 Viewer: wrap the snap() doc comment (#253)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:14:18 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 17:14:05 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-08 17:14:05 +07:00
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
2026-10-08 13:57:21 +07:00
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
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
bflatastic 25c01aa5a0 Docs: gaps.md lists the regression tests for the #227 shape fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:03:46 +07:00
bflatastic 89e075d1b1 Shapes: Tools bar Tool Creates Shape / Mask and Fill / Stroke Options (#227)
The Tools bar had Fill and Stroke swatches and a Stroke Width, kept in
the interface only, so agents couldn't set them and new shapes could
only be a solid fill with a solid stroke. As in After Effects:

- With a shape layer selected and a shape tool or the Pen active, Tool
  Creates Shape / Tool Creates Mask choose whether drawing adds shapes
  to the layer or masks on it (Fill and Stroke hide while drawing
  masks).
- Clicking the word "Fill" or "Stroke" opens Fill Options / Stroke
  Options: None, Solid Color, Linear Gradient or Radial Gradient, the
  blend mode and the opacity. The swatches show the kind; Alt-click
  steps through the kinds.
- New shapes and Pen paths are painted with them: a Gradient Fill or
  Gradient Stroke across the shape for the gradient kinds.

The options are engine state (EditorState.shape_tool) set with the new
shape.toolOptions command; shape.newShape and shape.newPath use them
unless given paint parameters (which now include fillType, fillBlend,
fillOpacity and their stroke twins; layer.newShape takes them too). A
Pen path without paint parameters now takes the Tools bar's stroke
(none by default) instead of a fixed 2 px white one. layer.addMask also
draws rounded rectangle, polygon and star masks, so every shape tool
makes a mask on a selected footage or solid layer (rounded rectangles,
polygons and stars made a new shape layer).

Test: shape_tool_options_paint_new_shapes,
masks_of_every_shape_tool_kind,
tool_creates_mask_draws_a_mask_on_the_selected_shape_layer,
fill_options_paint_the_next_shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:03:38 +07:00
bflatastic 0048569682 Shapes: Duplicate duplicates the selected shape items, not the layer (#227)
Edit > Duplicate (Ctrl+D) with a shape group or other shape item
selected in the Timeline duplicated the whole shape layer. As in After
Effects it now duplicates the selected items inside their group, each
copy above its original and numbered on ("Rectangle 1" gives
"Rectangle 2"), and selects the copies. With only layers selected the
layers are duplicated as before.

Test: duplicate_with_shape_items_selected_duplicates_them_in_place,
ctrl_d_duplicates_the_selected_shape_group_in_its_layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:03:38 +07:00
bflatastic 95bb346c89 Shapes: Copy, Cut and Paste shape items between shape layers (#227)
Edit > Copy with a shape group (or a path, fill, stroke or path
operation) selected in a shape layer's Contents copied the whole layer,
and Paste made a new layer. As in After Effects, Copy now copies the
selected items, Paste puts them into each selected shape layer's
Contents (above its selected item, in that item's group, else on top,
with names kept unique there: "Rectangle 2"), and Cut removes them, so
Cut + Paste moves them. Pasting with no shape layer selected is an
error rather than a new layer. Copying anything else clears the shape
item clipboard, as it does the other clipboards.

Test: shape_contents_copy_cut_and_paste_between_shape_layers,
ctrl_c_and_ctrl_v_copy_a_shape_group_into_another_shape_layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:03:38 +07:00
bflatastic 9968950b71 Shapes: drawing with a shape tool adds to the selected shape layer (#227)
With a shape layer selected, the Rectangle, Rounded Rectangle, Ellipse,
Polygon and Star tools made a new shape layer for every shape. After
Effects draws into the selected shape layer instead: the shape becomes a
new group on top of its Contents ("Rectangle 2", ...), placed by the
group's Transform. The new shape.newShape command does that (in the
layer's space, so a moved or scaled layer still gets the shape where it
was drawn), and makes a new shape layer only when no unlocked shape
layer is selected. The viewer's shape tools use it; masks on other
selected layers are unchanged. layer.newShape and the Pen's
shape.newPath share its layer and Contents helpers.

Test: shape_tools_draw_into_the_selected_shape_layer,
shape_tool_draws_into_the_selected_shape_layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:03:38 +07:00
BFlat 7e4a7cd863
Merge pull request #238 from storytold/fix/227-interface
Spacebar hand tool, Effect Controls menu, New Comp drop, switch drags (#227)
2026-10-08 13:02:49 +07:00
bflatastic 764eee409b Gaps: regression evidence for the #227 interface fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:59:40 +07:00