diff --git a/_locales/en/messages.json b/_locales/en/messages.json index e32d7c7012..53e0e9aca3 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -147,6 +147,14 @@ "message": "Set Up as Standalone Device", "description": "Only available on development modes, menu option to open up the standalone device setup sequence" }, + "contextMenuCopyLink": { + "message": "Copy Link", + "description": "Shown in the context menu for a link to indicate that the user can copy the link" + }, + "contextMenuNoSuggestions": { + "message": "No Suggestions", + "description": "Shown in the context menu for a misspelled word to indicate that there are no suggestions to replace the misspelled word" + }, "avatarMenuViewArchive": { "message": "View Archive", "description": "One of the menu options available in the Avatar Popup menu" @@ -1080,6 +1088,10 @@ "message": "Enable spell check of text entered in message composition box", "description": "Description of the media permission description" }, + "spellCheckDirty": { + "message": "You must restart Signal to apply your new settings", + "description": "Shown when the user changes their spellcheck setting to indicate that they must restart Signal." + }, "clearDataHeader": { "message": "Clear Data", "description": "Header in the settings dialog for the section dealing with data deletion" diff --git a/app/spell_check.js b/app/spell_check.js new file mode 100644 index 0000000000..98e9a3425d --- /dev/null +++ b/app/spell_check.js @@ -0,0 +1,102 @@ +/* global exports, require */ +/* eslint-disable strict */ + +const { Menu, clipboard } = require('electron'); +const osLocale = require('os-locale'); + +exports.setup = (browserWindow, messages) => { + const { session } = browserWindow.webContents; + const userLocale = osLocale.sync().replace(/_/g, '-'); + const userLocales = [userLocale, userLocale.split('-')[0]]; + const available = session.availableSpellCheckerLanguages; + const languages = userLocales.filter(l => available.includes(l)); + console.log(`spellcheck: user locale: ${userLocale}`); + console.log('spellcheck: available spellchecker languages: ', available); + console.log('spellcheck: setting languages to: ', languages); + session.setSpellCheckerLanguages(languages); + + browserWindow.webContents.on('context-menu', (_event, params) => { + const { editFlags } = params; + const isMisspelled = Boolean(params.misspelledWord); + const isLink = Boolean(params.linkURL); + const showMenu = params.isEditable || editFlags.canCopy || isLink; + + // Popup editor menu + if (showMenu) { + const template = []; + + if (isMisspelled) { + if (params.dictionarySuggestions.length > 0) { + template.push( + ...params.dictionarySuggestions.map(label => ({ + label, + click: () => { + browserWindow.webContents.replaceMisspelling(label); + }, + })) + ); + } else { + template.push({ + label: messages.contextMenuNoSuggestions.message, + enabled: false, + }); + } + template.push({ type: 'separator' }); + } + + if (params.isEditable) { + if (editFlags.canUndo) { + template.push({ label: messages.editMenuUndo.message, role: 'undo' }); + } + // This is only ever `true` if undo was triggered via the context menu + // (not ctrl/cmd+z) + if (editFlags.canRedo) { + template.push({ label: messages.editMenuRedo.message, role: 'redo' }); + } + if (editFlags.canUndo || editFlags.canRedo) { + template.push({ type: 'separator' }); + } + if (editFlags.canCut) { + template.push({ label: messages.editMenuCut.message, role: 'cut' }); + } + } + + if (editFlags.canCopy || isLink) { + template.push({ + label: isLink + ? messages.contextMenuCopyLink.message + : messages.editMenuCopy.message, + role: isLink ? undefined : 'copy', + click: isLink + ? () => { + clipboard.writeText(params.linkURL); + } + : undefined, + }); + } + + if (editFlags.canPaste) { + template.push({ label: messages.editMenuPaste.message, role: 'paste' }); + } + + if (editFlags.canPaste) { + template.push({ + label: messages.editMenuPasteAndMatchStyle.message, + role: 'pasteAndMatchStyle', + }); + } + + // Only enable select all in editors because select all in non-editors + // results in all the UI being selected + if (editFlags.canSelectAll && params.isEditable) { + template.push({ + label: messages.editMenuSelectAll.message, + role: 'selectall', + }); + } + + const menu = Menu.buildFromTemplate(template); + menu.popup(browserWindow); + } + }); +}; diff --git a/js/background.js b/js/background.js index f7144a094a..7e961e136a 100644 --- a/js/background.js +++ b/js/background.js @@ -334,7 +334,6 @@ getSpellCheck: () => storage.get('spell-check', true), setSpellCheck: value => { storage.put('spell-check', value); - startSpellCheck(); }, // eslint-disable-next-line eqeqeq @@ -545,19 +544,6 @@ } }); - const startSpellCheck = () => { - if (!window.enableSpellCheck || !window.disableSpellCheck) { - return; - } - - if (window.Events.getSpellCheck()) { - window.enableSpellCheck(); - } else { - window.disableSpellCheck(); - } - }; - startSpellCheck(); - try { await Promise.all([ ConversationController.load(), diff --git a/js/spell_check.js b/js/spell_check.js deleted file mode 100644 index 4a4f31ed7d..0000000000 --- a/js/spell_check.js +++ /dev/null @@ -1,172 +0,0 @@ -/* global require, process, _ */ - -/* eslint-disable strict */ - -const electron = require('electron'); - -const Typo = require('typo-js'); -const osLocale = require('os-locale'); - -const { remote, webFrame } = electron; - -// `remote.require` since `Menu` is a main-process module. -const buildEditorContextMenu = remote.require('electron-editor-context-menu'); - -const EN_VARIANT = /^en/; - -// Prevent the spellchecker from showing contractions as errors. -const ENGLISH_SKIP_WORDS = [ - 'ain', - 'couldn', - 'didn', - 'doesn', - 'hadn', - 'hasn', - 'mightn', - 'mustn', - 'needn', - 'oughtn', - 'shan', - 'shouldn', - 'wasn', - 'weren', - 'wouldn', -]; - -function setupLinux(locale) { - if (EN_VARIANT.test(locale)) { - window.log.info('Detected English locale on Linux. Enabling spell check.'); - - return new Typo(locale); - } - - window.log.info( - 'Detected non-English locale on Linux. Disabling spell check.' - ); - - return null; -} - -// We load locale this way and not via app.getLocale() because this call returns -// 'es_ES' and not just 'es.' And hunspell requires the fully-qualified locale. -const locale = osLocale.sync().replace('-', '_'); - -// The LANG environment variable is how node spellchecker finds its default language: -// https://github.com/atom/node-spellchecker/blob/59d2d5eee5785c4b34e9669cd5d987181d17c098/lib/spellchecker.js#L29 -if (!process.env.LANG) { - process.env.LANG = locale; -} - -let spellchecker = null; - -if (process.platform === 'linux') { - spellchecker = setupLinux(locale); -} else { - spellchecker = new Typo(locale); - // OSX and Windows 8+ have OS-level spellcheck APIs - window.log.info( - 'Using OS-level spell check API with locale', - process.env.LANG - ); -} - -const simpleChecker = { - spellCheck(words, callback) { - const mispelled = words.filter(word => this.isMisspelled(word)); - callback(mispelled); - }, - isMisspelled(word) { - if (!spellchecker) { - return false; - } - - const misspelled = !spellchecker.check(word); - - // The idea is to make this as fast as possible. For the many, many calls which - // don't result in the red squiggly, we minimize the number of checks. - if (!misspelled) { - return false; - } - - // Only if we think we've found an error do we check the locale and skip list. - if (locale.match(EN_VARIANT) && _.contains(ENGLISH_SKIP_WORDS, word)) { - return false; - } - - return true; - }, - getSuggestions(text) { - if (!spellchecker) { - return []; - } - - return spellchecker.suggest(text); - }, - add() {}, -}; - -const dummyChecker = { - spellCheck(words, callback) { - callback([]); - }, - isMisspelled() { - return false; - }, - getSuggestions() { - return []; - }, - add() { - // nothing - }, -}; - -window.spellChecker = simpleChecker; -window.disableSpellCheck = () => { - window.removeEventListener('contextmenu', spellCheckHandler); - window.addEventListener('contextmenu', defaultContextMenuHandler); - webFrame.setSpellCheckProvider('en-US', dummyChecker); -}; - -window.enableSpellCheck = () => { - webFrame.setSpellCheckProvider('en-US', simpleChecker); - window.addEventListener('contextmenu', spellCheckHandler); - window.removeEventListener('contextmenu', defaultContextMenuHandler); -}; - -const defaultContextMenuHandler = e => { - // Only show the context menu in text editors. - if (!e.target.closest('textarea, input, [contenteditable="true"]')) { - return; - } - - const menu = buildEditorContextMenu({}); - - // @see js/spell_check.js:177 - setTimeout(() => { - menu.popup(remote.getCurrentWindow()); - }, 30); -}; - -const spellCheckHandler = e => { - // Only show the context menu in text editors. - if (!e.target.closest('textarea, input, [contenteditable="true"]')) { - return; - } - - const selectedText = window.getSelection().toString(); - const isMisspelled = selectedText && simpleChecker.isMisspelled(selectedText); - const spellingSuggestions = - isMisspelled && simpleChecker.getSuggestions(selectedText).slice(0, 5); - const menu = buildEditorContextMenu({ - isMisspelled, - spellingSuggestions, - }); - - // The 'contextmenu' event is emitted after 'selectionchange' has fired - // but possibly before the visible selection has changed. Try to wait - // to show the menu until after that, otherwise the visible selection - // will update after the menu dismisses and look weird. - setTimeout(() => { - menu.popup(remote.getCurrentWindow()); - }, 30); -}; diff --git a/js/views/settings_view.js b/js/views/settings_view.js index e1b879cc1b..52055c0683 100644 --- a/js/views/settings_view.js +++ b/js/views/settings_view.js @@ -106,7 +106,17 @@ el: this.$('.spell-check-setting'), name: 'spell-check-setting', value: window.initialData.spellCheck, - setFn: window.setSpellCheck, + setFn: val => { + const $msg = this.$('.spell-check-setting-message'); + if (val !== window.appStartInitialSpellcheckSetting) { + $msg.show(); + $msg.attr('aria-hidden', false); + } else { + $msg.hide(); + $msg.attr('aria-hidden', true); + } + window.setSpellCheck(val); + }, }); if (Settings.isHideMenuBarSupported()) { new CheckboxView({ @@ -131,6 +141,10 @@ 'click .clear-data': 'onClearData', }, render_attributes() { + const spellCheckDirty = + window.initialData.spellCheck !== + window.appStartInitialSpellcheckSetting; + return { deviceNameLabel: i18n('deviceName'), deviceName: window.initialData.deviceName, @@ -157,6 +171,9 @@ mediaPermissionsDescription: i18n('mediaPermissionsDescription'), generalHeader: i18n('general'), spellCheckDescription: i18n('spellCheckDescription'), + spellCheckHidden: spellCheckDirty ? 'false' : 'true', + spellCheckDisplay: spellCheckDirty ? 'inherit' : 'none', + spellCheckDirtyText: i18n('spellCheckDirty'), }; }, onClose() { diff --git a/main.js b/main.js index 520974eca3..5708cef3dc 100644 --- a/main.js +++ b/main.js @@ -16,6 +16,7 @@ const electron = require('electron'); const packageJson = require('./package.json'); const GlobalErrors = require('./app/global_errors'); +const { setup: setupSpellChecker } = require('./app/spell_check'); GlobalErrors.addHandler(); @@ -94,6 +95,19 @@ const { } = require('./app/protocol_filter'); const { installPermissionsHandler } = require('./app/permissions'); +let appStartInitialSpellcheckSetting = true; + +async function getSpellCheckSetting() { + const json = await sql.getItemById('spell-check'); + + // Default to `true` if setting doesn't exist yet + if (!json) { + return true; + } + + return json.value; +} + function showWindow() { if (!mainWindow) { return; @@ -182,6 +196,7 @@ function prepareURL(pathSegments, moreKeys) { contentProxyUrl: config.contentProxyUrl, importMode: importMode ? true : undefined, // for stringify() serverTrustRoot: config.get('serverTrustRoot'), + appStartInitialSpellcheckSetting, ...moreKeys, }, }); @@ -240,7 +255,7 @@ function isVisible(window, bounds) { ); } -function createWindow() { +async function createWindow() { const { screen } = electron; const windowOptions = Object.assign( { @@ -260,6 +275,7 @@ function createWindow() { contextIsolation: false, preload: path.join(__dirname, 'preload.js'), nativeWindowOpen: true, + spellcheck: await getSpellCheckSetting(), }, icon: path.join(__dirname, 'images', 'icon_256.png'), }, @@ -296,6 +312,7 @@ function createWindow() { // Create the browser window. mainWindow = new BrowserWindow(windowOptions); + setupSpellChecker(mainWindow, locale.messages); if (!usingTrayIcon && windowConfig && windowConfig.maximized) { mainWindow.maximize(); } @@ -525,7 +542,7 @@ function showAbout() { } let settingsWindow; -async function showSettingsWindow() { +function showSettingsWindow() { if (settingsWindow) { settingsWindow.show(); return; @@ -621,10 +638,12 @@ async function showStickerCreator() { contextIsolation: false, preload: path.join(__dirname, 'sticker-creator/preload.js'), nativeWindowOpen: true, + spellcheck: await getSpellCheckSetting(), }, }; stickerCreatorWindow = new BrowserWindow(options); + setupSpellChecker(stickerCreatorWindow, locale.messages); handleCommonWindowEvents(stickerCreatorWindow); @@ -797,6 +816,8 @@ app.on('ready', async () => { console.log('sql.initialize was unsuccessful; returning early'); return; } + // eslint-disable-next-line more/no-then + appStartInitialSpellcheckSetting = await getSpellCheckSetting(); await sqlChannels.initialize(); try { diff --git a/package.json b/package.json index c072213905..6d462d152a 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,6 @@ "copy-text-to-clipboard": "2.1.0", "curve25519-n": "https://github.com/scottnonnenberg-signal/node-curve25519.git#3e94f60bc54b2426476520d8d1a0aa835c25f5cc", "draft-js": "0.10.5", - "electron-context-menu": "0.11.0", - "electron-editor-context-menu": "1.1.1", "electron-mocha": "8.1.1", "electron-notarize": "0.1.1", "emoji-datasource": "5.0.1", @@ -139,7 +137,6 @@ "tmp": "0.0.33", "to-arraybuffer": "1.0.1", "typeface-inter": "3.10.0", - "typo-js": "1.1.0", "underscore": "1.9.0", "uuid": "3.3.2", "websocket": "1.0.28" diff --git a/patches/typo-js+1.1.0.patch b/patches/typo-js+1.1.0.patch deleted file mode 100644 index 9a96068279..0000000000 --- a/patches/typo-js+1.1.0.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/node_modules/typo-js/typo.js b/node_modules/typo-js/typo.js -index 68c285b..95ebc6c 100644 ---- a/node_modules/typo-js/typo.js -+++ b/node_modules/typo-js/typo.js -@@ -431,7 +431,7 @@ Typo.prototype = { - dictionaryTable[word] = null; - } - -- if (rules.length > 0) { -+ if (rules && rules.length > 0) { - if (dictionaryTable[word] === null) { - dictionaryTable[word] = []; - } -@@ -546,6 +546,7 @@ Typo.prototype = { - else if (this.flags.FLAG === "num") { - return textCodes.split(","); - } -+ return []; - }, - - /** diff --git a/preload.js b/preload.js index 357d8f1909..231c1261f6 100644 --- a/preload.js +++ b/preload.js @@ -395,23 +395,17 @@ try { window.Signal.Debug = require('./js/modules/debug'); window.Signal.Logs = require('./js/modules/logs'); - // Add right-click listener for selected text and urls - const contextMenu = require('electron-context-menu'); - - contextMenu({ - showInspectElement: false, - shouldShowMenu: (event, params) => - Boolean( - !params.isEditable && - params.mediaType === 'none' && - (params.linkURL || params.selectionText) - ), + window.addEventListener('contextmenu', e => { + const editable = e.target.closest( + 'textarea, input, [contenteditable="true"]' + ); + const link = e.target.closest('a'); + const selection = Boolean(window.getSelection().toString()); + if (!editable && !selection && !link) { + e.preventDefault(); + } }); - // We pull this in last, because the native module involved appears to be sensitive to - // /tmp mounted as noexec on Linux. - require('./js/spell_check'); - if (config.environment === 'test') { /* eslint-disable global-require, import/no-extraneous-dependencies */ window.test = { diff --git a/settings.html b/settings.html index bdef5810cf..338ac5ee7b 100644 --- a/settings.html +++ b/settings.html @@ -100,6 +100,9 @@
+

+ {{ spellCheckDirtyText }} +


diff --git a/settings_preload.js b/settings_preload.js index 23b6b60083..f6eb9ac236 100644 --- a/settings_preload.js +++ b/settings_preload.js @@ -14,6 +14,8 @@ const { nativeTheme } = remote.require('electron'); window.platform = process.platform; window.theme = config.theme; window.i18n = i18n.setup(locale, localeMessages); +window.appStartInitialSpellcheckSetting = + config.appStartInitialSpellcheckSetting === 'true'; function setSystemTheme() { window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; diff --git a/test/index.html b/test/index.html index 9eb925556a..873a015cd2 100644 --- a/test/index.html +++ b/test/index.html @@ -407,7 +407,6 @@ - diff --git a/test/spellcheck_test.js b/test/spellcheck_test.js deleted file mode 100644 index b51daa4e98..0000000000 --- a/test/spellcheck_test.js +++ /dev/null @@ -1,15 +0,0 @@ -describe('spellChecker', () => { - it('should work', () => { - let result = null; - - window.spellChecker.spellCheck(['correct'], answer => { - result = answer; - }); - assert.deepEqual(result, []); - - window.spellChecker.spellCheck(['fhqwgads'], answer => { - result = answer; - }); - assert.deepEqual(result, ['fhqwgads']); - }); -}); diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index e5f976816a..ff1650b953 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -883,73 +883,81 @@ { "rule": "jQuery-$(", "path": "js/views/settings_view.js", - "line": " el: this.$('.menu-bar-setting'),", - "lineNumber": 113, + "line": " const $msg = this.$('.spell-check-setting-message');", + "lineNumber": 110, "reasonCategory": "usageTrusted", - "updated": "2019-04-08T18:24:35.255Z", + "updated": "2020-03-19T16:06:32.598Z" + }, + { + "rule": "jQuery-$(", + "path": "js/views/settings_view.js", + "line": " el: this.$('.menu-bar-setting'),", + "lineNumber": 123, + "reasonCategory": "usageTrusted", + "updated": "2020-03-20T16:47:14.450Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.media-permissions'),", - "lineNumber": 120, + "lineNumber": 130, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T16:47:14.450Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync-setting').append(syncView.el);", - "lineNumber": 126, + "lineNumber": 136, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T16:47:14.450Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-append(", "path": "js/views/settings_view.js", "line": " this.$('.sync-setting').append(syncView.el);", - "lineNumber": 126, + "lineNumber": 136, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T18:13:29.628Z", + "updated": "2020-03-20T16:47:14.450Z", "reasonDetail": "Interacting with already-existing DOM nodes" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync').text(i18n('syncNow'));", - "lineNumber": 181, + "lineNumber": 198, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T19:55:50.800Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync').attr('disabled', 'disabled');", - "lineNumber": 185, + "lineNumber": 202, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T19:55:50.800Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.synced_at').hide();", - "lineNumber": 197, + "lineNumber": 214, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T19:55:50.800Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync_failed').hide();", - "lineNumber": 202, + "lineNumber": 219, "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", + "updated": "2020-03-20T19:55:50.800Z", "reasonDetail": "Protected from arbitrary input" }, { @@ -3141,22 +3149,6 @@ "reasonCategory": "falseMatch", "updated": "2018-09-19T18:13:29.628Z" }, - { - "rule": "jQuery-prepend(", - "path": "node_modules/electron-context-menu/index.js", - "line": "\t\t\tconst result = options.prepend(props, win);", - "lineNumber": 122, - "reasonCategory": "falseMatch", - "updated": "2019-04-10T19:08:25.356Z" - }, - { - "rule": "jQuery-append(", - "path": "node_modules/electron-context-menu/index.js", - "line": "\t\t\tconst result = options.append(props, win);", - "lineNumber": 130, - "reasonCategory": "falseMatch", - "updated": "2019-04-10T19:08:25.356Z" - }, { "rule": "jQuery-load(", "path": "node_modules/electron-download/node_modules/debug/src/browser.js", @@ -11816,4 +11808,4 @@ "reasonCategory": "falseMatch", "updated": "2020-02-07T19:52:28.522Z" } -] +] \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 23ab63ec1a..8ee9026c37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6181,23 +6181,6 @@ electron-chromedriver@~3.0.0: electron-download "^4.1.0" extract-zip "^1.6.5" -electron-context-menu@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-0.11.0.tgz#3ecefb0231151add474c9b0df2fb66fde3ad5731" - integrity sha512-sgDIGqjgazUQ5fbfz0ObRkmODAsw00eylQprp5q4jyuL6dskd27yslhoJTrjLbFGErfVVYzRXPW2rQPJxARKmg== - dependencies: - electron-dl "^1.2.0" - electron-is-dev "^1.0.1" - -electron-dl@^1.2.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/electron-dl/-/electron-dl-1.14.0.tgz#1466f1b945664ca3d784268307c2b935728177bf" - integrity sha512-4okyei42a1mLsvLK7hLrIfd20EQzB18nIlLTwBV992aMSmTGLUEFRTmO1MfSslGNrzD8nuPuy1l/VxO8so4lig== - dependencies: - ext-name "^5.0.0" - pupa "^1.0.0" - unused-filename "^1.0.0" - electron-download@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.0.tgz#bf932c746f2f87ffcc09d1dd472f2ff6b9187845" @@ -6212,17 +6195,6 @@ electron-download@^4.1.0: semver "^5.3.0" sumchecker "^2.0.1" -electron-editor-context-menu@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/electron-editor-context-menu/-/electron-editor-context-menu-1.1.1.tgz#dc30098e0dfb37f62628e43303124c7f3379572d" - integrity sha1-3DAJjg37N/YmKOQzAxJMfzN5Vy0= - dependencies: - lodash.clonedeep "^4.3.0" - lodash.defaults "^4.0.1" - lodash.isarray "^4.0.0" - lodash.isempty "^4.1.2" - lodash.isfunction "^3.0.8" - electron-icon-maker@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/electron-icon-maker/-/electron-icon-maker-0.0.3.tgz#bcd2e91896d7200f84fcc6652aed924fdaaa8307" @@ -6232,11 +6204,6 @@ electron-icon-maker@0.0.3: icon-gen "^1.0.7" jimp "^0.2.27" -electron-is-dev@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.0.1.tgz#6e0a184736fe7aea77d18210b0b0f6a02402c4bc" - integrity sha512-iwM3EotA9HTXqMGpQRkR/kT8OZqBbdfHTnlwcxsjSLYqY8svvsq0MuujsWCn3/vtgRmDv/PC/gKUUpoZvi5C1w== - electron-mocha@8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/electron-mocha/-/electron-mocha-8.1.1.tgz#e540e7d9ba80a024007a18533ae491c18f9a0ce2" @@ -6950,21 +6917,6 @@ express@^4.17.0, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -10264,10 +10216,6 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.clonedeep@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - lodash.cond@^4.3.0: version "4.5.2" resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" @@ -10277,26 +10225,10 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" -lodash.isarray@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403" - -lodash.isempty@^4.1.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" - -lodash.isfunction@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.8.tgz#4db709fc81bc4a8fd7127a458a5346c5cdce2c6b" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -10804,11 +10736,6 @@ mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-db@^1.28.0: - version "1.39.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.39.0.tgz#f95a20275742f7d2ad0429acfe40f4233543780e" - integrity sha512-DTsrw/iWVvwHH+9Otxccdyy0Tgiil6TWK/xhfARJZF/QFhwOgZgOIvA2/VIGpM8U7Q8z5nDmdDWC6tuVMJNibw== - mime-db@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" @@ -11104,11 +11031,6 @@ mocha@~6.2.0: yargs-parser "13.0.0" yargs-unparser "1.5.0" -modify-filename@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" - integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= - moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" @@ -13156,11 +13078,6 @@ punycode@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" -pupa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-1.0.0.tgz#9a9568a5af7e657b8462a6e9d5328743560ceff6" - integrity sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y= - pupa@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" @@ -15312,13 +15229,6 @@ socks@~2.2.0: ip "^1.1.5" smart-buffer "^4.0.1" -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -16622,11 +16532,6 @@ typescript@3.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19" integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw== -typo-js@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.1.0.tgz#a5a9f592bcb453666bf70c9694da58705d025ed8" - integrity sha512-W3kLbx+ML9PBl5Bzso/lTvVxk4BCveSNAtQeht59FEtxCdGThmn6wSHA4Xq3eQYAK24NHdisMM4JmsK0GFy/pg== - ua-parser-js@^0.7.18: version "0.7.19" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b" @@ -16855,14 +16760,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -unused-filename@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unused-filename/-/unused-filename-1.0.0.tgz#d340880f71ae2115ebaa1325bef05cc6684469c6" - integrity sha1-00CID3GuIRXrqhMlvvBcxmhEacY= - dependencies: - modify-filename "^1.1.0" - path-exists "^3.0.0" - unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"