feat(project): optimize image imports and converter

- Optimize imported scene preview images (smart WebP/JPEG/PNG, preserve alpha, keep pixel size)

- Update converter to re-encode existing image assets with same algorithm

- Improve import/export progress overlay and reduce presentation slide stutter

Made-with: Cursor
This commit is contained in:
Ivan Fontosh
2026-04-23 17:59:57 +08:00
parent 1d051f8bf9
commit 8f8eef53c9
33 changed files with 3684 additions and 68 deletions
+36 -2
View File
@@ -27,6 +27,20 @@ import {
waitForEditorWindowReady,
} from './windows/createWindows';
function emitZipProgress(evt: {
kind: 'import' | 'export';
stage: string;
percent: number;
detail?: string;
}): void {
for (const win of BrowserWindow.getAllWindows()) {
win.webContents.send(
evt.kind === 'import' ? ipcChannels.project.importZipProgress : ipcChannels.project.exportZipProgress,
evt,
);
}
}
/**
* Отключение GPU ломает скорость вторичных окон (презентация/пульт — WebGL). По умолчанию не трогаем.
* При чёрном экране в упакованной сборке: `DND_DISABLE_GPU=1`.
@@ -403,7 +417,18 @@ async function main() {
if (canceled || !filePaths[0]) {
return { canceled: true as const };
}
const project = await projectStore.importProjectFromExternalZip(filePaths[0]);
const srcPath = filePaths[0];
emitZipProgress({ kind: 'import', stage: 'copy', percent: 0, detail: 'Копирование…' });
// Let store import; progress for unzip is emitted from unzipToDir wrapper in store.
const project = await projectStore.importProjectFromExternalZip(srcPath, (p) => {
emitZipProgress({
kind: 'import',
stage: p.stage,
percent: p.percent,
...(p.detail ? { detail: p.detail } : null),
});
});
emitZipProgress({ kind: 'import', stage: 'done', percent: 100, detail: 'Готово' });
emitSessionState();
return { canceled: false as const, project };
});
@@ -428,7 +453,16 @@ async function main() {
if (!lower.endsWith('.dnd.zip')) {
dest = lower.endsWith('.zip') ? dest.replace(/\.zip$/iu, '.dnd.zip') : `${dest}.dnd.zip`;
}
await projectStore.exportProjectZipToPath(projectId, dest);
emitZipProgress({ kind: 'export', stage: 'copy', percent: 0, detail: 'Экспорт…' });
await projectStore.exportProjectZipToPath(projectId, dest, (p) => {
emitZipProgress({
kind: 'export',
stage: p.stage,
percent: p.percent,
...(p.detail ? { detail: p.detail } : null),
});
});
emitZipProgress({ kind: 'export', stage: 'done', percent: 100, detail: 'Готово' });
return { canceled: false as const };
});
registerHandler(ipcChannels.project.deleteProject, async ({ projectId }) => {