Commit Graph
98 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 11848da0a1 App: only a held lock means another launch is starting (#283 follow-up)
A filesystem without lock support returned an error from try_lock, which
read as 'held' and disabled crash detection for good.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 00:04:47 -04:00
Brandon Thomas 5d5da086c8 Merge pull request #283 from prekabreki/fix/launch-guard-concurrent-and-gl-stuck
App: a second copy opening, or OpenGL that fails, no longer strands Window Graphics
2026-10-08 23:51:22 -04:00
Brandon Thomas 6c838325fa Merge pull request #280 from jonassneu/fix/mcp-autosave
MCP: preserve unsaved work across server restarts (#258)

# Conflicts:
#	apps/effectcraft-cli/tests/cli.rs
#	contributors/people.toml
2026-10-08 23:51:21 -04:00
Oldrich333andClaude Sonnet 5.5 a560830b64 CLI: render --out is relative to the working directory (#300)
`effectcraft-cli render --out proj/out/d.mp4 --project proj/x.ecproj` wrote
`proj/proj/out/d.mp4`: the Render Queue resolves a relative output against its own base, while
`--project` and every other CLI path are relative to the working directory. The CLI now makes
`--out` absolute against the working directory before queueing it, so the file lands where the
shell user expects; an absolute path is unchanged, and `rendered[].output` in the JSON result is
the absolute path. Render Queue outputs set inside a project (and `render --queue`) resolve as
before: tokens expanded, then `export.defaultOutputFolder`, the project's folder, the working
directory.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-08 20:32:51 +00:00
prekabrekiandClaude Opus 5.5 384c8cc094 App: a second copy opening, or OpenGL that fails, no longer strands Window Graphics
Two copies opened close together made the second take the first's
launch-pending marker for a crash and switch Window Graphics to OpenGL.
Where OpenGL can't open a window (NVIDIA on X11: "gl not compatible with
provided surface"), every later launch then failed, with no way to reach
the setting.

The marker is now locked while its launch runs, so a locked marker is a
launch still opening, not a crash. And a window that fails to open on
OpenGL puts Window Graphics back to Automatic.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 15:03:19 +02:00
jonassneu 9d9f8ecbb4 Fix #258: preserve unsaved headless MCP work with opt-in checkpoints 2026-10-08 14:38:24 +02:00
bflatasticandClaude Opus 5.5 4eb69e7577 Render: render.backend says why it renders on the CPU; mcp --gpu documented (#262)
get_project / render.backend reported "gpuAcceleration": true with
"active": "cpu" and no reason, which read as a broken GPU. The
headless MCP server only attaches the GPU compositor when started with
--gpu, which docs/agents.md didn't mention, and a failed --gpu said
only "no usable GPU adapter" (the adapter's error went to a log no
one sees). The session now keeps the host's reason there is no GPU
compositor (accel_note): the CLI's "headless: renders on the CPU
unless started with --gpu", or the desktop app's compositor setup or
retirement error. The status reports it as `why` whenever it renders
on the CPU (or that the project's renderer is Software Only). --gpu
failures include the adapter's error (Gpu::try_headless).
docs/agents.md documents `mcp --gpu`.

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

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

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

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

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

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:32:16 +07:00
bflatasticandClaude Opus 5.5 db3b34b134 Web: a lost graphics device says so and offers a reload (#225)
When the page's WebGPU device was lost, the canvas kept showing its
last frame: edits still went through and rendered frames were right,
but the editor never showed them and nothing said why (the status
message about it is drawn in the canvas that can't present any more).
The page now covers the canvas with a notice that offers Save Project
(a download) and Reload. Reload first writes pending changes to
browser storage, so the session snapshot brings the project and its
unsaved changes back; if that write fails it says so and offers
Reload Anyway. Without a kept session (?empty, memory storage) the
notice says to save first.

Test: apps/effectcraft-web/tests/page.mjs (Node, fake DOM).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 12:38:11 +07:00
bflatasticandClaude Opus 5.5 82a1862fa1 Web: Media Browser reports a picked file it can't read (#226)
Add Files... and Upload Folder... read every picked file in one go and
rejected on the first file the browser couldn't read; the Media
Browser dropped that rejection, so nothing was added and nothing said
why. Each file is now read on its own: the readable ones are added and
the status bar names the unreadable ones with the browser's reason and
suggests picking them again. Cancelling the picker is not reported.

Test: apps/effectcraft-web/tests/page.mjs (Node, fake DOM).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 12:38:11 +07:00
Claude 1ca78821b9 Web: a failed storage write is retried and flush() rejects (#215)
A write to browser storage that failed (quota exceeded) was dropped:
effectcraft.flush() resolved anyway, nothing retried it, and a reload
restored the project from before the change. The failed key now stays
pending and is written again after 2 s (doubling to a minute while it
keeps failing; a newer change of the key goes with it), and flush()
rejects with the error.

Test: failed_writes_retry (store). The flush itself runs only in the
browser.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018gnTnH3Jmk5zr4tMq1Mc9f
2026-10-07 22:33:04 +00:00
Claude 72b935fe26 Web: a job worker that fails to start fails its job (#216)
worker.js left a rejected start (its async init) unhandled, which a
page's Worker error event doesn't see: the page waited for "ready"
forever and the render stayed pending until cancelled. The failed start
is now re-raised where the page's onerror sees it, like the other
worker failures; the job fails with the error and the worker is
terminated.

Test: apps/effectcraft-web/tests/workers.mjs (worker.js with a stub
module whose init rejects; host.js with a worker whose start fails).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018gnTnH3Jmk5zr4tMq1Mc9f
2026-10-07 22:33:04 +00:00
Claude a2f22bb35b Web: a replaced file goes to reused workers again, even at the same size (#218)
Job workers remembered the files they were sent by path and byte
length, so footage replaced by different bytes of the same length at
the same path was never resent: a Render Queue export through a reused
worker rendered the old image while the page showed the new one.
Frame workers remembered paths only, so they never got a replacement
at all.

Every write of a stored file now gets a version, and both kinds of
worker remember the version they were sent.

Tests: versions_change_with_every_write (store) and
apps/effectcraft-web/tests/workers.mjs (Node, fake Worker: the
replacement is sent once, an unchanged file not again).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018gnTnH3Jmk5zr4tMq1Mc9f
2026-10-07 22:33:04 +00:00
Claude 188cc06d2b App: the window's GPU device asks for what the adapter has (#198)
eframe's default device request asks for wgpu's default limits (8
storage buffers per stage and more). An older GPU such as Intel HD
Graphics 5500 can't meet them, so the device request failed before
EffectCraft ran, and the release build on Windows quit without a word.

The device now asks for the adapter's own limits (textures capped at
16384 px like the headless renderer). The compositor already checks
what it needs and composites on the CPU when the device can't run it.
If the window still can't open, a message dialog says why instead of
the app vanishing.

Test: device_limits_fit_the_adapter. Not checked on that GPU.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018gnTnH3Jmk5zr4tMq1Mc9f
2026-10-07 22:13:19 +00:00
Claude 24aed7f219 App: launching opens an empty project, not the demo (#204)
The desktop app opened the demo project whenever it wasn't given a
project file, so File ▸ New Composition on the Home screen added "Comp
4" to the three demo comps. As in After Effects, a launch now starts
with an empty Untitled Project. --demo opens the demo (Help ▸ Open
Demo Project and the Home screen tile still do); --empty is kept.

The arguments are parsed by a function now, with a test
(no_arguments_start_an_empty_project).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018gnTnH3Jmk5zr4tMq1Mc9f
2026-10-07 22:13:19 +00:00
BFlatandClaude Opus 5.5 f2b2fa811c Render Queue: clicking the Output To file name opens a save dialog (#192)
Clicking an item's Output To file name opened an inline editor on the
name template (`[compName].[fileExtension]`), so there was no way to
pick a folder from there. Choose File… in the menu beside it did open
a dialog, but the project save dialog: filtered to .ecproj and opening
wherever the OS chose.

As After Effects' Output Movie To, clicking the file name now opens a
save dialog at the current output (its folder and name) for the output
format's extension, and the chosen file becomes the output. Choose
File… opens the same dialog. Alt-click still edits the template in
place, and hosts without a save dialog (the web) keep the inline
editor. The desktop `pick_save_file` hook opens in the folder of a
default path.

Test: clicking_the_output_file_name_opens_the_save_dialog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 20:29:29 +07:00
bflatasticandClaude Opus 5.5 3aaa840471 Scripting: script.run reports .jsxbin scripts as unsupported (#176)
`effectcraft-cli script FILE` passes the file's text to script.run, which
ran a compiled .jsxbin script as JavaScript and failed with "SyntaxError:
unexpected '@'" at 1:1, while file.runScript already says .jsxbin isn't
supported.

script.run now refuses a script named `.jsxbin` or whose text starts with
the `@JSXBIN@` header, with file.runScript's message (one shared
constant). The check is in the command, so the CLI with or without
--bridge, MCP run_script and the Script Console all report it.

Tests: engine_entry_points (by header and by name), script_file_and_eval
(the CLI exits 1 with the message and no SyntaxError).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 15:48:17 +07:00
bflatasticandClaude Opus 5.5 50f26e6a47 CLI: unknown options are usage errors (#168)
Args::parse kept any unrecognised `--option` as a valueless flag nobody
read, so `exec comp.new --bogus` ran with defaults and exit 0, and a
misspelt valued option left its value behind as a positional:
`--saveas z.ecproj` opened z.ecproj as the project and failed with an
unrelated "cannot read" error.

Options are now checked against the declared lists (VALUED and the new
FLAGS) before anything runs; unknown ones are a usage error naming them
(exit 2), and `--flag=value` on a flag says it takes no value.
render-frame's `--transparent` (the MCP render_frame option) was one of
the silently ignored options; it now keeps the frame's alpha.

Tests: unknown_options_are_refused, every_option_read_is_declared (every
option the code reads is declared, so none is refused),
unknown_options_are_usage_errors (exit 2, nothing runs or renders, and
--transparent renders alpha).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 15:48:17 +07:00
bflatasticandClaude Opus 5.5 2dda81c9bc CLI: a reader that closes stdout early is not a crash (#167)
The CLI printed with println!/eprintln!, which panic when the stream is
gone, so `exec --list | head` (or any output larger than the pipe buffer)
ended in a broken-pipe panic with exit status 101.

Output now goes through say!/note!, which never panic. Once stdout fails,
later output is dropped but the command still finishes its work (a
`run ... --save` sequence isn't cut short); a closed pipe is quiet and
exits 0, any other stdout error is reported and makes the exit status 1.
The MCP server ends quietly when its client closes stdout.

Test: a_closed_stdout_is_not_a_crash (info --json, exec --list, a saving
run sequence and mcp with stdout closed before they write).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 15:48:17 +07:00
Luskish 2fc7da7d29 Control: close the connection on input that isn't a request
A line that is not a JSON object with a string `method` (an HTTP request
line, other non-JSON text, a JSON array, invalid UTF-8, a line over 4 MiB)
now gets one error reply and the connection is closed, so a browser's
cross-origin fetch can't smuggle a command in its body. Connections are
capped at 16. Same transport as LightCraft (storytold/lightcraft#129) and
FilmCraft (storytold/filmcraft#123).
2026-10-06 22:54:21 -04:00
Dexter 94dbccf69f fix: contain GPU initialization and retire failed readbacks 2026-10-06 16:40:12 -05:00
BFlat c750b299cf Merge pull request #118 from storytold/claude/system-fonts-rmnw4m
G2: font menus list the installed fonts
2026-10-07 00:37:13 +07:00
Claude dda9961889 G2: font menus list the installed fonts
A Windows user reported that the Properties panel's font menu offered only the bundled Inter,
JetBrains Mono and Noto Serif. The system font folders were scanned only when a project asked for
a family the bundled fonts lack, so until then every font menu (Properties, Character, Essential
Graphics) and `file.findMissing {what: fonts}` saw the bundled families alone, on every
platform. Listing the families now scans the system fonts first, and the desktop app starts that
scan on a background thread at launch so the menus are complete when first opened.

The style menus in the Properties and Character panels list the chosen family's own styles
instead of a fixed Regular / Medium / SemiBold / Bold / Italic. Both menus build their lists only
while open.

New query for agents: `text.fonts {query?, rescan?}` (MCP tool `list_fonts`) lists every family
with its styles, origin (bundled / system / user) and own-language name; `rescan` picks up fonts
installed since launch.

Tests: a fresh-process test that the family list includes every family in the platform's font
folders without a prior scan (failed before), and `text.fonts` filtering, rescan and per-family
styles, and the `list_fonts` MCP tool.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019EjTYdt76hTG4Kh2CJFnCr
2026-10-06 15:14:55 +00:00
Brandon Thomas 633fbcfaeb Merge PR #71: Persist Japanese menu language in General settings 2026-10-06 08:42:00 -04:00
Brandon Thomas 98872a4690 Merge PR #95: Improve AE-style editing, easing fidelity and Windows GPU rendering 2026-10-06 08:41:33 -04:00
Brandon ThomasandClaude Opus 5.5 17b17bcce3 Windows packaging: Start Menu/Add-Remove icon, no console window, correct description
The MSI Icon id had no .ico/.exe extension, which Windows Installer needs for
advertised shortcuts and ARPPRODUCTICON, so both showed a blank icon. Release
builds of the desktop app are now GUI-subsystem (no console window; the CLI stays
a console program), the shortcut/summary description names what EffectCraft does, and
packaging-lint rejects an Icon id without an extension. Found in printcraft#57.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 07:51:06 -04:00
midasdf 25233abbcb G9: persist Japanese menu language without bundled fonts 2026-10-06 16:22:23 +09:00
Dexter 39e6907b5f G2: improve editing reliability and Windows rendering fidelity 2026-10-06 00:00:31 -05:00
bflatasticandClaude Opus 5.5 b424c321dc Fix #43: the AppImage's window shows the EffectCraft icon on Wayland
Wayland compositors take a window's icon from the desktop entry named by its app ID; the .deb,
.rpm and Flatpak install it, an AppImage installs nothing, so its window got the generic icon.
Started from an AppImage ($APPIMAGE), the app now writes a hidden (NoDisplay) desktop entry
pointing at it, and its 256 px icon, under $XDG_DATA_HOME (~/.local/share) before the window
opens. Nothing is written when an installed package or the user's own entry provides the ID;
the entry follows the AppImage when it moves; failures are only logged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:31:06 +07:00
bflatasticandClaude Opus 5.5 0286a49f0a Fix #68: open links in the system browser (Discord buttons, Help menu, Settings)
The desktop app built eframe without its `links` feature, so egui's open_url was a no-op
on native: the header and Home Discord buttons, Help menu links and Settings links did nothing.
Enabling it links the `webbrowser` crate (MIT OR Apache-2.0), which opens URLs with the
system browser on macOS, Windows and Linux.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:26:29 +07:00
bflatasticandClaude Opus 5.5 7579de21c6 M13.35: Roto Brush trained model — swappable segment module, MobileSAM in pure Rust
- New L1 crate effectcraft-segment: the MaskModel interface (prompts → foreground probability),
  a registry of open-licensed models (licence must be MIT/Apache/BSD/…; URL, size, SHA-256),
  a PyTorch checkpoint reader (zip + pickle subset), dependency-free SHA-256, and MobileSAM
  (TinyViT-5M + SAM prompt encoder / two-way transformer / mask decoder) on rayon and ndarray's
  safe GEMM. Loads the official mobile_sam.pt as published; checked against candle
  (embedding 0.3% RMS, 99.9% equal mask pixels); ~0.7 s/frame encoder at 32 threads, cached
  per frame, ~0.1 s decoder.
- track::roto: segment_with / propagate_with prompt a model with the strokes or the flow-warped
  matte (box, core points, mask); its probability is a strong, unblurred prior for the same
  graph cut (strokes stay hard constraints); a failing model or one disagreeing with the flow
  (IoU < 0.5) is ignored. Prior gets a weight.
- effects::roto: Version 2.0 / 3.0 use the model chosen in Settings (set_model / model_for);
  the model id is part of the chain seed.
- engine: roto_models (roto.models, roto.model.select / install / download / remove), weights in
  the models folder (host::config_dir()/models, shared by the app, CLI and MCP), background
  loading, downloads through the system curl, verified; Settings ▸ Roto Brush page (prefs
  roto.model). host::config_dir moved from the desktop app.
- Tests: segment (kernels vs direct sums, checkpoint reader, SHA-256 vectors, registry), track
  (fake model: prior shapes the cut, failing / disagreeing models ignored; real weights with
  EFFECTCRAFT_MOBILESAM: base IoU 0.989, propagated ≥ 0.980 vs classic 0.973), engine (registry,
  verification, selection; real install + load), MCP.
- Docs: README, ROADMAP, parity (MSK-4 done: 90/2/0), preferences, agents, architecture,
  crates/segment/README.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:55:02 +07:00
bflatasticandClaude Opus 5.5 a12d4ae1fa M3.9: a browser session restored with unsaved changes stays modified
The web app reopens the last visit's project and, when it had unsaved changes, marked it
modified with `bump()`. Since M3.9 the modified mark follows the saved snapshot, and a bump on
the very project just opened counts as "back at the saved state", so restored changes looked
saved (no `*`, no unsaved-changes prompt). `Session::mark_unsaved` forgets the saved snapshot
instead: the project stays modified until saved, whatever is undone. Regression test included.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:33:40 +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 ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-04 14:52:39 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-04 13:27:33 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-04 03:40:46 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-04 03:15:54 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-04 02:57:17 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-03 18:35:03 -04: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 ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-03 12:30:11 -04:00
Brandon Thomas 9904525591 Merge main into M13.14 2026-10-03 11:50:14 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
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 ThomasandClaude Opus 5.5 ea292a6e81 M13.13: fuse 8/16 bpc quantise into the layer composite; steady-state bench traffic
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 10:44:52 -04:00
Brandon ThomasandClaude Opus 5.5 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 <[email protected]>
2026-10-03 10:31:31 -04:00
Brandon ThomasandClaude Opus 5.5 25aebc11d7 Merge main into M13.11
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 10:29:28 -04:00