Merge PR #8 from storybored379

Timeline: Slide moves linked audio with the video

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-04 10:50:15 -04:00
commit d0048b0256
2 changed files with 27 additions and 1 deletions

View File

@ -1793,7 +1793,21 @@ fn build() -> Vec<CommandSpec> {
.map(Tick)
.or_else(|| p.get("deltaFrames").and_then(Value::as_i64).map(|f| s.sequence_rate().tick_of(f)))
.unwrap_or_default();
s.edit_sequence("Slide", |q, ctx, _| Ok(edit::slide(q, c, d, ctx)?))?;
let clips = with_links(s, &[c]);
s.edit_sequence("Slide", |q, ctx, _| {
// clamp across all linked partners, then slide them by the common delta
let mut dd = d;
for c in &clips {
let x = edit::slide(&mut q.clone(), *c, dd, ctx)?;
if x.abs() < dd.abs() {
dd = x;
}
}
for c in &clips {
edit::slide(q, *c, dd, ctx)?;
}
Ok(())
})?;
Ok(Value::Null)
}),
cmd!("timeline.rateStretch", "Rate Stretch", [], None, r#"{"clip":id,"edge":"in|out","delta":ticks}"#, has_seq, |s, p| {

View File

@ -83,6 +83,18 @@ fn trim_linked_and_ripple_delete() {
assert_eq!(q.video_tracks[0].items[0].start, s.sequence_rate().tick_of(12), "the trim gap remains");
}
#[test]
fn slide_moves_linked_audio_with_the_video() {
let mut s = demo();
let v = s.active_sequence().unwrap().video_tracks[0].items[1].clone();
s.execute("timeline.slide", json!({"clip": v.id.0, "deltaFrames": 5})).unwrap();
let q = s.active_sequence().unwrap();
let (_, nv) = q.find_item(v.id).unwrap();
let a = q.audio_tracks[0].items.iter().find(|i| i.link == v.link).unwrap();
assert_eq!(nv.start, v.start + s.sequence_rate().tick_of(5));
assert_eq!((a.start, a.source_in), (nv.start, nv.source_in), "linked audio slides too and stays in sync");
}
#[test]
fn zero_fps_is_refused() {
let mut s = demo();