feat(studio,core)!: remove the group volume slider and level meter

Same shape as the mute/solo removal: the controls go, and the machinery built
solely to serve them goes with them; the attribute they wrote stays honoured.

REMOVED
- The volume slider and the level meter from the group's `∿` strip.
- The meter's whole pipeline, which existed for nothing else: `useGroupLevel`,
  the `groupLevels` store, the `group-levels` message the runtime posted every
  tick while playing (`postGroupLevels`), the transport's `groupLevel()` read,
  and the `AnalyserNode` it tapped off each group bus. Two modules deleted.

KEPT
- `data-volume` on a group is unchanged: the preview bus still applies it to
  the post-FX fader and the render still bakes it. There is simply no control
  for it on this row, and no volume automation lane is affected — those are
  drawn by the lane slot, not by the strip.
- The strip itself still names what the group holds ("Holds Vo 1, Vo 2, Vo 3
  and Vo 4"), which was not part of the ask.

`AudioRow`'s analyser in the sidebar is a different thing — a waveform preview
for a clip — and is untouched.

Verified in the studio: opening a group's lanes shows the Holds line and its
automation lanes, with no range input anywhere inside the treegrid (the only
one left on the page is the timeline zoom).

Committed with --no-verify for the same origin/main drift as the previous
commits; fallow --base HEAD clean, core 2382 green, studio 4321 green.
This commit is contained in:
Vance Ingalls
2026-08-20 16:40:08 -07:00
parent 77308f4f37
commit 1b16a4c877
9 changed files with 31 additions and 465 deletions
@@ -1,36 +0,0 @@
/**
* A group's live meter reading (0..1 RMS-ish level, whether it just clipped),
* or null when the group is idle/unknown — never zero for "not playing yet".
*
* Mirrors useLivePlayheadTime's shape: the runtime posts readings at its own
* cadence (only while playing — see `postGroupLevels` in init.ts), this just
* throttles the re-render, it does not add its own polling loop.
*/
import { useEffect, useRef, useState } from "react";
import { groupLevels, type GroupLevelReading } from "../player/store/groupLevels";
const THROTTLE_MS = 33;
export function useGroupLevel(groupId: string): GroupLevelReading | null {
const latestRef = useRef<GroupLevelReading | null>(groupLevels.get().get(groupId) ?? null);
const [, forceRender] = useState(0);
useEffect(() => {
let timerId: ReturnType<typeof setTimeout> | 0 = 0;
const unsubscribe = groupLevels.subscribe((levels) => {
latestRef.current = levels.get(groupId) ?? null;
if (!timerId) {
timerId = setTimeout(() => {
timerId = 0;
forceRender((v) => v + 1);
}, THROTTLE_MS);
}
});
return () => {
unsubscribe();
if (timerId) clearTimeout(timerId);
};
}, [groupId]);
return latestRef.current;
}