feat(editor): drag-and-drop для аудио и превью сцены
Добавлена загрузка файлов перетаскиванием в блоки «Аудио игры», «Аудио сцены» и «Превью сцены»: подсветка зоны, подсказки, фильтрация по типу, импорт без диалога. IPC и main принимают пути файлов напрямую; для Electron 41 пути получаются через webUtils.getPathForFile в preload (File.path в renderer больше недоступен). Улучшен dev-запуск: Vite слушает 127.0.0.1, ожидание готовности по localhost. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+55
-43
@@ -392,39 +392,47 @@ async function main() {
|
||||
emitSessionState();
|
||||
return { scene: next };
|
||||
});
|
||||
registerHandler(ipcChannels.project.importMedia, async ({ sceneId }) => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Видео и аудио',
|
||||
extensions: ['mp4', 'webm', 'mov', 'mp3', 'wav', 'ogg', 'm4a', 'aac'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || filePaths.length === 0) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { project, imported: [] };
|
||||
registerHandler(ipcChannels.project.importMedia, async ({ sceneId, filePaths: pathsFromDrop }) => {
|
||||
let filePaths = pathsFromDrop ?? [];
|
||||
if (!pathsFromDrop) {
|
||||
const { canceled, filePaths: picked } = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Видео и аудио',
|
||||
extensions: ['mp4', 'webm', 'mov', 'mp3', 'wav', 'ogg', 'm4a', 'aac'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || picked.length === 0) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { project, imported: [] };
|
||||
}
|
||||
filePaths = picked;
|
||||
}
|
||||
const result = await projectStore.importMediaFiles(sceneId, filePaths);
|
||||
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: [] };
|
||||
registerHandler(ipcChannels.project.importCampaignAudio, async ({ filePaths: pathsFromDrop }) => {
|
||||
let filePaths = pathsFromDrop ?? [];
|
||||
if (!pathsFromDrop) {
|
||||
const { canceled, filePaths: picked } = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Аудио',
|
||||
extensions: ['mp3', 'wav', 'ogg', 'm4a', 'aac'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || picked.length === 0) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { canceled: true as const, project, imported: [] };
|
||||
}
|
||||
filePaths = picked;
|
||||
}
|
||||
const result = await projectStore.importCampaignAudioFiles(filePaths);
|
||||
emitSessionState();
|
||||
@@ -435,22 +443,26 @@ async function main() {
|
||||
emitSessionState();
|
||||
return { project };
|
||||
});
|
||||
registerHandler(ipcChannels.project.importScenePreview, async ({ sceneId }) => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Изображения и видео',
|
||||
extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'mp4', 'webm', 'mov'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || !filePaths[0]) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { project, assetId: null, background: false };
|
||||
registerHandler(ipcChannels.project.importScenePreview, async ({ sceneId, filePath: pathFromDrop }) => {
|
||||
let filePath = pathFromDrop;
|
||||
if (!filePath) {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Изображения и видео',
|
||||
extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'mp4', 'webm', 'mov'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (canceled || !filePaths[0]) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { project, assetId: null, background: false };
|
||||
}
|
||||
filePath = filePaths[0];
|
||||
}
|
||||
const result = await projectStore.importScenePreviewMedia(sceneId, filePaths[0]);
|
||||
const result = await projectStore.importScenePreviewMedia(sceneId, filePath);
|
||||
emitSessionState();
|
||||
emitScenePreviewImportProgress({
|
||||
sceneId,
|
||||
|
||||
Reference in New Issue
Block a user