Commit Graph

595 Commits

Author SHA1 Message Date
bflatastic b21e04b301 M4.12: never crash: hostile input in New Comp from Selection, an empty work area in Cache Frames When Idle
Following AGENTS.md's Never crash rules for the M3 / M4 additions:
- Cache Frames When Idle panicked (`clamp` with min > max) when the work area was inverted
  (start set past the end); an empty work area now has nothing to cache.
- `file.newCompFromSelection`: the sequence overlap times the layer count overflowed for a huge
  `overlapDuration` (wrapping to a wrong comp length without overflow checks); negative or
  non-finite values fall back to 1 s and the product saturates. The lead item is read with
  `get()` instead of indexing.
- Layer Settings' enablement reads the first selected layer with `first()`.
- Regression tests for both (each failed before the fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 06:30:46 +07:00
Brandon Thomas 339a05d771
Merge pull request #37 from storytold/pr/never-crash-clean-crates
Never crash: deny panic lints in the already-clean crates and apps
2026-10-04 15:34:52 -04:00
Brandon Thomas 7f1e2df862
Merge pull request #24 from storytold/pr/never-crash-opus-vp9
Never crash: Opus and VP9 encoders without expect()
2026-10-04 15:34:33 -04:00
Brandon Thomas 61e859e3de
Merge pull request #23 from storytold/pr/never-crash-hevcenc
Never crash: HEVC encoder without expect()/unreachable!()
2026-10-04 15:34:15 -04:00
Brandon Thomas 6aa490b826
Merge pull request #22 from storytold/pr/never-crash-av1enc
Never crash: AV1 encoder without expect()/unreachable!()
2026-10-04 15:33:56 -04:00
Brandon Thomas c6b8f5412f
Merge pull request #36 from storytold/pr/never-crash-ui
Never crash: frame-pool panic handler and ui-egui without expect()
2026-10-04 15:33:38 -04:00
Brandon Thomas 4926dd2c4c
Merge pull request #35 from storytold/pr/never-crash-render-gpu
Never crash: render and gpu crates without panic!()
2026-10-04 15:33:18 -04:00
Brandon Thomas fc0eee53dc
Merge pull request #31 from storytold/pr/never-crash-raster-path
Never crash: raster and path crates without expect()/unwrap()/unreachable!()
2026-10-04 15:32:58 -04:00
Brandon Thomas 5d716c2f5a
Merge pull request #29 from storytold/pr/never-crash-text
Never crash: text layout and font resolution without expect()
2026-10-04 15:32:39 -04:00
Brandon Thomas f82ac49855
Merge pull request #26 from storytold/pr/never-crash-track
Never crash: trackers and camera solver without expect()
2026-10-04 15:32:18 -04:00
Brandon Thomas 64531e4aac
Merge pull request #33 from storytold/pr/never-crash-effects
Never crash: effects crate without expect()
2026-10-04 15:32:00 -04:00
Brandon Thomas c6488d0cd4
Merge pull request #30 from storytold/pr/never-crash-engine
Never crash: engine without expect() (job polling, commands, menus)
2026-10-04 15:31:40 -04:00
Brandon Thomas f864b29549
Merge pull request #27 from storytold/pr/never-crash-script
Never crash: scripts nested too deeply return an error instead of overflowing the stack
2026-10-04 15:31:21 -04:00
Brandon Thomas 46cd757f99
Merge pull request #21 from storytold/pr/never-crash-expr
Never crash: reject deeply nested expressions instead of overflowing the stack
2026-10-04 15:31:01 -04:00
Brandon Thomas a3976913c2
Merge pull request #34 from storytold/pr/never-crash-time
Never crash: Tick and timecode arithmetic can't overflow or divide by zero
2026-10-04 15:30:42 -04:00
Brandon Thomas 6f52a0aab8
Merge pull request #19 from storytold/pr/never-crash-project
Never crash: project crate without expect()
2026-10-04 15:30:22 -04:00
Brandon Thomas 010cb8129f
Merge pull request #17 from storytold/pr/never-crash-media
Never crash: media crate without expect()
2026-10-04 15:29:59 -04:00
Brandon Thomas 43c485e31e
Merge pull request #14 from storytold/pr/never-crash-interchange
Never crash: interchange import/export (bad precomp refs, deep XML) without panics
2026-10-04 15:29:41 -04:00
Brandon Thomas d350f4e7ac
Merge pull request #25 from storytold/pr/never-crash-lottie
Never crash: Lottie enum values can't overflow
2026-10-04 15:29:22 -04:00
Brandon Thomas a792844d7e
Merge pull request #16 from storytold/pr/never-crash-pdf
Never crash: PDF/EPS close-path, CFF outlines and PostScript radix numbers
2026-10-04 15:29:03 -04:00
Brandon Thomas e171c33686
Merge pull request #15 from storytold/pr/never-crash-svg
Never crash: SVG path data loop and deep nesting
2026-10-04 15:28:44 -04:00
Brandon Thomas 91a3b14cb2
Merge pull request #18 from storytold/pr/never-crash-psd
Never crash: PSD reader bounds overflow and input-sized allocations
2026-10-04 15:28:25 -04:00
Brandon Thomas a69f417d63
Merge pull request #28 from storytold/pr/never-crash-guard
Never crash: last-resort guard around command execution, and a logging panic hook
2026-10-04 15:28:05 -04:00
bflatastic 995d99f938 M4.11: one motion blur gate for layers, masks, effects and Advanced 3D
`Renderer::mb_on` (render options × the comp's switch × the layer's switch as the precomp
layers above limit it) now decides everywhere:
- Mask motion blur checked only the comp's switch and the layer's own switch, so Render
  Settings ▸ Motion Blur ▸ Off for All Layers and a precomp layer with motion blur off still
  blurred masks; it now takes the gate (`masks::MaskBlur`) and the comp's Samples Per Frame
  (four in Draft) instead of a fixed eight.
- Effects that read the shutter (`EffectEnv::shutter`) get it only when the layer is motion
  blurred in this render.
- Advanced 3D layers follow the precomp layers' Motion Blur switches too.
- The layer cache key of a layer with masks or effects includes whether it is blurred and the
  comp's shutter and samples, so a buffer blurred for the viewer is never reused by a render
  with motion blur off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:55:34 +07:00
Brandon Thomas c5e33f1a02 Never crash: deny panic lints in the already-clean crates and apps
These crates have no production panic sites: add the deny attribute so they stay clean
(geom, color, model, automation, host, export, plugin, and the desktop, CLI and web apps). The
plugin's wasm-only test module gets a separate cfg(test) attribute so clippy recognises it as
test code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:52:39 -04:00
Brandon Thomas 543942d085 Never crash: frame-pool panic handler and ui-egui without expect()
ui-egui without expect()/unreachable!(): the frame pool is optional (rayon's global pool if the
OS refuses threads) and gets a panic_handler, because a panic in a rayon spawn() job aborts the
whole process by default; render-queue progress, the text-edit anchor and the GPU viewer
texture are bound where they're checked; the settings panel's Bool arm no longer panics.
Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:52:28 -04:00
bflatastic 0b5cff1743 M4.10: a nested comp shows nothing before it starts or after it ends
A precomp layer trimmed out (or time-remapped) past its nested comp's end kept drawing the
nested layers whose bars ran on. Like After Effects, the nested comp now shows nothing outside
its own span (`Comp::covers`), flattened, through a proxy or with Collapse Transformations, on
the CPU and the GPU (both go through `Renderer::source` / `collapse_into`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:47:54 +07:00
bflatastic 0fe6db00e8 M4.9: Preserve frame rate when nested or in render queue, Preserve resolution when nested
Composition Settings ▸ Advanced has both options (they were missing from the model, the
renderer and the dialog):
- Preserve frame rate: nested in another comp, the comp shows only its own frames
  (`EvalCtx::nested_time` floors the precomp layer's source time to the nested comp's frame
  rate; used for the nested frame, its proxy, aux channels, collapsed transformations and the
  viewer's internal wireframes), and the Render Queue renders it at its own rate whatever the
  Frame Rate override (`RenderSettings::rate`).
- Preserve resolution: nested in a comp rendered at Half / Third / Quarter, it renders at full
  size and the buffer's scale places it.
- `comp.settings {preserveFrameRate, preserveResolution}`, `comp.info` reports them, scripts
  read and set `CompItem.preserveNestedFrameRate` / `preserveNestedResolution`, Pre-compose
  copies them, and the dialog registers `dialog.comp.preserveFrameRate` /
  `dialog.comp.preserveResolution`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:33:14 +07:00
Brandon Thomas e910858445 Never crash: PostScript radix numbers outside 2..=36
In EPS/PostScript, `0#7` asked i64::from_str_radix for radix 0, which panics (found by
fuzzing). Radixes outside 2..=36 are not numbers now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:21:14 -04:00
Brandon Thomas fe89ab9146 Never crash: render and gpu crates without panic!()
render: the debug-only quantisation check uses debug_assert! instead of panic!. gpu: the
test-only panic on a wgpu validation error is cfg(test) with a narrow allow, and release
builds only log. Test-only unreachable!() calls become panic!(). Denies the panic lints in both
crates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:20:37 -04:00
Brandon Thomas fc737d76da Never crash: Tick and timecode arithmetic can't overflow or divide by zero
Tick arithmetic saturates, seconds-to-tick conversion clamps to Tick::MIN..=Tick::MAX (NaN is
zero), zero rates/timebases no longer divide by zero, and timecode / feet+frames parsing and
formatting use checked or saturating math. `layer.timing {"delta": -1e308}` over the control
channel overflowed FrameRate::snap_nearest (found by fuzzing every command), and a timecode
field holding 99999999999999:00:00:00 overflowed parse_timecode. Denies the panic lints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:20:33 -04:00
Brandon Thomas 5dd7d9bb57 Never crash: effects crate without expect()
Effects without expect(): nested parameter groups are looked up with ? (falling back to the
effect's top level), the OCIO effects use named constants for the built-in ACES spaces instead
of expect()ing a table lookup, curve fitting and Path Text no longer expect on just-pushed
vectors. Test-only unreachable!() becomes panic!(). Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:14:16 -04:00
Brandon Thomas 19c1d2ea1d Never crash: reject SVGs nested too deeply
roxmltree recurses per element and overflowed a 2 MiB stack at a few thousand nested
elements (aborting the app). parse() now scans the element depth first and returns
Error::Xml beyond 256 levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:05:58 -04:00
Brandon Thomas ad5aa542b4 Never crash: reject timeline documents nested too deeply
FCP 7 XML / FCPXML nested about 1 000 elements deep overflowed an 8 MiB stack in the XML
parser (aborting the app). Import now scans XML and OTIO JSON nesting first and returns
Error::TooDeep beyond 256 levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:05:57 -04:00
Brandon Thomas d1413d5209 Never crash: bound PSD descriptor nesting
The descriptor depth limit restarted at 0 inside every nested descriptor (Objc / GlbO /
ObAr), so a crafted file of 100 000 nested descriptors (2.4 MB) recursed until the stack
overflowed and aborted the app. The depth now carries through nested descriptors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:05:55 -04:00
bflatastic fa27d621e0 M4.8: Edit ▸ Purge clears what it names: disk and snapshot purges keep the RAM preview, memory purges drop decoded footage
- Every purge emptied the viewers' RAM preview; Purge ▸ All Disk Cache and Snapshot now leave
  it (Image Cache Memory, All Memory, All Memory & Disk Cache, All and All 3D Cache still
  clear it).
- The memory purges also drop the decoded footage frames (`FootageSource::purge`, the media
  pool's `clear_frames`; decoders stay open), so footage reads decode again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:03:47 +07:00
bflatastic 5ce641c6b3 M4.7: Cache Frames When Idle renders the work area in the background
Composition ▸ Preview ▸ Cache Frames When Idle was only a saved checkbox. After a second
without input or edits it now renders the work area's frames (from the current time on, then
from the start) into the RAM preview, a few at a time behind the viewer's own frame, until the
work area is cached or the RAM preview budget is full.

Docs: preferences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:56:49 +07:00
bflatastic 6214aecdb9 M4.6: previews: Auto resolution keeps its scale while playing, Cache Before Playback plays what fits, achieved frame rate in the Info panel
- Viewer Resolution Auto rendered at half its scale while a preview played, so frames cached
  while scrubbing were not played and frames cached by the preview were re-rendered once it
  stopped. Auto now follows the zoom alike playing and paused (Fast Previews still lowers it
  while dragging).
- Cache Before Playback waited for every frame of the range: a range larger than the RAM
  preview budget evicted its first frames and never started. The range is now cut to the
  frames that fit (90 % of Settings ▸ Memory & CPU's budget) with a message, then cached and
  played.
- The Info panel shows the frame rate a playing preview achieves and whether it is real time
  (`Playback::achieved_fps` / `real_time`, automation id `info.previewFps`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:52:50 +07:00
bflatastic 5648fdb1c2 M4.5: RAM preview: render options in the frame key, least-recently-shown eviction, stale frames freed at once
- `FrameKey` carries a hash of the render options (`frames::opts_hash`): switching Fast
  Previews (Draft / Fast Draft) or Realtime Shadows no longer shows frames rendered the other
  way.
- Eviction drops the least recently shown frame (`Frames::get` touches it), not the oldest
  inserted: a looping preview a little over budget no longer evicts the frame it shows next.
- Frames of earlier project revisions are dropped on every change (`Frames::drop_stale`); they
  could never be shown again but held memory (and GPU textures) until evicted.
- The green cache bars (Timeline, Preview panel) count the frames of exactly what the viewer
  shows — scale, 3D view / region of interest and options (`EffectcraftApp::shown_series`) —
  instead of any frame at that scale.
- Playback loops compute the key's fixed part once (`EffectcraftApp::frame_series`).
- Docs: architecture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:43:56 +07:00
Brandon Thomas 786e4e8b97 Never crash: raster and path crates without expect()/unwrap()/unreachable!()
raster: the inpainting pyramid stops at a missing level, and video inpainting falls back to a
plain patch fill if the previous frame is missing, instead of expect(). path: curve fitting of
an empty point list returns a degenerate cubic instead of indexing/unwrapping, and Merge Paths
no longer has an unreachable!() arm. Denies the panic lints in both crates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:41:09 -04:00
Brandon Thomas 3d5432c318 Never crash: engine without expect() (job polling, commands, menus)
Removes the engine's production expect() calls: background-job polling (camera, mask, roto,
point and warp tracking, render queue) uses let-else / if-let instead of take().expect("job"),
command helpers bind what they checked instead of re-reading it with expect, the menu bar falls
back to an empty tree (logged) instead of expect()ing that a constant parses, and
autotrace/text animator group lookups no longer expect a just-inserted group. perf.rs, the
benchmark harness behind `effectcraft-cli bench --ops`, opts out with a module-level allow.
Test-only unreachable!() calls become panic!() (clippy.toml allows panics in tests). Denies the
panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:40:46 -04:00
Brandon Thomas 0873393e43 Never crash: text layout and font resolution without expect()
Text layout and font resolution without expect(): an empty line list keeps the default bounds,
the line breaker stops instead of expect()ing a checked option, and font resolution falls back
to the first bundled face. The one remaining expect (the bundled-font database is never empty)
carries the documented narrow allow. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:27:38 -04:00
Brandon Thomas 6144ca0804 Never crash: last-resort guard around command execution, and a logging panic hook
Session::execute runs every command (UI, CLI, control channel, MCP, scripts; file import,
export and save are commands too) through guard::guarded, which catch_unwinds a panic that
escapes the command and returns EngineError::Other("command `x` failed with an internal error
(...); it was stopped and your project is still open"). Edits work on a copy of the project, so
a command that panics mid-edit leaves the project untouched. The desktop app also installs a
panic hook that logs every panic through the app logger (then runs the default hook).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:27:33 -04:00
Brandon Thomas f8460971f0 Never crash: scripts nested too deeply return an error instead of overflowing the stack
Scripts (Script Console, effectcraft-cli script, the MCP run_script tool) are parsed by Boa,
which recurses per nesting level and overflows the stack (aborting the app) at about 200
levels on an 8 MiB thread. run() now rejects a script nested deeper than MAX_NESTING (96)
brackets with a ScriptError before parsing; strings, template literals and comments are
skipped. Also replaces expect("console context") with an error, and denies the panic lints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:26:05 -04:00
Brandon Thomas bf8962becf Never crash: trackers and camera solver without expect()
The camera solver, KLT pyramid and subspace (warp stabilizer) tracker expect()ed data-structure
invariants: a track point seen in a frame, a solved camera, a pyramid level. A missing value now
skips that point/track (or, for finish(), returns a SolveError: "no frame was solved") instead of
panicking. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:24:17 -04:00
Brandon Thomas 1da7dc4659 Never crash: Lottie enum values can't overflow
Lottie stores line cap, line join, merge mode and some text-selector enums 1-based, and the
importer subtracted 1 from whatever number the file held: -1e308 (rounded to i64::MIN)
overflowed, a panic in debug builds (found by structural fuzzing). Use saturating_sub. Denies
the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:24:14 -04:00
Brandon Thomas 5cc376a818 Never crash: Opus and VP9 encoders without expect()
opusenc: the SILK state is used through if-let / let-else instead of expect("SILK state").
vp9enc: the motion-search SAD and inter prediction return early without a reference frame
instead of expect("reference"). Denies the panic lints in both crates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:23:06 -04:00
Brandon Thomas da3c7a842d Never crash: HEVC encoder without expect()/unreachable!()
Replaces the encoder's 8 expect()/unwrap()/unreachable!() calls with handling that can't
panic: coding-unit decisions without a candidate report an infinite cost, inter helpers
without a reference picture return a neutral result, residual_coding of an all-zero block
writes nothing, the impossible Intra arms are no-ops, and the edge padding of an empty row uses
0 instead of unwrapping. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:23:03 -04:00
Brandon Thomas ee90d87742 Never crash: AV1 encoder without expect()/unreachable!()
Replaces the encoder's 11 expect()/unreachable!() calls on internal invariants with handling
that can't panic: the rate-control state is passed in rather than re-borrowed, inter decisions
without a reference frame fall back to intra, per-plane results are bound directly, and the
writer's impossible branches become debug_asserts. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:22:59 -04:00
Brandon Thomas fc8fea031c Never crash: reject deeply nested expressions instead of overflowing the stack
An expression nested about 60 brackets deep overflowed the stack of a 2 MiB thread in the
JavaScript parser (release and debug; ~200 levels on 8 MiB), which aborts the whole app: it
can't be caught. The array-maths rewrite also nests one call per arithmetic operator, so a long
sum went the same way, and the rewriter itself recursed per bracket. rewrite() now measures the
nesting (brackets plus chains of prefix operators) before and after rewriting and turns an
expression deeper than MAX_NESTING (32) into a RangeError; check_syntax reports the same error.
Also replaces the unreachable!() in the bracket-tree builder with a let-chain, and denies the
panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:22:36 -04:00
BFlat 249cc145e3
Merge pull request #8 from storybored379/pr/fullscreen-shortcut-off-mac
Shortcuts: don't bind macOS-only Ctrl+Cmd shortcuts off macOS
2026-10-05 00:16:09 +07:00
bflatastic 948f06fbf2 M3.14: project files name the version that wrote them, never save unreadable, keep their format
- `Project::to_file_json` writes `savedBy` (the EffectCraft version) ahead of the project and
  checks that the text reads back before anything is written: JSON has no NaN or infinity, so
  such a value made a file that saved but could never be opened. Save now fails with a message
  and the file on disk is left as it was.
- Opening a project saved by a newer EffectCraft warns that settings this version doesn't know
  are lost on saving; `file.open` reports `savedBy`. A file that isn't a project names itself in
  the error.
- Increment and Save keeps the extension (`.ecprojx` stays XML); Save a Copy writes XML for
  `.ecprojx` / `.xml` paths, like Save As.
- Docs: architecture (document model), parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:08:07 +07:00
Brandon Thomas 587d4803fe Never crash: project crate without expect()
Layer::motion_trackers_mut expect()ed the group it had just inserted; it returns an Option
now and track.motion reports an error if the group is somehow missing. Denies the panic
lints in the crate (the project model behind .ecproj files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:58:51 -04:00
bflatastic 6fc39eb133 M3.13: menu bar: Group/Ungroup Shapes in the Layer menu, Window ▸ Learn, flowchart shortcuts, Center In View shortcut, Reveal in Explorer on Windows
- Group Shapes (Ctrl+G) / Ungroup Shapes (Ctrl+Shift+G) sit in the Layer menu itself, as in
  After Effects (they were under Mask and Shape Path).
- Window ▸ Learn opens the Home screen's Learn tab.
- Composition ▸ Composition Flowchart is Ctrl+Shift+F11 and Window ▸ Flowchart Ctrl+F11 (the
  flowchart had the panel's shortcut and the panel none).
- Layer ▸ Transform ▸ Center In View shows Ctrl+Home.
- "Reveal in Finder" reads "Reveal in Explorer" on Windows and "Reveal in File Manager" on
  Linux (`menus::platform_label`).
- Docs: parity (View ▸ New Viewer noted as not yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:58:00 +07:00
bflatastic a34d0c421b M3.12: Pre-compose keeps the composition's settings; New Comp from Selection is one undo step with After Effects' options
- Pre-compose makes the new composition with the original's pixel aspect, background, motion
  blur and frame blending settings and 3D renderer (`Comp::nested_like`); it kept only size,
  rate and background.
- File ▸ New Comp from Selection records one undo step (two per item before) and takes each
  item's pixel aspect. Several items selected open the New Composition from Selection dialog:
  Single / Multiple Compositions, Use Dimensions From, Still Duration (default from Settings ▸
  Import ▸ Still Footage), Add to Render Queue, Sequence Layers with Overlap, Duration and
  Transition. `file.newCompFromSelection {single, dimensionsFrom, duration, addToRenderQueue,
  sequence, overlap, overlapDuration, transition}`; `layer.addItem {duration}` for stills.
- Docs: parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:48:54 +07:00
bflatastic 89653f37de M3.11: layer commands respect locks, keep children in place on delete, arrange step by step, keep depth and separated dimensions, keep links on duplicate/paste
- Locked layers can't be selected (Timeline click, Select All, Ctrl+Up/Down; shy layers while
  hidden too) and are left alone by Clear/Delete, Arrange, Layer ▸ Transform and Center Anchor
  Point, which fail when every target is locked (`commands::unlocked`, `commands::selectable`).
- Deleting a parent unparents its children where they are, in the same undo step: the
  pick-whip compensation moved out of `layer.setParent` into `layer::parent_fixes` /
  `ParentFix::apply`, which now also handles separated X/Y Position.
- Bring Forward / Send Backward move each selected layer one step (a non-contiguous selection
  was squeezed into one block).
- Center In View / Fit to Comp keep a 3D layer's Z (and Fit keeps Scale/Anchor Z); Reset /
  Center / Fit / Center Anchor Point work with separated dimensions (`layer::update_position`).
- Duplicate and paste keep parents and track mattes, following the copies when both were
  copied; paste dropped them always.
- Docs: agents, parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:33:54 +07:00
Brandon Thomas dc573f77e6 Never crash: PSD reader bounds overflow and input-sized allocations
Layer bounds come straight from the file as four i32s. Rect::width/height subtracted them in
i32 (overflow: a panic in debug builds, garbage sizes in release), and layer_pixels allocated
width x height pixels before reading any data, so a 100-byte file claiming a 2^31-square layer
hit "capacity overflow" (or an out-of-memory abort). Widths are now computed in i64 and
clamped, every decoded image is capped at 2^28 pixels (Error::Unsupported beyond that), zlib
data is inflated with a limit of the expected size, RLE count tables and UTF-16 strings
reserve no more than the remaining bytes, and the per-pixel copy only visits the part of the
layer that lands in the output. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:27:18 -04:00
bflatastic 753de070a0 M3.10: Layer Settings opens Solid Settings with "Affect all layers that use this solid"; settings dialogs register every control
- Layer ▸ Layer Settings on a solid or adjustment layer opens Solid Settings on the layer's
  solid (it ran an empty command before): name (renames the layer and the solid), size, Pixel
  Aspect Ratio, colour, and "Affect all layers that use this solid". It starts off, as in After
  Effects, so changing a solid other layers share gives this layer its own copy.
  `layer.settings {pixelAspect?, affectAll?}` → `{layer, solid?}`; `layer.newSolid {pixelAspect?}`.
- Nulls ask for their name; text and shape layers have no settings (the entry is disabled).
- Composition Settings / New Composition and Solid Settings register an automation id for
  every control (`dialog.comp.*`, `dialog.solid.*`). Composition Settings takes any typed frame
  rate besides the list, and New Composition starts from the last settings used.
- Colour buttons in both dialogs show the stored sRGB colour (`widgets::srgb_color_button`);
  egui's linear picker showed it lighter.
- Docs: agents, parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:22:09 +07:00
Brandon Thomas 37c9b449c6 Never crash: media crate without expect()
The smart-object branch of the layered-file decoder bound the footage layer twice and
re-read it with expect("embedded implies a layer"); bind it once with a let-chain. Denies the
panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:18:36 -04:00
Brandon Thomas 8e4e29844e Never crash: PDF close-path operators and CFF outlines without a current point
The s, b and b* operators closed the path even when there was no current point, which kurbo
asserts against (a panic in debug builds; found by fuzzing). Close only when a subpath is open.
CFF charstrings that draw before their first rmoveto now start the subpath at the current
point, as Type 1 charstrings already did. Denies the panic lints in the crate (it had no panic
sites).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:15:02 -04:00
Brandon Thomas 6708ff55b4 Never crash: SVG path data no longer loops forever on a number after Z
Path data like "M0 0 L10 0 Z9" repeated Z implicitly for the stray number; Z consumes no
numbers, so the parser looped forever appending close-paths until memory ran out (a 1-byte
change to a valid SVG used 24 GB and was killed). A number after Z is a syntax error, so
parsing now stops there and keeps what was read, as the spec asks. Also denies the panic lints
in the crate (it had no panic sites).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:14:58 -04:00
Brandon Thomas ed54a8cd15 Never crash: interchange export/import without expect()
Removes the six expect() calls from timeline export/import and denies the panic lints in the
crate. A precomp layer whose source item is not a composition (a damaged project, or an item
that was deleted) made export panic in Exporter::sequence; it now leaves that layer out.
Exporter::sequence and track_item return Option, the Motion/Opacity/Volume effect defaults
come from effect_or_new (None if FilmCraft's registry lacks one), and import's motion() skips
a layer without a transform group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:10:09 -04:00
bflatastic e0ed28f55d M3.9: closing a modified project asks to save it first; the modified mark follows undo
- Quit and the window's close button, File ▸ New Project, Open Project, Open Recent, Close
  Project, the demo project and a Home template show Save / Don't Save / Cancel when the
  project has unsaved changes (Save asks for a path when untitled; cancelling that keeps the
  project open). File ▸ Revert asks to discard. Before, all of them dropped unsaved work
  without asking.
- The window title shows the project's path and `*` while it is modified.
- The modified mark follows undo: the session keeps the snapshot it last opened or saved
  (`Session::saved_project`), so undoing, jumping in the History panel or rolling back a batch
  to the saved state clears it.
- An edit that changes nothing (a value set to what it already is) records no undo step and
  doesn't mark the project modified (`Project::same_content`, comparing untouched comps by
  pointer).
- Agents' `engine.execute` / MCP calls are never asked; `ui.menu.invoke` shows the prompt like a
  click (automation ids `dialog.unsaved.save` / `dontSave` / `revert` / `cancel`), and the
  control channel's `app.quit {force: true}` skips it.
- Docs: architecture, agents, control protocol, parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:02:14 +07:00
storybored379 0a8cdfc67e Scripting: Property.keyInSpatialTangent / keyOutSpatialTangent under their AE names
The getters were registered as keySpatialInTangent / keySpatialOutTangent,
so scripts using the documented After Effects names failed with
"TypeError: not a callable function" (setSpatialTangentsAtKey and the
other keySpatial* methods already use AE's names).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:17:34 -07:00
storybored379 3411fc80ab Keyframes: a Hold key no longer eases the motion into it
segment_value treated any non-Linear in-side of the next key as Bezier,
so a key set to Hold (KeyframeInterpolationType.HOLD, add_keyframe
"interpolation": "hold", or the Keyframe Interpolation dialog, which set
both sides) turned the linear segment into it into an ease-in with the
default ease: 0 -> 100 over 2 s read 27.0 instead of 25 at 0.5 s. Hold
only affects what follows the key, which the out side already handles.
The Graph Editor and the Lottie exporter already treat a Hold in-side as
linear, so exports didn't match the preview either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:17:34 -07:00
storybored379 18fcc48046 Shortcuts: don't bind macOS-only Ctrl+Cmd shortcuts off macOS
Off macOS `Cmd` maps to Ctrl, so Enter Full Screen's Ctrl+Cmd+F parsed
to the same keys as Find's Cmd+F and both commands were bound to Ctrl+F
(ui_and_menu_shortcuts_do_not_collide fails on Linux and Windows).
Shortcuts that use both Ctrl and Cmd can't be typed distinctly there,
so they are skipped when the key bindings are built off macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:17:34 -07:00
Brandon Thomas a006ed5765 M13.32: web main-thread stall: Warp Stabilizer plans solved in job workers
warp.status solved the stabilization plan (Subspace Warp eigen-decompositions and mesh fits)
on the page the first time it saw an analysis: ~4.9 s blocking the browser's event loop in
the smoke test. The analysis job now sends the plan summary with its result, and a settings
change has a WarpPlan job solve it (status reports stabilizing meanwhile). Desktop unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 03:40:46 -04:00
Brandon Thomas f9e1807962 Merge branch 'main' into worktree-agent-abff9a10f06a98f2c
# Conflicts:
#	crates/gpu/src/tests_deferred.rs
2026-10-04 03:16:01 -04:00
Brandon Thomas 3921e6c680 M13.30: Content-Aware Fill in a job worker on its GPU
contentFill.generate sends its plan (FillPlan) to a browser job worker:
the layer's frames render in passes on the worker's WebGPU device, the PNG
sequence goes back to browser storage (Services::store_file), and the page
adds the fill layer when the plan returns (WorkerReply::Fill). The smoke
test checks it; docs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 03:15:54 -04:00
Brandon Thomas c59e0a6407 Merge main into M13.28 worktree
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:57:55 -04:00
Brandon Thomas 91f4a603a9 Merge branch 'main' into worktree-agent-abff9a10f06a98f2c
# Conflicts:
#	docs/parity.md
2026-10-04 02:57:35 -04:00
Brandon Thomas f3715aac32 M13.30: web depth and polish
- GPU in browser job workers: each job worker opens its own WebGPU device
  (deferred readbacks); the Render Queue export and the analyses' frame loops
  are futures (render::passes, offload::run_request_async) whose frames
  render in passes, awaiting the device between them. GPU particles and
  Advanced 3D read back under keys too. workers.jobs reports passes and
  readbacks; the smoke test measures them.
- Layer buffers in the browser's OPFS disk cache: frame workers back their
  layer caches with a PrefetchStore; the page plans what to fetch before a
  request from recent misses (LayerPrefetch over the shared DiskIndex).
- Templates embed footage (default on, size-capped with a warning) and
  creating from a template extracts it next to the new project / into
  browser storage.
- ICC LUT profiles (lut8/lut16/lutAtoB/lutBtoA, from the public ICC spec) in
  Simulate Output > My Custom RGB, baked into a 3D LUT for the viewer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:57:17 -04:00
Brandon Thomas c4315818da M13.28: GPU generators (fx_gen2): Lightning, Advanced Lightning, Beam, Lens Flare, Radio Waves, Vegas, Stroke, Scribble, Write-on, Paint Bucket, Eyedropper Fill, CC Glue Gun, CC Threads, Audio Spectrum/Waveform, Basic Text, Path Text
The geometry stays on the CPU as per-frame plans shared with the CPU effects (bolts, mask
strokes, scribbles, contour segments, audio marks, glyph strokes, waves, flood-fill region,
eyedropper sample); segments and convex polygons are tile-binned and rasterised on the GPU
with the CPU's coverage functions. CPU output unchanged (gen_golden). CC Threads' exact ties
at round parameter values are a documented exception; Fractal stays on the CPU (needs f64).
Regenerated docs/effects.md, docs/parity.md EFF-5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:56:00 -04:00
Brandon Thomas 1c855c3e07 M13.29: GPU effects part D (particles and remaining fallbacks)
GPU render passes for CC Particle World / Systems II, Particle Playground,
CC Ball Action, CC Pixel Polly, CC Scatterize and Curl Noise (fx_particles,
chunked multi-pass raster); Shatter wireframe views, Foam extras, Timewarp
with a Matte Layer, strong Warp Fisheye/Twist (CPU f64 inverse map) and
custom .ocio configs no longer fall back to the CPU. CPU refactors pinned by
particle_golden / timewarp_golden / sim_golden hashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:35:56 -04:00
Brandon Thomas 5df9cde182 M13.28: GPU pixel ports (fx_pixel2): CC Cross/Radial/Vector Blur, Reduce Interlace Flicker, CC Composite, Color Link, Cineon, HDR Compander/Highlight Compression, Grow Bounds, CC Overbrights, CC Block Load, CC Burn Film, CC Glass/Plastic, CC HexTile, CC Mr. Smoothie, Inner/Outer Key, CC Simple Wire Removal, Basic 3D
Inner/Outer Key's trimap and Cleanup strokes, Color Link's sample and CC Glass/Plastic's
light are shared with the CPU effects (CPU output unchanged: gen_golden). CC Plastic now
reads its Bump Layer (it read the nonexistent bumpMap id).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:33:54 -04:00
Brandon Thomas 764c3a31ad M13.28: golden hashes of the plan-drawn CPU effects (pinned before the GPU refactor)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:06:40 -04:00
Brandon Thomas b9f09869eb M13.29: particle effects golden hashes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:04:43 -04:00
Brandon Thomas d5bb5ad994 Merge branch 'main' into worktree-agent-a9fea98fe9839069d 2026-10-04 00:52:53 -04:00
Brandon Thomas b3a228ed74 Merge branch 'main' into worktree-agent-a3d97efbded4f5ed3 2026-10-04 00:50:03 -04:00
Brandon Thomas ce6ece4cd4 M13.27: sim effects golden hashes
Pin exact CPU pixel hashes of the simulation effects refactored into shared
GPU plans in M13.23 (CC Rainfall/Snowfall/Star Burst/Bubbles/Drizzle/Hair/
Mr. Mercury, Caustics, Wave World, Foam, Shatter, Card Dance, Card Wipe):
404 frames over default and non-default settings, four times incl. a seek
backwards, 8 and 32 bpc, full and half resolution. Verified bit-identical
between 7a5a93b (before M13.23) and 808ca70 (after).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:50:03 -04:00
Brandon Thomas b73028d999 Merge branch 'main' into worktree-agent-a9fea98fe9839069d (filmcraft rev 2c5ba7a: AAF and OMF interchange)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:45:00 -04:00
Brandon Thomas 8cb6d34c58 M13.26: import command keeps After Effects' menu label
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:41:06 -04:00
Brandon Thomas 1b61d81f3b Merge branch 'worktree-agent-a139f16b3a4130998' 2026-10-04 00:39:13 -04:00
Brandon Thomas 05b614990b Merge branch 'main' into worktree-agent-a9fea98fe9839069d 2026-10-04 00:25:43 -04:00
Brandon Thomas 955422819c M13.26: Premiere Pro interop commands (file.importTimeline, file.exportTimeline with ProRes 4444 pre-renders), File menu entries, export dialog, docs and pixel round-trip tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:25:43 -04:00
Brandon Thomas 808ca70d47 Merge origin/main (M13.22 GPU effects part A) into M13.23: both GPU effect lists (236), docs recounted 2026-10-04 00:17:37 -04:00
Brandon Thomas 299fe09efe Merge branch 'main' into worktree-agent-a139f16b3a4130998 2026-10-04 00:15:57 -04:00
Brandon Thomas 32c53aa35e av1enc tests: decode with a single-threaded filmcraft-av1 decoder
FilmCraft's AV1 decoder now frame-threads by default, so a picture may
come out of a later decode call; the round-trip check wants each
packet's picture from its own call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:15:39 -04:00
Brandon Thomas fe80862eeb M13.26: effectcraft-interchange crate (L3): timeline documents to compositions and back via filmcraft-interchange (FCP7 XML, FCPXML, OTIO, EDL)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:05:54 -04:00
Brandon Thomas 7c675124ae Merge origin/main (M13.24) into M13.23 2026-10-03 23:59:41 -04:00
Brandon Thomas 4a2ca9115b M13.23: Shatter wireframe views and Foam's texture / map extras fall back via gpu_supported (no readback attempt)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:59:22 -04:00
Brandon Thomas d3a7777939 Merge branch 'main' into worktree-agent-a139f16b3a4130998 2026-10-03 23:57:57 -04:00
Brandon Thomas cc5c831c0a media: WebM VP9 alpha imports as straight alpha (FilmCraft 2c5ba7a)
Bump the FilmCraft crates from 89cfbf2 to 2c5ba7a (118 commits; no API
changes needed here), whose Matroska reader now decodes the VP9 alpha
stream from BlockAdditions. The compositing QA scenario now checks that
WebM renders round-trip their alpha like ProRes 4444.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:57:57 -04:00
Brandon Thomas ca9a30bf0c Merge branch 'main' into worktree-agent-a38a174c36aa9f23a 2026-10-03 19:52:55 -04:00
Brandon Thomas 49d0c16c9f M13.23: docs — GPU effects part B (parity EFF-5, architecture GPU section, ROADMAP count, gpu crate docs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:36:26 -04:00
Brandon Thomas a55b07ef01 Merge main into M13.23 2026-10-03 18:35:42 -04:00
Brandon Thomas 7d9f369a5e Merge branch 'main' into worktree-agent-acd022ffe72ec3783
# Conflicts:
#	docs/parity.md
2026-10-03 18:35:29 -04:00
Brandon Thomas 1ec9336d7c M13.23: GPU simulation render passes
CC Rainfall, Snowfall, Star Burst, Bubbles, Drizzle, Hair, Mr. Mercury,
Caustics, Wave World, Foam, Shatter, Card Dance and Card Wipe render on the GPU
(fx_sim.rs / fx_sim.wgsl). The per-frame simulation / layout stays on the CPU
and is shared with the CPU effect through plans (effects::sprite_plan,
piece_plan, bubble_list, mercury_blobs, drizzle_drops, wave_world_plan,
caustics_setup); a tiled item rasteriser composites sprites, bubbles, blob
fields and textured pieces in the CPU's order and shading. Layer-dependent
sprite colours (refracting rain, star-burst and hair roots) are sampled per
pixel; piece plans read the frame back once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:35:22 -04:00
Brandon Thomas 1b726349ab M13.24: web app depth (WEB-1) — GPU effects in frame workers, OPFS disk cache, storage manager
- GPU in the browser: each frame worker opens its own WebGPU device (Gpu::request_deferred) and
  renders the GPU compositor and GPU effects with deferred readbacks (effectcraft-gpu deferred):
  readbacks keyed by content start with map_async and miss, the pass returns placeholders, the
  layer cache's gate drops inserts, and FrameServer::resume renders again once the device
  delivered. Backend Auto times whole frames per comp in the workers. Frames with effects go to
  GPU workers instead of the page. CPU fallback without WebGPU in workers (?nogpuworkers).
- Fix: a WGSL constant (3.40282347e38) that Chrome rejects invalidated every GPU kernel in Chrome
  (the page's compositor too); a test checks every float literal fits f32.
- Disk cache in the Origin Private File System: workers write sealed frame entries with sync
  access handles (temp file + move), the page keeps the DiskIndex (LRU under the settings' limit,
  at most half the quota) and reads hits instead of rendering; survives reloads; blue cache bar.
- Storage manager: Settings ▸ Disk ▸ Browser Storage (usage/quota, persistent storage, Clear
  buttons, automation ids) and storage.info / storage.persist / storage.clear (StorageHost).
- Docs: web.md (GPU workers, deferred passes, disk cache, storage manager, threading design:
  one engine instance per worker), architecture, preferences, parity (WEB-1), roadmap.
- Tests: deferred passes vs CPU on a real device, placeholder gating, frame server passes and
  Auto, GPU worker routing + disk cache across a reload, storage commands and widgets, store
  usage by kind; smoke test: GPU effect frames in workers with pixel parity, disk hits after a
  reload, storage manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:35:03 -04:00
Brandon Thomas 590c85bfb0 Merge branch 'main' into worktree-agent-a38a174c36aa9f23a
# Conflicts:
#	docs/parity.md
2026-10-03 18:28:15 -04:00
Brandon Thomas 0c4e533460 M13.22: GPU compositing of Advanced 3D runs with blend modes, track mattes (2D and 3D), Preserve Transparency, and environment backgrounds (Classic and Advanced 3D skies)
render: three_d::adv::SplitRun (Renderer::split_adv_run) hands draw_run's split path to an
accelerator as data (main scene, special layers far to near, 3D mattes drawn solo);
Renderer::sky_draw / three_d::SkyDraw for Environment Light Background layers. gpu: the walk
draws skies (adv_sky kernel), resolves each special layer, hides it behind the nearer main
scene (occlude kernel) and composites it through the 2D path without readbacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:26:06 -04:00
Brandon Thomas 3881dbb32e M13.25: Home Templates gallery (thumbnails, open, delete, Save Current as Template), Save as Template and My Custom RGB dialogs, form automation ids; parity update
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:14:17 -04:00
Brandon Thomas a909de43b7 M13.22: GPU time effects (Time Difference, Time Displacement, CC Force Motion Blur, CC Wide Time, Pixel Motion Blur, Timewarp's frame building; motion vectors and frame fetches on the CPU)
Timewarp without a Matte Layer is now planned once (time_fx::timewarp_plan: frames, shutter
instants, motion fields) and built by the CPU or the GPU from the plan; with a Matte Layer it
renders on the CPU (catalog::gpu_supported).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:57:03 -04:00
Brandon Thomas 2e5ea051a8 M13.23: GPU Immersive Video effects
VR Blur, Chromatic Aberrations, Color Gradients, Converter (all projections and
cube layouts), De-Noise, Digital Glitch, Fractal Noise, Glow, Plane to Sphere,
Rotate Sphere, Sharpen and Sphere to Plane run on the GPU (fx_vr.rs /
fx_vr.wgsl) with the CPU's equirectangular maths: seam-wrapping, pole-continuing
sampling and latitude-widened box blurs, per eye for stereo layouts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:53:23 -04:00
Brandon Thomas d0b92948ee M13.22: GPU transitions and perspective (Block Dissolve, CC Glass/Grid/Image Wipe, CC Jaws, CC Line Sweep, CC Radial ScaleWipe, CC Scale Wipe, CC Twister, CC WarpoMatic, Radial Shadow, CC Bender, CC Blobbylize, CC Cylinder, CC Sphere, CC Spotlight, CC Environment, 3D Glasses) and text (Numbers, Timecode: CPU glyph coverage, GPU composite)
CC Environment now fades nearly transparent environment texels to black continuously
(colour / max(alpha, 1e-3)) instead of a hard 1e-6 un-premultiply cut-off, which turned the
filtered map's float noise into colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:48:02 -04:00
Brandon Thomas 9e70cde725 M13.25: View > Simulate Output > My Custom RGB (primaries, white, gamma or an RGB ICC profile; kept in Settings; view.customRgb) and File menu entries for templates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:46:41 -04:00
Brandon Thomas e3e35a7ec2 M13.23: GPU colour management (LUTs, OCIO, Color Profile Converter, Lumetri LUT/look)
Apply Color LUT, OCIO CDL / Color Space / Display / File / Look Transform and
Color Profile Converter compile to colour programs (effects::color_program,
built next to the CPU effects) that fx_lut.wgsl interprets per pixel; LUTs
(1D, 3D, cineSpace shapers, inverses) are read from the storage buffer with the
CPU's nearest / trilinear / tetrahedral interpolation. Lumetri's Input LUT and
Look no longer force the CPU. Custom .ocio configs stay on the CPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:43:24 -04:00
Brandon Thomas b760f1d23a M13.25: project templates engine — eight original built-in templates with Essential Graphics controls and rendered thumbnails; user .ectemplate templates in the config Templates folder; templates.list/thumbnail/create/saveAs/delete
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:37:18 -04:00
Brandon Thomas 6b91afc087 M13.23: GPU 3D Channel effects (aux channels as textures)
3D Channel Extract, Cryptomatte, Depth Matte, Depth of Field, EXtractoR, Fog 3D,
ID Matte and IDentifier run on the GPU (fx_depth.rs / fx_depth.wgsl): aux planes
upload as a texture and are resampled onto the buffer grid with the CPU's index
arithmetic; tests compare against the CPU at 1x / 2x aux resolution, padded
buffers, half resolution and 8 / 32 bpc composites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:33:41 -04:00
Brandon Thomas f7927c8f9f M13.22: GPU CC light family (CC Light Rays, CC Light Burst 2.5, CC Light Sweep, CC Light Wipe)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:31:26 -04:00
Brandon Thomas 081ce79de3 Merge M13.14 agent-interface audit + lazy open (worktree-agent-a5f8967ebd42ce98b)
The branch's commits say M13.14; on main that id is the puppet work, so this arc is M13.21.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:23:10 -04:00
bflatastic f9990f7387 M13.20: every Timeline property shortcut — double presses AA (Material Options), PP (paint / Roto Brush / Puppet), SS (selected properties), RR (Time Remap), TT (Mask Opacity), MM (all mask properties; a single M now shows Mask Path), FF (missing effects), EE (expressions) next to LL and UU; Alt+Shift+A/P/S/R/T add or remove a keyframe at the current time on every selected layer in one undo step (keys.toggleTransform: separated X/Y/Z Position, Orientation and X/Y/Z Rotation on 3D layers); Ctrl/Cmd+` twirls the selected layers open or closed (from a reveal, their full tree) 2026-10-04 01:10:33 +07:00
bflatastic aef72378f8 M13.19: the Timeline's rename field and twirl arrows work — the name field took the keyboard focus every frame (Enter and clicking away could never leave it, which also silenced every single-key shortcut); it now focuses once with the name selected, Enter or a click elsewhere commits, Escape cancels; the layer row's click area sat on top of the twirl arrow and the name field (it was registered after them) and now sits under them; Ctrl-click a layer's arrow opens every group inside 2026-10-04 01:09:20 +07:00
bflatastic 52cf74e997 M13.18: drag layer rows in the Timeline to reorder them — a line shows where they land, the selected layers move together (an unselected row moves alone), one undo step, nothing for a drop in place; layer.arrange {above: layer} for agents 2026-10-04 00:31:59 +07:00
bflatastic bb973e2c76 M13.17: the viewer keeps its pan after a middle-button or Hand drag — egui reports no total drag on the release frame, so the pan was recomputed as the drag's start and snapped back 2026-10-04 00:31:59 +07:00
BFlat bb06a66083
Merge pull request #2 from storytold/puppet-rigging
Puppet tools: pin editing, rigging to nulls, follow-through; expression runtime fix
2026-10-04 00:26:24 +07:00
bflatastic a02bcc8441 M13.16: puppet Follow-Through — puppet.follow (Puppet tool bar ▸ Follow-Through…) makes pins trail a leader pin for hair, cloth and overlapping action: each follower's Position adds the leader's displacement from rest delayed by `delay` (the k-th nearest by k × delay with Cascade), scaled by Amount, keeping its own keyframes; leader = the first selected pin, followers = the others; one undo step; reachable over MCP (execute_command) 2026-10-04 00:19:26 +07:00
bflatastic 6ec177de83 M13.15: rig puppet pins to nulls — Points Follow Nulls / Nulls Follow Points (Window ▸ Create Nulls From Paths, paths.* {pins?}) take selected pins, named pins, or every pin of a layer with no path: a null per Position / Advanced pin drives its Position (fromComp of the null's anchor, so parented nulls rig limbs), or rides on it (toComp of the pin's Position) to carry other layers; one undo step 2026-10-04 00:19:25 +07:00
bflatastic 6869cff51b M13.14: puppet pin editing — click / Shift-click selects pins (puppet.selectPins), a drag from empty canvas or Alt-drag marquee-selects them, Select All with a pin selected takes every pin of that kind, Delete removes the selected pins instead of their layer; Advanced and Bend pins rotate by their ring and scale by its square (Shift: 15° / 5 % steps, one undo step per drag); ⌘/Ctrl-dragging one of several selected pins records them all; pins in their kind's colour, larger under the pointer; pushpin icons for the five pin tools 2026-10-04 00:19:24 +07:00
bflatastic 244cee4687 M8.1: expressions keep working on long-lived threads — boa leaves values on its VM stack when an exception unwinds through call frames (every pending host request does), so after tens to hundreds of evaluations that read layers or effects every expression on the thread failed with "maximum stack size"; a run that hits the limit now gets a fresh context and is retried once (regression test: 6000 host-reading evaluations on one thread) 2026-10-04 00:19:22 +07:00
Brandon Thomas 91bd8ca7c8 Merge main into M13.14
# Conflicts:
#	crates/engine/src/lib.rs
#	docs/agents.md
2026-10-03 13:13:50 -04:00
Brandon Thomas 56b3917a14 Merge main into M13.15
# Conflicts:
#	crates/engine/src/commands/essential.rs
2026-10-03 13:00:33 -04:00
Brandon Thomas 8b43adb8a0 Merge main into M13.14 2026-10-03 12:59:27 -04:00
Brandon Thomas e2d7a5a182 M13.15: qa batch tool and reply affordances
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:56:58 -04:00
Brandon Thomas 12235b5dc5 M13.15: qa project lifecycle; Collect Files makes its folder; agents cookbook
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:56:41 -04:00
storybored379 780419c235 Compositions: reject zero and negative frame rates instead of panicking
comp.new / comp.settings passed `frameRate` straight to
FrameRate::from_f64, so 0 (or anything below 0.0005) became a 0/1 rate
and the next frame-duration computation divided by zero, killing the
process (and an MCP session with its unsaved project); a negative rate
was accepted as-is. Scripts hit it via addComp(..., 0) or
comp.frameRate = 0. Return a parameter error like renderQueue.add's
frameRate check does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 09:31:41 -07:00
storybored379 0ae7a1925a Expressions: keep the runtime usable after errors and property reads
boa leaves the value stack of inner call frames behind when an exception
leaves a script (or a function called from Rust) from a nested call. Every
property read throws the pending-host-request abort from inside a prelude
helper, and so does any helper error (`thisComp.layer("Ghost")`), so the
thread's runtime filled up after ~100 evaluations of a pick-whip
expression: from then on every expression on that thread failed with
"RuntimeLimitError: reached the maximum stack size" and silently fell back
to its keyframed value (e.g. a follower layer stops following after ~90
render_frame calls).

Wrap the script and `__finish` in try/catch and rethrow from their own
frame, which unwinds the stack; backtraces (error line numbers) survive.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 09:31:41 -07:00
Brandon Thomas 77ab92fb7d M13.15: qa essential graphics template via CLI script
Fill's ADBE Fill-0002 is Color (documented numbering); essential.* controls by name with
a listing error; an explicit comp param satisfies a command's active-comp enablement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:30:11 -04:00
Brandon Thomas 97329edb39 M13.14: performance section with before/after numbers; test tidy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:17:46 -04:00
Brandon Thomas fecf054e5f M13.15: qa scenarios 1-4 (kinetic type, shape logo, compositing, tracking) and fixes
engine.batch + MCP batch tool; layer.newText/newShape name; prop.get evaluated
expression value; keys.select by path with errors; @uid paths; plain-JSON gradients;
addShapeItem/effect.apply return paths; unknown layers error; conform rescales
footage duration; render log name for [###]; render_frame transparent; clearer
mask errors; truncated groups in flat get_layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:09:41 -04:00
Brandon Thomas 9904525591 Merge main into M13.14 2026-10-03 11:50:14 -04:00
Brandon Thomas 8a4a2bfc52 M13.14: lazy project open, non-blocking UI with large projects, bench --ops
- footage.check: background footage check after File > Open (Progress panel), not an undo step
- timeline: track-matte / parent menus built only while open (was O(n^2) per frame);
  expression-row lookup per layer; panels hold Arc<Comp> snapshots instead of deep clones
- Project panel: virtualised vertical scrolling, selection reveal, thumbnails rendered off
  the UI thread
- effectcraft-cli bench --ops (engine::perf + ui-egui::bench)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 11:49:55 -04:00
Brandon Thomas a4f4d72f27 Merge branch 'main' into worktree-agent-a4b0610bd14276508 2026-10-03 10:53:00 -04:00
Brandon Thomas 750868244a M13.13: GPU Circle, Ellipse, Iris Wipe, Bevel Alpha, Bevel Edges, Gaussian Blur (Legacy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:53:00 -04:00
Brandon Thomas ea292a6e81 M13.13: fuse 8/16 bpc quantise into the layer composite; steady-state bench traffic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:44:52 -04:00
Brandon Thomas 9ff0e5aa3c M13.13: gpu texture pool, Auto per-comp backend choice, warp-family GPU ports
- gpu: pooled working textures (released once every encoder that could read
  them is submitted), shared zero images, reused readback staging buffers,
  direct readback into Image; NaN-poisoned scratch images under test and
  NaN-aware diffs; validation errors panic under test
- render: AutoPick (per comp / scale / path EWMA of CPU vs GPU frame times,
  warm-up and periodic re-probe) behind Backend::Auto; viewer uses it too;
  cached adjustment footprints (uploaded once)
- gpu fx_warp: Warp, Bezier Warp, CC Bend It, CC Page Turn, Smear, Reshape,
  Color Emboss, Cartoon; sliding-histogram median for large radii; CC Power
  Pin perspective < 100
- cli: bench --gpu reports Auto and per-frame upload / readback MB

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:31:31 -04:00
Brandon Thomas 25aebc11d7 Merge main into M13.11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:29:28 -04:00
Brandon Thomas cb516c91c3 M13.11: scriptui concave fills, real images, wasm cfg gating
onDraw fillPath tessellates any path (non-zero winding trapezoid
decomposition: concave, self-intersecting, holes) instead of egui's
convex-only polygon. ScriptUIImage holds a file path or embedded PNG/JPEG
bytes; drawImage, image controls and icon buttons draw the decoded image.
Script-host thread code (host_main, run_threaded, ToHost::Start) is gated
off wasm32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:28:56 -04:00
Brandon Thomas 41f672eef5 M13.14: large-project generator, background compact auto-save
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:59:23 -04:00
Brandon Thomas 0d5f60c4b7 M13.14: agent-interface audit (every command over MCP, control channel and exec --list; automation ids for every widget)
- command.describe / list_commands {schemas} give each command's params JSON Schema;
  MCP describe_command, add_effect, list_effects and get_state (app.capabilities)
- effectcraft-cli exec --list [--schemas]
- headless UI audit: every panel kind's interactive egui widgets are covered by
  automation ids (fixed: timeline navigator, label swatch, bar trim edges, group and
  property name cells)
- command lookup is indexed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:59:23 -04:00
Brandon Thomas dc132bc219 Merge main into M13.11 (bokeh kernel_spans/blur_levels, Essential Graphics as_type controls with mirrors and links)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:54:37 -04:00
Brandon Thomas f35c820c53 Merge main into M13.12
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:51:48 -04:00
Brandon Thomas 5db4bff461 M13.12: docs (pdf README, parity, plug-ins), EPS Type 3 fonts reported
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:51:22 -04:00
Brandon Thomas 2f1788bdf6 M13.12: script socket port validation, resource-string trailing commas, parser and socket edge-case tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:48:14 -04:00
Brandon Thomas c1e79d7bb2 M13.4: engine tests after merge (12 formats; templates name the module)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:44:54 -04:00
Brandon Thomas 88384197f9 M13.12: plug-in ABI hardening (memory limit, manifest length, slider schema) and tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:42:44 -04:00
Brandon Thomas d774d11231 M13.11: essential graphics mirrored and linked properties
Adding a property already in the Essential Graphics panel adds a mirror
(EgKind::Mirror: own name and place, one value in the comp and in every
instance; editing, reverting or pushing either acts on both). A property
control can also drive further properties of the comp (links follow the
main property both ways and take instance values). essential.addMirror,
essential.linkProperty, essential.unlinkProperty; panel badges and menu;
ExtendScript addToMotionGraphicsTemplate(As), canAddToMotionGraphicsTemplate,
motionGraphicsTemplateName, controller count/name and
exportAsMotionGraphicsTemplate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:38:19 -04:00
Brandon Thomas bb24b931a6 M13.11: advanced 3d gpu
Advanced 3D runs render entirely on the GPU: every motion-blur sub-sample
rasterised, resolved and averaged, the depth-based iris depth of field
(Classic 3D bokeh spans, highlight boost, progressive levels), the sRGB
encode and the composite over the GPU canvas (Accelerator::render_3d,
Renderer::prepare_adv_run, adv3d.wgsl). Wireframe outlines draw on the GPU.
Fixes NaN environment lookups for straight up/down normals on Metal.
bench --gpu gains an Advanced 3D comp (--adv3d).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:38:11 -04:00
Brandon Thomas c3532df7ef M13.12: Variable Font Axes animators change advances (text re-spaced)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:34:15 -04:00
Brandon Thomas 454df5d143 Merge main into M13.4 (codec completion)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:24:05 -04:00
Brandon Thomas fdeb146fb2 M13.12: Create Shapes from Vector Layer keeps images as parented footage layers in paint order
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:23:29 -04:00
Brandon Thomas 374d21e6a5 M13.4: opus extreme-input tests, SILK doc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:23:03 -04:00
Brandon Thomas 1906d9d639 M13.4: WebM Opus uses the module's bitrate and application (SILK / hybrid / CELT), low-bitrate export test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:17:57 -04:00
Brandon Thomas 3eeba31c43 M13.4: opus SILK (NB/MB/WB, mid/side stereo) and hybrid (SWB/FB) modes
Application/Mode/Bandwidth API with OpusEncoder::with_application and
with_mode; SILK analysis-by-synthesis encoder mirroring the decoder's
fixed-point reconstruction with noise feedback, LTP, NLSF quantisation and
per-frame rate control; hybrid CELT from band 17 in the shared range coder;
pre-skip 120 in every mode. Oracle tests against filmcraft-opus and ffmpeg
(native opus + libopus).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:16:07 -04:00
Brandon Thomas 9379ba1267 M13.10: clippy (FRAC_1_SQRT_2 in the Colorama test)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:14:38 -04:00
Brandon Thomas 740faf1c1f M13.12: EPS text (show variants, charpath, stringwidth, embedded Type 1/CFF, fallback fonts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:01:10 -04:00
Brandon Thomas 5c54ccd8d0 Merge main into M13.10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:59:35 -04:00
Brandon Thomas 248cf6af3a M13.10: visual editors for Lumetri curves, Colorama output cycle, Glow colour map, Reshape correspondence points; scrolling Preview, AE-style comp tabs; web docs
Effect Controls: Lumetri RGB and hue/saturation curve graphs, Colorama's
palette wheel, Glow's colour map strip and Arbitrary Map curves, Reshape
correspondence buttons and viewer handles (all prop.set, one undo step per
gesture, automation ids). Preview panel scrolls; Composition/Timeline tabs
get close, label swatch and viewer lock; Effect Controls tab the layer
swatch; dropdowns elide long text. Fix the browser start (no default disk
cache folder on wasm32). Browser smoke test covers frame workers, waiting
renders and the Media Browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:59:04 -04:00
Brandon Thomas f309381cd3 M13.9: gpu crate docs, tidy GPU_EFFECTS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:57:33 -04:00
Brandon Thomas d0df520bc1 M13.12: pdf CCITT G3/G4 images, parser robustness tests and panic fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:54:05 -04:00
Brandon Thomas 3d536cfe5a M13.12: pdf /W2 and /DW2 vertical metrics, CMap WMode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:48:25 -04:00
Brandon Thomas d15b69c734 M13.12: pdf knockout/isolated transparency groups, group alpha and masks on group results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:46:41 -04:00
Brandon Thomas 4ce3651723 Merge main into M13.9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:43:44 -04:00
Brandon Thomas b0b8d4c01a M13.12: pdf mesh shadings (types 4-7) and function-based shadings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:42:49 -04:00
Brandon Thomas 05b229825e M13.9: gpu Refine Soft / Hard Matte (single-frame refinement)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:41:11 -04:00
Brandon Thomas 5bb6768766 M13.10: web frame workers fed by project diffs, offloaded Roto Brush propagation, non-blocking wait jobs, browser Media Browser
Viewer frames render in long-lived Web Workers holding a project replica
(engine::remote: structural JSON diff/patch, Mirror, FrameServer); the
frame cache hands jobs to a RemoteFrames renderer (UI-thread fallback).
Roto Brush propagation runs in a job worker and streams segmentations
back (FrameSeg binary form), relayed to frame workers. Control-channel
wait:true commands whose job is offloaded reply when it ends. The Media
Browser browses a pluggable Browser: the file system on the desktop;
browser storage and File System Access folders on the web.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:36:00 -04:00
Brandon Thomas 0fbde2f4ec Merge M13.9 gpu stylize and distort (fx_stylize)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:32:18 -04:00
Brandon Thomas 6e556690ad M13.9: gpu Color Stabilizer apply pass
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:28:27 -04:00
Brandon Thomas 7f6e9c5e41 M13.9: gpu Twirl (Legacy), CC Ripple Pulse, CC Power Pin (full perspective), CC Flo Motion
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:25:22 -04:00
Brandon Thomas 6cbb6bfb93 Merge M13.9 gpu noise, blurs and time (fx_noise)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:21:23 -04:00
Brandon Thomas 7513807972 Merge main into M13.6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:19:31 -04:00
Brandon Thomas 3bf7c4bcd2 M13.9: gpu Shadow/Highlight, CC Color Neutralizer, Invert HLS / YIQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:17:52 -04:00
Brandon Thomas 2e5d35dcee M13.9: gpu stylize and distort (posterize, threshold, CC Threshold / RGB, strobe, CC Vignette, scatter, brush strokes, roughen edges, texturize, motion tile, CC Kaleida, CC RepeTile, mirror, offset, polar, spherize, corner pin, optics compensation, magnify, CC Slant / Smear / Split / Split 2 / Tiler / Griddler, liquify; glow operations and arbitrary map; transform motion blur)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:16:58 -04:00
Brandon Thomas 4afe56cbca M13.9: gpu noise, blurs and time (median, dust & scratches, remove grain, turbulent / fractal noise modes, noise alpha / HLS, smart / bilateral / channel / compound blur, sharpen, unsharp mask, minimax, echo, posterize time)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:15:11 -04:00
Brandon Thomas 7a96fab302 M13.9: gpu colour corrections (fx_tone)
Levels (Individual Controls), Gamma/Pedestal/Gain, Photo Filter, Change Color,
Change to Color, Leave Color, Broadcast Colors, Color Balance (HLS), Video
Limiter, PS Arbitrary Map, CC Toner, CC Color Offset, CC Kernel, Auto Levels /
Contrast / Color and Equalize (histograms measured on the CPU, applied on the
GPU) on the GPU; Hue/Saturation colour ranges and Levels' channel controls no
longer fall back to the CPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:13:58 -04:00
Brandon Thomas 417ddd1d89 Merge branch 'worktree-agent-ac0d75bd55dd48956'
# Conflicts:
#	docs/parity.md
2026-10-03 08:09:29 -04:00
Brandon Thomas 597c6162c4 Merge main into M13.6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:03:27 -04:00
Brandon Thomas 8ba936f87b M13.9: gpu keying, matte and channel effects (fx_key)
Color Key, Luma Key, Color Range, Extract, Difference Matte, Color Difference
Key, Screen Key, Spill Suppressor, Advanced Spill Suppressor, Key Cleaner,
Unmult, Simple Choker, Matte Choker, Set Matte, Set Channels, Shift Channels,
Remove Color Matting, Arithmetic, Solid Composite, Channel Combiner, Blend,
Calculations, Compound Arithmetic on the GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:02:03 -04:00
Brandon Thomas cbf9e40528 Merge main into M13.7 2026-10-03 07:55:24 -04:00
Brandon Thomas 0698b23cd7 M13.7: Save Frame As Photoshop Layers / ProEXR, Watch Folder, Vanishing Point (disabled), Create Nulls From Paths, VR Comp Editor, Help tutorials
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:55:04 -04:00
Brandon Thomas 404b855986 Merge main into M13.6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:53:31 -04:00
Brandon Thomas 47f61debcb M13.9: gpu effect family scaffolding (fx_key, fx_stylize, fx_noise)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:51:33 -04:00
Brandon Thomas 6cb90c2091 Merge branch 'main' into worktree-agent-a56a28d82c228c05a 2026-10-03 07:47:59 -04:00
Brandon Thomas 07da394ccb M13.8: Advanced 3D iris depth of field, collapsed precomps, extruded strokes
Depth of field in Advanced 3D comps uses the camera's iris (Fast Rectangle, Triangle to
Decagon, rotation, roundness, aspect, diffraction fringe) and highlight gain/threshold/
saturation through the Classic 3D bokeh kernel. Collapsed precomps in an Advanced 3D comp
add their nested layers as real geometry (meshes, extrusions, cards) lit by the parent;
2D collapsed precomps draw nested 3D layers with the parent's renderer. Text and shape
strokes extrude as bevelled meshes stacked in paint order (model extrude_stacked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:47:25 -04:00
Brandon Thomas a2693c9d15 M13.6: Classic 3D bokeh depth of field on the GPU (WGSL); 3D-matte GPU check
Planes prepared for the GPU carry their depth of field (padding, radius map, levels) and the
bokeh_boost / bokeh_prefix / bokeh_gather kernels blur them where they are drawn, with the CPU's
spans; GPU-vs-CPU agreement tests for iris shapes, highlights, fringe, Fast Rectangle, tilted
planes and a 2D layer with a 3D track matte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:44:43 -04:00
Brandon Thomas 553a51316c M13.8: Preview panel shortcuts and Align panel commands
Preview panel: five shortcuts (Spacebar, Shift+Spacebar, Numpad 0, Shift+Numpad 0,
Alt+Numpad 0) each with saved options (include video/audio/overlays/layer controls, loop,
cache before playback, range incl. play around current time, play from, frame rate, skip,
resolution, full screen, play cached frames, move time to preview time), persisted in
prefs; playback.settings.get/set; the playback loop follows the preview plan.

Align panel: layer.align {edge, to} and layer.distribute {mode} engine commands (bounds
through transform and parents, one undo step); Align Layers to dropdown and Distribute row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:38:46 -04:00
Brandon Thomas f89af341a6 M13.6: variable font axes in the character style
CharStyle/TextDoc variations (layer.setText variations), shaped with harfrust instances so
advances follow HVAR/gvar, outlines drawn at the design-space position, animator deltas relative
to the style's values, Character panel Variable Font Axes fields (character.axis.<tag>).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:36:53 -04:00
Brandon Thomas a30cf38180 Merge main into M13.4 (codec completion): HEVC/AV1 on the export pipeline (Cx), one key-frame interval for all movie codecs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:29:32 -04:00
Brandon Thomas 225f31bf50 Merge main into M9.13 (GPU kernels defer CPU-only controls via catalog::gpu_supported)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:29:30 -04:00
Brandon Thomas 1772250fc4 Merge main into M13.7 2026-10-03 07:23:25 -04:00
Brandon Thomas 558de71720 M13.7: Essential Graphics scripting hooks, Font and uniform Scale controls, Open in Essential Graphics
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:23:22 -04:00
Brandon Thomas 7b0a449c5f M13.6: PSD smart objects apply perspective quads and placed-layer warps
nonAffineTransform and the warp descriptor (named styles with bend / distortion, custom
envelope quilt meshes via ObAr meshPoints) are baked as placed over the placed bounds
(SourceLayer::placed), at up to 4x resolution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:21:00 -04:00
Brandon Thomas 70e7800b74 M9.13: VR Converter Facebook / EAC cube maps and FullDome fisheye, VR Digital Glitch full controls; PARTIAL list empty
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:15:36 -04:00
Brandon Thomas bfc81ef34a M13.4: av1enc README wording
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:13:05 -04:00
Brandon Thomas 04e2e6b898 M13.4: av1enc SATD intra mode ranking, intra benches
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:12:59 -04:00
Brandon Thomas 7d944ca494 M13.6: PDF/AI text, images, tiling patterns, blend modes, soft masks, pages; clip groups to masks / Merge Paths
Text with embedded TrueType, CFF (Type1C / CIDFontType0C / OpenType), Type 1 and Type 3 fonts,
standard-14 fallback to the bundled OFL fonts; Flate / DCT / inline / stencil images with SMask,
Indexed and ICC alternates; luminosity / alpha soft masks and the 16 blend modes in the SVG
render tree; tiling patterns; calculator functions; file.import page and Footage::page;
Create Shapes from Vector Layer keeps clips (page clips as layer masks, nested clips as Merge
Paths Intersect).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:49:36 -04:00
Brandon Thomas 5aae82b05c M13.7: ScriptUI resource strings, onDraw graphics, live onChanging, Socket
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:49:05 -04:00
Brandon Thomas 863ff22dc4 M9.13: Caustics Sky group and light types, Wave World wireframe controls, ground and dry areas
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:48:50 -04:00
Brandon Thomas 88ca7b45fc M13.4: av1enc speedups (vectorisable forward transform, separable sub-sample paths, cheaper loop filter search), more oracle cases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:40:03 -04:00
Brandon Thomas aae00dfaca M9.13: Add / Match / Remove Grain preview region, presets, channel balance, sampling and temporal filtering
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:39:06 -04:00