From 216205200c68c1168b2bfcd256f973ac258a65e3 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 15:30:33 +0300 Subject: [PATCH 01/52] fix in-app-menu navbar opacity + scrollbar color --- plugins/in-app-menu/style.css | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/in-app-menu/style.css b/plugins/in-app-menu/style.css index 25ae7c4a..19dd59fb 100644 --- a/plugins/in-app-menu/style.css +++ b/plugins/in-app-menu/style.css @@ -8,36 +8,36 @@ .menubar-menu-container { overflow-y: visible !important; } + /* fixes scrollbar positioning relative to nav bar */ #nav-bar-background.ytmusic-app-layout { right: 15px !important; } + /* remove window dragging for nav bar (conflict with titlebar drag) */ ytmusic-nav-bar, .tab-titleiron-icon, ytmusic-pivot-bar-item-renderer { - -webkit-app-region: unset; + -webkit-app-region: unset !important; } -/* Move navBar downwards and make it opaque */ -ytmusic-app-layout { - --ytmusic-nav-bar-height: 120px; -} - -ytmusic-search-box.ytmusic-nav-bar { - margin-top: 29px; -} - -.center-content.ytmusic-nav-bar { - background: #030303; -} -yt-page-navigation-progress, -#progress.yt-page-navigation-progress, -ytmusic-item-section-renderer[has-item-section-tabbed-header-renderer_] - #header.ytmusic-item-section-renderer, +/* fix weird positioning in search screen*/ ytmusic-header-renderer.ytmusic-search-page { - top: 90px !important; + position: unset !important; } + +/* Move navBar downwards */ +ytmusic-app-layout > [slot="nav-bar"], +#nav-bar-background.ytmusic-app-layout { + top: 17px !important; +} + +/* fix page progress bar position*/ +yt-page-navigation-progress, +#progress.yt-page-navigation-progress { + top: 30px !important; +} + /* Custom scrollbar */ ::-webkit-scrollbar { width: 12px; @@ -56,13 +56,13 @@ ytmusic-header-renderer.ytmusic-search-page { background-clip: padding-box; border: 2px solid rgba(0, 0, 0, 0); - background: rgb(49, 0, 0); + background: #3a3a3a; border-radius: 100px; -moz-border-radius: 100px; -webkit-border-radius: 100px; } ::-webkit-scrollbar-thumb:vertical:active { - background: rgb(56, 0, 0); /* Some darker color when you click it */ + background: #4d4c4c; /* Some darker color when you click it */ border-radius: 100px; -moz-border-radius: 100px; -webkit-border-radius: 100px; From 2b3a20c5ffd438eeb6b144ed1ca8c55316b83d36 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 15:47:32 +0300 Subject: [PATCH 02/52] Minimalize tray menu -doesnt include main menu template anymore --- menu.js | 134 ++++++++++++++++++++++++++------------------------------ tray.js | 9 +++- 2 files changed, 69 insertions(+), 74 deletions(-) diff --git a/menu.js b/menu.js index f974c4e4..e41fd9e0 100644 --- a/menu.js +++ b/menu.js @@ -23,18 +23,16 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ }, }); -const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ +const mainMenuTemplate = (win, withRoles = true) => [ { label: "Plugins", submenu: [ ...getAllPlugins().map((plugin) => { - const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js"); - + const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js") if (existsSync(pluginPath)) { if (!config.plugins.isEnabled(plugin)) { return pluginEnabledMenu(win, plugin, "", true); } - const getPluginMenu = require(pluginPath); return { label: plugin, @@ -46,7 +44,7 @@ const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ ], }; } - + return pluginEnabledMenu(win, plugin); }), { type: "separator" }, @@ -111,12 +109,12 @@ const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ config.set("options.hideMenu", item.checked); }, }, - ] + ] : []), ...(is.windows() || is.macOS() ? // Only works on Win/Mac - // https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows - [ + // https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows + [ { label: "Start at login", type: "checkbox", @@ -125,7 +123,7 @@ const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ config.set("options.startAtLogin", item.checked); }, }, - ] + ] : []), { label: "Tray", @@ -192,59 +190,55 @@ const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ }, ], }, - ...(!isTray - ? [ - { - label: "View", - submenu: withRoles - ? [ - { role: "reload" }, - { role: "forceReload" }, - { type: "separator" }, - { role: "zoomIn" }, - { role: "zoomOut" }, - { role: "resetZoom" }, - ] - : [ - { - label: "Reload", - click: () => { - win.webContents.reload(); - }, - }, - { - label: "Force Reload", - click: () => { - win.webContents.reloadIgnoringCache(); - }, - }, - { type: "separator" }, - { - label: "Zoom In", - click: () => { - win.webContents.setZoomLevel( - win.webContents.getZoomLevel() + 1 - ); - }, - }, - { - label: "Zoom Out", - click: () => { - win.webContents.setZoomLevel( - win.webContents.getZoomLevel() - 1 - ); - }, - }, - { - label: "Reset Zoom", - click: () => { - win.webContents.setZoomLevel(0); - }, - }, - ], - }, - ] - : []), + { + label: "View", + submenu: withRoles + ? [ + { role: "reload" }, + { role: "forceReload" }, + { type: "separator" }, + { role: "zoomIn" }, + { role: "zoomOut" }, + { role: "resetZoom" }, + ] + : [ + { + label: "Reload", + click: () => { + win.webContents.reload(); + }, + }, + { + label: "Force Reload", + click: () => { + win.webContents.reloadIgnoringCache(); + }, + }, + { type: "separator" }, + { + label: "Zoom In", + click: () => { + win.webContents.setZoomLevel( + win.webContents.getZoomLevel() + 1 + ); + }, + }, + { + label: "Zoom Out", + click: () => { + win.webContents.setZoomLevel( + win.webContents.getZoomLevel() - 1 + ); + }, + }, + { + label: "Reset Zoom", + click: () => { + win.webContents.setZoomLevel(0); + }, + }, + ], + }, { label: "Navigation", submenu: [ @@ -271,16 +265,12 @@ const mainMenuTemplate = (win, withRoles = true, isTray = false) => [ app.quit(); }, }, - ...(!isTray - ? [ - { - label: "Quit App", - click: () => { - app.quit(); - }, - }, - ] - : []), + { + label: "Quit App", + click: () => { + app.quit(); + }, + }, ], }, ]; diff --git a/tray.js b/tray.js index e871f4bf..7d0331f7 100644 --- a/tray.js +++ b/tray.js @@ -3,7 +3,6 @@ const path = require("path"); const { Menu, nativeImage, Tray } = require("electron"); const config = require("./config"); -const { mainMenuTemplate } = require("./menu"); const getSongControls = require("./providers/song-controls"); // Prevent tray being garbage collected @@ -57,7 +56,13 @@ module.exports.setUpTray = (app, win) => { win.show(); }, }, - ...mainMenuTemplate(win, true, true), + { + label: "Restart App", + click: () => { + app.relaunch(); + app.quit(); + }, + }, { label: "Quit", click: () => { From 80a7d2c255d3238adaf4e712de35194fd2633853 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 15:54:51 +0300 Subject: [PATCH 03/52] remove redundant roles --- menu.js | 115 +++++++++++++++++------------------- plugins/in-app-menu/back.js | 14 ++--- 2 files changed, 60 insertions(+), 69 deletions(-) diff --git a/menu.js b/menu.js index e41fd9e0..893ae015 100644 --- a/menu.js +++ b/menu.js @@ -23,7 +23,7 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ }, }); -const mainMenuTemplate = (win, withRoles = true) => [ +const mainMenuTemplate = (win) => [ { label: "Plugins", submenu: [ @@ -44,7 +44,7 @@ const mainMenuTemplate = (win, withRoles = true) => [ ], }; } - + return pluginEnabledMenu(win, plugin); }), { type: "separator" }, @@ -101,28 +101,28 @@ const mainMenuTemplate = (win, withRoles = true) => [ }, ...(is.windows() || is.linux() ? [ - { - label: "Hide menu", - type: "checkbox", - checked: config.get("options.hideMenu"), - click: (item) => { - config.set("options.hideMenu", item.checked); - }, + { + label: "Hide menu", + type: "checkbox", + checked: config.get("options.hideMenu"), + click: (item) => { + config.set("options.hideMenu", item.checked); }, + }, ] : []), ...(is.windows() || is.macOS() ? // Only works on Win/Mac // https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows [ - { - label: "Start at login", - type: "checkbox", - checked: config.get("options.startAtLogin"), - click: (item) => { - config.set("options.startAtLogin", item.checked); - }, + { + label: "Start at login", + type: "checkbox", + checked: config.get("options.startAtLogin"), + click: (item) => { + config.set("options.startAtLogin", item.checked); }, + }, ] : []), { @@ -192,52 +192,43 @@ const mainMenuTemplate = (win, withRoles = true) => [ }, { label: "View", - submenu: withRoles - ? [ - { role: "reload" }, - { role: "forceReload" }, - { type: "separator" }, - { role: "zoomIn" }, - { role: "zoomOut" }, - { role: "resetZoom" }, - ] - : [ - { - label: "Reload", - click: () => { - win.webContents.reload(); - }, - }, - { - label: "Force Reload", - click: () => { - win.webContents.reloadIgnoringCache(); - }, - }, - { type: "separator" }, - { - label: "Zoom In", - click: () => { - win.webContents.setZoomLevel( - win.webContents.getZoomLevel() + 1 - ); - }, - }, - { - label: "Zoom Out", - click: () => { - win.webContents.setZoomLevel( - win.webContents.getZoomLevel() - 1 - ); - }, - }, - { - label: "Reset Zoom", - click: () => { - win.webContents.setZoomLevel(0); - }, - }, - ], + submenu: [ + { + label: "Reload", + click: () => { + win.webContents.reload(); + }, + }, + { + label: "Force Reload", + click: () => { + win.webContents.reloadIgnoringCache(); + }, + }, + { type: "separator" }, + { + label: "Zoom In", + click: () => { + win.webContents.setZoomLevel( + win.webContents.getZoomLevel() + 1 + ); + }, + }, + { + label: "Zoom Out", + click: () => { + win.webContents.setZoomLevel( + win.webContents.getZoomLevel() - 1 + ); + }, + }, + { + label: "Reset Zoom", + click: () => { + win.webContents.setZoomLevel(0); + }, + }, + ], }, { label: "Navigation", diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 3c77c377..f5925815 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -16,7 +16,7 @@ const originalBuildMenu = Menu.buildFromTemplate; // This function natively gets called on all submenu so no more reason to use recursion Menu.buildFromTemplate = (template) => { // Fix checkboxes and radio buttons - updateCheckboxesAndRadioButtons(win, template); + updateCheckboxesAndRadioButtons(template); // return as normal return originalBuildMenu(template); @@ -39,21 +39,21 @@ module.exports = (winImport) => { //register keyboard shortcut && hide menu if hideMenu is enabled if (config.get("options.hideMenu")) { - switchMenuVisibility(win); + switchMenuVisibility(); electronLocalshortcut.register(win, "Esc", () => { - switchMenuVisibility(win); + switchMenuVisibility(); }); } }); }; let visible = true; -function switchMenuVisibility(win) { +function switchMenuVisibility() { visible = !visible; win.webContents.send("updateMenu", visible); } -function checkCheckbox(win, item) { +function checkCheckbox(item) { //check item item.checked = !item.checked; //update menu (closes it) @@ -61,14 +61,14 @@ function checkCheckbox(win, item) { } // Update checkboxes/radio buttons -function updateCheckboxesAndRadioButtons(win, template) { +function updateCheckboxesAndRadioButtons(template) { for (let item of template) { // Change onClick of checkbox+radio if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) { let originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - checkCheckbox(win, itemClicked); + checkCheckbox(itemClicked); }; item.fixed = true; } From 5524a14c871ccf47a2f4237468eb5cdc0c678b21 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 19:20:56 +0300 Subject: [PATCH 04/52] move advanced options to dedicated submenu + add "Use Proxy" checkbox --- menu.js | 102 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/menu.js b/menu.js index 893ae015..c0843247 100644 --- a/menu.js +++ b/menu.js @@ -47,13 +47,6 @@ const mainMenuTemplate = (win) => [ return pluginEnabledMenu(win, plugin); }), - { type: "separator" }, - { - label: "Advanced options", - click: () => { - config.edit(); - }, - }, ], }, { @@ -67,30 +60,6 @@ const mainMenuTemplate = (win) => [ config.set("options.autoUpdates", item.checked); }, }, - { - label: "Disable hardware acceleration", - type: "checkbox", - checked: config.get("options.disableHardwareAcceleration"), - click: (item) => { - config.set("options.disableHardwareAcceleration", item.checked); - }, - }, - { - label: "Restart on config changes", - type: "checkbox", - checked: config.get("options.restartOnConfigChanges"), - click: (item) => { - config.set("options.restartOnConfigChanges", item.checked); - }, - }, - { - label: "Reset App cache when app starts", - type: "checkbox", - checked: config.get("options.autoResetAppCache"), - click: (item) => { - config.set("options.autoResetAppCache", item.checked); - }, - }, { label: "Resume last song when app starts", type: "checkbox", @@ -169,24 +138,63 @@ const mainMenuTemplate = (win) => [ ], }, { type: "separator" }, - { - label: "Toggle DevTools", - // Cannot use "toggleDevTools" role in MacOS - click: () => { - const { webContents } = win; - if (webContents.isDevToolsOpened()) { - webContents.closeDevTools(); - } else { - const devToolsOptions = {}; - webContents.openDevTools(devToolsOptions); - } - }, - }, { label: "Advanced options", - click: () => { - config.edit(); - }, + submenu: [ + { + label: "Use Proxy", + type: "checkbox", + checked: !!config.get("options.proxy"), + click: (item) => { + const value = item.checked? "socks5://127.0.0.1:9999" : ""; + config.set("options.proxy", value); + } + }, + { + label: "Disable hardware acceleration", + type: "checkbox", + checked: config.get("options.disableHardwareAcceleration"), + click: (item) => { + config.set("options.disableHardwareAcceleration", item.checked); + }, + }, + { + label: "Restart on config changes", + type: "checkbox", + checked: config.get("options.restartOnConfigChanges"), + click: (item) => { + config.set("options.restartOnConfigChanges", item.checked); + }, + }, + { + label: "Reset App cache when app starts", + type: "checkbox", + checked: config.get("options.autoResetAppCache"), + click: (item) => { + config.set("options.autoResetAppCache", item.checked); + }, + }, + { type: "separator" }, + { + label: "Toggle DevTools", + // Cannot use "toggleDevTools" role in MacOS + click: () => { + const { webContents } = win; + if (webContents.isDevToolsOpened()) { + webContents.closeDevTools(); + } else { + const devToolsOptions = {}; + webContents.openDevTools(devToolsOptions); + } + }, + }, + { + label: "Edit config.json", + click: () => { + config.edit(); + }, + }, + ] }, ], }, From ec3adff70676f2b9f5fe94f7bd0ed670f6373bb4 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 20:15:48 +0300 Subject: [PATCH 05/52] fix bug when loading window with no connection --- plugins/in-app-menu/back.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index f5925815..c8660ecb 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -36,6 +36,8 @@ module.exports = (winImport) => { done = true; setApplicationMenu(win); + //fix bug when loading window with no internet connection + switchMenuVisibility(); //register keyboard shortcut && hide menu if hideMenu is enabled if (config.get("options.hideMenu")) { @@ -47,7 +49,7 @@ module.exports = (winImport) => { }); }; -let visible = true; +let visible = false; function switchMenuVisibility() { visible = !visible; win.webContents.send("updateMenu", visible); From 061e4a9e5f0e8d6743f933df14aefa6685bbb8ed Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 21:51:35 +0300 Subject: [PATCH 06/52] add 'electron-prompt' and use it for setting proxy --- menu.js | 36 +++++++++++++++++++++++++++++++++--- package.json | 1 + yarn.lock | 19 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/menu.js b/menu.js index c0843247..3c6efa86 100644 --- a/menu.js +++ b/menu.js @@ -6,11 +6,13 @@ const is = require("electron-is"); const { getAllPlugins } = require("./plugins/utils"); const config = require("./config"); +const prompt = require('electron-prompt'); const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, type: "checkbox", checked: config.plugins.isEnabled(plugin), + hasSubmenu: hasSubmenu || undefined, click: (item) => { if (item.checked) { config.plugins.enable(plugin); @@ -142,12 +144,12 @@ const mainMenuTemplate = (win) => [ label: "Advanced options", submenu: [ { - label: "Use Proxy", + label: "Proxy", type: "checkbox", checked: !!config.get("options.proxy"), + //fixed: true, //skip in-app-menu click() override click: (item) => { - const value = item.checked? "socks5://127.0.0.1:9999" : ""; - config.set("options.proxy", value); + setProxy(item, win); } }, { @@ -307,3 +309,31 @@ module.exports.setApplicationMenu = (win) => { const menu = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menu); }; + +// +const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); +function setProxy(item, win) { + prompt({ + title: 'Set Proxy', + label: 'Enter Proxy Adress:', + value: config.get("options.proxy") || `Example - "socks5://127.0.0.1:9999"`, + inputAttrs: { + type: 'text' + }, + type: 'input', + alwaysOnTop: true, + icon: iconPath + }) + .then((input) => { + if(input !== null) { + console.log(`setting proxy to ${input}`); + config.set("options.proxy", input); + if(input === "") + item.checked = false; + item.checked = (input === "") ? false : true; + } else { //user pressed cancel + item.checked = !item.checked; //reset checkbox + } + }) + .catch(console.error); +} \ No newline at end of file diff --git a/package.json b/package.json index ce144c38..736374c5 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "electron-debug": "^3.2.0", "electron-is": "^3.0.0", "electron-localshortcut": "^3.2.1", + "electron-prompt": "^1.6.2", "electron-store": "^7.0.2", "electron-unhandled": "^3.0.2", "electron-updater": "^4.3.6", diff --git a/yarn.lock b/yarn.lock index c969527e..b029d432 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2908,6 +2908,13 @@ dmg-builder@22.9.1: js-yaml "^3.14.0" sanitize-filename "^1.6.3" +doc-ready@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/doc-ready/-/doc-ready-1.0.4.tgz#37f5391969cff994303fdfef2e5d50357f8164d3" + integrity sha1-N/U5GWnP+ZQwP9/vLl1QNX+BZNM= + dependencies: + eventie "^1" + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3086,6 +3093,13 @@ electron-localshortcut@^3.1.0, electron-localshortcut@^3.2.1: keyboardevent-from-electron-accelerator "^2.0.0" keyboardevents-areequal "^0.2.1" +electron-prompt@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/electron-prompt/-/electron-prompt-1.6.2.tgz#e26bd3d359120bd45ce2f9625bdc380deb7ee269" + integrity sha512-gC9ZpMopIgz1kW92J7UfsXarkLdPH2zhr77bnJFlDSduYjRlr9nEPm4ux1vyzJsO72AdOyvuPPIQB0j+gWsncQ== + dependencies: + doc-ready "^1.0.4" + electron-publish@22.9.1: version "22.9.1" resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.9.1.tgz#7cc76ac4cc53efd29ee31c1e5facb9724329068e" @@ -3605,6 +3619,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +eventie@^1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/eventie/-/eventie-1.0.6.tgz#d4ffc8b0c2b5e493c2aa1b22cbe918d3aee74437" + integrity sha1-1P/IsMK15JPCqhsiy+kY067nRDc= + events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" From 317521bba618dadb20304596766a360a9b897462 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 21:53:39 +0300 Subject: [PATCH 07/52] fix memory leak + in-app-menu updates menu only if needed --- index.js | 6 ++++++ plugins/in-app-menu/back.js | 16 ++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 82bb3320..478d2f86 100644 --- a/index.js +++ b/index.js @@ -152,7 +152,13 @@ function createMainWindow() { return win; } +let createdWindow = false; app.on("browser-window-created", (event, win) => { + //Ensure listeners aren't registered when creating input dialog + if(createdWindow){ + return; + } + createdWindow = true; loadPlugins(win); win.webContents.on("did-fail-load", () => { diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index c8660ecb..fba6eb6d 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -16,7 +16,7 @@ const originalBuildMenu = Menu.buildFromTemplate; // This function natively gets called on all submenu so no more reason to use recursion Menu.buildFromTemplate = (template) => { // Fix checkboxes and radio buttons - updateCheckboxesAndRadioButtons(template); + updateTemplate(template); // return as normal return originalBuildMenu(template); @@ -55,22 +55,26 @@ function switchMenuVisibility() { win.webContents.send("updateMenu", visible); } -function checkCheckbox(item) { - //check item +function updateCheckboxesAndRadioButtons(item, isRadio, hasSubmenu) { + if (!isRadio) { + //fix checkbox item.checked = !item.checked; - //update menu (closes it) + } + //update menu if radio / hasSubmenu + if (isRadio || hasSubmenu) { win.webContents.send("updateMenu", true); + } } // Update checkboxes/radio buttons -function updateCheckboxesAndRadioButtons(template) { +function updateTemplate(template) { for (let item of template) { // Change onClick of checkbox+radio if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) { let originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - checkCheckbox(itemClicked); + updateCheckboxesAndRadioButtons(itemClicked, item.type==='radio', item.hasSubmenu); }; item.fixed = true; } From 70b03b71e4bfa45df6cb70130bf896dd0ec8d755 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 22:04:40 +0300 Subject: [PATCH 08/52] refactor and stylecheck --- menu.js | 3 --- plugins/in-app-menu/back.js | 13 ++++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/menu.js b/menu.js index 3c6efa86..b51fb516 100644 --- a/menu.js +++ b/menu.js @@ -147,7 +147,6 @@ const mainMenuTemplate = (win) => [ label: "Proxy", type: "checkbox", checked: !!config.get("options.proxy"), - //fixed: true, //skip in-app-menu click() override click: (item) => { setProxy(item, win); } @@ -310,7 +309,6 @@ module.exports.setApplicationMenu = (win) => { Menu.setApplicationMenu(menu); }; -// const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); function setProxy(item, win) { prompt({ @@ -326,7 +324,6 @@ function setProxy(item, win) { }) .then((input) => { if(input !== null) { - console.log(`setting proxy to ${input}`); config.set("options.proxy", input); if(input === "") item.checked = false; diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index fba6eb6d..8a6efdf7 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -9,6 +9,8 @@ const { injectCSS } = require("../utils"); //check that menu doesn't get created twice let done = false; +//tracks menu visibility +let visible = true; // win hook for fixing menu let win; @@ -41,17 +43,22 @@ module.exports = (winImport) => { //register keyboard shortcut && hide menu if hideMenu is enabled if (config.get("options.hideMenu")) { - switchMenuVisibility(); + visible = false; electronLocalshortcut.register(win, "Esc", () => { switchMenuVisibility(); }); } + // fix bug with menu not applying on start + setMenuVisibility(visible); }); }; -let visible = false; function switchMenuVisibility() { - visible = !visible; + setMenuVisibility(!visible); +} + +function setMenuVisibility(value){ + visible = value; win.webContents.send("updateMenu", visible); } From 1cae35a62b5c4aa687eb66cd9449cb3351947498 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 22:34:12 +0300 Subject: [PATCH 09/52] fix in-app-menu hideMenu on launch --- plugins/in-app-menu/back.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 8a6efdf7..53bb9f8d 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -38,17 +38,15 @@ module.exports = (winImport) => { done = true; setApplicationMenu(win); - //fix bug when loading window with no internet connection - switchMenuVisibility(); //register keyboard shortcut && hide menu if hideMenu is enabled if (config.get("options.hideMenu")) { - visible = false; + switchMenuVisibility(); electronLocalshortcut.register(win, "Esc", () => { switchMenuVisibility(); }); - } - // fix bug with menu not applying on start + } + // fix bug with menu not applying on start when no internet connection available setMenuVisibility(visible); }); }; From 8b6c60bb178e3bc22c8c917f650153a960ea9ae6 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 22:59:20 +0300 Subject: [PATCH 10/52] fix Connection Error when using in-app-menu --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 478d2f86..bc3db73a 100644 --- a/index.js +++ b/index.js @@ -93,6 +93,7 @@ function createMainWindow() { contextIsolation: false, preload: path.join(__dirname, "preload.js"), nodeIntegrationInSubFrames: true, + nodeIntegration: true, nativeWindowOpen: true, // window.open return Window object(like in regular browsers), not BrowserWindowProxy enableRemoteModule: true, affinity: "main-window", // main window, and addition windows should work in one process From 2e6fffc903953b2f4cd6c5321f36f22677572d37 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 3 Apr 2021 23:46:16 +0300 Subject: [PATCH 11/52] Revert "fix Connection Error when using in-app-menu" This reverts commit 8b6c60bb178e3bc22c8c917f650153a960ea9ae6. --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index bc3db73a..478d2f86 100644 --- a/index.js +++ b/index.js @@ -93,7 +93,6 @@ function createMainWindow() { contextIsolation: false, preload: path.join(__dirname, "preload.js"), nodeIntegrationInSubFrames: true, - nodeIntegration: true, nativeWindowOpen: true, // window.open return Window object(like in regular browsers), not BrowserWindowProxy enableRemoteModule: true, affinity: "main-window", // main window, and addition windows should work in one process From 8291cdfc12d37b4902cc1bd2b32af975ba6d49f3 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 02:37:19 +0300 Subject: [PATCH 12/52] disable dev tools on electron-prompt --- index.js | 4 +++- menu.js | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 478d2f86..baa8c49c 100644 --- a/index.js +++ b/index.js @@ -37,7 +37,9 @@ if (config.get("options.proxy")) { } // Adds debug features like hotkeys for triggering dev tools and reload -require("electron-debug")(); +require("electron-debug")({ + showDevTools: false //disable dev tools on electron-prompt +}); // Prevent window being garbage collected let mainWindow; diff --git a/menu.js b/menu.js index b51fb516..9deef729 100644 --- a/menu.js +++ b/menu.js @@ -148,7 +148,7 @@ const mainMenuTemplate = (win) => [ type: "checkbox", checked: !!config.get("options.proxy"), click: (item) => { - setProxy(item, win); + setProxy(item); } }, { @@ -310,7 +310,7 @@ module.exports.setApplicationMenu = (win) => { }; const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); -function setProxy(item, win) { +function setProxy(item) { prompt({ title: 'Set Proxy', label: 'Enter Proxy Adress:', @@ -325,8 +325,6 @@ function setProxy(item, win) { .then((input) => { if(input !== null) { config.set("options.proxy", input); - if(input === "") - item.checked = false; item.checked = (input === "") ? false : true; } else { //user pressed cancel item.checked = !item.checked; //reset checkbox From 421fe6793022386ef21707e78f56739f5689ecf1 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 02:46:12 +0300 Subject: [PATCH 13/52] ignore proxy if equal to example --- menu.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/menu.js b/menu.js index 9deef729..9dd0b8e2 100644 --- a/menu.js +++ b/menu.js @@ -310,11 +310,12 @@ module.exports.setApplicationMenu = (win) => { }; const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); +const example = `Example: "socks5://127.0.0.1:9999"`; function setProxy(item) { prompt({ title: 'Set Proxy', - label: 'Enter Proxy Adress:', - value: config.get("options.proxy") || `Example - "socks5://127.0.0.1:9999"`, + label: 'Enter Proxy Adress (or leave empty to disable)', + value: config.get("options.proxy") || example, inputAttrs: { type: 'text' }, @@ -323,7 +324,7 @@ function setProxy(item) { icon: iconPath }) .then((input) => { - if(input !== null) { + if(input !== null && input !== example) { config.set("options.proxy", input); item.checked = (input === "") ? false : true; } else { //user pressed cancel From d12d16348aff03d6659a0211976d597c50396331 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 15:06:14 +0300 Subject: [PATCH 14/52] custom dark skin for prompt --- darkPrompt.css | 37 +++++++++++++++++++++++++++++++++++++ menu.js | 5 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 darkPrompt.css diff --git a/darkPrompt.css b/darkPrompt.css new file mode 100644 index 00000000..100a6ed5 --- /dev/null +++ b/darkPrompt.css @@ -0,0 +1,37 @@ +body { + background-color: rgba(0, 0, 0, 0.3); + background-image: linear-gradient(315deg, #200000 0%, #13253a 74%); + overflow: hidden; + color:whitesmoke; +} +#container { + background: rgba( 0, 0, 0, 0.7 ); + box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 ); + backdrop-filter: blur( 10.0px ); + -webkit-backdrop-filter: blur( 10.0px ); + border-radius: 10px; + border: 1px solid rgba(80, 0, 0, 0.4); + + overflow: hidden; +} + +#data { + background: unset; + color: whitesmoke; + border: 1px solid rgb(54, 54, 54); +} + +#data:focus { + outline: unset; + border: 1px solid rgb(85, 85, 85); +} + + #ok:focus, #cancel:focus { + outline: rgba(30, 0, 0, 0.3) solid 1px; + border: 1px solid rgba(60, 0, 0, 0.4); +} + +#ok, #cancel { + background-color: rgb(0, 0, 0); + color: whitesmoke; +} \ No newline at end of file diff --git a/menu.js b/menu.js index 9dd0b8e2..bbe7204f 100644 --- a/menu.js +++ b/menu.js @@ -314,14 +314,15 @@ const example = `Example: "socks5://127.0.0.1:9999"`; function setProxy(item) { prompt({ title: 'Set Proxy', - label: 'Enter Proxy Adress (or leave empty to disable)', + label: 'Enter Proxy Address (leave empty to disable)', value: config.get("options.proxy") || example, inputAttrs: { type: 'text' }, type: 'input', alwaysOnTop: true, - icon: iconPath + icon: iconPath, + customStylesheet: path.join(__dirname, "darkPrompt.css"), }) .then((input) => { if(input !== null && input !== example) { From 7c8e946871fba05e5cbedcf9fa5e9b7a33117ccf Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 19:58:25 +0300 Subject: [PATCH 15/52] implement custom prompt --- menu.js | 18 ++- package.json | 2 +- prompt/customTitlebar.js | 12 ++ darkPrompt.css => prompt/darkPrompt.css | 11 +- prompt/index.js | 127 ++++++++++++++++++ prompt/page/prompt.css | 72 ++++++++++ prompt/page/prompt.html | 18 +++ prompt/page/prompt.js | 170 ++++++++++++++++++++++++ yarn.lock | 7 - 9 files changed, 424 insertions(+), 13 deletions(-) create mode 100644 prompt/customTitlebar.js rename darkPrompt.css => prompt/darkPrompt.css (88%) create mode 100644 prompt/index.js create mode 100644 prompt/page/prompt.css create mode 100644 prompt/page/prompt.html create mode 100644 prompt/page/prompt.js diff --git a/menu.js b/menu.js index bbe7204f..0312778f 100644 --- a/menu.js +++ b/menu.js @@ -6,7 +6,7 @@ const is = require("electron-is"); const { getAllPlugins } = require("./plugins/utils"); const config = require("./config"); -const prompt = require('electron-prompt'); +const prompt = require('./prompt'); const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, @@ -312,7 +312,7 @@ module.exports.setApplicationMenu = (win) => { const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); const example = `Example: "socks5://127.0.0.1:9999"`; function setProxy(item) { - prompt({ + let options = { title: 'Set Proxy', label: 'Enter Proxy Address (leave empty to disable)', value: config.get("options.proxy") || example, @@ -322,8 +322,18 @@ function setProxy(item) { type: 'input', alwaysOnTop: true, icon: iconPath, - customStylesheet: path.join(__dirname, "darkPrompt.css"), - }) + customStylesheet: path.join(__dirname, "prompt","darkPrompt.css"), + }; + if (config.plugins.isEnabled("in-app-menu")) { + Object.assign(options, { + frame: false, + customScript: path.join(__dirname, "prompt","customTitlebar.js"), + enableRemoteModule: true, + height: 200, + width: 450, + }) + } + prompt(options) .then((input) => { if(input !== null && input !== example) { config.set("options.proxy", input); diff --git a/package.json b/package.json index 736374c5..36591897 100644 --- a/package.json +++ b/package.json @@ -70,11 +70,11 @@ "browser-id3-writer": "^4.4.0", "custom-electron-titlebar": "^3.2.6", "discord-rpc": "^3.2.0", + "doc-ready": "^1.0.4", "downloads-folder": "^3.0.1", "electron-debug": "^3.2.0", "electron-is": "^3.0.0", "electron-localshortcut": "^3.2.1", - "electron-prompt": "^1.6.2", "electron-store": "^7.0.2", "electron-unhandled": "^3.0.2", "electron-updater": "^4.3.6", diff --git a/prompt/customTitlebar.js b/prompt/customTitlebar.js new file mode 100644 index 00000000..203828d4 --- /dev/null +++ b/prompt/customTitlebar.js @@ -0,0 +1,12 @@ +const customTitlebar = require("custom-electron-titlebar"); + +module.exports = () => { + const bar = new customTitlebar.Titlebar({ + backgroundColor: customTitlebar.Color.fromHex("#050505"), + }); + try { + bar.updateMenu(null); + } catch (e) { + //will always throw type error - null isn't menu, but it works + } +} \ No newline at end of file diff --git a/darkPrompt.css b/prompt/darkPrompt.css similarity index 88% rename from darkPrompt.css rename to prompt/darkPrompt.css index 100a6ed5..3bdc5647 100644 --- a/darkPrompt.css +++ b/prompt/darkPrompt.css @@ -4,6 +4,16 @@ body { overflow: hidden; color:whitesmoke; } + +::-webkit-scrollbar { + width: 0 !important; + display: none; +} + +#label { + text-align: center; +} + #container { background: rgba( 0, 0, 0, 0.7 ); box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 ); @@ -11,7 +21,6 @@ body { -webkit-backdrop-filter: blur( 10.0px ); border-radius: 10px; border: 1px solid rgba(80, 0, 0, 0.4); - overflow: hidden; } diff --git a/prompt/index.js b/prompt/index.js new file mode 100644 index 00000000..d6664305 --- /dev/null +++ b/prompt/index.js @@ -0,0 +1,127 @@ +const electron = require('electron'); + +const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow; +const ipcMain = electron.ipcMain || electron.remote.ipcMain; +const url = require('url'); +const path = require('path'); + +const DEFAULT_WIDTH = 370; +const DEFAULT_HEIGHT = 160; + +function electronPrompt(options, parentWindow) { + return new Promise((resolve, reject) => { + const id = `${Date.now()}-${Math.random()}`; + + const options_ = Object.assign( + { + width: DEFAULT_WIDTH, + height: DEFAULT_HEIGHT, + minWidth: DEFAULT_WIDTH, + minHeight: DEFAULT_HEIGHT, + resizable: false, + title: 'Prompt', + label: 'Please input a value:', + buttonLabels: null, + alwaysOnTop: false, + value: null, + type: 'input', + selectOptions: null, + icon: null, + useHtmlLabel: false, + customStylesheet: null, + menuBarVisible: false, + skipTaskbar: true, + frame: true, + customScript: null, + enableRemoteModule: false + }, + options || {} + ); + + if (options_.type === 'select' && (options_.selectOptions === null || typeof options_.selectOptions !== 'object')) { + reject(new Error('"selectOptions" must be an object')); + return; + } + + let promptWindow = new BrowserWindow({ + frame: options_.frame, + width: options_.width, + height: options_.height, + minWidth: options_.minWidth, + minHeight: options_.minHeight, + resizable: options_.resizable, + minimizable: false, + fullscreenable: false, + maximizable: false, + parent: parentWindow, + skipTaskbar: options_.skipTaskbar, + alwaysOnTop: options_.alwaysOnTop, + useContentSize: options_.resizable, + modal: Boolean(parentWindow), + title: options_.title, + icon: options_.icon || undefined, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + enableRemoteModule: options_.enableRemoteModule + } + }); + + promptWindow.setMenu(null); + promptWindow.setMenuBarVisibility(options_.menuBarVisible); + + const getOptionsListener = event => { + event.returnValue = JSON.stringify(options_); + }; + + const cleanup = () => { + ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener); + ipcMain.removeListener('prompt-post-data:' + id, postDataListener); + ipcMain.removeListener('prompt-error:' + id, errorListener); + + if (promptWindow) { + promptWindow.close(); + promptWindow = null; + } + }; + + const postDataListener = (event, value) => { + resolve(value); + event.returnValue = null; + cleanup(); + }; + + const unresponsiveListener = () => { + reject(new Error('Window was unresponsive')); + cleanup(); + }; + + const errorListener = (event, message) => { + reject(new Error(message)); + event.returnValue = null; + cleanup(); + }; + + ipcMain.on('prompt-get-options:' + id, getOptionsListener); + ipcMain.on('prompt-post-data:' + id, postDataListener); + ipcMain.on('prompt-error:' + id, errorListener); + promptWindow.on('unresponsive', unresponsiveListener); + + promptWindow.on('closed', () => { + promptWindow = null; + cleanup(); + resolve(null); + }); + + const promptUrl = url.format({ + protocol: 'file', + slashes: true, + pathname: path.join(__dirname, 'page', 'prompt.html'), + hash: id + }); + + promptWindow.loadURL(promptUrl); + }); +} + +module.exports = electronPrompt; diff --git a/prompt/page/prompt.css b/prompt/page/prompt.css new file mode 100644 index 00000000..b9d37777 --- /dev/null +++ b/prompt/page/prompt.css @@ -0,0 +1,72 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.5em; + color: #333; + background-color: #fff; +} + +#container { + align-items: center; + justify-content: center; + display: flex; + height: 100%; + overflow: auto; +} + +#form { + width: 100%; + padding-top: .5em; +} + +#label { + max-width: 100%; + max-height: 100%; + margin-bottom: .8em; + padding: 0 .5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#data { + border-radius: 2px; + background: #fff; + width: 90%; + padding: .4em .5em; + border: 1px solid black; + min-height: 2em; + margin: 0 0 1.2em; +} + +select#data { + height: 2em; +} + +#data-container { + text-align: center; +} + +#buttons { + text-align: right; + padding: 0 .5em 0 0; +} + +#buttons > button, +#buttons > input[type=submit] { + border-radius: 2px; + border: 0; + margin: 0 0 0 .5em; + font-size: .8em; + line-height: 1em; + padding: .6em 1em +} + +#ok { + background-color: #3879D9; + color: white; +} + +#cancel { + background-color: #DDD; + color: black; +} diff --git a/prompt/page/prompt.html b/prompt/page/prompt.html new file mode 100644 index 00000000..cdbfa544 --- /dev/null +++ b/prompt/page/prompt.html @@ -0,0 +1,18 @@ + + + + + +
+
+
...
+
+
+ + +
+
+
+ + + diff --git a/prompt/page/prompt.js b/prompt/page/prompt.js new file mode 100644 index 00000000..754d0e74 --- /dev/null +++ b/prompt/page/prompt.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const {ipcRenderer} = require('electron'); +const docReady = require('doc-ready'); + +let promptId = null; +let promptOptions = null; + +function promptError(error) { + if (error instanceof Error) { + error = error.message; + } + + ipcRenderer.sendSync('prompt-error:' + promptId, error); +} + +function promptCancel() { + ipcRenderer.sendSync('prompt-post-data:' + promptId, null); +} + +function promptSubmit() { + const dataElement = document.querySelector('#data'); + let data = null; + + if (promptOptions.type === 'input') { + data = dataElement.value; + } else if (promptOptions.type === 'select') { + if (promptOptions.selectMultiple) { + data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value')); + } else { + data = dataElement.value; + } + } + + ipcRenderer.sendSync('prompt-post-data:' + promptId, data); +} + +function promptCreateInput() { + const dataElement = document.createElement('input'); + dataElement.setAttribute('type', 'text'); + + if (promptOptions.value) { + dataElement.value = promptOptions.value; + } else { + dataElement.value = ''; + } + + if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') { + for (const k in promptOptions.inputAttrs) { + if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { + continue; + } + + dataElement.setAttribute(k, promptOptions.inputAttrs[k]); + } + } + + dataElement.addEventListener('keyup', event => { + if (event.key === 'Escape') { + promptCancel(); + } + }); + + dataElement.addEventListener('keypress', event => { + if (event.key === 'Enter') { + event.preventDefault(); + document.querySelector('#ok').click(); + } + }); + + return dataElement; +} + +function promptCreateSelect() { + const dataElement = document.createElement('select'); + let optionElement; + + for (const k in promptOptions.selectOptions) { + if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) { + continue; + } + + optionElement = document.createElement('option'); + optionElement.setAttribute('value', k); + optionElement.textContent = promptOptions.selectOptions[k]; + if (k === promptOptions.value) { + optionElement.setAttribute('selected', 'selected'); + } + + dataElement.append(optionElement); + } + + return dataElement; +} + +function promptRegister() { + promptId = document.location.hash.replace('#', ''); + + try { + promptOptions = JSON.parse(ipcRenderer.sendSync('prompt-get-options:' + promptId)); + } catch (error) { + return promptError(error); + } + + if (promptOptions.useHtmlLabel) { + document.querySelector('#label').innerHTML = promptOptions.label; + } else { + document.querySelector('#label').textContent = promptOptions.label; + } + + if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { + document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok; + } + + if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { + document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel; + } + + if (promptOptions.customStylesheet) { + try { + const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); + if (customStyleContent) { + const customStyle = document.createElement('style'); + customStyle.setAttribute('rel', 'stylesheet'); + customStyle.append(document.createTextNode(customStyleContent)); + document.head.append(customStyle); + } + } catch (error) { + return promptError(error); + } + } + + document.querySelector('#form').addEventListener('submit', promptSubmit); + document.querySelector('#cancel').addEventListener('click', promptCancel); + + const dataContainerElement = document.querySelector('#data-container'); + + let dataElement; + if (promptOptions.type === 'input') { + dataElement = promptCreateInput(); + } else if (promptOptions.type === 'select') { + dataElement = promptCreateSelect(); + } else { + return promptError(`Unhandled input type '${promptOptions.type}'`); + } + + dataContainerElement.append(dataElement); + dataElement.setAttribute('id', 'data'); + + dataElement.focus(); + if (promptOptions.type === 'input') { + dataElement.select(); + } + + if (promptOptions.customScript) { + try { + const customScript = require(promptOptions.customScript); + customScript(); + } catch (error) { + return promptError(error); + } + } +} + +window.addEventListener('error', error => { + if (promptId) { + promptError('An error has occured on the prompt window: \n' + error); + } +}); + +docReady(promptRegister); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b029d432..f9909214 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3093,13 +3093,6 @@ electron-localshortcut@^3.1.0, electron-localshortcut@^3.2.1: keyboardevent-from-electron-accelerator "^2.0.0" keyboardevents-areequal "^0.2.1" -electron-prompt@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/electron-prompt/-/electron-prompt-1.6.2.tgz#e26bd3d359120bd45ce2f9625bdc380deb7ee269" - integrity sha512-gC9ZpMopIgz1kW92J7UfsXarkLdPH2zhr77bnJFlDSduYjRlr9nEPm4ux1vyzJsO72AdOyvuPPIQB0j+gWsncQ== - dependencies: - doc-ready "^1.0.4" - electron-publish@22.9.1: version "22.9.1" resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.9.1.tgz#7cc76ac4cc53efd29ee31c1e5facb9724329068e" From 73b0ddc2cecb08509a17bef4d222e8f5b183599e Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 21:25:41 +0300 Subject: [PATCH 16/52] css tweaks --- prompt/customTitlebar.js | 7 +++++++ prompt/darkPrompt.css | 17 +++++++++-------- prompt/page/prompt.css | 8 +++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/prompt/customTitlebar.js b/prompt/customTitlebar.js index 203828d4..5dc942cf 100644 --- a/prompt/customTitlebar.js +++ b/prompt/customTitlebar.js @@ -3,10 +3,17 @@ const customTitlebar = require("custom-electron-titlebar"); module.exports = () => { const bar = new customTitlebar.Titlebar({ backgroundColor: customTitlebar.Color.fromHex("#050505"), + minimizable: false, + maximizable: false, + unfocusEffect: true, }); try { bar.updateMenu(null); } catch (e) { //will always throw type error - null isn't menu, but it works } + let container = document.querySelector('#container'); + container.style.width = '100%'; + container.style.position = 'fixed'; + container.style.border = 'unset'; } \ No newline at end of file diff --git a/prompt/darkPrompt.css b/prompt/darkPrompt.css index 3bdc5647..311c42b5 100644 --- a/prompt/darkPrompt.css +++ b/prompt/darkPrompt.css @@ -1,15 +1,10 @@ + body { background-color: rgba(0, 0, 0, 0.3); background-image: linear-gradient(315deg, #200000 0%, #13253a 74%); - overflow: hidden; color:whitesmoke; } -::-webkit-scrollbar { - width: 0 !important; - display: none; -} - #label { text-align: center; } @@ -30,14 +25,20 @@ body { border: 1px solid rgb(54, 54, 54); } +#data:hover { + border: 1px solid rgb(85, 85, 85); +} + #data:focus { outline: unset; border: 1px solid rgb(85, 85, 85); } - #ok:focus, #cancel:focus { - outline: rgba(30, 0, 0, 0.3) solid 1px; + #ok:hover, #cancel:hover { + outline: rgba(60, 0, 0, 0.4) solid 2px; + /* border: 1px solid rgba(60, 0, 0, 0.4); + */ } #ok, #cancel { diff --git a/prompt/page/prompt.css b/prompt/page/prompt.css index b9d37777..b18d1fc0 100644 --- a/prompt/page/prompt.css +++ b/prompt/page/prompt.css @@ -3,6 +3,12 @@ body { line-height: 1.5em; color: #333; background-color: #fff; + overflow-y: hidden; +} + +::-webkit-scrollbar { + width: 0 !important; + display: none; } #container { @@ -10,7 +16,7 @@ body { justify-content: center; display: flex; height: 100%; - overflow: auto; + overflow-y: hidden; } #form { From 10e29090d8318555d6ca7860d2ce00d9fcc3ce9e Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 4 Apr 2021 21:52:16 +0300 Subject: [PATCH 17/52] stylecheck --- menu.js | 41 ++++++++----------- prompt/customTitlebar.js | 4 +- prompt/darkPrompt.css | 52 ++++++++++++------------ prompt/page/prompt.css | 88 ++++++++++++++++++++-------------------- 4 files changed, 90 insertions(+), 95 deletions(-) diff --git a/menu.js b/menu.js index 0312778f..18764681 100644 --- a/menu.js +++ b/menu.js @@ -148,7 +148,7 @@ const mainMenuTemplate = (win) => [ type: "checkbox", checked: !!config.get("options.proxy"), click: (item) => { - setProxy(item); + setProxy(item, win); } }, { @@ -311,36 +311,31 @@ module.exports.setApplicationMenu = (win) => { const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); const example = `Example: "socks5://127.0.0.1:9999"`; -function setProxy(item) { +function setProxy(item, win) { let options = { title: 'Set Proxy', - label: 'Enter Proxy Address (leave empty to disable)', + label: 'Enter Proxy Address: (leave empty to disable)', value: config.get("options.proxy") || example, inputAttrs: { type: 'text' }, type: 'input', - alwaysOnTop: true, icon: iconPath, - customStylesheet: path.join(__dirname, "prompt","darkPrompt.css"), + customStylesheet: path.join(__dirname, "prompt", "darkPrompt.css"), + frame: false, + customScript: path.join(__dirname, "prompt", "customTitlebar.js"), + enableRemoteModule: true, + height: 200, + width: 450, }; - if (config.plugins.isEnabled("in-app-menu")) { - Object.assign(options, { - frame: false, - customScript: path.join(__dirname, "prompt","customTitlebar.js"), - enableRemoteModule: true, - height: 200, - width: 450, + prompt(options, win) + .then((input) => { + if (input !== null && input !== example) { + config.set("options.proxy", input); + item.checked = (input === "") ? false : true; + } else { //user pressed cancel + item.checked = !item.checked; //reset checkbox + } }) - } - prompt(options) - .then((input) => { - if(input !== null && input !== example) { - config.set("options.proxy", input); - item.checked = (input === "") ? false : true; - } else { //user pressed cancel - item.checked = !item.checked; //reset checkbox - } - }) - .catch(console.error); + .catch(console.error); } \ No newline at end of file diff --git a/prompt/customTitlebar.js b/prompt/customTitlebar.js index 5dc942cf..116aa56c 100644 --- a/prompt/customTitlebar.js +++ b/prompt/customTitlebar.js @@ -2,11 +2,11 @@ const customTitlebar = require("custom-electron-titlebar"); module.exports = () => { const bar = new customTitlebar.Titlebar({ - backgroundColor: customTitlebar.Color.fromHex("#050505"), + backgroundColor: customTitlebar.Color.fromHex("#050505"), minimizable: false, maximizable: false, unfocusEffect: true, - }); + }); try { bar.updateMenu(null); } catch (e) { diff --git a/prompt/darkPrompt.css b/prompt/darkPrompt.css index 311c42b5..8b343a25 100644 --- a/prompt/darkPrompt.css +++ b/prompt/darkPrompt.css @@ -1,47 +1,45 @@ - body { - background-color: rgba(0, 0, 0, 0.3); - background-image: linear-gradient(315deg, #200000 0%, #13253a 74%); - color:whitesmoke; + background-color: rgba(0, 0, 0, 0.3); + background-image: linear-gradient(315deg, #200000 0%, #13253a 74%); + color: whitesmoke; } #label { - text-align: center; + text-align: center; } #container { - background: rgba( 0, 0, 0, 0.7 ); - box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 ); - backdrop-filter: blur( 10.0px ); - -webkit-backdrop-filter: blur( 10.0px ); - border-radius: 10px; - border: 1px solid rgba(80, 0, 0, 0.4); - overflow: hidden; + background: rgba(0, 0, 0, 0.7); + box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-radius: 10px; + border: 1px solid rgba(80, 0, 0, 0.4); + overflow: hidden; } #data { - background: unset; - color: whitesmoke; - border: 1px solid rgb(54, 54, 54); + background: unset; + color: whitesmoke; + border: 1px solid rgb(54, 54, 54); } #data:hover { - border: 1px solid rgb(85, 85, 85); + border: 1px solid rgb(85, 85, 85); } #data:focus { - outline: unset; - border: 1px solid rgb(85, 85, 85); + outline: unset; + border: 1px solid rgb(85, 85, 85); } - #ok:hover, #cancel:hover { - outline: rgba(60, 0, 0, 0.4) solid 2px; - /* - border: 1px solid rgba(60, 0, 0, 0.4); - */ +#ok:hover, +#cancel:hover { + outline: rgba(60, 0, 0, 0.4) solid 2px; } -#ok, #cancel { - background-color: rgb(0, 0, 0); - color: whitesmoke; -} \ No newline at end of file +#ok, +#cancel { + background-color: rgb(0, 0, 0); + color: whitesmoke; +} diff --git a/prompt/page/prompt.css b/prompt/page/prompt.css index b18d1fc0..c3530e57 100644 --- a/prompt/page/prompt.css +++ b/prompt/page/prompt.css @@ -1,78 +1,80 @@ body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif; - line-height: 1.5em; - color: #333; - background-color: #fff; - overflow-y: hidden; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, + sans-serif; + line-height: 1.5em; + color: #333; + background-color: #fff; + overflow-y: hidden; } ::-webkit-scrollbar { - width: 0 !important; - display: none; + width: 0 !important; + display: none; } #container { - align-items: center; - justify-content: center; - display: flex; - height: 100%; - overflow-y: hidden; + align-items: center; + justify-content: center; + display: flex; + height: 100%; + overflow-y: hidden; } #form { - width: 100%; - padding-top: .5em; + width: 100%; + padding-top: 0.5em; } #label { - max-width: 100%; - max-height: 100%; - margin-bottom: .8em; - padding: 0 .5em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + max-width: 100%; + max-height: 100%; + margin-bottom: 0.8em; + padding: 0 0.5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } #data { - border-radius: 2px; - background: #fff; - width: 90%; - padding: .4em .5em; - border: 1px solid black; - min-height: 2em; - margin: 0 0 1.2em; + border-radius: 2px; + background: #fff; + width: 90%; + padding: 0.4em 0.5em; + border: 1px solid black; + min-height: 2em; + margin: 0 0 1.2em; } select#data { - height: 2em; + height: 2em; } #data-container { - text-align: center; + text-align: center; } #buttons { - text-align: right; - padding: 0 .5em 0 0; + text-align: right; + padding: 0 0.5em 0 0; } #buttons > button, -#buttons > input[type=submit] { - border-radius: 2px; - border: 0; - margin: 0 0 0 .5em; - font-size: .8em; - line-height: 1em; - padding: .6em 1em +#buttons > input[type="submit"] { + border-radius: 2px; + border: 0; + margin: 0 0 0 0.5em; + font-size: 0.8em; + line-height: 1em; + padding: 0.6em 1em; } #ok { - background-color: #3879D9; - color: white; + background-color: #3879d9; + color: white; } #cancel { - background-color: #DDD; - color: black; + background-color: #ddd; + color: black; } From 28d366ab19cdf85fdad4537db9762e40c86d4da6 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 03:04:06 +0300 Subject: [PATCH 18/52] add back-to-front logger This somehow fix "did-fail-load" being called on song start, with in-app-plugin-activated --- index.js | 23 +++++++++++++++++++++-- preload.js | 4 ++++ providers/logger.js | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 providers/logger.js diff --git a/index.js b/index.js index baa8c49c..97f4dcb5 100644 --- a/index.js +++ b/index.js @@ -163,10 +163,29 @@ app.on("browser-window-created", (event, win) => { createdWindow = true; loadPlugins(win); - win.webContents.on("did-fail-load", () => { + win.webContents.on("did-fail-load", ( + event, + errorCode, + errorDescription, + validatedURL, + isMainFrame, + frameProcessId, + frameRoutingId, + ) => { + let log = { + error: "did-fail-load", + event, + errorCode, + errorDescription, + validatedURL, + isMainFrame, + frameProcessId, + frameRoutingId, + }; if (is.dev()) { - console.log("did fail load"); + console.log(log); } + win.webContents.send("log", log); win.webContents.loadFile(path.join(__dirname, "error.html")); }); diff --git a/preload.js b/preload.js index 94f2a72a..33e67306 100644 --- a/preload.js +++ b/preload.js @@ -32,6 +32,10 @@ document.addEventListener("DOMContentLoaded", () => { // inject song-info provider const songInfoProviderPath = path.join(__dirname, "providers", "song-info-front.js") fileExists(songInfoProviderPath, require(songInfoProviderPath)); + + // inject front logger + const loggerPath = path.join(__dirname, "providers", "logger.js") + fileExists(loggerPath, require(loggerPath)); // Add action for reloading global.reload = () => diff --git a/providers/logger.js b/providers/logger.js new file mode 100644 index 00000000..84163c52 --- /dev/null +++ b/providers/logger.js @@ -0,0 +1,14 @@ +const { ipcRenderer } = require("electron"); + +module.exports = () => { + ipcRenderer.on("log", (event, log) => { + let string = log.toString() || log; + if (string) { + console.log(string); + } else { + for (let propery of log) { + console.log(propery.toString() || propery); + } + } + }) +} \ No newline at end of file From 11bd1adbd406160b9ee4215fa0b329b5ecd4391d Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 03:25:45 +0300 Subject: [PATCH 19/52] stylecheck --- index.js | 32 ++++++++++++++++---------------- preload.js | 2 +- providers/logger.js | 18 +++++++++--------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index 97f4dcb5..9e8b7233 100644 --- a/index.js +++ b/index.js @@ -37,8 +37,8 @@ if (config.get("options.proxy")) { } // Adds debug features like hotkeys for triggering dev tools and reload -require("electron-debug")({ - showDevTools: false //disable dev tools on electron-prompt +require("electron-debug")({ + showDevTools: false //disable automatic devTools on new window }); // Prevent window being garbage collected @@ -100,18 +100,18 @@ function createMainWindow() { affinity: "main-window", // main window, and addition windows should work in one process ...(isTesting() ? { - // Only necessary when testing with Spectron - contextIsolation: false, - nodeIntegration: true, - } + // Only necessary when testing with Spectron + contextIsolation: false, + nodeIntegration: true, + } : undefined), }, frame: !is.macOS() && !useInlineMenu, titleBarStyle: useInlineMenu ? "hidden" : is.macOS() - ? "hiddenInset" - : "default", + ? "hiddenInset" + : "default", autoHideMenuBar: config.get("options.hideMenu"), }); if (windowPosition) { @@ -157,20 +157,20 @@ function createMainWindow() { let createdWindow = false; app.on("browser-window-created", (event, win) => { //Ensure listeners aren't registered when creating input dialog - if(createdWindow){ + if (createdWindow) { return; } createdWindow = true; loadPlugins(win); win.webContents.on("did-fail-load", ( - event, - errorCode, - errorDescription, - validatedURL, - isMainFrame, - frameProcessId, - frameRoutingId, + event, + errorCode, + errorDescription, + validatedURL, + isMainFrame, + frameProcessId, + frameRoutingId, ) => { let log = { error: "did-fail-load", diff --git a/preload.js b/preload.js index 33e67306..f25dd6b7 100644 --- a/preload.js +++ b/preload.js @@ -36,7 +36,7 @@ document.addEventListener("DOMContentLoaded", () => { // inject front logger const loggerPath = path.join(__dirname, "providers", "logger.js") fileExists(loggerPath, require(loggerPath)); - + // Add action for reloading global.reload = () => remote.getCurrentWindow().webContents.loadURL(config.get("url")); diff --git a/providers/logger.js b/providers/logger.js index 84163c52..560b696b 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -2,13 +2,13 @@ const { ipcRenderer } = require("electron"); module.exports = () => { ipcRenderer.on("log", (event, log) => { - let string = log.toString() || log; - if (string) { - console.log(string); - } else { - for (let propery of log) { - console.log(propery.toString() || propery); - } - } - }) + let string = log.toString() || log; + if (string) { + console.log(string); + } else { + for (let propery of log) { + console.log(propery.toString() || propery); + } + } + }) } \ No newline at end of file From 6472002f8a441f21a746b72fc876433698b19190 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:04:29 +0300 Subject: [PATCH 20/52] fix hide-menu timing + minify log function --- plugins/in-app-menu/back.js | 35 ++++++++++++++++++++++------------- providers/logger.js | 13 +++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 53bb9f8d..5c17317a 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -8,7 +8,9 @@ const { setApplicationMenu } = require("../../menu"); const { injectCSS } = require("../utils"); //check that menu doesn't get created twice -let done = false; +let calledReadyToShow = false; +//check menu state isn't changed twice +let calledFinishedLoad = false //tracks menu visibility let visible = true; // win hook for fixing menu @@ -32,42 +34,49 @@ module.exports = (winImport) => { win.on("ready-to-show", () => { // (apparently ready-to-show is called twice) - if (done) { + if (calledReadyToShow) { return; } - done = true; + calledReadyToShow = true; setApplicationMenu(win); //register keyboard shortcut && hide menu if hideMenu is enabled if (config.get("options.hideMenu")) { - switchMenuVisibility(); electronLocalshortcut.register(win, "Esc", () => { switchMenuVisibility(); }); - } - // fix bug with menu not applying on start when no internet connection available - setMenuVisibility(visible); + } }); + + //set menu visibility on load + win.webContents.on("did-finish-load", () => { + if (calledFinishedLoad) { + return; + } + calledFinishedLoad = true; + // fix bug with menu not applying on start when no internet connection available + setMenuVisibility(!config.get("options.hideMenu")); + }) }; function switchMenuVisibility() { setMenuVisibility(!visible); } -function setMenuVisibility(value){ +function setMenuVisibility(value) { visible = value; win.webContents.send("updateMenu", visible); } function updateCheckboxesAndRadioButtons(item, isRadio, hasSubmenu) { if (!isRadio) { - //fix checkbox - item.checked = !item.checked; - } + //fix checkbox + item.checked = !item.checked; + } //update menu if radio / hasSubmenu if (isRadio || hasSubmenu) { - win.webContents.send("updateMenu", true); + win.webContents.send("updateMenu", true); } } @@ -79,7 +88,7 @@ function updateTemplate(template) { let originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - updateCheckboxesAndRadioButtons(itemClicked, item.type==='radio', item.hasSubmenu); + updateCheckboxesAndRadioButtons(itemClicked, item.type === 'radio', item.hasSubmenu); }; item.fixed = true; } diff --git a/providers/logger.js b/providers/logger.js index 560b696b..dc91f87b 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -3,12 +3,9 @@ const { ipcRenderer } = require("electron"); module.exports = () => { ipcRenderer.on("log", (event, log) => { let string = log.toString() || log; - if (string) { - console.log(string); - } else { - for (let propery of log) { - console.log(propery.toString() || propery); - } - } + if (!string || string === "[object Object]") { + string = JSON.stringify(log); + } + console.log(string); }) -} \ No newline at end of file +}; \ No newline at end of file From 106e461beb5a7cc4a92f8970acd041cd1b09b8f9 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:08:55 +0300 Subject: [PATCH 21/52] fix typo --- plugins/in-app-menu/back.js | 4 ---- providers/logger.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 5c17317a..b17ee032 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -51,10 +51,6 @@ module.exports = (winImport) => { //set menu visibility on load win.webContents.on("did-finish-load", () => { - if (calledFinishedLoad) { - return; - } - calledFinishedLoad = true; // fix bug with menu not applying on start when no internet connection available setMenuVisibility(!config.get("options.hideMenu")); }) diff --git a/providers/logger.js b/providers/logger.js index dc91f87b..08a940f3 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -2,7 +2,7 @@ const { ipcRenderer } = require("electron"); module.exports = () => { ipcRenderer.on("log", (event, log) => { - let string = log.toString() || log; + let string = log || log.toString(); if (!string || string === "[object Object]") { string = JSON.stringify(log); } From 6d44a579a44f4420e6ab60e2c6655f7bcf2e429e Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:21:52 +0300 Subject: [PATCH 22/52] move prompt to provide --- menu.js | 19 ++++++++++++------- .../prompt}/customTitlebar.js | 0 {prompt => providers/prompt}/darkPrompt.css | 0 {prompt => providers/prompt}/index.js | 0 {prompt => providers/prompt}/page/prompt.css | 0 {prompt => providers/prompt}/page/prompt.html | 0 {prompt => providers/prompt}/page/prompt.js | 0 7 files changed, 12 insertions(+), 7 deletions(-) rename {prompt => providers/prompt}/customTitlebar.js (100%) rename {prompt => providers/prompt}/darkPrompt.css (100%) rename {prompt => providers/prompt}/index.js (100%) rename {prompt => providers/prompt}/page/prompt.css (100%) rename {prompt => providers/prompt}/page/prompt.html (100%) rename {prompt => providers/prompt}/page/prompt.js (100%) diff --git a/menu.js b/menu.js index 18764681..3754d14c 100644 --- a/menu.js +++ b/menu.js @@ -6,7 +6,7 @@ const is = require("electron-is"); const { getAllPlugins } = require("./plugins/utils"); const config = require("./config"); -const prompt = require('./prompt'); +const prompt = require('./providers/prompt'); const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, @@ -321,13 +321,18 @@ function setProxy(item, win) { }, type: 'input', icon: iconPath, - customStylesheet: path.join(__dirname, "prompt", "darkPrompt.css"), - frame: false, - customScript: path.join(__dirname, "prompt", "customTitlebar.js"), - enableRemoteModule: true, - height: 200, - width: 450, + customStylesheet: path.join(__dirname, "providers", "prompt", "darkPrompt.css"), }; + //TODO: custom bar on prompt need testing on macOS + if(!is.macOS()) { + Object.assign(options, { + frame: false, + customScript: path.join(__dirname, "providers", "prompt", "customTitlebar.js"), + enableRemoteModule: true, + height: 200, + width: 450, + }); + } prompt(options, win) .then((input) => { if (input !== null && input !== example) { diff --git a/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js similarity index 100% rename from prompt/customTitlebar.js rename to providers/prompt/customTitlebar.js diff --git a/prompt/darkPrompt.css b/providers/prompt/darkPrompt.css similarity index 100% rename from prompt/darkPrompt.css rename to providers/prompt/darkPrompt.css diff --git a/prompt/index.js b/providers/prompt/index.js similarity index 100% rename from prompt/index.js rename to providers/prompt/index.js diff --git a/prompt/page/prompt.css b/providers/prompt/page/prompt.css similarity index 100% rename from prompt/page/prompt.css rename to providers/prompt/page/prompt.css diff --git a/prompt/page/prompt.html b/providers/prompt/page/prompt.html similarity index 100% rename from prompt/page/prompt.html rename to providers/prompt/page/prompt.html diff --git a/prompt/page/prompt.js b/providers/prompt/page/prompt.js similarity index 100% rename from prompt/page/prompt.js rename to providers/prompt/page/prompt.js From 25d0f50f3da69c2f59b27f5640d54315202e4c0b Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:52:22 +0300 Subject: [PATCH 23/52] Create readme.md --- providers/prompt/readme.md | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 providers/prompt/readme.md diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md new file mode 100644 index 00000000..2eb13b74 --- /dev/null +++ b/providers/prompt/readme.md @@ -0,0 +1,76 @@ +# Prompt Documentation + +

