diff --git a/packages/studio/src/components/editor/audioFxSignalPath.test.ts b/packages/studio/src/components/editor/audioFxSignalPath.test.ts index b1e56ac0a..407a318b0 100644 --- a/packages/studio/src/components/editor/audioFxSignalPath.test.ts +++ b/packages/studio/src/components/editor/audioFxSignalPath.test.ts @@ -13,9 +13,12 @@ const group = (over: Partial = {}): HfAudioGroup => ({ describe("audioFxSignalPath", () => { // The design doc's §5 mockup, both columns. + // Copy taken from the rendered designs, which are more specific than the + // ASCII stand-ins in the markdown: "vo-1 and vo-2, together" / "into + // Voiceover", not "vo-1, vo-2" / "to Voiceover". it("names what a group sums, and sends it to the mix", () => { expect(audioFxSignalPath("hf-audio-group", "voiceover", [group()])).toEqual({ - inLabel: "vo-1, vo-2", + inLabel: "vo-1 and vo-2, together", outLabel: "to mix", subject: "group", }); @@ -24,7 +27,7 @@ describe("audioFxSignalPath", () => { it("names the group a member feeds, so routing reads from either end", () => { expect(audioFxSignalPath("audio", "vo-1", [group()])).toEqual({ inLabel: "this track", - outLabel: "to Voiceover", + outLabel: "into Voiceover", subject: "track", }); }); diff --git a/packages/studio/src/components/editor/audioFxSignalPath.ts b/packages/studio/src/components/editor/audioFxSignalPath.ts index 1f9e6ef20..43bc54a4a 100644 --- a/packages/studio/src/components/editor/audioFxSignalPath.ts +++ b/packages/studio/src/components/editor/audioFxSignalPath.ts @@ -37,6 +37,12 @@ export const CLIP_SIGNAL_PATH: AudioFxSignalPath = { * `groups` is the resolved set from the composition; `elementId` and `tag` come * from the selection. Pure so the labels can be asserted without a DOM. */ +/** "a", "a and b", "a, b and c" — how the designs read a member list aloud. */ +function joinNatural(items: readonly string[]): string { + if (items.length <= 1) return items[0] ?? ""; + return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`; +} + export function audioFxSignalPath( tag: string | undefined, elementId: string | undefined, @@ -49,11 +55,17 @@ export function audioFxSignalPath( // making one, so it must not look like a bug. const members = group?.memberIds ?? []; return { - inLabel: members.length > 0 ? members.join(", ") : "nothing yet", + // "vo-1 and vo-2, together" — the rendered design's exact phrasing, not a + // comma list. The trailing "together" is the point: it says the group is + // ONE signal hearing both, which is the thing two separate copies of a + // chain cannot do, and it says it without "sum" or "bus". + inLabel: members.length > 0 ? `${joinNatural(members)}, together` : "nothing yet", outLabel: "to mix", subject: "group", }; } const owner = elementId ? groups.find((g) => g.memberIds.includes(elementId)) : undefined; - return owner ? { ...CLIP_SIGNAL_PATH, outLabel: `to ${owner.label}` } : CLIP_SIGNAL_PATH; + // "into Voiceover", not "to" — a member feeds the group, and the design uses + // the preposition that says so. + return owner ? { ...CLIP_SIGNAL_PATH, outLabel: `into ${owner.label}` } : CLIP_SIGNAL_PATH; } diff --git a/packages/studio/src/player/components/TimelineGroupHeader.tsx b/packages/studio/src/player/components/TimelineGroupHeader.tsx index 42933344d..d43d407d8 100644 --- a/packages/studio/src/player/components/TimelineGroupHeader.tsx +++ b/packages/studio/src/player/components/TimelineGroupHeader.tsx @@ -117,7 +117,13 @@ export function TimelineGroupHeader({ - {label} + {/* Struck through, not merely dimmed — the designs are explicit that "a + muted track that only looks dim is a track someone re-mutes by + accident", and a muted GROUP silences every member at once, so it is + the most expensive one to misread. */} + + {label} +