import { renderKeyboardTrack } from './audio/export'; import type { SampleCache } from './audio/sampleCache'; import { saveAudio, saveProject } from './platform/files'; import type { LegacySwitchType, Marker, Project, SwitchType } from './types'; import { app, setActiveSwitch, setMarkers, setSelectedIds } from './store.svelte'; const LEGACY_SWITCH_MAP: Record = { 'cherry-mx-blue': 'mxblue', 'cherry-mx-red': 'mxblack', 'cherry-mx-brown': 'mxbrown', }; function migrateSwitch(value: string): SwitchType { if (value in LEGACY_SWITCH_MAP) { return LEGACY_SWITCH_MAP[value as LegacySwitchType]; } return value as SwitchType; } function migrateMarker(marker: Marker): Marker { return { ...marker, code: marker.code, releaseTime: marker.releaseTime, }; } export function buildProject(): Project { return { version: 2, switch: app.activeSwitch, markers: app.markers.map((m) => ({ ...m })), }; } export async function downloadProject(filename = 'project.json') { const project = buildProject(); const content = JSON.stringify(project, null, 2); await saveProject(content, filename); } export function loadProject(data: Project & { version?: number; switch?: string }) { if (data.version !== 1 && data.version !== 2) { throw new Error(`Unsupported project version: ${data.version}`); } setMarkers((data.markers ?? []).map(migrateMarker)); setActiveSwitch(migrateSwitch(data.switch ?? 'mxbrown')); setSelectedIds(new Set()); } /** * Export keyboard SFX only as a WAV file (no video audio). * Rendered client-side at 1× via OfflineAudioContext. */ export async function exportAudio(duration: number, cache?: SampleCache) { const project = buildProject(); const blob = await renderKeyboardTrack({ duration, switchType: project.switch, markers: project.markers, cache, }); await saveAudio(blob, 'keyboard_track.wav'); }