fix: dynamic sizing for sequencer and piano roll modules
Module width now adapts to step/bar count so extra steps are never hidden. Sequencer width scales with numSteps, piano roll width scales with bar count using a fixed BEAT_PX density. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,20 @@ import KeyboardWidget from './KeyboardWidget.jsx';
|
||||
import SequencerWidget from './SequencerWidget.jsx';
|
||||
import PianoRollWidget from './PianoRollWidget.jsx';
|
||||
|
||||
// Dynamic module widths for sequencer/pianoroll based on step/bar count
|
||||
function getModuleWidth(mod, type) {
|
||||
if (type === 'sequencer') {
|
||||
const numSteps = parseInt(mod?.params?.steps || '16');
|
||||
return Math.max(200, numSteps * 18 + 20); // CELL_W=18 + padding
|
||||
}
|
||||
if (type === 'pianoroll') {
|
||||
const bars = parseInt(mod?.params?.bars || '4');
|
||||
const totalBeats = bars * 4;
|
||||
return 24 + totalBeats * 30 + 20; // KEY_W + beats*BEAT_PX + padding
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Map input port names → the param name they modulate (for visual feedback)
|
||||
const PORT_TO_PARAM = {
|
||||
filter: { cutoff: 'frequency' },
|
||||
@@ -150,7 +164,7 @@ export default function ModuleNode({ mod, zoom, onStartConnect, onPortPosition }
|
||||
style={{
|
||||
left: mod.x * zoom, top: mod.y * zoom,
|
||||
transform: `scale(${zoom})`, transformOrigin: 'top left',
|
||||
...(mod.type === 'pianoroll' ? { width: 520 } : mod.type === 'sequencer' ? { width: 310 } : {}),
|
||||
...(mod.type === 'pianoroll' ? { width: getModuleWidth(mod, 'pianoroll') } : mod.type === 'sequencer' ? { width: getModuleWidth(mod, 'sequencer') } : {}),
|
||||
}}
|
||||
data-module-id={mod.id}
|
||||
onPointerDown={(e) => {
|
||||
|
||||
@@ -79,7 +79,7 @@ const MARIO_MELODY = [
|
||||
{ note: 71, start: 70*s, duration: 2*s }, // B4
|
||||
];
|
||||
|
||||
const ROLL_W = 500;
|
||||
const BEAT_PX = 30; // pixels per beat — constant density regardless of bar count
|
||||
const ROLL_H = 200;
|
||||
const KEY_W = 24;
|
||||
const MIN_NOTE = 48; // C3
|
||||
@@ -110,7 +110,8 @@ export default function PianoRollWidget({ moduleId }) {
|
||||
const notesRef = useRef(notes);
|
||||
notesRef.current = notes;
|
||||
|
||||
const beatW = (ROLL_W - KEY_W) / totalBeats;
|
||||
const rollW = KEY_W + totalBeats * BEAT_PX;
|
||||
const beatW = BEAT_PX;
|
||||
|
||||
// Draw the piano roll
|
||||
const draw = useCallback(() => {
|
||||
@@ -220,7 +221,7 @@ export default function PianoRollWidget({ moduleId }) {
|
||||
ctx.fillStyle = 'rgba(0,229,255,0.3)';
|
||||
ctx.fillRect(x, row * ROW_H, Math.max(nw, 2), ROW_H);
|
||||
}
|
||||
}, [totalBeats, beatW, playPos]);
|
||||
}, [totalBeats, beatW, playPos, rollW]);
|
||||
|
||||
// Animation loop
|
||||
useEffect(() => {
|
||||
@@ -415,7 +416,7 @@ export default function PianoRollWidget({ moduleId }) {
|
||||
}, [mod]);
|
||||
|
||||
return (
|
||||
<div style={{ width: ROLL_W }}>
|
||||
<div style={{ width: rollW }}>
|
||||
{/* Mini toolbar */}
|
||||
<div style={{ display: 'flex', gap: 4, marginBottom: 3 }}>
|
||||
<button
|
||||
@@ -453,9 +454,9 @@ export default function PianoRollWidget({ moduleId }) {
|
||||
</div>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
width={ROLL_W}
|
||||
width={rollW}
|
||||
height={ROLL_H}
|
||||
style={{ width: ROLL_W, height: ROLL_H, borderRadius: 4, cursor: tool === 'draw' ? 'crosshair' : 'pointer' }}
|
||||
style={{ width: rollW, height: ROLL_H, borderRadius: 4, cursor: tool === 'draw' ? 'crosshair' : 'pointer' }}
|
||||
onPointerDown={handleMouseDown}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user