An effect in a sub-folder was drawn left of that sub-folder's label (Gaussian Blur at 47 points,
Blur & Sharpen at 55), so folders and effects read as one level. Rows now follow one rule,
`tree_x(depth)`: an effect sits one level deeper than its folder (sub-folder or, for loose
effects, the top folder), with its icon and label where a folder at that depth has them; user and
built-in presets and Lumetri presets the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
The first effect added from the library after a clip's fixed effects (Motion, Opacity, Time
Remapping; Volume, Channel Volume, Panner on audio) gets an accent-coloured line on its top edge.
It is drawn on the row's edge, so no row moves; with only fixed effects there is none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
#630 shipped German for the menus. This adds the other 3,213 strings, so
de.tsv has every entry of the Spanish catalog (3,590): panels, dialogs,
effects and their parameters, transitions, presets, Settings, shortcut
commands, export presets, tooltips and status messages. Same style as the
menus (du, infinitives, placeholders, ellipses and escapes kept); the
glossary in the header is extended. The 377 menu entries are unchanged.
german_entries_cover_the_original_menu_catalog required every entry to be
a menu label. It becomes german_covers_the_menus_and_has_no_dead_entries,
like the French and Italian tests: every entry needs a Spanish counterpart,
the core menus and the native macOS app menu are required, later strings
may stay English. german_renders_with_bundled_fonts checks the umlauts,
ß and German quotation marks against the bundled fonts.
Docs: ROADMAP.md, docs/localization-parity.md, docs/gaps.md, ui-egui README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KJhvSJ5odpWzFw9HhPzAix
Letter spacing (Tracking) does animate in the engine — keyframes on the text
layer interpolate and the rendered width follows them — but the Essential
Graphics panel had no way to start that: its number and point rows had no
stopwatch, so a change there always set the value for the whole clip.
Every animatable number/point row in the panel (Tracking, Leading, Font
Size, Baseline Shift, Position, Anchor, Scale, Rotation, Opacity…) now has
the same stopwatch as Effect Controls. It calls `effects.toggleAnimation` on
the layer; once a property animates, a change in the panel adds or updates a
keyframe at the playhead (graphics.set was already keyframe-aware). A
Type-tool character selection edits character styles, which have no
keyframes, so those rows show no stopwatch while it is active.
Automation id: `graphics.prop.<id>.stopwatch`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
A track can carry a colour label, as many NLEs offer. The strip at the
left edge of a track header opens a menu with None and the 16 labels
(with the user's label names and colours, as in Edit > Label). The label
tints the header and the lane.
- `Track.label: Option<Label>`, serde default and skipped when None, so
older projects load and plain tracks save as before
- `timeline.setTrack {label}` (a label name; "" / "None" / null clears),
an undoable Track Settings step; unknown names are refused
- `sequence.inspect` reports each track's `label`
- automation ids `timeline.track.<V1|A1…>.color` and `.color.<Label>`
Renaming, adding and deleting tracks from the header are covered by the
open PRs for #654 and #1630, so this only adds the colour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
"Animate zoom, scrolling and menus" (appearance.animations, on by default). Off, the Timeline
zooms and scrolls in one step instead of easing, and egui's fades and slides (menus, collapsing
sections, tooltips) are instant. The style is checked every frame, because a theme or font change
installs it again. Translated in the catalogs that cover the Settings rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
The Graphics tab of the Text panel was a placeholder ("Graphics text search
arrives with M10.1–M10.2"), so text in graphics never showed up there.
It now lists the text of every text layer of every graphic clip in the
active sequence, as Premiere's does: in timeline order (then lower tracks
first, then paint order), each entry with its clip's start timecode and
track (and the layer's name when it has one). A search filters by text or
name. The timecode moves the playhead to the clip and selects the clip and
the layer (so Essential Graphics and the Program monitor's handles follow);
the text edits in place and is committed with `graphics.set` when the field
loses focus — one undo step, per-character styles kept as for any text edit.
Text that is keyframed shows its value at the playhead when the playhead is
in the clip.
Automation ids: `text.graphics.search`, `text.graphics.<clip>.<layer>.row`,
`.goto`, `.text`. The three new messages are translated in the catalogs the
coverage test requires.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
Every frame shown as a timeline tile or a Project panel hover scrub got a
texture of its own (thumb-<item>-<frame>-<width>) that was kept until the
project was replaced, so scrolling, zooming and scrubbing grew GPU (on
integrated GPUs, system) memory without bound. Beyond 512 thumbnail
textures the least recently used are freed, never one drawn in the
current pass.
Refs #1682
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rendered previews are full-resolution ProRes (gigabytes a minute), and
PreviewStore::open read each file whole into memory, keeping up to six
open; the render also opened every finished segment right away. Rendering
previews and playing them back filled memory until the OS killed the
app. Native builds now open preview files through a file reader (the
container's index now, frames on demand), refusing any whole-file read,
and a finished segment is opened when it is first played.
Fixes#2185
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
Writer::add rejected a duplicate name by comparing the new name with every
sibling through compare_names, which allocates two Vec<u16> per call, so n
children in one storage cost O(n^2) allocating comparisons. An AAF export
puts every marker and component in one storage: 20 000 markers took 58 s,
20 000 clips 56 s, 20 000 connected clips 87 s and 100 000 markers never
finished (an FCPXML of 1 MB is enough). Doubling the input quadrupled the
time; every other exporter stayed linear.
Each storage keeps a set of its children's names as sibling order compares
them (UTF-16 code units, upper-cased), so the duplicate check is a hash
lookup. Same result: case-insensitive, also beyond ASCII, per storage.
20 000 markers now export to AAF in 0.38 s, 100 000 in 2.0 s, 20 000 clips in
0.23 s and 20 000 connected clips in 0.45 s.
Tests: adding 40 000 children must take under 3 s (0.02 s now, 21 s before)
and the duplicate rules are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/ui-egui/src/calc.rs:34-41 (eval)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/tts-text/src/normalize.rs:352 (parse_amount, used by parse_money at :266)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>