Commit Graph

131 Commits

Author SHA1 Message Date
bflatastic 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 <noreply@anthropic.com>
2026-10-07 15:48:17 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-07 15:48:17 +07:00
bflatastic 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 <noreply@anthropic.com>
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) <noreply@anthropic.com>
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 Thomas 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 <noreply@anthropic.com>
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
bflatastic 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 <noreply@anthropic.com>
2026-10-06 00:31:06 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-05 23:26:29 +07:00
bflatastic 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 <noreply@anthropic.com>
2026-10-05 17:55:02 +07:00
bflatastic 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 <noreply@anthropic.com>
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 280f7d509c M13.4: HEVC / AV1 output modules (MP4 hvc1/av01, WebM AV1), codec + Opus options in engine, UI, CLI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:01:43 -04:00
Brandon Thomas 47d2da04de Merge main into M13.1 (scripts in File ▸ Scripts / Window via @dynamic menu nodes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:27:09 -04:00
Brandon Thomas 00eb951711 M13.1: ScriptUI frontend (dialogs, palettes, dockable panels), script menus, example plug-in, docs
- ui-egui: scriptui_view draws script windows at engine layout bounds; PanelKind::ScriptPanel; File ▸ Scripts and Window menu list scripts/panels (egui + native menus)
- examples/plugins/posterize-bands (WebAssembly, API v1); host plug-in tests; desktop loads <config>/Plug-ins
- MCP: script_ui and history tools; docs/plugins.md, agents.md, parity, architecture, ATTRIBUTION

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:24:01 -04:00
Brandon Thomas 89c9b1b6ee M13.1: branching history, puppet pin recording, WebAssembly plug-in API, ScriptUI model and hosts, File ▸ Scripts, deterministic render test
- engine: History keeps undone lines as branches; edit.history.list/goto; History panel tree; MCP history tool
- puppet.recordPin / puppet.recordOptions; viewer Cmd-drag records; Record Options form
- effects::plugin (EffectPlugin trait, register_plugin, API v1); effectcraft-plugin (wasmi, sandboxed, fuel-limited); effect.plugins.load/list
- ScriptUI: engine scriptui model + layout + scriptui.* commands; script crate scriptui.js and script-host threads (dialogs block show())
- File ▸ Scripts: install script / ScriptUI panel, sample scripts, window.scriptPanel
- host test: bit-identical renders across runs and thread counts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:07:20 -04:00
Brandon Thomas 871362ba96 Merge branch 'worktree-agent-aba044eaf08eded94'
# Conflicts:
#	crates/render/src/three_d/compose.rs
2026-10-03 03:33:36 -04:00
Brandon Thomas b17d2c05e6 Merge main into M14.5 (settings and menus)
Reconciled with main's keyframe labels (same Keyframe::label field; keys.setLabel and
keys.selectLabelGroup from main are kept, M14.5's duplicates removed; Edit ▸ Label on
selected keys delegates to the label helper), proxies (RenderOpts::proxy next to
nested_switches/draft_shadows), offloaded renders (Work carries nested_switches), wasm
audio pump (preview mix rate), variable font axes and mask feather points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:11:45 -04:00
Brandon Thomas c4a8c141b5 M12.7: bench --gpu adds an adjustment-layer comp, speedup and GPU-vs-CPU agreement columns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:59:57 -04:00
Brandon Thomas f855a4b55b M14.5: wire the remaining settings; keyframe assistants and labels; AE menu entries
Settings: 56 of 59 not-yet-wired settings now change behaviour (nested-comp switches,
constant vertex count, related-item time sync, match-name pick whip, split-layer order,
Home on open, motion path span, internal wireframes, zoom quality, label-coloured tabs,
mask colour cycling, gradients, grid/guide styles, text engine, font preview/recent/native
names, missing frames, unlabeled alpha, drag import, default output folder, segmenting,
bit depth names, preview sample rate, disk/media/conformed-audio caches, RAM reserve and
low-memory reduction, Video Preview window, 3D reference axes/extended viewer/draft
shadows, script file gate + File.execute warning, expression editor options and error
banner). Three stay display-only and are documented.

