Skip to content

feat: knob magnetic snap near default + reset brightness pulse + detent marker#1322

Closed
ChuxiJ wants to merge 1 commit intomainfrom
feat/issue-1320-knob-reset-snap
Closed

feat: knob magnetic snap near default + reset brightness pulse + detent marker#1322
ChuxiJ wants to merge 1 commit intomainfrom
feat/issue-1320-knob-reset-snap

Conversation

@ChuxiJ
Copy link
Copy Markdown

@ChuxiJ ChuxiJ commented Apr 1, 2026

Summary

  • Magnetic snap: Drag sensitivity halves within 3% of default value, snaps to exact default within 1%
  • Default detent marker: Tiny dot on arc track at default position
  • Reset brightness pulse: Colored ring fades out on double-click reset
  • No audio parameter lag — only visual smoothing

Closes #1320

Test plan

  • npx tsc --noEmit — 0 errors
  • npm test — 3744 passed
  • Visual: compressor knobs show detent markers in dev server

🤖 Generated with Claude Code

…nt marker

Three micro-interactions for the Knob component:

1. **Magnetic snap near default**: When dragging within 3% of default value,
   sensitivity reduces by 50%. Within 1% it snaps to exact default. Makes
   it easy to return to factory setting without precision input.

2. **Default detent marker**: Tiny dot on the arc track at the default
   value position. Provides a visual target for where "neutral" is.

3. **Reset brightness pulse**: On double-click reset, a colored ring
   fades out over 200ms around the knob, confirming the action.

Closes #1320

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 1, 2026 10:45
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the Knob UI micro-interactions by adding a magnetic snap region around the default value, rendering a default-position detent marker, and showing a brief brightness pulse on double-click reset.

Changes:

  • Add magnetic snap behavior near defaultValue (reduced drag sensitivity + snap-to-default threshold).
  • Render a default detent marker on the knob track and a reset pulse ring during reset.
  • Add @keyframes knob-pulse to support the reset pulse animation.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/index.css Adds knob-pulse keyframes used by the reset pulse ring animation.
src/components/ui/Knob.tsx Implements magnetic snap logic, detent marker rendering, and reset pulse visuals/transitioning.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +83 to +89
let sensitivity = fine ? range / 2000 : range / 200;

// Magnetic snap: reduce sensitivity near default value
const snapZone = range * 0.03; // 3% of range
if (Math.abs(dragStart.current.value - defaultValue) < snapZone) {
sensitivity *= 0.5;
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

defaultValue is now referenced inside the onMouseDown callback (via the onMove handler) but it is not included in the useCallback dependency list. If defaultValue ever changes between renders, the magnetic snap logic can use a stale value. Add defaultValue to the dependency array (or refactor so the callback doesn’t capture it).

Copilot uses AI. Check for mistakes.

// Snap to exact default if within snap zone
if (Math.abs(newVal - defaultValue) < snapZone * 0.3) {
newVal = defaultValue;
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The snap-to-default branch assigns newVal = defaultValue without applying roundToStep/clamping. This can emit a value outside [min,max] or off-step if defaultValue isn’t perfectly aligned. Consider using applyStep(defaultValue) (or otherwise ensuring defaultValue is validated) before calling onChange.

Suggested change
newVal = defaultValue;
newVal = applyStep(defaultValue);

Copilot uses AI. Check for mistakes.
@@ -251,6 +264,36 @@ export function Knob({
style={isResetting ? { transition: 'd 200ms ease-out' } : undefined}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

transition: 'd 200ms ease-out' relies on animating the SVG path d attribute, which is not consistently supported across browsers via CSS transitions. If this is meant to guarantee the 200ms reset animation, consider animating a reliably animatable property (e.g., stroke-dashoffset/length) or driving a separate visual-only animated value over 200ms while keeping onChange(defaultValue) immediate.

Suggested change
style={isResetting ? { transition: 'd 200ms ease-out' } : undefined}

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +97
// Magnetic snap: reduce sensitivity near default value
const snapZone = range * 0.03; // 3% of range
if (Math.abs(dragStart.current.value - defaultValue) < snapZone) {
sensitivity *= 0.5;
}

const delta = mv.movementY * sensitivity;
const newVal = applyStep(dragStart.current.value + delta);
let newVal = applyStep(dragStart.current.value + delta);

// Snap to exact default if within snap zone
if (Math.abs(newVal - defaultValue) < snapZone * 0.3) {
newVal = defaultValue;
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

New behaviors (magnetic snap thresholding and snapping-to-default) aren’t covered by unit tests. Since this component already has interaction tests (e.g., src/components/ui/__tests__/Knob.test.tsx), add assertions for: reduced sensitivity within ±3% of default, snapping to exact default within ~1%, and that snapping respects step/clamping.

Copilot uses AI. Check for mistakes.
Comment on lines +267 to +295
{/* Default value detent marker — tiny tick on the track */}
{(() => {
const defAngle = valueToAngle(defaultValue, min, max, arc);
const defPos = polarToXY(defAngle - 90, trackR);
return (
<circle
cx={defPos.x}
cy={defPos.y}
r={1}
fill="rgba(255,255,255,0.15)"
/>
);
})()}

{/* Reset brightness pulse */}
{isResetting && (
<circle
cx={radius}
cy={radius}
r={trackR + strokeWidth}
fill="none"
stroke={color}
strokeWidth={1}
opacity={0.6}
style={{
animation: 'knob-pulse 200ms ease-out forwards',
}}
/>
)}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The new detent marker and reset pulse rendering isn’t currently exercised by tests. Consider adding a small DOM-level assertion in Knob.test.tsx that the detent marker circle is present (and that the pulse circle appears only while data-resetting is true) to prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +268 to +276
{(() => {
const defAngle = valueToAngle(defaultValue, min, max, arc);
const defPos = polarToXY(defAngle - 90, trackR);
return (
<circle
cx={defPos.x}
cy={defPos.y}
r={1}
fill="rgba(255,255,255,0.15)"
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

valueToAngle(defaultValue, min, max, arc) is used directly for the detent marker position. If defaultValue is ever out of range, the marker will be rendered off-arc. Consider clamping defaultValue to [min,max] for marker geometry (and ideally for the snap target too) to keep visuals consistent.

Copilot uses AI. Check for mistakes.
@ChuxiJ
Copy link
Copy Markdown
Author

ChuxiJ commented Apr 1, 2026

Closing — the same features (magnetic snap, detent marker, reset pulse) were already merged to main via another parallel worktree. No action needed.

@ChuxiJ ChuxiJ closed this Apr 1, 2026
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.

feat: knob reset animation + magnetic snap near default (#1242)

2 participants