refactor: migrate project storage from IndexedDB to OPFS with folder-based structure
- Replace monolithic KGStorage with KGProjectStorage (OPFS) and KGConfigStorage (IndexedDB) - Each project stored as folder: project.json + meta.json + media/ - Add KGConfigUpgrader with V1 upgrader to auto-migrate existing IndexedDB projects to OPFS - Request persistent storage via navigator.storage.persist() to prevent browser eviction - Show loading spinner during one-time migration - Enforce safe project name characters (letters, numbers, space, hyphen, underscore, period, parens) - Export projects as .kgstudio zip bundles instead of raw JSON - Import supports .kgstudio bundles (with meta.json validation) and legacy JSON files - Auto-deduplicate project names on import with (1), (2), etc. suffix - Add OPFS shell debugger (pwd, ls, cd, cat, dl) accessible via KGDebugger.opfs() - Add ESLint semi rule for consistent semicolons - Delete KGStorage.ts — all logic absorbed by new storage classes - Add jszip dependency for zip export/import
This commit is contained in:
+35
-3
@@ -2,6 +2,8 @@ import type { Selectable } from '../components/interfaces';
|
||||
import { KGProject } from './KGProject';
|
||||
import { KGAudioInterface } from './audio-interface/KGAudioInterface';
|
||||
import { ConfigManager } from './config/ConfigManager';
|
||||
import { KGProjectStorage } from './io/KGProjectStorage';
|
||||
import { KGConfigUpgrader } from './config-upgrader/KGConfigUpgrader';
|
||||
import { KGMidiRegion } from './region/KGMidiRegion';
|
||||
import { KGMidiNote } from './midi/KGMidiNote';
|
||||
import { KGRegion } from './region/KGRegion';
|
||||
@@ -30,6 +32,8 @@ export class KGCore {
|
||||
private copiedItems: Selectable[] = [];
|
||||
|
||||
private isPlaying: boolean = false;
|
||||
private isMigrating: boolean = false;
|
||||
private migrationStateChangeCallback: ((isMigrating: boolean) => void) | null = null;
|
||||
|
||||
// Timer management for playback
|
||||
private playbackIntervalId: number | null = null;
|
||||
@@ -71,11 +75,23 @@ export class KGCore {
|
||||
// Initialize configuration manager
|
||||
const configManager = ConfigManager.instance();
|
||||
await configManager.initialize();
|
||||
|
||||
|
||||
// Initialize OPFS project storage
|
||||
const projectStorage = KGProjectStorage.getInstance();
|
||||
await projectStorage.initialize();
|
||||
|
||||
// Run app-level migrations (e.g., IndexedDB -> OPFS project migration)
|
||||
this.setMigrating(true);
|
||||
try {
|
||||
await KGConfigUpgrader.upgradeToLatest();
|
||||
} finally {
|
||||
this.setMigrating(false);
|
||||
}
|
||||
|
||||
// Initialize audio interface
|
||||
const audioInterface = KGAudioInterface.instance();
|
||||
await audioInterface.initialize();
|
||||
|
||||
|
||||
console.log("KGCore components initialized successfully");
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize KGCore:", error);
|
||||
@@ -194,7 +210,23 @@ export class KGCore {
|
||||
this.selectionChangeCallbacks.forEach(callback => callback());
|
||||
}
|
||||
|
||||
// play
|
||||
// Migration state
|
||||
public getIsMigrating(): boolean {
|
||||
return this.isMigrating;
|
||||
}
|
||||
|
||||
private setMigrating(value: boolean): void {
|
||||
this.isMigrating = value;
|
||||
if (this.migrationStateChangeCallback) {
|
||||
this.migrationStateChangeCallback(value);
|
||||
}
|
||||
}
|
||||
|
||||
public setMigrationStateChangeCallback(callback: (isMigrating: boolean) => void): void {
|
||||
this.migrationStateChangeCallback = callback;
|
||||
}
|
||||
|
||||
// play
|
||||
public getIsPlaying(): boolean {
|
||||
return this.isPlaying;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user