diff --git a/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx b/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx
index 706595b82..e86a976ac 100644
--- a/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx
+++ b/packages/studio/src/components/editor/propertyPanelFxCarveModule.tsx
@@ -230,6 +230,8 @@ export function FxCarveModule({
// sets `tracking-normal`, and two Tailwind tracking utilities on one
// element resolve by stylesheet order, not by the order written.
style={{ color: tint, letterSpacing: "0.16em" }}
+ // Truncates in a narrow panel like every other name in the rack.
+ title="Voiceover carve"
aria-expanded={open}
onClick={onToggleOpen}
>
@@ -256,7 +258,10 @@ export function FxCarveModule({
-
+
Listen to
{soleVoice ? (
diff --git a/packages/studio/src/components/editor/propertyPanelFxControls.test.tsx b/packages/studio/src/components/editor/propertyPanelFxControls.test.tsx
index e49887582..68e35c8e8 100644
--- a/packages/studio/src/components/editor/propertyPanelFxControls.test.tsx
+++ b/packages/studio/src/components/editor/propertyPanelFxControls.test.tsx
@@ -113,3 +113,48 @@ describe("FxParamRow commit", () => {
expect(onCommit).not.toHaveBeenCalled();
});
});
+
+describe("FxParamRow label tooltip", () => {
+ // The label column is a fixed 86px and truncates: "Gap between repeats" reads
+ // as "Gap between re…", "How many repeats" as "How many repe…". The row
+ // already carried a `title`, but that is the HINT — what the knob does — so a
+ // truncated name had no way to be read in full.
+ const LONG: HfAudioFxNumberParam = {
+ kind: "number",
+ key: "time",
+ label: "Gap between repeats",
+ min: 0,
+ max: 1000,
+ step: 1,
+ default: 250,
+ unit: "ms",
+ hint: "How long before the echo comes back.",
+ };
+
+ it("titles the label with the full name, and leaves the hint on the row", () => {
+ const host = document.createElement("div");
+ document.body.append(host);
+ act(() => {
+ createRoot(host).render();
+ });
+ const label = host.querySelector(".hf-fx-label");
+ expect(label?.getAttribute("title")).toBe("Gap between repeats");
+ // The hint stays where it was: the two are different questions, and the
+ // name is not a substitute for the explanation either.
+ expect(host.querySelector(".hf-fx-row")?.getAttribute("title")).toBe(
+ "How long before the echo comes back.",
+ );
+ });
+
+ it("titles it even with no hint to fall back on", () => {
+ const host = document.createElement("div");
+ document.body.append(host);
+ const { hint: _hint, ...noHint } = LONG;
+ act(() => {
+ createRoot(host).render();
+ });
+ expect(host.querySelector(".hf-fx-label")?.getAttribute("title")).toBe(
+ "Gap between repeats",
+ );
+ });
+});
diff --git a/packages/studio/src/components/editor/propertyPanelFxControls.tsx b/packages/studio/src/components/editor/propertyPanelFxControls.tsx
index 7b41e1f16..6895b0787 100644
--- a/packages/studio/src/components/editor/propertyPanelFxControls.tsx
+++ b/packages/studio/src/components/editor/propertyPanelFxControls.tsx
@@ -188,7 +188,15 @@ export function FxParamRow({
if (param.kind === "enum") {
return (