From 09f920eade0f26daee6a62f6a8060ad23b96da0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20H=C3=A4usler?= Date: Sat, 2 Nov 2019 16:41:51 +0100 Subject: [PATCH] Upgrade to Electron 7 (#1543) * Upgrade to Electron 7 * Fix E2E test issue with Electron 7 * Fix native theme event * Remove runtime native theme detection * Update Electron to v7.0.1 * Fix keytar exception --- electron-builder.yml | 2 + package.json | 11 +- src/main/app/index.js | 57 ++++++---- src/main/config.js | 2 +- src/main/dataCenter/index.js | 4 +- src/main/filesystem/watcher.js | 2 +- src/main/index.js | 4 +- src/main/menu/actions/file.js | 41 ++----- src/main/menu/actions/window.js | 4 +- src/main/menu/index.js | 2 +- src/main/menu/templates/window.js | 6 - src/main/platform/win32/registry.js | 34 ------ src/main/preferences/index.js | 20 +--- src/main/preferences/schema.json | 9 ++ src/main/utils/imagePathAutoComplement.js | 5 +- src/main/windows/setting.js | 10 +- src/renderer/pages/app.vue | 1 + src/renderer/prefComponents/theme/config.js | 11 ++ src/renderer/prefComponents/theme/index.vue | 24 ++-- src/renderer/store/preferences.js | 1 + static/preference.json | 1 + yarn.lock | 115 ++++++++++++-------- 22 files changed, 180 insertions(+), 186 deletions(-) delete mode 100755 src/main/platform/win32/registry.js diff --git a/electron-builder.yml b/electron-builder.yml index 12b264c68..8d1a53bbb 100755 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -19,6 +19,7 @@ files: - "!node_modules/vega-lite/build/vega-lite*.js.map" # Don't bundle build files - "!node_modules/@felixrieseberg/spellchecker/bin" +- "!node_modules/@hfelix/spellchecker/bin" - "!node_modules/ced/bin" - "!node_modules/ced/vendor" - "!node_modules/cld/bin" @@ -34,6 +35,7 @@ files: - "!node_modules/ced/build/vendor" # Don't bundle LGPL source files - "!node_modules/@felixrieseberg/spellchecker/vendor" +- "!node_modules/@hfelix/spellchecker/vendor" extraFiles: - "LICENSE" - from: "resources/THIRD-PARTY-LICENSES.txt" diff --git a/package.json b/package.json index ceabf11dc..aad09e280 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "@hfelix/electron-localshortcut": "^3.1.1", - "@hfelix/electron-spellchecker": "^1.0.0-rc.1", + "@hfelix/electron-spellchecker": "^1.0.0-rc.3", "@octokit/rest": "^16.33.1", "arg": "^4.1.1", "axios": "^0.19.0", @@ -65,7 +65,7 @@ "joplin-turndown-plugin-gfm": "^1.0.11", "katex": "^0.11.1", "keyboard-layout": "^2.0.16", - "keytar": "^5.0.0-beta.3", + "keytar": "5.0.0-beta.4", "mermaid": "^8.4.0", "plist": "^3.0.1", "popper.js": "^1.16.0", @@ -113,7 +113,7 @@ "del": "^5.1.0", "devtron": "^1.4.0", "dotenv": "^8.2.0", - "electron": "^6.1.0", + "electron": "^7.0.1", "electron-builder": "^21.2.0", "electron-devtools-installer": "^2.2.4", "electron-rebuild": "^1.8.6", @@ -153,7 +153,7 @@ "postcss-preset-env": "^6.6.0", "raw-loader": "^3.1.0", "require-dir": "^1.2.0", - "spectron": "^8.0.0", + "spectron": "^9.0.0", "style-loader": "^1.0.0", "svg-sprite-loader": "^4.1.6", "svgo": "^1.3.0", @@ -171,9 +171,6 @@ "webpack-hot-middleware": "^2.25.0", "webpack-merge": "^4.2.1" }, - "optionalDependencies": { - "vscode-windows-registry": "^1.0.2" - }, "repository": { "type": "git", "url": "git@github.com:marktext/marktext.git" diff --git a/src/main/app/index.js b/src/main/app/index.js index 2954984d6..981faa952 100644 --- a/src/main/app/index.js +++ b/src/main/app/index.js @@ -3,7 +3,7 @@ import fse from 'fs-extra' import { exec } from 'child_process' import dayjs from 'dayjs' import log from 'electron-log' -import { app, BrowserWindow, clipboard, dialog, ipcMain, systemPreferences } from 'electron' +import { app, BrowserWindow, clipboard, dialog, ipcMain, nativeTheme } from 'electron' import { isChildOfDirectory } from 'common/filesystem/paths' import { isLinux, isOsx, isWindows } from '../config' import parseArgs from '../cli/parser' @@ -115,7 +115,7 @@ class App { const { paths } = this._accessor ensureDefaultDict(paths.userDataPath) .catch(error => { - log.error(error) + log.error('Error copying Hunspell dictionary: ', error) }) } @@ -143,7 +143,13 @@ class App { } } - const { startUpAction, defaultDirectoryToOpen } = preferences.getAll() + const { + startUpAction, + defaultDirectoryToOpen, + autoSwitchTheme, + theme + } = preferences.getAll() + if (startUpAction === 'folder' && defaultDirectoryToOpen) { const info = normalizeMarkdownPath(defaultDirectoryToOpen) if (info) { @@ -151,29 +157,32 @@ class App { } } - if (isOsx) { - app.dock.setMenu(dockMenu) + // Set initial native theme for theme in preferences. + const isDarkTheme = /dark/i.test(theme) + if (autoSwitchTheme === 0 && isDarkTheme !== nativeTheme.shouldUseDarkColors) { + selectTheme(nativeTheme.shouldUseDarkColors ? 'dark' : 'light') + nativeTheme.themeSource = nativeTheme.shouldUseDarkColors ? 'dark' : 'light' + } else { + nativeTheme.themeSource = isDarkTheme ? 'dark' : 'light' + } - // Listen for system theme change and change Mark Text own `dark` and `light`. - // In macOS 10.14 Mojave, Apple introduced a new system-wide dark mode for - // all macOS computers. - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - () => { - const preferences = this._accessor.preferences - const { theme } = preferences.getAll() - - // Application menu is automatically updated via preference manager. - if (systemPreferences.isDarkMode() && theme !== 'dark' && - theme !== 'material-dark' && theme !== 'one-dark') { - selectTheme('dark') - } - if (!systemPreferences.isDarkMode() && theme !== 'light' && - theme !== 'ulysses' && theme !== 'graphite') { - selectTheme('light') - } + let isDarkMode = nativeTheme.shouldUseDarkColors + ipcMain.on('broadcast-preferences-changed', change => { + // Set Chromium's color for native elements after theme change. + if (change.theme) { + const isDarkTheme = /dark/i.test(change.theme) + if (isDarkMode !== isDarkTheme) { + isDarkMode = isDarkTheme + nativeTheme.themeSource = isDarkTheme ? 'dark' : 'light' + } else if (nativeTheme.themeSource === 'system') { + // Need to set dark or light theme because we set `system` to get the current system theme. + nativeTheme.themeSource = isDarkMode ? 'dark' : 'light' } - ) + } + }) + + if (isOsx) { + app.dock.setMenu(dockMenu) } else if (isWindows) { app.setJumpList([{ type: 'recent' diff --git a/src/main/config.js b/src/main/config.js index f1f28771d..90523f28f 100644 --- a/src/main/config.js +++ b/src/main/config.js @@ -16,7 +16,7 @@ export const editorWinOptions = { zoomFactor: 1.0 } -export const defaultPreferenceWinOptions = { +export const preferencesWinOptions = { width: 950, height: 650, webPreferences: { diff --git a/src/main/dataCenter/index.js b/src/main/dataCenter/index.js index 9742fe389..4b59b7d93 100644 --- a/src/main/dataCenter/index.js +++ b/src/main/dataCenter/index.js @@ -71,7 +71,7 @@ class DataCenter extends EventEmitter { return Object.assign(data, encryptObj) } catch (err) { - log.error(err) + log.error('Failed to decrypt secure keys:', err) return data } } @@ -133,7 +133,7 @@ class DataCenter extends EventEmitter { try { return await keytar.setPassword(serviceName, key, value) } catch (err) { - log.error(err) + log.error('dataCenter::setItem:', err) } } else { return this.store.set(key, value) diff --git a/src/main/filesystem/watcher.js b/src/main/filesystem/watcher.js index fa5a36965..5d242a500 100644 --- a/src/main/filesystem/watcher.js +++ b/src/main/filesystem/watcher.js @@ -235,7 +235,7 @@ class Watcher { }) } } else { - log.error(error) + log.error('Error while watching files:', error) } }) diff --git a/src/main/index.js b/src/main/index.js index 19a588dd2..60d394443 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -60,9 +60,7 @@ try { // Catch errors that may come from invalid configuration files like settings. const msgHint = err.message.includes('Config schema violation') ? 'This seems to be an issue with your configuration file(s). ' : '' - - log.error(`Loading Mark Text failed during initialization! ${msgHint}`) - log.error(err) + log.error(`Loading Mark Text failed during initialization! ${msgHint}`, err) const EXIT_ON_ERROR = !!process.env.MARKTEXT_EXIT_ON_ERROR const SHOW_ERROR_DIALOG = !process.env.MARKTEXT_ERROR_INTERACTION diff --git a/src/main/menu/actions/file.js b/src/main/menu/actions/file.js index 4eba8183b..1dccfa3dc 100644 --- a/src/main/menu/actions/file.js +++ b/src/main/menu/actions/file.js @@ -62,7 +62,7 @@ const handleResponseForExport = async (e, { type, content, pathname, title, page } win.webContents.send('AGANI::export-success', { type, filePath }) } catch (err) { - log.error(err) + log.error('Error while exporting:', err) const ERROR_MSG = err.message || `Error happened when export ${filePath}` win.webContents.send('AGANI::show-notification', { title: 'Export failure', @@ -80,19 +80,9 @@ const handleResponseForExport = async (e, { type, content, pathname, title, page const handleResponseForPrint = e => { const win = BrowserWindow.fromWebContents(e.sender) - - // See GH#749, Electron#16085 and Electron#17523. - dialog.showMessageBox(win, { - type: 'info', - buttons: ['OK'], - defaultId: 0, - noLink: true, - message: 'Printing doesn\'t work', - detail: 'Printing is disabled due to an Electron upstream issue. Please export the document as PDF and print the PDF file. We apologize for the inconvenience!' + win.webContents.print({ printBackground: true }, () => { + removePrintServiceFromWindow(win) }) - // win.webContents.print({ printBackground: true }, () => { - // removePrintServiceFromWindow(win) - // }) } const handleResponseForSave = async (e, { id, filename, markdown, pathname, options, defaultPath }) => { @@ -140,7 +130,7 @@ const handleResponseForSave = async (e, { id, filename, markdown, pathname, opti return id }) .catch(err => { - log.error(err) + log.error('Error while saving:', err) win.webContents.send('mt::tab-save-failure', id, err.message) }) } @@ -185,7 +175,7 @@ const openPandocFile = async (windowId, pathname) => { const data = await converter() ipcMain.emit('app-open-markdown-by-id', windowId, data) } catch (err) { - log.error(err) + log.error('Error while converting file:', err) } } @@ -216,7 +206,7 @@ ipcMain.on('mt::save-and-close-tabs', async (e, unsavedFiles) => { win.send('mt::force-close-tabs-by-id', tabIds) }) .catch(err => { - log.error(err.error) + log.error('Error while save all:', err.error) }) } else { const tabIds = unsavedFiles.map(f => f.id) @@ -262,7 +252,7 @@ ipcMain.on('AGANI::response-file-save-as', async (e, { id, filename, markdown, p } }) .catch(err => { - log.error(err) + log.error('Error while save as:', err) win.webContents.send('mt::tab-save-failure', id, err.message) }) } @@ -282,8 +272,7 @@ ipcMain.on('mt::close-window-confirm', async (e, unsavedFiles) => { ipcMain.emit('window-close-by-id', win.id) }) .catch(err => { - console.log(err) - log.error(err) + log.error('Error while saving before quit:', err) // Notify user about the problem. dialog.showMessageBox(win, { @@ -446,19 +435,9 @@ export const importFile = async win => { } export const print = win => { - if (!win) { - return + if (win) { + win.webContents.send('mt::show-export-dialog', 'print') } - // See GH#749, Electron#16085 and Electron#17523. - dialog.showMessageBox(win, { - type: 'info', - buttons: ['OK'], - defaultId: 0, - noLink: true, - message: 'Printing doesn\'t work', - detail: 'Printing is disabled due to an Electron upstream issue. Please export the document as PDF and print the PDF file. We apologize for the inconvenience!' - }) - // win.webContents.send('mt::show-export-dialog', 'print') } export const openFile = async win => { diff --git a/src/main/menu/actions/window.js b/src/main/menu/actions/window.js index 4874f605a..01a0a0773 100644 --- a/src/main/menu/actions/window.js +++ b/src/main/menu/actions/window.js @@ -9,13 +9,13 @@ export const toggleAlwaysOnTop = win => { export const zoomIn = win => { const { webContents } = win const zoom = webContents.getZoomFactor() - // WORKAROUND: Electron#16018 + // WORKAROUND: We need to set zoom on the browser window due to Electron#16018. webContents.send('mt::window-zoom', Math.min(2.0, zoom + 0.125)) } export const zoomOut = win => { const { webContents } = win const zoom = webContents.getZoomFactor() - // WORKAROUND: Electron#16018 + // WORKAROUND: We need to set zoom on the browser window due to Electron#16018. webContents.send('mt::window-zoom', Math.max(1.0, zoom - 0.125)) } diff --git a/src/main/menu/index.js b/src/main/menu/index.js index 0ac669fa6..6f811c530 100644 --- a/src/main/menu/index.js +++ b/src/main/menu/index.js @@ -92,7 +92,7 @@ class AppMenu { } return recentDocuments } catch (err) { - log.error(err) + log.error('Error while read recently used documents:', err) return [] } } diff --git a/src/main/menu/templates/window.js b/src/main/menu/templates/window.js index cc0b5e387..4e69c5665 100755 --- a/src/main/menu/templates/window.js +++ b/src/main/menu/templates/window.js @@ -17,19 +17,13 @@ export default function (keybindings) { toggleAlwaysOnTop(browserWindow) } }, { - // TODO: Disable due GH#1225. - visible: false, type: 'separator' }, { - // TODO: Disable due GH#1225. - visible: false, label: 'Zoom In', click (menuItem, browserWindow) { zoomIn(browserWindow) } }, { - // TODO: Disable due GH#1225. - visible: false, label: 'Zoom Out', click (menuItem, browserWindow) { zoomOut(browserWindow) diff --git a/src/main/platform/win32/registry.js b/src/main/platform/win32/registry.js deleted file mode 100755 index d027d7514..000000000 --- a/src/main/platform/win32/registry.js +++ /dev/null @@ -1,34 +0,0 @@ -import { isWindows } from '../../config' - -let GetStringRegKey = null -if (isWindows) { - try { - GetStringRegKey = require('vscode-windows-registry').GetStringRegKey - } catch (e) { - // Ignore webpack build error on macOS and Linux. - } -} - -export const winHKEY = { - HKCU: 'HKEY_CURRENT_USER', - HKLM: 'HKEY_LOCAL_MACHINE', - HKCR: 'HKEY_CLASSES_ROOT', - HKU: 'HKEY_USERS', - HKCC: 'HKEY_CURRENT_CONFIG' -} - -/** - * Returns the registry key value. - * - * @param {winHKEY} hive The registry key - * @param {string} path The registry subkey - * @param {string} name The registry name - * @returns {string|null|undefined} The registry key value or null/undefined. - */ -export const getStringRegKey = (hive, path, name) => { - try { - return GetStringRegKey(hive, path, name) - } catch (e) { - return null - } -} diff --git a/src/main/preferences/index.js b/src/main/preferences/index.js index 48e4fa81f..69f696878 100644 --- a/src/main/preferences/index.js +++ b/src/main/preferences/index.js @@ -3,24 +3,12 @@ import fs from 'fs' import path from 'path' import EventEmitter from 'events' import Store from 'electron-store' -import { BrowserWindow, ipcMain, systemPreferences } from 'electron' +import { BrowserWindow, ipcMain, nativeTheme } from 'electron' import log from 'electron-log' -import { isOsx, isWindows } from '../config' +import { isWindows } from '../config' import { hasSameKeys } from '../utils' -import { getStringRegKey, winHKEY } from '../platform/win32/registry.js' import schema from './schema' -const isDarkSystemMode = () => { - if (isOsx) { - return systemPreferences.isDarkMode() - } else if (isWindows) { - // NOTE: This key is a 32-Bit DWORD but converted to JS string! - const buf = getStringRegKey(winHKEY.HKCU, 'Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize', 'AppsUseLightTheme') - return buf === '' // zero (0) - } - return false -} - const PREFERENCES_FILE_NAME = 'preferences' class Preference extends EventEmitter { @@ -50,7 +38,9 @@ class Preference extends EventEmitter { let defaultSettings = null try { defaultSettings = fse.readJsonSync(this.staticPath) - if (isDarkSystemMode()) { + + // Set best theme on first application start. + if (nativeTheme.shouldUseDarkColors) { defaultSettings.theme = 'dark' } } catch (err) { diff --git a/src/main/preferences/schema.json b/src/main/preferences/schema.json index 110916fa6..f14c99764 100644 --- a/src/main/preferences/schema.json +++ b/src/main/preferences/schema.json @@ -247,6 +247,15 @@ "description": "Theme--Select the theme used in Mark Text", "type": "string" }, + "autoSwitchTheme": { + "description": "Theme--Automatically adjust application theme according system.", + "default": 2, + "enum": [ + 0, + 1, + 2 + ] + }, "spellcheckerEnabled": { "description": "Spelling--Whether spell checking is enabled.", diff --git a/src/main/utils/imagePathAutoComplement.js b/src/main/utils/imagePathAutoComplement.js index e9a3d69b2..235c86c6a 100644 --- a/src/main/utils/imagePathAutoComplement.js +++ b/src/main/utils/imagePathAutoComplement.js @@ -46,8 +46,9 @@ const filesHandler = (files, directory, key) => { const rebuild = (directory) => { fs.readdir(directory, (err, files) => { - if (err) log.error(err) - else { + if (err) { + log.error('imagePathAutoComplement::rebuild:', err) + } else { filesHandler(files, directory) } }) diff --git a/src/main/windows/setting.js b/src/main/windows/setting.js index f0a27751f..b9eb55544 100644 --- a/src/main/windows/setting.js +++ b/src/main/windows/setting.js @@ -3,7 +3,7 @@ import { BrowserWindow, ipcMain } from 'electron' import electronLocalshortcut from '@hfelix/electron-localshortcut' import BaseWindow, { WindowLifecycle, WindowType } from './base' import { centerWindowOptions } from './utils' -import { TITLE_BAR_HEIGHT, defaultPreferenceWinOptions, isLinux, isOsx } from '../config' +import { TITLE_BAR_HEIGHT, preferencesWinOptions, isLinux, isOsx, isWindows } from '../config' class SettingWindow extends BaseWindow { /** @@ -21,12 +21,18 @@ class SettingWindow extends BaseWindow { */ createWindow (options = {}) { const { menu: appMenu, env, keybindings, preferences } = this._accessor - const winOptions = Object.assign({}, defaultPreferenceWinOptions, options) + const winOptions = Object.assign({}, preferencesWinOptions, options) centerWindowOptions(winOptions) if (isLinux) { winOptions.icon = path.join(__static, 'logo-96px.png') } + // WORKAROUND: Electron has issues with different DPI per monitor when + // setting a fixed window size. + if (isWindows) { + winOptions.resizable = true + } + // Enable native or custom/frameless window and titlebar const { titleBarStyle, theme } = preferences.getAll() if (!isOsx) { diff --git a/src/renderer/pages/app.vue b/src/renderer/pages/app.vue index 50dad342a..2b42f9ed6 100644 --- a/src/renderer/pages/app.vue +++ b/src/renderer/pages/app.vue @@ -146,6 +146,7 @@ export default { dispatch('LINTEN_FOR_PRINT_SERVICE_CLEARUP') dispatch('LINTEN_FOR_EXPORT_SUCCESS') dispatch('LISTEN_FOR_FILE_CHANGE') + dispatch('LISTEN_WINDOW_ZOOM') // module: notification dispatch('LISTEN_FOR_NOTIFICATION') diff --git a/src/renderer/prefComponents/theme/config.js b/src/renderer/prefComponents/theme/config.js index f124330ae..20bb426b6 100644 --- a/src/renderer/prefComponents/theme/config.js +++ b/src/renderer/prefComponents/theme/config.js @@ -18,3 +18,14 @@ export const themes = [ name: 'one-dark' } ] + +export const autoSwitchThemeOptions = [{ + label: 'Adjust theme at startup', // Always + value: 0 +}, /* { + label: 'Only at runtime', + value: 1 +}, */ { + label: 'Never', + value: 2 +}] diff --git a/src/renderer/prefComponents/theme/index.vue b/src/renderer/prefComponents/theme/index.vue index 8bb3372a0..a7685d33c 100644 --- a/src/renderer/prefComponents/theme/index.vue +++ b/src/renderer/prefComponents/theme/index.vue @@ -4,12 +4,19 @@
+ +
Open the themes folder @@ -27,21 +34,25 @@