Files
2026-06-01 23:15:20 +03:00

312 lines
9.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 applicationModes = {
word: {
title: "QWord",
windowTitle: "Документ1 - QWord",
route: "/qword"
},
sheets: {
title: "QExcell",
windowTitle: "Книга1 - QExcell",
route: "/qexcell"
},
slides: {
title: "QPowerPoint",
windowTitle: "Презентация1 - QPowerPoint",
route: "/qpowerpoint"
},
mail: {
title: "QOutlook",
windowTitle: "Почта - QOutlook",
route: "/qoutlook"
}
};
const applicationAliases = {
word: "word",
qword: "word",
docx: "word",
sheets: "sheets",
sheet: "sheets",
excel: "sheets",
excell: "sheets",
qexcell: "sheets",
xlsx: "sheets",
slides: "slides",
slide: "slides",
powerpoint: "slides",
qpowerpoint: "slides",
pptx: "slides",
mail: "mail",
outlook: "mail",
qoutlook: "mail",
eml: "mail",
msg: "mail"
};
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 applicationModeFromArgs(argv = process.argv, env = process.env) {
const explicitArg = argv.find((argument) => argument.startsWith("--qoffice-app=") || argument.startsWith("--app="));
const rawMode = explicitArg?.split("=")[1] ?? env.QOFFICE_APP ?? "";
const mode = applicationAliases[String(rawMode).trim().toLowerCase()] ?? "word";
return applicationModes[mode];
}
function createApplicationMenu(applicationMode) {
const isMac = process.platform === "darwin";
const template = [
...(isMac
? [
{
label: applicationMode.title,
submenu: [
{ role: "about", label: `О ${applicationMode.title}` },
{ type: "separator" },
{ role: "hide", label: `Скрыть ${applicationMode.title}` },
{ role: "hideOthers", label: "Скрыть остальные" },
{ role: "unhide", label: "Показать все" },
{ type: "separator" },
{ role: "quit", label: `Выйти из ${applicationMode.title}` }
]
}
]
: []),
{
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: `Выход из ${applicationMode.title}` }
]
},
{
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(applicationMode) {
const mainWindow = new BrowserWindow({
width: 1440,
height: 920,
minWidth: 1080,
minHeight: 720,
title: applicationMode.windowTitle,
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(new URL(applicationMode.route, "http://127.0.0.1:5173").toString());
} else {
mainWindow.loadFile(path.join(__dirname, "../dist/index.html"), { hash: applicationMode.route });
}
}
const launchApplicationMode = applicationModeFromArgs();
app.setName(launchApplicationMode.title);
app.whenReady().then(() => {
createApplicationMenu(launchApplicationMode);
createWindow(launchApplicationMode);
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow(launchApplicationMode);
}
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});