feat: persistence selected mode
This commit is contained in:
@@ -27,7 +27,7 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
initialPosition,
|
initialPosition,
|
||||||
initialSize
|
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
|
// Tool state for piano roll
|
||||||
const [activeTool, setActiveTool] = useState<'pointer' | 'pencil'>('pointer');
|
const [activeTool, setActiveTool] = useState<'pointer' | 'pencil'>('pointer');
|
||||||
@@ -39,9 +39,6 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
// Snapping state
|
// Snapping state
|
||||||
const [snapping, setSnapping] = useState<string>('NO SNAP');
|
const [snapping, setSnapping] = useState<string>('NO SNAP');
|
||||||
|
|
||||||
// Mode state
|
|
||||||
const [selectedMode, setSelectedMode] = useState<string>('ionian');
|
|
||||||
|
|
||||||
// Piano roll state with temporary initial values
|
// Piano roll state with temporary initial values
|
||||||
const [position, setPosition] = useState(initialPosition || { x: 0, y: 0 });
|
const [position, setPosition] = useState(initialPosition || { x: 0, y: 0 });
|
||||||
|
|
||||||
@@ -302,11 +299,10 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
// Handle mode selection
|
// Handle mode selection
|
||||||
const handleModeSelect = useCallback((value: string) => {
|
const handleModeSelect = useCallback((value: string) => {
|
||||||
setSelectedMode(value);
|
setSelectedMode(value);
|
||||||
KGPianoRollState.instance().setCurrentMode(value);
|
|
||||||
if (DEBUG_MODE.PIANO_ROLL) {
|
if (DEBUG_MODE.PIANO_ROLL) {
|
||||||
console.log(`Selected mode: ${value}`);
|
console.log(`Selected mode: ${value}`);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [setSelectedMode]);
|
||||||
|
|
||||||
// Handler for receiving the setNoteUpdateCounter function from PianoRollContent
|
// Handler for receiving the setNoteUpdateCounter function from PianoRollContent
|
||||||
const handleSetNoteUpdateTrigger = (setNoteFn: React.Dispatch<React.SetStateAction<number>>) => {
|
const handleSetNoteUpdateTrigger = (setNoteFn: React.Dispatch<React.SetStateAction<number>>) => {
|
||||||
|
|||||||
+15
-2
@@ -31,11 +31,15 @@ export class KGProject {
|
|||||||
@WithDefault("C major")
|
@WithDefault("C major")
|
||||||
private keySignature: KeySignature = "C major";
|
private keySignature: KeySignature = "C major";
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
@WithDefault("ionian")
|
||||||
|
private selectedMode: string = "ionian";
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
@WithDefault(0)
|
@WithDefault(0)
|
||||||
private projectStructureVersion: number = 0;
|
private projectStructureVersion: number = 0;
|
||||||
|
|
||||||
public static readonly CURRENT_PROJECT_STRUCTURE_VERSION: number = 1;
|
public static readonly CURRENT_PROJECT_STRUCTURE_VERSION: number = 2;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
@Type(() => KGTrack, {
|
@Type(() => KGTrack, {
|
||||||
@@ -50,13 +54,14 @@ export class KGProject {
|
|||||||
private tracks: KGTrack[] = [];
|
private tracks: KGTrack[] = [];
|
||||||
|
|
||||||
// Constructor
|
// 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.name = name;
|
||||||
this.maxBars = maxBars;
|
this.maxBars = maxBars;
|
||||||
this.currentBars = currentBars;
|
this.currentBars = currentBars;
|
||||||
this.bpm = bpm;
|
this.bpm = bpm;
|
||||||
this.timeSignature = timeSignature;
|
this.timeSignature = timeSignature;
|
||||||
this.keySignature = keySignature;
|
this.keySignature = keySignature;
|
||||||
|
this.selectedMode = selectedMode;
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
this.projectStructureVersion = projectStructureVersion;
|
this.projectStructureVersion = projectStructureVersion;
|
||||||
}
|
}
|
||||||
@@ -90,6 +95,10 @@ export class KGProject {
|
|||||||
return this.keySignature;
|
return this.keySignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSelectedMode(): string {
|
||||||
|
return this.selectedMode;
|
||||||
|
}
|
||||||
|
|
||||||
public getTracks(): KGTrack[] {
|
public getTracks(): KGTrack[] {
|
||||||
return this.tracks;
|
return this.tracks;
|
||||||
}
|
}
|
||||||
@@ -115,6 +124,10 @@ export class KGProject {
|
|||||||
this.keySignature = keySignature;
|
this.keySignature = keySignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setSelectedMode(selectedMode: string): void {
|
||||||
|
this.selectedMode = selectedMode;
|
||||||
|
}
|
||||||
|
|
||||||
public setTracks(tracks: KGTrack[]): void {
|
public setTracks(tracks: KGTrack[]): void {
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface ProjectUpdateProperties {
|
|||||||
bpm?: number;
|
bpm?: number;
|
||||||
timeSignature?: TimeSignature;
|
timeSignature?: TimeSignature;
|
||||||
keySignature?: KeySignature;
|
keySignature?: KeySignature;
|
||||||
|
selectedMode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +43,7 @@ export class ChangeProjectPropertyCommand extends KGCommand {
|
|||||||
bpm: this.targetProject.getBpm(),
|
bpm: this.targetProject.getBpm(),
|
||||||
timeSignature: { ...this.targetProject.getTimeSignature() }, // Create a copy
|
timeSignature: { ...this.targetProject.getTimeSignature() }, // Create a copy
|
||||||
keySignature: this.targetProject.getKeySignature(),
|
keySignature: this.targetProject.getKeySignature(),
|
||||||
|
selectedMode: this.targetProject.getSelectedMode(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply updates and track what actually changes
|
// 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}"`);
|
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) {
|
if (updatedProperties.length > 0) {
|
||||||
console.log(`Updated project: ${updatedProperties.join(', ')}`);
|
console.log(`Updated project: ${updatedProperties.join(', ')}`);
|
||||||
} else {
|
} else {
|
||||||
@@ -147,6 +156,12 @@ export class ChangeProjectPropertyCommand extends KGCommand {
|
|||||||
restoredProperties.push(`keySignature: "${this.originalProperties.keySignature}"`);
|
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(', ')}`);
|
console.log(`Restored project: ${restoredProperties.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +186,9 @@ export class ChangeProjectPropertyCommand extends KGCommand {
|
|||||||
if (this.newProperties.keySignature !== undefined) {
|
if (this.newProperties.keySignature !== undefined) {
|
||||||
updatedProps.push('key signature');
|
updatedProps.push('key signature');
|
||||||
}
|
}
|
||||||
|
if (this.newProperties.selectedMode !== undefined) {
|
||||||
|
updatedProps.push('selected mode');
|
||||||
|
}
|
||||||
|
|
||||||
if (updatedProps.length === 1) {
|
if (updatedProps.length === 1) {
|
||||||
return `Change project ${updatedProps[0]}`;
|
return `Change project ${updatedProps[0]}`;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { KGProject } from '../KGProject';
|
import { KGProject } from '../KGProject';
|
||||||
import { upgradeToV1 } from './upgradeToV1';
|
import { upgradeToV1 } from './upgradeToV1';
|
||||||
|
import { upgradeToV2 } from './upgradeToV2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade the given project to the latest structure version, one version at a time.
|
* 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);
|
workingProject = upgradeToV1(workingProject);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 2: {
|
||||||
|
workingProject = upgradeToV2(workingProject);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
// If an upgrader is missing, throw to prevent loading incompatible structures
|
// If an upgrader is missing, throw to prevent loading incompatible structures
|
||||||
throw new Error(`No upgrader found for project structure version ${nextVersion}`);
|
throw new Error(`No upgrader found for project structure version ${nextVersion}`);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ interface ProjectState {
|
|||||||
timeSignature: TimeSignature;
|
timeSignature: TimeSignature;
|
||||||
bpm: number;
|
bpm: number;
|
||||||
keySignature: KeySignature;
|
keySignature: KeySignature;
|
||||||
|
selectedMode: string;
|
||||||
playheadPosition: number; // in beats
|
playheadPosition: number; // in beats
|
||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
currentTime: string; // formatted time string
|
currentTime: string; // formatted time string
|
||||||
@@ -87,6 +88,7 @@ interface ProjectState {
|
|||||||
setMaxBars: (maxBars: number) => void;
|
setMaxBars: (maxBars: number) => void;
|
||||||
setTimeSignature: (timeSignature: TimeSignature) => void;
|
setTimeSignature: (timeSignature: TimeSignature) => void;
|
||||||
setKeySignature: (keySignature: KeySignature) => void;
|
setKeySignature: (keySignature: KeySignature) => void;
|
||||||
|
setSelectedMode: (selectedMode: string) => void;
|
||||||
|
|
||||||
// Selection actions
|
// Selection actions
|
||||||
syncSelectionFromCore: () => void;
|
syncSelectionFromCore: () => void;
|
||||||
@@ -222,6 +224,7 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
timeSignature: currentProject.getTimeSignature(),
|
timeSignature: currentProject.getTimeSignature(),
|
||||||
bpm: currentProject.getBpm(),
|
bpm: currentProject.getBpm(),
|
||||||
keySignature: currentProject.getKeySignature(),
|
keySignature: currentProject.getKeySignature(),
|
||||||
|
selectedMode: currentProject.getSelectedMode(),
|
||||||
playheadPosition: KGCore.instance().getPlayheadPosition(),
|
playheadPosition: KGCore.instance().getPlayheadPosition(),
|
||||||
isPlaying: KGCore.instance().getIsPlaying(),
|
isPlaying: KGCore.instance().getIsPlaying(),
|
||||||
currentTime: beatsToTimeString(KGCore.instance().getPlayheadPosition(), currentProject.getBpm(), currentProject.getTimeSignature()),
|
currentTime: beatsToTimeString(KGCore.instance().getPlayheadPosition(), currentProject.getBpm(), currentProject.getTimeSignature()),
|
||||||
@@ -515,6 +518,7 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
timeSignature,
|
timeSignature,
|
||||||
bpm,
|
bpm,
|
||||||
keySignature,
|
keySignature,
|
||||||
|
selectedMode: projectToLoad.getSelectedMode(),
|
||||||
playheadPosition: 0, // Ensure store state is also updated
|
playheadPosition: 0, // Ensure store state is also updated
|
||||||
currentTime: beatsToTimeString(0, bpm, timeSignature) // Reset time display
|
currentTime: beatsToTimeString(0, bpm, timeSignature) // Reset time display
|
||||||
});
|
});
|
||||||
@@ -627,6 +631,22 @@ export const useProjectStore = create<ProjectState>((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
|
// Selection actions
|
||||||
syncSelectionFromCore,
|
syncSelectionFromCore,
|
||||||
|
|
||||||
@@ -784,7 +804,8 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
maxBars: project.getMaxBars(),
|
maxBars: project.getMaxBars(),
|
||||||
timeSignature: project.getTimeSignature(),
|
timeSignature: project.getTimeSignature(),
|
||||||
bpm: project.getBpm(),
|
bpm: project.getBpm(),
|
||||||
keySignature: project.getKeySignature()
|
keySignature: project.getKeySignature(),
|
||||||
|
selectedMode: project.getSelectedMode()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sync CSS variables that affect layout
|
// Sync CSS variables that affect layout
|
||||||
|
|||||||
Reference in New Issue
Block a user