Skip to content

feat(studio): make the FX rack speak the author's language - #3192

Merged
vanceingalls merged 772 commits into
mainfrom
wa-20c-rack-copy
Aug 13, 2026
Merged

feat(studio): make the FX rack speak the author's language#3192
vanceingalls merged 772 commits into
mainfrom
wa-20c-rack-copy

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Wires the plain-language layer into the rack, to three UX decisions in plans/audio-fx-ux/README.md §Decided.

The plain name replaces the DSP name, which moves inside as Details — High-pass. Every knob is renamed too — plainDef writes the copy over the registry's labels while leaving range, step, unit and automatability alone, because those are facts and only the words were ever the problem.

Presets read as the complaint they answer — "My voice sounds boomy", with the name underneath, which is also how the name gets learned.

Hover-audition. Four things have to hold, each with a test that fails without it: leaving reverts, applying does not revert (the audition WAS the preset), the panel unmounting reverts, and moving between two entries in a shelf is not leaving it.

Two of those four were bugs caught by review rather than tests — a static mount never re-renders and never moves the pointer.
🤖 Generated with Claude Code

@terencecho terencecho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits. Coupling to #3184 is dictionary-shape via bracket lookup — mediated by the typed exported interface (EffectCopy / ParamCopy / Ends), so refactors that renamed shape fields would surface as TypeScript breaks, not silent runtime bugs.

Consumption sites (all import from @hyperframes/core/audio-fx-copy):

  • propertyPanelFxNodeRow.tsx:16, 18EFFECT_COPY, SUMMARY; reads EFFECT_COPY[def.id], copy.params[param.key].label|.hint (in plainDef at ~L420-431), EFFECT_COPY[node.type]?.title (header at ~L465), SUMMARY[node.type]?.(params) (at ~L451).
  • propertyPanelFxPresetMenu.tsx:15PRESET_PROBLEM; PRESET_PROBLEM[preset.id] ?? preset.description at ~L589.
  • Test files — propertyPanelAudioFxGroup.test.tsx:7 and propertyPanelFxSection.test.tsx:11-12 use plainLabel(effectId, key) helper that throws on missing copy — enforces completeness in tests while prod degrades gracefully.

Coupling verdict. 4/4 sites use EFFECT_COPY[...].params[key].label-style shape. No getCopy() function exists — none is called. However: the shape is exported as a typed interface (EffectCopy, ParamCopy, Ends), so a #3184-side refactor renaming titlename would break at the TypeScript layer, not silently at runtime.

Pair-mergeability. #3192's base ref IS #3184's head branch — graphite naturally sequences #3184 first. Not a "must merge atomic" case: #3184 can ship alone (rack falls back to registry names via the 3-tier fallback chain); #3192 cannot ship without #3184 (import fails). This is a softer coupling than wa-18's #3212+#3213 or wa-19's #3177+#3178 — the graphite sequencing carries the load.

UI changes (all propertyPanelFxNodeRow.tsx unless noted):

  • Rack row header: label={node.label ?? EFFECT_COPY[node.type]?.title ?? registryDef.label} (~L465) — 3-tier fallback.
  • New summary line under header: <p className="hf-fx-node-summary"> (~L474-477) from SUMMARY[node.type]?.(params).
  • New "Details — {registryDef.label}" strip inside open module (~L495-497) — parks the DSP name where the mechanism lives.
  • plainDef() (~L419-432) overlays plain names/hints onto registry def.params, preserving range/step/unit/automatability.
  • propertyPanelFxPresetMenu.tsx:588-593 — preset button now stacks .hf-fx-preset-problem sentence over the preset's own name.
  • propertyPanelFxSection.tsx — big new hover-audition apparatus (auditionBase ref, audition(), previewRef indirection, unmount teardown, per-shelf leave handlers, levelling audition wiring).
  • propertyPanelAudioFxGroup.tsx — new decoded ref caches decoded audio per src for the leveller (drops stale decodes on hover-away).

Accessibility. Header text reads "Remove Rumble" (plain) instead of "highpass" (DSP) — better for screen readers. No aria-label / aria-pressed changed.

Fallback chain (no undefined leaks to DOM):

  • Missing EFFECT_COPY[node.type]?.titleregistryDef.label renders (DSP name).
  • Missing param copy → plainDef returns registry param unchanged.
  • Missing PRESET_PROBLEM[preset.id]?? preset.description.
  • Missing SUMMARY[node.type]{summary ? <p>… : null} renders nothing.

OLD-assumption counterfactual: the OLD tests spelled out rowFor(host, "Cutoff"). With copy-driven code, "Cutoff" no longer appears (plain label for lowpass.frequency is "Cut above"), so those pins would fail. Positive pins at propertyPanelFxSection.test.tsx:695-696: expect(name).toBe(EFFECT_COPY.highpass?.title) AND expect(name).not.toBe(getAudioFxDef("highpass")?.label) — explicitly forbid a regression to hardcoded/registry labels.