Features: Convert Audio to Keyframes, RPF Camera Import (JSON/CSV camera data),
keyframe colour labels + Select Keyframe Label Group, Recent Animation Presets.
Menus: Edit > History, File > Import Recent Footage, Replace Footage > With Layered
Comp, Save a Copy As XML (.ecprojx, opens again), Composition > VR (Create VR
Environment, Extract Cubemap), Assign Shortcut to 3D view / workspace, Window viewer
labels + open viewers, Help > System Compatibility Report / Enable Logging / Reveal
Logging File. Dynamic menu nodes (@dynamic:…) shared by the egui and native menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:45:44 -04:00
Brandon Thomas aafe46e091 M12.7: Classic 3D runs on the GPU (per-pixel depth sort, lights, ray-cast shadows, DOF); Auto sends 3D comps to the GPU
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:15:01 -04:00
Brandon Thomas 4fffbace5c M15.2: merge main (disk cache, 3D Camera Tracker); camera analysis in workers; no disk cache on wasm32
The 3D Camera Tracker analysis joins the offloaded jobs. The disk cache stays off in the
browser (std::env::temp_dir panics on wasm32 once a config store exists).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:03:43 -04:00
Brandon Thomas 0bee1ab2da M15.2: web app depth (WEB-1): browser storage, Web Audio, worker renders, WebGPU viewer, PWA
- Persistence: settings, shortcuts, recent projects, imported media, saved projects and
  auto-saves live in OPFS (IndexedDB fallback) through an in-memory mirror flushed in the
  background (apps/effectcraft-web/src/store.rs, persist.rs); ConfigStore gains FileOps so
  auto-save / recovery work in the browser store; the session snapshot restores the last
  project + editor state on reload; File > Open Recent works from storage.
- Audio: Web Audio output (AudioWorklet, ScriptProcessor fallback) fed by AudioPlayback::pump
  on wasm32; A/V sync from AudioContext.currentTime; meters; unlock on first gesture.
- Background jobs: engine::offload (WorkerRequest/WorkerReply protocol, Offload trait,
  run_request); Render Queue, tracker, mask tracker, Warp Stabilizer and Roto Freeze run in Web
  Workers (a second engine instance on the shared compiled module), progress streamed back;
  cancel terminates the worker.
- GPU: WebGPU viewer frames drawn straight from the compositor's texture (gpu_display on
  wasm32 when the viewer draws plainly); async readback (Gpu::read_display_async) for pixels.
- PWA: manifest, service worker precaching the build (xtask fills list/version), icons copied
  from assets/app-icon; COOP/COEP kept from cached responses; offline reload works.
- window.effectcraft: saveToBrowser, listStored, removeStored, flush; info() reports storage,
  audio, gpu, workers.
- Tests: native store + protocol + offload (render, track, cancel) tests; smoke.mjs extended
  (GPU path, worker render responsiveness, audio gesture + playback, reload persistence,
  Open Recent, offline reload).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:42:00 -04:00
Brandon Thomas 0631ef29dc Merge branch 'worktree-agent-ae879257b39393c25'
# Conflicts:
#	docs/parity.md
2026-10-02 22:56:18 -04:00
Brandon Thomas 36683d95ee Merge main into IO-2/SHP-5/OUT-2/PRV-2 work
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:39:47 -04:00
Brandon Thomas 8f4546fca4 Merge branch 'main' into worktree-agent-af1868dabbf03697e
# Conflicts:
#	docs/parity.md
2026-10-02 22:36:47 -04:00
Brandon Thomas 406706e3d5 OUT-2: WebM export (VP9 intra encoder + alpha, Opus), WAV/AIFF audio-only outputs
- effectcraft-vp9enc (L0): VP9 profile 0 key-frame encoder from the VP9 bitstream spec
  (8x8 intra blocks, DC/V/H/TM, 4x4 DCT/ADST, lossless WHT at quality 100, tile columns);
  round-trips exactly through FilmCraft's VP9 decoder and ffmpeg.
