259 lines
8.2 KiB
JavaScript
259 lines
8.2 KiB
JavaScript
const { app, BrowserWindow, dialog, ipcMain, Menu, shell } = require("electron");
|
||
const fs = require("node:fs/promises");
|
||
const path = require("node:path");
|
||
|
||
const isDev = !app.isPackaged;
|
||
const fileKinds = {
|
||
docx: {
|
||
title: "Открыть документ Word",
|
||
saveTitle: "Сохранить документ Word",
|
||
defaultName: "Документ QWord.docx",
|
||
filters: [{ name: "Документы Word (.docx)", extensions: ["docx"] }]
|
||
},
|
||
xlsx: {
|
||
title: "Открыть книгу Excel",
|
||
saveTitle: "Сохранить книгу Excel",
|
||
defaultName: "Книга QExcell.xlsx",
|
||
filters: [{ name: "Книги Excel (.xlsx)", extensions: ["xlsx"] }]
|
||
},
|
||
pptx: {
|
||
title: "Открыть презентацию PowerPoint",
|
||
saveTitle: "Сохранить презентацию PowerPoint",
|
||
defaultName: "Презентация QPowerPoint.pptx",
|
||
filters: [{ name: "Презентации PowerPoint (.pptx)", extensions: ["pptx"] }]
|
||
},
|
||
eml: {
|
||
title: "Открыть письмо EML",
|
||
saveTitle: "Сохранить письмо EML",
|
||
defaultName: "Письмо.eml",
|
||
filters: [{ name: "Письма EML (.eml)", extensions: ["eml"] }]
|
||
},
|
||
mail: {
|
||
title: "Открыть письмо Outlook",
|
||
saveTitle: "Сохранить письмо EML",
|
||
defaultName: "Письмо.eml",
|
||
filters: [{ name: "Письма Outlook (.eml, .msg)", extensions: ["eml", "msg"] }]
|
||
}
|
||
};
|
||
|
||
function validatedKind(kind) {
|
||
if (typeof kind !== "string" || !Object.hasOwn(fileKinds, kind)) {
|
||
throw new Error("Неподдерживаемый тип файла QOffice.");
|
||
}
|
||
|
||
return fileKinds[kind];
|
||
}
|
||
|
||
function safeDefaultName(kind, defaultFileName) {
|
||
const config = validatedKind(kind);
|
||
const extension = config.filters[0].extensions[0];
|
||
return safeFileNameWithExtension(defaultFileName, extension, config.defaultName);
|
||
}
|
||
|
||
function safeFileNameWithExtension(defaultFileName, extension, fallbackName) {
|
||
const candidate = typeof defaultFileName === "string" && defaultFileName.trim() ? path.basename(defaultFileName.trim()) : fallbackName;
|
||
return candidate.toLowerCase().endsWith(`.${extension}`) ? candidate : `${candidate}.${extension}`;
|
||
}
|
||
|
||
function ownerWindow(event) {
|
||
return BrowserWindow.fromWebContents(event.sender) ?? BrowserWindow.getFocusedWindow() ?? undefined;
|
||
}
|
||
|
||
function sendFocusedCommand(command) {
|
||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
||
focusedWindow.webContents.send("qoffice:command", command);
|
||
}
|
||
}
|
||
|
||
function createApplicationMenu() {
|
||
const isMac = process.platform === "darwin";
|
||
const template = [
|
||
...(isMac
|
||
? [
|
||
{
|
||
label: "QOffice",
|
||
submenu: [
|
||
{ role: "about", label: "О QOffice" },
|
||
{ type: "separator" },
|
||
{ role: "hide", label: "Скрыть QOffice" },
|
||
{ role: "hideOthers", label: "Скрыть остальные" },
|
||
{ role: "unhide", label: "Показать все" },
|
||
{ type: "separator" },
|
||
{ role: "quit", label: "Выйти из QOffice" }
|
||
]
|
||
}
|
||
]
|
||
: []),
|
||
{
|
||
label: "Файл",
|
||
submenu: [
|
||
{ label: "Открыть...", accelerator: "CmdOrCtrl+O", click: () => sendFocusedCommand("open") },
|
||
{ label: "Сохранить как...", accelerator: "CmdOrCtrl+S", click: () => sendFocusedCommand("save") },
|
||
{ type: "separator" },
|
||
{ label: "Экспорт в PDF...", accelerator: "CmdOrCtrl+Shift+E", click: () => sendFocusedCommand("export-pdf") },
|
||
{ type: "separator" },
|
||
{ label: "Печать...", accelerator: "CmdOrCtrl+P", click: () => sendFocusedCommand("print") },
|
||
{ type: "separator" },
|
||
isMac ? { role: "close", label: "Закрыть окно" } : { role: "quit", label: "Выход" }
|
||
]
|
||
},
|
||
{
|
||
label: "Правка",
|
||
submenu: [
|
||
{ role: "undo", label: "Отменить" },
|
||
{ role: "redo", label: "Повторить" },
|
||
{ type: "separator" },
|
||
{ role: "cut", label: "Вырезать" },
|
||
{ role: "copy", label: "Копировать" },
|
||
{ role: "paste", label: "Вставить" },
|
||
{ role: "selectAll", label: "Выделить все" }
|
||
]
|
||
},
|
||
{
|
||
label: "Вид",
|
||
submenu: [
|
||
{ role: "resetZoom", label: "Сбросить масштаб" },
|
||
{ role: "zoomIn", label: "Увеличить" },
|
||
{ role: "zoomOut", label: "Уменьшить" },
|
||
{ type: "separator" },
|
||
{ role: "togglefullscreen", label: "Во весь экран" }
|
||
]
|
||
},
|
||
{
|
||
label: "Окно",
|
||
submenu: [
|
||
{ role: "minimize", label: "Свернуть" },
|
||
{ role: "reload", label: "Перезагрузить" },
|
||
...(isDev ? [{ role: "toggleDevTools", label: "Инструменты разработчика" }] : [])
|
||
]
|
||
}
|
||
];
|
||
|
||
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
||
}
|
||
|
||
ipcMain.handle("qoffice:open-office-file", async (event, kind) => {
|
||
const config = validatedKind(kind);
|
||
const result = await dialog.showOpenDialog(ownerWindow(event), {
|
||
title: config.title,
|
||
properties: ["openFile"],
|
||
filters: config.filters
|
||
});
|
||
|
||
if (result.canceled || result.filePaths.length === 0) {
|
||
return { canceled: true };
|
||
}
|
||
|
||
const filePath = result.filePaths[0];
|
||
const bytes = await fs.readFile(filePath);
|
||
return {
|
||
canceled: false,
|
||
fileName: path.basename(filePath),
|
||
filePath,
|
||
base64Data: bytes.toString("base64")
|
||
};
|
||
});
|
||
|
||
ipcMain.handle("qoffice:save-office-file", async (event, payload) => {
|
||
if (!payload || typeof payload !== "object") {
|
||
throw new Error("Некорректный запрос сохранения файла QOffice.");
|
||
}
|
||
|
||
const { kind, defaultFileName, base64Data } = payload;
|
||
const config = validatedKind(kind);
|
||
if (typeof base64Data !== "string") {
|
||
throw new Error("Некорректные данные файла QOffice.");
|
||
}
|
||
|
||
const result = await dialog.showSaveDialog(ownerWindow(event), {
|
||
title: config.saveTitle,
|
||
defaultPath: safeDefaultName(kind, defaultFileName),
|
||
filters: config.filters
|
||
});
|
||
|
||
if (result.canceled || !result.filePath) {
|
||
return { canceled: true };
|
||
}
|
||
|
||
await fs.writeFile(result.filePath, Buffer.from(base64Data, "base64"));
|
||
return {
|
||
canceled: false,
|
||
fileName: path.basename(result.filePath),
|
||
filePath: result.filePath
|
||
};
|
||
});
|
||
|
||
ipcMain.handle("qoffice:export-pdf", async (event, defaultFileName) => {
|
||
const sourceWindow = ownerWindow(event);
|
||
if (!sourceWindow) {
|
||
throw new Error("Не удалось найти окно QOffice для экспорта PDF.");
|
||
}
|
||
|
||
const result = await dialog.showSaveDialog(sourceWindow, {
|
||
title: "Экспорт PDF",
|
||
defaultPath: safeFileNameWithExtension(defaultFileName, "pdf", "QOffice.pdf"),
|
||
filters: [{ name: "PDF (.pdf)", extensions: ["pdf"] }]
|
||
});
|
||
|
||
if (result.canceled || !result.filePath) {
|
||
return { canceled: true };
|
||
}
|
||
|
||
const pdf = await event.sender.printToPDF({
|
||
printBackground: true,
|
||
preferCSSPageSize: true
|
||
});
|
||
|
||
await fs.writeFile(result.filePath, pdf);
|
||
return {
|
||
canceled: false,
|
||
fileName: path.basename(result.filePath),
|
||
filePath: result.filePath
|
||
};
|
||
});
|
||
|
||
function createWindow() {
|
||
const mainWindow = new BrowserWindow({
|
||
width: 1440,
|
||
height: 920,
|
||
minWidth: 1080,
|
||
minHeight: 720,
|
||
title: "QOffice",
|
||
backgroundColor: "#f6f8fb",
|
||
webPreferences: {
|
||
preload: path.join(__dirname, "preload.cjs"),
|
||
contextIsolation: true,
|
||
nodeIntegration: false
|
||
}
|
||
});
|
||
|
||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||
shell.openExternal(url);
|
||
return { action: "deny" };
|
||
});
|
||
|
||
if (isDev) {
|
||
mainWindow.loadURL("http://127.0.0.1:5173");
|
||
} else {
|
||
mainWindow.loadFile(path.join(__dirname, "../dist/index.html"));
|
||
}
|
||
}
|
||
|
||
app.whenReady().then(() => {
|
||
createApplicationMenu();
|
||
createWindow();
|
||
|
||
app.on("activate", () => {
|
||
if (BrowserWindow.getAllWindows().length === 0) {
|
||
createWindow();
|
||
}
|
||
});
|
||
});
|
||
|
||
app.on("window-all-closed", () => {
|
||
if (process.platform !== "darwin") {
|
||
app.quit();
|
||
}
|
||
});
|