The Preview panel draws Shortcut, Range, Play From, Frame Rate, Resolution, Overlays, Layer
Controls, Loop, Cache Before Playback, Pre-roll, Post-roll, Full Screen and the caching hint from a
local closure, a check table and a roll tuple, so the call-shape scan cannot see them and no catalog
had rows for most of them. They ask the catalog now, with Simplified and Traditional rows.
Verified on Linux: fmt, cargo test -p effectcraft-ui-egui, clippy --all-targets -D warnings.
A startup key (last, empty or demo) chooses the project the page opens on load. empty restores the last visit's project and otherwise starts blank, never with the demo; demo always opens the demo project; last, or no key, is the current behaviour. ?empty and ?demo still win. The config file is fetched once per page load and the service worker never caches it.
A missing or unusable curve no longer consumes the last value. The long form reads argument 5 and the short form argument 3, with the other arities falling back to linear interpolation. Adds the missing-curve and four-element-array cases reported in review.
- accept only four-element arrays for bezier(): extra values make the curve
malformed, so it falls back to linear like the other invalid forms (#1884);
- stop Newton on the estimated y error (f * dy/dx) instead of a residual in x
alone: near the endpoints dy/dx is in the thousands, so a 1e-9 residual in x
could still move the returned value;
- tests: a five-element curve is linear, and three steep curves match a
bisection reference within 1e-6.
Add the helper to the expression editor's completions (syntax highlighting and auto-complete) and to the Expression Language menu's Interpolation group, so accepting it inserts the call parentheses like linear/ease.
Add bezier(t, tMin, tMax, v1, v2, [x1, y1, x2, y2]) and the short bezier(t, v1, v2, curve) form: a CSS cubic-bezier easing curve solved with Newton-Raphson and a bisection fallback.
Scalars and arrays work like linear/ease; x1/x2 clamp to 0..1, y1/y2 may overshoot, t clamps to tMin..tMax, and a curve that is not four finite numbers falls back to linear instead of throwing.
Tests cover known curves (ease is [1/3, 0, 2/3, 1]), clamps, arrays, the short form, invalid curves and overshoot, plus two fuzz seeds.
Closes#1884.
Conflicts were main's new code next to this branch's additions; both kept:
- crates/effects/src/lib.rs: main's Buf::pad_sides stays as main wrote it
(Transform's transform_grow bounds that padding itself); Buf::pad keeps
this branch's effective-pad clamp.
- crates/raster/src/lib.rs: main's quantize_px/round_half_up and this
branch's MAX_PADDED_SIDE (still equal to limits::MAX_SIZE on main).
Conflict in crates/ui-egui/src/panels/viewer_text.rs: main moved the
editor's key handling into key_event(). Took main's version and moved
this branch's Duplicate forwarding arm into key_event(), using its ctx
parameter.
main follows the browser's language preferences itself now (set_system_locales /
first_supported), so this branch keeps only what it adds: the Russian, French and German catalogs
selectable by Match System and a locale, and their Undo and Redo menu labels.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
CC Composite stays After Effects-pure: its Layer parameter is gone, so
parameter indices match AE again (scripts and expressions that address
parameters by index). The model reference lives in its own effect,
ec.3d.instance3d (3D Channel, CPU, one layer parameter), which replaces
the layer's pixels with the referenced layer's render — alpha included.
A referenced model's solo render is now hashed into the owner's cache key
instead of declining the entry (Bflat, review of #2410): the layer, its
animated values and transform, the mesh item, the comp camera and lights,
draft 3D, the renderer setting and the in/out gate. Covered by a render
test: the copy is cached, served on the second render, and re-rendered
when the camera pans or the model leaves its in/out range.
- add_environment takes the evaluated light (&EnvLight) instead of nine
loose parameters — strict clippy (#2496) denies too_many_arguments.
- The Environment light's shadow map moves into env_shadow_map so
shadow_maps stays under the cognitive-complexity limit.
- tests/it/ui_env_light_repro.rs deleted: it duplicated ui_env_light_gpu
and the engine tests, and polled with sleeps of up to 12 s.
Signed-off-by: Minher0 <minhero@outlook.fr>
lib.rs and menus.rs were split (#2491): the dialog_state accessors, the undimmed canvas while a
dialog previews, and Escape-as-Cancel for the Gradient Editor and Preview move to app/window.rs,
app/frame.rs and menus/shortcuts.rs. Layer Styles' rollback keeps main's text-editor handling inside
the shared restore(). The dialog preview integration test joins the tests/it binary.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
The shape-tool double-click, Cmd+W handling and their tests move to the split header, menus/invoke
and engine test modules (#2491); the docking check in the Cmd+W test uses the craft_ui dock API.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
effectcraft_project::cineon_log_to_lin does the log-to-linear conversion
for Interpret Footage > Cineon Settings and the Cineon Converter's Log to
Linear. Both raise the result to 1 / Gamma, so Gamma 1 gives linear light
and the same Gamma gives the same values in both. The converter's Linear
to Log, its Log to Log reference and the GPU shader use the same Gamma
scale, with a smallest Gamma of 0.01 / 1.7 (CINEON_MIN_GAMMA).
A test runs the converter at Gamma 1.7 (the default), 1 and 2.4 and
checks it against Cineon Settings at each code value.
The mixdown evaluated levels and effect parameters once per block, and export mixes WAV/AIFF
in one-second blocks, so a level fade became a staircase that changed once a second. Animated
Audio Levels are now evaluated on a 1 ms grid of comp samples and interpolated in dB between,
and animated per-sample effects (Stereo Mixer, Backwards) run in 1 ms pieces with their
parameters at each piece. The grid is aligned to comp sample indices, so the result still does
not depend on the block size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests now sit with the crate's other test modules under crates/script/src/tests/ and are declared in tests.rs. docs/scripting-parity.md points at the new path.
Dither can round a colour byte up while alpha rounds down (colour = alpha = 100.25/255 gives
alpha 100, colour 101), breaking the premultiplied invariant. Clamp each dithered colour byte
to the written alpha byte.
Test: dithered_premultiplied_colour_never_exceeds_alpha.
Resolve the export tests' module list (banding and h264_gpu_yuv both stay). #1631's
yuv420_frame checked Render Settings Color Depth itself; that depth is now the project's
depth for the whole render, so the GPU YUV path is taken only for 8 bpc renders. Above
8 bpc frames take the RGBA path, which dithers, so H.264 exports don't band either.
Test: renders_above_8_bpc_skip_the_planes.
Per review: as in After Effects, the region of interest lies inside the comp, so each converted
ROI edge is clamped to [0, w] / [0, h] and the far edge never passes the near one. A hostile ROI
reaching far outside the frame can no longer pad it into a huge crop, the wide-frame case keeps
its full width, and the i64 widening is no longer needed (frame_size is back to main's code).
Tests: hostile quads give edges >= 0 and a frame between 1 x 1 and the frame size, and an ROI
covering more than the frame is the whole frame.
wiggle_transform looks up its Transform group itself and returns when
the group is missing, so it takes seven parameters and no longer needs
the clippy::too_many_arguments allow.
Polish menus (every fixed menu entry, 630 rows) and panels (every row of
the Chinese panel catalogs, 695), the undo/redo, Reveal and Assign Shortcut
labels, the macOS Services item, Settings ▸ General ▸ Language and
Match System for pl-* locales. Tests cover the locale mapping, the
settings registry and the macOS application menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
The CJK render test skipped wherever no Japanese face was installed, which
is every CI runner, and on a Mac it drew glyphs with or without the fix, so
it caught nothing. Replace it with a fallback-order test: fixture faces for
`Hiragino Sans` and `Hiragino Sans GB` show that a zh-hans Han lookup tries
the Chinese family first, that ja tries the Japanese one, and that a layout
gets Hiragino Sans GB. Synthetic fonts in an isolated child process keep it
independent of the host's fonts; with the Simplified list reverted it fails
on `[7]` against `[8, 7]`.
Delete `TEST_CJK_LOCALE`: the locale-mutating tests moved to an isolated
child process, so nothing used the lock and the test-only static failed
clippy's `-D warnings` gate.
Trim the documentation edits to the single docs/gaps.md gap-11 row: revert
the header and changelog edits in ROADMAP.md, docs/roadmap.md,
docs/localization-parity.md and docs/target-app-parity.md, and the gaps.md
header and changelog row, which conflict with other PRs.
Tests: cargo clippy -p effectcraft-text -p effectcraft-render --all-targets
-- -D warnings, cargo clippy --workspace --all-targets -- -D warnings,
cargo test -p effectcraft-text (79 lib), cargo test -p effectcraft-render
(264 passed, 1 ignored perf test), cargo fmt --all -- --check.