- export: WebM muxer (EBML/Matroska spec) with VP9, VP9 alpha in BlockAdditional
  (AlphaMode 1), Opus (CodecDelay = pre-skip); WAV and AIFF outputs.
- OutputFormat WebM/Wav/Aiff in the Render Queue, renderQueue.add formats, CLI --format.
- Tests: FilmCraft decode-back (frames, size, pixels, audio RMS), ffprobe/ffmpeg oracles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:28:15 -04:00
Brandon Thomas 8404fead60 UI-8, UI-3: native macOS menu bar; timeline columns, search, reveal-add, UU, CTI snapping
Native menu bar (apps/effectcraft, muda, macOS only): the engine's After Effects
menu tree mirrored into NSMenu through a pure, serializable spec
(ui-egui native_menu: submenus, separators, live labels, check marks, enabled
state, Cmd/Ctrl/function-key shortcuts as accelerators, Open Recent). Hide /
Hide Others / Show All are macOS' predefined items (app.hideOthers/app.showAll
are real commands now, via NSApplication); Services added; About, Settings and
Quit dispatch our commands. State is pushed only when the session revision,
selection, history, settings or journal change; structural changes rebuild.
Activations dispatch the same command ids as the in-window bar; Edit
Cut/Copy/Paste/Select All/Undo/Redo act on a focused text field. Settings >
Appearance > Use In-Window Menu Bar on macOS switches back.

Timeline: column visibility from the column header's context menu (A/V
Features, Keys, Label, #, Comment, Switches, Modes, Parent & Link, In, Out,
Duration, Stretch; Source/Layer Name toggle), Comment editing
(layer.setComment), draggable In/Out/Duration cells, Stretch opens Time
Stretch; property search filters layers and reveals matching properties with
their groups; Shift+P/S/R/T/A/E/M/F/L/U add to the revealed properties; UU
reveals all modified properties; Shift-drag in the ruler snaps the CTI to keys,
in/out points, markers and the work area. Commands: timeline.column,
timeline.sourceName, timeline.search, timeline.revealAdd.*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:21:08 -04:00
Brandon Thomas 78cd313d3c M14.4: scripting with an After Effects-style JavaScript object model
New crate effectcraft-script (L4): boa runtime with app/Project/CompItem/
Layer/PropertyGroup/Property/TextDocument/MarkerValue/RenderQueue/File
object model over engine commands (undoable, undo groups), AE match-name
table, file/network security gate. Entry points: script.run command,
File > Scripts > Run Script File (.jsx/.js), Window > Script Console,
effectcraft-cli script, MCP run_script. Engine: project.delete/duplicate,
prop.renameGroup/setGroupEnabled/removeGroup/duplicateGroup/moveGroup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:13:09 -04:00
Brandon Thomas 4bcf1f447c Merge landed EffectCraft work (waves 1-3 tail: anim tools/markers/docking, tracking core, GPU compositor, bpc perf, parity docs, tests)
Restores commits dropped from local main by a reset to origin/main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	xtask/src/main.rs
#	xtask/src/version.rs
2026-10-02 20:30:28 -04:00
Brandon Thomas 935cf691d1 Release fixes: CLI --version, ALSA on Linux, web build via xtask, xtask version
- effectcraft-cli: support --version/-V/version (the packaging smoke tests call it).
- Linux release job installs libasound2-dev for alsa-sys; deb/rpm depend on
  libasound2/alsa-lib; Flatpak gets --socket=pulseaudio.
- packaging/web/package.sh builds with `cargo xtask web` (wasm-bindgen) and zips
  <target>/web/dist; the release job installs wasm-bindgen-cli 0.2.129.
