From 0187217c86be535bd3960c4b6b04f46c5e7769f4 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Tue, 29 Dec 2015 02:18:37 +0900 Subject: [PATCH] renewal key binding --- browser/components/CodeEditor.js | 13 ++- browser/lib/modal.js | 12 ++- browser/main/HomePage/ArticleDetail/index.js | 36 ++++++- browser/main/HomePage/ArticleList.js | 53 ++++++++++- browser/main/HomePage/ArticleNavigator.js | 30 +++++- browser/main/HomePage/ArticleTopBar.js | 20 +++- browser/main/HomePage/index.js | 40 ++++---- browser/styles/main/ArticleDetail.styl | 17 +++- browser/styles/main/ArticleList.styl | 2 + browser/styles/main/ArticleTopBar.styl | 15 ++- .../styles/main/modal/DeleteArticleModal.styl | 6 +- browser/styles/vars.styl | 2 + lib/finder-app.js | 2 +- lib/finder-menu.js | 94 +++++++++++++++++++ lib/main-app.js | 2 +- lib/{menu-template.js => main-menu.js} | 82 ++++++++++++++-- lib/main-window.js | 10 ++ 17 files changed, 388 insertions(+), 48 deletions(-) create mode 100644 lib/finder-menu.js rename lib/{menu-template.js => main-menu.js} (64%) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 9dd4a452..d4fec528 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -2,7 +2,9 @@ import React from 'react' import ReactDOM from 'react-dom' import modes from '../lib/modes' import _ from 'lodash' -var ace = window.ace + +const remote = require('electron').remote +const ace = window.ace module.exports = React.createClass({ propTypes: { @@ -40,6 +42,15 @@ module.exports = React.createClass({ }, readOnly: true }) + editor.commands.addCommand({ + name: 'Focus title', + bindKey: {win: 'Esc', mac: 'Esc'}, + exec: function (editor, e) { + console.log(e) + remote.getCurrentWebContents().send('detail-edit') + }, + readOnly: true + }) editor.setReadOnly(!!this.props.readOnly) diff --git a/browser/lib/modal.js b/browser/lib/modal.js index 105888af..7e330178 100644 --- a/browser/lib/modal.js +++ b/browser/lib/modal.js @@ -1,6 +1,8 @@ import React from 'react' import ReactDOM from 'react-dom' +const remote = require('electron').remote + class ModalBase extends React.Component { constructor (props) { super(props) @@ -13,6 +15,8 @@ class ModalBase extends React.Component { close () { if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true}) + + remote.getCurrentWebContents().send('list-focus') } render () { @@ -38,9 +42,15 @@ export function openModal (component, props) { export function closeModal () { if (modalBase == null) { return } - modalBase.setState({component: null, componentProps: null, isHidden: true}) + modalBase.close() } export function isModalOpen () { return !modalBase.state.isHidden } + +export default { + open: openModal, + close: closeModal, + isOpen: isModalOpen +} diff --git a/browser/main/HomePage/ArticleDetail/index.js b/browser/main/HomePage/ArticleDetail/index.js index e1816e0b..e6106f31 100644 --- a/browser/main/HomePage/ArticleDetail/index.js +++ b/browser/main/HomePage/ArticleDetail/index.js @@ -14,11 +14,12 @@ import TagSelect from 'browser/components/TagSelect' import ModeSelect from 'browser/components/ModeSelect' import activityRecord from 'browser/lib/activityRecord' import ShareButton from './ShareButton' -import { openModal } from 'browser/lib/modal' +import { openModal, isModalOpen } from 'browser/lib/modal' import DeleteArticleModal from '../../modal/DeleteArticleModal' const electron = require('electron') const clipboard = electron.clipboard +const ipc = electron.ipcRenderer const OSX = process.platform === 'darwin' const BRAND_COLOR = '#18AF90' @@ -88,6 +89,23 @@ export default class ArticleDetail extends React.Component { constructor (props) { super(props) + this.saveHandler = e => { + if (isModalOpen()) return true + this.handleSaveButtonClick() + } + this.deleteHandler = e => { + if (isModalOpen()) return true + this.handleDeleteButtonClick() + } + this.togglePreviewHandler = e => { + if (isModalOpen()) return true + this.handleTogglePreviewButtonClick() + } + this.editHandler = e => { + if (isModalOpen()) return true + this.editArticle() + } + this.state = { article: Object.assign({content: ''}, props.activeArticle), previewMode: false, @@ -102,10 +120,20 @@ export default class ArticleDetail extends React.Component { this.shareDropdownInterceptor = e => { e.stopPropagation() } + + ipc.on('detail-save', this.saveHandler) + ipc.on('detail-delete', this.deleteHandler) + ipc.on('detail-toggle-preview', this.togglePreviewHandler) + ipc.on('detail-edit', this.editHandler) } componentWillUnmount () { clearInterval(this.refreshTimer) + + ipc.removeListener('detail-save', this.saveHandler) + ipc.removeListener('detail-delete', this.deleteHandler) + ipc.removeListener('detail-toggle-preview', this.togglePreviewHandler) + ipc.removeListener('detail-on', this.editHandler) } componentWillReceiveProps (nextProps) { @@ -127,6 +155,12 @@ export default class ArticleDetail extends React.Component { } } + editArticle () { + ReactDOM.findDOMNode(this.refs.title).focus() + ReactDOM.findDOMNode(this.refs.title).select() + this.setState({previewMode: false}) + } + cacheArticle () { let { dispatch } = this.props diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js index 1b8d9cbe..b988d419 100644 --- a/browser/main/HomePage/ArticleList.js +++ b/browser/main/HomePage/ArticleList.js @@ -7,13 +7,25 @@ import FolderMark from 'browser/components/FolderMark' import TagLink from './TagLink' import _ from 'lodash' +const electron = require('electron') +const remote = electron.remote +const ipc = electron.ipcRenderer + export default class ArticleList extends React.Component { + constructor (props) { + super(props) + + this.focusHandler = e => this.focus() + } + componentDidMount () { this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) + ipc.on('list-focus', this.focusHandler) } componentWillUnmount () { clearInterval(this.refreshTimer) + ipc.removeListener('list-focus', this.focusHandler) } componentDidUpdate () { @@ -36,6 +48,10 @@ export default class ArticleList extends React.Component { } } + focus () { + ReactDOM.findDOMNode(this).focus() + } + // 移動ができなかったらfalseを返す: selectPriorArticle () { let { articles, activeArticle, dispatch } = this.props @@ -68,6 +84,41 @@ export default class ArticleList extends React.Component { } } + handleArticleListKeyDown (e) { + console.log(e.keyCode) + if (e.metaKey || e.ctrlKey) return true + + if (e.keyCode === 65) { + e.preventDefault() + remote.getCurrentWebContents().send('nav-new-post') + } + + if (e.keyCode === 68) { + e.preventDefault() + remote.getCurrentWebContents().send('detail-delete') + } + + if (e.keyCode === 69) { + e.preventDefault() + remote.getCurrentWebContents().send('detail-edit') + } + + if (e.keyCode === 83) { + e.preventDefault() + remote.getCurrentWebContents().send('detail-save') + } + + if (e.keyCode === 38) { + e.preventDefault() + this.selectPriorArticle() + } + + if (e.keyCode === 40) { + e.preventDefault() + this.selectNextArticle() + } + } + render () { let { articles, modified, activeArticle, folders } = this.props @@ -130,7 +181,7 @@ export default class ArticleList extends React.Component { }) return ( -
+
this.handleArticleListKeyDown(e)} className='ArticleList'> {articleElements}
) diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index 1e77d7cb..cde86851 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -1,12 +1,14 @@ import React, { PropTypes } from 'react' import { findWhere } from 'lodash' import { setSearchFilter, switchFolder, saveArticle } from '../actions' -import { openModal } from 'browser/lib/modal' +import { openModal, isModalOpen } from 'browser/lib/modal' import FolderMark from 'browser/components/FolderMark' import Preferences from '../modal/Preferences' import CreateNewFolder from '../modal/CreateNewFolder' import keygen from 'browser/lib/keygen' +const ipc = require('electron').ipcRenderer + const BRAND_COLOR = '#18AF90' const preferenceTutorialElement = ( @@ -58,6 +60,28 @@ c-3.4-6.1-8.2-11.3-13.8-15.4C50.2,11.6,31,10.9,15.3,19C13.6,19.8,15.1,22.4,16.8, ) export default class ArticleNavigator extends React.Component { + constructor (props) { + super(props) + this.newPostHandler = e => { + if (isModalOpen()) return true + this.handleNewPostButtonClick(e) + } + this.newFolderHandler = e => { + if (isModalOpen()) return true + this.handleNewFolderButton(e) + } + } + + componentDidMount () { + ipc.on('nav-new-post', this.newPostHandler) + ipc.on('nav-new-folder', this.newFolderHandler) + } + + componentWillUnmount () { + ipc.removeListener('nav-new-post', this.newPostHandler) + ipc.removeListener('nav-new-folder', this.newFolderHandler) + } + handlePreferencesButtonClick (e) { openModal(Preferences) } @@ -136,7 +160,7 @@ export default class ArticleNavigator extends React.Component {
{status.isTutorialOpen ? newPostTutorialElement : null} @@ -148,7 +172,7 @@ export default class ArticleNavigator extends React.Component {
Folders
{status.isTutorialOpen ? newFolderTutorialElement : null} diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index f0f2fb21..0b235123 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -3,11 +3,13 @@ import ReactDOM from 'react-dom' import ExternalLink from 'browser/components/ExternalLink' import { setSearchFilter, clearSearch, toggleOnlyUnsavedFilter, toggleTutorial, saveAllArticles, switchArticle } from '../actions' import store from '../store' +import { isModalOpen } from 'browser/lib/modal' const electron = require('electron') const remote = electron.remote const Menu = remote.Menu const MenuItem = remote.MenuItem +const ipc = electron.ipcRenderer const OSX = process.platform === 'darwin' @@ -64,6 +66,15 @@ export default class ArticleTopBar extends React.Component { constructor (props) { super(props) + this.saveAllHandler = e => { + if (isModalOpen()) return true + this.handleSaveAllButtonClick(e) + } + this.focusSearchHandler = e => { + if (isModalOpen()) return true + this.focusInput(e) + } + this.state = { isTooltipHidden: true, isLinksDropdownOpen: false @@ -87,11 +98,17 @@ export default class ArticleTopBar extends React.Component { } } document.addEventListener('click', this.hideLinksDropdown) + + ipc.on('top-save-all', this.saveAllHandler) + ipc.on('top-focus-search', this.focusSearchHandler) } componentWillUnmount () { document.removeEventListener('click', this.hideLinksDropdown) this.linksButton.removeEventListener('click', this.showLinksDropdown()) + + ipc.removeListener('top-save-all', this.saveAllHandler) + ipc.removeListener('top-focus-search', this.focusSearchHandler) } handleTooltipRequest (e) { @@ -112,10 +129,10 @@ export default class ArticleTopBar extends React.Component { dispatch(clearSearch()) return } - this.blurInput() } focusInput () { + console.log('focinp') this.searchInput.focus() } @@ -145,6 +162,7 @@ export default class ArticleTopBar extends React.Component { let { dispatch } = this.props dispatch(saveAllArticles()) + remote.getCurrentWebContents().send('list-focus') } handleSaveMenuButtonClick (e) { diff --git a/browser/main/HomePage/index.js b/browser/main/HomePage/index.js index f747c5ca..f2f5de5d 100644 --- a/browser/main/HomePage/index.js +++ b/browser/main/HomePage/index.js @@ -1,5 +1,6 @@ import React, { PropTypes} from 'react' import { connect } from 'react-redux' +import ReactDOM from 'react-dom' import { toggleTutorial } from '../actions' import ArticleNavigator from './ArticleNavigator' import ArticleTopBar from './ArticleTopBar' @@ -26,15 +27,14 @@ class HomePage extends React.Component { } handleKeyDown (e) { - let cmdOrCtrl = process.platform === 'darwin' ? e.metaKey : e.ctrlKey - if (isModalOpen()) { if (e.keyCode === 27) closeModal() return } let { status, dispatch } = this.props - let { nav, top, list, detail } = this.refs + let { top, list } = this.refs + let listElement = ReactDOM.findDOMNode(list) if (status.isTutorialOpen) { dispatch(toggleTutorial()) @@ -42,28 +42,24 @@ class HomePage extends React.Component { return } - // Search inputがfocusされていたら大体のキー入力は無視される。 - if (top.isInputFocused() && !(e.metaKey || e.ctrlKey)) { - if (e.keyCode === 13 || e.keyCode === 27) top.escape() + if (e.keyCode === 13 && top.isInputFocused()) { + listElement.focus() + return + } + if (e.keyCode === 27 && top.isInputFocused()) { + if (status.search.length > 0) top.escape() + else listElement.focus() return } - // `detail`の`openDeleteConfirmMenu`が`true`なら呼ばれない。 - if (e.keyCode === 27 || (e.keyCode === 70 && cmdOrCtrl)) { - top.focusInput() - } - - if (e.keyCode === 38) { - list.selectPriorArticle() - } - - if (e.keyCode === 40) { - list.selectNextArticle() - } - - if (e.keyCode === 78 && cmdOrCtrl) { - nav.handleNewPostButtonClick() - e.preventDefault() + // Search inputがfocusされていたら大体のキー入力は無視される。 + if (e.keyCode === 27) { + if (document.activeElement !== listElement) { + listElement.focus() + } else { + top.focusInput() + } + return } } diff --git a/browser/styles/main/ArticleDetail.styl b/browser/styles/main/ArticleDetail.styl index ffe48f21..7c8626eb 100644 --- a/browser/styles/main/ArticleDetail.styl +++ b/browser/styles/main/ArticleDetail.styl @@ -1,5 +1,4 @@ noTagsColor = #999 -iptFocusBorderColor = #369DCD infoButton() display inline-block @@ -7,14 +6,17 @@ infoButton() cursor pointer height 33px width 33px - border none margin-right 5px font-size 18px color inactiveTextColor background-color white padding 0 + border 1px solid white + &:focus + border-color focusBorderColor &:hover color inherit + .ArticleDetail absolute right bottom top 60px @@ -55,7 +57,7 @@ infoButton() &:hover background-color white &:focus - border-color iptFocusBorderColor + border-color focusBorderColor &>.tutorial position fixed z-index 35 @@ -124,11 +126,15 @@ infoButton() border-top solid 1px darken(brandColor, 5%) border-bottom solid 1px darken(brandColor, 5%) border-left solid 1px darken(brandColor, 5%) + border-right solid 1px transparent border-radius left 2px background-color brandColor &:hover background-color lighten(brandColor, 10%) border-color lighten(brandColor, 10%) + &:focus + background-color lighten(brandColor, 10%) + border-color focusBorderColor .TagSelect-tags-item-label background-color brandColor float left @@ -142,12 +148,13 @@ infoButton() input.TagSelect-input background-color transparent border none + border-bottom 1px solid transparent outline none margin 0 2px transition 0.15s height 18px &:focus - font-size 13px + border-color focusBorderColor .TagSelect-suggest position fixed width 150px @@ -208,7 +215,7 @@ infoButton() padding 0 5px line-height 33px &.edit - border-color iptFocusBorderColor + border-color focusBorderColor input width 120px line-height 31px diff --git a/browser/styles/main/ArticleList.styl b/browser/styles/main/ArticleList.styl index 820b5334..ee0da8c1 100644 --- a/browser/styles/main/ArticleList.styl +++ b/browser/styles/main/ArticleList.styl @@ -8,6 +8,8 @@ articleItemColor = #777 width 250px border-top 1px solid borderColor border-right 1px solid borderColor + &:focus + border-color focusBorderColor overflow-y auto noSelect() &>div diff --git a/browser/styles/main/ArticleTopBar.styl b/browser/styles/main/ArticleTopBar.styl index e147c0ba..8f2daa76 100644 --- a/browser/styles/main/ArticleTopBar.styl +++ b/browser/styles/main/ArticleTopBar.styl @@ -137,6 +137,11 @@ infoBtnActiveBgColor = #3A3A3A &:active color inherit background-color lighten(topBarBtnBgColor, 15%) + &:disabled + color inactiveTextColor + background transparent + &:focus + color focusBorderColor &.ArticleTopBar-left-unsaved-save-button position relative .ArticleTopBar-left-unsaved-save-button-count @@ -175,8 +180,10 @@ infoBtnActiveBgColor = #3A3A3A background-color infoBtnBgColor color bgColor border-radius 11px - border none + border 1px solid bgColor transition 0.1s + &:focus + border-color focusBorderColor .tooltip tooltip() margin-left -50px @@ -192,6 +199,12 @@ infoBtnActiveBgColor = #3A3A3A top 8px right 15px opacity 0.7 + border-radius 23px + height 46px + width 46px + border 1px solid transparent + &:focus + border-color focusBorderColor &:hover opacity 1 .tooltip diff --git a/browser/styles/main/modal/DeleteArticleModal.styl b/browser/styles/main/modal/DeleteArticleModal.styl index b28d1b14..741224a8 100644 --- a/browser/styles/main/modal/DeleteArticleModal.styl +++ b/browser/styles/main/modal/DeleteArticleModal.styl @@ -1,5 +1,5 @@ .DeleteArticleModal.modal - width 350px + width 350px !important top 100px user-select none .title @@ -21,9 +21,13 @@ margin-left 5px &:hover background-color darken(white, 10%) + &:focus + border-color focusBorderColor &.danger border-color #E9432A background-color #E9432A color white &:hover background-color lighten(#E9432A, 15%) + &:focus + background-color lighten(#E9432A, 15%) diff --git a/browser/styles/vars.styl b/browser/styles/vars.styl index c24dca04..66aac4c8 100644 --- a/browser/styles/vars.styl +++ b/browser/styles/vars.styl @@ -3,6 +3,8 @@ highlightenBorderColor = darken(borderColor, 20%) invBorderColor = #404849 brandBorderColor = #3FB399 +focusBorderColor = #369DCD + buttonBorderColor = #4C4C4C lightButtonColor = #898989 diff --git a/lib/finder-app.js b/lib/finder-app.js index 4e0f82c1..5ae68823 100755 --- a/lib/finder-app.js +++ b/lib/finder-app.js @@ -9,7 +9,7 @@ app.on('ready', function () { app.dock.hide() } - var template = require('./menu-template') + var template = require('./finder-menu') var menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) diff --git a/lib/finder-menu.js b/lib/finder-menu.js new file mode 100644 index 00000000..65a1d32c --- /dev/null +++ b/lib/finder-menu.js @@ -0,0 +1,94 @@ +const electron = require('electron') +const BrowserWindow = electron.BrowserWindow + +const OSX = process.platform === 'darwin' +const WIN = process.platform === 'win32' + +var edit = { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'Command+Z', + selector: 'undo:' + }, + { + label: 'Redo', + accelerator: 'Shift+Command+Z', + selector: 'redo:' + }, + { + type: 'separator' + }, + { + label: 'Cut', + accelerator: 'Command+X', + selector: 'cut:' + }, + { + label: 'Copy', + accelerator: 'Command+C', + selector: 'copy:' + }, + { + label: 'Paste', + accelerator: 'Command+V', + selector: 'paste:' + }, + { + label: 'Select All', + accelerator: 'Command+A', + selector: 'selectAll:' + } + ] +} + +var view = { + label: 'View', + submenu: [ + { + label: 'Focus Search', + accelerator: 'Control + Alt + F', + click: function () { + console.log('focus find') + } + }, + { + type: 'separator' + }, + { + label: 'Toggle Markdown Preview', + accelerator: OSX ? 'Command + P' : 'Ctrl + P', + click: function () { + console.log('markdown') + } + }, + { + type: 'separator' + }, + { + label: 'Reload', + accelerator: (function () { + if (process.platform === 'darwin') return 'Command+R' + else return 'Ctrl+R' + })(), + click: function () { + BrowserWindow.getFocusedWindow().reload() + } + }, + { + label: 'Toggle Developer Tools', + accelerator: (function () { + if (process.platform === 'darwin') return 'Alt+Command+I' + else return 'Ctrl+Shift+I' + })(), + click: function (item, focusedWindow) { + if (focusedWindow) BrowserWindow.getFocusedWindow().toggleDevTools() + } + } + ] +} + +module.exports = process.platform === 'darwin' + ? [edit, view] + : [view] diff --git a/lib/main-app.js b/lib/main-app.js index 54baf77a..f00d9c96 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -227,7 +227,7 @@ app.on('ready', function () { if (finderProcess) finderProcess.kill() }) - var template = require('./menu-template') + var template = require('./main-menu') if (process.platform === 'win32') { template.unshift({ label: 'Boostnote', diff --git a/lib/menu-template.js b/lib/main-menu.js similarity index 64% rename from lib/menu-template.js rename to lib/main-menu.js index 5a327023..478580b8 100644 --- a/lib/menu-template.js +++ b/lib/main-menu.js @@ -1,6 +1,10 @@ const electron = require('electron') const BrowserWindow = electron.BrowserWindow const shell = electron.shell +const mainWindow = require('./main-window') + +const OSX = process.platform === 'darwin' +const WIN = process.platform === 'win32' var boost = { label: 'Boostnote', @@ -12,13 +16,6 @@ var boost = { { type: 'separator' }, - { - label: 'Services', - submenu: [] - }, - { - type: 'separator' - }, { label: 'Hide Boostnote', accelerator: 'Command+H', @@ -44,6 +41,53 @@ var boost = { ] } +var file = { + label: 'File', + submenu: [ + { + label: 'New Post', + accelerator: OSX ? 'Command + N' : 'Control + N', + click: function () { + mainWindow.webContents.send('nav-new-post') + } + }, + { + label: 'New Folder', + accelerator: OSX ? 'Command + Shift + N' : 'Control + Shift + N', + click: function () { + mainWindow.webContents.send('nav-new-folder') + } + }, + { + type: 'separator' + }, + { + label: 'Save Post', + accelerator: OSX ? 'Command + S' : 'Control + S', + click: function () { + mainWindow.webContents.send('detail-save') + } + }, + { + label: 'Save All Posts', + accelerator: OSX ? 'Command + Shift + S' : 'Control + Shift + S', + click: function () { + mainWindow.webContents.send('top-save-all') + } + }, + { + type: 'separator' + }, + { + label: 'Delete Post', + accelerator: OSX ? 'Control + Backspace' : 'Control + Delete', + click: function () { + mainWindow.webContents.send('detail-delete') + } + } + ] +} + var edit = { label: 'Edit', submenu: [ @@ -86,6 +130,26 @@ var edit = { var view = { label: 'View', submenu: [ + { + label: 'Focus Search', + accelerator: 'Control + Alt + F', + click: function () { + mainWindow.webContents.send('top-focus-search') + } + }, + { + type: 'separator' + }, + { + label: 'Toggle Markdown Preview', + accelerator: OSX ? 'Command + P' : 'Ctrl + P', + click: function () { + mainWindow.webContents.send('detail-toggle-preview') + } + }, + { + type: 'separator' + }, { label: 'Reload', accelerator: (function () { @@ -156,5 +220,5 @@ var help = { } module.exports = process.platform === 'darwin' - ? [boost, edit, view, window, help] - : [view, help] + ? [boost, file, edit, view, window, help] + : [file, view, help] diff --git a/lib/main-window.js b/lib/main-window.js index 5ab63a6c..0f04552f 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -21,6 +21,16 @@ mainWindow.webContents.on('new-window', function (e) { e.preventDefault() }) +mainWindow.webContents.sendInputEvent({ + type: 'keyDown', + keyCode: '\u0008' +}) + +mainWindow.webContents.sendInputEvent({ + type: 'keyUp', + keyCode: '\u0008' +}) + app.on('activate', function () { if (mainWindow == null) return null mainWindow.show()