45075b3328
Replace hidden file inputs and anchor downloads with native open/save dialogs via a thin platform layer. WAV export sends base64 audio data to the Go backend for writing to the user-chosen path. Co-authored-by: Cursor <cursoragent@cursor.com>
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
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<LegacySwitchType, SwitchType> = {
|
||
'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');
|
||
}
|