From bd0bcd66beb1b75065585ec66d1b43071e358562 Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Wed, 19 Aug 2026 13:28:41 -0700 Subject: [PATCH] fix(studio): hide a group's lane toggle when it automates nothing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A track header already gates its own automation toggle on having something to disclose (`disclosable`). A group's did not, so every group offered a wave button that opened an empty row — which is how the author learns the group has no automation, one click too late. Gated on the same count the badge already used. Nothing becomes unreachable: automation appears on a group by being written, from the rack or a keyframe, not by opening this. The suite had nothing covering the toggle's presence, so the test is new and was mutation-checked — forcing the condition true fails it. Co-Authored-By: Claude Opus 5 (1M context) --- .../player/components/TimelineGroupHeader.tsx | 46 +++++++++++-------- .../components/TimelineGroupRow.test.tsx | 28 ++++++++++- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/packages/studio/src/player/components/TimelineGroupHeader.tsx b/packages/studio/src/player/components/TimelineGroupHeader.tsx index 0f815eb6f..d8890ce09 100644 --- a/packages/studio/src/player/components/TimelineGroupHeader.tsx +++ b/packages/studio/src/player/components/TimelineGroupHeader.tsx @@ -141,27 +141,33 @@ export function TimelineGroupHeader({ auditionSpans={auditionSpans} onOpenRack={onOpenFxRack} /> - + + )} ); diff --git a/packages/studio/src/player/components/TimelineGroupRow.test.tsx b/packages/studio/src/player/components/TimelineGroupRow.test.tsx index 295d57321..0b89493a7 100644 --- a/packages/studio/src/player/components/TimelineGroupRow.test.tsx +++ b/packages/studio/src/player/components/TimelineGroupRow.test.tsx @@ -36,7 +36,7 @@ const GROUP: TimelineTrackGroupInfo = { hidden: false, }; -function renderRow() { +function renderRow(overrides: Partial = {}) { const onSetAudioGroupAttributeQuiet = vi.fn(); const onSetElementAttributeQuiet = vi.fn(); const host = document.createElement("div"); @@ -47,7 +47,7 @@ function renderRow() { { // The members are the point: not one write reaches them. expect(onSetElementAttributeQuiet).not.toHaveBeenCalled(); }); + + // A disclosure over nothing tells the author their group has no automation + // only AFTER they open an empty row. Track headers already gate their own + // toggle on having something to disclose; the group's did not. + it("hides the lane toggle until the group actually automates something", () => { + const laneToggle = (host: HTMLElement) => + Array.from(host.querySelectorAll("button")).find((b) => + /lanes$/.test(b.getAttribute("aria-label") ?? ""), + ); + + expect(laneToggle(renderRow().host)).toBeUndefined(); + + const automated = renderRow({ + fxChain: JSON.stringify({ + version: 1, + nodes: [{ type: "peaking", id: "p1", params: { frequency: 1000, gain: -3, q: 1 } }], + }), + automation: JSON.stringify({ + version: 1, + lanes: [{ target: "fx.p1.gain", points: [{ t: 0, v: 0 }] }], + }), + }); + expect(laneToggle(automated.host)).toBeDefined(); + }); });