volume panel; copilot instructions

This commit is contained in:
2026-04-15 16:10:35 -06:00
parent 0df967507f
commit 4f90750497
11 changed files with 688 additions and 106 deletions

View File

@ -8,6 +8,17 @@ let pythonBackend = null;
const isDev = !app.isPackaged;
const BACKEND_PORT = 8642;
function getProjectDirectory() {
const fs = require('fs');
// Keep project files alongside the TalkEdit workspace in development.
// electron/main.js lives under <repo>/electron, so repo root is ../
const projectsDir = path.join(__dirname, '..', 'Projects');
if (!fs.existsSync(projectsDir)) {
fs.mkdirSync(projectsDir, { recursive: true });
}
return projectsDir;
}
function createWindow() {
mainWindow = new BrowserWindow({
width: 1400,
@ -92,7 +103,9 @@ ipcMain.handle('dialog:saveFile', async (_event, options) => {
});
ipcMain.handle('dialog:openProject', async () => {
const projectDir = getProjectDirectory();
const result = await dialog.showOpenDialog(mainWindow, {
defaultPath: projectDir,
properties: ['openFile'],
filters: [
{ name: 'AI Video Editor Project', extensions: ['aive'] },
@ -101,6 +114,18 @@ ipcMain.handle('dialog:openProject', async () => {
return result.canceled ? null : result.filePaths[0];
});
ipcMain.handle('dialog:saveProject', async (_event, options) => {
const projectDir = getProjectDirectory();
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: path.join(projectDir, 'project.aive'),
filters: [
{ name: 'AI Video Editor Project', extensions: ['aive'] },
],
...options,
});
return result.canceled ? null : result.filePath;
});
ipcMain.handle('safe-storage:encrypt', (_event, data) => {
if (safeStorage.isEncryptionAvailable()) {
return safeStorage.encryptString(data).toString('base64');
@ -119,6 +144,48 @@ ipcMain.handle('get-backend-url', () => {
return `http://localhost:${BACKEND_PORT}`;
});
ipcMain.handle('backend:ensureModel', async (_event, modelName) => {
// Python backend downloads models lazily during transcription.
// Keep this IPC for compatibility with existing renderer flow.
return modelName;
});
ipcMain.handle('backend:transcribe', async (_event, payload) => {
const filePath = payload?.filePath;
const modelName = payload?.modelName || 'base';
const language = payload?.language;
if (!filePath) {
throw new Error('Missing file path for transcription');
}
const res = await fetch(`http://127.0.0.1:${BACKEND_PORT}/transcribe`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
file_path: filePath,
model: modelName,
language,
use_gpu: true,
use_cache: true,
diarize: false,
}),
});
if (!res.ok) {
let detail = `${res.status} ${res.statusText}`;
try {
const body = await res.json();
if (body?.detail) detail = `${detail}: ${body.detail}`;
} catch (_err) {
// ignore JSON parse errors for non-JSON responses
}
throw new Error(`Transcription failed: ${detail}`);
}
return await res.json();
});
ipcMain.handle('fs:readFile', async (_event, filePath) => {
const fs = require('fs');
return fs.readFileSync(filePath, 'utf-8');