diff --git a/src/components/piano-roll/PianoRoll.tsx b/src/components/piano-roll/PianoRoll.tsx index ec050f8..8f88505 100644 --- a/src/components/piano-roll/PianoRoll.tsx +++ b/src/components/piano-roll/PianoRoll.tsx @@ -21,13 +21,13 @@ interface PianoRollProps { initialSize?: { width: number; height: number }; } -const PianoRoll: React.FC = ({ - onClose, +const PianoRoll: React.FC = ({ + onClose, regionId, initialPosition, initialSize }) => { - const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showInstrumentSelection, keySignature } = useProjectStore(); + const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showInstrumentSelection, keySignature, selectedMode, setSelectedMode } = useProjectStore(); // Tool state for piano roll const [activeTool, setActiveTool] = useState<'pointer' | 'pencil'>('pointer'); @@ -39,9 +39,6 @@ const PianoRoll: React.FC = ({ // 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 }); @@ -302,11 +299,10 @@ const PianoRoll: React.FC = ({ // Handle mode selection const handleModeSelect = useCallback((value: string) => { setSelectedMode(value); - KGPianoRollState.instance().setCurrentMode(value); if (DEBUG_MODE.PIANO_ROLL) { console.log(`Selected mode: ${value}`); } - }, []); + }, [setSelectedMode]); // Handler for receiving the setNoteUpdateCounter function from PianoRollContent const handleSetNoteUpdateTrigger = (setNoteFn: React.Dispatch>) => { diff --git a/src/core/KGProject.ts b/src/core/KGProject.ts index d1b9660..6cf07a8 100644 --- a/src/core/KGProject.ts +++ b/src/core/KGProject.ts @@ -31,11 +31,15 @@ export class KGProject { @WithDefault("C major") private keySignature: KeySignature = "C major"; + @Expose() + @WithDefault("ionian") + private selectedMode: string = "ionian"; + @Expose() @WithDefault(0) private projectStructureVersion: number = 0; - public static readonly CURRENT_PROJECT_STRUCTURE_VERSION: number = 1; + public static readonly CURRENT_PROJECT_STRUCTURE_VERSION: number = 2; @Expose() @Type(() => KGTrack, { @@ -50,13 +54,14 @@ export class KGProject { private tracks: KGTrack[] = []; // Constructor - constructor(name: string = "Untitled Project", maxBars: number = 32, currentBars: number = 0, bpm: number = 125, timeSignature: TimeSignature = { numerator: 4, denominator: 4 }, keySignature: KeySignature = "C major", tracks: KGTrack[] = [], projectStructureVersion: number = KGProject.CURRENT_PROJECT_STRUCTURE_VERSION) { + constructor(name: string = "Untitled Project", maxBars: number = 32, currentBars: number = 0, bpm: number = 125, timeSignature: TimeSignature = { numerator: 4, denominator: 4 }, keySignature: KeySignature = "C major", selectedMode: string = "ionian", tracks: KGTrack[] = [], projectStructureVersion: number = KGProject.CURRENT_PROJECT_STRUCTURE_VERSION) { this.name = name; this.maxBars = maxBars; this.currentBars = currentBars; this.bpm = bpm; this.timeSignature = timeSignature; this.keySignature = keySignature; + this.selectedMode = selectedMode; this.tracks = tracks; this.projectStructureVersion = projectStructureVersion; } @@ -90,6 +95,10 @@ export class KGProject { return this.keySignature; } + public getSelectedMode(): string { + return this.selectedMode; + } + public getTracks(): KGTrack[] { return this.tracks; } @@ -115,6 +124,10 @@ export class KGProject { this.keySignature = keySignature; } + public setSelectedMode(selectedMode: string): void { + this.selectedMode = selectedMode; + } + public setTracks(tracks: KGTrack[]): void { this.tracks = tracks; } diff --git a/src/core/commands/project/ChangeProjectPropertyCommand.ts b/src/core/commands/project/ChangeProjectPropertyCommand.ts index 0724eb0..28b1f6b 100644 --- a/src/core/commands/project/ChangeProjectPropertyCommand.ts +++ b/src/core/commands/project/ChangeProjectPropertyCommand.ts @@ -13,6 +13,7 @@ export interface ProjectUpdateProperties { bpm?: number; timeSignature?: TimeSignature; keySignature?: KeySignature; + selectedMode?: string; } /** @@ -42,6 +43,7 @@ export class ChangeProjectPropertyCommand extends KGCommand { bpm: this.targetProject.getBpm(), timeSignature: { ...this.targetProject.getTimeSignature() }, // Create a copy keySignature: this.targetProject.getKeySignature(), + selectedMode: this.targetProject.getSelectedMode(), }; // Apply updates and track what actually changes @@ -95,6 +97,13 @@ export class ChangeProjectPropertyCommand extends KGCommand { updatedProperties.push(`keySignature: "${this.originalProperties.keySignature}" → "${this.newProperties.keySignature}"`); } + // Update selected mode + if (this.newProperties.selectedMode !== undefined && this.newProperties.selectedMode !== this.originalProperties.selectedMode) { + this.targetProject.setSelectedMode(this.newProperties.selectedMode); + this.changedProperties.add('selectedMode'); + updatedProperties.push(`selectedMode: "${this.originalProperties.selectedMode}" → "${this.newProperties.selectedMode}"`); + } + if (updatedProperties.length > 0) { console.log(`Updated project: ${updatedProperties.join(', ')}`); } else { @@ -147,6 +156,12 @@ export class ChangeProjectPropertyCommand extends KGCommand { restoredProperties.push(`keySignature: "${this.originalProperties.keySignature}"`); } + // Restore selected mode (only if it was changed) + if (this.changedProperties.has('selectedMode') && this.originalProperties.selectedMode !== undefined) { + this.targetProject.setSelectedMode(this.originalProperties.selectedMode); + restoredProperties.push(`selectedMode: "${this.originalProperties.selectedMode}"`); + } + console.log(`Restored project: ${restoredProperties.join(', ')}`); } @@ -171,6 +186,9 @@ export class ChangeProjectPropertyCommand extends KGCommand { if (this.newProperties.keySignature !== undefined) { updatedProps.push('key signature'); } + if (this.newProperties.selectedMode !== undefined) { + updatedProps.push('selected mode'); + } if (updatedProps.length === 1) { return `Change project ${updatedProps[0]}`; diff --git a/src/core/project-upgrader/KGProjectUpgrader.ts b/src/core/project-upgrader/KGProjectUpgrader.ts index a3e3089..048d7dd 100644 --- a/src/core/project-upgrader/KGProjectUpgrader.ts +++ b/src/core/project-upgrader/KGProjectUpgrader.ts @@ -1,5 +1,6 @@ import { KGProject } from '../KGProject'; import { upgradeToV1 } from './upgradeToV1'; +import { upgradeToV2 } from './upgradeToV2'; /** * Upgrade the given project to the latest structure version, one version at a time. @@ -23,6 +24,10 @@ export function upgradeProjectToLatest(project: KGProject): KGProject { workingProject = upgradeToV1(workingProject); break; } + case 2: { + workingProject = upgradeToV2(workingProject); + break; + } default: { // If an upgrader is missing, throw to prevent loading incompatible structures throw new Error(`No upgrader found for project structure version ${nextVersion}`); diff --git a/src/core/project-upgrader/upgradeToV2.ts b/src/core/project-upgrader/upgradeToV2.ts new file mode 100644 index 0000000..fc9aadf --- /dev/null +++ b/src/core/project-upgrader/upgradeToV2.ts @@ -0,0 +1,20 @@ +import { KGProject } from '../KGProject'; + +/** + * Upgrade a project from structure version 1 to 2. + * Adds the selectedMode field with default value "ionian". + */ +export function upgradeToV2(project: KGProject): KGProject { + try { + // Set default selectedMode to "ionian" if not already set + const currentMode = project.getSelectedMode?.(); + if (!currentMode) { + project.setSelectedMode("ionian"); + } + } finally { + // Always set the project structure version to 2 to mark migration complete + project.setProjectStructureVersion(2); + } + + return project; +} diff --git a/src/stores/projectStore.ts b/src/stores/projectStore.ts index 26906d9..4804ec3 100644 --- a/src/stores/projectStore.ts +++ b/src/stores/projectStore.ts @@ -39,6 +39,7 @@ interface ProjectState { timeSignature: TimeSignature; bpm: number; keySignature: KeySignature; + selectedMode: string; playheadPosition: number; // in beats isPlaying: boolean; currentTime: string; // formatted time string @@ -87,7 +88,8 @@ interface ProjectState { setMaxBars: (maxBars: number) => void; setTimeSignature: (timeSignature: TimeSignature) => void; setKeySignature: (keySignature: KeySignature) => void; - + setSelectedMode: (selectedMode: string) => void; + // Selection actions syncSelectionFromCore: () => void; clearAllSelections: () => void; @@ -222,6 +224,7 @@ export const useProjectStore = create((set, get) => { timeSignature: currentProject.getTimeSignature(), bpm: currentProject.getBpm(), keySignature: currentProject.getKeySignature(), + selectedMode: currentProject.getSelectedMode(), playheadPosition: KGCore.instance().getPlayheadPosition(), isPlaying: KGCore.instance().getIsPlaying(), currentTime: beatsToTimeString(KGCore.instance().getPlayheadPosition(), currentProject.getBpm(), currentProject.getTimeSignature()), @@ -515,6 +518,7 @@ export const useProjectStore = create((set, get) => { timeSignature, bpm, keySignature, + selectedMode: projectToLoad.getSelectedMode(), playheadPosition: 0, // Ensure store state is also updated currentTime: beatsToTimeString(0, bpm, timeSignature) // Reset time display }); @@ -616,10 +620,10 @@ export const useProjectStore = create((set, get) => { // Create and execute the change project property command const command = new ChangeProjectPropertyCommand({ keySignature }); KGCore.instance().executeCommand(command); - + // Update the store state set({ keySignature }); - + console.log(`Set key signature to ${keySignature}`); } catch (error) { console.error('Error setting key signature:', error); @@ -627,6 +631,22 @@ export const useProjectStore = create((set, get) => { } }, + setSelectedMode: (selectedMode: string) => { + try { + // Create and execute the change project property command + const command = new ChangeProjectPropertyCommand({ selectedMode }); + KGCore.instance().executeCommand(command); + + // Update the store state + set({ selectedMode }); + + console.log(`Set selected mode to ${selectedMode}`); + } catch (error) { + console.error('Error setting selected mode:', error); + get().setStatus('Failed to set selected mode'); + } + }, + // Selection actions syncSelectionFromCore, @@ -784,7 +804,8 @@ export const useProjectStore = create((set, get) => { maxBars: project.getMaxBars(), timeSignature: project.getTimeSignature(), bpm: project.getBpm(), - keySignature: project.getKeySignature() + keySignature: project.getKeySignature(), + selectedMode: project.getSelectedMode() }); // Sync CSS variables that affect layout