Skip to content

feat(studio): two faces, and the shared frequency ruler - #3186

Merged
vanceingalls merged 822 commits into
mainfrom
wa-20d2-two-faces-ruler
Aug 13, 2026
Merged

feat(studio): two faces, and the shared frequency ruler#3186
vanceingalls merged 822 commits into
mainfrom
wa-20d2-two-faces-ruler

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Two faces. A module opens on its name, a line saying what it is for, and the single control that carries it; everything else is behind a Details disclosure, which is also where the DSP name lives. Ten of fifteen effects — the other five want a derived knob that has not shipped yet, and they open on all their controls until it does.

Worth naming: peaking's primary is "how much", and that only became coherent in the PR below this one. Two faces is honest for it because the jobs landed first.

The ruler. BANDS has named seven ranges since the copy layer landed and nothing showed them. Log-spaced, because hearing is: rumble is 20–80 Hz, three tenths of one percent of the range linearly and about a fifth of it by ear. A test asserts the bottom segment is over a tenth of the bar, which a linear layout fails.

🤖 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.

Verdict: LGTM_WITH_NITS. Two-faces UX and log-spaced shared frequency ruler both verified against the diff; tests earn their weight; CI failure is the stack-wide pre-existing oxfmt drift, not this PR.

Verified claims (file:line at 72210c5):

  • Two faces — module opens on name + does + one knob; Details behind disclosure. packages/studio/src/components/editor/propertyPanelFxNodeRow.tsx:274-296 (primary-only onlyPrimary, useState(false) for details), :323-329 (.hf-fx-node-does), :330-360 (primary knob + primaryEnds + FxBandRuler), :370-378 (Details disclosure, aria-expanded, DSP label moves here).
  • Five effects with primary: "strength" open on all controls, no disclosure. primaryParamOf returns null when primary isn't a registry param key (:33-40); .hf-fx-node-details button gated on primary (:370); test asserts EFFECT_COPY.compressor?.primary === "strength" and no .hf-fx-node-details (propertyPanelFxSection.test.tsx:399-405).
  • BANDS reused, not duplicated. Ruler imports audioBandAt, BANDS from @hyperframes/core/audio-fx-copy (propertyPanelFxBandRuler.tsx:19); segments map over BANDS (:49); segment widths use log-scale positionOf (:23-27).
  • Ruler bottom segment >10% of bar under log layout. audioFxCopy.ts:294-300 + positionOf gives rumble 20-80Hz ≈ 20% (log10(80/20)/log10(20000/20) = 0.602/3). Linear layout gives 0.3%. Test at propertyPanelFxSection.test.tsx:365-372 asserts >10.

OLD-assumption counterfactuals (new tests):

  • audioBandAt boundary test (audioFxCopy.test.ts:85-91) — fails an OLD hz > band.from && hz <= band.to (closed-upper). Current hz >= from && hz < to passes.
  • clamps past both ends (:93-98) — fails an OLD find-only impl (no clamps); 5Hz and 30000Hz would be nameless.
  • NaN → undefined (:100-102) — fails an OLD impl missing the Number.isFinite guard; Math.log10(NaN) = NaN cascade would yield unhelpful behavior.
  • Rumble >10% (propertyPanelFxSection.test.tsx:370-372) — fails a linear-scale ruler.
  • Reorder Details closes (:298-313) — fails an OLD "expand always" or shared-state disclosure; useState local to FxNodeRow + key={node.id} in parent gives per-effect state.
  • Compressor no-disclosure (:390-405) — fails an OLD "primary is any truthy string" impl that would treat "strength" as a real param key.

Cross-PR coupling:

  • No new HfAudioFxNode field. details is React useState — view-local, not persisted. Parse/serialize allow-list untouched. ✓
  • Ruler consumes #3184's BANDS directly, no duplication. ✓
  • FxBandRuler renders only when copy?.band && typeof params.frequency === "number" (propertyPanelFxNodeRow.tsx:355) — limiter case correctly excluded, test at :381-386.

Nits (non-blocking):

  • Ruler bar has no role="img" or aria-label — a screen-reader user gets data-band="Mud" and per-segment title tooltips (propertyPanelFxBandRuler.tsx:41,55), but no spoken "acting in Mud (200-500 Hz)" summary. Consider aria-label={Acting in ${here.name}, ${here.says}} on the container.
  • FxBandRuler recomputes Math.log10(HIGH) - Math.log10(LOW) per segment via positionOf calls in a .map — cheap but memoizable as a module const alongside LOW/HIGH.

CI: Preflight FAILURE across all three workflows is the recurring 9-file oxfmt --check drift (README.md, plans/*, skills/hyperframes-audio/*) — none of the drifted files are in this PR's diff. Stack-wide pre-existing signal. No THIS-PR real Preflight issue.

— 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): two faces, and the shared frequency ruler — #3186

Verdict: LGTM

Frequency ruler is correct. BANDS covers 20Hz-20kHz in seven contiguous non-overlapping ranges. positionOf() uses Math.log10 spacing — industry standard for audio frequency visualization. 20-200Hz gets one-third of the ruler width (matching the ear's perception), verified by the log-spacing test asserting Rumble's width >10% (would be ~0.3% on a linear ruler).

No CDN fonts. All typography uses existing Tailwind utilities and the project's established font stack. Arbitrary sizes (text-[9px], text-[10px]) match the pattern used throughout the studio panel components.

Two-face design is clean: primary view shows one knob + ends + ruler (the outcome face), Details disclosure reveals all params (the mechanism face). The primaryParamOf fallback for "strength" returns null so profiled effects don't split — forward-compatible for when profiles land as a real registry parameter.

Tests cover: ruler renders with correct band, no ruler for non-spectral effects, strength primary fallback, audioBandAt boundary assignment, edge clamping, NaN handling. The log-spacing assertion is a nice proof that the ruler isn't accidentally linear.

aria-expanded on the Details button, aria-hidden="true" on decorative elements, descriptive title tooltips on ruler segments. Adequate accessibility.


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-20d1-named-jobs to main August 13, 2026 17:07
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 19 findings.

Duplication (18)
Severity Rule Location Description
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:160 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:204 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:368 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:394 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:547 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:587 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:638 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:708 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:825 Code clone group 5 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:838 Code clone group 6 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:855 Code clone group 5 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:868 Code clone group 6 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1036 Code clone group 7 (7 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1044 Code clone group 7 (7 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1217 Code clone group 8 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1375 Code clone group 8 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1519 Code clone group 9 (11 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelAudioFxGroup.test.tsx:1574 Code clone group 9 (11 lines, 2 instances)
Health (1)
Severity Rule Location Description
major fallow/high-crap-score packages/studio/src/components/editor/propertyPanelFxNodeRow.tsx:259 'FxNodeRow' has CRAP score 30.6 (threshold: 30.0, cyclomatic 28)

Generated by fallow.

@vanceingalls
vanceingalls merged commit 96fd4d0 into main Aug 13, 2026
57 of 58 checks passed
@vanceingalls
vanceingalls deleted the wa-20d2-two-faces-ruler branch August 13, 2026 17:35
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