fix: game audio persistence and editor perf
- Keep game/campaign audio assets referenced (no prune) - Flush pending project save on quit/switch/export to avoid losing campaignAudios - Control: prevent game music restarts on scene changes; allow always-on controls; handle autoplay-after-scene-audio - Editor: reduce ReactFlow churn with stable scene card map; lazy/async image decode - Add contract/unit tests and update test script Made-with: Cursor
This commit is contained in:
@@ -172,6 +172,27 @@ async function runStartupAfterHandlers(licenseService: LicenseService): Promise<
|
||||
|
||||
async function main() {
|
||||
await app.whenReady();
|
||||
/**
|
||||
* `ZipProjectStore` пишет `project.json` в кэш синхронно с мутациями, а упаковку `.dnd.zip`
|
||||
* откладывает (`queueSave`, ~250 мс). Без финального `saveNow()` при выходе на диске
|
||||
* остаётся старый zip — при следующем открытии кэш пересоздаётся из него и теряются
|
||||
* недавние поля (в т.ч. `campaignAudios`).
|
||||
*/
|
||||
let appQuittingAfterPendingSave = false;
|
||||
app.on('before-quit', (e) => {
|
||||
if (appQuittingAfterPendingSave) return;
|
||||
e.preventDefault();
|
||||
appQuittingAfterPendingSave = true;
|
||||
void projectStore
|
||||
.saveNow()
|
||||
.catch((err: unknown) => {
|
||||
console.error('[before-quit] saveNow failed', err);
|
||||
})
|
||||
.finally(() => {
|
||||
app.quit();
|
||||
});
|
||||
});
|
||||
|
||||
const licenseService = new LicenseService(app.getPath('userData'));
|
||||
setLicenseAssert(() => {
|
||||
licenseService.assertForIpc();
|
||||
@@ -286,6 +307,30 @@ async function main() {
|
||||
emitSessionState();
|
||||
return result;
|
||||
});
|
||||
registerHandler(ipcChannels.project.importCampaignAudio, async () => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Аудио',
|
||||
extensions: ['mp3', 'wav', 'ogg', 'm4a', 'aac'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || filePaths.length === 0) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { canceled: true as const, project, imported: [] };
|
||||
}
|
||||
const result = await projectStore.importCampaignAudioFiles(filePaths);
|
||||
emitSessionState();
|
||||
return { canceled: false as const, ...result };
|
||||
});
|
||||
registerHandler(ipcChannels.project.updateCampaignAudios, async ({ audios }) => {
|
||||
const project = await projectStore.setCampaignAudios(audios);
|
||||
emitSessionState();
|
||||
return { project };
|
||||
});
|
||||
registerHandler(ipcChannels.project.importScenePreview, async ({ sceneId }) => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
|
||||
Reference in New Issue
Block a user