From 6fc39eb1333735f13dbac106e66d9628de25720b Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 4 Oct 2026 23:58:00 +0700 Subject: [PATCH] =?UTF-8?q?M3.13:=20menu=20bar:=20Group/Ungroup=20Shapes?= =?UTF-8?q?=20in=20the=20Layer=20menu,=20Window=20=E2=96=B8=20Learn,=20flo?= =?UTF-8?q?wchart=20shortcuts,=20Center=20In=20View=20shortcut,=20Reveal?= =?UTF-8?q?=20in=20Explorer=20on=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Group Shapes (Ctrl+G) / Ungroup Shapes (Ctrl+Shift+G) sit in the Layer menu itself, as in After Effects (they were under Mask and Shape Path). - Window ▸ Learn opens the Home screen's Learn tab. - Composition ▸ Composition Flowchart is Ctrl+Shift+F11 and Window ▸ Flowchart Ctrl+F11 (the flowchart had the panel's shortcut and the panel none). - Layer ▸ Transform ▸ Center In View shows Ctrl+Home. - "Reveal in Finder" reads "Reveal in Explorer" on Windows and "Reveal in File Manager" on Linux (`menus::platform_label`). - Docs: parity (View ▸ New Viewer noted as not yet). Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/commands/frontend.rs | 2 +- crates/engine/src/commands/paths.rs | 12 ++---------- crates/engine/src/menus.rs | 26 ++++++++++++++++++-------- crates/engine/src/menus/tests.rs | 24 ++++++++++++++++++++++++ docs/parity.md | 10 ++++++++++ 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/crates/engine/src/commands/frontend.rs b/crates/engine/src/commands/frontend.rs index 62cfd351..a1e74157 100644 --- a/crates/engine/src/commands/frontend.rs +++ b/crates/engine/src/commands/frontend.rs @@ -85,7 +85,7 @@ pub fn specs() -> Vec { fe!("window.saveWorkspaceAs", "Save as New Workspace...", ["Window", "Workspace"], None, "{name}", always), fe!("window.editWorkspaces", "Edit Workspaces...", ["Window", "Workspace"], None, "{name, rename?: new name, delete?: bool}", always), fe!("window.resetWorkspace", "Reset to Saved Layout", ["Window", "Workspace"], None, "{}", always), - fe!("comp.flowchart", "Composition Flowchart", ["Composition"], Some("Cmd+F11"), "{}", always), + fe!("comp.flowchart", "Composition Flowchart", ["Composition"], Some("Cmd+Shift+F11"), "{}", always), fe!("comp.miniFlowchart", "Composition Mini-Flowchart", ["Composition"], Some("Tab"), "{}", has_comp), fe!("layer.openLayer", "Open Layer", ["Layer"], None, "{layer?}", has_layers), fe!("effect.manage", "Manage Effects...", ["Effect"], None, "{}", always), diff --git a/crates/engine/src/commands/paths.rs b/crates/engine/src/commands/paths.rs index 57736101..8219306a 100644 --- a/crates/engine/src/commands/paths.rs +++ b/crates/engine/src/commands/paths.rs @@ -450,16 +450,8 @@ pub fn specs() -> Vec { vec![ cmd!("path.rotoBezier", "RotoBezier", ["Layer", "Mask and Shape Path"], None, "{layer?, mask?, value?}", has_layers, roto_bezier), cmd!("path.convertToBezier", "Convert To Bezier Path", ["Layer", "Mask and Shape Path"], None, "{layer?, mask?}", has_layers, convert_to_bezier), - cmd!("path.groupShapes", "Group Shapes", ["Layer", "Mask and Shape Path"], Some("Cmd+G"), "{layer?, items?: [uid]}", has_shape_items, group_shapes), - cmd!( - "path.ungroupShapes", - "Ungroup Shapes", - ["Layer", "Mask and Shape Path"], - Some("Cmd+Shift+G"), - "{layer?, items?: [group uid]}", - has_shape_items, - ungroup_shapes - ), + cmd!("path.groupShapes", "Group Shapes", ["Layer"], Some("Cmd+G"), "{layer?, items?: [uid]}", has_shape_items, group_shapes), + cmd!("path.ungroupShapes", "Ungroup Shapes", ["Layer"], Some("Cmd+Shift+G"), "{layer?, items?: [group uid]}", has_shape_items, ungroup_shapes), cmd!( "path.setFirstVertex", "Set First Vertex", diff --git a/crates/engine/src/menus.rs b/crates/engine/src/menus.rs index 2a197579..02fa8177 100644 --- a/crates/engine/src/menus.rs +++ b/crates/engine/src/menus.rs @@ -344,6 +344,15 @@ pub fn checked(s: &Session, command: &str, params: &Value) -> Option { } } +/// Labels that name the platform's own tools: "Reveal in Finder" is "Reveal in Explorer" on +/// Windows (as in After Effects) and "Reveal in File Manager" elsewhere. +fn platform_label(label: &str, mac: bool) -> String { + match label.strip_prefix("Reveal in Finder") { + Some(rest) if !mac => format!("Reveal in {}{rest}", if cfg!(target_os = "windows") { "Explorer" } else { "File Manager" }), + _ => label.to_string(), + } +} + /// Parse the tree text. `mac` selects `[mac]` / `[!mac]` lines. pub fn parse(text: &str, mac: bool) -> Result, String> { struct Line<'a> { @@ -378,7 +387,7 @@ pub fn parse(text: &str, mac: bool) -> Result, String> { } lines.push(Line { depth, body }); } - fn build(lines: &[Line], i: &mut usize, depth: usize) -> Result, String> { + fn build(lines: &[Line], i: &mut usize, depth: usize, mac: bool) -> Result, String> { let mut out = vec![]; while *i < lines.len() { let l = &lines[*i]; @@ -402,7 +411,7 @@ pub fn parse(text: &str, mac: bool) -> Result, String> { continue; } let mut parts = l.body.split(" | "); - let label = parts.next().unwrap_or_default().trim().to_string(); + let label = platform_label(parts.next().unwrap_or_default().trim(), mac); match parts.next() { Some(cmd) => { let cmd = cmd.trim(); @@ -416,7 +425,7 @@ pub fn parse(text: &str, mac: bool) -> Result, String> { out.push(MenuNode::Item(MenuEntry { label, command, params, shortcut })); } None => { - let children = build(lines, i, depth + 1)?; + let children = build(lines, i, depth + 1, mac)?; if children.is_empty() { return Err(format!("`{label}` has neither a command nor children")); } @@ -427,7 +436,7 @@ pub fn parse(text: &str, mac: bool) -> Result, String> { Ok(out) } let mut i = 0; - build(&lines, &mut i, 0) + build(&lines, &mut i, 0, mac) } /// Effect ▸ category submenus from the effect registry. @@ -744,8 +753,6 @@ Layer RotoBezier | path.rotoBezier Closed | mask.setClosed Convert To Bezier Path | path.convertToBezier - Group Shapes | path.groupShapes - Ungroup Shapes | path.ungroupShapes Set First Vertex | path.setFirstVertex Free Transform Points | path.freeTransform Quality @@ -782,7 +789,7 @@ Layer --- Flip Horizontal | layer.transform {"op":"flipH"} Flip Vertical | layer.transform {"op":"flipV"} - Center In View | layer.transform {"op":"center"} + Center In View | layer.transform {"op":"center"} | Cmd+Home Center Anchor Point in Layer Content | layer.centerAnchor Fit to Comp | layer.transform {"op":"fit"} | Cmd+Alt+F Fit to Comp Width | layer.transform {"op":"fitWidth"} | Cmd+Alt+Shift+H @@ -883,6 +890,8 @@ Layer Color Overlay | layer.style.colorOverlay Gradient Overlay | layer.style.gradientOverlay Stroke | layer.style.stroke + Group Shapes | path.groupShapes + Ungroup Shapes | path.ungroupShapes --- Arrange Bring Layer to Front | layer.arrange {"to":"front"} | Cmd+Shift+] @@ -1151,6 +1160,7 @@ Window Effects & Presets | window.panel {"panel":"effectsPresets"} | Cmd+5 Essential Graphics | window.panel {"panel":"essentialGraphics"} Info | window.panel {"panel":"info"} | Cmd+2 + Learn | help.inAppTutorials Lumetri Scopes | window.panel {"panel":"lumetriScopes"} Mask Interpolation | window.panel {"panel":"maskInterpolation"} Media Browser | window.panel {"panel":"mediaBrowser"} @@ -1169,7 +1179,7 @@ Window --- Composition | window.panel {"panel":"composition"} Effect Controls | window.panel {"panel":"effectControls"} | F3 - Flowchart | window.panel {"panel":"flowchart"} + Flowchart | window.panel {"panel":"flowchart"} | Cmd+F11 Footage | window.panel {"panel":"footage"} Layer | window.panel {"panel":"layer"} Project | window.panel {"panel":"project"} | Cmd+0 diff --git a/crates/engine/src/menus/tests.rs b/crates/engine/src/menus/tests.rs index 6fdbd2a7..913a0e92 100644 --- a/crates/engine/src/menus/tests.rs +++ b/crates/engine/src/menus/tests.rs @@ -155,3 +155,27 @@ fn parse_rejects_bad_trees() { assert!(parse("File\n Odd | file.save\n", true).is_err()); assert!(parse("File\n Bad | file.save {nope}\n", true).is_err()); } + +#[test] +fn m3_13_entries_follow_after_effects() { + let find = |mac: bool, path: &[&str], label: &str| platform_entries(mac).into_iter().find(|(p, e)| p == path && e.label == label).map(|(_, e)| e); + // Group / Ungroup Shapes sit in the Layer menu itself. + for mac in [true, false] { + assert_eq!(find(mac, &["Layer"], "Group Shapes").unwrap().shortcut.as_deref(), Some("Cmd+G")); + assert_eq!(find(mac, &["Layer"], "Ungroup Shapes").unwrap().shortcut.as_deref(), Some("Cmd+Shift+G")); + assert!(find(mac, &["Layer", "Mask and Shape Path"], "Group Shapes").is_none()); + // Composition Flowchart Ctrl+Shift+F11, Window ▸ Flowchart Ctrl+F11; Window ▸ Learn. + assert_eq!(find(mac, &["Composition"], "Composition Flowchart").unwrap().shortcut.as_deref(), Some("Cmd+Shift+F11")); + assert_eq!(find(mac, &["Window"], "Flowchart").unwrap().shortcut.as_deref(), Some("Cmd+F11")); + assert_eq!(find(mac, &["Window"], "Learn").unwrap().command, "help.inAppTutorials"); + assert_eq!(find(mac, &["Layer", "Transform"], "Center In View").unwrap().shortcut.as_deref(), Some("Cmd+Home")); + } + // "Reveal in Finder" names the platform's file browser. + assert!(find(true, &["File"], "Reveal in Finder").is_some()); + let other = if cfg!(target_os = "windows") { "Reveal in Explorer" } else { "Reveal in File Manager" }; + for path in [&["File"][..], &["Layer"], &["Layer", "Reveal"]] { + assert!(find(false, path, other).is_some(), "{path:?}"); + assert!(find(false, path, "Reveal in Finder").is_none(), "{path:?}"); + } + assert_eq!(platform_label("Reveal Layer Source in Project", false), "Reveal Layer Source in Project"); +} diff --git a/docs/parity.md b/docs/parity.md index 59301747..2d1637f1 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -383,6 +383,16 @@ Browser in the web app (landed in M13.10: browser storage and File System Access wrong signatures and versions, fuel exhaustion, traps and bit-identical output; script `Socket` validates ports, and ScriptUI resource strings accept trailing array commas. +## Update: M3.13 menu bar + +Group Shapes (Ctrl+G) and Ungroup Shapes (Ctrl+Shift+G) moved to the Layer menu itself, where +After Effects has them; Window ▸ Learn opens the Home screen's Learn tab; Composition ▸ +Composition Flowchart is Ctrl+Shift+F11 and Window ▸ Flowchart Ctrl+F11 (the flowchart had +Window's shortcut); Layer ▸ Transform ▸ Center In View shows Ctrl+Home; "Reveal in Finder" reads +"Reveal in Explorer" on Windows (and "Reveal in File Manager" on Linux). Not yet: View ▸ New +Viewer (several unlocked Composition viewers at once; View ▸ Split with New Locked Viewer adds +the one locked viewer the interface has). + ## Update: M3.12 Pre-compose and New Comp from Selection - **Pre-compose** makes the new composition with the original's settings (pixel aspect,