prompt-preview

+ +## Usage + +```js +prompt([options, parentBrowserWindow]).then(...).catch(...) +``` + +## Example + +```js +const prompt = require('./providers/prompt'); + +prompt({ + title: 'Prompt example', + label: 'URL:', + value: 'http://example.org', + inputAttrs: { + type: 'url' + }, + type: 'input' +}) +.then((r) => { + if(r === null) { + console.log('user cancelled'); + } else { + console.log('result', r); + } +}) +.catch(console.error); +``` + +### Options object (optional) + +| Key | Explanation | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| title | (optional, string) The title of the prompt window. Defaults to 'Prompt'. | +| label | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. | +| buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. | +| value | (optional, string) The default value for the input field. Defaults to null. | +| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input. Defaults to 'input'. | +| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' | +| selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | +| useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | +| width | (optional, integer) The width of the prompt window. Defaults to 370. | +| minWidth | (optional, integer) The minimum allowed width for the prompt window. Same default value as width. | +| height | (optional, integer) The height of the prompt window. Defaults to 130. | +| minHeight | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. | +| resizable | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. | +| alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false | +| icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. | +| customStylesheet | (optional, string) The local path of a CSS file to stylize the prompt window. Defaults to null. | +| menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. | +| skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. | +| frame | (optional, boolean) Wether to create prompt with frame. Defaults to true. | +| customScript | (optional, string) The local path of a JS file to run on preload. Defaults to null. | +| enableRemoteModule | (optional, boolean) Wether the prompt window have remote modules activated, Defaults to false. | + +If not supplied, it uses the defaults listed in the table above. + +### parentBrowserWindow (optional) + +The window in which to display the prompt on. If not supplied, the parent window of the prompt will be null. + +### customScript (optional) + +Create the script with the following template: + +```node +module.exports = () => { + // This function will be called as a preload script + // So you can use front features like `document.querySelector` +}; +``` From a215035d0792891845eeb625ba140fd3dadfb241 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 18:28:27 +0300 Subject: [PATCH 24/52] refactor and css fix --- providers/prompt/customTitlebar.js | 15 +++++---------- providers/prompt/darkPrompt.css | 4 +++- providers/prompt/index.js | 6 +++--- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/providers/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js index 116aa56c..6c508210 100644 --- a/providers/prompt/customTitlebar.js +++ b/providers/prompt/customTitlebar.js @@ -5,15 +5,10 @@ module.exports = () => { backgroundColor: customTitlebar.Color.fromHex("#050505"), minimizable: false, maximizable: false, - unfocusEffect: true, + menu: null }); - try { - bar.updateMenu(null); - } catch (e) { - //will always throw type error - null isn't menu, but it works - } - let container = document.querySelector('#container'); - container.style.width = '100%'; - container.style.position = 'fixed'; - container.style.border = 'unset'; + let mainStyle = document.querySelector('#container').style; + mainStyle.width = '100%'; + mainStyle.position = 'fixed'; + mainStyle.border = 'unset'; } \ No newline at end of file diff --git a/providers/prompt/darkPrompt.css b/providers/prompt/darkPrompt.css index 8b343a25..49e0f88e 100644 --- a/providers/prompt/darkPrompt.css +++ b/providers/prompt/darkPrompt.css @@ -34,7 +34,9 @@ body { } #ok:hover, -#cancel:hover { +#ok:focus, +#cancel:hover, +#cancel:focus { outline: rgba(60, 0, 0, 0.4) solid 2px; } diff --git a/providers/prompt/index.js b/providers/prompt/index.js index d6664305..0d89ebe9 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -50,9 +50,9 @@ function electronPrompt(options, parentWindow) { minWidth: options_.minWidth, minHeight: options_.minHeight, resizable: options_.resizable, - minimizable: false, - fullscreenable: false, - maximizable: false, + minimizable: !options_.skipTaskbar && !parentWindow && !options_.alwaysOnTop, + fullscreenable: options_.resizable, + maximizable: options_.resizable, parent: parentWindow, skipTaskbar: options_.skipTaskbar, alwaysOnTop: options_.alwaysOnTop, From 724c213af10c4ade43fb15b564fbb12123672ffb Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:18:54 +0300 Subject: [PATCH 25/52] xo --fix --- menu.js | 4 +- package.json | 12 ++- plugins/in-app-menu/back.js | 9 +- providers/prompt/custom-titlebar.js | 14 +++ providers/prompt/customTitlebar.js | 14 --- .../{darkPrompt.css => dark-prompt.css} | 0 providers/prompt/index.js | 30 +++---- providers/prompt/page/prompt.js | 88 +++++++++---------- 8 files changed, 89 insertions(+), 82 deletions(-) create mode 100644 providers/prompt/custom-titlebar.js delete mode 100644 providers/prompt/customTitlebar.js rename providers/prompt/{darkPrompt.css => dark-prompt.css} (100%) diff --git a/menu.js b/menu.js index 3754d14c..c0495d68 100644 --- a/menu.js +++ b/menu.js @@ -321,13 +321,13 @@ function setProxy(item, win) { }, type: 'input', icon: iconPath, - customStylesheet: path.join(__dirname, "providers", "prompt", "darkPrompt.css"), + customStylesheet: path.join(__dirname, "providers", "prompt", "dark-prompt.css"), }; //TODO: custom bar on prompt need testing on macOS if(!is.macOS()) { Object.assign(options, { frame: false, - customScript: path.join(__dirname, "providers", "prompt", "customTitlebar.js"), + customScript: path.join(__dirname, "providers", "prompt", "custom-titlebar.js"), enableRemoteModule: true, height: 200, width: 450, diff --git a/package.json b/package.json index 36591897..144ac53f 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,16 @@ "envs": [ "node", "browser" - ] + ], + "rules": { + "quotes": [ + "error", + "double", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ] + } } } diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index b17ee032..938c5475 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -9,8 +9,6 @@ const { injectCSS } = require("../utils"); //check that menu doesn't get created twice let calledReadyToShow = false; -//check menu state isn't changed twice -let calledFinishedLoad = false //tracks menu visibility let visible = true; // win hook for fixing menu @@ -37,6 +35,7 @@ module.exports = (winImport) => { if (calledReadyToShow) { return; } + calledReadyToShow = true; setApplicationMenu(win); @@ -53,7 +52,7 @@ module.exports = (winImport) => { win.webContents.on("did-finish-load", () => { // fix bug with menu not applying on start when no internet connection available setMenuVisibility(!config.get("options.hideMenu")); - }) + }); }; function switchMenuVisibility() { @@ -81,10 +80,10 @@ function updateTemplate(template) { for (let item of template) { // Change onClick of checkbox+radio if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) { - let originalOnclick = item.click; + const originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - updateCheckboxesAndRadioButtons(itemClicked, item.type === 'radio', item.hasSubmenu); + updateCheckboxesAndRadioButtons(itemClicked, item.type === "radio", item.hasSubmenu); }; item.fixed = true; } diff --git a/providers/prompt/custom-titlebar.js b/providers/prompt/custom-titlebar.js new file mode 100644 index 00000000..c36ce5f5 --- /dev/null +++ b/providers/prompt/custom-titlebar.js @@ -0,0 +1,14 @@ +const customTitlebar = require("custom-electron-titlebar"); + +module.exports = () => { + const bar = new customTitlebar.Titlebar({ + backgroundColor: customTitlebar.Color.fromHex("#050505"), + minimizable: false, + maximizable: false, + menu: null + }); + const mainStyle = document.querySelector("#container").style; + mainStyle.width = "100%"; + mainStyle.position = "fixed"; + mainStyle.border = "unset"; +}; diff --git a/providers/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js deleted file mode 100644 index 6c508210..00000000 --- a/providers/prompt/customTitlebar.js +++ /dev/null @@ -1,14 +0,0 @@ -const customTitlebar = require("custom-electron-titlebar"); - -module.exports = () => { - const bar = new customTitlebar.Titlebar({ - backgroundColor: customTitlebar.Color.fromHex("#050505"), - minimizable: false, - maximizable: false, - menu: null - }); - let mainStyle = document.querySelector('#container').style; - mainStyle.width = '100%'; - mainStyle.position = 'fixed'; - mainStyle.border = 'unset'; -} \ No newline at end of file diff --git a/providers/prompt/darkPrompt.css b/providers/prompt/dark-prompt.css similarity index 100% rename from providers/prompt/darkPrompt.css rename to providers/prompt/dark-prompt.css diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 0d89ebe9..302eeea0 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -1,9 +1,9 @@ -const electron = require('electron'); +const electron = require("electron"); const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow; const ipcMain = electron.ipcMain || electron.remote.ipcMain; -const url = require('url'); -const path = require('path'); +const url = require("url"); +const path = require("path"); const DEFAULT_WIDTH = 370; const DEFAULT_HEIGHT = 160; @@ -38,7 +38,7 @@ function electronPrompt(options, parentWindow) { options || {} ); - if (options_.type === 'select' && (options_.selectOptions === null || typeof options_.selectOptions !== 'object')) { + if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { reject(new Error('"selectOptions" must be an object')); return; } @@ -75,9 +75,9 @@ function electronPrompt(options, parentWindow) { }; const cleanup = () => { - ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener); - ipcMain.removeListener('prompt-post-data:' + id, postDataListener); - ipcMain.removeListener('prompt-error:' + id, errorListener); + ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); + ipcMain.removeListener("prompt-post-data:" + id, postDataListener); + ipcMain.removeListener("prompt-error:" + id, errorListener); if (promptWindow) { promptWindow.close(); @@ -92,7 +92,7 @@ function electronPrompt(options, parentWindow) { }; const unresponsiveListener = () => { - reject(new Error('Window was unresponsive')); + reject(new Error("Window was unresponsive")); cleanup(); }; @@ -102,21 +102,21 @@ function electronPrompt(options, parentWindow) { cleanup(); }; - ipcMain.on('prompt-get-options:' + id, getOptionsListener); - ipcMain.on('prompt-post-data:' + id, postDataListener); - ipcMain.on('prompt-error:' + id, errorListener); - promptWindow.on('unresponsive', unresponsiveListener); + ipcMain.on("prompt-get-options:" + id, getOptionsListener); + ipcMain.on("prompt-post-data:" + id, postDataListener); + ipcMain.on("prompt-error:" + id, errorListener); + promptWindow.on("unresponsive", unresponsiveListener); - promptWindow.on('closed', () => { + promptWindow.on("closed", () => { promptWindow = null; cleanup(); resolve(null); }); const promptUrl = url.format({ - protocol: 'file', + protocol: "file", slashes: true, - pathname: path.join(__dirname, 'page', 'prompt.html'), + pathname: path.join(__dirname, "page", "prompt.html"), hash: id }); diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 754d0e74..67acd31d 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const {ipcRenderer} = require('electron'); -const docReady = require('doc-ready'); +const fs = require("fs"); +const {ipcRenderer} = require("electron"); +const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; @@ -10,41 +10,39 @@ function promptError(error) { error = error.message; } - ipcRenderer.sendSync('prompt-error:' + promptId, error); + ipcRenderer.sendSync("prompt-error:" + promptId, error); } function promptCancel() { - ipcRenderer.sendSync('prompt-post-data:' + promptId, null); + ipcRenderer.sendSync("prompt-post-data:" + promptId, null); } function promptSubmit() { - const dataElement = document.querySelector('#data'); + const dataElement = document.querySelector("#data"); let data = null; - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { data = dataElement.value; - } else if (promptOptions.type === 'select') { - if (promptOptions.selectMultiple) { - data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value')); - } else { - data = dataElement.value; - } + } else if (promptOptions.type === "select") { + data = promptOptions.selectMultiple ? + dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : + dataElement.value; } - ipcRenderer.sendSync('prompt-post-data:' + promptId, data); + ipcRenderer.sendSync("prompt-post-data:" + promptId, data); } function promptCreateInput() { - const dataElement = document.createElement('input'); - dataElement.setAttribute('type', 'text'); + const dataElement = document.createElement("input"); + dataElement.setAttribute("type", "text"); if (promptOptions.value) { dataElement.value = promptOptions.value; } else { - dataElement.value = ''; + dataElement.value = ""; } - if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') { + if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { for (const k in promptOptions.inputAttrs) { if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { continue; @@ -54,16 +52,16 @@ function promptCreateInput() { } } - dataElement.addEventListener('keyup', event => { - if (event.key === 'Escape') { + dataElement.addEventListener("keyup", event => { + if (event.key === "Escape") { promptCancel(); } }); - dataElement.addEventListener('keypress', event => { - if (event.key === 'Enter') { + dataElement.addEventListener("keypress", event => { + if (event.key === "Enter") { event.preventDefault(); - document.querySelector('#ok').click(); + document.querySelector("#ok").click(); } }); @@ -71,7 +69,7 @@ function promptCreateInput() { } function promptCreateSelect() { - const dataElement = document.createElement('select'); + const dataElement = document.createElement("select"); let optionElement; for (const k in promptOptions.selectOptions) { @@ -79,11 +77,11 @@ function promptCreateSelect() { continue; } - optionElement = document.createElement('option'); - optionElement.setAttribute('value', k); + optionElement = document.createElement("option"); + optionElement.setAttribute("value", k); optionElement.textContent = promptOptions.selectOptions[k]; if (k === promptOptions.value) { - optionElement.setAttribute('selected', 'selected'); + optionElement.setAttribute("selected", "selected"); } dataElement.append(optionElement); @@ -93,34 +91,34 @@ function promptCreateSelect() { } function promptRegister() { - promptId = document.location.hash.replace('#', ''); + promptId = document.location.hash.replace("#", ""); try { - promptOptions = JSON.parse(ipcRenderer.sendSync('prompt-get-options:' + promptId)); + promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); } catch (error) { return promptError(error); } if (promptOptions.useHtmlLabel) { - document.querySelector('#label').innerHTML = promptOptions.label; + document.querySelector("#label").innerHTML = promptOptions.label; } else { - document.querySelector('#label').textContent = promptOptions.label; + document.querySelector("#label").textContent = promptOptions.label; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok; + document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel; + document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; } if (promptOptions.customStylesheet) { try { const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); if (customStyleContent) { - const customStyle = document.createElement('style'); - customStyle.setAttribute('rel', 'stylesheet'); + const customStyle = document.createElement("style"); + customStyle.setAttribute("rel", "stylesheet"); customStyle.append(document.createTextNode(customStyleContent)); document.head.append(customStyle); } @@ -129,25 +127,25 @@ function promptRegister() { } } - document.querySelector('#form').addEventListener('submit', promptSubmit); - document.querySelector('#cancel').addEventListener('click', promptCancel); + document.querySelector("#form").addEventListener("submit", promptSubmit); + document.querySelector("#cancel").addEventListener("click", promptCancel); - const dataContainerElement = document.querySelector('#data-container'); + const dataContainerElement = document.querySelector("#data-container"); let dataElement; - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement = promptCreateInput(); - } else if (promptOptions.type === 'select') { + } else if (promptOptions.type === "select") { dataElement = promptCreateSelect(); } else { return promptError(`Unhandled input type '${promptOptions.type}'`); } dataContainerElement.append(dataElement); - dataElement.setAttribute('id', 'data'); + dataElement.setAttribute("id", "data"); dataElement.focus(); - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement.select(); } @@ -161,10 +159,10 @@ function promptRegister() { } } -window.addEventListener('error', error => { +window.addEventListener("error", error => { if (promptId) { - promptError('An error has occured on the prompt window: \n' + error); + promptError("An error has occured on the prompt window: \n" + error); } }); -docReady(promptRegister); \ No newline at end of file +docReady(promptRegister); From 0e9b15722a02fc1e423fb0d5eec4cd969d397dae Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:30:47 +0300 Subject: [PATCH 26/52] add inline doc --- index.js | 18 ++-- menu.js | 1 + providers/logger.js | 20 ++-- providers/prompt/index.js | 41 +++++--- providers/prompt/page/prompt.js | 168 ++++++++++++++++++-------------- providers/prompt/readme.md | 5 +- 6 files changed, 148 insertions(+), 105 deletions(-) diff --git a/index.js b/index.js index 9e8b7233..7fd88754 100644 --- a/index.js +++ b/index.js @@ -110,8 +110,8 @@ function createMainWindow() { titleBarStyle: useInlineMenu ? "hidden" : is.macOS() - ? "hiddenInset" - : "default", + ? "hiddenInset" + : "default", autoHideMenuBar: config.get("options.hideMenu"), }); if (windowPosition) { @@ -156,7 +156,7 @@ function createMainWindow() { let createdWindow = false; app.on("browser-window-created", (event, win) => { - //Ensure listeners aren't registered when creating input dialog + //Ensures listeners are registered only once if (createdWindow) { return; } @@ -172,7 +172,7 @@ app.on("browser-window-created", (event, win) => { frameProcessId, frameRoutingId, ) => { - let log = { + const log = { error: "did-fail-load", event, errorCode, @@ -183,7 +183,7 @@ app.on("browser-window-created", (event, win) => { frameRoutingId, }; if (is.dev()) { - console.log(log); + console.log(log.toString()); } win.webContents.send("log", log); win.webContents.loadFile(path.join(__dirname, "error.html")); @@ -306,13 +306,11 @@ app.on("ready", () => { } // Optimized for Mac OS X - if (is.macOS()) { - if (!config.get("options.appVisible")) { - app.dock.hide(); - } + if (is.macOS() && !config.get("options.appVisible")) { + app.dock.hide(); } - var forceQuit = false; + let forceQuit = false; app.on("before-quit", () => { forceQuit = true; }); diff --git a/menu.js b/menu.js index c0495d68..21ac1e7a 100644 --- a/menu.js +++ b/menu.js @@ -12,6 +12,7 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, type: "checkbox", checked: config.plugins.isEnabled(plugin), + //Submenu check used in in-app-menu hasSubmenu: hasSubmenu || undefined, click: (item) => { if (item.checked) { diff --git a/providers/logger.js b/providers/logger.js index 08a940f3..730a3dfa 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -1,11 +1,15 @@ const { ipcRenderer } = require("electron"); +function logToString(log) { + let string = (typeof log === "string") ? log : log.toString(); + if (!string || string.includes("[object Object]")) { + string = JSON.stringify(log); + } + return string; +} + module.exports = () => { - ipcRenderer.on("log", (event, log) => { - let string = log || log.toString(); - if (!string || string === "[object Object]") { - string = JSON.stringify(log); - } - console.log(string); - }) -}; \ No newline at end of file + ipcRenderer.on("log", (event, log) => { + console.log(logToString(log)); + }); +}; diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 302eeea0..bbba8b78 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -10,8 +10,10 @@ const DEFAULT_HEIGHT = 160; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { + //id used to ensure unique listeners per window const id = `${Date.now()}-${Math.random()}`; + //custom options override default const options_ = Object.assign( { width: DEFAULT_WIDTH, @@ -19,12 +21,12 @@ function electronPrompt(options, parentWindow) { minWidth: DEFAULT_WIDTH, minHeight: DEFAULT_HEIGHT, resizable: false, - title: 'Prompt', - label: 'Please input a value:', + title: "Prompt", + label: "Please input a value:", buttonLabels: null, alwaysOnTop: false, value: null, - type: 'input', + type: "input", selectOptions: null, icon: null, useHtmlLabel: false, @@ -70,10 +72,7 @@ function electronPrompt(options, parentWindow) { promptWindow.setMenu(null); promptWindow.setMenuBarVisibility(options_.menuBarVisible); - const getOptionsListener = event => { - event.returnValue = JSON.stringify(options_); - }; - + //called on exit const cleanup = () => { ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); ipcMain.removeListener("prompt-post-data:" + id, postDataListener); @@ -85,6 +84,12 @@ function electronPrompt(options, parentWindow) { } }; + ///transfer options to front + const getOptionsListener = event => { + event.returnValue = JSON.stringify(options_); + }; + + //get input from front const postDataListener = (event, value) => { resolve(value); event.returnValue = null; @@ -96,12 +101,14 @@ function electronPrompt(options, parentWindow) { cleanup(); }; + //get error from front const errorListener = (event, message) => { reject(new Error(message)); event.returnValue = null; cleanup(); }; + //attach listeners ipcMain.on("prompt-get-options:" + id, getOptionsListener); ipcMain.on("prompt-post-data:" + id, postDataListener); ipcMain.on("prompt-error:" + id, errorListener); @@ -113,13 +120,23 @@ function electronPrompt(options, parentWindow) { resolve(null); }); - const promptUrl = url.format({ - protocol: "file", - slashes: true, - pathname: path.join(__dirname, "page", "prompt.html"), - hash: id + //should never happen + promptWindow.webContents.on("did-fail-load", ( + event, + errorCode, + errorDescription, + validatedURL, + ) => { + const log = { + error: "did-fail-load", + errorCode, + errorDescription, + validatedURL, + }; + reject(new Error("prompt.html did-fail-load, log:\n", + log.toString())); }); + //Finally, load prompt promptWindow.loadURL(promptUrl); }); } diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 67acd31d..b7a54120 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -5,6 +5,93 @@ const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; + +docReady(promptRegister); +//start here +function promptRegister() { + //get custom session id + promptId = document.location.hash.replace("#", ""); + + //get options from back + try { + promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); + } catch (error) { + return promptError(error); + } + + //set label + if (promptOptions.useHtmlLabel) { + document.querySelector("#label").innerHTML = promptOptions.label; + } else { + document.querySelector("#label").textContent = promptOptions.label; + } + + //set button label + if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { + document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; + } + + if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { + document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; + } + + //inject custom stylesheet from options + if (promptOptions.customStylesheet) { + try { + const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); + if (customStyleContent) { + const customStyle = document.createElement("style"); + customStyle.setAttribute("rel", "stylesheet"); + customStyle.append(document.createTextNode(customStyleContent)); + document.head.append(customStyle); + } + } catch (error) { + return promptError(error); + } + } + + //add button listeners + document.querySelector("#form").addEventListener("submit", promptSubmit); + document.querySelector("#cancel").addEventListener("click", promptCancel); + + + //create input/select + const dataContainerElement = document.querySelector("#data-container"); + let dataElement; + if (promptOptions.type === "input") { + dataElement = promptCreateInput(); + } else if (promptOptions.type === "select") { + dataElement = promptCreateSelect(); + } else { + return promptError(`Unhandled input type '${promptOptions.type}'`); + } + + dataContainerElement.append(dataElement); + dataElement.setAttribute("id", "data"); + + dataElement.focus(); + if (promptOptions.type === "input") { + dataElement.select(); + } + + //load custom script from options + if (promptOptions.customScript) { + try { + const customScript = require(promptOptions.customScript); + customScript(); + } catch (error) { + return promptError(error); + } + } +} + +window.addEventListener("error", error => { + if (promptId) { + promptError("An error has occured on the prompt window: \n" + error); + } +}); + +//send error to back function promptError(error) { if (error instanceof Error) { error = error.message; @@ -13,10 +100,12 @@ function promptError(error) { ipcRenderer.sendSync("prompt-error:" + promptId, error); } +//send to back: input=null function promptCancel() { ipcRenderer.sendSync("prompt-post-data:" + promptId, null); } +//transfer input data to back function promptSubmit() { const dataElement = document.querySelector("#data"); let data = null; @@ -32,6 +121,7 @@ function promptSubmit() { ipcRenderer.sendSync("prompt-post-data:" + promptId, data); } +//creates input box function promptCreateInput() { const dataElement = document.createElement("input"); dataElement.setAttribute("type", "text"); @@ -42,6 +132,7 @@ function promptCreateInput() { dataElement.value = ""; } + //insert custom input attributes if in options if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { for (const k in promptOptions.inputAttrs) { if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { @@ -52,12 +143,14 @@ function promptCreateInput() { } } + //Cancel/Exit on 'Escape' dataElement.addEventListener("keyup", event => { if (event.key === "Escape") { promptCancel(); } }); + //Confrim on 'Enter' dataElement.addEventListener("keypress", event => { if (event.key === "Enter") { event.preventDefault(); @@ -68,6 +161,7 @@ function promptCreateInput() { return dataElement; } +//create multiple select function promptCreateSelect() { const dataElement = document.createElement("select"); let optionElement; @@ -90,79 +184,5 @@ function promptCreateSelect() { return dataElement; } -function promptRegister() { - promptId = document.location.hash.replace("#", ""); - try { - promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); - } catch (error) { - return promptError(error); - } - if (promptOptions.useHtmlLabel) { - document.querySelector("#label").innerHTML = promptOptions.label; - } else { - document.querySelector("#label").textContent = promptOptions.label; - } - - if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; - } - - if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; - } - - if (promptOptions.customStylesheet) { - try { - const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); - if (customStyleContent) { - const customStyle = document.createElement("style"); - customStyle.setAttribute("rel", "stylesheet"); - customStyle.append(document.createTextNode(customStyleContent)); - document.head.append(customStyle); - } - } catch (error) { - return promptError(error); - } - } - - document.querySelector("#form").addEventListener("submit", promptSubmit); - document.querySelector("#cancel").addEventListener("click", promptCancel); - - const dataContainerElement = document.querySelector("#data-container"); - - let dataElement; - if (promptOptions.type === "input") { - dataElement = promptCreateInput(); - } else if (promptOptions.type === "select") { - dataElement = promptCreateSelect(); - } else { - return promptError(`Unhandled input type '${promptOptions.type}'`); - } - - dataContainerElement.append(dataElement); - dataElement.setAttribute("id", "data"); - - dataElement.focus(); - if (promptOptions.type === "input") { - dataElement.select(); - } - - if (promptOptions.customScript) { - try { - const customScript = require(promptOptions.customScript); - customScript(); - } catch (error) { - return promptError(error); - } - } -} - -window.addEventListener("error", error => { - if (promptId) { - promptError("An error has occured on the prompt window: \n" + error); - } -}); - -docReady(promptRegister); diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index 2eb13b74..6683aced 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -3,11 +3,14 @@

prompt-preview

## Usage - ```js prompt([options, parentBrowserWindow]).then(...).catch(...) ``` +Promise resolve returns input +If user presses cancel/exit window, input = null; + +On error, Prompise reject returns custom error message ## Example ```js From 17fd499420486cdb4d77ec5d8387df8f692bcbbe Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:51:11 +0300 Subject: [PATCH 27/52] fix typo --- menu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu.js b/menu.js index 21ac1e7a..90487970 100644 --- a/menu.js +++ b/menu.js @@ -338,10 +338,10 @@ function setProxy(item, win) { .then((input) => { if (input !== null && input !== example) { config.set("options.proxy", input); - item.checked = (input === "") ? false : true; + item.checked = input !== ""; } else { //user pressed cancel item.checked = !item.checked; //reset checkbox } }) .catch(console.error); -} \ No newline at end of file +} From 980ffb45e927335b66ab950751707e283be48674 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Tue, 6 Apr 2021 21:57:16 +0300 Subject: [PATCH 28/52] Create readme.md refactor and css fix xo --fix add inline doc fix typo --- index.js | 18 +- menu.js | 9 +- package.json | 12 +- plugins/in-app-menu/back.js | 9 +- providers/logger.js | 20 +- providers/prompt/custom-titlebar.js | 14 ++ providers/prompt/customTitlebar.js | 19 -- .../{darkPrompt.css => dark-prompt.css} | 4 +- providers/prompt/index.js | 73 +++--- providers/prompt/page/prompt.js | 232 ++++++++++-------- providers/prompt/readme.md | 79 ++++++ 11 files changed, 306 insertions(+), 183 deletions(-) create mode 100644 providers/prompt/custom-titlebar.js delete mode 100644 providers/prompt/customTitlebar.js rename providers/prompt/{darkPrompt.css => dark-prompt.css} (94%) create mode 100644 providers/prompt/readme.md diff --git a/index.js b/index.js index 9e8b7233..7fd88754 100644 --- a/index.js +++ b/index.js @@ -110,8 +110,8 @@ function createMainWindow() { titleBarStyle: useInlineMenu ? "hidden" : is.macOS() - ? "hiddenInset" - : "default", + ? "hiddenInset" + : "default", autoHideMenuBar: config.get("options.hideMenu"), }); if (windowPosition) { @@ -156,7 +156,7 @@ function createMainWindow() { let createdWindow = false; app.on("browser-window-created", (event, win) => { - //Ensure listeners aren't registered when creating input dialog + //Ensures listeners are registered only once if (createdWindow) { return; } @@ -172,7 +172,7 @@ app.on("browser-window-created", (event, win) => { frameProcessId, frameRoutingId, ) => { - let log = { + const log = { error: "did-fail-load", event, errorCode, @@ -183,7 +183,7 @@ app.on("browser-window-created", (event, win) => { frameRoutingId, }; if (is.dev()) { - console.log(log); + console.log(log.toString()); } win.webContents.send("log", log); win.webContents.loadFile(path.join(__dirname, "error.html")); @@ -306,13 +306,11 @@ app.on("ready", () => { } // Optimized for Mac OS X - if (is.macOS()) { - if (!config.get("options.appVisible")) { - app.dock.hide(); - } + if (is.macOS() && !config.get("options.appVisible")) { + app.dock.hide(); } - var forceQuit = false; + let forceQuit = false; app.on("before-quit", () => { forceQuit = true; }); diff --git a/menu.js b/menu.js index 3754d14c..90487970 100644 --- a/menu.js +++ b/menu.js @@ -12,6 +12,7 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, type: "checkbox", checked: config.plugins.isEnabled(plugin), + //Submenu check used in in-app-menu hasSubmenu: hasSubmenu || undefined, click: (item) => { if (item.checked) { @@ -321,13 +322,13 @@ function setProxy(item, win) { }, type: 'input', icon: iconPath, - customStylesheet: path.join(__dirname, "providers", "prompt", "darkPrompt.css"), + customStylesheet: path.join(__dirname, "providers", "prompt", "dark-prompt.css"), }; //TODO: custom bar on prompt need testing on macOS if(!is.macOS()) { Object.assign(options, { frame: false, - customScript: path.join(__dirname, "providers", "prompt", "customTitlebar.js"), + customScript: path.join(__dirname, "providers", "prompt", "custom-titlebar.js"), enableRemoteModule: true, height: 200, width: 450, @@ -337,10 +338,10 @@ function setProxy(item, win) { .then((input) => { if (input !== null && input !== example) { config.set("options.proxy", input); - item.checked = (input === "") ? false : true; + item.checked = input !== ""; } else { //user pressed cancel item.checked = !item.checked; //reset checkbox } }) .catch(console.error); -} \ No newline at end of file +} diff --git a/package.json b/package.json index 36591897..144ac53f 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,16 @@ "envs": [ "node", "browser" - ] + ], + "rules": { + "quotes": [ + "error", + "double", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ] + } } } diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index b17ee032..938c5475 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -9,8 +9,6 @@ const { injectCSS } = require("../utils"); //check that menu doesn't get created twice let calledReadyToShow = false; -//check menu state isn't changed twice -let calledFinishedLoad = false //tracks menu visibility let visible = true; // win hook for fixing menu @@ -37,6 +35,7 @@ module.exports = (winImport) => { if (calledReadyToShow) { return; } + calledReadyToShow = true; setApplicationMenu(win); @@ -53,7 +52,7 @@ module.exports = (winImport) => { win.webContents.on("did-finish-load", () => { // fix bug with menu not applying on start when no internet connection available setMenuVisibility(!config.get("options.hideMenu")); - }) + }); }; function switchMenuVisibility() { @@ -81,10 +80,10 @@ function updateTemplate(template) { for (let item of template) { // Change onClick of checkbox+radio if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) { - let originalOnclick = item.click; + const originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - updateCheckboxesAndRadioButtons(itemClicked, item.type === 'radio', item.hasSubmenu); + updateCheckboxesAndRadioButtons(itemClicked, item.type === "radio", item.hasSubmenu); }; item.fixed = true; } diff --git a/providers/logger.js b/providers/logger.js index 08a940f3..730a3dfa 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -1,11 +1,15 @@ const { ipcRenderer } = require("electron"); +function logToString(log) { + let string = (typeof log === "string") ? log : log.toString(); + if (!string || string.includes("[object Object]")) { + string = JSON.stringify(log); + } + return string; +} + module.exports = () => { - ipcRenderer.on("log", (event, log) => { - let string = log || log.toString(); - if (!string || string === "[object Object]") { - string = JSON.stringify(log); - } - console.log(string); - }) -}; \ No newline at end of file + ipcRenderer.on("log", (event, log) => { + console.log(logToString(log)); + }); +}; diff --git a/providers/prompt/custom-titlebar.js b/providers/prompt/custom-titlebar.js new file mode 100644 index 00000000..c36ce5f5 --- /dev/null +++ b/providers/prompt/custom-titlebar.js @@ -0,0 +1,14 @@ +const customTitlebar = require("custom-electron-titlebar"); + +module.exports = () => { + const bar = new customTitlebar.Titlebar({ + backgroundColor: customTitlebar.Color.fromHex("#050505"), + minimizable: false, + maximizable: false, + menu: null + }); + const mainStyle = document.querySelector("#container").style; + mainStyle.width = "100%"; + mainStyle.position = "fixed"; + mainStyle.border = "unset"; +}; diff --git a/providers/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js deleted file mode 100644 index 116aa56c..00000000 --- a/providers/prompt/customTitlebar.js +++ /dev/null @@ -1,19 +0,0 @@ -const customTitlebar = require("custom-electron-titlebar"); - -module.exports = () => { - const bar = new customTitlebar.Titlebar({ - backgroundColor: customTitlebar.Color.fromHex("#050505"), - minimizable: false, - maximizable: false, - unfocusEffect: true, - }); - try { - bar.updateMenu(null); - } catch (e) { - //will always throw type error - null isn't menu, but it works - } - let container = document.querySelector('#container'); - container.style.width = '100%'; - container.style.position = 'fixed'; - container.style.border = 'unset'; -} \ No newline at end of file diff --git a/providers/prompt/darkPrompt.css b/providers/prompt/dark-prompt.css similarity index 94% rename from providers/prompt/darkPrompt.css rename to providers/prompt/dark-prompt.css index 8b343a25..49e0f88e 100644 --- a/providers/prompt/darkPrompt.css +++ b/providers/prompt/dark-prompt.css @@ -34,7 +34,9 @@ body { } #ok:hover, -#cancel:hover { +#ok:focus, +#cancel:hover, +#cancel:focus { outline: rgba(60, 0, 0, 0.4) solid 2px; } diff --git a/providers/prompt/index.js b/providers/prompt/index.js index d6664305..bbba8b78 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -1,17 +1,19 @@ -const electron = require('electron'); +const electron = require("electron"); const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow; const ipcMain = electron.ipcMain || electron.remote.ipcMain; -const url = require('url'); -const path = require('path'); +const url = require("url"); +const path = require("path"); const DEFAULT_WIDTH = 370; const DEFAULT_HEIGHT = 160; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { + //id used to ensure unique listeners per window const id = `${Date.now()}-${Math.random()}`; + //custom options override default const options_ = Object.assign( { width: DEFAULT_WIDTH, @@ -19,12 +21,12 @@ function electronPrompt(options, parentWindow) { minWidth: DEFAULT_WIDTH, minHeight: DEFAULT_HEIGHT, resizable: false, - title: 'Prompt', - label: 'Please input a value:', + title: "Prompt", + label: "Please input a value:", buttonLabels: null, alwaysOnTop: false, value: null, - type: 'input', + type: "input", selectOptions: null, icon: null, useHtmlLabel: false, @@ -38,7 +40,7 @@ function electronPrompt(options, parentWindow) { options || {} ); - if (options_.type === 'select' && (options_.selectOptions === null || typeof options_.selectOptions !== 'object')) { + if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { reject(new Error('"selectOptions" must be an object')); return; } @@ -50,9 +52,9 @@ function electronPrompt(options, parentWindow) { minWidth: options_.minWidth, minHeight: options_.minHeight, resizable: options_.resizable, - minimizable: false, - fullscreenable: false, - maximizable: false, + minimizable: !options_.skipTaskbar && !parentWindow && !options_.alwaysOnTop, + fullscreenable: options_.resizable, + maximizable: options_.resizable, parent: parentWindow, skipTaskbar: options_.skipTaskbar, alwaysOnTop: options_.alwaysOnTop, @@ -70,14 +72,11 @@ function electronPrompt(options, parentWindow) { promptWindow.setMenu(null); promptWindow.setMenuBarVisibility(options_.menuBarVisible); - const getOptionsListener = event => { - event.returnValue = JSON.stringify(options_); - }; - + //called on exit const cleanup = () => { - ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener); - ipcMain.removeListener('prompt-post-data:' + id, postDataListener); - ipcMain.removeListener('prompt-error:' + id, errorListener); + ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); + ipcMain.removeListener("prompt-post-data:" + id, postDataListener); + ipcMain.removeListener("prompt-error:" + id, errorListener); if (promptWindow) { promptWindow.close(); @@ -85,6 +84,12 @@ function electronPrompt(options, parentWindow) { } }; + ///transfer options to front + const getOptionsListener = event => { + event.returnValue = JSON.stringify(options_); + }; + + //get input from front const postDataListener = (event, value) => { resolve(value); event.returnValue = null; @@ -92,34 +97,46 @@ function electronPrompt(options, parentWindow) { }; const unresponsiveListener = () => { - reject(new Error('Window was unresponsive')); + reject(new Error("Window was unresponsive")); cleanup(); }; + //get error from front const errorListener = (event, message) => { reject(new Error(message)); event.returnValue = null; cleanup(); }; - ipcMain.on('prompt-get-options:' + id, getOptionsListener); - ipcMain.on('prompt-post-data:' + id, postDataListener); - ipcMain.on('prompt-error:' + id, errorListener); - promptWindow.on('unresponsive', unresponsiveListener); + //attach listeners + ipcMain.on("prompt-get-options:" + id, getOptionsListener); + ipcMain.on("prompt-post-data:" + id, postDataListener); + ipcMain.on("prompt-error:" + id, errorListener); + promptWindow.on("unresponsive", unresponsiveListener); - promptWindow.on('closed', () => { + promptWindow.on("closed", () => { promptWindow = null; cleanup(); resolve(null); }); - const promptUrl = url.format({ - protocol: 'file', - slashes: true, - pathname: path.join(__dirname, 'page', 'prompt.html'), - hash: id + //should never happen + promptWindow.webContents.on("did-fail-load", ( + event, + errorCode, + errorDescription, + validatedURL, + ) => { + const log = { + error: "did-fail-load", + errorCode, + errorDescription, + validatedURL, + }; + reject(new Error("prompt.html did-fail-load, log:\n", + log.toString())); }); + //Finally, load prompt promptWindow.loadURL(promptUrl); }); } diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 754d0e74..b7a54120 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,126 +1,47 @@ -const fs = require('fs'); -const {ipcRenderer} = require('electron'); -const docReady = require('doc-ready'); +const fs = require("fs"); +const {ipcRenderer} = require("electron"); +const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; -function promptError(error) { - if (error instanceof Error) { - error = error.message; - } - - ipcRenderer.sendSync('prompt-error:' + promptId, error); -} - -function promptCancel() { - ipcRenderer.sendSync('prompt-post-data:' + promptId, null); -} - -function promptSubmit() { - const dataElement = document.querySelector('#data'); - let data = null; - - if (promptOptions.type === 'input') { - data = dataElement.value; - } else if (promptOptions.type === 'select') { - if (promptOptions.selectMultiple) { - data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value')); - } else { - data = dataElement.value; - } - } - - ipcRenderer.sendSync('prompt-post-data:' + promptId, data); -} - -function promptCreateInput() { - const dataElement = document.createElement('input'); - dataElement.setAttribute('type', 'text'); - - if (promptOptions.value) { - dataElement.value = promptOptions.value; - } else { - dataElement.value = ''; - } - - if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') { - for (const k in promptOptions.inputAttrs) { - if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { - continue; - } - - dataElement.setAttribute(k, promptOptions.inputAttrs[k]); - } - } - - dataElement.addEventListener('keyup', event => { - if (event.key === 'Escape') { - promptCancel(); - } - }); - - dataElement.addEventListener('keypress', event => { - if (event.key === 'Enter') { - event.preventDefault(); - document.querySelector('#ok').click(); - } - }); - - return dataElement; -} - -function promptCreateSelect() { - const dataElement = document.createElement('select'); - let optionElement; - - for (const k in promptOptions.selectOptions) { - if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) { - continue; - } - - optionElement = document.createElement('option'); - optionElement.setAttribute('value', k); - optionElement.textContent = promptOptions.selectOptions[k]; - if (k === promptOptions.value) { - optionElement.setAttribute('selected', 'selected'); - } - - dataElement.append(optionElement); - } - - return dataElement; -} +docReady(promptRegister); +//start here function promptRegister() { - promptId = document.location.hash.replace('#', ''); + //get custom session id + promptId = document.location.hash.replace("#", ""); + //get options from back try { - promptOptions = JSON.parse(ipcRenderer.sendSync('prompt-get-options:' + promptId)); + promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); } catch (error) { return promptError(error); } + //set label if (promptOptions.useHtmlLabel) { - document.querySelector('#label').innerHTML = promptOptions.label; + document.querySelector("#label").innerHTML = promptOptions.label; } else { - document.querySelector('#label').textContent = promptOptions.label; + document.querySelector("#label").textContent = promptOptions.label; } + //set button label if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok; + document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel; + document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; } + //inject custom stylesheet from options if (promptOptions.customStylesheet) { try { const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); if (customStyleContent) { - const customStyle = document.createElement('style'); - customStyle.setAttribute('rel', 'stylesheet'); + const customStyle = document.createElement("style"); + customStyle.setAttribute("rel", "stylesheet"); customStyle.append(document.createTextNode(customStyleContent)); document.head.append(customStyle); } @@ -129,28 +50,31 @@ function promptRegister() { } } - document.querySelector('#form').addEventListener('submit', promptSubmit); - document.querySelector('#cancel').addEventListener('click', promptCancel); + //add button listeners + document.querySelector("#form").addEventListener("submit", promptSubmit); + document.querySelector("#cancel").addEventListener("click", promptCancel); - const dataContainerElement = document.querySelector('#data-container'); + //create input/select + const dataContainerElement = document.querySelector("#data-container"); let dataElement; - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement = promptCreateInput(); - } else if (promptOptions.type === 'select') { + } else if (promptOptions.type === "select") { dataElement = promptCreateSelect(); } else { return promptError(`Unhandled input type '${promptOptions.type}'`); } dataContainerElement.append(dataElement); - dataElement.setAttribute('id', 'data'); + dataElement.setAttribute("id", "data"); dataElement.focus(); - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement.select(); } + //load custom script from options if (promptOptions.customScript) { try { const customScript = require(promptOptions.customScript); @@ -161,10 +85,104 @@ function promptRegister() { } } -window.addEventListener('error', error => { +window.addEventListener("error", error => { if (promptId) { - promptError('An error has occured on the prompt window: \n' + error); + promptError("An error has occured on the prompt window: \n" + error); } }); -docReady(promptRegister); \ No newline at end of file +//send error to back +function promptError(error) { + if (error instanceof Error) { + error = error.message; + } + + ipcRenderer.sendSync("prompt-error:" + promptId, error); +} + +//send to back: input=null +function promptCancel() { + ipcRenderer.sendSync("prompt-post-data:" + promptId, null); +} + +//transfer input data to back +function promptSubmit() { + const dataElement = document.querySelector("#data"); + let data = null; + + if (promptOptions.type === "input") { + data = dataElement.value; + } else if (promptOptions.type === "select") { + data = promptOptions.selectMultiple ? + dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : + dataElement.value; + } + + ipcRenderer.sendSync("prompt-post-data:" + promptId, data); +} + +//creates input box +function promptCreateInput() { + const dataElement = document.createElement("input"); + dataElement.setAttribute("type", "text"); + + if (promptOptions.value) { + dataElement.value = promptOptions.value; + } else { + dataElement.value = ""; + } + + //insert custom input attributes if in options + if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { + for (const k in promptOptions.inputAttrs) { + if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { + continue; + } + + dataElement.setAttribute(k, promptOptions.inputAttrs[k]); + } + } + + //Cancel/Exit on 'Escape' + dataElement.addEventListener("keyup", event => { + if (event.key === "Escape") { + promptCancel(); + } + }); + + //Confrim on 'Enter' + dataElement.addEventListener("keypress", event => { + if (event.key === "Enter") { + event.preventDefault(); + document.querySelector("#ok").click(); + } + }); + + return dataElement; +} + +//create multiple select +function promptCreateSelect() { + const dataElement = document.createElement("select"); + let optionElement; + + for (const k in promptOptions.selectOptions) { + if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) { + continue; + } + + optionElement = document.createElement("option"); + optionElement.setAttribute("value", k); + optionElement.textContent = promptOptions.selectOptions[k]; + if (k === promptOptions.value) { + optionElement.setAttribute("selected", "selected"); + } + + dataElement.append(optionElement); + } + + return dataElement; +} + + + diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md new file mode 100644 index 00000000..6683aced --- /dev/null +++ b/providers/prompt/readme.md @@ -0,0 +1,79 @@ +# Prompt Documentation + +

prompt-preview

+ +## Usage +```js +prompt([options, parentBrowserWindow]).then(...).catch(...) +``` +Promise resolve returns input + +If user presses cancel/exit window, input = null; + +On error, Prompise reject returns custom error message +## Example + +```js +const prompt = require('./providers/prompt'); + +prompt({ + title: 'Prompt example', + label: 'URL:', + value: 'http://example.org', + inputAttrs: { + type: 'url' + }, + type: 'input' +}) +.then((r) => { + if(r === null) { + console.log('user cancelled'); + } else { + console.log('result', r); + } +}) +.catch(console.error); +``` + +### Options object (optional) + +| Key | Explanation | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| title | (optional, string) The title of the prompt window. Defaults to 'Prompt'. | +| label | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. | +| buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. | +| value | (optional, string) The default value for the input field. Defaults to null. | +| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input. Defaults to 'input'. | +| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' | +| selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | +| useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | +| width | (optional, integer) The width of the prompt window. Defaults to 370. | +| minWidth | (optional, integer) The minimum allowed width for the prompt window. Same default value as width. | +| height | (optional, integer) The height of the prompt window. Defaults to 130. | +| minHeight | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. | +| resizable | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. | +| alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false | +| icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. | +| customStylesheet | (optional, string) The local path of a CSS file to stylize the prompt window. Defaults to null. | +| menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. | +| skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. | +| frame | (optional, boolean) Wether to create prompt with frame. Defaults to true. | +| customScript | (optional, string) The local path of a JS file to run on preload. Defaults to null. | +| enableRemoteModule | (optional, boolean) Wether the prompt window have remote modules activated, Defaults to false. | + +If not supplied, it uses the defaults listed in the table above. + +### parentBrowserWindow (optional) + +The window in which to display the prompt on. If not supplied, the parent window of the prompt will be null. + +### customScript (optional) + +Create the script with the following template: + +```node +module.exports = () => { + // This function will be called as a preload script + // So you can use front features like `document.querySelector` +}; +``` From 80b12076406e5e06a219f5b7824f1f3821b54832 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Fri, 9 Apr 2021 02:19:06 +0300 Subject: [PATCH 29/52] fix rare crash due to unfocus effect --- plugins/in-app-menu/front.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/in-app-menu/front.js b/plugins/in-app-menu/front.js index 3287dc5d..8ee1c9cf 100644 --- a/plugins/in-app-menu/front.js +++ b/plugins/in-app-menu/front.js @@ -6,6 +6,8 @@ module.exports = () => { const bar = new customTitlebar.Titlebar({ backgroundColor: customTitlebar.Color.fromHex("#050505"), itemBackgroundColor: customTitlebar.Color.fromHex("#121212"), + // !important - unfocus effect can sometimes cause crash as of v3.2.6 of custom-electron-titlebar + unfocusEffect: false }); bar.updateTitle(" "); document.title = "Youtube Music"; From 3d41d04818ef355cd912e4e6ec7adad081854733 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Fri, 9 Apr 2021 22:28:55 +0300 Subject: [PATCH 30/52] Revert "fix rare crash due to unfocus effect" This reverts commit 80b12076406e5e06a219f5b7824f1f3821b54832. --- plugins/in-app-menu/front.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/in-app-menu/front.js b/plugins/in-app-menu/front.js index 8ee1c9cf..3287dc5d 100644 --- a/plugins/in-app-menu/front.js +++ b/plugins/in-app-menu/front.js @@ -6,8 +6,6 @@ module.exports = () => { const bar = new customTitlebar.Titlebar({ backgroundColor: customTitlebar.Color.fromHex("#050505"), itemBackgroundColor: customTitlebar.Color.fromHex("#121212"), - // !important - unfocus effect can sometimes cause crash as of v3.2.6 of custom-electron-titlebar - unfocusEffect: false }); bar.updateTitle(" "); document.title = "Youtube Music"; From 09d9f72db289aff51c20fd83538fbb40b3ee652c Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sat, 10 Apr 2021 02:51:38 +0300 Subject: [PATCH 31/52] add unresponsive listener --- index.js | 44 ++++++++++++++++++++----- plugins/in-app-menu/back.js | 10 +----- preload.js | 2 +- providers/{logger.js => frontLogger.js} | 2 +- 4 files changed, 39 insertions(+), 19 deletions(-) rename providers/{logger.js => frontLogger.js} (87%) diff --git a/index.js b/index.js index 7fd88754..addd98ed 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const path = require("path"); const electron = require("electron"); +const { dialog } = require("electron"); const is = require("electron-is"); const unhandled = require("electron-unhandled"); const { autoUpdater } = require("electron-updater"); @@ -144,7 +145,11 @@ function createMainWindow() { }); } }); - + + win.webContents.on("render-process-gone", (event, webContents, details) => { + showUnresponsiveDialog(win, details); + }); + win.once("ready-to-show", () => { if (config.get("options.appVisible")) { win.show(); @@ -154,13 +159,7 @@ function createMainWindow() { return win; } -let createdWindow = false; -app.on("browser-window-created", (event, win) => { - //Ensures listeners are registered only once - if (createdWindow) { - return; - } - createdWindow = true; +app.once("browser-window-created", (event, win) => { loadPlugins(win); win.webContents.on("did-fail-load", ( @@ -325,3 +324,32 @@ app.on("ready", () => { }); } }); + +function showUnresponsiveDialog(win, details) { + if (!!details) { + console.log("Unresponsive Error!\n"+JSON.stringify(details, null, "\t")) + } + dialog.showMessageBox(win, { + type: "error", + title: "Window Unresponsive", + message: "The Application is Unresponsive", + details: "We are sorry for the inconveniance! please choose what to do with the application:", + buttons: ["Wait", "Relaunch", "Quit"], + cancelId: 0 + }).then( response => { + switch (response) { + case 1: //if relaunch - relaunch+exit + app.relaunch(); + case 2: + app.exit(); + break; + case 0: + default: + return; + //maybe set a timer and afterwards check responsivness and call function again if failed + } + }); +} + +module.exports.aDialog = showUnresponsiveDialog; + diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 938c5475..8365ac8b 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -7,8 +7,6 @@ const config = require("../../config"); const { setApplicationMenu } = require("../../menu"); const { injectCSS } = require("../utils"); -//check that menu doesn't get created twice -let calledReadyToShow = false; //tracks menu visibility let visible = true; // win hook for fixing menu @@ -30,13 +28,7 @@ module.exports = (winImport) => { // css for custom scrollbar + disable drag area(was causing bugs) injectCSS(win.webContents, path.join(__dirname, "style.css")); - win.on("ready-to-show", () => { - // (apparently ready-to-show is called twice) - if (calledReadyToShow) { - return; - } - - calledReadyToShow = true; + win.once("ready-to-show", () => { setApplicationMenu(win); diff --git a/preload.js b/preload.js index f25dd6b7..5fcb2e07 100644 --- a/preload.js +++ b/preload.js @@ -34,7 +34,7 @@ document.addEventListener("DOMContentLoaded", () => { fileExists(songInfoProviderPath, require(songInfoProviderPath)); // inject front logger - const loggerPath = path.join(__dirname, "providers", "logger.js") + const loggerPath = path.join(__dirname, "providers", "frontLogger.js") fileExists(loggerPath, require(loggerPath)); // Add action for reloading diff --git a/providers/logger.js b/providers/frontLogger.js similarity index 87% rename from providers/logger.js rename to providers/frontLogger.js index 730a3dfa..210e3ff8 100644 --- a/providers/logger.js +++ b/providers/frontLogger.js @@ -3,7 +3,7 @@ const { ipcRenderer } = require("electron"); function logToString(log) { let string = (typeof log === "string") ? log : log.toString(); if (!string || string.includes("[object Object]")) { - string = JSON.stringify(log); + string = JSON.stringify(log, null, "\t"); } return string; } From c764d657d74fc5d85d0410ec1115d42e84bdc7df Mon Sep 17 00:00:00 2001 From: Araxeus Date: Wed, 14 Apr 2021 13:42:39 +0300 Subject: [PATCH 32/52] cleanup unresponsiveDialog --- index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/index.js b/index.js index addd98ed..13eaa2db 100644 --- a/index.js +++ b/index.js @@ -341,15 +341,11 @@ function showUnresponsiveDialog(win, details) { case 1: //if relaunch - relaunch+exit app.relaunch(); case 2: - app.exit(); + app.quit(); break; case 0: default: return; - //maybe set a timer and afterwards check responsivness and call function again if failed } }); } - -module.exports.aDialog = showUnresponsiveDialog; - From bb6ad14111608d3b085a50e1c4a0a6d77ce37252 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Thu, 15 Apr 2021 12:40:09 +0300 Subject: [PATCH 33/52] navbar background black fix visual bug --- plugins/in-app-menu/style.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/in-app-menu/style.css b/plugins/in-app-menu/style.css index 19dd59fb..c6339093 100644 --- a/plugins/in-app-menu/style.css +++ b/plugins/in-app-menu/style.css @@ -21,6 +21,16 @@ ytmusic-pivot-bar-item-renderer { -webkit-app-region: unset !important; } +/* navbar background black */ +.center-content.ytmusic-nav-bar { + background: #030303; +} + +/* move up item selectrion renderer by 15 px */ +ytmusic-item-section-renderer[has-item-section-tabbed-header-renderer_] #header.ytmusic-item-section-renderer { + top: 75 !important; +} + /* fix weird positioning in search screen*/ ytmusic-header-renderer.ytmusic-search-page { position: unset !important; From ff6a486daf145c5a56ec5f500f29fdbf1c38391b Mon Sep 17 00:00:00 2001 From: Araxeus Date: Thu, 15 Apr 2021 15:34:05 +0300 Subject: [PATCH 34/52] fix unresponsive dialog response --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 13eaa2db..82ba695d 100644 --- a/index.js +++ b/index.js @@ -333,11 +333,11 @@ function showUnresponsiveDialog(win, details) { type: "error", title: "Window Unresponsive", message: "The Application is Unresponsive", - details: "We are sorry for the inconveniance! please choose what to do with the application:", + details: "We are sorry for the inconveniance! please choose what to do:", buttons: ["Wait", "Relaunch", "Quit"], cancelId: 0 - }).then( response => { - switch (response) { + }).then( result => { + switch (result.response) { case 1: //if relaunch - relaunch+exit app.relaunch(); case 2: @@ -345,7 +345,7 @@ function showUnresponsiveDialog(win, details) { break; case 0: default: - return; + break; } }); } From 341a06aae70cf42b913ca73f5a0deb5b3afa3537 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sat, 17 Apr 2021 20:30:06 +0300 Subject: [PATCH 35/52] add prompt with number counter --- menu.js | 2 +- package.json | 1 - providers/prompt/dark-prompt.css | 5 ++ providers/prompt/index.js | 16 ++++- providers/prompt/page/prompt.css | 32 +++++++++ providers/prompt/page/prompt.js | 114 +++++++++++++++++++++++-------- providers/prompt/readme.md | 5 +- yarn.lock | 12 ---- 8 files changed, 140 insertions(+), 47 deletions(-) diff --git a/menu.js b/menu.js index 90487970..588dad7c 100644 --- a/menu.js +++ b/menu.js @@ -335,7 +335,7 @@ function setProxy(item, win) { }); } prompt(options, win) - .then((input) => { + .then(input => { if (input !== null && input !== example) { config.set("options.proxy", input); item.checked = input !== ""; diff --git a/package.json b/package.json index 144ac53f..a856babf 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "browser-id3-writer": "^4.4.0", "custom-electron-titlebar": "^3.2.6", "discord-rpc": "^3.2.0", - "doc-ready": "^1.0.4", "downloads-folder": "^3.0.1", "electron-debug": "^3.2.0", "electron-is": "^3.0.0", diff --git a/providers/prompt/dark-prompt.css b/providers/prompt/dark-prompt.css index 49e0f88e..74fea61e 100644 --- a/providers/prompt/dark-prompt.css +++ b/providers/prompt/dark-prompt.css @@ -45,3 +45,8 @@ body { background-color: rgb(0, 0, 0); color: whitesmoke; } + +.minus, +.plus { + background:rgb(0, 0, 0); +} \ No newline at end of file diff --git a/providers/prompt/index.js b/providers/prompt/index.js index bbba8b78..1c14cfa2 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -6,7 +6,9 @@ const url = require("url"); const path = require("path"); const DEFAULT_WIDTH = 370; +const DEFAULT_COUNTER_WIDTH = 300; const DEFAULT_HEIGHT = 160; +const DEFAULT_COUNTER_HEIGHT= 150; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { @@ -18,8 +20,6 @@ function electronPrompt(options, parentWindow) { { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, - minWidth: DEFAULT_WIDTH, - minHeight: DEFAULT_HEIGHT, resizable: false, title: "Prompt", label: "Please input a value:", @@ -40,6 +40,11 @@ function electronPrompt(options, parentWindow) { options || {} ); + options_.minWidth = options.minWidth || options.width || + options_.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH; + options_.minHeight = options.minHeight || options.height || + options_.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT; + if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { reject(new Error('"selectOptions" must be an object')); return; @@ -136,6 +141,13 @@ function electronPrompt(options, parentWindow) { reject(new Error("prompt.html did-fail-load, log:\n", + log.toString())); }); + const promptUrl = url.format({ + protocol: 'file', + slashes: true, + pathname: path.join(__dirname, 'page', 'prompt.html'), + hash: id + }); + //Finally, load prompt promptWindow.loadURL(promptUrl); }); diff --git a/providers/prompt/page/prompt.css b/providers/prompt/page/prompt.css index c3530e57..10654b8d 100644 --- a/providers/prompt/page/prompt.css +++ b/providers/prompt/page/prompt.css @@ -78,3 +78,35 @@ select#data { background-color: #ddd; color: black; } + +/* Counter mode css */ + +span { + cursor: pointer; +} +.number { + margin: 100px; +} +.minus, +.plus { + user-select: none; + width: 20px; + height: 20px; + background: #f2f2f2; + border-radius: 4px; + padding: 8px 5px 8px 5px; + border: 1px solid #ddd; + display: inline-block; + vertical-align: middle; + text-align: center; +} +.inputt { + height: 34px; + width: 100px; + text-align: center; + font-size: 26px; + border: 1px solid #ddd; + border-radius: 4px; + display: inline-block; + vertical-align: middle; +} diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index b7a54120..042ba546 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,13 +1,13 @@ const fs = require("fs"); const {ipcRenderer} = require("electron"); -const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; +function $(selector) {return document.querySelector(selector)} + +document.addEventListener( 'DOMContentLoaded', promptRegister); -docReady(promptRegister); -//start here function promptRegister() { //get custom session id promptId = document.location.hash.replace("#", ""); @@ -21,18 +21,18 @@ function promptRegister() { //set label if (promptOptions.useHtmlLabel) { - document.querySelector("#label").innerHTML = promptOptions.label; + $("#label").innerHTML = promptOptions.label; } else { - document.querySelector("#label").textContent = promptOptions.label; + $("#label").textContent = promptOptions.label; } //set button label if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; + $("#ok").textContent = promptOptions.buttonLabels.ok; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; + $("#cancel").textContent = promptOptions.buttonLabels.cancel; } //inject custom stylesheet from options @@ -51,26 +51,40 @@ function promptRegister() { } //add button listeners - document.querySelector("#form").addEventListener("submit", promptSubmit); - document.querySelector("#cancel").addEventListener("click", promptCancel); + $("#form").addEventListener("submit", promptSubmit); + $("#cancel").addEventListener("click", promptCancel); //create input/select - const dataContainerElement = document.querySelector("#data-container"); + const dataContainerElement = $("#data-container"); let dataElement; - if (promptOptions.type === "input") { - dataElement = promptCreateInput(); - } else if (promptOptions.type === "select") { - dataElement = promptCreateSelect(); - } else { - return promptError(`Unhandled input type '${promptOptions.type}'`); - } - dataContainerElement.append(dataElement); + switch (promptOptions.type) { + case "counter": + case "input": + dataElement = promptCreateInput(); + break; + case "select": + dataElement = promptCreateSelect(); + break; + default: + return promptError(`Unhandled input type '${promptOptions.type}'`); + } + if (promptOptions.type === "counter") { + dataElement.classList.add("input"); + dataElement.style.width = "unset"; + dataElement.style["text-align"] = "center"; + //dataElement.style["min-height"] = "1.5em"; + dataContainerElement.append(createMinus(dataElement)); + dataContainerElement.append(dataElement); + dataContainerElement.append(createPlus(dataElement)); + } else { + dataContainerElement.append(dataElement); + } dataElement.setAttribute("id", "data"); dataElement.focus(); - if (promptOptions.type === "input") { + if (promptOptions.type === "input" || promptOptions.type === "counter") { dataElement.select(); } @@ -87,7 +101,8 @@ function promptRegister() { window.addEventListener("error", error => { if (promptId) { - promptError("An error has occured on the prompt window: \n" + error); + promptError("An error has occured on the prompt window: \n" + JSON.stringify(error, ["message", "arguments", "type", "name"]) + ); } }); @@ -107,15 +122,23 @@ function promptCancel() { //transfer input data to back function promptSubmit() { - const dataElement = document.querySelector("#data"); + const dataElement = $("#data"); let data = null; - if (promptOptions.type === "input") { - data = dataElement.value; - } else if (promptOptions.type === "select") { - data = promptOptions.selectMultiple ? - dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : - dataElement.value; + switch (promptOptions.type) { + case "input": + data = dataElement.value; + break; + case "counter": + data = validateCounterInput(dataElement.value); + break; + case "select": + data = promptOptions.selectMultiple ? + dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : + dataElement.value; + break; + default: //will never happen + return promptError(`Unhandled input type '${promptOptions.type}'`); } ipcRenderer.sendSync("prompt-post-data:" + promptId, data); @@ -127,6 +150,9 @@ function promptCreateInput() { dataElement.setAttribute("type", "text"); if (promptOptions.value) { + if (promptOptions.type === "counter") { + promptOptions.value = validateCounterInput(promptOptions.value); + } dataElement.value = promptOptions.value; } else { dataElement.value = ""; @@ -150,11 +176,11 @@ function promptCreateInput() { } }); - //Confrim on 'Enter' + //Confirm on 'Enter' dataElement.addEventListener("keypress", event => { if (event.key === "Enter") { event.preventDefault(); - document.querySelector("#ok").click(); + $("#ok").click(); } }); @@ -184,5 +210,35 @@ function promptCreateSelect() { return dataElement; } +function createMinus(dataElement) { + const minus = document.createElement("span"); + minus.textContent = "-"; + minus.classList.add("minus"); + minus.onmousedown = () => { + dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); + }; + return minus; +} +function createPlus(dataElement) { + const plus = document.createElement("span"); + plus.textContent = "+"; + plus.classList.add("plus"); + plus.onmousedown = () => { + dataElement.value = validateCounterInput(parseInt(dataElement.value) + 1); + }; + return plus; +} +//validate counter +function validateCounterInput(input) { + const min = promptOptions.counterOptions?.minimum, + max = promptOptions.counterOptions?.maximum; + if (min !== undefined && input < min) { + return min; + } + if (max !== undefined && input > max) { + return max; + } + return input; +} diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index 6683aced..eff1f907 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -43,8 +43,9 @@ prompt({ | label | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. | | buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. | | value | (optional, string) The default value for the input field. Defaults to null. | -| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input. Defaults to 'input'. | -| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' | +| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input or 'counter' for a number counter with buttons. Defaults to 'input'. | +| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' +| counterOptions | (optional, object) minimum and maximum of counter in format: `{minimum: %int%, maximum: %int%} ` | | selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | | useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | | width | (optional, integer) The width of the prompt window. Defaults to 370. | diff --git a/yarn.lock b/yarn.lock index f9909214..c969527e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2908,13 +2908,6 @@ dmg-builder@22.9.1: js-yaml "^3.14.0" sanitize-filename "^1.6.3" -doc-ready@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/doc-ready/-/doc-ready-1.0.4.tgz#37f5391969cff994303fdfef2e5d50357f8164d3" - integrity sha1-N/U5GWnP+ZQwP9/vLl1QNX+BZNM= - dependencies: - eventie "^1" - doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3612,11 +3605,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eventie@^1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/eventie/-/eventie-1.0.6.tgz#d4ffc8b0c2b5e493c2aa1b22cbe918d3aee74437" - integrity sha1-1P/IsMK15JPCqhsiy+kY067nRDc= - events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" From 3f50ab7cfc8c0efcacd35848baf2cf499f8f4706 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sat, 17 Apr 2021 20:43:48 +0300 Subject: [PATCH 36/52] lint --- providers/prompt/dark-prompt.css | 4 ++-- providers/prompt/page/prompt.css | 14 -------------- providers/prompt/page/prompt.js | 11 +++++------ 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/providers/prompt/dark-prompt.css b/providers/prompt/dark-prompt.css index 74fea61e..8bf39791 100644 --- a/providers/prompt/dark-prompt.css +++ b/providers/prompt/dark-prompt.css @@ -48,5 +48,5 @@ body { .minus, .plus { - background:rgb(0, 0, 0); -} \ No newline at end of file + background: rgb(0, 0, 0); +} diff --git a/providers/prompt/page/prompt.css b/providers/prompt/page/prompt.css index 10654b8d..3224cf6a 100644 --- a/providers/prompt/page/prompt.css +++ b/providers/prompt/page/prompt.css @@ -80,13 +80,9 @@ select#data { } /* Counter mode css */ - span { cursor: pointer; } -.number { - margin: 100px; -} .minus, .plus { user-select: none; @@ -100,13 +96,3 @@ span { vertical-align: middle; text-align: center; } -.inputt { - height: 34px; - width: 100px; - text-align: center; - font-size: 26px; - border: 1px solid #ddd; - border-radius: 4px; - display: inline-block; - vertical-align: middle; -} diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 042ba546..d3abfeab 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,12 +1,12 @@ const fs = require("fs"); -const {ipcRenderer} = require("electron"); +const { ipcRenderer } = require("electron"); let promptId = null; let promptOptions = null; -function $(selector) {return document.querySelector(selector)} +function $(selector) { return document.querySelector(selector) } -document.addEventListener( 'DOMContentLoaded', promptRegister); +document.addEventListener('DOMContentLoaded', promptRegister); function promptRegister() { //get custom session id @@ -71,7 +71,6 @@ function promptRegister() { return promptError(`Unhandled input type '${promptOptions.type}'`); } if (promptOptions.type === "counter") { - dataElement.classList.add("input"); dataElement.style.width = "unset"; dataElement.style["text-align"] = "center"; //dataElement.style["min-height"] = "1.5em"; @@ -215,7 +214,7 @@ function createMinus(dataElement) { minus.textContent = "-"; minus.classList.add("minus"); minus.onmousedown = () => { - dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); + dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); }; return minus; } @@ -233,7 +232,7 @@ function createPlus(dataElement) { //validate counter function validateCounterInput(input) { const min = promptOptions.counterOptions?.minimum, - max = promptOptions.counterOptions?.maximum; + max = promptOptions.counterOptions?.maximum; if (min !== undefined && input < min) { return min; } From 97a9e632317c674bae580f818d474fd21487698d Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sat, 17 Apr 2021 21:17:07 +0300 Subject: [PATCH 37/52] xo --fix --- preload.js | 2 +- providers/{frontLogger.js => front-logger.js} | 0 providers/prompt/index.js | 16 +++--- providers/prompt/page/prompt.js | 51 +++++++++++-------- 4 files changed, 38 insertions(+), 31 deletions(-) rename providers/{frontLogger.js => front-logger.js} (100%) diff --git a/preload.js b/preload.js index 5fcb2e07..74860cec 100644 --- a/preload.js +++ b/preload.js @@ -34,7 +34,7 @@ document.addEventListener("DOMContentLoaded", () => { fileExists(songInfoProviderPath, require(songInfoProviderPath)); // inject front logger - const loggerPath = path.join(__dirname, "providers", "frontLogger.js") + const loggerPath = path.join(__dirname, "providers", "front-logger.js") fileExists(loggerPath, require(loggerPath)); // Add action for reloading diff --git a/providers/frontLogger.js b/providers/front-logger.js similarity index 100% rename from providers/frontLogger.js rename to providers/front-logger.js diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 1c14cfa2..1e29766f 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -8,7 +8,7 @@ const path = require("path"); const DEFAULT_WIDTH = 370; const DEFAULT_COUNTER_WIDTH = 300; const DEFAULT_HEIGHT = 160; -const DEFAULT_COUNTER_HEIGHT= 150; +const DEFAULT_COUNTER_HEIGHT = 150; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { @@ -40,9 +40,9 @@ function electronPrompt(options, parentWindow) { options || {} ); - options_.minWidth = options.minWidth || options.width || + options_.minWidth = options.minWidth || options.width || options_.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH; - options_.minHeight = options.minHeight || options.height || + options_.minHeight = options.minHeight || options.height || options_.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT; if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { @@ -130,21 +130,21 @@ function electronPrompt(options, parentWindow) { event, errorCode, errorDescription, - validatedURL, + validatedURL ) => { const log = { error: "did-fail-load", errorCode, errorDescription, - validatedURL, + validatedURL }; - reject(new Error("prompt.html did-fail-load, log:\n", + log.toString())); + reject(new Error("prompt.html did-fail-load, log:\n" + log.toString())); }); const promptUrl = url.format({ - protocol: 'file', + protocol: "file", slashes: true, - pathname: path.join(__dirname, 'page', 'prompt.html'), + pathname: path.join(__dirname, "page", "prompt.html"), hash: id }); diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index d3abfeab..ac79f106 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -4,9 +4,11 @@ const { ipcRenderer } = require("electron"); let promptId = null; let promptOptions = null; -function $(selector) { return document.querySelector(selector) } +function $(selector) { + return document.querySelector(selector); +} -document.addEventListener('DOMContentLoaded', promptRegister); +document.addEventListener("DOMContentLoaded", promptRegister); function promptRegister() { //get custom session id @@ -54,7 +56,6 @@ function promptRegister() { $("#form").addEventListener("submit", promptSubmit); $("#cancel").addEventListener("click", promptCancel); - //create input/select const dataContainerElement = $("#data-container"); let dataElement; @@ -70,19 +71,20 @@ function promptRegister() { default: return promptError(`Unhandled input type '${promptOptions.type}'`); } + if (promptOptions.type === "counter") { dataElement.style.width = "unset"; dataElement.style["text-align"] = "center"; - //dataElement.style["min-height"] = "1.5em"; - dataContainerElement.append(createMinus(dataElement)); + dataContainerElement.append(createMinusButton(dataElement)); dataContainerElement.append(dataElement); - dataContainerElement.append(createPlus(dataElement)); + dataContainerElement.append(createPlusButton(dataElement)); } else { dataContainerElement.append(dataElement); } - dataElement.setAttribute("id", "data"); + dataElement.setAttribute("id", "data"); dataElement.focus(); + if (promptOptions.type === "input" || promptOptions.type === "counter") { dataElement.select(); } @@ -100,7 +102,8 @@ function promptRegister() { window.addEventListener("error", error => { if (promptId) { - promptError("An error has occured on the prompt window: \n" + JSON.stringify(error, ["message", "arguments", "type", "name"]) + promptError("An error has occured on the prompt window: \n" + + JSON.stringify(error, ["message", "arguments", "type", "name"]) ); } }); @@ -209,35 +212,39 @@ function promptCreateSelect() { return dataElement; } -function createMinus(dataElement) { - const minus = document.createElement("span"); - minus.textContent = "-"; - minus.classList.add("minus"); - minus.onmousedown = () => { +function createMinusButton(dataElement) { + const minusBtn = document.createElement("span"); + minusBtn.textContent = "-"; + minusBtn.classList.add("minus"); + minusBtn.onmousedown = () => { dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); }; - return minus; + return minusBtn; } -function createPlus(dataElement) { - const plus = document.createElement("span"); - plus.textContent = "+"; - plus.classList.add("plus"); - plus.onmousedown = () => { +function createPlusButton(dataElement) { + const plusBtn = document.createElement("span"); + plusBtn.textContent = "+"; + plusBtn.classList.add("plus"); + plusBtn.onmousedown = () => { dataElement.value = validateCounterInput(parseInt(dataElement.value) + 1); }; - return plus; + + return plusBtn; } //validate counter function validateCounterInput(input) { - const min = promptOptions.counterOptions?.minimum, - max = promptOptions.counterOptions?.maximum; + const min = promptOptions.counterOptions?.minimum; + const max = promptOptions.counterOptions?.maximum; + if (min !== undefined && input < min) { return min; } + if (max !== undefined && input > max) { return max; } + return input; } From b7b1316e7095565eb0c6370f46dc1fee972df61e Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sat, 17 Apr 2021 22:55:25 +0300 Subject: [PATCH 38/52] add rapidFire option to counter prompt --- providers/prompt/index.js | 16 +++++--- providers/prompt/page/prompt.js | 69 +++++++++++++++++++++++++++------ providers/prompt/readme.md | 2 +- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 1e29766f..009c8c7a 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -18,8 +18,8 @@ function electronPrompt(options, parentWindow) { //custom options override default const options_ = Object.assign( { - width: DEFAULT_WIDTH, - height: DEFAULT_HEIGHT, + width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH, + height:options?.type === "counter" ? DEFAULT_COUNTER_HEIGHT: DEFAULT_HEIGHT, resizable: false, title: "Prompt", label: "Please input a value:", @@ -28,6 +28,7 @@ function electronPrompt(options, parentWindow) { value: null, type: "input", selectOptions: null, + counterOptions: {minimum: null, maximum: null, multiFire: false}, icon: null, useHtmlLabel: false, customStylesheet: null, @@ -40,10 +41,13 @@ function electronPrompt(options, parentWindow) { options || {} ); - options_.minWidth = options.minWidth || options.width || - options_.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH; - options_.minHeight = options.minHeight || options.height || - options_.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT; + options_.minWidth = options?.minWidth || options?.width || options_.width; + options_.minHeight = options?.minHeight || options?.height || options_.height; + + if (options_.type === "counter" && (options_.counterOptions !== null && typeof options_.selectOptions !== "object")) { + reject(new Error('"counterOptions" must be an object if specified')); + return; + } if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { reject(new Error('"selectOptions" must be an object')); diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index ac79f106..3cad04ac 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -4,7 +4,7 @@ const { ipcRenderer } = require("electron"); let promptId = null; let promptOptions = null; -function $(selector) { +function $(selector) { return document.querySelector(selector); } @@ -62,6 +62,8 @@ function promptRegister() { switch (promptOptions.type) { case "counter": + dataElement = promptCreateCounter(); + break; case "input": dataElement = promptCreateInput(); break; @@ -73,8 +75,6 @@ function promptRegister() { } if (promptOptions.type === "counter") { - dataElement.style.width = "unset"; - dataElement.style["text-align"] = "center"; dataContainerElement.append(createMinusButton(dataElement)); dataContainerElement.append(dataElement); dataContainerElement.append(createPlusButton(dataElement)); @@ -212,27 +212,74 @@ function promptCreateSelect() { return dataElement; } +let pressed = false; +function multiFire(timer, scaleSpeed, callback, ...args) { + if (!pressed) { + return; + } + if (timer > scaleSpeed) { + timer -= scaleSpeed; + } + callback(...args); + setTimeout(multiFire, timer, timer, scaleSpeed, callback, ...args) +} + function createMinusButton(dataElement) { + function doMinus() { + dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); + } const minusBtn = document.createElement("span"); minusBtn.textContent = "-"; minusBtn.classList.add("minus"); - minusBtn.onmousedown = () => { - dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); - }; + if (promptOptions.counterOptions?.multiFire) { + minusBtn.onmousedown = () => { + pressed = true; + multiFire(500, 100, doMinus); + }; + + } else { + minusBtn.onmousedown = () => { + doMinus(); + }; + } return minusBtn; } function createPlusButton(dataElement) { + function doPlus() { + dataElement.value = validateCounterInput(parseInt(dataElement.value) + 1); + } const plusBtn = document.createElement("span"); plusBtn.textContent = "+"; plusBtn.classList.add("plus"); - plusBtn.onmousedown = () => { - dataElement.value = validateCounterInput(parseInt(dataElement.value) + 1); - }; - + if (promptOptions.counterOptions?.multiFire) { + plusBtn.onmousedown = () => { + pressed = true; + multiFire(500, 100, doPlus); + }; + } else { + plusBtn.onmousedown = () => { + doPlus(); + }; + } return plusBtn; } +function promptCreateCounter() { + if (promptOptions.counterOptions?.multiFire) { + document.onmouseup = () => { + pressed = false; + }; + } + + const dataElement = promptCreateInput(); + + dataElement.style.width = "unset"; + dataElement.style["text-align"] = "center"; + + return dataElement; +} + //validate counter function validateCounterInput(input) { const min = promptOptions.counterOptions?.minimum; @@ -245,6 +292,6 @@ function validateCounterInput(input) { if (max !== undefined && input > max) { return max; } - + return input; } diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index eff1f907..ec272375 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -45,7 +45,7 @@ prompt({ | value | (optional, string) The default value for the input field. Defaults to null. | | type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input or 'counter' for a number counter with buttons. Defaults to 'input'. | | inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' -| counterOptions | (optional, object) minimum and maximum of counter in format: `{minimum: %int%, maximum: %int%} ` | +| counterOptions | (optional, object) minimum and maximum of counter, and if continuous input is enabled. format: `{minimum: %int%, maximum: %int%, multiFire: %boolean%`. min+max values defaults to null and multiFire defaults to false. | | selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | | useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | | width | (optional, integer) The width of the prompt window. Defaults to 370. | From 00468c7d0ec1db09786ae3385fc3a0413226e40a Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sun, 18 Apr 2021 00:44:22 +0300 Subject: [PATCH 39/52] use timeout ID to stop callback --- providers/prompt/page/prompt.js | 73 ++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 3cad04ac..b4759e2d 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -212,29 +212,60 @@ function promptCreateSelect() { return dataElement; } -let pressed = false; -function multiFire(timer, scaleSpeed, callback, ...args) { - if (!pressed) { - return; +let nextTimeoutID = null; + +/* Function execute callback in 3 accelerated intervals based on timer. + * Terminated from document.onmouseup() that is registered from promptCreateCounter() + * @param {function} callback: function to execute + * @param {object} timer: { + * * time: First delay in miliseconds. + * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially + * * scaleSpeed: Speed change per tick on first acceleration + * } + * @param {int} stepArgs: argument for callback representing Initial steps per click, default to 1 + * steps starts to increase when speed is too fast to notice + * @param {int} counter: used internally to decrease timer.limit + */ +function multiFire(callback, timer = { time: 500, scaleSpeed: 140, limit: 100 }, stepsArg = 1, counter = 0) { + callback(stepsArg); + + const nextTimeout = timer.time + + if (counter > 20) { + counter = 0 - stepsArg; + if (timer.limit > 1) { + timer.limit = timer.limit / 2; + } else { + stepsArg *= 2; + } } - if (timer > scaleSpeed) { - timer -= scaleSpeed; + if (timer.time != timer.limit) { + timer.time = timer.time > timer.limit ? + timer.time - timer.scaleSpeed : + timer.limit; } - callback(...args); - setTimeout(multiFire, timer, timer, scaleSpeed, callback, ...args) + + nextTimeoutID = setTimeout( + multiFire, //callback + nextTimeout, //timer + //multiFire args: + callback, + timer, + stepsArg, + counter + 1 + ) } function createMinusButton(dataElement) { - function doMinus() { - dataElement.value = validateCounterInput(parseInt(dataElement.value) - 1); + function doMinus(steps) { + dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps); } const minusBtn = document.createElement("span"); minusBtn.textContent = "-"; minusBtn.classList.add("minus"); if (promptOptions.counterOptions?.multiFire) { minusBtn.onmousedown = () => { - pressed = true; - multiFire(500, 100, doMinus); + multiFire(doMinus); }; } else { @@ -246,16 +277,15 @@ function createMinusButton(dataElement) { } function createPlusButton(dataElement) { - function doPlus() { - dataElement.value = validateCounterInput(parseInt(dataElement.value) + 1); + function doPlus(steps) { + dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps); } const plusBtn = document.createElement("span"); plusBtn.textContent = "+"; plusBtn.classList.add("plus"); if (promptOptions.counterOptions?.multiFire) { plusBtn.onmousedown = () => { - pressed = true; - multiFire(500, 100, doPlus); + multiFire(doPlus); }; } else { plusBtn.onmousedown = () => { @@ -268,7 +298,10 @@ function createPlusButton(dataElement) { function promptCreateCounter() { if (promptOptions.counterOptions?.multiFire) { document.onmouseup = () => { - pressed = false; + if (!!nextTimeoutID) { + clearTimeout(nextTimeoutID) + nextTimeoutID = null; + } }; } @@ -284,12 +317,12 @@ function promptCreateCounter() { function validateCounterInput(input) { const min = promptOptions.counterOptions?.minimum; const max = promptOptions.counterOptions?.maximum; - - if (min !== undefined && input < min) { + //note that !min/max would proc if min/max are 0 + if (min !== null && min !== undefined && input < min) { return min; } - if (max !== undefined && input > max) { + if (max !== null && max !== undefined && input > max) { return max; } From 72716afcd3fcba96714c360257d95cb77af125a8 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sun, 18 Apr 2021 01:44:18 +0300 Subject: [PATCH 40/52] lint --- menu.js | 6 +++--- providers/prompt/index.js | 8 ++++++-- providers/prompt/page/prompt.js | 18 ++++++++++++------ providers/prompt/readme.md | 6 +++--- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/menu.js b/menu.js index 588dad7c..dddce721 100644 --- a/menu.js +++ b/menu.js @@ -149,7 +149,7 @@ const mainMenuTemplate = (win) => [ type: "checkbox", checked: !!config.get("options.proxy"), click: (item) => { - setProxy(item, win); + setProxy(item, win); } }, { @@ -322,10 +322,10 @@ function setProxy(item, win) { }, type: 'input', icon: iconPath, - customStylesheet: path.join(__dirname, "providers", "prompt", "dark-prompt.css"), + customStylesheet: "dark", }; //TODO: custom bar on prompt need testing on macOS - if(!is.macOS()) { + if (!is.macOS()) { Object.assign(options, { frame: false, customScript: path.join(__dirname, "providers", "prompt", "custom-titlebar.js"), diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 009c8c7a..95babc9c 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -19,7 +19,7 @@ function electronPrompt(options, parentWindow) { const options_ = Object.assign( { width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH, - height:options?.type === "counter" ? DEFAULT_COUNTER_HEIGHT: DEFAULT_HEIGHT, + height: options?.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT, resizable: false, title: "Prompt", label: "Please input a value:", @@ -28,7 +28,7 @@ function electronPrompt(options, parentWindow) { value: null, type: "input", selectOptions: null, - counterOptions: {minimum: null, maximum: null, multiFire: false}, + counterOptions: { minimum: null, maximum: null, multiFire: false }, icon: null, useHtmlLabel: false, customStylesheet: null, @@ -44,6 +44,10 @@ function electronPrompt(options, parentWindow) { options_.minWidth = options?.minWidth || options?.width || options_.width; options_.minHeight = options?.minHeight || options?.height || options_.height; + if (options_.customStylesheet === "dark") { + options_.customStylesheet = require("path").join(__dirname, "dark-prompt.css"); + } + if (options_.type === "counter" && (options_.counterOptions !== null && typeof options_.selectOptions !== "object")) { reject(new Error('"counterOptions" must be an object if specified')); return; diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index b4759e2d..7f609b52 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -229,17 +229,18 @@ let nextTimeoutID = null; function multiFire(callback, timer = { time: 500, scaleSpeed: 140, limit: 100 }, stepsArg = 1, counter = 0) { callback(stepsArg); - const nextTimeout = timer.time + const nextTimeout = timer.time; if (counter > 20) { counter = 0 - stepsArg; if (timer.limit > 1) { - timer.limit = timer.limit / 2; + timer.limit /= 2; } else { stepsArg *= 2; } } - if (timer.time != timer.limit) { + + if (timer.time !== timer.limit) { timer.time = timer.time > timer.limit ? timer.time - timer.scaleSpeed : timer.limit; @@ -253,26 +254,28 @@ function multiFire(callback, timer = { time: 500, scaleSpeed: 140, limit: 100 }, timer, stepsArg, counter + 1 - ) + ); } function createMinusButton(dataElement) { function doMinus(steps) { dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps); } + const minusBtn = document.createElement("span"); minusBtn.textContent = "-"; minusBtn.classList.add("minus"); + if (promptOptions.counterOptions?.multiFire) { minusBtn.onmousedown = () => { multiFire(doMinus); }; - } else { minusBtn.onmousedown = () => { doMinus(); }; } + return minusBtn; } @@ -280,9 +283,11 @@ function createPlusButton(dataElement) { function doPlus(steps) { dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps); } + const plusBtn = document.createElement("span"); plusBtn.textContent = "+"; plusBtn.classList.add("plus"); + if (promptOptions.counterOptions?.multiFire) { plusBtn.onmousedown = () => { multiFire(doPlus); @@ -292,13 +297,14 @@ function createPlusButton(dataElement) { doPlus(); }; } + return plusBtn; } function promptCreateCounter() { if (promptOptions.counterOptions?.multiFire) { document.onmouseup = () => { - if (!!nextTimeoutID) { + if (nextTimeoutID) { clearTimeout(nextTimeoutID) nextTimeoutID = null; } diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index ec272375..a3775b8d 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -1,6 +1,6 @@ # Prompt Documentation -

prompt-preview

+

Simplest Prompt:
prompt-preview

## Usage ```js @@ -49,13 +49,13 @@ prompt({ | selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | | useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | | width | (optional, integer) The width of the prompt window. Defaults to 370. | -| minWidth | (optional, integer) The minimum allowed width for the prompt window. Same default value as width. | +| minWidth | (optional, integer) The minimum allowed width for the prompt window. Default to width if specified or default_width(370). | | | height | (optional, integer) The height of the prompt window. Defaults to 130. | | minHeight | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. | | resizable | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. | | alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false | | icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. | -| customStylesheet | (optional, string) The local path of a CSS file to stylize the prompt window. Defaults to null. | +| customStylesheet | (optional, string) The local path of a CSS file to customize the style of the prompt window, you can use just "dark" to use the premade dark skin. Defaults to null. | | menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. | | skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. | | frame | (optional, boolean) Wether to create prompt with frame. Defaults to true. | From a1940461688bb3ab42450e426a26fd8ff4f72edf Mon Sep 17 00:00:00 2001 From: Araxeus Date: Fri, 23 Apr 2021 04:06:54 +0300 Subject: [PATCH 41/52] `win.once()` instead of `win.on` --- plugins/in-app-menu/back.js | 2 +- plugins/taskbar-mediacontrol/back.js | 2 +- plugins/touchbar/back.js | 2 +- plugins/utils.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index 8365ac8b..82b0af4f 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -41,7 +41,7 @@ module.exports = (winImport) => { }); //set menu visibility on load - win.webContents.on("did-finish-load", () => { + win.webContents.once("did-finish-load", () => { // fix bug with menu not applying on start when no internet connection available setMenuVisibility(!config.get("options.hideMenu")); }); diff --git a/plugins/taskbar-mediacontrol/back.js b/plugins/taskbar-mediacontrol/back.js index b4d7dd3c..a8133539 100644 --- a/plugins/taskbar-mediacontrol/back.js +++ b/plugins/taskbar-mediacontrol/back.js @@ -24,7 +24,7 @@ module.exports = win => { const {playPause, next, previous} = getSongControls(win); // If the page is ready, register the callback - win.on('ready-to-show', () => { + win.once('ready-to-show', () => { registerCallback(songInfo => { // Wait for song to start before setting thumbar if (songInfo.title === '') { diff --git a/plugins/touchbar/back.js b/plugins/touchbar/back.js index 87240ed0..0fca17a7 100644 --- a/plugins/touchbar/back.js +++ b/plugins/touchbar/back.js @@ -63,7 +63,7 @@ module.exports = (win) => { const { playPause, next, previous, like, dislike } = getSongControls(win); // If the page is ready, register the callback - win.on("ready-to-show", () => { + win.once("ready-to-show", () => { controls = [previous, playPause, next, like, dislike]; // Register the callback diff --git a/plugins/utils.js b/plugins/utils.js index b265692f..ed8c7c0a 100644 --- a/plugins/utils.js +++ b/plugins/utils.js @@ -43,7 +43,7 @@ module.exports.fileExists = (path, callbackIfExists) => { }; module.exports.injectCSS = (webContents, filepath, cb = undefined) => { - webContents.on("did-finish-load", async () => { + webContents.once("did-finish-load", async () => { await webContents.insertCSS(fs.readFileSync(filepath, "utf8")); if (cb) { cb(); From eae4cca1484d610e18c71ea6d191b433bffc6dbe Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Fri, 23 Apr 2021 04:25:48 +0300 Subject: [PATCH 42/52] Update readme.md --- providers/prompt/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index a3775b8d..613b4676 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -1,6 +1,6 @@ -# Prompt Documentation +# Prompt Component Documentation -

