Skip to content

Commit c1acbad

Browse files
adewaleclaude
andcommitted
feat: Add advancedStepInput feature flag for multi-select and drag-to-paint
Adds a combined feature flag to disable both multi-select (Ctrl+Click, Shift+Click to extend) and drag-to-paint under a single toggle. When disabled (VITE_FEATURE_ADVANCED_STEP_INPUT=false), the sequencer reverts to classic TR-808 style interaction: - Single click still toggles steps - Drag only affects first clicked step (no paint mode) - Ctrl+Click does nothing - Shift+Click always opens p-lock menu - Selection badge never appears Default: enabled (preserves current behavior) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b95a579 commit c1acbad

File tree

3 files changed

+27
-7
lines changed

3 files changed

+27
-7
lines changed

app/src/components/StepSequencer.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,8 @@ export function StepSequencer() {
578578
/>
579579
</div>
580580

581-
{/* Phase 31F: Selection indicator badge */}
582-
{selectionCount > 0 && (
581+
{/* Phase 31F: Selection indicator badge (only when advanced input enabled) */}
582+
{features.advancedStepInput && selectionCount > 0 && (
583583
<div className="selection-badge" title={`${selectionCount} step${selectionCount > 1 ? 's' : ''} selected • ESC to clear • Delete to remove`}>
584584
<span className="selection-count">{selectionCount}</span>
585585
<span className="selection-label">selected</span>
@@ -642,10 +642,10 @@ export function StepSequencer() {
642642
onEuclideanFill={(hits) => handleEuclideanFill(track.id, hits)}
643643
onSetName={(name) => handleSetName(track.id, name)}
644644
onSetTrackSwing={(swing) => handleSetTrackSwing(track.id, swing)}
645-
selectedSteps={selectedSteps}
646-
selectionAnchor={selectionAnchor}
647-
hasSelection={selectionCount > 0}
648-
onSelectStep={(step, mode) => handleSelectStep(track.id, step, mode)}
645+
selectedSteps={features.advancedStepInput ? selectedSteps : undefined}
646+
selectionAnchor={features.advancedStepInput ? selectionAnchor : undefined}
647+
hasSelection={features.advancedStepInput && selectionCount > 0}
648+
onSelectStep={features.advancedStepInput ? (step, mode) => handleSelectStep(track.id, step, mode) : undefined}
649649
loopRegion={state.loopRegion}
650650
isDragTarget={isDragTarget}
651651
isDragging={isDragging}

app/src/components/TrackRow.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useRemoteChanges } from '../context/RemoteChangeContext';
1313
import { getInstrumentCategory, getInstrumentName, TONE_SYNTH_CATEGORIES, SAMPLED_CATEGORIES } from './sample-constants';
1414
import { getTransposedRoot, type NoteName } from '../music/music-theory';
1515
import { isInRange, isInOptimalRange } from '../audio/instrument-ranges';
16+
import { features } from '../config/features';
1617
import './TrackRow.css';
1718
import './ChromaticGrid.css';
1819
import './PianoRoll.css';
@@ -342,13 +343,22 @@ export const TrackRow = React.memo(function TrackRow({
342343
const handlePaintStart = useCallback((stepIndex: number) => {
343344
const wasActive = track.steps[stepIndex];
344345
const newState = !wasActive;
345-
setPaintMode(newState ? 'on' : 'off');
346+
347+
// Always toggle the clicked step
346348
onToggleStep(stepIndex);
349+
350+
// Only enable drag continuation if advanced input is on
351+
if (features.advancedStepInput) {
352+
setPaintMode(newState ? 'on' : 'off');
353+
}
347354
}, [track.steps, onToggleStep]);
348355

349356
// Continue painting: apply paint mode to entered step
350357
// Uses paintModeRef to avoid stale closure issues with paint mode
351358
const handlePaintEnter = useCallback((stepIndex: number) => {
359+
// No drag painting when advanced input is disabled
360+
if (!features.advancedStepInput) return;
361+
352362
const currentPaintMode = paintModeRef.current;
353363
if (currentPaintMode === null) return;
354364
const isActive = track.steps[stepIndex];

app/src/config/features.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ export const features = {
6666
* Default: true (stable feature)
6767
*/
6868
multiplayer: parseEnvBool(import.meta.env.VITE_FEATURE_MULTIPLAYER, true),
69+
70+
/**
71+
* Advanced Step Input - Multi-select and drag-to-paint
72+
* Default: true (stable feature)
73+
* When disabled, reverts to classic TR-808 style: click-to-toggle only
74+
* - Ctrl+Click: Does nothing (instead of toggle selection)
75+
* - Shift+Click: Always opens p-lock menu (instead of extend selection)
76+
* - Drag: Only toggles first step (instead of paint mode)
77+
*/
78+
advancedStepInput: parseEnvBool(import.meta.env.VITE_FEATURE_ADVANCED_STEP_INPUT, true),
6979
} as const;
7080

7181
/**

0 commit comments

Comments
 (0)