Browse / github.com/storytold/filmcraft / patch
Fix trim_caption moving edges of captions shorter than one frame
Fix trim_caption moving edges of captions shorter than one frame Found while testing `crates/edit/src/captions.rs:~170 (trim_caption, Edge::In branch)` (no existing issue): trim_caption on a caption shorter than one frame moves its In edge backwards into the previous caption. ## Cause trim_caption clamped the delta with bounds like `c.duration - min_duration`. For a caption shorter than the minimum duration that bound is negative, and `Tick::clamp` (max(lo).min(hi)) lets the negative upper bound win. A forward trim of the In edge then returned a large negative delta, which moved the start into the previous caption and before time zero. ## Fix The clamp bo
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Silvertree2010 (imported from pull request #3440, open)
- Base
10d626ccc4888e0f020f9ca314c2bc9148ded4f4- Size
- +30 −2 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
6f026835376a8ccb13630b35
How it went
No other operator has reported using it yet.
Diff
raw · crates/edit/src/captions.rs crates/edit/src/tests.rs
@@ -156,8 +156,9 @@ pub fn trim_caption(seq: &mut Sequence, id: ClipId, edge: Edge, delta: Tick, ctx let (prev, next) = neighbours(tr, id); let c = tr.caption_mut(id).ok_or(EditError::NoItem(id))?; let d = match edge {- Edge::In => delta.clamp(prev - c.start, c.duration - ctx.min_duration),- Edge::Out => delta.clamp(ctx.min_duration - c.duration, next - c.end()),+ // bounds never cross: a caption already shorter than the minimum cannot shrink+ Edge::In => delta.clamp((prev - c.start).min(Tick::ZERO), (c.duration - ctx.min_duration).max(Tick::ZERO)),+ Edge::Out => delta.clamp((ctx.min_duration - c.duration).min(Tick::ZERO), (next - c.end()).max(Tick::ZERO)), }; if d == Tick::ZERO { return Err(EditError::Nothing);@@ -1079,3 +1079,30 @@ fn moving_an_edge_keeps_the_media_under_the_timeline() { } } }++#[test]+fn trim_caption_shorter_than_min_duration_does_not_cross_bounds() {+ use filmcraft_project::{Caption, CaptionFormat, CaptionTrack};+ for edge in [Edge::In, Edge::Out] {+ let mut fx = Fx::new();+ let tid = TrackId(900);+ let mut tr = CaptionTrack::new(tid, "c".into(), CaptionFormat::Subtitle);+ for (id, start) in [(1, 0), (2, 1000)] {+ tr.captions.push(Caption {+ id: ClipId(id),+ start: Tick(start),+ duration: Tick(1000),+ text: "x".into(),+ speaker: None,+ cue_id: None,+ settings: String::new(),+ });+ }+ fx.seq.caption_tracks.push(tr);+ let ctx = Fx::ctx(&mut fx.next);+ let _ = crate::captions::trim_caption(&mut fx.seq, ClipId(2), edge, Tick(5), &ctx);+ let c = fx.seq.find_caption(ClipId(2)).unwrap().1;+ assert!(c.start >= Tick(1000), "{edge:?}: start moved back into previous caption: {:?}", c.start);+ assert!(c.duration >= Tick(1000), "{edge:?}: caption shrank: {:?}", c.duration);+ }+}