Commit Graph
1248 Commits
Author SHA1 Message Date
Claude faffad3a6e Media: a multi-layer EXR's picture and its EXtractoR / Cryptomatte channels share one decode (#474)
A multi-layer OpenEXR file without an unnamed RGB layer (Blender's compositor File Output)
was decoded twice per frame: `layered_image` decoded every channel to find the picture,
then `MediaPool::aux` decoded the same bytes again into the aux-channel cache for
EXtractoR and Cryptomatte. The first EXtractoR layer doubled the frame time.

The footage path now reads the picture from the aux-channel cache (`cached_layered_image`),
so the decode it makes is the one EXtractoR and Cryptomatte reuse. (Step 1 of the issue;
reading only the picture's channels and keeping half channels as f16 are still to do.)

Test: after a frame of a layered file is decoded, its aux channels come from the cache
without reading the file again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 20:40:50 +00:00
BFlat f8b2552d1c Merge pull request #645 from storytold/claude/community-pr-review-lwtrlr
Fix 28 community-reported issues (crash, security, AE parity)
2026-10-11 03:25:25 +07:00
Claude 5e2c7560d6 Import: only dropped files default to stills; scripts and agents naming a frame still get its run (#431)
The UI behaves as in After Effects (the Import dialog's Sequence checkbox
starts off, dropped files are stills, dropped folders make sequences), but
file.import without sequence or drag keeps importing a numbered still's
run, so existing scripts and agents keep working.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 20:15:24 +00:00
Claude 8b42f970b6 Merge branch 'fix/f4': community issue fixes 2026-10-10 20:00:37 +00:00
BFlat fddca8c6d9 Merge pull request #643 from storytold/claude/community-pr-review-lwtrlr
Land community PRs, batch 3, with follow-ups
2026-10-11 02:55:09 +07:00
Claude 8d32d67f0e Color: Project Settings ▸ Working Gamma 2.2 / 2.4 with no working space, as in After Effects (#495)
After Effects' `app.project.workingGamma` (2.2 or 2.4, ignored with a working space) sets
the curve an unmanaged project uses: at 2.2 the piecewise sRGB curve, at 2.4 a pure 2.4
power curve. EffectCraft always used sRGB, so linear EXRs (Interpret As Linear Light) from
a 2.4 project came out darker in the shadows (0.18 → 0.461 instead of 0.490).

- `ProjectSettings::working_gamma` (default 2.2, serde default for old projects).
- The colour pipe's unmanaged curve is the Working Gamma's (sRGB, or Rec. 709's pure 2.4
  curve): Interpret As Linear Light encodes untagged footage with it and Blend Colors Using
  1.0 Gamma linearizes with it; it is part of the pipe's cache key. The GPU path takes the
  same conversions.
- `file.projectSettings {workingGamma: 2.2|2.4}` (other values are refused, as in AE),
  `app.project.workingGamma` in scripts and a Working gamma choice in Project Settings.

Test: render test with linear 0.051 / 0.18 / 0.5 footage (0.290 / 0.490 / 0.749 at 2.4,
0.250 / 0.461 / 0.735 at 2.2, the values measured in AE), linear blending at 2.4, a working
space ignoring the setting, and the cache key.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:54:17 +00:00
Claude a220b63bfb Help: the System Compatibility Report says why there is no GPU and names the window's adapter (#596)
With no GPU compositor the report only said "GPU: none", though the host had recorded the
reason (`Session::accel_note`: the compositor's setup error, a retired GPU preview, a
headless start) and the window's wgpu adapter was known. Users with a capable GPU (#596, an
RTX 4070) couldn't tell why it wasn't used.

The report now carries `gpuNote` (the reason, when there is no compositor) and
`windowGraphics` (the adapter, backend, device type and driver the window draws with, which
the desktop app records when it sets up the GPU), and the text shows
"GPU: none (CPU compositing): <reason>" and "Window graphics: …"; the issue line names the
reason too.

Test: the compatibility report test sets a reason and an adapter and checks the JSON and text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:54:17 +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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit aa3c31db9ab3a774ee2f8ba867e1bafc1fce7aba)
2026-10-10 19:48:25 +00:00
Claude 86d47dc3fe Snapping and Align: shape layers meet at their visual edges, with no gap (#352)
Cause: render::content_bounds (used by snapping's targets and source, Align,
Distribute, the viewer's bounding box and handles) took shape layers' bounds
from draw_bounds, the rasteriser's safety rectangle: 1 px of anti-aliasing
padding around fills and width x miter limit / 2 + 1 around strokes. Two
squares snapped edge to edge therefore stayed 2 px apart, or much more with
strokes.

Fix: shapes::content_bounds uses visual bounds, as After Effects' layer bounds
do: the path bounds plus half the stroke width (through the draw's group
scale), with no anti-aliasing or miter padding. Rendering keeps draw_bounds.

Tests: render shape_content_bounds_are_the_visual_edges (a 100 x 100
rectangle is exactly +-50, +-55 with a 10 px stroke); engine
shape_layers_align_by_their_visual_edges (widths 100 / 110, Align Left puts
both edges exactly at 0).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit bfd0abd7437ad1d38652e3aef51f9ba5dea9100b)
2026-10-10 19:48:25 +00:00
Claude 9b7f8cf005 Effects: Write-on draws a stroke along the animated brush, honouring Stroke Length and Brush Spacing (#536)
Cause: write_on_plan drew a single dab at the current Brush Position. Stroke
Length, Brush Spacing and the Paint/Brush Time Properties were declared but
unused, so an animated brush showed a moving dot that dropped every earlier
mark.

Fix: as After Effects describes Write-on, a dab is laid every Brush Spacing
seconds from the layer's start up to the current time, each lasting Stroke
Length seconds (0: for ever). Their positions come from the host's parameters
at their times (EffectHost::params_at, which the renderer implements). Paint
Time Properties > Opacity and Brush Time Properties > Size (or Size &
Hardness) keep each dab's own opacity / size; otherwise the current values
apply to all. One frame draws at most 4096 dabs (longer strokes space them
out and join them). Without a host the current dab draws, as before. Colour
and hardness stay one per stroke (the paint plan has one colour and hardness),
so Paint Time Properties > Color and per-dab hardness aren't modelled. The GPU
path uses the same plan.

Test: write_on_accumulates_a_stroke_along_the_animated_brush (the report's
fixture: start and midpoint marks stay at 0.5 s with the end still clear, all
marks at 1 s, Stroke Length 0.25 keeps only the recent trail, Brush Spacing
0.5 gives three separate marks, per-dab opacity).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit c82c9aeb685eab0cb36ca69c0b337781f4bad258)
2026-10-10 19:48:25 +00:00
Claude 77dfd44658 Puppet: a pin added to a bent mesh holds it where it is; nothing jumps (#318)
Cause: puppet::solve binds each moving pin to its nearest mesh vertex and fixes
that vertex at position + (vertex - rest), an offset in rest space with no
rotation or scale. puppet.addPin set a new pin's position to the click, so in
a bent region the bound vertex was pulled to where it would be unrotated, and
the mesh jumped as soon as the pin was added.

Fix: addPin picks the position that keeps the bound vertex at its current
deformed place: the vertex's deformed position minus the rest offset. On an
unbent mesh that is the click itself (round-off is snapped back to it), so
existing pins behave as before.

Test: a_pin_added_to_a_bent_mesh_moves_nothing (two pins, one moved to bend
the layer, then pins added at three places on the bent part: at most a few
edge pixels change from solver round-off; before the fix about 250 did).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit 16151203b304d565b425873fdd33ae082cae483b)
2026-10-10 19:48:25 +00:00
Claude 7e0e641606 Edit: Copy with a property selected copies that property, and Paste sets it on the selected layers (#493)
Cause: edit.copy handled text editing, selected keyframes, effects and shape
items, then fell back to copying the selected layers. Selecting a property
selects its keys, so an animated property copied, but an unanimated one (or
the Transform group, whose keys prop.select doesn't select) copied the whole
layer, and Paste added a duplicate layer instead of setting the property.

Fix: with properties (or the Transform group) selected, Edit > Copy copies, as
After Effects does, each animated property's keys (the selected ones, else all
of them, through keys.copy) and each unanimated property's value into a new
value clipboard keyed by match path. Edit > Paste sets those values on the
same properties of the selected layers (a key at the CTI where the target is
animated) and pastes the keys at the CTI, as one undo step (keys.paste now
honours `merge`). Keys alone still answer with their count, as before.

Test: copy_a_selected_property_pastes_onto_the_same_property (static
Position, then the Transform group with animated Opacity: values and keys
land on layer B, no layer is added, one undo step each).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit 8267519ebe20e889196e538de23b85b04a1fa76a)
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 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:32:18 +00:00
Claude 485e4bb59d Interface: Hangul is probed after the other scripts, so a catch-all face never draws Arabic (#616 follow-up)
Without a Korean font the Hangul lookup can land on Unifont, which also
covers Arabic but can't join it; registered first, it took Arabic too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:17:26 +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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:36 +00:00
Claude 29d9ba5fbb i18n: the Home screen's buttons, pages, tiles, columns and empty states go through the interface catalog (#574)
panels/home.rs was in the converted list, but most of its text was drawn
straight from English literals (New Project, Open Project…, the page
tabs, the quick-start tiles, the filter placeholder, the column
headings, the empty states, the Opened/Kind cells and the version line),
so a Chinese interface still showed them in English. They now go through
tr()/tr_args(), with rows in both Chinese catalogs (Traditional's Join
the ArtCraft Discord row was also still English). Automation labels stay
English. The Opened column's plurals are separate rows so each language
words them itself.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:28 +00:00
Claude a207b19b12 #336: Ctrl+\ / Cmd+\ maximizes or restores the application window
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:07 +00:00
Claude dbbc0e2662 #577: font previews are scaled and moved to fit inside their row
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:03 +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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 19:16:03 +00:00
Claude 7210b37bb2 Format effect.list's parameter metadata (#631 follow-up)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:51:45 +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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:51:45 +00:00
ClaudeandNigel Ihza Firdausy 72b2e9774e Dock: test the empty-dock reopen with the reporter's Minimal workspace (#629 follow-up, test from #630)
Co-Authored-By: Nigel Ihza Firdausy <[email protected]>

Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 c742145b28 Merge pull request #631 from Ni-zav/fix/effect-parameter-catalog-s20261010T163100Z-6b8a 2026-10-10 18:51:15 +00:00
Claude 1c086fb134 Merge pull request #629 from MarekSpodBiedry/issue-529 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
Claude 8a65313226 Merge pull request #627 from MarekSpodBiedry/issue-446 2026-10-10 18:51:15 +00:00
Claude 3f840ab37f Merge pull request #626 from MarekSpodBiedry/issue-467 2026-10-10 18:51:14 +00:00
Claude da06c42de1 Media: OpenEXR files with HTJ2K compression say it isn't supported (#482)
The exr crate reads HTJ2K (OpenEXR 3.4's High-Throughput JPEG 2000) headers
but can't decompress the pixels, and the image paths turned that into a
misleading "no non-deep rgb channels" (multi-layer files) or imported the
file and showed a transparent frame (single-layer files).

`exr_channels::unsupported_compression` reads the headers; import (probe)
and frame decoding fail with "<file>: OpenEXR HTJ2K compression isn't
supported yet: re-save the file with ZIP, PIZ or DWAA compression".
Decoding HTJ2K itself needs a pure-Rust JPEG 2000 Part 15 decoder and is
left for later.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:51:12 +00:00
Claude 34f2955585 Media: an audio file that fails to decode no longer stays silent for good (#324)
With the Conformed Audio folder on (the default), the conform thread wrote
whatever `decode_audio` returned, and that returned silence when the source
couldn't be opened or a read failed. The conformed file is found by the
source's path, size and date, so one failed decode (an MP3 the decoder
stumbled on once) kept the footage silent in previews and exports in every
later session, while a renamed copy played.

The conform path now decodes with `try_decode_audio`, which fails instead of
returning silence; a failed conform removes its temporary file, writes
nothing and isn't retried this session (reads go to the decoder). Playback
reads still fall back to silence. CONFORM_VERSION goes to 3 so the silent
files earlier builds wrote are ignored and conformed again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:51:07 +00:00
Claude 605b7c3a25 Scripting: scripts can't grant themselves file and network access (#590)
A running script reached `prefs.set` through `app.run` and could turn on
Settings > Scripting & Expressions > Allow Scripts to Write Files and Access
Network (or turn off Warn User When Executing Files), which the File, Socket
and system.callSystem gates then read: the permission the user withheld was
the script's to take.

Commands a script issues now go through `Session::execute_for_script`, which
marks the session as running for a script for the command and everything it
runs (engine.batch, ScriptUI callbacks, scheduled tasks). While it is,
`prefs.set` refuses any change that would leave the Application Scripting
settings different (compared on the resulting settings, so dotted keys,
whole-page objects and batches are all covered), and a script's
`prefs.reset` keeps them as the user set them. Command scripts read from a
file (File > Scripts > Run Script File... with .json/.jsonl) count as
scripts too. The UI, CLI, control channel and MCP still change them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:50:57 +00:00
Claude 58e91fafbf Viewer: the locked viewer shares the main viewer's display key (#619 follow-up)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:50:32 +00:00
Claude ff56c5ad24 Audio: the short preview buffer is Linux-only and within the device's range (#614 follow-up)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:50:32 +00:00
Claude 4de11be339 Time: in Frames style a typed number is a frame count (#610 follow-up)
parse_timecode reads bare digits as timecode pairs, so 00100 went to
frame 30 (1 s) instead of frame 100.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 18:50:32 +00:00
Claude a3cc4d04b0 Merge pull request #614 from vbasky/fix/554-linux-audio-preview 2026-10-10 18:49:52 +00:00