- xtask: version.rs (ported from PhotoCraft) called helpers that don't exist here,
  so xtask didn't compile; use the local run() and wire up `cargo xtask version`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:19:03 -04:00
Brandon Thomas 485f562e19 M12.2: gpu compositor and gpu effects
New L3 crate effectcraft-gpu: a wgpu compute backend for the per-frame
compositing walk (layer transforms with the CPU's nearest/bilinear/bicubic
sampling and minification pre-filter, motion-blur accumulation, track
mattes, Preserve Transparency, layer style passes and knockout, all 38
blend modes as a WGSL port of color::blend, 8/16 bpc clamp+quantise,
colour-space conversions). 3D runs, adjustment and wireframe layers run on
the CPU between GPU steps. GPU effects with the CPU semantics: Gaussian,
Fast Box and Directional Blur, Glow, Levels, Curves, Hue/Saturation, Tint,
Fill, Gradient Ramp, Fractal Noise, Drop Shadow, Brightness & Contrast,
Exposure, Invert, Transform (registry gpu badge from effects::GPU_EFFECTS).

render: Backend (Cpu/Gpu/Auto) in RenderOpts, the Accelerator trait and
Renderer::accel, GPU effect chains in the effect stack, hooks for the GPU
walk. Project setting gpu_acceleration (Mercury GPU Acceleration / Software
Only), render.backend command, file.projectSettings renderer. Session and
Render Queue exports carry the accelerator. CLI --gpu and bench --gpu.

