Skip to content

Commit d5f62c8

Browse files
vanceingallsclaude
andcommitted
refactor(studio): split the FX node row out of FxSection
Clears the health findings the FX stack left behind: the chain-node render callback was a 70-line closure over half of FxSection's state, and the two reorder arrows were the same button written twice. Also drops two exports with no consumers, and registers the audio FX runtime stub as an entry point — it is bundled by file path, so nothing imports it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent cd8d016 commit d5f62c8

3 files changed

Lines changed: 170 additions & 73 deletions

File tree

packages/core/src/audio/audioFxWorklets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Kept as a source string so it can be registered from a Blob URL without a
1010
* separate bundled asset, which keeps the studio's build unchanged.
1111
*/
12-
export const AUDIO_FX_WORKLET_SOURCE = `
12+
const AUDIO_FX_WORKLET_SOURCE = `
1313
const dbToLin = (db) => Math.pow(10, db / 20);
1414
1515
/**

packages/studio/src/components/editor/propertyPanelFxControls.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,5 +184,3 @@ export function FxParams({ def, params, onChange, onCommit, disabled }: FxParams
184184
</div>
185185
);
186186
}
187-
188-
export const __testables = { toSlider, fromSlider, display };

packages/studio/src/components/editor/propertyPanelFxSection.tsx

Lines changed: 169 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,160 @@ export interface AudioTrackOption {
3434
label: string;
3535
}
3636

37+
interface FxNodeRowProps {
38+
node: HfAudioFxNode;
39+
index: number;
40+
open: boolean;
41+
/** Last in the chain, so it cannot move further down. */
42+
last: boolean;
43+
disabled?: boolean;
44+
onToggleOpen(): void;
45+
onUpdate(index: number, patch: Partial<HfAudioFxNode>): void;
46+
onMove(index: number, delta: number): void;
47+
onRemove(index: number): void;
48+
onPreview(index: number, params: HfAudioFxParamValues): void;
49+
}
50+
51+
/** Reorder arrow. Disabled at the end of the chain it would move past. */
52+
function FxMoveButton({
53+
label,
54+
glyph,
55+
disabled,
56+
onClick,
57+
}: {
58+
label: string;
59+
glyph: string;
60+
disabled: boolean;
61+
onClick(): void;
62+
}) {
63+
return (
64+
<button
65+
type="button"
66+
className="hf-fx-move px-1 font-mono text-[10px] text-panel-text-4 hover:text-panel-text-0 disabled:opacity-25"
67+
title={label}
68+
disabled={disabled}
69+
onClick={onClick}
70+
>
71+
{glyph}
72+
</button>
73+
);
74+
}
75+
76+
/** Name, bypass, reorder and remove for one effect. */
77+
function FxNodeHeader({
78+
label,
79+
open,
80+
bypassed,
81+
first,
82+
last,
83+
disabled,
84+
onToggleOpen,
85+
onToggleBypass,
86+
onMove,
87+
onRemove,
88+
}: {
89+
label: string;
90+
open: boolean;
91+
bypassed: boolean;
92+
first: boolean;
93+
last: boolean;
94+
disabled?: boolean;
95+
onToggleOpen(): void;
96+
onToggleBypass(): void;
97+
onMove(delta: number): void;
98+
onRemove(): void;
99+
}) {
100+
return (
101+
<div className="hf-fx-node-head flex min-h-7 items-center gap-1 px-1.5">
102+
<button
103+
type="button"
104+
className="hf-fx-node-name flex-1 truncate text-left text-[11px] font-semibold text-panel-text-1 hover:text-panel-text-0"
105+
aria-expanded={open}
106+
onClick={onToggleOpen}
107+
>
108+
{label}
109+
</button>
110+
<button
111+
type="button"
112+
className="hf-fx-bypass rounded-[3px] border border-panel-border-input px-1.5 py-0.5 font-mono text-[9px] text-panel-text-4 hover:text-panel-text-0 disabled:opacity-40"
113+
aria-pressed={bypassed}
114+
title={bypassed ? "Enable" : "Bypass"}
115+
disabled={disabled}
116+
onClick={onToggleBypass}
117+
>
118+
{bypassed ? "Off" : "On"}
119+
</button>
120+
<FxMoveButton
121+
label="Move up"
122+
glyph="&uarr;"
123+
disabled={Boolean(disabled) || first}
124+
onClick={() => onMove(-1)}
125+
/>
126+
<FxMoveButton
127+
label="Move down"
128+
glyph="&darr;"
129+
disabled={Boolean(disabled) || last}
130+
onClick={() => onMove(1)}
131+
/>
132+
<button
133+
type="button"
134+
className="hf-fx-remove px-1 font-mono text-[11px] text-panel-text-4 hover:text-red-400 disabled:opacity-40"
135+
title="Remove"
136+
disabled={disabled}
137+
onClick={onRemove}
138+
>
139+
&times;
140+
</button>
141+
</div>
142+
);
143+
}
144+
145+
/** One effect in the chain: its header controls, and its knobs when open. */
146+
function FxNodeRow({
147+
node,
148+
index,
149+
open,
150+
last,
151+
disabled,
152+
onToggleOpen,
153+
onUpdate,
154+
onMove,
155+
onRemove,
156+
onPreview,
157+
}: FxNodeRowProps) {
158+
const def = getAudioFxDef(node.type);
159+
if (!def) return null;
160+
const bypassed = node.enabled === false;
161+
return (
162+
<div
163+
className={`hf-fx-node rounded-[4px] border border-panel-border-input${bypassed ? " opacity-50" : ""}`}
164+
data-fx-node={node.type}
165+
>
166+
<FxNodeHeader
167+
label={def.label}
168+
open={open}
169+
bypassed={bypassed}
170+
first={index === 0}
171+
last={last}
172+
disabled={disabled}
173+
onToggleOpen={onToggleOpen}
174+
onToggleBypass={() => onUpdate(index, { enabled: bypassed })}
175+
onMove={(delta) => onMove(index, delta)}
176+
onRemove={() => onRemove(index)}
177+
/>
178+
{open ? (
179+
<FxParams
180+
def={def}
181+
params={node.params ?? defaultAudioFxParams(node.type)}
182+
disabled={disabled || bypassed}
183+
onChange={(params: HfAudioFxParamValues) => onPreview(index, params)}
184+
onCommit={(params: HfAudioFxParamValues) => onUpdate(index, { params })}
185+
/>
186+
) : null}
187+
</div>
188+
);
189+
}
190+
37191
export interface FxSectionProps {
38192
chain: HfAudioFxChain;
39193
/** Structural edits and gesture-end writes; this is the one that persists. */
@@ -128,76 +282,21 @@ export function FxSection({
128282
No effects on this track.
129283
</p>
130284
) : (
131-
chain.nodes.map((node, i) => {
132-
const def = getAudioFxDef(node.type);
133-
if (!def) return null;
134-
const bypassed = node.enabled === false;
135-
const open = openNode === i;
136-
return (
137-
<div
138-
key={`${node.type}-${i}`}
139-
className={`hf-fx-node rounded-[4px] border border-panel-border-input${bypassed ? " opacity-50" : ""}`}
140-
data-fx-node={node.type}
141-
>
142-
<div className="hf-fx-node-head flex min-h-7 items-center gap-1 px-1.5">
143-
<button
144-
type="button"
145-
className="hf-fx-node-name flex-1 truncate text-left text-[11px] font-semibold text-panel-text-1 hover:text-panel-text-0"
146-
aria-expanded={open}
147-
onClick={() => setOpenNode(open ? null : i)}
148-
>
149-
{def.label}
150-
</button>
151-
<button
152-
type="button"
153-
className="hf-fx-bypass rounded-[3px] border border-panel-border-input px-1.5 py-0.5 font-mono text-[9px] text-panel-text-4 hover:text-panel-text-0 disabled:opacity-40"
154-
aria-pressed={bypassed}
155-
title={bypassed ? "Enable" : "Bypass"}
156-
disabled={disabled}
157-
onClick={() => updateNode(i, { enabled: bypassed })}
158-
>
159-
{bypassed ? "Off" : "On"}
160-
</button>
161-
<button
162-
type="button"
163-
className="hf-fx-move px-1 font-mono text-[10px] text-panel-text-4 hover:text-panel-text-0 disabled:opacity-25"
164-
title="Move up"
165-
disabled={disabled || i === 0}
166-
onClick={() => moveNode(i, -1)}
167-
>
168-
&uarr;
169-
</button>
170-
<button
171-
type="button"
172-
className="hf-fx-move px-1 font-mono text-[10px] text-panel-text-4 hover:text-panel-text-0 disabled:opacity-25"
173-
title="Move down"
174-
disabled={disabled || i === chain.nodes.length - 1}
175-
onClick={() => moveNode(i, 1)}
176-
>
177-
&darr;
178-
</button>
179-
<button
180-
type="button"
181-
className="hf-fx-remove px-1 font-mono text-[11px] text-panel-text-4 hover:text-red-400 disabled:opacity-40"
182-
title="Remove"
183-
disabled={disabled}
184-
onClick={() => removeNode(i)}
185-
>
186-
&times;
187-
</button>
188-
</div>
189-
{open ? (
190-
<FxParams
191-
def={def}
192-
params={node.params ?? defaultAudioFxParams(node.type)}
193-
disabled={disabled || bypassed}
194-
onChange={(params: HfAudioFxParamValues) => previewNode(i, params)}
195-
onCommit={(params: HfAudioFxParamValues) => updateNode(i, { params })}
196-
/>
197-
) : null}
198-
</div>
199-
);
200-
})
285+
chain.nodes.map((node, i) => (
286+
<FxNodeRow
287+
key={`${node.type}-${i}`}
288+
node={node}
289+
index={i}
290+
open={openNode === i}
291+
last={i === chain.nodes.length - 1}
292+
disabled={disabled}
293+
onToggleOpen={() => setOpenNode(openNode === i ? null : i)}
294+
onUpdate={updateNode}
295+
onMove={moveNode}
296+
onRemove={removeNode}
297+
onPreview={previewNode}
298+
/>
299+
))
201300
)}
202301
</div>
203302

0 commit comments

Comments
 (0)