Commit Graph
748 Commits
Author SHA1 Message Date
Brandon Thomas dfd8aac431 keyframe: never-crash deny lints (craftrules standards/never-crash.md)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 2232354b902238827b2e80a29ef266c2e5bde272)
(cherry picked from commit 95da7943454ca5b2575993d11f9ba046822ce255)
2026-10-06 08:41:33 -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 e6c817bb3f Merge pull request #99 from storytold/pr/release-0.3.1
Release: EffectCraft v0.3.1
v0.3.1
2026-10-06 08:04:15 -04:00
Brandon ThomasandClaude Opus 5.5 e1e0a78be5 Release: EffectCraft v0.3.1
Fixes the Windows installer: the Start Menu shortcut and Add/Remove Programs icon were blank. The shortcut description was wrong. Release builds no longer open a console window behind the app.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 08:04:07 -04:00
Brandon Thomas 722d7d673c Merge pull request #98 from storytold/pr/windows-packaging
Windows packaging: Start Menu/Add-Remove icon, no console window, correct description
2026-10-06 07:54:09 -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
BFlat 80c246d843 Merge pull request #92 from storytold/m3-delete-confirm
M3.16: deleting Project items in use asks first (After Effects parity)
2026-10-06 11:36:27 +07:00
bflatasticandClaude Opus 5.5 9624024dc1 M3.16: deleting Project items that compositions use asks first
As in After Effects, the Delete key and the Project panel's trash button ask before deleting
items that layers in other compositions use, saying how many layers in how many compositions go
with them (Delete / Cancel; Escape cancels). Unused items are deleted at once; agents running
project.delete directly are not asked. project.usage {items?} reports the count, sharing the
deletion's item and usage helpers. Follow-up to #72.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 11:35:57 +07:00
BFlat b46be83ab4 Merge pull request #84 from fortesnow/jp-font-fallback
text: script-aware system font fallback; accept PostScript / full font names
2026-10-06 10:57:36 +07:00
BFlat 86ae2fa218 Merge pull request #72 from jonassneu/fix/project-delete-shortcuts
Project: delete selected items with Delete and Backspace
2026-10-06 10:56:43 +07:00
bflatastic f0578a5940 Merge branch 'main' into pr72-land
# Conflicts:
#	docs/gaps.md
2026-10-06 10:55:43 +07:00
BFlat 42401e7672 Merge pull request #70 from jonassneu/fix/viewer-visible-layer-selection
Viewer: respect solo and visibility in layer selection
2026-10-06 10:54:56 +07:00
fortesnow c8e9efb962 text: script-aware system font fallback and PostScript / full font names
Japanese (and Chinese, Korean) text in a family that lacks the glyphs, such as the default Inter,
rendered as missing-glyph boxes: `fallback_for` only searched the bundled faces and faces some
other layer had already loaded, so the result depended on render order. Now, after those cheap
candidates, it tries the platform's usual family for the character's writing system (kana and
hangul pick their language; Han ideographs follow the locale, Japanese first when it is unknown,
`EFFECTCRAFT_CJK_LOCALE` overrides) in the preferred face's style, and finally any installed face
that covers the character, reading files without keeping them. Results are cached per 256-character
block and preferred face.

`resolve` also accepts a face's full name (`Yu Gothic Bold`) or PostScript name (`YuGothic-Bold`),
which is how After Effects scripts set `textDocument.font`; the sfnt reader now records name id 6.