CI: Preflight red on the same plans/audio-fx-ux/README.md as #3184. Needs bun run format.

Nits:

  • SUMMARY[node.type]?.(params) invokes a function-valued record entry; if #3184 ever changes the P shape (Record<string, unknown>) to a stronger Partial<HfAudioFxParamValues> type, that call signature is silent-breaking at runtime, not a TS break. Consider a summaryFor(type, params) wrapper in #3184 to anchor the call signature.
  • plainLabel in tests throws on miss while production silently falls back to registry — the asymmetry is fine (tests enforce completeness, prod degrades gracefully); worth a comment.

— Review by tai (pr-review)

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat(studio): make the FX rack speak the author's language — #3192

Verdict: LGTM

Coupling with #3184 is export-surface only. Imports exactly EFFECT_COPY, SUMMARY, and PRESET_PROBLEM — no reach into internals, no structural assumptions. Via's concern is unfounded here; this is the exact kind of import-surface dependency that package boundaries are for. The completeness test in #3184 guarantees every registry effect has copy, so these lookups are safe.

Fallback behavior is thorough. Every lookup has a graceful chain:

  • plainDef(): no copy → returns registry def unchanged. No copy for a param → keeps original label. Never crashes, never blanks.
  • Header label: node.label ?? EFFECT_COPY[type]?.title ?? registryDef.label — three tiers.
  • Summary: SUMMARY[type]?.(params) with a null guard — missing summary renders nothing.
  • Preset problem: PRESET_PROBLEM[id] ?? preset.description — falls back gracefully.

Intentional naming asymmetry is well-justified. The rack and preset menu speak plain language. The "Add effect" menu keeps DSP registry names. This is documented in the plan's "Decided" section — the expert's tool stays in signal-processing vocabulary. The test "names an effect for the job it does" confirms the intent.

Hover-audition machinery is well-tested. Tests cover: preview without commit, revert on leave, no revert after apply, revert on unmount, survival through re-render (the playhead-tick stability bug via previewRef indirection), stale levelling measurement dropped, cross-neighbour cancel. The auditionRun counter pattern for stale results is correctly designed — increment on every gesture, check after every async boundary.

auditionBase ref + cleanup interaction is the trickiest part: the empty-deps cleanup captures previewRef.current to avoid re-render teardown, while auditionBase.current holds the pre-audition chain. The applyPreset/addEffect paths null out auditionBase.current to prevent the revert from undoing the write. Correct.

No blocking issues.


Review by Miga

🤖 Generated with Claude Code

# Conflicts:
#	packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx
#	packages/studio/src/components/editor/propertyPanelAudioFxGroup.tsx
#	packages/studio/src/components/editor/propertyPanelFxSection.tsx
#	skills-manifest.json
#	skills/hyperframes-audio/SKILL.md
#	skills/hyperframes-audio/references/attributes.md
#	skills/hyperframes-audio/scripts/carve.mjs
Base automatically changed from wa-20b3-copy-layer to main August 13, 2026 16:13
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 23 findings.

Duplication (18)
Severity Rule Location Description
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:144 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:187 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:351 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:377 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:528 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:568 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:619 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:689 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:806 Code clone group 5 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:819 Code clone group 6 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:836 Code clone group 5 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:849 Code clone group 6 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1017 Code clone group 7 (7 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1025 Code clone group 7 (7 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1198 Code clone group 8 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1356 Code clone group 8 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1500 Code clone group 9 (11 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1555 Code clone group 9 (11 lines, 2 instances)
Health (5)
Severity Rule Location Description
major fallow/high-crap-score packages/studio/src/components/editor/propertyPanelAudioFxGroup.tsx:209 'setCarve' has CRAP score 97.0 (threshold: 30.0, cyclomatic 19)
minor fallow/high-crap-score packages/studio/src/components/editor/propertyPanelAudioFxGroup.tsx:282 'carvedAgainstBy' has CRAP score 37.1 (threshold: 30.0, cyclomatic 11)
minor fallow/high-crap-score packages/studio/src/components/editor/propertyPanelAudioFxGroup.tsx:576 'auditionLevel' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)
critical fallow/high-crap-score packages/studio/src/components/editor/propertyPanelAudioFxGroup.tsx:623 'analyse' has CRAP score 148.4 (threshold: 30.0, cyclomatic 24)
major fallow/high-cognitive-complexity packages/studio/src/components/editor/propertyPanelFxSection.tsx:99 'FxSection' has cognitive complexity 30 (threshold: 15)

Generated by fallow.

@vanceingalls
vanceingalls merged commit 0e4da52 into main Aug 13, 2026
44 of 46 checks passed
@vanceingalls
vanceingalls deleted the wa-20c-rack-copy branch August 13, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants