From e761b309bfdeac0c9a75978200cbfeb021d94553 Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Fri, 3 Dec 2021 00:00:16 +1100 Subject: [PATCH] tray: hotkey, run in background window management features patched to reduce unexpected behaviour e.g. duplicate windows --- repo/collapsible-headers/mod.json | 2 +- repo/integrated-titlebar/buttons.mjs | 2 +- repo/integrated-titlebar/createWindow.cjs | 8 -- repo/registry.json | 2 + repo/theming/theme.css | 3 + repo/tray/client.mjs | 17 ++++ repo/tray/createWindow.cjs | 44 +++++++++ repo/tray/main.cjs | 112 ++++++++++++++++++++++ repo/tray/mod.json | 39 ++++++++ 9 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 repo/tray/client.mjs create mode 100644 repo/tray/createWindow.cjs create mode 100644 repo/tray/main.cjs create mode 100644 repo/tray/mod.json diff --git a/repo/collapsible-headers/mod.json b/repo/collapsible-headers/mod.json index dff3efd..14700b3 100644 --- a/repo/collapsible-headers/mod.json +++ b/repo/collapsible-headers/mod.json @@ -43,7 +43,7 @@ "key": "hotkey", "label": "toggle header collapse hotkey", "tooltip": "**opens/closes the currently focused header section**", - "value": "ctrl+enter" + "value": "Ctrl+Enter" } ] } diff --git a/repo/integrated-titlebar/buttons.mjs b/repo/integrated-titlebar/buttons.mjs index 0c77cd0..f8f04db 100644 --- a/repo/integrated-titlebar/buttons.mjs +++ b/repo/integrated-titlebar/buttons.mjs @@ -11,7 +11,7 @@ export const createWindowButtons = async ({ web, components }, db) => { maximizeIcon = (await db.get(['maximize_icon'])) || (await components.feather('maximize')), unmaximizeIcon = (await db.get(['unmaximize_icon'])) || (await components.feather('minimize')), - closeIcon = await db.get(['close_icon'], await components.feather('x')); + closeIcon = (await db.get(['close_icon'])) || (await components.feather('x')); minimizeIcon = minimizeIcon.trim(); maximizeIcon = maximizeIcon.trim(); unmaximizeIcon = unmaximizeIcon.trim(); diff --git a/repo/integrated-titlebar/createWindow.cjs b/repo/integrated-titlebar/createWindow.cjs index c74047a..fd9c08e 100644 --- a/repo/integrated-titlebar/createWindow.cjs +++ b/repo/integrated-titlebar/createWindow.cjs @@ -16,11 +16,3 @@ module.exports = async function (api, db, __exports, __eval) { }; `); }; - -// hide on open -// maybe only do once to hide first window? - -// exports.createWindow = (...args) => { -// warmWindowState.warmedWindow = true; -// createWindow(...args); -// }; diff --git a/repo/registry.json b/repo/registry.json index d468c32..4c8388b 100644 --- a/repo/registry.json +++ b/repo/registry.json @@ -4,6 +4,8 @@ "components", "integrated-titlebar", + "tray", + "tweaks", "font-chooser", "outliner", diff --git a/repo/theming/theme.css b/repo/theming/theme.css index dc4ce7f..a9009a3 100644 --- a/repo/theming/theme.css +++ b/repo/theming/theme.css @@ -595,6 +595,9 @@ body, box-shadow: var(--theme--accent_blue-active, rgba(26, 170, 220, 0.7)) 0px 0px 0px 1px inset, var(--theme--accent_blue-active, rgba(26, 170, 220, 0.4)) 0px 0px 0px 2px !important; } +.notion-sidebar-switcher:focus-visible { + box-shadow: none !important; +} .notion-onboarding-plan-type-team[style*='box-shadow: rgb(46, 170, 220)'], .notion-onboarding-plan-type-personal[style*='box-shadow: rgb(46, 170, 220)'] { box-shadow: var(--theme--accent_blue) 0px 0px 0px 2px !important; diff --git a/repo/tray/client.mjs b/repo/tray/client.mjs new file mode 100644 index 0000000..c4b934a --- /dev/null +++ b/repo/tray/client.mjs @@ -0,0 +1,17 @@ +/* + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ env, web }, db) { + const runInBackground = await db.get(['run_in_background']); + if (!runInBackground) return; + + // force new window creation on create new window hotkey + // hotkey is built into notion, so can't be changed, + // but is broken by this mod's window duplication prevention + web.addHotkeyListener([env.name === 'darwin' ? 'Meta' : 'Ctrl', 'Shift', 'N'], () => { + __enhancerElectronApi.sendMessage('create-new-window'); + }); +} diff --git a/repo/tray/createWindow.cjs b/repo/tray/createWindow.cjs new file mode 100644 index 0000000..0186e0f --- /dev/null +++ b/repo/tray/createWindow.cjs @@ -0,0 +1,44 @@ +/* + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function (api, db, __exports, __eval) { + const electron = require('electron'), + { isMenuOpen } = require('notion-enhancer/worker.cjs'), + runInBackground = await db.get(['run_in_background']); + if (!runInBackground) return; + + let appQuit = false; + electron.app.on('before-quit', () => { + appQuit = true; + }); + + const notionCreateWindow = __exports.createWindow; + __exports.createWindow = (relativeUrl = '', args, force = false) => { + const windows = electron.BrowserWindow.getAllWindows(); + if (windows.length) windows.forEach((win) => win.show()); + + if (force || !windows.length) { + // hijack close event to hide instead + const window = notionCreateWindow(relativeUrl, args); + window.prependListener('close', (e) => { + const isLastNotionWindow = + electron.BrowserWindow.getAllWindows().length === (isMenuOpen() ? 2 : 1); + if (!appQuit && isLastNotionWindow) { + window.hide(); + e.preventDefault(); + throw new Error(': prevent window close'); + } + }); + return window; + } else { + // prevents duplicate windows on dock/taskbar click + windows[0].focus(); + return windows[0]; + } + }; +}; diff --git a/repo/tray/main.cjs b/repo/tray/main.cjs new file mode 100644 index 0000000..33a2391 --- /dev/null +++ b/repo/tray/main.cjs @@ -0,0 +1,112 @@ +/* + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +let tray; + +module.exports = async function ({ env, registry }, db, __exports, __eval) { + const electron = require('electron'), + path = require('path'), + enhancerIcon = path.resolve(`${__dirname}/../../media/colour-x16.png`), + hotkey = await db.get(['hotkey']), + runInBackground = await db.get(['run_in_background']), + menuHotkey = await ( + await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e') + ).get(['hotkey']); + + const toggleWindows = (checkFocus = true) => { + const windows = electron.BrowserWindow.getAllWindows(); + if (runInBackground) { + // hide + if (windows.some((win) => (checkFocus ? win.isFocused() : true) && win.isVisible())) { + windows.forEach((win) => [win.isFocused() && win.blur(), win.hide()]); + } else windows.forEach((win) => win.show()); + } else { + // minimize + if (windows.some((win) => (checkFocus ? win.isFocused() : true) && !win.isMinimized())) { + windows.forEach((win) => win.minimize()); + } else windows.forEach((win) => win.restore()); + } + }; + + await electron.app.whenReady(); + tray = new electron.Tray(enhancerIcon); + tray.setToolTip('notion-enhancer'); + tray.on('click', () => toggleWindows(false)); + electron.globalShortcut.register(hotkey, toggleWindows); + + // connects to client hotkey listener + // manually forces new window creation + // since notion's default is broken by + // duplicate window prevention + const createWindow = () => { + const { createWindow } = env.notionRequire('main/createWindow.js'); + createWindow('', null, true); + }; + electron.ipcMain.on(`notion-enhancer:create-new-window`, createWindow); + + const contextMenu = electron.Menu.buildFromTemplate([ + { + type: 'normal', + label: 'notion-enhancer', + icon: enhancerIcon, + enabled: false, + }, + { type: 'separator' }, + { + type: 'normal', + label: 'docs', + click: () => electron.shell.openExternal('https://notion-enhancer.github.io/'), + }, + { + type: 'normal', + label: 'source code', + click: () => electron.shell.openExternal('https://github.com/notion-enhancer/'), + }, + { + type: 'normal', + label: 'community', + click: () => electron.shell.openExternal('https://discord.gg/sFWPXtA'), + }, + { + type: 'normal', + label: 'enhancements menu', + accelerator: menuHotkey, + click: env.focusMenu, + }, + { type: 'separator' }, + { + type: 'normal', + label: 'toggle visibility', + accelerator: hotkey, + click: toggleWindows, + }, + { + type: 'normal', + label: 'new window', + click: createWindow, + accelerator: 'CmdOrCtrl+Shift+N', + }, + { + label: 'relaunch', + click: env.reload, + }, + { + label: 'quit', + role: 'quit', + }, + ]); + tray.setContextMenu(contextMenu); +}; + +// hide on open +// maybe only do once to hide first window? + +// exports.createWindow = (...args) => { +// warmWindowState.warmedWindow = true; +// createWindow(...args); +// }; diff --git a/repo/tray/mod.json b/repo/tray/mod.json new file mode 100644 index 0000000..1d06f6c --- /dev/null +++ b/repo/tray/mod.json @@ -0,0 +1,39 @@ +{ + "name": "tray", + "id": "f96f4a73-21af-4e3f-a68f-ab4976b020da", + "environments": ["linux", "win32", "darwin"], + "version": "0.11.0", + "description": "adds an icon to the system tray/menubar for extra app/window management features (e.g. open on startup, a global hotkey).", + "tags": ["extension", "app"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": {}, + "js": { + "client": ["client.mjs"], + "electron": [ + { "source": "main.cjs", "target": "main/main.js" }, + { "source": "createWindow.cjs", "target": "main/createWindow.js" } + ] + }, + "options": [ + { + "type": "toggle", + "key": "run_in_background", + "label": "run notion in the background", + "tooltip": "**pressing the close button or toggling window visibility will hide the app, running notion in the background** (instead of quitting or minimizing it)", + "value": true + }, + { + "type": "hotkey", + "key": "hotkey", + "label": "toggle window visibility hotkey", + "value": "Ctrl+Shift+A" + } + ] +}