Tests: script classification, PostScript / full-name resolution on the bundled Inter, and (native,
skipped when no Japanese family is installed) that kana, ideographs and CJK punctuation in Inter
Bold fall back to an installed Japanese family's bold cut, with the cache returning the same face.
2026-10-06 10:41:15 +09:00
BFlat 877afeb25a Merge pull request #83 from storytold/pr/release-gate
Release gate: ui_agent_audit builds in release mode
v0.3.0
2026-10-06 08:37:17 +07:00
bflatasticandClaude Opus 5.5 f25b75486b Release gate: the agent-audit test builds in release mode
`cargo xtask ci` runs clippy and the tests with --release, where egui has no Style::debug (it
exists only with debug_assertions), so ui_agent_audit.rs failed to compile since M13.14. The
debug option is set only in debug builds, and the test, which needs the widget types egui records
with it, is ignored in release builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 08:37:01 +07:00
BFlat 857340036b Merge pull request #82 from storytold/docs/issue-status
Docs: user issues #41–#47 and #63–#68 in gaps.md
2026-10-06 00:39:08 +07:00
bflatasticandClaude Opus 5.5 25f353fb2c Docs: gaps.md records the user issues of 5 October and what became of them
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:38:43 +07:00
BFlat dd56ae5c00 Merge pull request #81 from storytold/fix/44-project-import
Fix #44: double-click the Project panel's empty area to import
2026-10-06 00:37:36 +07:00
bflatasticandClaude Opus 5.5 df12664b8e Fix #44: double-click the Project panel's empty area to import
As in After Effects, a double-click below the items in the Project panel opens File ▸ Import ▸
File... (rows and the scroll bar keep their own clicks). Dropping files on the window already
imported them; the test now covers both. (On Wayland, winit delivers no file drops at all, so
dragging files in works on X11, macOS and Windows.)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:37:05 +07:00
BFlat fdfbca5faf Merge pull request #80 from storytold/fix/43-wayland-icon
Fix #43: the AppImage's window shows the EffectCraft icon on Wayland
2026-10-06 00:31:29 +07: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
BFlat 72e60dc6fa Merge pull request #79 from storytold/fix/45-project-details
Fix #45: the Project panel's details stay inside the panel
2026-10-06 00:26:17 +07:00
bflatasticandClaude Opus 5.5 e6d4a64775 Fix #45: the Project panel's details stay inside the panel
The details header (item name, size, duration, codec) and the "Select an item" hint were painted
with no width limit, so in a narrow Project panel they ran past its right edge. They now stop
10 px short of it and end in "…" when they don't fit (widgets::text_fit, a shared helper), and
are registered for automation (project.details.*).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:25:57 +07:00
BFlat 83cd5d0f45 Merge pull request #78 from storytold/fix/46-docking
Test #46: docking by the panel menu button and resizing by the gutters
2026-10-06 00:18:37 +07:00
bflatasticandClaude Opus 5.5 efcbc2e412 Test #46: panels dock by a drag from the panel menu button; gutters resize
Covers what issue #46 reports (v0.1.1 on Linux): a panel dragged by its hamburger onto another
group's edge docks there as its own group, and dragging the gap between panels resizes them.
Both work on main (tab dragging was reworked in M3.15 for v0.2.0); this keeps them working.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:18:11 +07:00
BFlat bb7b5f51de Merge pull request #77 from storytold/fix/65-drag-refresh
Fix #65: the viewer keeps up while a layer is dragged
2026-10-06 00:16:25 +07:00
bflatasticandClaude Opus 5.5 1b8780f6da Fix #65: the viewer keeps up while a layer is dragged
While paused, the app prefetched the frames around the current one as soon as the viewer's
frame finished. During a drag every step changes the project, so those frames were stale at the
next step, and they held the CPU and GPU the next viewer frame needed: on the demo project a
drag step took 102 ms on average to show (up to 413 ms) though a frame renders in 20 ms. Paused
prefetch now waits until edits pause for 0.3 s: 16 ms a step (11 ms render).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:16:04 +07:00
BFlat 75640044b8 Merge pull request #76 from storytold/fix/64-double-axes
Fix #64: one 3D compass in the viewer
2026-10-05 23:49:03 +07:00
bflatasticandClaude Opus 5.5 77c056a8db Fix #64: one 3D compass in the viewer
Two implementations of Show 3D Reference Axes had landed (M13.5 and M14.5) and both drew,
offset by a few pixels: lines, and arrows. Keep the arrowed one (perspective-correct, shown
while a 3D layer is active) and test that only one is drawn.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:48:41 +07:00
BFlat 0dcbe325ab Merge pull request #74 from storytold/fix/63-viewer-scale
Fix #63: scaling a layer by its handles follows the pointer
2026-10-05 23:44:30 +07:00
bflatasticandClaude Opus 5.5 782a67f228 Fix #63: scaling a layer by its handles follows the pointer
The handle drag rescaled incrementally from the current scale every frame: errors built up,
edge handles scaled both axes, and once a factor reached ~0 the drag could never bring it
back. It also tested the handle at the point where egui recognised the drag (past the drag
threshold), so a quick grab often missed the handle and moved the layer instead.

Now, as in After Effects, the drag scales about the anchor point relative to the layer as the
drag began, so the grabbed point stays under the pointer: corners scale both axes, edges one,
Shift keeps the proportions, and dragging past the anchor flips. Selection-tool gestures start
at the press point.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:43:50 +07:00
BFlat 4373c24962 Merge pull request #73 from storytold/fix/68-open-links
Fix #68: Discord buttons and other links open the browser again
2026-10-05 23:27:45 +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
Jonas Neubauer 41f5ee3aec G2 (#67): delete Project items with Delete and Backspace 2026-10-05 18:24:14 +02:00
Jonas Neubauer a0c1b60d61 G2 (#66): respect solo and visibility in viewer selection 2026-10-05 18:01:41 +02:00
Brandon Thomas e5c58600fd Merge pull request #60 from storytold/pr/freebsd
FreeBSD: system info reports memory, CPU model and OS version
2026-10-05 11:33:17 -04:00
Brandon ThomasandClaude Opus 5.5 b70c808d60 FreeBSD: system info reports memory, CPU model and OS version
sysctl hw.physmem / hw.pagesize / vm free+inactive page counts, hw.model,
and freebsd-version, with a unit test for the sysctl parser. The FreeBSD
CI workflow itself landed with #69.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 11:33:06 -04:00
Brandon Thomas 7f3caeea70 Merge pull request #69 from storytold/pr/gpu-gl-fallback
GPU on wgpu's OpenGL backend (FreeBSD / Linux without Vulkan)
2026-10-05 11:29:02 -04:00
Brandon ThomasandClaude Opus 5.5 3767332efd GPU compositor declines devices whose limits its pipelines exceed
egui-wgpu creates its device on GL with WebGL2's limits (no storage buffers), so the desktop
app on FreeBSD built the compositor's pipelines on it: validation errors (only logged in
release builds) and wrong frames. GpuContext::new now checks the limits its pipelines bind
(storage buffers and textures per stage, bind groups, workgroup sizes) and returns Err, which
keeps the app on the CPU. ui_gpu skips its GPU-vs-CPU comparison when that happens on GL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 10:52:04 -04:00
Brandon ThomasandClaude Opus 5.5 6eedf8e2c5 GPU on wgpu's OpenGL backend: renderable depth target, portable shaders, serialised tests
FreeBSD's CI VM (Mesa llvmpipe over GL) failed 22 effectcraft-gpu tests:

- Advanced 3D's camera-depth target was R32Float, which GLES can't render to without
  EXT_color_buffer_float: create_render_pipeline failed validation (release builds only log
  that and draw nothing). The target is now R32Uint holding the f32 bits (renderable on every
  backend, lossless), and GpuContext checks the adapter's format features (render attachment,
  blendable colour) and independent blending up front; when they're missing the render
  pipelines are never built and Advanced 3D renders on the CPU.
