/** * An effect row's open face: the derived or primary one-knob control, and * Details underneath it. * * Split out of `propertyPanelFxNodeRow.tsx`, which owned all of this before * the file grew past a size where the header and the open face were still one * thing to read. */ import { applyAudioFxProfile, audioFxProfileStrength, getAudioFxProfile, } from "@hyperframes/core/audio-fx-profiles"; import type { HfAudioFxDef, HfAudioFxNode, HfAudioFxParam, HfAudioFxParamValues, } from "@hyperframes/core/audio-fx"; import type { EFFECT_COPY } from "@hyperframes/core/audio-fx-copy"; import { trackProfileCommitted } from "./audioFxTelemetry.js"; import { FxParamRow } from "./propertyPanelFxControls.js"; import { FxBandRuler } from "./propertyPanelFxBandRuler.js"; import { FxNodeParams, type FxNodeControlHandlers } from "./propertyPanelFxNodeParams.js"; /** * The derived one-knob face, for a module with no real parameter that can be * its own. Not routed through `FxNodeParams`: this knob is not in the * registry, so it has no AudioParam behind it and nothing to automate. What * automation there is belongs to the parameters it sets, under Details, where * they can be aimed at individually. */ function FxNodeDerivedKnob({ node, derived, profile, disabled, bypassed, params, index, onPreview, onUpdate, trackKind, }: { node: HfAudioFxNode; derived: HfAudioFxParam | null; profile: ReturnType; disabled?: boolean; bypassed: boolean; params: HfAudioFxParamValues; index: number; onPreview(index: number, params: HfAudioFxParamValues): void; onUpdate(index: number, patch: Partial): void; trackKind?: string; }) { if (!derived) return null; return ( <>
onPreview(index, applyAudioFxProfile(node.type, Number(v), params))} onCommit={(_k, v) => { trackProfileCommitted(node.type, Number(v), { trackKind }); onUpdate(index, { params: applyAudioFxProfile(node.type, Number(v), params) }); }} />
{profile ? (

{profile.ends.low} {profile.ends.high}

) : null} ); } /** * The primary knob's face: the one control, what its two ends sound like, and * — for a spectral module — the ruler that teaches where it is working. */ function FxNodePrimaryKnob({ node, onlyPrimary, primary, copy, params, index, disabled, bypassed, automatedTargets, liveAutomationValues, onUpdate, onPreview, onAutomateParam, onRemoveParamAutomation, trackKind, }: FxNodeControlHandlers & { node: HfAudioFxNode; onlyPrimary: HfAudioFxDef; primary: string | null; copy: (typeof EFFECT_COPY)[string] | undefined; params: HfAudioFxParamValues; index: number; disabled?: boolean; bypassed: boolean; }) { if (!primary) return null; return ( <> {/* What the two ends of that knob sound like. A number tells an author where the control is; this tells them which way to move it, which is the question they actually have. */} {copy?.primaryEnds ? (

{copy.primaryEnds.low} {copy.primaryEnds.high}

) : null} {/* Where it is working, in the words the rack shares. Only for a module that acts on a range at all — there is nothing spectral about a limiter, and a ruler under one would be noise. */} {copy?.band && typeof params.frequency === "number" ? ( ) : null} ); } /** * Everything below the header: does-copy, the one-knob face, and Details. The * derived and primary knobs are already their own components — what is left * is five independent conditionals deciding which pieces of the open face to * show, which is the section's actual job rather than an avoidable branch. */ // fallow-ignore-next-line complexity export function FxNodeOpenBody({ node, registryDef, def, onlyPrimary, primary, derived, profile, oneKnob, details, onToggleDetails, copy, params, index, disabled, bypassed, automatedTargets, liveAutomationValues, onUpdate, onPreview, onAutomateParam, onRemoveParamAutomation, trackKind, }: FxNodeControlHandlers & { node: HfAudioFxNode; registryDef: HfAudioFxDef; def: HfAudioFxDef; onlyPrimary: HfAudioFxDef; primary: string | null; derived: HfAudioFxParam | null; profile: ReturnType; oneKnob: boolean; details: boolean; onToggleDetails(): void; copy: (typeof EFFECT_COPY)[string] | undefined; params: HfAudioFxParamValues; index: number; disabled?: boolean; bypassed: boolean; }) { return ( <> {/* What it is for, before what it is made of. */} {copy?.does ? (

{copy.does}

) : null} {!details ? ( ) : null} {!details ? ( ) : null} {/* The DSP name lives on the disclosure, so it is read at the moment the author asks what this really is — and never before. */} {oneKnob ? ( ) : (

Details — {registryDef.label}

)} {details || !oneKnob ? ( ) : null} ); }