Simplest Prompt:
prompt-preview

+

Simplest Prompt with no stylesheet:
prompt-preview

## Usage ```js From 5dc1179d54d6d0e7431c073e95309370cf8b268c Mon Sep 17 00:00:00 2001 From: Araxeus Date: Tue, 27 Apr 2021 23:48:06 +0300 Subject: [PATCH 43/52] implement keybind prompt --- providers/prompt/dark-prompt.css | 28 ++- providers/prompt/index.js | 34 ++-- providers/prompt/page/counter.js | 140 ++++++++++++++ providers/prompt/page/keybind.js | 305 +++++++++++++++++++++++++++++++ providers/prompt/page/prompt.css | 24 ++- providers/prompt/page/prompt.js | 120 ++++++------ 6 files changed, 578 insertions(+), 73 deletions(-) create mode 100644 providers/prompt/page/counter.js create mode 100644 providers/prompt/page/keybind.js diff --git a/providers/prompt/dark-prompt.css b/providers/prompt/dark-prompt.css index 8bf39791..768b9e52 100644 --- a/providers/prompt/dark-prompt.css +++ b/providers/prompt/dark-prompt.css @@ -18,7 +18,8 @@ body { overflow: hidden; } -#data { +#data, +.keybindData { background: unset; color: whitesmoke; border: 1px solid rgb(54, 54, 54); @@ -41,12 +42,35 @@ body { } #ok, -#cancel { +#cancel, +.clearButton { background-color: rgb(0, 0, 0); color: whitesmoke; } +/* For Counter Prompt */ .minus, .plus { background: rgb(0, 0, 0); } + +/* For Select Prompt */ +option { + background-color: #07070C; +} + +/* For Keybind Prompt */ +.clearButton:focus { + outline: none; +} +.clearButton:hover { + background-color: rgb(5, 5, 5); +} +.keybindData:hover { + border: 1px solid rgb(56, 0, 0); +} + +.keybindData:focus { + outline: 3px solid #1E0919; + border: 1px solid rgb(56, 0, 0); +} diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 95babc9c..a5c2230c 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -6,9 +6,10 @@ const url = require("url"); const path = require("path"); const DEFAULT_WIDTH = 370; +const DEFAULT_KEYBIND_WIDTH = 420; const DEFAULT_COUNTER_WIDTH = 300; -const DEFAULT_HEIGHT = 160; -const DEFAULT_COUNTER_HEIGHT = 150; +const DEFAULT_HEIGHT = 150; +const DEFAULT_KEYBIND_HEIGHT = options => (options.length * 40) + 100; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { @@ -18,8 +19,8 @@ function electronPrompt(options, parentWindow) { //custom options override default const options_ = Object.assign( { - width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH, - height: options?.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT, + width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : options?.type === "keybind" ? DEFAULT_KEYBIND_WIDTH : DEFAULT_WIDTH, + height: options?.type === "keybind" && options?.keybindOptions ? DEFAULT_KEYBIND_HEIGHT(options.keybindOptions) : DEFAULT_HEIGHT, resizable: false, title: "Prompt", label: "Please input a value:", @@ -28,6 +29,7 @@ function electronPrompt(options, parentWindow) { value: null, type: "input", selectOptions: null, + keybindOptions: null, counterOptions: { minimum: null, maximum: null, multiFire: false }, icon: null, useHtmlLabel: false, @@ -41,22 +43,21 @@ function electronPrompt(options, parentWindow) { options || {} ); - options_.minWidth = options?.minWidth || options?.width || options_.width; - options_.minHeight = options?.minHeight || options?.height || options_.height; + if (options_.customStylesheet === "dark") { options_.customStylesheet = require("path").join(__dirname, "dark-prompt.css"); } - if (options_.type === "counter" && (options_.counterOptions !== null && typeof options_.selectOptions !== "object")) { - reject(new Error('"counterOptions" must be an object if specified')); - return; + for (let type of ["counter", "select", "keybind"]) { + if (options_.type === type && (!options_[`${type}Options`] || typeof options_[`${type}Options`] !== "object")) { + reject(new Error(`"${type}Options" must be an object if type = ${type}`)); + return; + } } - if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { - reject(new Error('"selectOptions" must be an object')); - return; - } + options_.minWidth = options?.minWidth || options?.width || options_.width; + options_.minHeight = options?.minHeight || options?.height || options_.height; let promptWindow = new BrowserWindow({ frame: options_.frame, @@ -104,6 +105,11 @@ function electronPrompt(options, parentWindow) { //get input from front const postDataListener = (event, value) => { + if (options_.type === "keybind" && value) { + for (let i=0; i < value.length ;i++) { + value[i] = JSON.parse(value[i]) + } + } resolve(value); event.returnValue = null; cleanup(); @@ -135,7 +141,7 @@ function electronPrompt(options, parentWindow) { //should never happen promptWindow.webContents.on("did-fail-load", ( - event, + _event, errorCode, errorDescription, validatedURL diff --git a/providers/prompt/page/counter.js b/providers/prompt/page/counter.js new file mode 100644 index 00000000..6f773d97 --- /dev/null +++ b/providers/prompt/page/counter.js @@ -0,0 +1,140 @@ +const { promptCreateInput } = require("./prompt"); + +module.exports = { promptCreateCounter , validateCounterInput } + +let options; + + +function promptCreateCounter(promptOptions, parentElement) { + options = promptOptions; + if (options.counterOptions?.multiFire) { + document.onmouseup = () => { + if (nextTimeoutID) { + clearTimeout(nextTimeoutID) + nextTimeoutID = null; + } + }; + } + + options.value = validateCounterInput(options.value); + + const dataElement = promptCreateInput(); + dataElement.onkeypress = function isNumberKey(e) { + if (Number.isNaN(parseInt(e.key)) && e.key !== "Backspace" && e.key !== "Delete") + return false; + return true; + } + + dataElement.style.width = "unset"; + dataElement.style["text-align"] = "center"; + + parentElement.append(createMinusButton(dataElement)); + parentElement.append(dataElement); + parentElement.append(createPlusButton(dataElement)); + + return dataElement; +} + +let nextTimeoutID = null; + +/** Function execute callback in 3 accelerated intervals based on timer. + * Terminated from document.onmouseup() that is registered from promptCreateCounter() + * @param {function} callback function to execute + * @param {object} timer { + * * time: First delay in miliseconds + * * scaleSpeed: Speed change per tick on first acceleration + * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially + * } + * @param {int} stepArgs argument for callback representing Initial steps per click, default to 1 + * steps starts to increase when speed is too fast to notice + * @param {int} counter used internally to decrease timer.limit + */ +function multiFire(callback, timer = { time: 300, scaleSpeed: 100, limit: 100 }, stepsArg = 1, counter = 0) { + callback(stepsArg); + + const nextTimeout = timer.time; + + if (counter > 20) { + counter = 0 - stepsArg; + if (timer.limit > 1) { + timer.limit /= 2; + } else { + stepsArg *= 2; + } + } + + if (timer.time !== timer.limit) { + timer.time = Math.max(timer.time - timer.scaleSpeed, timer.limit) + } + + nextTimeoutID = setTimeout( + multiFire, //callback + nextTimeout, //timer + //multiFire args: + callback, + timer, + stepsArg, + counter + 1 + ); +} + +function createMinusButton(dataElement) { + function doMinus(steps) { + dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps); + } + + const minusBtn = document.createElement("span"); + minusBtn.textContent = "-"; + minusBtn.classList.add("minus"); + + if (options.counterOptions?.multiFire) { + minusBtn.onmousedown = () => { + multiFire(doMinus); + }; + } else { + minusBtn.onmousedown = () => { + doMinus(); + }; + } + + return minusBtn; +} + +function createPlusButton(dataElement) { + function doPlus(steps) { + dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps); + } + + const plusBtn = document.createElement("span"); + plusBtn.textContent = "+"; + plusBtn.classList.add("plus"); + + if (options.counterOptions?.multiFire) { + plusBtn.onmousedown = () => { + multiFire(doPlus); + }; + } else { + plusBtn.onmousedown = () => { + doPlus(); + }; + } + + return plusBtn; +} + +//validate counter +function validateCounterInput(input) { + + const min = options.counterOptions?.minimum; + const max = options.counterOptions?.maximum; + //note that !min/max would proc if min/max are 0 + if (min !== null && min !== undefined && input < min) { + return min; + } + + if (max !== null && max !== undefined && input > max) { + return max; + } + + return input; +} diff --git a/providers/prompt/page/keybind.js b/providers/prompt/page/keybind.js new file mode 100644 index 00000000..2406f736 --- /dev/null +++ b/providers/prompt/page/keybind.js @@ -0,0 +1,305 @@ +/* HTML + +
+ + + +
+ +*/ +/* CSS + +div.keybind { + display: grid; + grid-template-columns: max-content max-content max-content; + grid-gap: 5px; +} + +div.keybind button { + width: auto; +} + +div.keybind label { + text-align: right; +} + +div.keybind label:after { + content: ":"; +} + +*/ +const { promptError } = require("./prompt") + +class KeybindGetter { + value = null; + modifiers = null; + key = ""; + label = null; + txt = null; + clearButton = null; + + constructor(options, parentElement) { + if (!options.label || !options.value) { + promptError("keybind option must contain label and value"); + return; + } + + this.value = options.value + this.modifiers = new Set(); + this.key = ""; + + this.label = document.createElement("label"); + this.label.classList.add("keybindLabel"); + + this.txt = document.createElement("input"); + this.txt.setAttribute('readonly', true); + this.txt.classList.add("keybindData"); + + this.clearButton = document.createElement("button"); + this.clearButton.classList.add("clearButton"); + this.clearButton.textContent = "Clear"; + this.clearButton.onclick = (e) => e.preventDefault(); + + parentElement.append(this.label, this.txt, this.clearButton); + + this.setup(options); + if (options.default) { + this.setDefault(options.default) + } + } + + focus() { + this.txt.focus(); + } + + output() { + const output = {value: this.value, accelerator: this.txt.value.replaceAll(" ", "")} + return JSON.stringify(output); + } + + updateText() { + let result = ""; + for (let modifier of this.modifiers) { + result += modifier + " + "; + } + this.txt.value = result + this.key; + } + + setDefault(defaultValue) { + const accelerator = parseAccelerator(defaultValue).split("+"); + for (let key of accelerator) { + if (isModifier(key)) + this.modifiers.add(key); + else + this.key = key; + } + this.updateText(); + } + clear() { + this.modifiers.clear(); + this.key = ""; + this.txt.value = ""; + } + + setup(options) { + this.txt.addEventListener("keydown", (event) => { + event.preventDefault(); + if (event.repeat) { + return + } + let key = event.code || event.key; + if (key in virtualKeyCodes) + key = virtualKeyCodes[event.code]; + else { + console.log('Error, key "' + event.code + '" was not found'); + return; + } + + if (isModifier(key)) { + if (this.modifiers.size < 3) + this.modifiers.add(key); + } else { // is key + this.key = key; + } + this.updateText(); + }); + + this.clearButton.addEventListener("click", () => { + this.clear() + }); + this.label.textContent = options.label + " "; + } +} + +class keybindContainer { + elements = []; + + constructor(options, parentElement) { + parentElement.classList.add("keybind"); + this.elements = options.map(option => new KeybindGetter(option, parentElement)); + document.querySelector("#buttons").style["padding-top"] = "20px"; + } + + focus() { + if (this.elements.length > 0) + this.elements[0].focus(); + } + + submit() { + return this.elements.map(element => element.output()); + } +} + +function parseAccelerator(a) { + let accelerator = a.toString(); + + if (process.platform !== 'darwin') { + accelerator = accelerator.replace(/(Cmd)|(Command)/gi, ''); + } else { + accelerator = accelerator.replace(/(Ctrl)|(Control)/gi, ''); + } + + accelerator = accelerator.replace(/(Or)/gi, ''); + + return accelerator; +} + +function isModifier(key) { + for (let modifier of ["Shift", "Control", "Ctrl", "Command", "Cmd", "Alt", "AltGr", "Super"]) { + if (key === modifier) + return true; + } + return false; +} + +const virtualKeyCodes = { + ShiftLeft: "Shift", + ShiftRight: "Shift", + ControlLeft: "Ctrl", + ControlRight: "Ctrl", + AltLeft: "Alt", + AltRight: "Alt", + MetaLeft: "Super", + MetaRight: "Super", + NumLock: "NumLock", + NumpadDivide: "NumDiv", + NumpadMultiply: "NumMult", + NumpadSubtract: "NumSub", + NumpadAdd: "NumAdd", + NumpadDecimal: "NumDec ", + Numpad0: "Num0", + Numpad1: "Num1", + Numpad2: "Num2", + Numpad3: "Num3", + Numpad4: "Num4", + Numpad5: "Num5", + Numpad6: "Num6", + Numpad7: "Num7", + Numpad8: "Num8", + Numpad9: "Num9", + Digit0: "0", + Digit1: "1", + Digit2: "2", + Digit3: "3", + Digit4: "4", + Digit5: "5", + Digit6: "6", + Digit7: "7", + Digit8: "8", + Digit9: "9", + Minus: "-", + Equal: "=", + KeyQ: "Q", + KeyW: "W", + KeyE: "E", + KeyR: "R", + KeyT: "T", + KeyY: "Y", + KeyU: "U", + KeyI: "I", + KeyO: "O", + KeyP: "P", + KeyA: "A", + KeyS: "S", + KeyD: "D", + KeyF: "F", + KeyG: "G", + KeyH: "H", + KeyJ: "J", + KeyK: "K", + KeyL: "L", + KeyZ: "Z", + KeyX: "X", + KeyC: "C", + KeyV: "V", + KeyB: "B", + KeyN: "N", + KeyM: "M", + BracketLeft: "[", + BracketRight: "]", + Semicolon: ";", + Quote: "'", + Backquote: '"', + Backslash: "\\", + Comma: ",", + Period: "'.'", + Slash: "/", + plus: '+', + Space: "Space", + Tab: "Tab", + Backspace: "Backspace", + Delete: "Delete", + Insert: "Insert", + Return: "Return", + Enter: "Enter", + ArrowUp: "Up", + ArrowDown: "Down", + ArrowLeft: "Left", + ArrowRight: "Right", + Home: "Home", + End: "End", + PageUp: "PageUp", + PageDown: "PageDown", + Escape: "Escape", + AudioVolumeUp: "VolumeUp", + AudioVolumeDown: "VolumeDown", + AudioVolumeMute: "VolumeMute", + MediaTrackNext: "MediaNextTrack", + MediaTrackPrevious: "MediaPreviousTrack", + MediaStop: "MediaStop", + MediaPlayPause: "MediaPlayPause", + ScrollLock: "ScrollLock", + PrintScreen: "PrintScreen", + F1: "F1", + F2: "F2", + F3: "F3", + F4: "F4", + F5: "F5", + F6: "F6", + F7: "F7", + F8: "F8", + F9: "F9", + F10: "F10", + F11: "F11", + F12: "F12", + F13: "F13", + F14: "F14", + F15: "F15", + F16: "F16", + F17: "F17", + F18: "F18", + F19: "F19", + F20: "F20", + F21: "F21", + F22: "F22", + F23: "F23", + F24: "F24", +}; + + + +module.exports = function promptCreateKeybind(options, parentElement) { + return new keybindContainer(options, parentElement); +} diff --git a/providers/prompt/page/prompt.css b/providers/prompt/page/prompt.css index 3224cf6a..6e52379a 100644 --- a/providers/prompt/page/prompt.css +++ b/providers/prompt/page/prompt.css @@ -79,7 +79,7 @@ select#data { color: black; } -/* Counter mode css */ +/* Counter mode */ span { cursor: pointer; } @@ -96,3 +96,25 @@ span { vertical-align: middle; text-align: center; } + +/** Keybind mode */ +div.keybind { + display: grid; + grid-template-columns: max-content max-content max-content; + row-gap: 20px; + column-gap: 10px; + margin: auto 0; + justify-content: center; + } + + div.keybind button { + width: auto; + } + + div.keybind label { + text-align: right; + } + + div.keybind label:after { + content: ":"; + } \ No newline at end of file diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 7f609b52..e09816d3 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,16 +1,15 @@ const fs = require("fs"); const { ipcRenderer } = require("electron"); - let promptId = null; let promptOptions = null; +let dataElement = null; -function $(selector) { - return document.querySelector(selector); -} +function $(selector) { return document.querySelector(selector); } document.addEventListener("DOMContentLoaded", promptRegister); function promptRegister() { + //get custom session id promptId = document.location.hash.replace("#", ""); @@ -56,13 +55,12 @@ function promptRegister() { $("#form").addEventListener("submit", promptSubmit); $("#cancel").addEventListener("click", promptCancel); - //create input/select + //create input/select/counter/keybind const dataContainerElement = $("#data-container"); - let dataElement; switch (promptOptions.type) { case "counter": - dataElement = promptCreateCounter(); + dataElement = promptCreateCounter(dataContainerElement); break; case "input": dataElement = promptCreateInput(); @@ -70,22 +68,24 @@ function promptRegister() { case "select": dataElement = promptCreateSelect(); break; + case "keybind": + dataElement = require("./keybind")(promptOptions.keybindOptions, dataContainerElement); + break; default: return promptError(`Unhandled input type '${promptOptions.type}'`); } - if (promptOptions.type === "counter") { - dataContainerElement.append(createMinusButton(dataElement)); - dataContainerElement.append(dataElement); - dataContainerElement.append(createPlusButton(dataElement)); - } else { - dataContainerElement.append(dataElement); + if (promptOptions.type != "keybind") { + dataElement.setAttribute("id", "data"); + + if (promptOptions.type !== "counter") { + dataContainerElement.append(dataElement); + } } - dataElement.setAttribute("id", "data"); dataElement.focus(); - if (promptOptions.type === "input" || promptOptions.type === "counter") { + if (promptOptions.type !== "select" && promptOptions.type !== "keybind") { dataElement.select(); } @@ -100,10 +100,10 @@ function promptRegister() { } } -window.addEventListener("error", error => { +window.addEventListener("error", event => { if (promptId) { promptError("An error has occured on the prompt window: \n" + - JSON.stringify(error, ["message", "arguments", "type", "name"]) + `Message: ${event.message}\nURL: ${event.url}\nLine: ${event.lineNo}, Column: ${event.columnNo}\nStack: ${event.error.stack}` ); } }); @@ -111,7 +111,7 @@ window.addEventListener("error", error => { //send error to back function promptError(error) { if (error instanceof Error) { - error = error.message; + error = error.message + "\n" + error.stack; } ipcRenderer.sendSync("prompt-error:" + promptId, error); @@ -124,20 +124,18 @@ function promptCancel() { //transfer input data to back function promptSubmit() { - const dataElement = $("#data"); let data = null; switch (promptOptions.type) { case "input": + case "select": data = dataElement.value; break; case "counter": data = validateCounterInput(dataElement.value); break; - case "select": - data = promptOptions.selectMultiple ? - dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : - dataElement.value; + case "keybind": + data = dataElement.submit(); break; default: //will never happen return promptError(`Unhandled input type '${promptOptions.type}'`); @@ -152,9 +150,6 @@ function promptCreateInput() { dataElement.setAttribute("type", "text"); if (promptOptions.value) { - if (promptOptions.type === "counter") { - promptOptions.value = validateCounterInput(promptOptions.value); - } dataElement.value = promptOptions.value; } else { dataElement.value = ""; @@ -212,21 +207,50 @@ function promptCreateSelect() { return dataElement; } +function promptCreateCounter(parentElement) { + if (promptOptions.counterOptions?.multiFire) { + document.onmouseup = () => { + if (nextTimeoutID) { + clearTimeout(nextTimeoutID) + nextTimeoutID = null; + } + }; + } + + promptOptions.value = validateCounterInput(promptOptions.value); + + const dataElement = promptCreateInput(); + dataElement.onkeypress = function isNumberKey(e) { + if (Number.isNaN(parseInt(e.key)) && e.key !== "Backspace" && e.key !== "Delete") + return false; + return true; + } + + dataElement.style.width = "unset"; + dataElement.style["text-align"] = "center"; + + parentElement.append(createMinusButton(dataElement)); + parentElement.append(dataElement); + parentElement.append(createPlusButton(dataElement)); + + return dataElement; +} + let nextTimeoutID = null; -/* Function execute callback in 3 accelerated intervals based on timer. +/** Function execute callback in 3 accelerated intervals based on timer. * Terminated from document.onmouseup() that is registered from promptCreateCounter() - * @param {function} callback: function to execute - * @param {object} timer: { - * * time: First delay in miliseconds. - * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially + * @param {function} callback function to execute + * @param {object} timer { + * * time: First delay in miliseconds * * scaleSpeed: Speed change per tick on first acceleration - * } - * @param {int} stepArgs: argument for callback representing Initial steps per click, default to 1 + * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially + * } + * @param {int} stepArgs argument for callback representing Initial steps per click, default to 1 * steps starts to increase when speed is too fast to notice - * @param {int} counter: used internally to decrease timer.limit + * @param {int} counter used internally to decrease timer.limit */ -function multiFire(callback, timer = { time: 500, scaleSpeed: 140, limit: 100 }, stepsArg = 1, counter = 0) { +function multiFire(callback, timer = { time: 300, scaleSpeed: 100, limit: 100 }, stepsArg = 1, counter = 0) { callback(stepsArg); const nextTimeout = timer.time; @@ -241,9 +265,7 @@ function multiFire(callback, timer = { time: 500, scaleSpeed: 140, limit: 100 }, } if (timer.time !== timer.limit) { - timer.time = timer.time > timer.limit ? - timer.time - timer.scaleSpeed : - timer.limit; + timer.time = Math.max(timer.time - timer.scaleSpeed, timer.limit) } nextTimeoutID = setTimeout( @@ -301,26 +323,9 @@ function createPlusButton(dataElement) { return plusBtn; } -function promptCreateCounter() { - if (promptOptions.counterOptions?.multiFire) { - document.onmouseup = () => { - if (nextTimeoutID) { - clearTimeout(nextTimeoutID) - nextTimeoutID = null; - } - }; - } - - const dataElement = promptCreateInput(); - - dataElement.style.width = "unset"; - dataElement.style["text-align"] = "center"; - - return dataElement; -} - //validate counter function validateCounterInput(input) { + const min = promptOptions.counterOptions?.minimum; const max = promptOptions.counterOptions?.maximum; //note that !min/max would proc if min/max are 0 @@ -334,3 +339,6 @@ function validateCounterInput(input) { return input; } + +module.exports.promptError = promptError; + From 8dc486f18fe02a218b149838dc7ab939ec1b698a Mon Sep 17 00:00:00 2001 From: Araxeus Date: Wed, 28 Apr 2021 02:51:19 +0300 Subject: [PATCH 44/52] remove local prompt --- menu.js | 37 --- providers/prompt/custom-titlebar.js | 14 -- providers/prompt/dark-prompt.css | 76 ------ providers/prompt/index.js | 170 -------------- providers/prompt/page/counter.js | 140 ----------- providers/prompt/page/keybind.js | 305 ------------------------ providers/prompt/page/prompt.css | 120 ---------- providers/prompt/page/prompt.html | 18 -- providers/prompt/page/prompt.js | 344 ---------------------------- providers/prompt/readme.md | 80 ------- 10 files changed, 1304 deletions(-) delete mode 100644 providers/prompt/custom-titlebar.js delete mode 100644 providers/prompt/dark-prompt.css delete mode 100644 providers/prompt/index.js delete mode 100644 providers/prompt/page/counter.js delete mode 100644 providers/prompt/page/keybind.js delete mode 100644 providers/prompt/page/prompt.css delete mode 100644 providers/prompt/page/prompt.html delete mode 100644 providers/prompt/page/prompt.js delete mode 100644 providers/prompt/readme.md diff --git a/menu.js b/menu.js index dddce721..0ab62c7f 100644 --- a/menu.js +++ b/menu.js @@ -6,7 +6,6 @@ const is = require("electron-is"); const { getAllPlugins } = require("./plugins/utils"); const config = require("./config"); -const prompt = require('./providers/prompt'); const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, @@ -309,39 +308,3 @@ module.exports.setApplicationMenu = (win) => { const menu = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menu); }; - -const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); -const example = `Example: "socks5://127.0.0.1:9999"`; -function setProxy(item, win) { - let options = { - title: 'Set Proxy', - label: 'Enter Proxy Address: (leave empty to disable)', - value: config.get("options.proxy") || example, - inputAttrs: { - type: 'text' - }, - type: 'input', - icon: iconPath, - customStylesheet: "dark", - }; - //TODO: custom bar on prompt need testing on macOS - if (!is.macOS()) { - Object.assign(options, { - frame: false, - customScript: path.join(__dirname, "providers", "prompt", "custom-titlebar.js"), - enableRemoteModule: true, - height: 200, - width: 450, - }); - } - prompt(options, win) - .then(input => { - if (input !== null && input !== example) { - config.set("options.proxy", input); - item.checked = input !== ""; - } else { //user pressed cancel - item.checked = !item.checked; //reset checkbox - } - }) - .catch(console.error); -} diff --git a/providers/prompt/custom-titlebar.js b/providers/prompt/custom-titlebar.js deleted file mode 100644 index c36ce5f5..00000000 --- a/providers/prompt/custom-titlebar.js +++ /dev/null @@ -1,14 +0,0 @@ -const customTitlebar = require("custom-electron-titlebar"); - -module.exports = () => { - const bar = new customTitlebar.Titlebar({ - backgroundColor: customTitlebar.Color.fromHex("#050505"), - minimizable: false, - maximizable: false, - menu: null - }); - const mainStyle = document.querySelector("#container").style; - mainStyle.width = "100%"; - mainStyle.position = "fixed"; - mainStyle.border = "unset"; -}; diff --git a/providers/prompt/dark-prompt.css b/providers/prompt/dark-prompt.css deleted file mode 100644 index 768b9e52..00000000 --- a/providers/prompt/dark-prompt.css +++ /dev/null @@ -1,76 +0,0 @@ -body { - background-color: rgba(0, 0, 0, 0.3); - background-image: linear-gradient(315deg, #200000 0%, #13253a 74%); - color: whitesmoke; -} - -#label { - text-align: center; -} - -#container { - background: rgba(0, 0, 0, 0.7); - box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border-radius: 10px; - border: 1px solid rgba(80, 0, 0, 0.4); - overflow: hidden; -} - -#data, -.keybindData { - background: unset; - color: whitesmoke; - border: 1px solid rgb(54, 54, 54); -} - -#data:hover { - border: 1px solid rgb(85, 85, 85); -} - -#data:focus { - outline: unset; - border: 1px solid rgb(85, 85, 85); -} - -#ok:hover, -#ok:focus, -#cancel:hover, -#cancel:focus { - outline: rgba(60, 0, 0, 0.4) solid 2px; -} - -#ok, -#cancel, -.clearButton { - background-color: rgb(0, 0, 0); - color: whitesmoke; -} - -/* For Counter Prompt */ -.minus, -.plus { - background: rgb(0, 0, 0); -} - -/* For Select Prompt */ -option { - background-color: #07070C; -} - -/* For Keybind Prompt */ -.clearButton:focus { - outline: none; -} -.clearButton:hover { - background-color: rgb(5, 5, 5); -} -.keybindData:hover { - border: 1px solid rgb(56, 0, 0); -} - -.keybindData:focus { - outline: 3px solid #1E0919; - border: 1px solid rgb(56, 0, 0); -} diff --git a/providers/prompt/index.js b/providers/prompt/index.js deleted file mode 100644 index a5c2230c..00000000 --- a/providers/prompt/index.js +++ /dev/null @@ -1,170 +0,0 @@ -const electron = require("electron"); - -const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow; -const ipcMain = electron.ipcMain || electron.remote.ipcMain; -const url = require("url"); -const path = require("path"); - -const DEFAULT_WIDTH = 370; -const DEFAULT_KEYBIND_WIDTH = 420; -const DEFAULT_COUNTER_WIDTH = 300; -const DEFAULT_HEIGHT = 150; -const DEFAULT_KEYBIND_HEIGHT = options => (options.length * 40) + 100; - -function electronPrompt(options, parentWindow) { - return new Promise((resolve, reject) => { - //id used to ensure unique listeners per window - const id = `${Date.now()}-${Math.random()}`; - - //custom options override default - const options_ = Object.assign( - { - width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : options?.type === "keybind" ? DEFAULT_KEYBIND_WIDTH : DEFAULT_WIDTH, - height: options?.type === "keybind" && options?.keybindOptions ? DEFAULT_KEYBIND_HEIGHT(options.keybindOptions) : DEFAULT_HEIGHT, - resizable: false, - title: "Prompt", - label: "Please input a value:", - buttonLabels: null, - alwaysOnTop: false, - value: null, - type: "input", - selectOptions: null, - keybindOptions: null, - counterOptions: { minimum: null, maximum: null, multiFire: false }, - icon: null, - useHtmlLabel: false, - customStylesheet: null, - menuBarVisible: false, - skipTaskbar: true, - frame: true, - customScript: null, - enableRemoteModule: false - }, - options || {} - ); - - - - if (options_.customStylesheet === "dark") { - options_.customStylesheet = require("path").join(__dirname, "dark-prompt.css"); - } - - for (let type of ["counter", "select", "keybind"]) { - if (options_.type === type && (!options_[`${type}Options`] || typeof options_[`${type}Options`] !== "object")) { - reject(new Error(`"${type}Options" must be an object if type = ${type}`)); - return; - } - } - - options_.minWidth = options?.minWidth || options?.width || options_.width; - options_.minHeight = options?.minHeight || options?.height || options_.height; - - let promptWindow = new BrowserWindow({ - frame: options_.frame, - width: options_.width, - height: options_.height, - minWidth: options_.minWidth, - minHeight: options_.minHeight, - resizable: options_.resizable, - minimizable: !options_.skipTaskbar && !parentWindow && !options_.alwaysOnTop, - fullscreenable: options_.resizable, - maximizable: options_.resizable, - parent: parentWindow, - skipTaskbar: options_.skipTaskbar, - alwaysOnTop: options_.alwaysOnTop, - useContentSize: options_.resizable, - modal: Boolean(parentWindow), - title: options_.title, - icon: options_.icon || undefined, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: options_.enableRemoteModule - } - }); - - promptWindow.setMenu(null); - promptWindow.setMenuBarVisibility(options_.menuBarVisible); - - //called on exit - const cleanup = () => { - ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); - ipcMain.removeListener("prompt-post-data:" + id, postDataListener); - ipcMain.removeListener("prompt-error:" + id, errorListener); - - if (promptWindow) { - promptWindow.close(); - promptWindow = null; - } - }; - - ///transfer options to front - const getOptionsListener = event => { - event.returnValue = JSON.stringify(options_); - }; - - //get input from front - const postDataListener = (event, value) => { - if (options_.type === "keybind" && value) { - for (let i=0; i < value.length ;i++) { - value[i] = JSON.parse(value[i]) - } - } - resolve(value); - event.returnValue = null; - cleanup(); - }; - - const unresponsiveListener = () => { - reject(new Error("Window was unresponsive")); - cleanup(); - }; - - //get error from front - const errorListener = (event, message) => { - reject(new Error(message)); - event.returnValue = null; - cleanup(); - }; - - //attach listeners - ipcMain.on("prompt-get-options:" + id, getOptionsListener); - ipcMain.on("prompt-post-data:" + id, postDataListener); - ipcMain.on("prompt-error:" + id, errorListener); - promptWindow.on("unresponsive", unresponsiveListener); - - promptWindow.on("closed", () => { - promptWindow = null; - cleanup(); - resolve(null); - }); - - //should never happen - promptWindow.webContents.on("did-fail-load", ( - _event, - errorCode, - errorDescription, - validatedURL - ) => { - const log = { - error: "did-fail-load", - errorCode, - errorDescription, - validatedURL - }; - reject(new Error("prompt.html did-fail-load, log:\n" + log.toString())); - }); - - const promptUrl = url.format({ - protocol: "file", - slashes: true, - pathname: path.join(__dirname, "page", "prompt.html"), - hash: id - }); - - //Finally, load prompt - promptWindow.loadURL(promptUrl); - }); -} - -module.exports = electronPrompt; diff --git a/providers/prompt/page/counter.js b/providers/prompt/page/counter.js deleted file mode 100644 index 6f773d97..00000000 --- a/providers/prompt/page/counter.js +++ /dev/null @@ -1,140 +0,0 @@ -const { promptCreateInput } = require("./prompt"); - -module.exports = { promptCreateCounter , validateCounterInput } - -let options; - - -function promptCreateCounter(promptOptions, parentElement) { - options = promptOptions; - if (options.counterOptions?.multiFire) { - document.onmouseup = () => { - if (nextTimeoutID) { - clearTimeout(nextTimeoutID) - nextTimeoutID = null; - } - }; - } - - options.value = validateCounterInput(options.value); - - const dataElement = promptCreateInput(); - dataElement.onkeypress = function isNumberKey(e) { - if (Number.isNaN(parseInt(e.key)) && e.key !== "Backspace" && e.key !== "Delete") - return false; - return true; - } - - dataElement.style.width = "unset"; - dataElement.style["text-align"] = "center"; - - parentElement.append(createMinusButton(dataElement)); - parentElement.append(dataElement); - parentElement.append(createPlusButton(dataElement)); - - return dataElement; -} - -let nextTimeoutID = null; - -/** Function execute callback in 3 accelerated intervals based on timer. - * Terminated from document.onmouseup() that is registered from promptCreateCounter() - * @param {function} callback function to execute - * @param {object} timer { - * * time: First delay in miliseconds - * * scaleSpeed: Speed change per tick on first acceleration - * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially - * } - * @param {int} stepArgs argument for callback representing Initial steps per click, default to 1 - * steps starts to increase when speed is too fast to notice - * @param {int} counter used internally to decrease timer.limit - */ -function multiFire(callback, timer = { time: 300, scaleSpeed: 100, limit: 100 }, stepsArg = 1, counter = 0) { - callback(stepsArg); - - const nextTimeout = timer.time; - - if (counter > 20) { - counter = 0 - stepsArg; - if (timer.limit > 1) { - timer.limit /= 2; - } else { - stepsArg *= 2; - } - } - - if (timer.time !== timer.limit) { - timer.time = Math.max(timer.time - timer.scaleSpeed, timer.limit) - } - - nextTimeoutID = setTimeout( - multiFire, //callback - nextTimeout, //timer - //multiFire args: - callback, - timer, - stepsArg, - counter + 1 - ); -} - -function createMinusButton(dataElement) { - function doMinus(steps) { - dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps); - } - - const minusBtn = document.createElement("span"); - minusBtn.textContent = "-"; - minusBtn.classList.add("minus"); - - if (options.counterOptions?.multiFire) { - minusBtn.onmousedown = () => { - multiFire(doMinus); - }; - } else { - minusBtn.onmousedown = () => { - doMinus(); - }; - } - - return minusBtn; -} - -function createPlusButton(dataElement) { - function doPlus(steps) { - dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps); - } - - const plusBtn = document.createElement("span"); - plusBtn.textContent = "+"; - plusBtn.classList.add("plus"); - - if (options.counterOptions?.multiFire) { - plusBtn.onmousedown = () => { - multiFire(doPlus); - }; - } else { - plusBtn.onmousedown = () => { - doPlus(); - }; - } - - return plusBtn; -} - -//validate counter -function validateCounterInput(input) { - - const min = options.counterOptions?.minimum; - const max = options.counterOptions?.maximum; - //note that !min/max would proc if min/max are 0 - if (min !== null && min !== undefined && input < min) { - return min; - } - - if (max !== null && max !== undefined && input > max) { - return max; - } - - return input; -} diff --git a/providers/prompt/page/keybind.js b/providers/prompt/page/keybind.js deleted file mode 100644 index 2406f736..00000000 --- a/providers/prompt/page/keybind.js +++ /dev/null @@ -1,305 +0,0 @@ -/* HTML - -
- - - -
- -*/ -/* CSS - -div.keybind { - display: grid; - grid-template-columns: max-content max-content max-content; - grid-gap: 5px; -} - -div.keybind button { - width: auto; -} - -div.keybind label { - text-align: right; -} - -div.keybind label:after { - content: ":"; -} - -*/ -const { promptError } = require("./prompt") - -class KeybindGetter { - value = null; - modifiers = null; - key = ""; - label = null; - txt = null; - clearButton = null; - - constructor(options, parentElement) { - if (!options.label || !options.value) { - promptError("keybind option must contain label and value"); - return; - } - - this.value = options.value - this.modifiers = new Set(); - this.key = ""; - - this.label = document.createElement("label"); - this.label.classList.add("keybindLabel"); - - this.txt = document.createElement("input"); - this.txt.setAttribute('readonly', true); - this.txt.classList.add("keybindData"); - - this.clearButton = document.createElement("button"); - this.clearButton.classList.add("clearButton"); - this.clearButton.textContent = "Clear"; - this.clearButton.onclick = (e) => e.preventDefault(); - - parentElement.append(this.label, this.txt, this.clearButton); - - this.setup(options); - if (options.default) { - this.setDefault(options.default) - } - } - - focus() { - this.txt.focus(); - } - - output() { - const output = {value: this.value, accelerator: this.txt.value.replaceAll(" ", "")} - return JSON.stringify(output); - } - - updateText() { - let result = ""; - for (let modifier of this.modifiers) { - result += modifier + " + "; - } - this.txt.value = result + this.key; - } - - setDefault(defaultValue) { - const accelerator = parseAccelerator(defaultValue).split("+"); - for (let key of accelerator) { - if (isModifier(key)) - this.modifiers.add(key); - else - this.key = key; - } - this.updateText(); - } - clear() { - this.modifiers.clear(); - this.key = ""; - this.txt.value = ""; - } - - setup(options) { - this.txt.addEventListener("keydown", (event) => { - event.preventDefault(); - if (event.repeat) { - return - } - let key = event.code || event.key; - if (key in virtualKeyCodes) - key = virtualKeyCodes[event.code]; - else { - console.log('Error, key "' + event.code + '" was not found'); - return; - } - - if (isModifier(key)) { - if (this.modifiers.size < 3) - this.modifiers.add(key); - } else { // is key - this.key = key; - } - this.updateText(); - }); - - this.clearButton.addEventListener("click", () => { - this.clear() - }); - this.label.textContent = options.label + " "; - } -} - -class keybindContainer { - elements = []; - - constructor(options, parentElement) { - parentElement.classList.add("keybind"); - this.elements = options.map(option => new KeybindGetter(option, parentElement)); - document.querySelector("#buttons").style["padding-top"] = "20px"; - } - - focus() { - if (this.elements.length > 0) - this.elements[0].focus(); - } - - submit() { - return this.elements.map(element => element.output()); - } -} - -function parseAccelerator(a) { - let accelerator = a.toString(); - - if (process.platform !== 'darwin') { - accelerator = accelerator.replace(/(Cmd)|(Command)/gi, ''); - } else { - accelerator = accelerator.replace(/(Ctrl)|(Control)/gi, ''); - } - - accelerator = accelerator.replace(/(Or)/gi, ''); - - return accelerator; -} - -function isModifier(key) { - for (let modifier of ["Shift", "Control", "Ctrl", "Command", "Cmd", "Alt", "AltGr", "Super"]) { - if (key === modifier) - return true; - } - return false; -} - -const virtualKeyCodes = { - ShiftLeft: "Shift", - ShiftRight: "Shift", - ControlLeft: "Ctrl", - ControlRight: "Ctrl", - AltLeft: "Alt", - AltRight: "Alt", - MetaLeft: "Super", - MetaRight: "Super", - NumLock: "NumLock", - NumpadDivide: "NumDiv", - NumpadMultiply: "NumMult", - NumpadSubtract: "NumSub", - NumpadAdd: "NumAdd", - NumpadDecimal: "NumDec ", - Numpad0: "Num0", - Numpad1: "Num1", - Numpad2: "Num2", - Numpad3: "Num3", - Numpad4: "Num4", - Numpad5: "Num5", - Numpad6: "Num6", - Numpad7: "Num7", - Numpad8: "Num8", - Numpad9: "Num9", - Digit0: "0", - Digit1: "1", - Digit2: "2", - Digit3: "3", - Digit4: "4", - Digit5: "5", - Digit6: "6", - Digit7: "7", - Digit8: "8", - Digit9: "9", - Minus: "-", - Equal: "=", - KeyQ: "Q", - KeyW: "W", - KeyE: "E", - KeyR: "R", - KeyT: "T", - KeyY: "Y", - KeyU: "U", - KeyI: "I", - KeyO: "O", - KeyP: "P", - KeyA: "A", - KeyS: "S", - KeyD: "D", - KeyF: "F", - KeyG: "G", - KeyH: "H", - KeyJ: "J", - KeyK: "K", - KeyL: "L", - KeyZ: "Z", - KeyX: "X", - KeyC: "C", - KeyV: "V", - KeyB: "B", - KeyN: "N", - KeyM: "M", - BracketLeft: "[", - BracketRight: "]", - Semicolon: ";", - Quote: "'", - Backquote: '"', - Backslash: "\\", - Comma: ",", - Period: "'.'", - Slash: "/", - plus: '+', - Space: "Space", - Tab: "Tab", - Backspace: "Backspace", - Delete: "Delete", - Insert: "Insert", - Return: "Return", - Enter: "Enter", - ArrowUp: "Up", - ArrowDown: "Down", - ArrowLeft: "Left", - ArrowRight: "Right", - Home: "Home", - End: "End", - PageUp: "PageUp", - PageDown: "PageDown", - Escape: "Escape", - AudioVolumeUp: "VolumeUp", - AudioVolumeDown: "VolumeDown", - AudioVolumeMute: "VolumeMute", - MediaTrackNext: "MediaNextTrack", - MediaTrackPrevious: "MediaPreviousTrack", - MediaStop: "MediaStop", - MediaPlayPause: "MediaPlayPause", - ScrollLock: "ScrollLock", - PrintScreen: "PrintScreen", - F1: "F1", - F2: "F2", - F3: "F3", - F4: "F4", - F5: "F5", - F6: "F6", - F7: "F7", - F8: "F8", - F9: "F9", - F10: "F10", - F11: "F11", - F12: "F12", - F13: "F13", - F14: "F14", - F15: "F15", - F16: "F16", - F17: "F17", - F18: "F18", - F19: "F19", - F20: "F20", - F21: "F21", - F22: "F22", - F23: "F23", - F24: "F24", -}; - - - -module.exports = function promptCreateKeybind(options, parentElement) { - return new keybindContainer(options, parentElement); -} diff --git a/providers/prompt/page/prompt.css b/providers/prompt/page/prompt.css deleted file mode 100644 index 6e52379a..00000000 --- a/providers/prompt/page/prompt.css +++ /dev/null @@ -1,120 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, - sans-serif; - line-height: 1.5em; - color: #333; - background-color: #fff; - overflow-y: hidden; -} - -::-webkit-scrollbar { - width: 0 !important; - display: none; -} - -#container { - align-items: center; - justify-content: center; - display: flex; - height: 100%; - overflow-y: hidden; -} - -#form { - width: 100%; - padding-top: 0.5em; -} - -#label { - max-width: 100%; - max-height: 100%; - margin-bottom: 0.8em; - padding: 0 0.5em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -#data { - border-radius: 2px; - background: #fff; - width: 90%; - padding: 0.4em 0.5em; - border: 1px solid black; - min-height: 2em; - margin: 0 0 1.2em; -} - -select#data { - height: 2em; -} - -#data-container { - text-align: center; -} - -#buttons { - text-align: right; - padding: 0 0.5em 0 0; -} - -#buttons > button, -#buttons > input[type="submit"] { - border-radius: 2px; - border: 0; - margin: 0 0 0 0.5em; - font-size: 0.8em; - line-height: 1em; - padding: 0.6em 1em; -} - -#ok { - background-color: #3879d9; - color: white; -} - -#cancel { - background-color: #ddd; - color: black; -} - -/* Counter mode */ -span { - cursor: pointer; -} -.minus, -.plus { - user-select: none; - width: 20px; - height: 20px; - background: #f2f2f2; - border-radius: 4px; - padding: 8px 5px 8px 5px; - border: 1px solid #ddd; - display: inline-block; - vertical-align: middle; - text-align: center; -} - -/** Keybind mode */ -div.keybind { - display: grid; - grid-template-columns: max-content max-content max-content; - row-gap: 20px; - column-gap: 10px; - margin: auto 0; - justify-content: center; - } - - div.keybind button { - width: auto; - } - - div.keybind label { - text-align: right; - } - - div.keybind label:after { - content: ":"; - } \ No newline at end of file diff --git a/providers/prompt/page/prompt.html b/providers/prompt/page/prompt.html deleted file mode 100644 index cdbfa544..00000000 --- a/providers/prompt/page/prompt.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - -
-
-
...
-
-
- - -
-
-
- - - diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js deleted file mode 100644 index e09816d3..00000000 --- a/providers/prompt/page/prompt.js +++ /dev/null @@ -1,344 +0,0 @@ -const fs = require("fs"); -const { ipcRenderer } = require("electron"); -let promptId = null; -let promptOptions = null; -let dataElement = null; - -function $(selector) { return document.querySelector(selector); } - -document.addEventListener("DOMContentLoaded", promptRegister); - -function promptRegister() { - - //get custom session id - promptId = document.location.hash.replace("#", ""); - - //get options from back - try { - promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); - } catch (error) { - return promptError(error); - } - - //set label - if (promptOptions.useHtmlLabel) { - $("#label").innerHTML = promptOptions.label; - } else { - $("#label").textContent = promptOptions.label; - } - - //set button label - if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - $("#ok").textContent = promptOptions.buttonLabels.ok; - } - - if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - $("#cancel").textContent = promptOptions.buttonLabels.cancel; - } - - //inject custom stylesheet from options - if (promptOptions.customStylesheet) { - try { - const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); - if (customStyleContent) { - const customStyle = document.createElement("style"); - customStyle.setAttribute("rel", "stylesheet"); - customStyle.append(document.createTextNode(customStyleContent)); - document.head.append(customStyle); - } - } catch (error) { - return promptError(error); - } - } - - //add button listeners - $("#form").addEventListener("submit", promptSubmit); - $("#cancel").addEventListener("click", promptCancel); - - //create input/select/counter/keybind - const dataContainerElement = $("#data-container"); - - switch (promptOptions.type) { - case "counter": - dataElement = promptCreateCounter(dataContainerElement); - break; - case "input": - dataElement = promptCreateInput(); - break; - case "select": - dataElement = promptCreateSelect(); - break; - case "keybind": - dataElement = require("./keybind")(promptOptions.keybindOptions, dataContainerElement); - break; - default: - return promptError(`Unhandled input type '${promptOptions.type}'`); - } - - if (promptOptions.type != "keybind") { - dataElement.setAttribute("id", "data"); - - if (promptOptions.type !== "counter") { - dataContainerElement.append(dataElement); - } - } - - dataElement.focus(); - - if (promptOptions.type !== "select" && promptOptions.type !== "keybind") { - dataElement.select(); - } - - //load custom script from options - if (promptOptions.customScript) { - try { - const customScript = require(promptOptions.customScript); - customScript(); - } catch (error) { - return promptError(error); - } - } -} - -window.addEventListener("error", event => { - if (promptId) { - promptError("An error has occured on the prompt window: \n" + - `Message: ${event.message}\nURL: ${event.url}\nLine: ${event.lineNo}, Column: ${event.columnNo}\nStack: ${event.error.stack}` - ); - } -}); - -//send error to back -function promptError(error) { - if (error instanceof Error) { - error = error.message + "\n" + error.stack; - } - - ipcRenderer.sendSync("prompt-error:" + promptId, error); -} - -//send to back: input=null -function promptCancel() { - ipcRenderer.sendSync("prompt-post-data:" + promptId, null); -} - -//transfer input data to back -function promptSubmit() { - let data = null; - - switch (promptOptions.type) { - case "input": - case "select": - data = dataElement.value; - break; - case "counter": - data = validateCounterInput(dataElement.value); - break; - case "keybind": - data = dataElement.submit(); - break; - default: //will never happen - return promptError(`Unhandled input type '${promptOptions.type}'`); - } - - ipcRenderer.sendSync("prompt-post-data:" + promptId, data); -} - -//creates input box -function promptCreateInput() { - const dataElement = document.createElement("input"); - dataElement.setAttribute("type", "text"); - - if (promptOptions.value) { - dataElement.value = promptOptions.value; - } else { - dataElement.value = ""; - } - - //insert custom input attributes if in options - if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { - for (const k in promptOptions.inputAttrs) { - if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { - continue; - } - - dataElement.setAttribute(k, promptOptions.inputAttrs[k]); - } - } - - //Cancel/Exit on 'Escape' - dataElement.addEventListener("keyup", event => { - if (event.key === "Escape") { - promptCancel(); - } - }); - - //Confirm on 'Enter' - dataElement.addEventListener("keypress", event => { - if (event.key === "Enter") { - event.preventDefault(); - $("#ok").click(); - } - }); - - return dataElement; -} - -//create multiple select -function promptCreateSelect() { - const dataElement = document.createElement("select"); - let optionElement; - - for (const k in promptOptions.selectOptions) { - if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) { - continue; - } - - optionElement = document.createElement("option"); - optionElement.setAttribute("value", k); - optionElement.textContent = promptOptions.selectOptions[k]; - if (k === promptOptions.value) { - optionElement.setAttribute("selected", "selected"); - } - - dataElement.append(optionElement); - } - - return dataElement; -} - -function promptCreateCounter(parentElement) { - if (promptOptions.counterOptions?.multiFire) { - document.onmouseup = () => { - if (nextTimeoutID) { - clearTimeout(nextTimeoutID) - nextTimeoutID = null; - } - }; - } - - promptOptions.value = validateCounterInput(promptOptions.value); - - const dataElement = promptCreateInput(); - dataElement.onkeypress = function isNumberKey(e) { - if (Number.isNaN(parseInt(e.key)) && e.key !== "Backspace" && e.key !== "Delete") - return false; - return true; - } - - dataElement.style.width = "unset"; - dataElement.style["text-align"] = "center"; - - parentElement.append(createMinusButton(dataElement)); - parentElement.append(dataElement); - parentElement.append(createPlusButton(dataElement)); - - return dataElement; -} - -let nextTimeoutID = null; - -/** Function execute callback in 3 accelerated intervals based on timer. - * Terminated from document.onmouseup() that is registered from promptCreateCounter() - * @param {function} callback function to execute - * @param {object} timer { - * * time: First delay in miliseconds - * * scaleSpeed: Speed change per tick on first acceleration - * * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially - * } - * @param {int} stepArgs argument for callback representing Initial steps per click, default to 1 - * steps starts to increase when speed is too fast to notice - * @param {int} counter used internally to decrease timer.limit - */ -function multiFire(callback, timer = { time: 300, scaleSpeed: 100, limit: 100 }, stepsArg = 1, counter = 0) { - callback(stepsArg); - - const nextTimeout = timer.time; - - if (counter > 20) { - counter = 0 - stepsArg; - if (timer.limit > 1) { - timer.limit /= 2; - } else { - stepsArg *= 2; - } - } - - if (timer.time !== timer.limit) { - timer.time = Math.max(timer.time - timer.scaleSpeed, timer.limit) - } - - nextTimeoutID = setTimeout( - multiFire, //callback - nextTimeout, //timer - //multiFire args: - callback, - timer, - stepsArg, - counter + 1 - ); -} - -function createMinusButton(dataElement) { - function doMinus(steps) { - dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps); - } - - const minusBtn = document.createElement("span"); - minusBtn.textContent = "-"; - minusBtn.classList.add("minus"); - - if (promptOptions.counterOptions?.multiFire) { - minusBtn.onmousedown = () => { - multiFire(doMinus); - }; - } else { - minusBtn.onmousedown = () => { - doMinus(); - }; - } - - return minusBtn; -} - -function createPlusButton(dataElement) { - function doPlus(steps) { - dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps); - } - - const plusBtn = document.createElement("span"); - plusBtn.textContent = "+"; - plusBtn.classList.add("plus"); - - if (promptOptions.counterOptions?.multiFire) { - plusBtn.onmousedown = () => { - multiFire(doPlus); - }; - } else { - plusBtn.onmousedown = () => { - doPlus(); - }; - } - - return plusBtn; -} - -//validate counter -function validateCounterInput(input) { - - const min = promptOptions.counterOptions?.minimum; - const max = promptOptions.counterOptions?.maximum; - //note that !min/max would proc if min/max are 0 - if (min !== null && min !== undefined && input < min) { - return min; - } - - if (max !== null && max !== undefined && input > max) { - return max; - } - - return input; -} - -module.exports.promptError = promptError; - diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md deleted file mode 100644 index 613b4676..00000000 --- a/providers/prompt/readme.md +++ /dev/null @@ -1,80 +0,0 @@ -# Prompt Component Documentation - -

Simplest Prompt with no stylesheet:
prompt-preview

- -## Usage -```js -prompt([options, parentBrowserWindow]).then(...).catch(...) -``` -Promise resolve returns input - -If user presses cancel/exit window, input = null; - -On error, Prompise reject returns custom error message -## Example - -```js -const prompt = require('./providers/prompt'); - -prompt({ - title: 'Prompt example', - label: 'URL:', - value: 'http://example.org', - inputAttrs: { - type: 'url' - }, - type: 'input' -}) -.then((r) => { - if(r === null) { - console.log('user cancelled'); - } else { - console.log('result', r); - } -}) -.catch(console.error); -``` - -### Options object (optional) - -| Key | Explanation | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| title | (optional, string) The title of the prompt window. Defaults to 'Prompt'. | -| label | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. | -| buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. | -| value | (optional, string) The default value for the input field. Defaults to null. | -| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input or 'counter' for a number counter with buttons. Defaults to 'input'. | -| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' -| counterOptions | (optional, object) minimum and maximum of counter, and if continuous input is enabled. format: `{minimum: %int%, maximum: %int%, multiFire: %boolean%`. min+max values defaults to null and multiFire defaults to false. | -| selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | -| useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | -| width | (optional, integer) The width of the prompt window. Defaults to 370. | -| minWidth | (optional, integer) The minimum allowed width for the prompt window. Default to width if specified or default_width(370). | | -| height | (optional, integer) The height of the prompt window. Defaults to 130. | -| minHeight | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. | -| resizable | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. | -| alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false | -| icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. | -| customStylesheet | (optional, string) The local path of a CSS file to customize the style of the prompt window, you can use just "dark" to use the premade dark skin. Defaults to null. | -| menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. | -| skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. | -| frame | (optional, boolean) Wether to create prompt with frame. Defaults to true. | -| customScript | (optional, string) The local path of a JS file to run on preload. Defaults to null. | -| enableRemoteModule | (optional, boolean) Wether the prompt window have remote modules activated, Defaults to false. | - -If not supplied, it uses the defaults listed in the table above. - -### parentBrowserWindow (optional) - -The window in which to display the prompt on. If not supplied, the parent window of the prompt will be null. - -### customScript (optional) - -Create the script with the following template: - -```node -module.exports = () => { - // This function will be called as a preload script - // So you can use front features like `document.querySelector` -}; -``` From 0adb36cfb87102944b5ded57f745ff6ea75a7cf8 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Wed, 28 Apr 2021 03:08:25 +0300 Subject: [PATCH 45/52] leave debug for next PR --- index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.js b/index.js index 82ba695d..80ba9efe 100644 --- a/index.js +++ b/index.js @@ -38,9 +38,7 @@ if (config.get("options.proxy")) { } // Adds debug features like hotkeys for triggering dev tools and reload -require("electron-debug")({ - showDevTools: false //disable automatic devTools on new window -}); +require("electron-debug")(); // Prevent window being garbage collected let mainWindow; From b77643b92899fef607bae690e48a8b0354ee81d3 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Wed, 28 Apr 2021 03:54:59 +0300 Subject: [PATCH 46/52] remove proxy from menu --- menu.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/menu.js b/menu.js index 0ab62c7f..957d2b2b 100644 --- a/menu.js +++ b/menu.js @@ -143,14 +143,6 @@ const mainMenuTemplate = (win) => [ { label: "Advanced options", submenu: [ - { - label: "Proxy", - type: "checkbox", - checked: !!config.get("options.proxy"), - click: (item) => { - setProxy(item, win); - } - }, { label: "Disable hardware acceleration", type: "checkbox", From 442aafd2c56972614ba1f634c39a803a7f38db39 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sun, 2 May 2021 23:25:20 +0300 Subject: [PATCH 47/52] remove redundant dialog import --- index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index 80ba9efe..75d85348 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,6 @@ const path = require("path"); const electron = require("electron"); -const { dialog } = require("electron"); const is = require("electron-is"); const unhandled = require("electron-unhandled"); const { autoUpdater } = require("electron-updater"); @@ -327,7 +326,7 @@ function showUnresponsiveDialog(win, details) { if (!!details) { console.log("Unresponsive Error!\n"+JSON.stringify(details, null, "\t")) } - dialog.showMessageBox(win, { + electron.dialog.showMessageBox(win, { type: "error", title: "Window Unresponsive", message: "The Application is Unresponsive", From 1f69048c866a703c03c755573162ce1dc5d28b17 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 2 May 2021 23:25:34 +0300 Subject: [PATCH 48/52] minify switch case Co-authored-by: th-ch --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 80ba9efe..681b5a8e 100644 --- a/index.js +++ b/index.js @@ -341,7 +341,6 @@ function showUnresponsiveDialog(win, details) { case 2: app.quit(); break; - case 0: default: break; } From 02d16ca51084c1343bb0fb273f775971d2b844ee Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sun, 2 May 2021 23:25:46 +0300 Subject: [PATCH 49/52] fix typo Co-authored-by: th-ch --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 681b5a8e..9ee68492 100644 --- a/index.js +++ b/index.js @@ -331,7 +331,7 @@ function showUnresponsiveDialog(win, details) { type: "error", title: "Window Unresponsive", message: "The Application is Unresponsive", - details: "We are sorry for the inconveniance! please choose what to do:", + details: "We are sorry for the inconvenience! please choose what to do:", buttons: ["Wait", "Relaunch", "Quit"], cancelId: 0 }).then( result => { From 8f766bcbaad8da0ea3ec6f22e56fb9f893f27867 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Sun, 2 May 2021 23:41:16 +0300 Subject: [PATCH 50/52] resolve merge conflict --- plugins/taskbar-mediacontrol/back.js | 86 +++++++++++++--------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/plugins/taskbar-mediacontrol/back.js b/plugins/taskbar-mediacontrol/back.js index a8133539..46cb399e 100644 --- a/plugins/taskbar-mediacontrol/back.js +++ b/plugins/taskbar-mediacontrol/back.js @@ -2,57 +2,53 @@ const getSongControls = require('../../providers/song-controls'); const getSongInfo = require('../../providers/song-info'); const path = require('path'); +let controls; +let currentSongInfo; + module.exports = win => { - win.hide = function () { - win.minimize(); - win.setSkipTaskbar(true); - }; - - const show = win.show; - win.show = function () { - win.restore(); - win.focus(); - win.setSkipTaskbar(false); - show.apply(win); - }; - - win.isVisible = function () { - return !win.isMinimized(); - }; - const registerCallback = getSongInfo(win); - const {playPause, next, previous} = getSongControls(win); + const { playPause, next, previous } = getSongControls(win); + controls = { playPause, next, previous }; - // If the page is ready, register the callback - win.once('ready-to-show', () => { - registerCallback(songInfo => { - // Wait for song to start before setting thumbar - if (songInfo.title === '') { - return; - } - - // Win32 require full rewrite of components - win.setThumbarButtons([ - { - tooltip: 'Previous', - icon: get('backward.png'), - click() {previous(win.webContents);} - }, { - tooltip: 'Play/Pause', - // Update icon based on play state - icon: songInfo.isPaused ? get('play.png') : get('pause.png'), - click() {playPause(win.webContents);} - }, { - tooltip: 'Next', - icon: get('forward.png'), - click() {next(win.webContents);} - } - ]); - }); + registerCallback(songInfo => { + //update currentsonginfo for win.on('show') + currentSongInfo = songInfo; + // update thumbar + setThumbar(win, songInfo); }); + + // need to set thumbar again after win.show + win.on("show", () => { + setThumbar(win, currentSongInfo) + }) }; +function setThumbar(win, songInfo) { + // Wait for song to start before setting thumbar + if (!songInfo?.title) { + return; + } + + // Win32 require full rewrite of components + win.setThumbarButtons([ + { + tooltip: 'Previous', + icon: get('backward.png'), + click() { controls.previous(win.webContents); } + }, { + tooltip: 'Play/Pause', + // Update icon based on play state + icon: songInfo.isPaused ? get('play.png') : get('pause.png'), + click() { controls.playPause(win.webContents); } + }, { + tooltip: 'Next', + icon: get('forward.png'), + click() { controls.next(win.webContents); } + } + ]); +} + // Util function get(file) { - return path.join(__dirname,"assets", file); + return path.join(__dirname, "assets", file); } From d5b9e3c96029922812933350b655bd89de45ddcb Mon Sep 17 00:00:00 2001 From: Araxeus Date: Tue, 4 May 2021 01:22:47 +0300 Subject: [PATCH 51/52] stringify did-fail-load error directly preload front-logger simplify front-logger --- index.js | 6 +++--- preload.js | 3 +-- providers/front-logger.js | 8 +++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index e28607c1..8c98e2c9 100644 --- a/index.js +++ b/index.js @@ -168,7 +168,7 @@ app.once("browser-window-created", (event, win) => { frameProcessId, frameRoutingId, ) => { - const log = { + const log = JSON.stringify({ error: "did-fail-load", event, errorCode, @@ -177,9 +177,9 @@ app.once("browser-window-created", (event, win) => { isMainFrame, frameProcessId, frameRoutingId, - }; + }, null, "\t"); if (is.dev()) { - console.log(log.toString()); + console.log(log); } win.webContents.send("log", log); win.webContents.loadFile(path.join(__dirname, "error.html")); diff --git a/preload.js b/preload.js index 74860cec..e0ce9ffe 100644 --- a/preload.js +++ b/preload.js @@ -34,8 +34,7 @@ document.addEventListener("DOMContentLoaded", () => { fileExists(songInfoProviderPath, require(songInfoProviderPath)); // inject front logger - const loggerPath = path.join(__dirname, "providers", "front-logger.js") - fileExists(loggerPath, require(loggerPath)); + require("./providers/front-logger")(); // Add action for reloading global.reload = () => diff --git a/providers/front-logger.js b/providers/front-logger.js index 210e3ff8..986cbb6f 100644 --- a/providers/front-logger.js +++ b/providers/front-logger.js @@ -1,11 +1,9 @@ const { ipcRenderer } = require("electron"); function logToString(log) { - let string = (typeof log === "string") ? log : log.toString(); - if (!string || string.includes("[object Object]")) { - string = JSON.stringify(log, null, "\t"); - } - return string; + return (typeof log === "string") ? + log : + JSON.stringify(log, null, "\t"); } module.exports = () => { From fb81e1bdd5fd15dd97706eb0799b45966f7d9db1 Mon Sep 17 00:00:00 2001 From: Araxeus Date: Tue, 4 May 2021 02:04:17 +0300 Subject: [PATCH 52/52] ignore did-fail-load error code `-3` bug with in-app-menu --- index.js | 9 +++++---- providers/front-logger.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 8c98e2c9..81fd45b5 100644 --- a/index.js +++ b/index.js @@ -160,7 +160,7 @@ app.once("browser-window-created", (event, win) => { loadPlugins(win); win.webContents.on("did-fail-load", ( - event, + _event, errorCode, errorDescription, validatedURL, @@ -170,7 +170,6 @@ app.once("browser-window-created", (event, win) => { ) => { const log = JSON.stringify({ error: "did-fail-load", - event, errorCode, errorDescription, validatedURL, @@ -181,8 +180,10 @@ app.once("browser-window-created", (event, win) => { if (is.dev()) { console.log(log); } - win.webContents.send("log", log); - win.webContents.loadFile(path.join(__dirname, "error.html")); + if( !(config.plugins.isEnabled("in-app-menu") && errorCode === -3)) { // -3 is a false positive with in-app-menu + win.webContents.send("log", log); + win.webContents.loadFile(path.join(__dirname, "error.html")); + } }); win.webContents.on("will-prevent-unload", (event) => { diff --git a/providers/front-logger.js b/providers/front-logger.js index 986cbb6f..99da2329 100644 --- a/providers/front-logger.js +++ b/providers/front-logger.js @@ -7,7 +7,7 @@ function logToString(log) { } module.exports = () => { - ipcRenderer.on("log", (event, log) => { + ipcRenderer.on("log", (_event, log) => { console.log(logToString(log)); }); };