- Signed `%` in the shaders: naga emits GLSL `%`, undefined for negative operands, which broke
  the VR effects' horizontal wrap and Cell Pattern's tiling. A shared `imod` never takes `%` of
  a negative number.
- asin/acos: Mesa's are coarse approximations (~1e-4 rad), visible in every lens/sphere/cylinder
  mapping. `asin_p`/`acos_p` (Cephes' asinf polynomial) are f32-accurate on every backend.
- "Could not lock adapter context": the GL backend has one context lock per adapter with a
  timeout, so tests driving devices concurrently panic. Every gpu test helper that hands out a
  device holds one process-wide lock for the calling test; ui_web_job_gpu's two device tests
  share a lock too.
- On GL, up to 1 % of pixels may flip across a decision boundary (division through a
  reciprocal, unfused fma); the GPU-vs-CPU comparisons allow that there only.

Also adds the FreeBSD workflow (from the FreeBSD CI PR) so this branch is checked there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 10:08:21 -04:00
BFlat 24fcbdf3d2 Merge pull request #62 from storytold/pr/release-0.3.0
Release: EffectCraft v0.3.0
2026-10-05 20:42:35 +07:00
bflatasticandClaude Opus 5.5 044fc32a9c Release: EffectCraft v0.3.0
Bump the workspace version 0.2.0 -> 0.3.0 (cargo xtask version set 0.3.0).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 20:42:11 +07:00
BFlat b9ffd889bc Merge pull request #61 from storytold/m13-face-ml
M13.36: face tracking trained model (MediaPipe Face Landmarker, pure Rust)
2026-10-05 20:40:31 +07:00
bflatasticandClaude Opus 5.5 e43bf66775 M13.36: face tracking trained model — MediaPipe Face Landmarker on a pure-Rust TFLite interpreter
- effectcraft-segment: a FaceModel interface (find a face in a region, follow it; a Topology
  maps the model's points to the tracker's landmarks and outline), a TensorFlow Lite reader and
  interpreter (float ops, float16 weights folded; matches ai-edge-litert to ~1e-6), and
  MediaPipe Face Landmarker (Google, Apache-2.0: BlazeFace + Face Mesh V2, 3.8 MB, pinned
  SHA-256). The registry gains a task and authors per model; installed weights get a NOTICE.
- effectcraft-track: FaceTracker::new_with uses the model, falls back to the classical fitter
  when it finds no face; chin and jaw come from the outline either way so measurements agree.
- Engine: roto_models becomes models (both tasks); face.models / face.model.* commands,
  Settings ▸ Face Tracking, `wait` on select/install/download for scripts; track.mask reports
  faceModel.
- Credits: ATTRIBUTION.md lists the optional models and the Apache-2.0 specs the code follows.
- Docs: README, ROADMAP (G7), gaps, parity (TRK-3 score unchanged pending G1), agents,
  preferences, architecture, segment README.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 20:39:45 +07:00
Brandon ThomasandClaude Opus 5.5 e2f027f374 docs: point agents to the shared test corpora (#59)
Real-file test oracles live in storytold/photocraft-corpus, explained in
craftrules standards/test-corpora.md. Never commit large binary fixtures
here; fetch them pinned and sha256-verified.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-05 08:24:13 -04:00
BFlat 4624c00308 Merge pull request #58 from storytold/m13-roto-ml
M13.35: Roto Brush trained model — swappable segment module, MobileSAM in pure Rust
2026-10-05 17:57:36 +07:00
bflatasticandClaude Opus 5.5 174ab2b6af Merge main (gap assessment) into M13.35: keep parity numbers, record G7's decision and evidence
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:57:09 +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