From afdcd2c2e04a1a406e0fb9e3efbe92ebfd65e1e2 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sun, 28 Feb 2021 21:55:49 +0100 Subject: [PATCH 01/10] Remove unused variables. These were left-overs from a 4 year old commit. :P --- lib/window.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/window.js b/lib/window.js index 5d13cf88b..37d70674c 100644 --- a/lib/window.js +++ b/lib/window.js @@ -28,9 +28,6 @@ module.exports = function Window (config, path, opts) { h('title', title) ) - var shouldShow = ${opts.show !== false} - var shouldConnect = ${opts.connect !== false} - document.documentElement.replaceChild(h('body', [ rootView(config, data) ]), document.body) From a2698c2e06c200ae7ec6b2bd99c2801133c8c99a Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sat, 27 Feb 2021 22:40:06 +0100 Subject: [PATCH 02/10] uninstall electron-spellcheck Electron introduced native spell-checking in version 8, and this library was subsequently abandoned. --- package-lock.json | 176 +--------------------------------------------- package.json | 1 - 2 files changed, 2 insertions(+), 175 deletions(-) diff --git a/package-lock.json b/package-lock.json index a13e8f1f7..6ca7e702e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,26 +10,6 @@ "integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==", "dev": true }, - "@aabuhijleh/electron-remote": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@aabuhijleh/electron-remote/-/electron-remote-1.4.0.tgz", - "integrity": "sha512-EG4ZXxqbFY4lpX55vctwz14mFrEOcOHFCMLH5z5lOl6fiviTqscy86tSlKwEE3/o3ExtdPr2tECgCogYYL7d+g==", - "requires": { - "debug": "^2.5.1", - "hashids": "^1.1.1", - "lodash.get": "^4.4.2", - "pify": "^2.3.0", - "rxjs": "^5.0.0-beta.12", - "xmlhttprequest": "^1.8.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -213,14 +193,6 @@ } } }, - "@felixrieseberg/spellchecker": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@felixrieseberg/spellchecker/-/spellchecker-4.0.12.tgz", - "integrity": "sha512-jLAPnRALB1I6Un8ldHVJfJid7m2R1qXoafFF/95sdm7R5VPOsZ3xTreZ/wLKO5x9AdsD2t9zpOcjDFTsCf3VzQ==", - "requires": { - "nan": "^2.14.0" - } - }, "@sammacbeth/random-access-idb-mutable-file": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@sammacbeth/random-access-idb-mutable-file/-/random-access-idb-mutable-file-0.1.1.tgz", @@ -1250,11 +1222,6 @@ "resolved": "https://registry.npmjs.org/bash-color/-/bash-color-0.0.4.tgz", "integrity": "sha1-6b6M4zVAytpIgXaMWb1jhlc26RM=" }, - "bcp47": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", - "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=" - }, "bencode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bencode/-/bencode-1.0.0.tgz", @@ -1915,17 +1882,6 @@ "indexof": "0.0.1" } }, - "cld": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/cld/-/cld-2.5.1.tgz", - "integrity": "sha512-DwdvvcFVizwDdPCocoPPReFk3BwLEaTZ3RzFgJ4jLzsBzJKUC3cTna0ZmAZG4tFtMmQdl0ciso3+ijkH3OPZPA==", - "requires": { - "glob": "^5.0.10", - "nan": "^2.9.2", - "rimraf": "^2.4.0", - "underscore": "^1.6.0" - } - }, "cli-boxes": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", @@ -2935,40 +2891,6 @@ } } }, - "electron-spellchecker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/electron-spellchecker/-/electron-spellchecker-2.2.1.tgz", - "integrity": "sha512-IqxJmYq/5qyTNo9ONxHr9D/9UxiXVvDbl01s2f71S3aAHtDIc7I7qqEycvNUlFGR1WVBgFc/VzV4+deQwxgikA==", - "requires": { - "@aabuhijleh/electron-remote": "^1.4.0", - "@felixrieseberg/spellchecker": "^4.0.12", - "bcp47": "^1.1.2", - "cld": "^2.5.1", - "debug": "^4.1.1", - "keyboard-layout": "^2.0.16", - "lru-cache": "^5.1.1", - "mkdirp": "^0.5.1", - "pify": "^4.0.1", - "rxjs": "^5.0.1", - "rxjs-serial-subscription": "^0.1.1", - "spawn-rx": "^2.0.7" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "electron-window-state": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz", @@ -3800,11 +3722,6 @@ "es5-ext": "~0.10.14" } }, - "event-kit": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.5.3.tgz", - "integrity": "sha512-b7Qi1JNzY4BfAYfnIRanLk0DOD1gdkWHT4GISIn8Q2tAf3LpU8SP2CMwWaq40imYoKWbtN4ZhbSRxvsnikooZQ==" - }, "execa": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/execa/-/execa-0.5.1.tgz", @@ -4450,18 +4367,6 @@ "pinkie-promise": "^2.0.0" } }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -4623,11 +4528,6 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, - "hashids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/hashids/-/hashids-1.2.2.tgz", - "integrity": "sha512-dEHCG2LraR6PNvSGxosZHIRgxF5sNLOIBFEHbj8lfP9WWmu/PWPMzsip1drdVSOFi51N2pU7gZavrgn7sbGFuw==" - }, "hashlru": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", @@ -5545,15 +5445,6 @@ } } }, - "keyboard-layout": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.16.tgz", - "integrity": "sha512-eGrxmlV6jbm/mbPEOpYGuH53XEC7wIUj9ZxKcT2z9QHJ/RwrT9iVkvxka9zRxqHZHwQzcffgsa5OxoVAKnhK9w==", - "requires": { - "event-kit": "^2.0.0", - "nan": "^2.13.2" - } - }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -5796,11 +5687,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, "lodash.concat": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.concat/-/lodash.concat-4.5.0.tgz", @@ -5882,14 +5768,6 @@ "inherits": "^2.0.1" } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, "ltgt": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", @@ -6361,11 +6239,6 @@ "zerr": "^1.0.4" } }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" - }, "napi-macros": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", @@ -7116,7 +6989,8 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true }, "pinkie": { "version": "2.0.4", @@ -8472,22 +8346,6 @@ "resolved": "https://registry.npmjs.org/rwlock/-/rwlock-5.0.0.tgz", "integrity": "sha1-iI1qd6M1HMGiCSBO8u4XIgk4Ns8=" }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "requires": { - "symbol-observable": "1.0.1" - } - }, - "rxjs-serial-subscription": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/rxjs-serial-subscription/-/rxjs-serial-subscription-0.1.1.tgz", - "integrity": "sha1-pCsdsL8QlLCSMRkeJ3jKP8+e0Uc=", - "requires": { - "rxjs": "^5.0.0-beta.12" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -8951,16 +8809,6 @@ "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-6.13.0.tgz", "integrity": "sha512-gHOtAjPG2e2VK7o9yNevMrj9oJcACrazTI6pER97PfPF2Vdmp10aJPYc0QnrPmB6zHFJtbVboPk296rIICi0lw==" }, - "spawn-rx": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz", - "integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==", - "requires": { - "debug": "^2.5.1", - "lodash.assign": "^4.2.0", - "rxjs": "^5.1.1" - } - }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -10777,11 +10625,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -11092,11 +10935,6 @@ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -11603,11 +11441,6 @@ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -11619,11 +11452,6 @@ "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", "dev": true }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 8b22fcadf..9b98d35e3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "depject": "^4.1.1", "depnest": "^1.3.0", "electron-default-menu": "~1.0.1", - "electron-spellchecker": "^2.2.1", "electron-window-state": "^5.0.3", "escape-string-regexp": "^4.0.0", "fix-path": "^3.0.0", From 9b514132e92bd744e5003fc4f189a46ed5cdbf7e Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sat, 27 Feb 2021 22:41:20 +0100 Subject: [PATCH 03/10] Don't use deleted lib anymore --- lib/main-window.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/main-window.js b/lib/main-window.js index 5022528b9..8418c17c3 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -10,7 +10,6 @@ const themes = require('../styles') const nest = require('depnest') const LatestUpdate = require('./latest-update') const ref = require('ssb-ref') -const setupContextMenuAndSpellCheck = require('./context-menu-and-spellcheck') const watch = require('mutant/watch') const requireStyle = require('require-style') const ssbUri = require('ssb-uri') @@ -55,7 +54,6 @@ module.exports = function (config) { const i18n = api.intl.sync.i18n const language = api.settings.obs.get('patchwork.lang', '')() moment.locale(language) - setupContextMenuAndSpellCheck(api.config.sync.load(), { navigate, getMessageText, language }) const id = api.keys.sync.id() const latestUpdate = LatestUpdate() From fe4e56b2dea67a6b17e1545cc56ea259702342b1 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sun, 28 Feb 2021 01:23:51 +0100 Subject: [PATCH 04/10] Bump electron. To the latest version. Seems to work, mostly. There's some slowness happening in the search suggestions & completions, and of course there's still no context menu. But posting & scuttling & reading still works, so there's that. This still relies on the `remote` module, which is deprecated and considered harmful. Need to work around that in the future, which... makes sense. --- lib/window.js | 3 +- package-lock.json | 99 ++++++++++++++++++++++++++--------------------- package.json | 2 +- 3 files changed, 58 insertions(+), 46 deletions(-) diff --git a/lib/window.js b/lib/window.js index 37d70674c..106ce3e9a 100644 --- a/lib/window.js +++ b/lib/window.js @@ -6,7 +6,8 @@ module.exports = function Window (config, path, opts) { const window = new electron.BrowserWindow(extend({ show: false, webPreferences: { - nodeIntegration: true // XXX: Maybe not always necessary (?) + nodeIntegration: true, // XXX: Maybe not always necessary (?) + enableRemoteModule: true, } }, opts)) diff --git a/package-lock.json b/package-lock.json index 6ca7e702e..6281cf30e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110,9 +110,9 @@ } }, "@electron/get": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.2.tgz", - "integrity": "sha512-vAuHUbfvBQpYTJ5wB7uVIDq5c/Ry0fiTBMs7lnEYAo/qXXppIVcWdfBr57u6eRnKdVso7KSiH6p/LbQAG6Izrg==", + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz", + "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==", "dev": true, "requires": { "debug": "^4.1.1", @@ -122,17 +122,17 @@ "global-tunnel-ng": "^2.7.1", "got": "^9.6.0", "progress": "^2.0.3", - "sanitize-filename": "^1.6.2", + "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -1335,9 +1335,9 @@ } }, "boolean": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", - "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.2.tgz", + "integrity": "sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g==", "dev": true, "optional": true }, @@ -2650,9 +2650,9 @@ } }, "electron": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-8.5.5.tgz", - "integrity": "sha512-e355H+tRDial0m+X2v+l+0SnaATAPw4sNjv9qmdk/6MJz/glteVJwVJEnxTjPfEELIJSChrBWDBVpjdDvoBF4Q==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-11.3.0.tgz", + "integrity": "sha512-MhdS0gok3wZBTscLBbYrOhLaQybCSAfkupazbK1dMP5c+84eVMxJE/QGohiWQkzs0tVFIJsAHyN19YKPbelNrQ==", "dev": true, "requires": { "@electron/get": "^1.0.1", @@ -2661,9 +2661,9 @@ }, "dependencies": { "@types/node": { - "version": "12.19.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.14.tgz", - "integrity": "sha512-2U9uLN46+7dv9PiS8VQJcHhuoOjiDPZOLAt0WuA1EanEknIMae+2QbMhayF7cgGqjvRVIfNpt+6jLPczJZFiRw==", + "version": "12.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.4.tgz", + "integrity": "sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ==", "dev": true } } @@ -4393,18 +4393,21 @@ }, "dependencies": { "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.0.tgz", + "integrity": "sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ==", "dev": true, "optional": true }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -4436,9 +4439,9 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" }, "globalthis": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", - "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", "dev": true, "optional": true, "requires": { @@ -5768,6 +5771,16 @@ "inherits": "^2.0.1" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, "ltgt": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", @@ -5843,15 +5856,6 @@ "optional": true, "requires": { "escape-string-regexp": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "optional": true - } } }, "math-interval-parser": { @@ -8309,13 +8313,13 @@ "integrity": "sha1-30PoDZvIKtRDC8/vA/SccX6LLow=" }, "roarr": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.3.tgz", - "integrity": "sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==", + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "optional": true, "requires": { - "boolean": "^3.0.0", + "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", @@ -10604,12 +10608,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -11452,6 +11456,13 @@ "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 9b98d35e3..fdcf82644 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "devDependencies": { "changelog-version": "^2.0.0", "colors": "^1.3.3", - "electron": "^8.5.5", + "electron": "^11.3.0", "electron-builder": "^22.9.1", "standard": "^16.0.3" }, From 2d7dba965d21cf17cf96b29032c8aa1e027e73c9 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sun, 28 Feb 2021 09:19:46 +0100 Subject: [PATCH 05/10] Handle fullscreen state without accessing remote.window First step of getting remote out of the codebase. --- lib/fullscreen.js | 8 ++++---- lib/window.js | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/fullscreen.js b/lib/fullscreen.js index 9e7d5ba85..12a921a36 100644 --- a/lib/fullscreen.js +++ b/lib/fullscreen.js @@ -2,12 +2,12 @@ const electron = require('electron') const { Value } = require('mutant') module.exports = function () { - const win = electron.remote.getCurrentWindow() - const isFullScreen = Value(win.isFullScreen()) - win.on('enter-full-screen', () => { + const isFullScreen = Value(false) + // receive the OS window state from the main process + electron.ipcRenderer.on('enter-full-screen', () => { isFullScreen.set(true) }) - win.on('leave-full-screen', () => { + electron.ipcRenderer.on('leave-full-screen', () => { isFullScreen.set(false) }) return isFullScreen diff --git a/lib/window.js b/lib/window.js index 106ce3e9a..467eed7bd 100644 --- a/lib/window.js +++ b/lib/window.js @@ -11,6 +11,14 @@ module.exports = function Window (config, path, opts) { } }, opts)) + // have to forward the OS window state to the renderer because it cannot + // access directly + window.on('enter-full-screen', (event, alwaysOnTop) => { + window.webContents.send("enter-full-screen"); + }) + window.on('leave-full-screen', (event, alwaysOnTop) => { + window.webContents.send("leave-full-screen"); + }) electron.ipcMain.on('ready-to-show', handleReadyToShow) window.webContents.on('dom-ready', function () { From 8ccc9fb88d1bc2aa39a121fca766f8ba30115656 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sun, 28 Feb 2021 10:15:11 +0100 Subject: [PATCH 06/10] Expose host console and exit via ipcRenderer.send Another step towards abolishing `remote`. --- assets/base.html | 15 +++++++++++---- index.js | 4 ++++ package.json | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/assets/base.html b/assets/base.html index b6d7bcd0d..42991448d 100644 --- a/assets/base.html +++ b/assets/base.html @@ -7,8 +7,14 @@ var electron = require('electron') var localLog = console.log var localError = console.error - var remoteLog = electron.remote.getGlobal('console').log - var remoteError = electron.remote.getGlobal('console').error + var remoteLog = function (o) { + const payload = JSON.parse(JSON.stringify(o, null, 2)) + electron.ipcRenderer.invoke('consoleLog', `${payload}`) + } + var remoteError = function (o) { + const payload = JSON.parse(JSON.stringify(o, null, 2)) + electron.ipcRenderer.invoke('consoleError', `${payload}`) + } console.log = function (...args) { localLog.apply(console, args) @@ -20,11 +26,12 @@ remoteError(...args) } - process.exit = electron.remote.app.quit + process.exit = function(status) {electron.ipcRenderer.sendSync('exit')} // redirect errors to stderr window.addEventListener('error', function (e) { e.preventDefault() - console.error(e.error.stack || 'Uncaught ' + e.error) + console.log(e) + console.error(e.error?.stack || 'Uncaught ' + e.error) }) diff --git a/index.js b/index.js index 1673efa1b..66a6fd15c 100644 --- a/index.js +++ b/index.js @@ -150,6 +150,10 @@ electron.app.on('ready', () => { quitting = true }) + electron.ipcMain.handle('consoleLog', (ev, o) => console.log(o)) + electron.ipcMain.handle('consoleError', (ev, o) => console.error(o)) + electron.ipcMain.on('exit', (ev, code) => process.exit(code)) + electron.ipcMain.on('open-background-devtools', function () { if (windows.background) { windows.background.webContents.openDevTools({ mode: 'detach' }) diff --git a/package.json b/package.json index fdcf82644..46f701fab 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "deep-equal": "^2.0.1", "depject": "^4.1.1", "depnest": "^1.3.0", + "electron-context-menu": "^2.5.0", "electron-default-menu": "~1.0.1", "electron-window-state": "^5.0.3", "escape-string-regexp": "^4.0.0", From 809d3903e0c5c12da6a0c16b9bee86f7c03df6e6 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sun, 28 Feb 2021 21:03:43 +0100 Subject: [PATCH 07/10] Handle badgeCount updates without remote --- index.js | 3 +++ lib/main-window.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 66a6fd15c..47b2c8874 100644 --- a/index.js +++ b/index.js @@ -152,6 +152,9 @@ electron.app.on('ready', () => { electron.ipcMain.handle('consoleLog', (ev, o) => console.log(o)) electron.ipcMain.handle('consoleError', (ev, o) => console.error(o)) + electron.ipcMain.handle('badgeCount', (ev, count) => { + electron.app.badgeCount = count; + }); electron.ipcMain.on('exit', (ev, code) => process.exit(code)) electron.ipcMain.on('open-background-devtools', function () { diff --git a/lib/main-window.js b/lib/main-window.js index 8418c17c3..15da47bf3 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -134,7 +134,7 @@ module.exports = function (config) { const pendingCount = views.get('/mentions').pendingUpdates watch(pendingCount, count => { - electron.remote.app.badgeCount = count + electron.ipcRenderer.invoke('badgeCount', count) }) electron.ipcRenderer.on('goForward', views.goForward) From a40d71e4d68bf04bb03d8040fbf7614880514dc8 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Mon, 1 Mar 2021 09:33:35 +0100 Subject: [PATCH 08/10] Do window content loading without remote We were literally sending over a bunch of javascript code, and using remote to execute it. Now it's done via a handler. --- assets/base.html | 22 ++++++++++++++++++++++ lib/window.js | 25 ++++++------------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/assets/base.html b/assets/base.html index 42991448d..24c188859 100644 --- a/assets/base.html +++ b/assets/base.html @@ -33,6 +33,28 @@ console.log(e) console.error(e.error?.stack || 'Uncaught ' + e.error) }) + + electron.ipcRenderer.once('window-setup', (event, msg) => { + const { + config, + rootPath, + data, + title, + } = msg + var rootView = require(rootPath) + var h = require('mutant/h') + + electron.webFrame.setVisualZoomLevelLimits(1, 1) + + document.documentElement.querySelector('head').appendChild( + h('title', title) + ) + + document.documentElement.replaceChild(h('body', [ + rootView(config, data) + ]), document.body) + }) + diff --git a/lib/window.js b/lib/window.js index 467eed7bd..9c0249a24 100644 --- a/lib/window.js +++ b/lib/window.js @@ -22,25 +22,12 @@ module.exports = function Window (config, path, opts) { electron.ipcMain.on('ready-to-show', handleReadyToShow) window.webContents.on('dom-ready', function () { - window.webContents.executeJavaScript(` - var electron = require('electron') - var rootView = require(${JSON.stringify(path)}) - var h = require('mutant/h') - - electron.webFrame.setVisualZoomLevelLimits(1, 1) - - var config = ${JSON.stringify(config)} - var data = ${JSON.stringify(opts.data)} - var title = ${JSON.stringify(opts.title || 'Patchwork')} - - document.documentElement.querySelector('head').appendChild( - h('title', title) - ) - - document.documentElement.replaceChild(h('body', [ - rootView(config, data) - ]), document.body) - `) + window.webContents.send('window-setup', { + rootPath: path, + config: config, + data: opts.data || '', + title: opts.title || 'Patchwork', + }) }) // setTimeout(function () { From 8a334802737174dcd646eb0ad5d94fc718bb4724 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Tue, 2 Mar 2021 22:03:13 +0100 Subject: [PATCH 09/10] Do "More" drop-down without using electron.remote This one was a bit tougher. Basically I'm now creating a *descriptive* structure on the renderer side, and then adding the event handlers on the server side. The event handlers then simply trigger a navigation IPC message. --- index.js | 41 +++++++++++++ lib/main-window.js | 141 ++++++++++++++++++++++++--------------------- 2 files changed, 117 insertions(+), 65 deletions(-) diff --git a/index.js b/index.js index 47b2c8874..add3a7611 100644 --- a/index.js +++ b/index.js @@ -150,6 +150,19 @@ electron.app.on('ready', () => { quitting = true }) + electron.ipcMain.handle('navigation-menu-popup', (event, data) => { + const {items, x, y} = data + const window = event.sender + const factor = event.sender.zoomFactor + const menuItems = buildMenu(items, window) + const menu = electron.Menu.buildFromTemplate(menuItems); + menu.popup({ + window, + x: Math.round(x * factor), + y: Math.round(y * factor) + 4, + }); + }) + electron.ipcMain.handle('consoleLog', (ev, o) => console.log(o)) electron.ipcMain.handle('consoleError', (ev, o) => console.error(o)) electron.ipcMain.handle('badgeCount', (ev, count) => { @@ -164,6 +177,34 @@ electron.app.on('ready', () => { }) }) +function buildMenu(items, window) { + const result = [] + for (let item of items) { + switch (item.type) { + case 'separator': + result.push(item) + break + case 'submenu': + result.push({ + ...item, + submenu: buildMenu(item.submenu, window), + }) + break + case 'normal': + result.push({ + ...item, + click: () => { + window.send('navigate-to', item.target) + } + }) + break + default: + throw Error(`Unknown menu item of type "${item.type}": ${JSON.stringify(item, null, 2)}`); + } + } + return result +} + function openMainWindow () { if (!windows.main) { const windowState = WindowState({ diff --git a/lib/main-window.js b/lib/main-window.js index 15da47bf3..8867b0051 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -142,6 +142,10 @@ module.exports = function (config) { electron.ipcRenderer.on('goToSettings', () => api.app.navigate('/settings')) + electron.ipcRenderer.on("navigate-to", (ev, target) => { + navigate(target); + }); + document.head.appendChild( h('style', { innerHTML: computed(api.settings.obs.get('patchwork.theme', 'light'), themeName => { @@ -211,21 +215,7 @@ module.exports = function (config) { h('span.nav', [ tab(i18n('Public'), '/public'), tab(i18n('Private'), '/private'), - dropTab(i18n('More'), [ - getSubscribedChannelMenu, - subMenu(i18n('Participating'), [ - [i18n('All Threads'), '/participating'], - [i18n('Threads Started By You'), '/your-posts'] - ]), - subMenu(i18n('Gatherings'), [ - [i18n('All'), '/gatherings'], - [i18n('Attending'), '/attending-gatherings'] - ]), - [i18n('Tags'), `/tags/all/${encodeURIComponent(id)}`], - [i18n('Extended Network'), '/all'], - { separator: true }, - [i18n('Settings'), '/settings'] - ]) + dropTab(i18n('More')) ]), h('span.appTitle', [ h('span.title', i18n('Patchwork')), @@ -265,78 +255,99 @@ module.exports = function (config) { // scoped - function subMenu (label, items) { - return function () { - return { - label, - submenu: items.map(item => { - return { - label: item[0], - click () { - navigate(item[1]) - } - } - }) - } - } - } - function getSubscribedChannelMenu () { const channels = Array.from(subscribedChannels()).sort(localeCompare) if (channels.length) { return { + type: 'submenu', label: i18n('Channels'), submenu: [ { + type: 'normal', label: i18n('Browse Recently Active'), - click () { - navigate('/channels') - } + target: '/channels' }, { type: 'separator' } - ].concat(channels.map(channel => { - return { - label: `#${channel}`, - click () { - navigate(`#${channel}`) - } - } - })) + ].concat(channels.map(channel => ({ + type: 'normal', + label: `#${channel}`, + target: `#${channel}`, + }))) } } else { return { + type: 'normal', label: i18n('Browse Channels'), - click () { - navigate('/channels') - } + target: '/channels', } } } - function dropTab (title, items) { + function buildDropdownMenuItems() { + const dropTabItems = [ + getSubscribedChannelMenu(), + { + type: 'submenu', + label: i18n("Participating"), + submenu: [ + { + type: "normal", + label: i18n("All Threads"), + target: "/participating", + }, + { + type: "normal", + label: i18n("Threads Started By You"), + target: "/your-posts", + }, + ], + }, + { + type: "submenu", + label: i18n("Gatherings"), + submenu: [ + { + type: "normal", + label: i18n("All"), + target: "/gatherings", + }, + { + type: "normal", + label: i18n("Attending"), + target: "/attending-gatherings", + }, + ], + }, + { + type: "normal", + label: i18n("Tags"), + target: `/tags/all/${encodeURIComponent(id)}`, + }, + { + type: "normal", + label: i18n("Extended Network"), + target: "/all", + }, + { type: "separator" }, + { + type: "normal", + label: i18n("Settings"), + target: "/settings", + }, + ]; + return dropTabItems + } + + function dropTab (title) { const element = h('a -drop', { 'ev-click': () => { + const dropTabItems = buildDropdownMenuItems() const rects = element.getBoundingClientRect() - const factor = electron.remote.getCurrentWindow().webContents.zoomFactor - const menu = electron.remote.Menu.buildFromTemplate(items.map(item => { - if (typeof item === 'function') { - return item() - } else if (item.separator) { - return { type: 'separator' } - } else { - return { - label: item[0], - click () { - navigate(item[1]) - } - } - } - })) - menu.popup({ - window: electron.remote.getCurrentWindow(), - x: Math.round(rects.left * factor), - y: Math.round(rects.bottom * factor) + 4 + electron.ipcRenderer.invoke('navigation-menu-popup', { + x: rects.left, + y: rects.bottom, + items: dropTabItems, }) } }, title) From 4d9f238ff73f7136cd292da88f06c17fe1a8c445 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Sat, 6 Mar 2021 22:33:18 +0100 Subject: [PATCH 10/10] Port context menu to electron 11 & electron-context-menu The old menu was handling more than we needed: it was doing spell-checking, which has been built into electron since v8.0.0, and it was also overly complicated. The new version should be a bit easier to read. Avoiding use of the remote module means that a lot of things have to be handled via explicit IPC (instead of relying on implicit proxy objects from the remote module) and especially for the "Copy Message Text" and "Copy Message Reference" items and such I had to hack a bit. As a result, this *might* still have race conditions that would mean that text doesn't get copied, or an outdated version of it. --- index.js | 28 ++-- lib/context-menu-and-spellcheck.js | 196 ----------------------- lib/context-menu.js | 247 +++++++++++++++++++++++++++++ lib/main-window.js | 33 ++++ lib/window.js | 13 +- package-lock.json | 163 ++++++++++++++++++- 6 files changed, 470 insertions(+), 210 deletions(-) delete mode 100644 lib/context-menu-and-spellcheck.js create mode 100644 lib/context-menu.js diff --git a/index.js b/index.js index add3a7611..74db8c87e 100644 --- a/index.js +++ b/index.js @@ -170,13 +170,14 @@ electron.app.on('ready', () => { }); electron.ipcMain.on('exit', (ev, code) => process.exit(code)) - electron.ipcMain.on('open-background-devtools', function () { - if (windows.background) { - windows.background.webContents.openDevTools({ mode: 'detach' }) - } - }) }) +function openServerDevTools () { + if (windows.background) { + windows.background.webContents.openDevTools({ mode: 'detach' }) + } +} + function buildMenu(items, window) { const result = [] for (let item of items) { @@ -193,9 +194,7 @@ function buildMenu(items, window) { case 'normal': result.push({ ...item, - click: () => { - window.send('navigate-to', item.target) - } + click: () => navigateTo(item.target) }) break default: @@ -205,6 +204,12 @@ function buildMenu(items, window) { return result } +function navigateTo(target) { + if (windows?.main) { + windows.main.send('navigate-to', target) + } +} + function openMainWindow () { if (!windows.main) { const windowState = WindowState({ @@ -222,8 +227,11 @@ function openMainWindow () { title: 'Patchwork', show: true, backgroundColor: '#EEE', - icon: Path.join(__dirname, 'assets/icon.png') - }) + icon: Path.join(__dirname, 'assets/icon.png'), + }, + openServerDevTools, + navigateTo, + ) windowState.manage(windows.main) windows.main.setSheetOffset(40) diff --git a/lib/context-menu-and-spellcheck.js b/lib/context-menu-and-spellcheck.js deleted file mode 100644 index a6dd0c410..000000000 --- a/lib/context-menu-and-spellcheck.js +++ /dev/null @@ -1,196 +0,0 @@ -const { remote, shell, clipboard, ipcRenderer } = require('electron') -const { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } = require('electron-spellchecker') -const { MenuItem, Menu } = remote -const ref = require('ssb-ref') - -let navigateHandler = null -module.exports = setupContextMenuAndSpellCheck - -function setupContextMenuAndSpellCheck (config, { navigate, getMessageText, language }) { - navigateHandler = navigate - const spellCheckHandler = new SpellCheckHandler() - spellCheckHandler.attachToInput() - - // Start off as US English, America #1 (lol) - spellCheckHandler.switchLanguage(language || 'en-US') - - const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler, null, true, (menu, menuInfo) => { - const ddg = new MenuItem({ - label: 'Search With DuckDuckGo', - click: () => { - const url = `https://duckduckgo.com/?q=${encodeURIComponent(menuInfo.selectionText)}` - shell.openExternal(url) - } - }) - - // There's no menu.remove(id) so this is a convoluted way of removing the - // 'Search with Google' menu item - const oldItems = menu.items - menu.clear() - - oldItems.forEach(oldItem => { - if (oldItem.label === 'Search with Google') { - menu.append(ddg) - } else { - menu.append(oldItem) - } - }) - - if (menuInfo.mediaType !== 'none') { - console.log(menuInfo) - const copyEmbed = new MenuItem({ - label: 'Copy Embed Markdown', - click: () => { - // Omit the mailto: portion of the link; we just want the address - const extractedRef = ref.extract(menuInfo.srcURL) - clipboard.writeText(`![${menuInfo.titleText}](${extractedRef})`) - } - }) - menu.append(copyEmbed) - const openImageInBrowser = new MenuItem({ - label: 'Open Image With Browser', - click: () => { - shell.openExternal(menuInfo.srcURL) - } - }) - menu.append(openImageInBrowser) - } - }) - - contextMenuBuilder.buildMenuForLink = function (menuInfo) { - const element = document.elementFromPoint(menuInfo.x, menuInfo.y) - - const menu = new Menu() - const isEmailAddress = menuInfo.linkURL.startsWith('mailto:') - const isFile = menuInfo.linkURL.startsWith('file:') - - // use the anchor of a link if it directly references an ID - const extractedRef = element && ref.isLink(element.anchor) ? element.anchor : ref.extract(menuInfo.linkURL) - - if (!isFile) { - const copyLink = new MenuItem({ - label: isEmailAddress ? this.stringTable.copyMail() : this.stringTable.copyLinkUrl(), - click: () => { - // Omit the mailto: portion of the link; we just want the address - clipboard.writeText(isEmailAddress ? menuInfo.linkText : menuInfo.linkURL) - } - }) - - const openLink = new MenuItem({ - label: this.stringTable.openLinkUrl(), - click: () => { - shell.openExternal(menuInfo.linkURL) - } - }) - - menu.append(copyLink) - menu.append(openLink) - } - - if (extractedRef) { - if (navigateHandler) { - menu.append(new MenuItem({ - label: 'Find Link References', - click: function () { - navigateHandler('?' + extractedRef) - } - })) - this.addSeparator(menu) - } - const copyRef = new MenuItem({ - label: `Copy Link Ref (${extractedRef.slice(0, 10)}...)`, - click: () => { - // Omit the mailto: portion of the link; we just want the address - clipboard.writeText(extractedRef) - } - }) - menu.append(copyRef) - } - - if (this.isSrcUrlValid(menuInfo)) { - if (!isFile) this.addSeparator(menu) - this.addImageItems(menu, menuInfo) - } - - this.addInspectElement(menu, menuInfo) - this.processMenu(menu, menuInfo) - - return menu - } - - module.exports.menu = new ContextMenuListener((info) => { - contextMenuBuilder.buildMenuForElement(info).then((menu) => { - let element = document.elementFromPoint(info.x, info.y) - while (element && !element.msg) { - element = element.parentNode - } - - menu.append(new MenuItem({ - label: 'Inspect Server Process', - click: function () { - ipcRenderer.send('open-background-devtools') - } - })) - - menu.append(new MenuItem({ - type: 'separator' - })) - - menu.append(new MenuItem({ - label: 'Reload', - click: function (item, focusedWindow) { - if (focusedWindow) { - focusedWindow.reload() - } - } - })) - - if (element && element.msg) { - menu.append(new MenuItem({ - type: 'separator' - })) - menu.append(new MenuItem({ - label: 'Copy Message ID', - click: function () { - clipboard.writeText(element.msg.key) - } - })) - menu.append(new MenuItem({ - label: 'Copy Message Text', - click: function () { - getMessageText(element.msg.key, (err, value) => { - if (!err) { - clipboard.writeText(value) - } else { - showDialog({ - type: 'error', - title: 'Error', - buttons: ['OK'], - message: 'Could not get message text.', - detail: err && err.message - }) - } - }) - } - })) - menu.append(new MenuItem({ - label: 'Copy External Link', - click: function () { - const key = element.msg.key - const gateway = config.gateway || - 'https://viewer.scuttlebot.io' - const url = `${gateway}/${encodeURIComponent(key)}` - clipboard.writeText(url) - } - })) - } - menu.popup(remote.getCurrentWindow()) - }).catch((err) => { - throw err - }) - }) -} - -function showDialog (opts) { - remote.dialog.showMessageBox(remote.getCurrentWindow(), opts) -} diff --git a/lib/context-menu.js b/lib/context-menu.js new file mode 100644 index 000000000..dc0ed91c8 --- /dev/null +++ b/lib/context-menu.js @@ -0,0 +1,247 @@ +const { shell, clipboard } = require('electron') +const { BrowserWindow, ContextMenuParams, ipcMain, MenuItemConstructorOptions, WebContents } = require('electron') +const contextMenu = require('electron-context-menu') +const ref = require('ssb-ref') + +// used to receive out-of-band information about context-menu events +// see below, Ctrl-F "context-menu-info" +var lastClickInfo; + +module.exports = function ( + config, + serverDevToolsCallback, + navigateHandler, + window +) { + ipcMain.handle("context-menu-info", (event, info) => { + lastClickInfo = info; + return true; + }); + contextMenu({ + window, + menu: (defaultActions, parameters, _, dictionarySuggestions) => { + // elementAtPosition(window, parameters.x, parameters.y) + const isFileProtocol = parameters.linkURL.startsWith("file:"); + + // This is very similar to the boilerplate from electron-context-menu even + // though we don't use all the options. Some of the options are disabled + // via "condition && " guards just to clarify where we differ from the + // boilerplate. + // See the original menu structure here: + // https://github.com/sindresorhus/electron-context-menu/blob/621c29a8a133925ac25529e4bea2a738394e8609/index.js#L230 + // We could probably get away with heavy, heavy modification instead of + // menu but this seems more understandable, all things considered + let menuTemplate = [ + dictionarySuggestions.length > 0 && defaultActions.separator(), + ...dictionarySuggestions, + defaultActions.separator(), + + defaultActions.learnSpelling(), + defaultActions.separator(), + + defaultActions.lookUpSelection(), + defaultActions.separator(), + + searchwithDDG(parameters), // instead of defaultActions.searchWithGoogle() + defaultActions.separator(), + + defaultActions.cut(), + defaultActions.copy(), + defaultActions.paste(), + defaultActions.separator(), + + // We typically don't want to copy links, only external ones + copyEmbedMd(parameters), + copyMsgText(window), + // this and the next one might return the same id. Bit redundant but + // only if we right-click on the message timestamp or such + copyMsgKey(), + copyRef(parameters), + copyEmail(parameters), + // We could make our own copyLink() instead which sets + // visible: !isFileProtocol but this is easier + !isFileProtocol && defaultActions.copyLink(), + copyExternalLink(config), + openOnExternalViewer(config), + findRefs(parameters, navigateHandler), + defaultActions.separator(), + + openMediaInBrowser(parameters), + defaultActions.saveImage(), + defaultActions.saveImageAs(), + defaultActions.copyImage(), + defaultActions.copyImageAddress(), + defaultActions.separator(), + + // this could trigger a web request from within patchwork and we don't want that + false && defaultActions.saveLinkAs(), + defaultActions.separator(), + + defaultActions.inspect(), + openServerDevTools(serverDevToolsCallback), + defaultActions.services(), + defaultActions.separator(), + + reloadWindow(), + ]; + + return menuTemplate; + }, + }); +}; + +// Every function below here will produce one MenuItemConstructorOptions object + +function copyMsgKey() { + const msgKey = lastClickInfo?.msg?.key + return { + label: "Copy Message Reference", + visible: !!msgKey, + click: function () { + } + } +} + +function copyMsgText(window) { + const msgKey = lastClickInfo?.msg?.key + return { + label: "Copy Message Text", + visible: !!msgKey, + click: function () { + window.webContents.send('copy-message-text', msgKey) + }, + }; +} + +function openOnExternalViewer(config) { + const msgKey = lastClickInfo?.msg?.key + return { + label: 'Open In Online Viewer', + visible: !!msgKey, + click: function () { + const key = msgKey + const gateway = config.gateway || + 'https://viewer.scuttlebot.io' + const url = `${gateway}/${encodeURIComponent(key)}` + shell.openExternal(url); + } + } +} + +function copyExternalLink(config) { + const msgKey = lastClickInfo?.msg?.key + return { + label: 'Copy External Link', + visible: !!msgKey, + click: function () { + const key = msgKey + const gateway = config.gateway || + 'https://viewer.scuttlebot.io' + const url = `${gateway}/${encodeURIComponent(key)}` + clipboard.writeText(url) + } + } +} + +function findRefs(parameters, navigate) { + const extractedRef = + parameters.mediaType === "none" + ? ref.extract(parameters.linkURL) + : ref.extract(parameters.srcURL); + const usageOrRef = extractedRef && parameters.mediaType === "none" + ? 'References To' + : 'Usages Of' + const label = !!extractedRef + ? `Find ${usageOrRef} ${extractedRef.slice(0, 10).replaceAll("&", "&&&")}...` + : ""; + return { + label, + visible: !!extractedRef, + click: () => { + navigate(`?${extractedRef}`); + }, + }; +} + +function copyRef(parameters) { + const extractedRef = + parameters.mediaType === "none" + ? ref.extract(parameters.linkURL) + : ref.extract(parameters.srcURL); + const label = !!extractedRef + ? `Copy Reference ${extractedRef.slice(0, 10).replaceAll("&", "&&&")}...` + : ""; + return { + label, + visible: !!extractedRef, + click: () => { + clipboard.writeText(extractedRef); + }, + }; +} + +function copyEmail(parameters) { + return { + label: "Copy Email Address", + // FIXME: this fails for "mailto:" links that actually are hand-coded in markdown + // example: Mail me at my [work address](mailto:daan@business.corp) + visible: parameters.linkURL.startsWith("mailto:"), + click: () => { + // Omit the mailto: portion of the link; we just want the address + clipboard.writeText(parameters.linkText); + }, + }; +} + +function copyEmbedMd(parameters) { + return { + label: "Copy Embed Markdown", + visible: parameters.mediaType !== "none", + click: () => { + const extractedRef = ref.extract(parameters.srcURL); + clipboard.writeText(`![${parameters.titleText}](${extractedRef})`); + }, + }; +} + +function openMediaInBrowser(parameters) { + return { + label: "Open With Browser", + visible: parameters.mediaType !== "none", + click: () => { + shell.openExternal(parameters.srcURL); + }, + }; +} + +function searchwithDDG(parameters) { + return { + label: "Search With DuckDuckGo", + // Only show it when right-clicking text + visible: parameters.selectionText.trim().length > 0, + click: () => { + const url = `https://duckduckgo.com/?q=${encodeURIComponent( + parameters.selectionText + )}`; + shell.openExternal(url); + }, + }; +} + +function reloadWindow() { + return { + label: "Reload", + click: function (item, focusedWindow) { + if (focusedWindow) { + focusedWindow.reload(); + } + }, + }; +} + +function openServerDevTools(serverDevToolsCallback) { + return { + label: "Inspect Server Process", + click: serverDevToolsCallback, + }; +} \ No newline at end of file diff --git a/lib/main-window.js b/lib/main-window.js index 8867b0051..237813aaa 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -146,6 +146,39 @@ module.exports = function (config) { navigate(target); }); + // context menus are handled on the server. however, we need to know some + // information about the element under the cursor at the time of clicking. + // Sadly, electron doesn't seem to allow us to attach arbitrary information to + // an event and have it be transferred to the server directly. so we need to + // transfer that information "out of band" via sendSync. + // Note that invoke() might cause race conditions here IFF messages can change + // order in the queue between Renderer and Server, or if the IPC and document + // events use different queues. I'm not sure whether that's the case, but I + // haven't observed this happening yet. + document.addEventListener("contextmenu", (ev) => { + let element = document.elementFromPoint(ev.x, ev.y) + while (element && !element.msg) { + element = element.parentNode + } + // we only send the id, because that's cheap + // if the server actually wants to copy the text, it will + // ask us (see "copy-message-text" handler below) + electron.ipcRenderer.invoke("context-menu-info", { + msg: element?.msg || null, + }); + }) + + // for when the server process asks us to please copy a message text to + // clipboard needed for context menu popups, because we want to call the + // getMessage only *if* the text is actually to be copied + electron.ipcRenderer.on("copy-message-text", (ev, key) => { + getMessageText(key, (err, value) => { + electron.clipboard.writeText( + !err ? value : `Error while retrieving message ${key}:\n${err}` + ); + }); + }); + document.head.appendChild( h('style', { innerHTML: computed(api.settings.obs.get('patchwork.theme', 'light'), themeName => { diff --git a/lib/window.js b/lib/window.js index 9c0249a24..e08d93657 100644 --- a/lib/window.js +++ b/lib/window.js @@ -1,8 +1,9 @@ const Path = require('path') const electron = require('electron') const extend = require('xtend/mutable') +const setupContextMenu = require('./context-menu') -module.exports = function Window (config, path, opts) { +module.exports = function Window (config, path, opts, serverDevToolsCallback, navigateHandler) { const window = new electron.BrowserWindow(extend({ show: false, webPreferences: { @@ -48,6 +49,16 @@ module.exports = function Window (config, path, opts) { electron.ipcMain.removeListener('ready-to-show', handleReadyToShow) }) + // TODO: better way to determine whether this is the main window ? + if (opts.title === "Patchwork") { + setupContextMenu( + config, + serverDevToolsCallback, + navigateHandler, + window + ); + } + window.loadURL('file://' + Path.join(__dirname, '..', 'assets', 'base.html')) return window diff --git a/package-lock.json b/package-lock.json index 6281cf30e..92863711b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1888,6 +1888,81 @@ "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -2777,11 +2852,36 @@ } } }, + "electron-context-menu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.5.0.tgz", + "integrity": "sha512-kzvp8XUmbp2TG2hELJUl7Yjlq4Ag549JQu/C8mxvy1CmAU15UFmPC3bPdXMGE/e3xbi97shgxfttxeQ/6h4MoQ==", + "requires": { + "cli-truncate": "^2.1.0", + "electron-dl": "^3.1.0", + "electron-is-dev": "^1.2.0" + } + }, "electron-default-menu": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/electron-default-menu/-/electron-default-menu-1.0.2.tgz", "integrity": "sha512-YAL/UNR3kPG58wOOlmDpTG3i6+bzwhHx6NllIOaLuVrU7uYifeYGGdk5IH2Hap4wVEx2YTA8cqQ2PGSplYwDWQ==" }, + "electron-dl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.2.0.tgz", + "integrity": "sha512-Wz6YZi0fwy/hmdkrtrBwj7W4JlrFd1tY5jvNdLHd0PUTlJFJ6pGi9mPQGtZRdjodkXYpaiD1uio+wh01hqrZNg==", + "requires": { + "ext-name": "^5.0.0", + "pupa": "^2.0.1", + "unused-filename": "^2.1.0" + } + }, + "electron-is-dev": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", + "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==" + }, "electron-publish": { "version": "22.9.1", "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.9.1.tgz", @@ -3168,8 +3268,7 @@ "escape-goat": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" }, "escape-html": { "version": "1.0.3", @@ -3778,6 +3877,23 @@ "vary": "~1.1.2" } }, + "ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "requires": { + "mime-db": "^1.28.0" + } + }, + "ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "requires": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + } + }, "extract-zip": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", @@ -5005,6 +5121,11 @@ "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -5995,6 +6116,11 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "modify-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", + "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=" + }, "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -7768,7 +7894,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, "requires": { "escape-goat": "^2.0.0" } @@ -8790,6 +8915,22 @@ } } }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "requires": { + "sort-keys": "^1.0.0" + } + }, "sorted-array-functions": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", @@ -10964,6 +11105,22 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unused-filename": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", + "integrity": "sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg==", + "requires": { + "modify-filename": "^1.1.0", + "path-exists": "^4.0.0" + }, + "dependencies": { + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, "update-notifier": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz",