feat: implement global chord track

This commit is contained in:
Xiaohan-Tian
2026-05-24 23:06:08 -07:00
parent 77fb0e4c1f
commit 1879df8033
30 changed files with 2757 additions and 20 deletions
@@ -0,0 +1,86 @@
import { KGCommand } from '../KGCommand';
import { KGCore } from '../../KGCore';
import { GlobalTrackType } from '../../global-track';
import { KGChordRegion } from '../../region/KGChordRegion';
import { generateUniqueId } from '../../../util/miscUtil';
import { findChordRegionAtBeat, findGlobalTrackByType } from '../../../util/globalTrackUtil';
export class InsertChordRegionAtBeatCommand extends KGCommand {
private readonly insertBeat: number;
private readonly symbol: string;
private readonly regionId: string;
private createdRegion: KGChordRegion | null = null;
private targetRegionId: string | null = null;
private originalTargetLength = 0;
constructor(insertBeat: number, symbol: string = 'C', regionId?: string) {
super();
this.insertBeat = insertBeat;
this.symbol = symbol;
this.regionId = regionId ?? generateUniqueId('KGChordRegion');
}
execute(): void {
const project = KGCore.instance().getCurrentProject();
const chordTrack = findGlobalTrackByType(project, GlobalTrackType.Chord);
if (!chordTrack) {
throw new Error('Chord global track not found');
}
const occupiedRegion = findChordRegionAtBeat(project, this.insertBeat);
if (!occupiedRegion) {
throw new Error(`No chord region found at beat ${this.insertBeat}`);
}
const regionStart = occupiedRegion.getStartFromBeat();
const regionEnd = regionStart + occupiedRegion.getLength();
if (this.insertBeat <= regionStart || this.insertBeat >= regionEnd) {
throw new Error(`Cannot insert chord at beat ${this.insertBeat} without shrinking region below minimum length`);
}
this.targetRegionId = occupiedRegion.getId();
this.originalTargetLength = occupiedRegion.getLength();
occupiedRegion.setLength(this.insertBeat - regionStart);
this.createdRegion = new KGChordRegion(
this.regionId,
chordTrack.getId(),
chordTrack.getTrackIndex(),
this.symbol,
this.insertBeat,
regionEnd - this.insertBeat
);
chordTrack.setRegions(
[...chordTrack.getRegions(), this.createdRegion]
.sort((left, right) => left.getStartFromBeat() - right.getStartFromBeat())
);
}
undo(): void {
const project = KGCore.instance().getCurrentProject();
const chordTrack = findGlobalTrackByType(project, GlobalTrackType.Chord);
if (!chordTrack || !this.targetRegionId || !this.createdRegion) {
throw new Error('Cannot undo inserted chord region');
}
chordTrack.removeRegion(this.createdRegion.getId());
const targetRegion = chordTrack.getRegions().find((region): region is KGChordRegion => (
region instanceof KGChordRegion && region.getId() === this.targetRegionId
));
if (!targetRegion) {
throw new Error(`Chord region ${this.targetRegionId} not found during undo`);
}
targetRegion.setLength(this.originalTargetLength);
chordTrack.setRegions([...chordTrack.getRegions()].sort((left, right) => left.getStartFromBeat() - right.getStartFromBeat()));
}
getDescription(): string {
return `Insert chord "${this.symbol}"`;
}
public getCreatedRegion(): KGChordRegion | null {
return this.createdRegion;
}
}