A few kilobytes of internal entities expanded to millions of nodes (each top-level
reference stays under roxmltree's per-reference cap), so a 1 MB file exhausted memory.
SVG keeps DTDs, which Illustrator's exports use for entities, but caps the expanded
document at 4 million nodes. Final Cut Pro XML and FCPXML, which FilmCraft parses
without a node limit, are refused when they declare an entity; real exports never do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Generate row added here drew 描画, which Paint already used, so the Effect menu showed the same
category name twice. 描画 is Adobe's name for Generate, so Paint becomes ペイント, and a test now
rejects two categories sharing a Japanese name.
Found by the review bot on this PR.
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
The strict-clippy split of inverse_dct (#2496) passes n at run time; inlining keeps
inverse_dct_n's compile-time sizes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of the first commit found three places that still asserted the old cleanup: the UI integration
test (which would have failed CI), docs/agents.md, and the comments on TextEdit::created, layer.newText
and the engine test itself.
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
Clicking away from a Type-tool layer without typing deleted it, so a click that was meant to place the
text point lost the layer. AE keeps it in the timeline as <空白のテキストレイヤー>, and the layer came from the
user's click rather than being a mistake to clean up.
edit.deselectAll already routes through text.endEdit, so one change covers both paths. The tests that
asserted the old cleanup now assert the layer survives.
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
LayerInfo, sourceRectAtTime and Layer.source now resolve the effective footage item/size through the collapsed instance EvalCtx, matching the rendered pixels. Adds a regression test with different original and replacement footage sizes.
Carry property, Font and Media Replacement overrides into the collapsed nested EvalCtx so nested layers render beyond the precomp frame instead of falling back to a clipped, flattened image. Key the layer cache on instance overrides to keep distinct collapsed instances isolated.
cineon_log_to_lin takes the exponent scale from its caller. Interpret
Footage > Cineon Settings passes 1 (Gamma 1 gives linear light) and the
Cineon Converter passes 1.7, as before, so the converter's output and
its default Gamma 1.7 are unchanged. The converter's Linear to Log, Log
to Log reference and GPU shader return to their earlier code.
A test checks the converter at its default Gamma and at 2.4, for all
three conversion types, against the earlier formula.
An explicit demo startup no longer saves the session, so the demo cannot replace the last visit's project. The config request is aborted after 5 s, stops reading once the body passes the size limit, and is skipped for ?empty and ?demo. The packaged hosting notes link to the published docs.
JAPANESE_CATEGORIES had 20 of the 21 the Simplified table carries, so the Generate group in the
Effect menu drew its English name. AE calls it 描画.
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
Cancel and Preview-off merge the checkpoint, the preview's result and the project now (new
preview_merge module), so an edit made while the dialog was open stays. OK first brings the
preview up to the draft it finds, and Composition Settings OK applies the Resolution after a kept
preview. The Gradient Editor's OK syncs and sets the draft when it differs from the last preview.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
The referenced model's solo render goes through the comp camera: its
depth of field (focus, aperture, iris, highlights) blurs it, none of
which is in the projection matrix — the evaluated Dof is hashed in now
(CodeRabbit, review of #2410).
A model with an animation clip selected is posed at its layer time
(Animation Speed and Looping are static and hashed): the layer time is
folded into the key, so every time is its own entry. A model without a
clip keeps one key across times, as before.
An Environment light's image lights Advanced 3D models, and its source
can be any layer's render (or any environment layer) — too open-ended
to hash: while one is on, the read is not cached. Each is covered by a
render test.
The toolbar's tool labels come from Tool::label() through header.rs; they were the last English
surface on the chrome. 31 rows, Simplified and Traditional, and the header asks the catalog.
Two English surfaces the translation work so far could not reach.
The dock's tab titles: `PanelKind::title()` is drawn by `dock_ui.rs`'s `tab_titles`, which listed only
the tabs that name a comp, a layer or a script and let every other one fall back to `title()`'s English
- so every panel tab (`Timeline`, `Preview`, `Properties`, `Effects & Presets`, ...) stayed English. The
Window menu already carries a (`window.panel`, title) row in every catalog, so the dock reads that pair
and falls back to the panel catalog for `Markers` (the one panel only it names); `History` and
`ScriptUI Panel` have no row anywhere and keep their English names. `app.auto`'s labels and the dock's
serialized state keep the English title, which the control channel needs.
The scan's blind shapes: `converted_panels_have_no_unwrapped_literals` only sees a literal that follows
a call token, so a label passed to a helper, the third argument of `Painter::text`, a `(key, label, ...)`
table, a `let` binding and a `match` arm were all invisible. The two that can be checked without
guessing are added (`helper_and_painter_labels_are_translated`), with the same single-character
exemption the call scan makes, and the 25 sites they find now ask the catalog (`Apply`, `Analyze`,
`Cancel`, `Create Camera`, `Add Point`, `Remove Point`, `Clear`, `Render`, `Stop`, `Save Current as
Template...`, `New Composition From Footage`, the three search fields, `(no composition)`,
`per second`, `Waveform`, `steps`), with 15 rows added to the Simplified and Traditional catalogs in
the conventions those tables already use (Render 算圖, Apply 套用, Add Point 添加點).
The `uic!` labels and the Keyboard Shortcuts dialog are a different shape and are still to decide
(#1451).
Verified on Linux: cargo fmt --all --check, cargo test -p effectcraft-ui-egui (every suite passes),
cargo clippy -p effectcraft-ui-egui --all-targets -- -D warnings clean. Reverting the dock's catalog
lookup fails `the_dock_draws_every_panel_it_can_in_the_interface_language`; unwrapping a helper or a
painter label fails `helper_and_painter_labels_are_translated` ("panels/project.rs:434: the interface
text \"Search project\" that widgets::search_field draws is not in the catalog").
Also answers #1451's question about languages whose rows have no Simplified row to be a subset
of: six labels the Composition Settings dialog draws from a `(label, value)` table, a tab array and
a `match` arm (`Square Pixels`, `Basic`, `Advanced`, `3D Renderer`, `Black`, `Composition Settings`,
plus `White` from the same `color_name`) had no row anywhere, so every interface drew them in
English; they have Simplified and Traditional rows now, in the existing menu wording. The seventh,
`Lock Aspect Ratio`, is only an automation name - the drawn label is `Lock Aspect Ratio to {}`,
which already had rows, and `comp_settings.rs` built it with `format!` where `dialogs.rs` already
used `tr_args`, so that one is wired now too.
Review fixes (CodeRabbit): the scan now runs over the whole file with line comments blanked, so a
`.text(` call split across lines is still one argument list - which found five more labels the
per-line loop could not see (`No effects. Drag one from Effects & Presets.`, the colour-ring hint in
`fx_editors.rs`, the Learn blurb, the Script Console hint, the Templates blurb, plus
`Building waveform…` from the review), all wired with rows. And the New Composition title is back to
the English source: `modal_fit` keys the modal id and the drag offset on the title (its own comment
says the id stays on the untranslated source) and translates the heading itself, so `tr(..)` there
would have moved the dialog when the interface language changed.
And the argument list is bounded by the call's closing parenthesis: `args_of` used to flush a
trailing argument at end-of-string, so a last argument without a trailing comma carried the `)` and
the source after it, which made the check skip it as "not a literal". Reverting that one line is a
two-line test (a literal last argument in `header.rs` is caught now and was silent before).
The label scan also understands the two string forms a label can be written in: a plain string ends
at the first unescaped quote, a raw string (`r"…"`, `r#"…"#`) at a quote followed by as many `#` as
opened it, and `literal_text` decodes the argument before the exemptions run - so an escaped or raw
label is checked like any other instead of being skipped.
Two more scan fixes from review: `without_comments` tracks the escape state rather than the previous
character, so a string ending in a backslash (`"a\\"`) closes where it should; and the scanned range
ends at the test *module* - the first `#[cfg(test)]` followed by a `mod` - rather than at the first
`#[cfg(test)]`, because `dialogs.rs` keeps a test-only helper above its test module and production
code between them was not scanned.
And call tokens are looked for in a code-only mask: comments blanked, the contents of plain and raw
strings blanked too, with the quotes kept and every byte offset preserved (a blanked character
becomes as many spaces as its UTF-8 length, and a debug assertion pins the mask length). The argument
list is read from the original source at that offset, so a sample string that quotes a call cannot be
mistaken for one. Building the mask from chars instead of bytes shifted every offset after the first
non-ASCII character, which the debug assertion now catches.
The dock's display path has its own test now: `tab_titles` is called with `general.language` set to
Chinese and the labels a window shows are asserted (`Timeline` 时间轴 ...), plus `History` keeping its
English tab and the English interface keeping all of them. Reverting the dock to `PanelKind::title()`
fails it with "Timeline is not drawn in Chinese", which the catalog-only test could not see.
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).
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