From af1133a1f3d98f3b3929b9616fb3551f39ff17f8 Mon Sep 17 00:00:00 2001 From: maxaeexe Date: Tue, 13 Oct 2026 04:43:53 +0300 Subject: [PATCH] layer.addMarker: add composition marker when no layers are selected --- crates/engine/src/commands/layer_menu.rs | 19 +++++++++++++------ crates/engine/src/tests_markers.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/commands/layer_menu.rs b/crates/engine/src/commands/layer_menu.rs index 755dd898..b5aebb59 100644 --- a/crates/engine/src/commands/layer_menu.rs +++ b/crates/engine/src/commands/layer_menu.rs @@ -6,7 +6,7 @@ use effectcraft_project::{FrameBlend, GroupKind, ItemId, LayerId, LayerSource, M use effectcraft_time::Tick; use serde_json::{Value, json}; -use super::{CommandSpec, b_p, bad, comp_id, f_p, frontend, has_comp, has_layers, layer_mut, layer_p, layers_p, merge_p, str_p}; +use super::{CommandSpec, b_p, bad, comp_id, f_p, frontend, has_comp, has_layers, layer_mut, layer_p, layers_p, merge_p, str_p, time_p}; use crate::{EngineError, Result, Session, cmd}; // ---------------------------------------------------------------- enablement @@ -545,11 +545,18 @@ fn mask_lock_others(s: &mut Session, p: &Value) -> Result { fn add_marker(s: &mut Session, p: &Value) -> Result { let (cid, ids) = layers_p(s, p)?; - if ids.is_empty() { - return Err(bad("layer.addMarker", "no layer")); - } - let t = f_p(p, "time").map(Tick::from_seconds_f64).unwrap_or(s.time()); + let t = time_p(s, p, s.project.comp(cid)); let comment = str_p(p, "comment").unwrap_or_default().to_string(); + if ids.is_empty() { + s.edit("Add Marker", None, |proj, _| { + let c = proj.comp_mut(cid).ok_or(EngineError::NoComp)?; + c.markers.retain(|m| m.time != t); + c.markers.push(Marker { time: t, comment, ..Default::default() }); + c.markers.sort_by_key(|m| m.time); + Ok(()) + })?; + return Ok(json!({"time": t.seconds()})); + } s.edit("Add Marker", None, |proj, _| { for lid in &ids { let l = layer_mut(proj, cid, *lid)?; @@ -842,7 +849,7 @@ pub fn specs() -> Vec { cmd!("layer.mask.lock", "Locked", ["Layer", "Mask"], None, "{layer?, mask?, value?}", has_masks, mask_lock), cmd!("layer.mask.unlockAll", "Unlock All Masks", ["Layer", "Mask"], None, "{layers?}", has_masks, mask_unlock_all), cmd!("layer.mask.lockOthers", "Lock Other Masks", ["Layer", "Mask"], None, "{layer?, mask?}", has_masks, mask_lock_others), - cmd!("layer.addMarker", "Add Marker", ["Layer", "Markers"], None, "{layers?, time?, comment?}", has_layers, add_marker), + cmd!("layer.addMarker", "Add Marker", ["Layer", "Markers"], None, "{layers?, time?, comment?}", has_comp, add_marker), cmd!("layer.markersLock", "Lock Markers", ["Layer", "Markers"], None, "{layers?, value?}", has_layers, markers_lock), cmd!("layer.deleteAllMarkers", "Delete All Markers", ["Layer", "Markers"], None, "{layers?}", has_markers, delete_all_markers), cmd!("layer.trackMatte", "Track Matte", [], None, "{layers?, op: none|alpha|alphaInverted|luma|lumaInverted|above|below}", has_layers, track_matte_op), diff --git a/crates/engine/src/tests_markers.rs b/crates/engine/src/tests_markers.rs index 6673241d..afe4b989 100644 --- a/crates/engine/src/tests_markers.rs +++ b/crates/engine/src/tests_markers.rs @@ -181,3 +181,16 @@ fn nested_comp_markers_follow_the_precomp_layers_timing() { let solid = s.execute("layer.newSolid", json!({"color": "#ffffff"})).unwrap()["layer"].as_u64().unwrap(); assert!(s.execute("markers.nested", json!({"layer": solid})).is_err()); } + +#[test] +fn layer_add_marker_without_selection_adds_comp_marker() { + let (mut s, _) = setup(); + s.execute("time.set", json!({"time": 1.5})).unwrap(); + s.execute("edit.deselectAll", json!({})).unwrap(); + assert!(s.state.selected_layers.is_empty()); + s.execute("layer.addMarker", json!({"comment": "timeline marker"})).unwrap(); + let comp = s.active_comp().unwrap(); + assert_eq!(comp.markers.len(), 1); + assert!((comp.markers[0].time.seconds() - 1.5).abs() < 1e-9); + assert_eq!(comp.markers[0].comment, "timeline marker"); +}