Tests compare GPU vs CPU renders (blend modes x bit depths, transforms,
masks, mattes, motion blur, colour management, styles, collapse,
adjustment fallback, every GPU effect) and skip without an adapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:59:08 -04:00
Brandon Thomas 3411309d6d app icon: unicorn (owner's ArtCraft drawing), Dock/taskbar/launcher wiring, palette docs
- assets/app-icon/: master and light SVGs, 1024 px PNG, .icns (Apple grid), .ico (16-256),
  hicolor 16-512 + scalable, LICENSE.txt, README with palette (#e0368f / #efe9dc / #0b0b0c),
  tile geometry and provenance; attribution sidecars + ATTRIBUTION.md rows.
- packaging/icons.sh regenerates everything with resvg, iconutil and the new `cargo xtask ico`.
- Runtime icon via ViewportBuilder::with_icon (padded variant on macOS) and Wayland app ID
  ai.storyteller.effectcraft; packaging/linux/ai.storyteller.effectcraft.desktop.
- apps/effectcraft/build.rs embeds the .ico and VERSIONINFO into effectcraft.exe (winresource,
  Windows targets only; a missing resource compiler is a warning).
- Web favicon.svg is now the unicorn icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:56:28 -04:00
Brandon Thomas 590f2f67ba M3.7: auto-save + crash recovery
PRJ-7. `engine::autosave` with the session hooks the frontends call:

- Auto-save: every N minutes a dirty project is written to "EffectCraft Auto-Save" next to
  it (or a custom folder; untitled projects use the custom folder or the config dir) as
  "<name> auto-save N.ecproj", rotating through max versions (oldest overwritten, extra
  slots dropped when the maximum shrinks). Optional save when the render queue starts.
- Atomic writes for projects, auto-saves and config files: temp file + fsync + rename; a
  failed write removes the temp file and leaves the previous file intact.
- Crash recovery: a session.lock sentinel in the config dir records the open project and
  its latest auto-save; a clean exit removes it. At launch a leftover sentinel offers the
  latest auto-save in a Recover Project dialog (Settings > Startup can turn it off).
- File > Open Recent (stored in the settings) with Clear Recent Projects; Increment and
  Save uses After Effects naming ("Intro 2.ecproj"); Revert reloads the saved file.
- Commands: file.autoSave, file.recoveryInfo, file.openRecent {index|path},
  file.clearRecent.
- Tests: rotation, interval/dirty tick, simulated crash mid-write, unclean-exit detection
  and recovery, Open Recent / Increment and Save / Revert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:15:04 -04:00
Brandon Thomas 35f429363e M14.3: keyboard shortcut editor
Keyboard shortcut presets in the engine (`engine::shortcuts`): every menu entry (including
ones with bound parameters), registry command and frontend command is bindable, scoped
application-wide or to a panel. "EffectCraft Default" is After Effects' layout and
read-only; editing it creates a custom preset that stores only its differences. Presets
are saved in the config store; the shortcut dispatcher and the menus read the active one.

- Commands: shortcuts.list, shortcuts.set {command, params?, keys}, shortcuts.reset,
  shortcuts.preset {select|new|duplicate|delete|rename}, shortcuts.export/import (JSON),
  shortcuts.conflicts.
- Edit > Keyboard Shortcuts: preset bar, modifier toggles (or held keys), an on-screen
  keyboard coloured by scope (application-wide / panel-specific / both), key details,
  a searchable list of every command, record-or-type assignment with conflict warnings,
  reset to default; all widgets have automation ids.
- Tests: normalization, defaults conflict-free, conflict detection, custom preset creation
  and persistence, preset lifecycle + export/import, dispatch uses the custom preset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:14:54 -04:00
Brandon Thomas 47f7712364 M14.2: preferences
A real Settings model in the engine (`engine::prefs`): serde, versioned, dotted keys
(`general.undoLevels`, `labels.3.name`), migration of older layouts, unknown keys kept,
persisted through a `ConfigStore` trait (platform config dir on the desktop; MemoryConfig
for tests; the web app can back it with localStorage).

- After Effects 2026 pages (General … Scripting & Expressions) as a schema shared by the
  dialog, `prefs.pages` and docs/preferences.md (rows not wired yet are listed there and
  kept in step by a test).
- Commands: prefs.get, prefs.set {key, value | values}, prefs.reset {page?}, prefs.open
  {page?}, prefs.pages.
- Wired settings: undo levels, path point size, tool tips, layers at comp start / CTI,
  default spatial interpolation linear, Home screen on launch, new project template,
  rendering progress in Info, adaptive resolution limit, cache when idle, fast previews,
  theme + UI brightness, label names/colours everywhere (Label menu, timeline, project,
  render queue, viewer), label colour for handles, grid colour/spacing/subdivisions, guide
  colour, safe margins, still footage duration, sequence fps, audio output device and
  output mapping, layer / footage / preview cache budgets, default 3D renderer.
- Settings dialog: AE's left page list + right content, OK / Cancel (restores) / Previous /
  Next, every widget with an automation id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:14:45 -04:00
Brandon Thomas 362d12269f Merge branch 'worktree-agent-acb4c3cd3f5057c6a' (M15.1 web build)
Also: render uses web_time::Instant in layer styles; renderQueue.add documents
its render-settings/output-module keys so checked execution accepts them (+test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:41:56 -04:00
Brandon Thomas 31f192e2a9 M15.1: web build (eframe wasm) + xtask web
- apps/effectcraft-web (L6): eframe web runner booting the shared EffectcraftApp with a browser
  session: in-memory file table, <input type=file> pickers and drag-and-drop for open/import,
  downloads for Save and Render Queue outputs (several files as one stored .zip), and the
  window.effectcraft promise API over the control channel. Demo project on start.
- wasm32 runtime fixes: web-time for Instant/SystemTime (render, engine, export, ui-egui);
  Frames renders queued jobs on the UI thread (Frames::pump) instead of a thread pool;
  Render Queue runs inline; relative outputs resolve under / ; control PNGs inline.
- effectcraft-export: per-job output Sink (files to memory instead of disk); host FileExporter
  carries it; test that sink exports touch no file.
- xtask web [--dev] [--serve PORT] (wasm-bindgen 0.2.129, optional wasm-opt, COOP/COEP server);
  effectcraft-web in WEB_CRATES. docs/web.md, headless Chrome smoke test, favicon (+attribution).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:19:35 -04:00
Brandon Thomas ddb33cdfd0 M11.2: audio playback + waveforms + audio effects
- Audio effects (Backwards, Bass & Treble, Delay, Flange & Chorus,
  High-Low Pass, Modulator, Parametric EQ, Reverb, Stereo Mixer, Tone)
  applied per layer in the comp mixdown, so export and preview share them.
  RBJ-cookbook biquads, Schroeder/Moorer reverb, modulated delay lines;
  stateless per block via pre-roll. Tone makes any layer audible.
- Real-time audio preview: ui-egui AudioFeed/AudioPlayback (feeder thread
  mixing ahead) behind an AudioDevice hook; the desktop app implements it
  with cpal (L6). Audio clock drives playback (consumed frames minus device
  latency, looped through the work area). Preview panel Include Audio /
  playback.muteAudio.
- Audio panel VU meters with peak hold and clip indicators; selected
  layer's Audio Levels.
- Timeline Audio > Waveform for footage with audio (L reveals Audio
  Levels, LL the Waveform), drawn from a cached per-item peak summary.
- ui.playback reports audio state and meter levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:16:34 -04:00
Brandon Thomas 83aabf0a87 Merge main into M7 3D branch
Resolve conflicts with perf (layer cache / Arc<Buf>), export (RenderOpts.view: None
for output), timeline (separate dimensions + one-node POI visibility), mask/pen
viewer gestures (alongside camera tools and view-aware layer matrices), key
dialogs (alongside Camera/Light Settings), render queue + MCP command modules.
3D path tangent honours separated position; DOF blurs a per-frame copy so cached
layer buffers stay shared. New test: cached renders match uncached after camera,
focus, light and 3D view changes. Fix a clone-on-copy lint from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:24:08 -04:00
Brandon Thomas c6af0fa2e9 Merge branch 'worktree-agent-a549a3d1a8812e372'
# Conflicts:
#	apps/effectcraft-cli/src/main.rs
#	crates/host/src/lib.rs
#	crates/render/src/lib.rs
2026-10-01 22:42:32 -04:00
Brandon Thomas 5077aafffc Merge branch 'worktree-agent-afea1146abae6192c'
# Conflicts:
#	apps/effectcraft-cli/Cargo.toml
#	apps/effectcraft-cli/src/main.rs
#	crates/render/src/lib.rs
2026-10-01 22:35:20 -04:00
Brandon Thomas b168d902c7 Merge branch 'worktree-agent-a45c0c688bf960993'
# Conflicts:
#	Cargo.lock
#	apps/effectcraft-cli/Cargo.toml
#	apps/effectcraft-cli/src/main.rs
#	crates/host/src/lib.rs
#	crates/ui-egui/src/panels/mod.rs
2026-10-01 22:29:58 -04:00
Brandon Thomas 7f9c3a85c0 M10: render queue + export
- crates/export (effectcraft-export): render a comp's span to H.264 MP4 (+AAC),
  ProRes 422/4444 MOV (+PCM, alpha), PNG/JPEG/TIFF/EXR sequences and animated GIF
  via FilmCraft's pure-Rust encoders and MP4/MOV muxer; parallel batched frame
  rendering, progress callback with cancellation.
- project: Render Queue model (Render Settings, Output Module, output path
  templates, status) saved with the project.
- render: FootageSource::audio hook + comp audio mixdown (levels, in/out, solo,
  precomps); media implements it.
- engine: Exporter hook, background/blocking render job, renderQueue.* commands
  (add Cmd+M, remove, setRender, setRenderSettings, setOutputModule, setOutput,
  move, duplicate, render, stop, list, formats).
- host wires the exporter; CLI `render` subcommand.
- ui-egui: After Effects-style Render Queue panel with automation ids.
- tests: encode/decode round trips for every format (FilmCraft decoders,
  ffmpeg/ffprobe oracle when installed), audio, cancel, engine commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:02:06 -04:00
Brandon Thomas 16a5fa7a83 M7.2: 3D viewer — 3D views, camera tools, camera/light wireframes, Camera/Light Settings dialogs
- Viewer renders through the comp's 3D view (RenderOpts.view; FrameKey hashes
  the view camera), shows the view name, and the bottom-bar 3D View dropdown
  (automation id viewer.view3d) switches Active Camera/Front/…/Custom View 3.
- Orbit / Pan / Dolly tools drag the current view (camera layer in Active
  Camera, merged into one undo step; view cameras otherwise).
- Handles, gizmo and picking go through the view camera; 3D layers move in
  the view plane at their depth.
- Camera frustum + POI and light position/direction wireframes
  (viewer.camera.<id>, viewer.light.<id>).
- Camera Settings / Light Settings dialogs open from New Camera/Light,
  Camera/Light Settings and Layer Settings; OK runs the engine commands.
- Timeline: POI only for two-node cameras/auto-oriented lights; "Z Rotation"
  label on 3D layers.
- Headless egui_kittest tests (UI wiring + opt-in wgpu snapshots); fix the
  remaining workspace clippy lints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:01:37 -04:00
Brandon Thomas b307a562ed Merge branch 'worktree-agent-aaed9f382307fbe0b'
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-01 21:49:02 -04:00
Brandon Thomas 951ed1fa3d M5.2–M5.5: keyframe clipboard, easy ease, velocity/interpolation dialogs, roving, time remap/stretch/reverse/freeze, graph editor, separate dimensions, pick-whip
Engine (all commands with undo/redo + tests in engine/src/tests_timeline.rs):
- keys.copy/paste (Edit Copy/Cut/Paste route to keys when keys are selected;
  relative timing kept, paste at CTI, cross-property paste of same type),
  keys.selectAll (Cmd+Alt+A), keys.nudge* (Alt+Arrow, Alt+Shift+Arrow),
  keys.set / keys.setEase (graph editor drags), keys.info (dialog data),
  keys.timeReverse; keys.interpolation gains continuous/auto Bezier, spatial
  interpolation and roving; keys.velocity takes per-dimension values and
  Continuous.
- Roving keys (keyframe::retime_roving): constant speed along the motion path.
- Layer > Time: layer.timeStretch (percent|duration, hold in/current/out),
  layer.timeReverse (Cmd+Alt+R), layer.enableTimeRemap (Cmd+Alt+T),
  layer.freezeFrame, layer.freezeOnLastFrame; render uses Time Remap as the
  source time for precomp/footage layers.
- prop.separateDimensions (X/Y/Z Position; prop.set on Position writes them).
- layer.setParent compensates so the layer stays in place (compensate:false
  to opt out).
- prop.pickWhip writes AE-style reference expressions (thisComp.layer(..),
  effect(..)(..), mask(..).maskFeather, temp=…;[temp,temp], …[0]).
- prop.setExpression: syntax errors keep the text but disable the expression.
- expr: arrays of properties returned by an expression are resolved.

UI: Keyframe Velocity, Keyframe Interpolation and Time Stretch dialogs;
Graph Editor (value + speed graphs, draggable keys and Bezier/influence
handles, auto-zoom, fit, show selected, separate dimensions, hold/linear/
auto Bezier/Easy Ease buttons); key + layer context menus. Clears the
workspace clippy -D warnings failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:47:29 -04:00
Brandon Thomas c672e03160 M14.1: MCP server + agent CLI
- crates/automation (L5, no egui): hand-rolled JSON-RPC 2.0 MCP server over
  stdio (initialize, ping, tools/list, tools/call); headless backend (in-process
  Session) and bridge backend (desktop control channel). Tools: list_commands,
  execute_command, get_project/comp/layer, get/set_property, add_keyframe,
  render_frame (inline PNG), open/save_project, undo/redo; bridge-only
  screenshot, ui_* and raw control passthrough.
- effectcraft-cli: info, commands, exec, run, props, get, set, render-frame,
  mcp [--bridge PORT]; --json, --project/--demo/--empty, --save/--save-as.
- engine: prop.get, layer.tree node paths + time, Session::resolve_comp,
  render_rgba8, execute_checked (rejects unknown params, lists accepted keys).
- control channel: render.frame; engine.execute runs every engine command
  directly (no dialogs, validated params).
- docs: control-protocol.md, agents.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:40:31 -04:00
Brandon Thomas a2ff724ac2 M12.1: render cache + parallel compositing fast paths
- render: LayerCache (content-keyed processed-layer cache: source -> masks ->
  effects, keyed by a hash of the layer's non-transform props evaluated at the
  frame time, solid item, switches, scale; time folded in for clock-driven
  effects / wiggle paths). Static and transform-only layers render once;
  edits only re-render the edited layer. Session + UI frame jobs share it.
- effects: TIME_DEPENDENT list + test that every clock-reading effect is listed.
- raster: affine composite fast path (per-row span clipping, unchecked interior
  bilinear, integer-translation copies, inlined Normal blend), checked against
  composite_warp_reference; box/Gaussian blur without transposes, two
  ping-pong buffers; accumulate_warp so motion blur sums into one buffer
  instead of one full-frame image per sub-sample.
- ui: viewer frame is urgent and jumps the prefetch queue; stale-revision jobs
  are dropped; paused prefetch waits for the viewer frame; "Frame Render Time"
  reports viewer frames only; parallel f32 -> Color32 conversion.
- cli: `frame --bench N` (per-layer/per-effect timings, wall + CPU time) and
  `frame --bench-play N` (consecutive frames with/without the layer cache).
- tests: cache transparency (scrub, edits of values/keys/effects/masks/
  solids/text/timing), hit/miss accounting, time-dependent effects, demo
  cached == uncached, fast warp/blur/accumulate == reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:34:33 -04:00
Brandon Thomas 76ac3f74f7 M7.1: Classic 3D renderer, cameras, lights, 3D views and camera-tool commands
- render/three_d: camera eval (one/two-node, DOF), default 50mm comp camera,
  orthographic Front/Left/Top/Back/Right/Bottom + perspective Custom Views,
  orbit/pan/dolly rig math; lights (parallel/spot/point/ambient, falloff,
  cone feather) and material shading; per-pixel depth-sorted compositor for
  3D runs (exact intersections, perspective-correct sampling, blend modes,
  track mattes, motion blur), ray-cast planar shadows with transmission and
  diffusion, per-layer DOF blur. RenderOpts.view camera override.
- project: camera iris/highlight props, aperture per comp width, light
  orientation/rotations, two-node auto-orient defaults.
- engine: layer.newCamera/newLight with presets & settings,
  layer.cameraSettings/lightSettings (Layer Settings dispatches),
  view.set3DView + View > Switch 3D View items (F10/F11/F12), last/reset view,
  camera.orbit/pan/dolly, view.get3D; EditorState.views3d.
- demo: "3D Showcase" comp (camera move, key spot + ambient fill, shadows,
  DOF, intersecting planes, gridded floor).
- tests: projection, depth order, intersections, lighting, shadows, DOF,
  views, rig math; command undo/redo. Fix a few pre-existing clippy lints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:09:21 -04:00
Brandon Thomas 214345df13 control: keep channel responsive when window is covered (disable App Nap); fail fast on occluded screenshots
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:49:29 -04:00
Brandon Thomas 3a59a8470d M0.9–M0.10: egui frontend (AE-style panels, viewer, timeline, effect controls, dialogs), desktop app with control channel, host wiring; cargo fmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:48:31 -04:00
Brandon Thomas 6a5a2d9689 M0.8: engine session, undo history, ~100 commands, demo project; headless CLI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:28:21 -04:00