添加linux支持

This commit is contained in:
2020-07-23 21:44:25 +08:00
parent 5f2b0de85b
commit 0b019a42a1
13 changed files with 1223 additions and 256 deletions
+2
View File
@@ -5,3 +5,5 @@ node_modules
# Ignore built ts files # Ignore built ts files
/bin/ /bin/
dist/**/* dist/**/*
/config.json
+8 -3
View File
@@ -30,7 +30,6 @@ Google Translate网页版的封装,通过[Electron](https://www.electronjs.org
* 不支持离线使用; * 不支持离线使用;
* 不支持取词划词; * 不支持取词划词;
* 未测试Windows之外的平台;
对于Mini窗口自动获取输入焦点和自动清除输入,由于是通过`dom-ready`事件注入JavaScript实现,因此在`dom-ready`之后才能生效。而这需要大概十几秒,因为`translate.google.cn`尝试从`google.com`获取一些资源,而访问google.com会等待超时后失败(国内环境),但这些资源并不是完成翻译功能所必须的,因此并不影响翻译功能的使用。但未完成的非ajax请求会让`dom-ready`事件迟迟不能触发,导致依赖此事件注入JavaScript的代码直到请求超时后才执行。 对于Mini窗口自动获取输入焦点和自动清除输入,由于是通过`dom-ready`事件注入JavaScript实现,因此在`dom-ready`之后才能生效。而这需要大概十几秒,因为`translate.google.cn`尝试从`google.com`获取一些资源,而访问google.com会等待超时后失败(国内环境),但这些资源并不是完成翻译功能所必须的,因此并不影响翻译功能的使用。但未完成的非ajax请求会让`dom-ready`事件迟迟不能触发,导致依赖此事件注入JavaScript的代码直到请求超时后才执行。
但这只在程序启动时发生,个人认为勉强能够忍受,就没有再折腾。如果有必要,或许可以通过`ServiceWorker`拦截? 但这只在程序启动时发生,个人认为勉强能够忍受,就没有再折腾。如果有必要,或许可以通过`ServiceWorker`拦截?
@@ -54,9 +53,12 @@ Mini窗口的大小是固定的,想要修改只能修改代码。为了在更
`npm start` `npm start`
### 打包 ### 打包
`npm run package` #### Windows
`npm run package-win`
#### linux
`npm run package-linux`
通过`electron-packager`进行打包。当然您也可以使用其它Electron打包工具,例如[electron-builder](https://github.com/electron-userland/electron-builder)。但请劳烦您自行配置。 通过`electron-packager`进行打包。当然您也可以自定义打包器参数或使用其它Electron打包工具,例如[electron-builder](https://github.com/electron-userland/electron-builder)。但请劳烦您自行配置。
## 配置项 ## 配置项
@@ -76,6 +78,9 @@ Mini窗口的大小是固定的,想要修改只能修改代码。为了在更
| minimizeToTrayWhenClose | boolean | true | 关闭主窗口时最小化到托盘 | minimizeToTrayWhenClose | boolean | true | 关闭主窗口时最小化到托盘
| minimizeToTrayWenStart | boolean | true | 启动时自动隐藏主窗口 | minimizeToTrayWenStart | boolean | true | 启动时自动隐藏主窗口
| keepActiveInterval | number | 60000 | 自动操作Mini窗口以使其保持活跃的周期,单位ms,最低1000,为0禁止 | keepActiveInterval | number | 60000 | 自动操作Mini窗口以使其保持活跃的周期,单位ms,最低1000,为0禁止
| skipTrayIcon | boolean | false | 是否隐藏系统托盘图标
注意,在部分linux桌面上动态切换是否显示托盘图标可能会导致托盘图标残留,因此最好直接修改config.json再重启而不是动态修改是否显示托盘图标。
## 使用截图 ## 使用截图
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+997 -84
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -1,16 +1,17 @@
{ {
"name": "google-translate-electron", "name": "google-translate-electron",
"author": "mattuy<mattuylee@outlook.com>", "author": "mattuy<mattuylee@outlook.com>",
"version": "0.1.1", "version": "0.2.0",
"main": "./src/index.js", "main": "./src/index.js",
"description": "Google Translate for Windows Desktop, built with Electron", "description": "Google Translate for Windows Desktop, built with Electron",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "electron .", "start": "electron .",
"package": "electron-packager ./ --overwrite --win --out ./bin --icon ./appicon.ico --app-copyright \"copyright@mattuy<mattuylee@outlook.com>\" --win32metadata.CompanyName=\"\"" "package-win": "electron-packager ./ --overwrite --platform=win32 --arch=x64,ia32 --out ./bin --icon ./appicon.ico --app-copyright \"copyright@mattuy<mattuylee@outlook.com>\" --win32metadata.CompanyName=\"\"",
"package-linux": "electron-packager ./ --overwrite --platform=linux --arch=x64,ia32,arm64 --out ./bin --icon ./appicon.png"
}, },
"productName": "Google Translate Desktop", "productName": "google-translate-electron",
"originalFilename": "Google Translate Desktop", "originalFilename": "google-translate-electron",
"dependencies": { "dependencies": {
"lodash": "^4.17.15" "lodash": "^4.17.15"
}, },
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+4 -3
View File
@@ -1,6 +1,5 @@
const fs = require('fs'); const fs = require('fs');
const { promisify } = require('util'); const { promisify } = require('util');
const toast = require('./notify');
const { debounce } = require('lodash'); const { debounce } = require('lodash');
const config = { const config = {
@@ -14,7 +13,8 @@ const config = {
extraCssForMini: '', extraCssForMini: '',
minimizeToTrayWhenClose: true, minimizeToTrayWhenClose: true,
minimizeToTrayWenStart: true, minimizeToTrayWenStart: true,
keepActiveInterval: 60000 keepActiveInterval: 60000,
skipTrayIcon: false
}; };
try { try {
@@ -31,7 +31,8 @@ async function flushConfig() {
await flush('./config.json', JSON.stringify(config, null, 4)); await flush('./config.json', JSON.stringify(config, null, 4));
} }
catch { catch {
toast.show("保存配置失败。", { messageType: 'error' }); const { notify } = require('./tools');
notify("保存配置失败。", { messageType: 'error' });
} }
} }
+36 -87
View File
@@ -1,34 +1,53 @@
const path = require('path'); const { app, globalShortcut, BrowserWindow } = require('electron');
const { app, globalShortcut, BrowserWindow, Menu, Tray } = require('electron'); const { config } = require('./config');
const { createMiniWindow, toggleMiniWindowVisibility } = require('./mini'); const { createMainWindow } = require('./main');
const { createMainWindow, toggleMainWindowVisibility } = require('./main'); const { createMiniWindow } = require('./mini');
const getMenu = require('./menu'); const getMenu = require('./menu');
const { config, flushConfig } = require('./config'); const {
const toast = require('./notify'); initialSetup,
const main = require('./main'); notify,
dismissNotify,
//将tray保存到全局变量,防止其被自动回收 toggleMiniShortcut,
let tray; toggleTrayIcon,
} = require('./tools');
/** @type BrowserWindow */ /** @type BrowserWindow */
let mainWindow; let mainWindow;
/** @type BrowserWindow */ /** @type BrowserWindow */
let miniWindow; let miniWindow;
app.on('ready', () => { // 保证单一实例
setupTray(); const gotTheLock = app.requestSingleInstanceLock()
toggleAutoStart(true); if (!gotTheLock) {
app.exit();
return;
}
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (mainWindow) {
mainWindow.show();
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.focus();
}
})
app.on('ready', () => {
mainWindow = createMainWindow(); mainWindow = createMainWindow();
miniWindow = createMiniWindow();
initialSetup(mainWindow, miniWindow);
mainWindow.on('close', ev => { mainWindow.on('close', ev => {
if (config.minimizeToTrayWhenClose) { if (config.minimizeToTrayWhenClose) {
ev.preventDefault(); ev.preventDefault();
mainWindow.hide(); mainWindow.hide();
} }
else {
app.quit();
}
}); });
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
if (miniWindow) { if (miniWindow) {
toast.dismiss(); dismissNotify();
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
miniWindow.destroy(); miniWindow.destroy();
} }
@@ -51,18 +70,19 @@ app.on('ready', () => {
mainWindow.show(); mainWindow.show();
} }
else { else {
toast.show("Google Translate Desktop已在后台运行。", null, { notify("Google Translate Electron已在后台运行。", null, {
use: 'notification', use: 'notification',
clickCallback: () => mainWindow.show() clickCallback: () => mainWindow.show()
}); });
} }
toggleTrayIcon(config.skipTrayIcon);
registerMiniWindow(); registerMiniWindow();
const mainMenu = getMenu(mainWindow, miniWindow, mainWindow); const mainMenu = getMenu(mainWindow, miniWindow, mainWindow);
mainWindow.setMenu(mainMenu); mainWindow.setMenu(mainMenu);
}); });
function registerMiniWindow() { function registerMiniWindow() {
miniWindow = createMiniWindow();
miniWindow.setParentWindow(mainWindow); miniWindow.setParentWindow(mainWindow);
const miniMenu = getMenu(mainWindow, miniWindow, miniWindow); const miniMenu = getMenu(mainWindow, miniWindow, miniWindow);
miniWindow.setMenu(miniMenu); miniWindow.setMenu(miniMenu);
@@ -71,74 +91,3 @@ function registerMiniWindow() {
} }
} }
function setupTray() {
tray = new Tray(path.resolve(__dirname, 'assets/icon.ico'));
const contextMenu = Menu.buildFromTemplate([
{ label: "显示主界面", click: () => toggleMainWindowVisibility(mainWindow) },
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility(miniWindow, mainWindow) },
{
type: 'checkbox',
label: "开机启动",
checked: config.autoStart,
click: menu => menu.checked = toggleAutoStart()
},
{
type: 'checkbox',
label: "启用MINI窗口快捷键",
checked: config.miniWindowEnabled,
click: menu => menu.checked = toggleMiniShortcut()
},
{ label: "退出", click: () => app.exit() }
]);
tray.setToolTip("Google Translate Desktop");
tray.on('click', () => toggleMainWindowVisibility(mainWindow));
tray.setContextMenu(contextMenu);
}
function toggleAutoStart(initial) {
if (!initial) {
config.autoStart = !config.autoStart;
}
app.setLoginItemSettings({
openAtLogin: config.autoStart,
path: process.execPath,
openAsHidden: false,
args: [
'--autoStart'
]
});
if (config.autoStart) {
toast.show("已允许Google Translate Desktop在您登录时自动启动。", "提示");
}
else {
toast.show("已禁止Google Translate Desktop在您登录时自动启动。", "提示");
}
if (!initial) {
flushConfig();
}
return config.autoStart;
}
//启用/禁用mini窗口快捷键
function toggleMiniShortcut(initial) {
let enabled = initial ? config.miniWindowEnabled : !config.miniWindowEnabled;
let success = true;
if (enabled) {
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility(miniWindow, mainWindow))) {
toast.show("快捷键注册失败,请检查设置的快捷键是否被其他软件占用。", "错误", {
type: 'error'
});
success = false;
}
}
else {
globalShortcut.unregister(config.miniShortcut);
}
if (success) {
config.miniWindowEnabled = enabled;
if (!initial) {
flushConfig();
}
}
return enabled;
}
+1 -1
View File
@@ -6,7 +6,7 @@ function createMainWindow() {
const win = new BrowserWindow({ const win = new BrowserWindow({
width: config.mainWindowWidth, width: config.mainWindowWidth,
height: config.mainWindowHeight, height: config.mainWindowHeight,
icon: path.resolve(__dirname, './assets/icon.ico'), icon: path.resolve(__dirname, './assets/icon.png'),
title: 'Google Translate', title: 'Google Translate',
show: false, show: false,
webPreferences: { webPreferences: {
+8 -3
View File
@@ -1,4 +1,5 @@
const { Menu } = require('electron'); const { app, Menu } = require('electron');
const { toggleTrayIcon } = require('./tools');
function getMenu(/** @type Electron.BrowserWindow */mainWindow, miniWindow, current) { function getMenu(/** @type Electron.BrowserWindow */mainWindow, miniWindow, current) {
const template = [{ const template = [{
@@ -25,8 +26,12 @@ function getMenu(/** @type Electron.BrowserWindow */mainWindow, miniWindow, curr
click: () => current.webContents.openDevTools() click: () => current.webContents.openDevTools()
}, },
{ {
label: "退出", label: "显示托盘图标",
role: 'quit' click: () => toggleTrayIcon()
},
{
label: "退出程序",
click: () => app.exit(0)
} }
]) ])
}]; }];
+11 -33
View File
@@ -2,13 +2,6 @@ const path = require('path');
const { BrowserWindow } = require('electron'); const { BrowserWindow } = require('electron');
const { config } = require('./config'); const { config } = require('./config');
const miniControl = {
/** 迷你窗口是否处于可视区域 */
isMiniWindowVisible: true,
previousPos: null
}
function createMiniWindow () { function createMiniWindow () {
let miniWindow = new BrowserWindow({ let miniWindow = new BrowserWindow({
width: 500, width: 500,
@@ -17,7 +10,7 @@ function createMiniWindow () {
minHeight: 315, minHeight: 315,
maxWidth: 500, maxWidth: 500,
maxHeight: 315, maxHeight: 315,
icon: path.resolve(__dirname, './assets/icon.ico'), icon: path.resolve(__dirname, './assets/icon.png'),
webPreferences: { webPreferences: {
nodeIntegration: false nodeIntegration: false
}, },
@@ -29,7 +22,6 @@ function createMiniWindow () {
alwaysOnTop: true, alwaysOnTop: true,
fullscreenable: false, fullscreenable: false,
skipTaskbar: false, skipTaskbar: false,
}); });
miniWindow.on('focus', () => { miniWindow.on('focus', () => {
@@ -94,14 +86,11 @@ function createMiniWindow () {
miniWindow.insertCSS(config.extraCssForMini); miniWindow.insertCSS(config.extraCssForMini);
} }
}); });
miniControl.previousPos = miniWindow.getPosition();
toggleMiniWindowVisibility(miniWindow);
miniWindow.show();
let interval = config.keepActiveInterval | 0; let interval = config.keepActiveInterval | 0;
if (miniWindow && interval !== 0) { if (miniWindow && interval !== 0) {
interval = Math.max(1000, interval); interval = Math.max(1000, interval);
setInterval(() => { setInterval(() => {
if (!miniControl.isMiniWindowVisible) { if (!miniWindow.isVisible()) {
miniWindow.webContents.executeJavaScript(` miniWindow.webContents.executeJavaScript(`
var source = document.getElementById('source'); var source = document.getElementById('source');
if (source) { if (source) {
@@ -114,28 +103,17 @@ function createMiniWindow () {
return miniWindow; return miniWindow;
} }
function toggleMiniWindowVisibility(window, mainWindow) { function toggleMiniWindowVisibility(miniWindow, mainWindow) {
if (!window) { return; } if (!miniWindow) { return; }
miniControl.isMiniWindowVisible = !miniControl.isMiniWindowVisible; if (miniWindow.isVisible()) {
if (miniControl.isMiniWindowVisible) { miniWindow.hide();
if (miniControl.previousPos) { clearInput(miniWindow);
window.setPosition(...miniControl.previousPos);
}
else {
window.show();
}
if (!mainWindow || !mainWindow.isVisible()) {
window.setSkipTaskbar(false);
}
clearInput(window);
focusInput(window);
window.focus();
} }
else { else {
miniControl.previousPos = window.getPosition(); miniWindow.show();
window.setPosition(-1000, -1000, false); clearInput(miniWindow);
window.setSkipTaskbar(true); focusInput(miniWindow);
clearInput(window); miniWindow.focus();
} }
} }
-38
View File
@@ -1,38 +0,0 @@
const path = require('path');
const { Notification, dialog } = require('electron');
let current = null;
function notify(message, title, ops) {
const use = ops ? ops.use : 'auto';
if (use === 'dialog' || (!Notification.isSupported() && use !== 'notification')) {
let options = { message, title: title || "信息" };
if (ops && ops.messageType) {
options.type = ops.messageType;
}
dialog.showMessageBox(null, options);
return;
}
if (current) {
current.close();
}
current = new Notification({
title: "Google Translate Desktop",
body: message,
subtitle: ops && ops.subtitle,
icon: path.resolve(__dirname, './assets/icon.ico')
});
current.show();
if (ops && ops.clickCallback) {
current.on('click', ops.clickCallback);
}
}
function dismiss() {
current && current.close();
}
module.exports = {
show: notify,
dismiss
};
+151
View File
@@ -0,0 +1,151 @@
const { app, globalShortcut, ipcMain } = require('electron');
const path = require('path');
const { Menu, Tray, Notification, dialog } = require('electron');
const { config, flushConfig } = require('./config');
const { toggleMainWindowVisibility } = require('./main');
const { toggleMiniWindowVisibility } = require('./mini');
let mainWindow, miniWindow, tray, currentNotification;
function initialSetup(_mainWindow, _miniWindow) {
mainWindow = _mainWindow;
miniWindow = _miniWindow;
}
//启用/禁用mini窗口快捷键
function toggleMiniShortcut(initial) {
let enabled = initial ? config.miniWindowEnabled : !config.miniWindowEnabled;
let success = true;
if (enabled) {
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility(miniWindow, mainWindow))) {
notify("快捷键注册失败,请检查设置的快捷键是否被其他软件占用。", "错误", {
type: 'error'
});
success = false;
}
}
else {
globalShortcut.unregister(config.miniShortcut);
}
if (success) {
config.miniWindowEnabled = enabled;
if (!initial) {
flushConfig();
}
}
return enabled;
}
//切换是否自动启动
function toggleAutoStart(initial) {
if (!initial) {
config.autoStart = !config.autoStart;
}
app.setLoginItemSettings({
openAtLogin: config.autoStart,
path: process.execPath,
openAsHidden: false,
args: [
'--autoStart'
]
});
if (config.autoStart) {
notify("已允许Google Translate Electron在您登录时自动启动。", "提示");
}
else {
notify("已禁止Google Translate Electron在您登录时自动启动。", "提示");
}
if (!initial) {
flushConfig();
}
return config.autoStart;
}
function notify(message, title, ops) {
const use = ops ? ops.use : 'auto';
if (use === 'dialog' || (!Notification.isSupported() && use !== 'notification')) {
let options = { message, title: title || "信息" };
if (ops && ops.messageType) {
options.type = ops.messageType;
}
dialog.showMessageBox(null, options);
return;
}
if (currentNotification) {
currentNotification.close();
}
let extName = process.platform === 'win32' ? 'ico' : 'png';
currentNotification = new Notification({
title: "Google Translate Electron",
body: message,
subtitle: ops && ops.subtitle,
icon: path.resolve(__dirname, './assets/icon.' + extName)
});
currentNotification.show();
if (ops && ops.clickCallback) {
currentNotification.on('click', ops.clickCallback);
}
}
function dismissNotify() {
currentNotification && currentNotification.close();
}
function setupTrayIcon() {
if (tray) { return; }
let extName = process.platform === 'win32' ? 'ico' : 'png';
tray = new Tray(path.resolve(__dirname, 'assets/icon.' + extName));
const contextMenu = Menu.buildFromTemplate([
{ label: "显示主界面", click: () => toggleMainWindowVisibility(mainWindow) },
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility(miniWindow, mainWindow) },
{
type: 'checkbox',
label: "开机启动",
checked: config.autoStart,
click: menu => menu.checked = toggleAutoStart()
},
{
type: 'checkbox',
label: "启用MINI窗口快捷键",
checked: config.miniWindowEnabled,
click: menu => menu.checked = toggleMiniShortcut()
},
{ label: "退出", click: () => app.exit() }
]);
tray.setTitle("Google Translate Electron");
tray.setToolTip("Google Translate Electron");
tray.on('click', () => toggleMainWindowVisibility(mainWindow));
tray.setContextMenu(contextMenu);
}
function destroyTrayIcon() {
if (tray) {
tray.destroy();
}
tray = null;
}
function toggleTrayIcon(show) {
if (typeof show === 'boolean') {
config.skipTrayIcon = show;
}
else {
config.skipTrayIcon = !config.skipTrayIcon;
}
if (config.skipTrayIcon) {
destroyTrayIcon();
}
else {
setupTrayIcon();
}
flushConfig();
}
module.exports = {
initialSetup,
notify,
dismissNotify,
toggleTrayIcon,
toggleAutoStart,
toggleMiniShortcut,
}