From 98ed8a60ceeab1e270e1802819716e96624b6d5a Mon Sep 17 00:00:00 2001 From: Xiaohan-Tian <157918347+Xiaohan-Tian@users.noreply.github.com> Date: Mon, 8 Dec 2025 22:59:09 -0800 Subject: [PATCH] feat: added mode list dropdown --- public/resources/modes/mode_list.json | 59 +++++++++++++++++++ src/App.css | 10 ++++ src/App.tsx | 20 ++++++- src/components/piano-roll/PianoRoll.tsx | 16 ++++- .../piano-roll/PianoRollToolbar.tsx | 17 +++++- src/core/state/KGPianoRollState.ts | 10 ++++ 6 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 public/resources/modes/mode_list.json diff --git a/public/resources/modes/mode_list.json b/public/resources/modes/mode_list.json new file mode 100644 index 0000000..e255879 --- /dev/null +++ b/public/resources/modes/mode_list.json @@ -0,0 +1,59 @@ +{ + "modes": [ + { + "id": "ionian", + "name": "Ionian", + "steps": [2, 2, 1, 2, 2, 2, 1] + }, + { + "id": "aeolian", + "name": "Aeolian", + "steps": [2, 1, 2, 2, 1, 2, 2] + }, + { + "id": "harmonic_minor", + "name": "Harmonic Minor", + "steps": [2, 1, 2, 2, 1, 3, 1] + }, + { + "id": "melodic_minor", + "name": "Melodic Minor", + "steps": [2, 1, 2, 2, 2, 2, 1] + }, + { + "id": "dorian", + "name": "Dorian", + "steps": [2, 1, 2, 2, 2, 1, 2] + }, + { + "id": "phrygian", + "name": "Phrygian", + "steps": [1, 2, 2, 2, 1, 2, 2] + }, + { + "id": "lydian", + "name": "Lydian", + "steps": [2, 2, 2, 1, 2, 2, 1] + }, + { + "id": "mixolydian", + "name": "Mixolydian", + "steps": [2, 2, 1, 2, 2, 1, 2] + }, + { + "id": "locrian", + "name": "Locrian", + "steps": [1, 2, 2, 1, 2, 2, 2] + }, + { + "id": "phrygian_dominant", + "name": "Phrygian Dominant", + "steps": [1, 3, 1, 2, 1, 2, 2] + }, + { + "id": "harmonic_major", + "name": "Harmonic Major", + "steps": [2, 2, 1, 2, 1, 3, 1] + } + ] +} diff --git a/src/App.css b/src/App.css index 6249762..e26760b 100644 --- a/src/App.css +++ b/src/App.css @@ -597,6 +597,7 @@ body { padding: 0 10px; user-select: none; position: relative; + z-index: 100; /* Ensure toolbar and its dropdowns appear above piano roll content */ } /* Override pointer-events for piano roll toolbar sections */ @@ -609,6 +610,15 @@ body { font-size: 10px; } +.piano-roll-toolbar .toolbar-left .quant-button { + margin-left: 0px; + margin-right: 5px; +} + +.piano-roll-toolbar .toolbar-left .quant-dropdown { + left: 0; +} + /* Generic button blink effect */ .quant-button.button-blink { animation: buttonBlink 0.2s ease-in-out; diff --git a/src/App.tsx b/src/App.tsx index 3dfe826..bb49492 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import { SettingsPanel } from './components/settings'; import LoadingOverlay from './components/common/LoadingOverlay'; import { useEffect as useEffectReact, useState, useRef } from 'react'; import { KGToneBuffersPool } from './core/audio-interface/KGToneBuffersPool'; +import { KGPianoRollState } from './core/state/KGPianoRollState'; function App() { // Enable global keyboard handler for copy/paste and undo/redo @@ -29,14 +30,27 @@ function App() { const initializeApp = async () => { // Load the current project from KGCore loadProject(null); - + // Initialize store from config after ConfigManager is ready await initializeFromConfig(); - + + // Load mode list from JSON + try { + const response = await fetch(`${import.meta.env.BASE_URL}resources/modes/mode_list.json`); + const data = await response.json(); + const modeNames = data.modes.map((mode: { name: string; steps: number[] }) => mode.name); + KGPianoRollState.MODE_OPTIONS = modeNames; + console.log(`Loaded ${modeNames.length} modes:`, modeNames); + } catch (error) { + console.error('Failed to load mode list:', error); + // Fallback to default mode + KGPianoRollState.MODE_OPTIONS = ['ionian']; + } + // Log maxBars to console console.log(`Project max bars: ${maxBars}`); }; - + initializeApp(); }, [loadProject, maxBars, initializeFromConfig]); diff --git a/src/components/piano-roll/PianoRoll.tsx b/src/components/piano-roll/PianoRoll.tsx index 1602d26..c683aa8 100644 --- a/src/components/piano-roll/PianoRoll.tsx +++ b/src/components/piano-roll/PianoRoll.tsx @@ -35,9 +35,12 @@ const PianoRoll: React.FC = ({ // Quantization state const [quantPosition, setQuantPosition] = useState('1/8'); const [quantLength, setQuantLength] = useState('1/8'); - + // Snapping state const [snapping, setSnapping] = useState('NO SNAP'); + + // Mode state + const [selectedMode, setSelectedMode] = useState('ionian'); // Piano roll state with temporary initial values const [position, setPosition] = useState(initialPosition || { x: 0, y: 0 }); @@ -295,6 +298,15 @@ const PianoRoll: React.FC = ({ console.log(`Selected snapping: ${value}`); } }, []); + + // Handle mode selection + const handleModeSelect = useCallback((value: string) => { + setSelectedMode(value); + KGPianoRollState.instance().setCurrentMode(value); + if (DEBUG_MODE.PIANO_ROLL) { + console.log(`Selected mode: ${value}`); + } + }, []); // Handler for receiving the setNoteUpdateCounter function from PianoRollContent const handleSetNoteUpdateTrigger = (setNoteFn: React.Dispatch>) => { @@ -765,6 +777,8 @@ const PianoRoll: React.FC = ({ onQuantSelect={handleQuantSelect} snapping={snapping} onSnappingSelect={handleSnappingSelect} + selectedMode={selectedMode} + onModeChange={handleModeSelect} blinkButton={blinkButton} /> diff --git a/src/components/piano-roll/PianoRollToolbar.tsx b/src/components/piano-roll/PianoRollToolbar.tsx index 07232a7..3294ae4 100644 --- a/src/components/piano-roll/PianoRollToolbar.tsx +++ b/src/components/piano-roll/PianoRollToolbar.tsx @@ -11,6 +11,8 @@ interface PianoRollToolbarProps { onQuantSelect: (type: 'position' | 'length', value: string) => void; snapping: string; onSnappingSelect: (value: string) => void; + selectedMode: string; + onModeChange: (value: string) => void; blinkButton?: string | null; } @@ -22,12 +24,21 @@ const PianoRollToolbar: React.FC = ({ onQuantSelect, snapping, onSnappingSelect, + selectedMode, + onModeChange, blinkButton = null }) => { return (
- {/* Left section - can add more tools later */} + {/* Left section with mode dropdown */} + onModeChange(value)} + label="Mode" + buttonClassName="mode-dropdown" + />
@@ -58,7 +69,7 @@ const PianoRollToolbar: React.FC = ({ buttonClassName="snapping" showValueAsLabel={true} /> - + = ({ label="Qua. Pos." buttonClassName={`quant-position ${blinkButton === 'quant-position' ? 'button-blink' : ''}`} /> - +