diff --git a/.eslintrc.json b/.eslintrc.json index 1fa89a98..5aa4cf88 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,9 +3,7 @@ "react", "prettier", "@typescript-eslint", - "eslint-comments", - "lodash", - "import" + "eslint-comments" ], "extends": [ "eslint:recommended", @@ -34,11 +32,7 @@ "settings": { "react": { "version": "detect" - }, - "import/resolver": { - "typescript": {} - }, - "import/internal-regex": "^(electron|react)$" + } }, "rules": { "func-names": [ @@ -65,16 +59,7 @@ "bracketSameLine": false } ], - "eslint-comments/no-unused-disable": "error", - "react/no-unknown-property":[ - "error", - { - "ignore": [ - "jsx", - "global" - ] - } - ] + "eslint-comments/no-unused-disable": "error" }, "overrides": [ { @@ -98,33 +83,7 @@ "@typescript-eslint/no-shadow": ["error"], "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/consistent-type-imports": [ "error", { "disallowTypeAnnotations": false } ], - "lodash/prop-shorthand": [ "error", "always" ], - "lodash/import-scope": [ "error", "method" ], - "lodash/collection-return": "error", - "lodash/collection-method-value": "error", - "import/no-extraneous-dependencies": "error", - "import/no-anonymous-default-export": "error", - "import/order": [ - "error", - { - "groups": [ - "builtin", - "internal", - "external", - "parent", - "sibling", - "index" - ], - "newlines-between": "always", - "alphabetize": { - "order": "asc", - "orderImportKind": "desc", - "caseInsensitive": true - } - } - ] + "@typescript-eslint/restrict-template-expressions": "off" } }, { diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 569dda76..f2597d2b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,11 +35,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +50,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -64,4 +64,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/e2e_comment.yml b/.github/workflows/e2e_comment.yml index 573a4d59..9c6db543 100644 --- a/.github/workflows/e2e_comment.yml +++ b/.github/workflows/e2e_comment.yml @@ -14,14 +14,14 @@ jobs: WORKFLOW_RUN_INFO: ${{ toJSON(github.event.workflow_run) }} run: echo "$WORKFLOW_RUN_INFO" - name: Download Artifacts - uses: dawidd6/action-download-artifact@v3.1.4 + uses: dawidd6/action-download-artifact@v2.24.3 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: nodejs.yml run_id: ${{ github.event.workflow_run.id }} name: e2e - name: Get PR number - uses: dawidd6/action-download-artifact@v3.1.4 + uses: dawidd6/action-download-artifact@v2.24.3 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: nodejs.yml @@ -29,7 +29,7 @@ jobs: name: pr_num - name: Read the pr_num file id: pr_num_reader - uses: juliangruber/read-file-action@v1.1.7 + uses: juliangruber/read-file-action@v1.1.6 with: path: ./pr_num.txt - name: List images diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index d42e2995..5ebc3c06 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -8,45 +8,30 @@ on: defaults: run: shell: bash -env: - NODE_VERSION: 18.x jobs: build: runs-on: ${{matrix.os}} strategy: matrix: + node-version: [16.x] os: - - macos-latest - - ubuntu-latest + - macos-12 + - ubuntu-18.04 - windows-latest fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 - - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 with: - node-version: ${{ env.NODE_VERSION }} - - name: Fix node-gyp and Python - run: python3 -m pip install packaging setuptools - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - uses: actions/cache/restore@v4 - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + node-version: ${{ matrix.node-version }} + cache: yarn + cache-dependency-path: | + yarn.lock + app/yarn.lock - name: Install run: yarn install - env: - npm_config_node_gyp: ${{ github.workspace }}${{ runner.os == 'Windows' && '\node_modules\node-gyp\bin\node-gyp.js' || '/node_modules/node-gyp/bin/node-gyp.js' }} - - name: Install libarchive-tools - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install libarchive-tools - name: Lint and Run Unit Tests run: yarn run test - name: Getting Build Icon @@ -55,14 +40,7 @@ jobs: cp build/canary.ico build/icon.ico cp build/canary.icns build/icon.icns - name: Build - run: | - if [ -z "$CSC_LINK" ] ; then unset CSC_LINK ; fi - if [ -z "$CSC_KEY_PASSWORD" ] ; then unset CSC_KEY_PASSWORD ; fi - if [ -z "$WIN_CSC_LINK" ] ; then unset WIN_CSC_LINK ; fi - if [ -z "$WIN_CSC_KEY_PASSWORD" ] ; then unset WIN_CSC_KEY_PASSWORD ; fi - if [ -z "$APPLE_ID" ] ; then unset APPLE_ID ; fi - if [ -z "$APPLE_APP_SPECIFIC_PASSWORD" ] ; then unset APPLE_APP_SPECIFIC_PASSWORD ; fi - yarn run dist + run: if [ ! -z "$CSC_LINK" ] ; then yarn run dist ; else unset CSC_LINK && unset WIN_CSC_LINK && yarn run dist --publish=never ; fi env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} CSC_LINK: ${{ secrets.MAC_CERT_P12_BASE64 }} @@ -70,7 +48,7 @@ jobs: WIN_CSC_LINK: ${{ secrets.WIN_CERT_P12_BASE64 }} WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_P12_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} - name: Archive Build Artifacts uses: LabhanshAgrawal/upload-artifact@v3 with: @@ -80,19 +58,12 @@ jobs: dist/*.AppImage dist/*.deb dist/*.rpm - dist/*.pacman dist/*.exe - name: Run E2E Tests if: runner.os != 'Linux' run: yarn run test:e2e - - name: Run E2E Tests on Linux - if: runner.os == 'Linux' - uses: GabrielBB/xvfb-action@v1.6 - with: - run: yarn run test:e2e - env: - SHELL: /bin/bash - name: Archive E2E test screenshot + if: runner.os != 'Linux' uses: actions/upload-artifact@v3 with: name: e2e @@ -108,83 +79,3 @@ jobs: with: name: pr_num path: ./pr_num.txt - - uses: actions/cache/save@v4 - if: github.event_name == 'push' - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }} - - build-linux-arm: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - name: armv7l - cpu: cortex-a8 - image: raspios_lite:latest - - name: arm64 - cpu: cortex-a53 - image: raspios_lite_arm64:latest - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Fix node-gyp and Python - run: python3 -m pip install packaging setuptools - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - uses: actions/cache/restore@v4 - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Install - run: | - yarn install - sudo apt update - sudo apt install libarchive-tools - - name: Compile - run: yarn run build - - name: rebuild node-pty - uses: pguyot/arm-runner-action@v2.5.2 - with: - image_additional_mb: 2000 - base_image: ${{ matrix.image }} - cpu: ${{ matrix.cpu }} - shell: bash - copy_artifact_path: target/node_modules/node-pty - copy_artifact_dest: target/node_modules - commands: | - wget https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-${{ matrix.name }}.tar.xz - tar -xJf node-v18.16.0-linux-${{ matrix.name }}.tar.xz - sudo cp node-v18.16.0-linux-${{ matrix.name }}/* /usr/local/ -R - npm run rebuild-node-pty - - name: chown node-pty - run: | - sudo chown -R $USER:$USER target/node_modules/node-pty - - name: Prepare v8 snapshot - if: matrix.name == 'armv7l' - run: | - sudo dpkg --add-architecture i386 - sudo apt update - sudo apt install -y libglib2.0-0:i386 libexpat1:i386 libgcc-s1:i386 - npm_config_arch=armv7l yarn run v8-snapshot:arch - - name: Build - run: yarn run electron-builder -l deb rpm AppImage pacman --${{ matrix.name }} -c electron-builder-linux-ci.json - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Archive Build Artifacts - uses: LabhanshAgrawal/upload-artifact@v3 - with: - path: | - dist/*.snap - dist/*.AppImage - dist/*.deb - dist/*.rpm - dist/*.pacman diff --git a/app/auto-updater-linux.ts b/app/auto-updater-linux.ts index aa95c1d7..08bff975 100644 --- a/app/auto-updater-linux.ts +++ b/app/auto-updater-linux.ts @@ -1,6 +1,5 @@ -import {EventEmitter} from 'events'; - import fetch from 'electron-fetch'; +import {EventEmitter} from 'events'; class AutoUpdater extends EventEmitter implements Electron.AutoUpdater { updateURL!: string; @@ -27,12 +26,13 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater { this.emit('update-not-available'); return; } - return res.json().then(({name, notes, pub_date}: {name: string; notes: string; pub_date: string}) => { + return res.json().then(({name, notes, pub_date}) => { // Only name is mandatory, needed to construct release URL. if (!name) { throw new Error('Malformed server response: release name is missing.'); } - const date = pub_date ? new Date(pub_date) : new Date(); + // If `null` is passed to Date constructor, current time will be used. This doesn't work with `undefined` + const date = new Date(pub_date || null); this.emit('update-available', {}, notes, name, date); }); }) @@ -47,6 +47,4 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater { } } -const autoUpdaterLinux = new AutoUpdater(); - -export default autoUpdaterLinux; +export default new AutoUpdater(); diff --git a/app/commands.ts b/app/commands.ts index 469a5a3d..e285b163 100644 --- a/app/commands.ts +++ b/app/commands.ts @@ -1,6 +1,4 @@ -import {app, Menu} from 'electron'; -import type {BrowserWindow} from 'electron'; - +import {app, Menu, BrowserWindow} from 'electron'; import {openConfig, getConfig} from './config'; import {updatePlugins} from './plugins'; import {installCLI} from './utils/cli-install'; @@ -146,22 +144,6 @@ const commands: Record void> = { }; }); -//Profile specific commands -getConfig().profiles.forEach((profile) => { - commands[`window:new:${profile.name}`] = () => { - setTimeout(() => app.createWindow(undefined, undefined, profile.name), 0); - }; - commands[`tab:new:${profile.name}`] = (focusedWindow) => { - focusedWindow?.rpc.emit('termgroup add req', {profile: profile.name}); - }; - commands[`pane:splitRight:${profile.name}`] = (focusedWindow) => { - focusedWindow?.rpc.emit('split request vertical', {profile: profile.name}); - }; - commands[`pane:splitDown:${profile.name}`] = (focusedWindow) => { - focusedWindow?.rpc.emit('split request horizontal', {profile: profile.name}); - }; -}); - export const execCommand = (command: string, focusedWindow?: BrowserWindow) => { const fn = commands[command]; if (fn) { diff --git a/app/config.ts b/app/config.ts index b4613d57..979a352b 100644 --- a/app/config.ts +++ b/app/config.ts @@ -1,14 +1,11 @@ -import {app} from 'electron'; - import chokidar from 'chokidar'; - -import type {parsedConfig, configOptions} from '../typings/config'; - +import notify from './notify'; import {_import, getDefaultConfig} from './config/import'; import _openConfig from './config/open'; import {cfgPath, cfgDir} from './config/paths'; -import notify from './notify'; import {getColorMap} from './utils/colors'; +import {parsedConfig, configOptions} from '../lib/config'; +import {app} from 'electron'; const watchers: Function[] = []; let cfg: parsedConfig = {} as any; @@ -81,30 +78,8 @@ export const getConfigDir = () => { return cfgDir; }; -export const getDefaultProfile = () => { - return cfg.config.defaultProfile || cfg.config.profiles[0]?.name || 'default'; -}; - -// get config for the default profile, keeping it for backward compatibility export const getConfig = () => { - return getProfileConfig(getDefaultProfile()); -}; - -export const getProfiles = () => { - return cfg.config.profiles; -}; - -export const getProfileConfig = (profileName: string): configOptions => { - const {profiles, defaultProfile, ...baseConfig} = cfg.config; - const profileConfig = profiles.find((p) => p.name === profileName)?.config || {}; - for (const key in profileConfig) { - if (typeof baseConfig[key] === 'object' && !Array.isArray(baseConfig[key])) { - baseConfig[key] = {...baseConfig[key], ...profileConfig[key]}; - } else { - baseConfig[key] = profileConfig[key]; - } - } - return {...baseConfig, defaultProfile, profiles}; + return cfg.config; }; export const openConfig = () => { diff --git a/app/config/config-default.json b/app/config/config-default.json index 2a6a66ff..39c4e9fc 100644 --- a/app/config/config-default.json +++ b/app/config/config-default.json @@ -61,15 +61,7 @@ "disableAutoUpdates": false, "autoUpdatePlugins": true, "preserveCWD": true, - "screenReaderMode": false, - "imageSupport": true, - "defaultProfile": "default", - "profiles": [ - { - "name": "default", - "config": {} - } - ] + "screenReaderMode": false }, "plugins": [], "localPlugins": [], diff --git a/app/config/import.ts b/app/config/import.ts index b9965375..fd014a1c 100644 --- a/app/config/import.ts +++ b/app/config/import.ts @@ -1,11 +1,10 @@ -import {readFileSync, mkdirpSync} from 'fs-extra'; - -import type {rawConfig} from '../../typings/config'; -import notify from '../notify'; - -import {_init} from './init'; -import {migrateHyper3Config} from './migrate'; +import {readFileSync} from 'fs-extra'; +import {sync as mkdirpSync} from 'mkdirp'; import {defaultCfg, cfgPath, plugs, defaultPlatformKeyPath} from './paths'; +import {_init} from './init'; +import notify from '../notify'; +import {rawConfig} from '../../lib/config'; +import {migrateHyper3Config} from './migrate'; let defaultConfig: rawConfig; diff --git a/app/config/init.ts b/app/config/init.ts index a41b864c..8887fb64 100644 --- a/app/config/init.ts +++ b/app/config/init.ts @@ -1,14 +1,12 @@ import vm from 'vm'; - -import merge from 'lodash/merge'; - -import type {parsedConfig, rawConfig, configOptions} from '../../typings/config'; import notify from '../notify'; import mapKeys from '../utils/map-keys'; +import {parsedConfig, rawConfig, configOptions} from '../../lib/config'; +import _ from 'lodash'; const _extract = (script?: vm.Script): Record => { const module: Record = {}; - script?.runInNewContext({module}, {displayErrors: true}); + script?.runInNewContext({module}); if (!module.exports) { throw new Error('Error reading configuration: `module.exports` not set'); } @@ -18,10 +16,10 @@ const _extract = (script?: vm.Script): Record => { const _syntaxValidation = (cfg: string) => { try { - return new vm.Script(cfg, {filename: '.hyper.js'}); + return new vm.Script(cfg, {filename: '.hyper.js', displayErrors: true}); } catch (_err) { const err = _err as {name: string}; - notify(`Error loading config: ${err.name}`, JSON.stringify(err), {error: err}); + notify(`Error loading config: ${err.name}`, `${err}`, {error: err}); } }; @@ -34,19 +32,7 @@ const _init = (userCfg: rawConfig, defaultCfg: rawConfig): parsedConfig => { return { config: (() => { if (userCfg?.config) { - const conf = userCfg.config; - conf.defaultProfile = conf.defaultProfile || 'default'; - conf.profiles = conf.profiles || []; - conf.profiles = conf.profiles.length > 0 ? conf.profiles : [{name: 'default', config: {}}]; - conf.profiles = conf.profiles.map((p, i) => ({ - ...p, - name: p.name || `profile-${i + 1}`, - config: p.config || {} - })); - if (!conf.profiles.map((p) => p.name).includes(conf.defaultProfile)) { - conf.defaultProfile = conf.profiles[0].name; - } - return merge({}, defaultCfg.config, conf); + return _.merge({}, defaultCfg.config, userCfg.config); } else { notify('Error reading configuration: `config` key is missing'); return defaultCfg.config || ({} as configOptions); @@ -55,8 +41,8 @@ const _init = (userCfg: rawConfig, defaultCfg: rawConfig): parsedConfig => { // Merging platform specific keymaps with user defined keymaps keymaps: mapKeys({...defaultCfg.keymaps, ...userCfg?.keymaps}), // Ignore undefined values in plugin and localPlugins array Issue #1862 - plugins: userCfg?.plugins?.filter(Boolean) || [], - localPlugins: userCfg?.localPlugins?.filter(Boolean) || [] + plugins: (userCfg?.plugins && userCfg.plugins.filter(Boolean)) || [], + localPlugins: (userCfg?.localPlugins && userCfg.localPlugins.filter(Boolean)) || [] }; }; diff --git a/app/config/migrate.ts b/app/config/migrate.ts index 522da1e9..d5eef17a 100644 --- a/app/config/migrate.ts +++ b/app/config/migrate.ts @@ -1,14 +1,11 @@ -import {dirname, resolve} from 'path'; - -import {builders, namedTypes} from 'ast-types'; -import type {ExpressionKind} from 'ast-types/lib/gen/kinds'; -import {copy, copySync, existsSync, readFileSync, writeFileSync} from 'fs-extra'; -import merge from 'lodash/merge'; import {parse, prettyPrint} from 'recast'; +import {builders, namedTypes} from 'ast-types'; import * as babelParser from 'recast/parsers/babel'; +import {copy, copySync, existsSync, readFileSync, writeFileSync} from 'fs-extra'; +import {dirname, resolve} from 'path'; +import _ from 'lodash'; import notify from '../notify'; - import {_extractDefault} from './init'; import {cfgDir, cfgPath, defaultCfg, legacyCfgPath, plugs, schemaFile, schemaPath} from './paths'; @@ -66,7 +63,7 @@ export function configToPlugin(code: string): string { }); const statements = ast.program.body; let moduleExportsNode: namedTypes.AssignmentExpression | null = null; - let configNode: ExpressionKind | null = null; + let configNode: any = null; for (const statement of statements) { if (namedTypes.ExpressionStatement.check(statement)) { @@ -89,7 +86,7 @@ export function configToPlugin(code: string): string { namedTypes.Identifier.check(property.key) && property.key.name === 'config' ) { - configNode = property.value as ExpressionKind; + configNode = property.value; if (namedTypes.ObjectExpression.check(property.value)) { configNode = removeProperties(property.value); } @@ -168,7 +165,7 @@ export const migrateHyper3Config = () => { try { const legacyCfgRaw = readFileSync(legacyCfgPath, 'utf8'); const legacyCfgData = _extractDefault(legacyCfgRaw); - newCfgData = merge({}, defaultCfgData, legacyCfgData); + newCfgData = _.merge({}, defaultCfgData, legacyCfgData); const pluginCode = configToPlugin(legacyCfgRaw); if (pluginCode) { diff --git a/app/config/open.ts b/app/config/open.ts index 264c2292..1d5047c9 100644 --- a/app/config/open.ts +++ b/app/config/open.ts @@ -1,10 +1,7 @@ -import {exec} from 'child_process'; - import {shell} from 'electron'; - -import * as Registry from 'native-reg'; - import {cfgPath} from './paths'; +import * as Registry from 'native-reg'; +import {exec} from 'child_process'; const getUserChoiceKey = () => { try { @@ -60,7 +57,7 @@ const openNotepad = (file: string) => }); }); -const openConfig = () => { +export default () => { // Windows opens .js files with WScript.exe by default // If the user hasn't set up an editor for .js files, we fallback to notepad. if (process.platform === 'win32') { @@ -76,5 +73,3 @@ const openConfig = () => { } return shell.openPath(cfgPath).then((error) => error === ''); }; - -export default openConfig; diff --git a/app/config/paths.ts b/app/config/paths.ts index 2c2ce283..a0b6fa52 100644 --- a/app/config/paths.ts +++ b/app/config/paths.ts @@ -1,10 +1,8 @@ // This module exports paths, names, and other metadata that is referenced -import {statSync} from 'fs'; import {homedir} from 'os'; -import {resolve, join} from 'path'; - import {app} from 'electron'; - +import {statSync} from 'fs'; +import {resolve, join} from 'path'; import isDev from 'electron-is-dev'; const cfgFile = 'hyper.json'; @@ -17,15 +15,15 @@ const homeDirectory = homedir(); let cfgDir = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, 'Hyper') : process.platform === 'win32' - ? app.getPath('userData') - : join(homeDirectory, '.config', 'Hyper'); + ? app.getPath('userData') + : join(homeDirectory, '.config', 'Hyper'); const legacyCfgPath = join( process.env.XDG_CONFIG_HOME !== undefined ? join(process.env.XDG_CONFIG_HOME, 'hyper') : process.platform == 'win32' - ? app.getPath('userData') - : homedir(), + ? app.getPath('userData') + : homedir(), '.hyper.js' ); diff --git a/app/config/schema.json b/app/config/schema.json index 6bcf8500..afb8f2ee 100644 --- a/app/config/schema.json +++ b/app/config/schema.json @@ -24,19 +24,25 @@ } ], "description": "A string or number representing text font weight." - }, - "Partial": { + } + }, + "properties": { + "config": { "properties": { + "autoUpdatePlugins": { + "description": "if `true` (default), Hyper will update plugins every 5 hours\nyou can also set it to a custom time e.g. `1d` or `2h`", + "type": [ + "string", + "boolean" + ] + }, "backgroundColor": { "description": "terminal background color\n\nopacity is only supported on macOS", "type": "string" }, "bell": { "description": "Supported Options:\n1. 'SOUND' -> Enables the bell as a sound\n2. false: turns off the bell", - "enum": [ - "SOUND", - false - ] + "type": "string" }, "bellSound": { "description": "base64 encoded string of the sound file to use for the bell\nif null, the default bell will be used", @@ -157,6 +163,14 @@ ], "type": "string" }, + "defaultSSHApp": { + "description": "if `true` hyper will be set as the default protocol client for SSH", + "type": "boolean" + }, + "disableAutoUpdates": { + "description": "if `true` hyper will not check for updates", + "type": "boolean" + }, "disableLigatures": { "description": "if `false` Hyper will use ligatures provided by some fonts", "type": "boolean" @@ -188,10 +202,6 @@ "description": "color of the text", "type": "string" }, - "imageSupport": { - "description": "Whether to enable Sixel and iTerm2 inline image protocol support or not.", - "type": "boolean" - }, "letterSpacing": { "description": "letter spacing as a relative unit", "type": "number" @@ -243,11 +253,11 @@ "type": "string" }, "shell": { - "description": "the shell to run when spawning a new session (e.g. /usr/local/bin/fish)\nif left empty, your system's login shell will be used by default\n\nWindows\n- Make sure to use a full path if the binary name doesn't work\n- Remove `--login` in shellArgs\n\nWindows Subsystem for Linux (WSL) - previously Bash on Windows\n- Example: `C:\\\\Windows\\\\System32\\\\wsl.exe`\n\nGit-bash on Windows\n- Example: `C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe`\n\nPowerShell on Windows\n- Example: `C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`\n\nCygwin\n- Example: `C:\\\\cygwin64\\\\bin\\\\bash.exe`\n\nGit Bash\n- Example: `C:\\\\Program Files\\\\Git\\\\git-cmd.exe`\nThen Add `--command=usr/bin/bash.exe` to shellArgs", + "description": "the shell to run when spawning a new session (i.e. /usr/local/bin/fish)\nif left empty, your system's login shell will be used by default\n\nWindows\n- Make sure to use a full path if the binary name doesn't work\n- Remove `--login` in shellArgs\n\nWindows Subsystem for Linux (WSL) - previously Bash on Windows\n- Example: `C:\\\\Windows\\\\System32\\\\wsl.exe`\n\nGit-bash on Windows\n- Example: `C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe`\n\nPowerShell on Windows\n- Example: `C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`\n\nCygwin\n- Example: `C:\\\\cygwin64\\\\bin\\\\bash.exe`\n\nGit Bash\n- Example: `C:\\\\Program Files\\\\Git\\\\git-cmd.exe`\nThen Add `--command=usr/bin/bash.exe` to shellArgs", "type": "string" }, "shellArgs": { - "description": "for setting shell arguments (e.g. for using interactive shellArgs: `['-i']`)\nby default `['--login']` will be used", + "description": "for setting shell arguments (i.e. for using interactive shellArgs: `['-i']`)\nby default `['--login']` will be used", "items": { "type": "string" }, @@ -277,6 +287,17 @@ "uiFontFamily": { "type": "string" }, + "updateChannel": { + "description": "choose either `'stable'` for receiving highly polished, or `'canary'` for less polished but more frequent updates", + "enum": [ + "canary", + "stable" + ], + "type": "string" + }, + "useConpty": { + "type": "boolean" + }, "webGLRenderer": { "description": "Whether to use the WebGL renderer. Set it to false to use canvas-based\nrendering (slower, but supports transparent backgrounds)", "type": "boolean" @@ -311,414 +332,50 @@ "type": "string" } }, + "required": [ + "autoUpdatePlugins", + "backgroundColor", + "bell", + "bellSound", + "bellSoundURL", + "borderColor", + "colors", + "copyOnSelect", + "css", + "cursorAccentColor", + "cursorBlink", + "cursorColor", + "cursorShape", + "defaultSSHApp", + "disableAutoUpdates", + "disableLigatures", + "env", + "fontFamily", + "fontSize", + "fontWeight", + "fontWeightBold", + "foregroundColor", + "letterSpacing", + "lineHeight", + "macOptionSelectionMode", + "padding", + "preserveCWD", + "quickEdit", + "screenReaderMode", + "scrollback", + "selectionColor", + "shell", + "shellArgs", + "showHamburgerMenu", + "showWindowControls", + "termCSS", + "updateChannel", + "webGLRenderer", + "webLinksActivationKey", + "workingDirectory" + ], "type": "object" }, - "configOptions": { - "allOf": [ - { - "properties": { - "autoUpdatePlugins": { - "description": "if `true` (default), Hyper will update plugins every 5 hours\nyou can also set it to a custom time e.g. `1d` or `2h`", - "type": [ - "string", - "boolean" - ] - }, - "defaultSSHApp": { - "description": "if `true` hyper will be set as the default protocol client for SSH", - "type": "boolean" - }, - "disableAutoUpdates": { - "description": "if `true` hyper will not check for updates", - "type": "boolean" - }, - "updateChannel": { - "description": "choose either `'stable'` for receiving highly polished, or `'canary'` for less polished but more frequent updates", - "enum": [ - "canary", - "stable" - ], - "type": "string" - }, - "useConpty": { - "type": "boolean" - } - }, - "required": [ - "autoUpdatePlugins", - "defaultSSHApp", - "disableAutoUpdates", - "updateChannel" - ], - "type": "object" - }, - { - "properties": { - "backgroundColor": { - "description": "terminal background color\n\nopacity is only supported on macOS", - "type": "string" - }, - "bell": { - "description": "Supported Options:\n1. 'SOUND' -> Enables the bell as a sound\n2. false: turns off the bell", - "enum": [ - "SOUND", - false - ] - }, - "bellSound": { - "description": "base64 encoded string of the sound file to use for the bell\nif null, the default bell will be used", - "type": [ - "string", - "null" - ] - }, - "bellSoundURL": { - "description": "An absolute file path to a sound file on the machine.", - "type": [ - "string", - "null" - ] - }, - "borderColor": { - "description": "border color (window, tabs)", - "type": "string" - }, - "colors": { - "description": "the full list. if you're going to provide the full color palette,\nincluding the 6 x 6 color cubes and the grayscale map, just provide\nan array here instead of a color map object", - "properties": { - "black": { - "type": "string" - }, - "blue": { - "type": "string" - }, - "cyan": { - "type": "string" - }, - "green": { - "type": "string" - }, - "lightBlack": { - "type": "string" - }, - "lightBlue": { - "type": "string" - }, - "lightCyan": { - "type": "string" - }, - "lightGreen": { - "type": "string" - }, - "lightMagenta": { - "type": "string" - }, - "lightRed": { - "type": "string" - }, - "lightWhite": { - "type": "string" - }, - "lightYellow": { - "type": "string" - }, - "magenta": { - "type": "string" - }, - "red": { - "type": "string" - }, - "white": { - "type": "string" - }, - "yellow": { - "type": "string" - } - }, - "required": [ - "black", - "blue", - "cyan", - "green", - "lightBlack", - "lightBlue", - "lightCyan", - "lightGreen", - "lightMagenta", - "lightRed", - "lightWhite", - "lightYellow", - "magenta", - "red", - "white", - "yellow" - ], - "type": "object" - }, - "copyOnSelect": { - "description": "if `true` selected text will automatically be copied to the clipboard", - "type": "boolean" - }, - "css": { - "description": "custom CSS to embed in the main window", - "type": "string" - }, - "cursorAccentColor": { - "description": "terminal text color under BLOCK cursor", - "type": "string" - }, - "cursorBlink": { - "description": "set to `true` for blinking cursor", - "type": "boolean" - }, - "cursorColor": { - "description": "terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)", - "type": "string" - }, - "cursorShape": { - "description": "`'BEAM'` for |, `'UNDERLINE'` for _, `'BLOCK'` for █", - "enum": [ - "BEAM", - "BLOCK", - "UNDERLINE" - ], - "type": "string" - }, - "disableLigatures": { - "description": "if `false` Hyper will use ligatures provided by some fonts", - "type": "boolean" - }, - "env": { - "additionalProperties": { - "type": "string" - }, - "description": "for environment variables", - "type": "object" - }, - "fontFamily": { - "description": "font family with optional fallbacks", - "type": "string" - }, - "fontSize": { - "description": "default font size in pixels for all tabs", - "type": "number" - }, - "fontWeight": { - "$ref": "#/definitions/FontWeight", - "description": "default font weight eg:'normal', '400', 'bold'" - }, - "fontWeightBold": { - "$ref": "#/definitions/FontWeight", - "description": "font weight for bold characters eg:'normal', '600', 'bold'" - }, - "foregroundColor": { - "description": "color of the text", - "type": "string" - }, - "imageSupport": { - "description": "Whether to enable Sixel and iTerm2 inline image protocol support or not.", - "type": "boolean" - }, - "letterSpacing": { - "description": "letter spacing as a relative unit", - "type": "number" - }, - "lineHeight": { - "description": "line height as a relative unit", - "type": "number" - }, - "macOptionSelectionMode": { - "description": "choose either `'vertical'`, if you want the column mode when Option key is hold during selection (Default)\nor `'force'`, if you want to force selection regardless of whether the terminal is in mouse events mode\n(inside tmux or vim with mouse mode enabled for example).", - "type": "string" - }, - "modifierKeys": { - "properties": { - "altIsMeta": { - "type": "boolean" - }, - "cmdIsMeta": { - "type": "boolean" - } - }, - "required": [ - "altIsMeta", - "cmdIsMeta" - ], - "type": "object" - }, - "padding": { - "description": "custom padding (CSS format, i.e.: `top right bottom left` or `top horizontal bottom` or `vertical horizontal` or `all`)", - "type": "string" - }, - "preserveCWD": { - "description": "set to true to preserve working directory when creating splits or tabs", - "type": "boolean" - }, - "quickEdit": { - "description": "if `true` on right click selected text will be copied or pasted if no\nselection is present (`true` by default on Windows and disables the context menu feature)", - "type": "boolean" - }, - "screenReaderMode": { - "description": "set to true to enable screen reading apps (like NVDA) to read the contents of the terminal", - "type": "boolean" - }, - "scrollback": { - "type": "number" - }, - "selectionColor": { - "description": "terminal selection color", - "type": "string" - }, - "shell": { - "description": "the shell to run when spawning a new session (e.g. /usr/local/bin/fish)\nif left empty, your system's login shell will be used by default\n\nWindows\n- Make sure to use a full path if the binary name doesn't work\n- Remove `--login` in shellArgs\n\nWindows Subsystem for Linux (WSL) - previously Bash on Windows\n- Example: `C:\\\\Windows\\\\System32\\\\wsl.exe`\n\nGit-bash on Windows\n- Example: `C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe`\n\nPowerShell on Windows\n- Example: `C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`\n\nCygwin\n- Example: `C:\\\\cygwin64\\\\bin\\\\bash.exe`\n\nGit Bash\n- Example: `C:\\\\Program Files\\\\Git\\\\git-cmd.exe`\nThen Add `--command=usr/bin/bash.exe` to shellArgs", - "type": "string" - }, - "shellArgs": { - "description": "for setting shell arguments (e.g. for using interactive shellArgs: `['-i']`)\nby default `['--login']` will be used", - "items": { - "type": "string" - }, - "type": "array" - }, - "showHamburgerMenu": { - "description": "if you're using a Linux setup which show native menus, set to false\n\ndefault: `true` on Linux, `true` on Windows, ignored on macOS", - "enum": [ - "", - false, - true - ] - }, - "showWindowControls": { - "description": "set to `false` if you want to hide the minimize, maximize and close buttons\n\nadditionally, set to `'left'` if you want them on the left, like in Ubuntu\n\ndefault: `true` on Windows and Linux, ignored on macOS", - "enum": [ - "", - false, - "left", - true - ] - }, - "termCSS": { - "description": "custom CSS to embed in the terminal window", - "type": "string" - }, - "uiFontFamily": { - "type": "string" - }, - "webGLRenderer": { - "description": "Whether to use the WebGL renderer. Set it to false to use canvas-based\nrendering (slower, but supports transparent backgrounds)", - "type": "boolean" - }, - "webLinksActivationKey": { - "description": "keypress required for weblink activation: [ctrl | alt | meta | shift]", - "enum": [ - "", - "alt", - "ctrl", - "meta", - "shift" - ], - "type": "string" - }, - "windowSize": { - "description": "Initial window size in pixels", - "items": [ - { - "type": "number" - }, - { - "type": "number" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - }, - "workingDirectory": { - "description": "set custom startup directory (must be an absolute path)", - "type": "string" - } - }, - "required": [ - "backgroundColor", - "bell", - "bellSound", - "bellSoundURL", - "borderColor", - "colors", - "copyOnSelect", - "css", - "cursorAccentColor", - "cursorBlink", - "cursorColor", - "cursorShape", - "disableLigatures", - "env", - "fontFamily", - "fontSize", - "fontWeight", - "fontWeightBold", - "foregroundColor", - "imageSupport", - "letterSpacing", - "lineHeight", - "macOptionSelectionMode", - "padding", - "preserveCWD", - "quickEdit", - "screenReaderMode", - "scrollback", - "selectionColor", - "shell", - "shellArgs", - "showHamburgerMenu", - "showWindowControls", - "termCSS", - "webGLRenderer", - "webLinksActivationKey", - "workingDirectory" - ], - "type": "object" - }, - { - "properties": { - "defaultProfile": { - "description": "The default profile name to use when launching a new session", - "type": "string" - }, - "profiles": { - "description": "A list of profiles to use", - "items": { - "properties": { - "config": { - "$ref": "#/definitions/Partial", - "description": "Specify all the options you want to override for each profile.\nOptions set here override the defaults set in the root." - }, - "name": { - "type": "string" - } - }, - "required": [ - "config", - "name" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "defaultProfile", - "profiles" - ], - "type": "object" - } - ] - } - }, - "properties": { - "config": { - "$ref": "#/definitions/configOptions" - }, "keymaps": { "additionalProperties": { "anyOf": [ diff --git a/app/config/windows.ts b/app/config/windows.ts index 4cd6d500..c18518a7 100644 --- a/app/config/windows.ts +++ b/app/config/windows.ts @@ -1,6 +1,5 @@ -import type {BrowserWindow} from 'electron'; - import Config from 'electron-store'; +import {BrowserWindow} from 'electron'; export const defaults = { windowPosition: [50, 50] as [number, number], diff --git a/app/ext-modules.d.ts b/app/ext-modules.d.ts new file mode 100644 index 00000000..443d58ee --- /dev/null +++ b/app/ext-modules.d.ts @@ -0,0 +1,8 @@ +declare module 'git-describe' { + export function gitDescribe(...args: any[]): void; +} + +declare module 'default-shell' { + const val: string; + export default val; +} diff --git a/typings/extend-electron.d.ts b/app/extend-electron.d.ts similarity index 75% rename from typings/extend-electron.d.ts rename to app/extend-electron.d.ts index ae767301..557d5cc1 100644 --- a/typings/extend-electron.d.ts +++ b/app/extend-electron.d.ts @@ -1,17 +1,16 @@ -import type {Server} from '../app/rpc'; +import type {Server} from './rpc'; declare global { namespace Electron { interface App { - config: typeof import('../app/config'); - plugins: typeof import('../app/plugins'); + config: typeof import('./config'); + plugins: typeof import('./plugins'); getWindows: () => Set; getLastFocusedWindow: () => BrowserWindow | null; windowCallback?: (win: BrowserWindow) => void; createWindow: ( fn?: (win: BrowserWindow) => void, - options?: {size?: [number, number]; position?: [number, number]}, - profileName?: string + options?: {size?: [number, number]; position?: [number, number]} ) => BrowserWindow; setVersion: (version: string) => void; } @@ -23,7 +22,6 @@ declare global { focusTime: number; clean: () => void; rpc: Server; - profileName: string; } } } diff --git a/app/index.d.ts b/app/index.d.ts new file mode 100644 index 00000000..1349d7d9 --- /dev/null +++ b/app/index.d.ts @@ -0,0 +1 @@ +// Dummy file, required by tsc diff --git a/app/index.ts b/app/index.ts index 84a804a4..8537af8c 100644 --- a/app/index.ts +++ b/app/index.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import/order import {cfgPath} from './config/paths'; // Print diagnostic information for a few arguments instead of running Hyper. @@ -12,29 +11,25 @@ if (['--help', '-v', '--version'].includes(process.argv[1])) { } // Enable remote module -// eslint-disable-next-line import/order import {initialize as remoteInitialize} from '@electron/remote/main'; remoteInitialize(); -// set up config -// eslint-disable-next-line import/order -import * as config from './config'; -config.setup(); - // Native import {resolve} from 'path'; // Packages import {app, BrowserWindow, Menu, screen} from 'electron'; - -import isDev from 'electron-is-dev'; import {gitDescribe} from 'git-describe'; -import parseUrl from 'parse-url'; +import isDev from 'electron-is-dev'; +import * as config from './config'; + +// set up config +config.setup(); -import * as AppMenu from './menus/menu'; import * as plugins from './plugins'; -import {newWindow} from './ui/window'; import {installCLI} from './utils/cli-install'; +import * as AppMenu from './menus/menu'; +import {newWindow} from './ui/window'; import * as windowUtils from './utils/window-utils'; const windowSet = new Set([]); @@ -62,7 +57,7 @@ if (isDev) { console.log('running in dev mode'); // Override default appVersion which is set from package.json - gitDescribe({customArguments: ['--tags']}, (error: any, gitInfo: {raw: string}) => { + gitDescribe({customArguments: ['--tags']}, (error: any, gitInfo: any) => { if (!error) { app.setVersion(gitInfo.raw); } @@ -78,13 +73,15 @@ async function installDevExtensions(isDev_: boolean) { if (!isDev_) { return []; } - const {default: installer, REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS} = await import('electron-devtools-installer'); + const installer = await import('electron-devtools-installer'); - const extensions = [REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS]; + const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'] as const; const forceDownload = Boolean(process.env.UPGRADE_EXTENSIONS); return Promise.all( - extensions.map((extension) => installer(extension, {forceDownload, loadExtensionOptions: {allowFileAccess: true}})) + extensions.map((name) => + installer.default(installer[name], {forceDownload, loadExtensionOptions: {allowFileAccess: true}}) + ) ); } @@ -94,10 +91,9 @@ app.on('ready', () => .then(() => { function createWindow( fn?: (win: BrowserWindow) => void, - options: {size?: [number, number]; position?: [number, number]} = {}, - profileName: string = config.getDefaultProfile() + options: {size?: [number, number]; position?: [number, number]} = {} ) { - const cfg = plugins.getDecoratedConfig(profileName); + const cfg = plugins.getDecoratedConfig(); const winSet = config.getWin(); let [startX, startY] = winSet.position; @@ -139,7 +135,7 @@ app.on('ready', () => [startX, startY] = config.windowDefaults.windowPosition; } - const hwin = newWindow({width, height, x: startX, y: startY}, cfg, fn, profileName); + const hwin = newWindow({width, height, x: startX, y: startY}, cfg, fn); windowSet.add(hwin); void hwin.loadURL(url); @@ -238,6 +234,6 @@ app.on('open-file', (_event, path) => { app.on('open-url', (_event, sshUrl) => { GetWindow((win: BrowserWindow) => { - win.rpc.emit('open ssh', parseUrl(sshUrl)); + win.rpc.emit('open ssh', sshUrl); }); }); diff --git a/app/menus/menu.ts b/app/menus/menu.ts index df877e69..dc2f3c61 100644 --- a/app/menus/menu.ts +++ b/app/menus/menu.ts @@ -1,21 +1,19 @@ // Packages -import {app, dialog, Menu} from 'electron'; -import type {BrowserWindow} from 'electron'; +import {app, dialog, Menu, BrowserWindow} from 'electron'; // Utilities -import {execCommand} from '../commands'; import {getConfig} from '../config'; import {icon} from '../config/paths'; -import {getDecoratedKeymaps} from '../plugins'; -import {getRendererTypes} from '../utils/renderer-utils'; - -import darwinMenu from './menus/darwin'; -import editMenu from './menus/edit'; -import helpMenu from './menus/help'; -import shellMenu from './menus/shell'; -import toolsMenu from './menus/tools'; import viewMenu from './menus/view'; +import shellMenu from './menus/shell'; +import editMenu from './menus/edit'; +import toolsMenu from './menus/tools'; import windowMenu from './menus/window'; +import helpMenu from './menus/help'; +import darwinMenu from './menus/darwin'; +import {getDecoratedKeymaps} from '../plugins'; +import {execCommand} from '../commands'; +import {getRendererTypes} from '../utils/renderer-utils'; const appName = app.name; const appVersion = app.getVersion(); @@ -63,11 +61,7 @@ export const createMenu = ( }; const menu = [ ...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []), - shellMenu( - commandKeys, - execCommand, - getConfig().profiles.map((p) => p.name) - ), + shellMenu(commandKeys, execCommand), editMenu(commandKeys, execCommand), viewMenu(commandKeys, execCommand), toolsMenu(commandKeys, execCommand), diff --git a/app/menus/menus/darwin.ts b/app/menus/menus/darwin.ts index 59e514d1..1a1376d6 100644 --- a/app/menus/menus/darwin.ts +++ b/app/menus/menus/darwin.ts @@ -1,9 +1,8 @@ // This menu label is overrided by OSX to be the appName // The label is set to appName here so it matches actual behavior -import {app} from 'electron'; -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {app, BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const darwinMenu = ( +export default ( commandKeys: Record, execCommand: (command: string, focusedWindow?: BrowserWindow) => void, showAbout: () => void @@ -55,5 +54,3 @@ const darwinMenu = ( ] }; }; - -export default darwinMenu; diff --git a/app/menus/menus/edit.ts b/app/menus/menus/edit.ts index a9040f6e..46070a87 100644 --- a/app/menus/menus/edit.ts +++ b/app/menus/menus/edit.ts @@ -1,6 +1,6 @@ -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const editMenu = ( +export default ( commandKeys: Record, execCommand: (command: string, focusedWindow?: BrowserWindow) => void ) => { @@ -147,5 +147,3 @@ const editMenu = ( submenu }; }; - -export default editMenu; diff --git a/app/menus/menus/help.ts b/app/menus/menus/help.ts index 3c9214c1..2dcd7971 100644 --- a/app/menus/menus/help.ts +++ b/app/menus/menus/help.ts @@ -1,14 +1,10 @@ import {release} from 'os'; - -import {app, shell, dialog, clipboard} from 'electron'; -import type {MenuItemConstructorOptions} from 'electron'; - +import {app, shell, MenuItemConstructorOptions, dialog, clipboard} from 'electron'; import {getConfig, getPlugins} from '../../config'; +const {arch, env, platform, versions} = process; import {version} from '../../package.json'; -const {arch, env, platform, versions} = process; - -const helpMenu = (commands: Record, showAbout: () => void): MenuItemConstructorOptions => { +export default (commands: Record, showAbout: () => void): MenuItemConstructorOptions => { const submenu: MenuItemConstructorOptions[] = [ { label: `${app.name} Website`, @@ -110,5 +106,3 @@ ${JSON.stringify(getPlugins(), null, 2)} submenu }; }; - -export default helpMenu; diff --git a/app/menus/menus/shell.ts b/app/menus/menus/shell.ts index 651f071c..ec8bde39 100644 --- a/app/menus/menus/shell.ts +++ b/app/menus/menus/shell.ts @@ -1,9 +1,8 @@ -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const shellMenu = ( +export default ( commandKeys: Record, - execCommand: (command: string, focusedWindow?: BrowserWindow) => void, - profiles: string[] + execCommand: (command: string, focusedWindow?: BrowserWindow) => void ): MenuItemConstructorOptions => { const isMac = process.platform === 'darwin'; @@ -44,47 +43,6 @@ const shellMenu = ( { type: 'separator' }, - ...profiles.map( - (profile): MenuItemConstructorOptions => ({ - label: profile, - submenu: [ - { - label: 'New Tab', - accelerator: commandKeys[`tab:new:${profile}`], - click(item, focusedWindow) { - execCommand(`tab:new:${profile}`, focusedWindow); - } - }, - { - label: 'New Window', - accelerator: commandKeys[`window:new:${profile}`], - click(item, focusedWindow) { - execCommand(`window:new:${profile}`, focusedWindow); - } - }, - { - type: 'separator' - }, - { - label: 'Split Down', - accelerator: commandKeys[`pane:splitDown:${profile}`], - click(item, focusedWindow) { - execCommand(`pane:splitDown:${profile}`, focusedWindow); - } - }, - { - label: 'Split Right', - accelerator: commandKeys[`pane:splitRight:${profile}`], - click(item, focusedWindow) { - execCommand(`pane:splitRight:${profile}`, focusedWindow); - } - } - ] - }) - ), - { - type: 'separator' - }, { label: 'Close', accelerator: commandKeys['pane:close'], @@ -100,5 +58,3 @@ const shellMenu = ( ] }; }; - -export default shellMenu; diff --git a/app/menus/menus/tools.ts b/app/menus/menus/tools.ts index ac377728..843d3d35 100644 --- a/app/menus/menus/tools.ts +++ b/app/menus/menus/tools.ts @@ -1,6 +1,6 @@ -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const toolsMenu = ( +export default ( commands: Record, execCommand: (command: string, focusedWindow?: BrowserWindow) => void ): MenuItemConstructorOptions => { @@ -45,5 +45,3 @@ const toolsMenu = ( ] }; }; - -export default toolsMenu; diff --git a/app/menus/menus/view.ts b/app/menus/menus/view.ts index d0a2b071..98f9ef1e 100644 --- a/app/menus/menus/view.ts +++ b/app/menus/menus/view.ts @@ -1,6 +1,6 @@ -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const viewMenu = ( +export default ( commandKeys: Record, execCommand: (command: string, focusedWindow?: BrowserWindow) => void ): MenuItemConstructorOptions => { @@ -55,5 +55,3 @@ const viewMenu = ( ] }; }; - -export default viewMenu; diff --git a/app/menus/menus/window.ts b/app/menus/menus/window.ts index 7ae142a6..4f693bda 100644 --- a/app/menus/menus/window.ts +++ b/app/menus/menus/window.ts @@ -1,6 +1,6 @@ -import type {BrowserWindow, MenuItemConstructorOptions} from 'electron'; +import {BrowserWindow, MenuItemConstructorOptions} from 'electron'; -const windowMenu = ( +export default ( commandKeys: Record, execCommand: (command: string, focusedWindow?: BrowserWindow) => void ): MenuItemConstructorOptions => { @@ -94,5 +94,3 @@ const windowMenu = ( ] }; }; - -export default windowMenu; diff --git a/app/notifications.ts b/app/notifications.ts index 1a7abb06..ad7aaabd 100644 --- a/app/notifications.ts +++ b/app/notifications.ts @@ -1,9 +1,7 @@ -import type {BrowserWindow} from 'electron'; - -import fetch from 'electron-fetch'; import ms from 'ms'; - +import fetch from 'electron-fetch'; import {version} from './package.json'; +import {BrowserWindow} from 'electron'; const NEWS_URL = 'https://hyper-news.now.sh'; @@ -24,7 +22,7 @@ export default function fetchNotifications(win: BrowserWindow) { }) .then((res) => res.json()) .then((data) => { - const message: {text: string; url: string; dismissable: boolean} | '' = data.message || ''; + const {message} = data || {}; if (typeof message !== 'object' && message !== '') { throw new Error('Bad response'); } diff --git a/app/notify.ts b/app/notify.ts index ce5544fc..a31f0305 100644 --- a/app/notify.ts +++ b/app/notify.ts @@ -1,5 +1,4 @@ import {app, Notification} from 'electron'; - import {icon} from './config/paths'; export default function notify(title: string, body = '', details: {error?: any} = {}) { diff --git a/app/package.json b/app/package.json index a7c6f60f..d7ea30be 100644 --- a/app/package.json +++ b/app/package.json @@ -2,44 +2,41 @@ "name": "hyper", "productName": "Hyper", "description": "A terminal built on web technologies", - "version": "4.0.0-canary.5", + "version": "4.0.0-canary.3", "license": "MIT", "author": { "name": "ZEIT, Inc.", "email": "team@zeit.co" }, "repository": "zeit/hyper", - "scripts": { - "postinstall": "npx patch-package" - }, "dependencies": { - "@babel/parser": "7.24.4", - "@electron/remote": "2.1.2", - "ast-types": "^0.16.1", + "@babel/parser": "7.20.7", + "@electron/remote": "2.0.9", "async-retry": "1.3.3", - "chokidar": "^3.6.0", + "chokidar": "^3.5.3", "color": "4.2.3", "default-shell": "1.0.1", - "electron-devtools-installer": "3.2.0", "electron-fetch": "1.9.1", "electron-is-dev": "2.0.0", - "electron-store": "8.2.0", - "fs-extra": "11.2.0", + "electron-store": "8.1.0", + "fs-extra": "11.1.0", "git-describe": "4.1.1", "lodash": "4.17.21", + "mkdirp": "1.0.4", "ms": "2.1.3", "native-process-working-directory": "^1.0.2", - "node-pty": "1.0.0", + "node-pty": "0.11.0-beta27", "os-locale": "5.0.0", "parse-url": "8.1.0", + "pify": "5.0.0", "queue": "6.0.2", - "react": "18.2.0", - "react-dom": "18.2.0", - "recast": "0.23.6", - "semver": "7.6.0", + "react": "17.0.2", + "react-dom": "17.0.2", + "recast": "0.22.0", + "semver": "7.3.8", "shell-env": "3.0.1", "sudo-prompt": "^9.2.1", - "uuid": "9.0.1" + "uuid": "9.0.0" }, "optionalDependencies": { "native-reg": "1.1.1" diff --git a/app/patches/node-pty+1.0.0.patch b/app/patches/node-pty+1.0.0.patch deleted file mode 100644 index 51f70f2d..00000000 --- a/app/patches/node-pty+1.0.0.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/node_modules/node-pty/src/win/conpty.cc b/node_modules/node-pty/src/win/conpty.cc -index 47af75c..884d542 100644 ---- a/node_modules/node-pty/src/win/conpty.cc -+++ b/node_modules/node-pty/src/win/conpty.cc -@@ -472,10 +472,6 @@ static NAN_METHOD(PtyKill) { - } - } - -- DisconnectNamedPipe(handle->hIn); -- DisconnectNamedPipe(handle->hOut); -- CloseHandle(handle->hIn); -- CloseHandle(handle->hOut); - CloseHandle(handle->hShell); - } - diff --git a/app/plugins.ts b/app/plugins.ts index 8e460119..2e2d257b 100644 --- a/app/plugins.ts +++ b/app/plugins.ts @@ -1,28 +1,22 @@ /* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -import {exec, execFile} from 'child_process'; -import {writeFileSync} from 'fs'; +import {app, dialog, BrowserWindow, App, ipcMain} from 'electron'; import {resolve, basename} from 'path'; -import {promisify} from 'util'; - -import {app, dialog, ipcMain as _ipcMain} from 'electron'; -import type {BrowserWindow, App, MenuItemConstructorOptions} from 'electron'; -import React from 'react'; - +import {writeFileSync} from 'fs'; import Config from 'electron-store'; import ms from 'ms'; +import React from 'react'; import ReactDom from 'react-dom'; - -import type {IpcMainWithCommands} from '../typings/common'; -import type {configOptions} from '../typings/config'; - import * as config from './config'; -import {plugs} from './config/paths'; import notify from './notify'; import {availableExtensions} from './plugins/extensions'; import {install} from './plugins/install'; +import {plugs} from './config/paths'; import mapKeys from './utils/map-keys'; +import {configOptions} from '../lib/config'; +import {promisify} from 'util'; +import {exec, execFile} from 'child_process'; // local storage const cache = new Config(); @@ -282,7 +276,7 @@ function requirePlugins(): any[] { const {plugins: plugins_, localPlugins} = paths; const load = (path_: string) => { - let mod: Record; + let mod: any; try { mod = require(path_); const exposed = mod && Object.keys(mod).some((key) => availableExtensions.has(key)); @@ -318,7 +312,7 @@ function requirePlugins(): any[] { ...localPlugins.filter((p) => basename(p) !== 'migrated-hyper3-config') ] .map(load) - .filter((v): v is Record => Boolean(v)); + .filter((v) => Boolean(v)); } export const onApp = (app_: App) => { @@ -421,7 +415,7 @@ export const getDeprecatedConfig = () => { return deprecated; }; -export const decorateMenu = (tpl: MenuItemConstructorOptions[]) => { +export const decorateMenu = (tpl: any) => { return decorateObject(tpl, 'decorateMenu'); }; @@ -429,8 +423,8 @@ export const getDecoratedEnv = (baseEnv: Record) => { return decorateObject(baseEnv, 'decorateEnv'); }; -export const getDecoratedConfig = (profile: string) => { - const baseConfig = config.getProfileConfig(profile); +export const getDecoratedConfig = () => { + const baseConfig = config.getConfig(); const decoratedConfig = decorateObject(baseConfig, 'decorateConfig'); const fixedConfig = config.fixConfigDefaults(decoratedConfig); const translatedConfig = config.htermConfigTranslate(fixedConfig); @@ -462,19 +456,12 @@ export const decorateSessionClass = (Session: T): T => { export {toDependencies as _toDependencies}; -const ipcMain = _ipcMain as IpcMainWithCommands; - -ipcMain.handle('child_process.exec', (event, command, options) => { +ipcMain.handle('child_process.exec', (event, args) => { + const {command, options} = args; return promisify(exec)(command, options); }); -ipcMain.handle('child_process.execFile', (event, file, args, options) => { +ipcMain.handle('child_process.execFile', (event, _args) => { + const {file, args, options} = _args; return promisify(execFile)(file, args, options); }); - -ipcMain.handle('getLoadedPluginVersions', () => getLoadedPluginVersions()); -ipcMain.handle('getPaths', () => getPaths()); -ipcMain.handle('getBasePaths', () => getBasePaths()); -ipcMain.handle('getDeprecatedConfig', () => getDeprecatedConfig()); -ipcMain.handle('getDecoratedConfig', (e, profile) => getDecoratedConfig(profile)); -ipcMain.handle('getDecoratedKeymaps', () => getDecoratedKeymaps()); diff --git a/app/plugins/install.ts b/app/plugins/install.ts index 35ed7245..61fec231 100644 --- a/app/plugins/install.ts +++ b/app/plugins/install.ts @@ -1,8 +1,6 @@ import cp from 'child_process'; - -import ms from 'ms'; import queue from 'queue'; - +import ms from 'ms'; import {yarn, plugs} from '../config/paths'; export const install = (fn: (err: string | null) => void) => { diff --git a/app/rpc.ts b/app/rpc.ts index aead6363..dd8661ed 100644 --- a/app/rpc.ts +++ b/app/rpc.ts @@ -1,22 +1,15 @@ import {EventEmitter} from 'events'; - -import {ipcMain} from 'electron'; -import type {BrowserWindow, IpcMainEvent} from 'electron'; - +import {ipcMain, BrowserWindow} from 'electron'; import {v4 as uuidv4} from 'uuid'; -import type {TypedEmitter, MainEvents, RendererEvents, FilterNever} from '../typings/common'; - -export class Server { - emitter: TypedEmitter; +export class Server extends EventEmitter { destroyed = false; win: BrowserWindow; id!: string; - constructor(win: BrowserWindow) { - this.emitter = new EventEmitter(); + super(); this.win = win; - this.emit = this.emit.bind(this); + this.ipcListener = this.ipcListener.bind(this); if (this.destroyed) { return; @@ -25,13 +18,14 @@ export class Server { const uid = uuidv4(); this.id = uid; + // eslint-disable-next-line @typescript-eslint/unbound-method ipcMain.on(uid, this.ipcListener); // we intentionally subscribe to `on` instead of `once` // to support reloading the window and re-initializing // the channel this.wc.on('did-finish-load', () => { - this.wc.send('init', uid, win.profileName); + this.wc.send('init', uid); }); } @@ -39,35 +33,23 @@ export class Server { return this.win.webContents; } - ipcListener = (event: IpcMainEvent, {ev, data}: {ev: U; data: MainEvents[U]}) => - this.emitter.emit(ev, data); + ipcListener(event: any, {ev, data}: {ev: string; data: any}) { + super.emit(ev, data); + } - on = (ev: U, fn: (arg0: MainEvents[U]) => void) => { - this.emitter.on(ev, fn); - return this; - }; - - once = (ev: U, fn: (arg0: MainEvents[U]) => void) => { - this.emitter.once(ev, fn); - return this; - }; - - emit>>(ch: U): boolean; - emit>(ch: U, data: RendererEvents[U]): boolean; - emit(ch: U, data?: RendererEvents[U]) { + emit(ch: string, data: any = {}): any { // This check is needed because data-batching can cause extra data to be // emitted after the window has already closed if (!this.win.isDestroyed()) { this.wc.send(this.id, {ch, data}); - return true; } - return false; } destroy() { - this.emitter.removeAllListeners(); + this.removeAllListeners(); this.wc.removeAllListeners(); if (this.id) { + // eslint-disable-next-line @typescript-eslint/unbound-method ipcMain.removeListener(this.id, this.ipcListener); } else { // mark for `genUid` in constructor @@ -76,8 +58,6 @@ export class Server { } } -const createRPC = (win: BrowserWindow) => { +export default (win: BrowserWindow) => { return new Server(win); }; - -export default createRPC; diff --git a/app/session.ts b/app/session.ts index 4b706b3d..5c0759f4 100644 --- a/app/session.ts +++ b/app/session.ts @@ -1,17 +1,14 @@ import {EventEmitter} from 'events'; -import {dirname} from 'path'; import {StringDecoder} from 'string_decoder'; - import defaultShell from 'default-shell'; -import type {IPty, IWindowsPtyForkOptions, spawn as npSpawn} from 'node-pty'; -import osLocale from 'os-locale'; -import shellEnv from 'shell-env'; - -import * as config from './config'; -import {cliScriptPath} from './config/paths'; -import {productName, version} from './package.json'; import {getDecoratedEnv} from './plugins'; -import {getFallBackShellConfig} from './utils/shell-fallback'; +import {productName, version} from './package.json'; +import * as config from './config'; +import {IPty, IWindowsPtyForkOptions, spawn as npSpawn} from 'node-pty'; +import {cliScriptPath} from './config/paths'; +import {dirname} from 'path'; +import shellEnv from 'shell-env'; +import osLocale from 'os-locale'; const createNodePtyError = () => new Error( @@ -26,6 +23,7 @@ try { throw createNodePtyError(); } +const envFromConfig = config.getConfig().env || {}; const useConpty = config.getConfig().useConpty; // Max duration to batch session data before sending it to the renderer process. @@ -58,7 +56,7 @@ class DataBatcher extends EventEmitter { this.timeout = null; } - write(chunk: Buffer | string) { + write(chunk: Buffer) { if (this.data.length + chunk.length >= BATCH_MAX_SIZE) { // We've reached the max batch size. Flush it and start another one if (this.timeout) { @@ -68,7 +66,7 @@ class DataBatcher extends EventEmitter { this.flush(); } - this.data += typeof chunk === 'string' ? chunk : this.decoder.write(chunk); + this.data += this.decoder.write(chunk); if (!this.timeout) { this.timeout = setTimeout(() => this.flush(), BATCH_DURATION_MS); @@ -86,12 +84,11 @@ class DataBatcher extends EventEmitter { interface SessionOptions { uid: string; - rows?: number; - cols?: number; - cwd?: string; - shell?: string; - shellArgs?: string[]; - profile: string; + rows: number; + cols: number; + cwd: string; + shell: string; + shellArgs: string[]; } export default class Session extends EventEmitter { pty: IPty | null; @@ -99,7 +96,6 @@ export default class Session extends EventEmitter { shell: string | null; ended: boolean; initTimestamp: number; - profile!: string; constructor(options: SessionOptions) { super(); this.pty = null; @@ -110,25 +106,22 @@ export default class Session extends EventEmitter { this.init(options); } - init({uid, rows, cols, cwd, shell: _shell, shellArgs: _shellArgs, profile}: SessionOptions) { - this.profile = profile; - const envFromConfig = config.getProfileConfig(profile).env || {}; - const defaultShellArgs = ['--login']; - - const shell = _shell || defaultShell; - const shellArgs = _shellArgs || defaultShellArgs; - + init({uid, rows, cols: columns, cwd, shell: _shell, shellArgs: _shellArgs}: SessionOptions) { const cleanEnv = process.env['APPIMAGE'] && process.env['APPDIR'] ? shellEnv.sync(_shell || defaultShell) : process.env; - const baseEnv: Record = { - ...cleanEnv, - LANG: `${osLocale.sync().replace(/-/, '_')}.UTF-8`, - TERM: 'xterm-256color', - COLORTERM: 'truecolor', - TERM_PROGRAM: productName, - TERM_PROGRAM_VERSION: version, - ...envFromConfig - }; + const baseEnv = Object.assign( + {}, + cleanEnv, + { + LANG: `${osLocale.sync().replace(/-/, '_')}.UTF-8`, + TERM: 'xterm-256color', + COLORTERM: 'truecolor', + TERM_PROGRAM: productName, + TERM_PROGRAM_VERSION: version + }, + envFromConfig + ); + // path to AppImage mount point is added to PATH environment variable automatically // which conflicts with the cli if (baseEnv['APPIMAGE'] && baseEnv['APPDIR']) { @@ -144,8 +137,10 @@ export default class Session extends EventEmitter { delete baseEnv.GOOGLE_API_KEY; } + const defaultShellArgs = ['--login']; + const options: IWindowsPtyForkOptions = { - cols, + cols: columns, rows, cwd, env: getDecoratedEnv(baseEnv) @@ -156,6 +151,9 @@ export default class Session extends EventEmitter { options.useConpty = useConpty; } + const shell = _shell || defaultShell; + const shellArgs = _shellArgs || defaultShellArgs; + try { this.pty = spawn(shell, shellArgs, options); } catch (_err) { @@ -172,7 +170,7 @@ export default class Session extends EventEmitter { if (this.ended) { return; } - this.batcher?.write(chunk); + this.batcher?.write(chunk as any); }); this.batcher.on('flush', (data: string) => { @@ -185,32 +183,15 @@ export default class Session extends EventEmitter { // this will inform users in case there are errors in the config instead of instant exit const runDuration = new Date().getTime() - this.initTimestamp; if (e.exitCode > 0 && runDuration < 1000) { - const fallBackShellConfig = getFallBackShellConfig(shell, shellArgs, defaultShell, defaultShellArgs); - if (fallBackShellConfig) { - const msg = ` + const defaultShellConfig = {shell: defaultShell, shellArgs: defaultShellArgs}; + const msg = ` shell exited in ${runDuration} ms with exit code ${e.exitCode} please check the shell config: ${JSON.stringify({shell, shellArgs}, undefined, 2)} -using fallback shell config: ${JSON.stringify(fallBackShellConfig, undefined, 2)} +fallback to default shell config: ${JSON.stringify(defaultShellConfig, undefined, 2)} `; - console.warn(msg); - this.batcher?.write(msg.replace(/\n/g, '\r\n')); - this.init({ - uid, - rows, - cols, - cwd, - shell: fallBackShellConfig.shell, - shellArgs: fallBackShellConfig.shellArgs, - profile - }); - } else { - const msg = ` -shell exited in ${runDuration} ms with exit code ${e.exitCode} -No fallback available, please check the shell config. -`; - console.warn(msg); - this.batcher?.write(msg.replace(/\n/g, '\r\n')); - } + console.warn(msg); + this.batcher?.write(msg.replace(/\n/g, '\r\n') as any); + this.init({uid, rows, cols: columns, cwd, ...defaultShellConfig}); } else { this.ended = true; this.emit('exit'); diff --git a/app/tsconfig.json b/app/tsconfig.json index 5c7450fd..d18cc80a 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -7,8 +7,6 @@ }, "include": [ "./**/*", - "./package.json", - "../typings/extend-electron.d.ts", - "../typings/ext-modules.d.ts" + "./package.json" ] } diff --git a/app/ui/contextmenu.ts b/app/ui/contextmenu.ts index 634ea882..f6819811 100644 --- a/app/ui/contextmenu.ts +++ b/app/ui/contextmenu.ts @@ -1,11 +1,8 @@ -import type {MenuItemConstructorOptions, BrowserWindow} from 'electron'; - -import {execCommand} from '../commands'; -import {getProfiles} from '../config'; import editMenu from '../menus/menus/edit'; import shellMenu from '../menus/menus/shell'; +import {execCommand} from '../commands'; import {getDecoratedKeymaps} from '../plugins'; - +import {MenuItemConstructorOptions, BrowserWindow} from 'electron'; const separator: MenuItemConstructorOptions = {type: 'separator'}; const getCommandKeys = (keymaps: Record): Record => @@ -23,20 +20,14 @@ const filterCutCopy = (selection: string, menuItem: MenuItemConstructorOptions) return menuItem; }; -const contextMenuTemplate = ( +export default ( createWindow: (fn?: (win: BrowserWindow) => void, options?: Record) => BrowserWindow, selection: string ) => { const commandKeys = getCommandKeys(getDecoratedKeymaps()); - const _shell = shellMenu( - commandKeys, - execCommand, - getProfiles().map((p) => p.name) - ).submenu as MenuItemConstructorOptions[]; + const _shell = shellMenu(commandKeys, execCommand).submenu as MenuItemConstructorOptions[]; const _edit = editMenu(commandKeys, execCommand).submenu.filter(filterCutCopy.bind(null, selection)); return _edit .concat(separator, _shell) .filter((menuItem) => !Object.prototype.hasOwnProperty.call(menuItem, 'enabled') || menuItem.enabled); }; - -export default contextMenuTemplate; diff --git a/app/ui/window.ts b/app/ui/window.ts index 87eba6d7..ee1a719e 100644 --- a/app/ui/window.ts +++ b/app/ui/window.ts @@ -1,36 +1,28 @@ -import {existsSync} from 'fs'; +import {app, BrowserWindow, shell, Menu, BrowserWindowConstructorOptions, Event} from 'electron'; import {isAbsolute, normalize, sep} from 'path'; import {URL, fileURLToPath} from 'url'; - -import {app, BrowserWindow, shell, Menu} from 'electron'; -import type {BrowserWindowConstructorOptions} from 'electron'; - -import {enable as remoteEnable} from '@electron/remote/main'; -import isDev from 'electron-is-dev'; -import {getWorkingDirectoryFromPID} from 'native-process-working-directory'; import {v4 as uuidv4} from 'uuid'; - -import type {sessionExtraOptions} from '../../typings/common'; -import type {configOptions} from '../../typings/config'; -import {execCommand} from '../commands'; -import {getDefaultProfile} from '../config'; -import {icon, homeDirectory} from '../config/paths'; -import fetchNotifications from '../notifications'; -import notify from '../notify'; -import {decorateSessionOptions, decorateSessionClass} from '../plugins'; -import createRPC from '../rpc'; -import Session from '../session'; +import isDev from 'electron-is-dev'; import updater from '../updater'; -import {setRendererType, unsetRendererType} from '../utils/renderer-utils'; import toElectronBackgroundColor from '../utils/to-electron-background-color'; - +import {icon, homeDirectory} from '../config/paths'; +import createRPC from '../rpc'; +import notify from '../notify'; +import fetchNotifications from '../notifications'; +import Session from '../session'; import contextMenuTemplate from './contextmenu'; +import {execCommand} from '../commands'; +import {setRendererType, unsetRendererType} from '../utils/renderer-utils'; +import {decorateSessionOptions, decorateSessionClass} from '../plugins'; +import {enable as remoteEnable} from '@electron/remote/main'; +import {configOptions} from '../../lib/config'; +import {getWorkingDirectoryFromPID} from 'native-process-working-directory'; +import {existsSync} from 'fs'; export function newWindow( options_: BrowserWindowConstructorOptions, cfg: configOptions, - fn?: (win: BrowserWindow) => void, - profileName: string = getDefaultProfile() + fn?: (win: BrowserWindow) => void ): BrowserWindow { const classOpts = Object.assign({uid: uuidv4()}); app.plugins.decorateWindowClass(classOpts); @@ -56,8 +48,6 @@ export function newWindow( }; const window = new BrowserWindow(app.plugins.getDecoratedBrowserOptions(winOpts)); - window.profileName = profileName; - // Enable remote module on this window remoteEnable(window.webContents); @@ -70,13 +60,28 @@ export function newWindow( const sessions = new Map(); const updateBackgroundColor = () => { - const cfg_ = app.plugins.getDecoratedConfig(profileName); + const cfg_ = app.plugins.getDecoratedConfig(); window.setBackgroundColor(toElectronBackgroundColor(cfg_.backgroundColor || '#000')); }; + // set working directory + let argPath = process.argv[1]; + if (argPath && process.platform === 'win32') { + if (/[a-zA-Z]:"/.test(argPath)) { + argPath = argPath.replace('"', sep); + } + argPath = normalize(argPath + sep); + } + let workingDirectory = homeDirectory; + if (argPath && isAbsolute(argPath)) { + workingDirectory = argPath; + } else if (cfg.workingDirectory && isAbsolute(cfg.workingDirectory)) { + workingDirectory = cfg.workingDirectory; + } + // config changes const cfgUnsubscribe = app.config.subscribe(() => { - const cfg_ = app.plugins.getDecoratedConfig(profileName); + const cfg_ = app.plugins.getDecoratedConfig(); // notify renderer window.webContents.send('config change'); @@ -119,58 +124,34 @@ export function newWindow( } }); - function createSession(extraOptions: sessionExtraOptions = {}) { + function createSession(extraOptions: any = {}) { const uid = uuidv4(); - const extraOptionsFiltered: sessionExtraOptions = {}; + const extraOptionsFiltered: any = {}; Object.keys(extraOptions).forEach((key) => { if (extraOptions[key] !== undefined) extraOptionsFiltered[key] = extraOptions[key]; }); - const profile = extraOptionsFiltered.profile || profileName; - const activeSession = extraOptionsFiltered.activeUid ? sessions.get(extraOptionsFiltered.activeUid) : undefined; let cwd = ''; - if (cfg.preserveCWD !== false && activeSession && activeSession.profile === profile) { - const activePID = activeSession.pty?.pid; - if (activePID !== undefined) { - try { - cwd = getWorkingDirectoryFromPID(activePID) || ''; - } catch (error) { - console.error(error); - } + if (cfg.preserveCWD === undefined || cfg.preserveCWD) { + const activePID = extraOptionsFiltered.activeUid && sessions.get(extraOptionsFiltered.activeUid)?.pty?.pid; + try { + cwd = activePID && getWorkingDirectoryFromPID(activePID); + } catch (error) { + console.error(error); } cwd = cwd && isAbsolute(cwd) && existsSync(cwd) ? cwd : ''; } - const profileCfg = app.plugins.getDecoratedConfig(profile); - - // set working directory - let argPath = process.argv[1]; - if (argPath && process.platform === 'win32') { - if (/[a-zA-Z]:"/.test(argPath)) { - argPath = argPath.replace('"', sep); - } - argPath = normalize(argPath + sep); - } - let workingDirectory = homeDirectory; - if (argPath && isAbsolute(argPath)) { - workingDirectory = argPath; - } else if (profileCfg.workingDirectory && isAbsolute(profileCfg.workingDirectory)) { - workingDirectory = profileCfg.workingDirectory; - } - // remove the rows and cols, the wrong value of them will break layout when init create const defaultOptions = Object.assign( { cwd: cwd || workingDirectory, splitDirection: undefined, - shell: profileCfg.shell, - shellArgs: profileCfg.shellArgs && Array.from(profileCfg.shellArgs) + shell: cfg.shell, + shellArgs: cfg.shellArgs && Array.from(cfg.shellArgs) }, extraOptionsFiltered, - { - profile: extraOptionsFiltered.profile || profileName, - uid - } + {uid} ); const options = decorateSessionOptions(defaultOptions); const DecoratedSession = decorateSessionClass(Session); @@ -190,8 +171,7 @@ export function newWindow( splitDirection: options.splitDirection, shell: session.shell, pid: session.pty ? session.pty.pid : null, - activeUid: options.activeUid ?? undefined, - profile: options.profile + activeUid: options.activeUid }); session.on('data', (data: string) => { @@ -226,8 +206,8 @@ export function newWindow( session.resize({cols, rows}); } }); - rpc.on('data', ({uid, data, escaped}) => { - const session = uid && sessions.get(uid); + rpc.on('data', ({uid, data, escaped}: {uid: string; data: string; escaped: boolean}) => { + const session = sessions.get(uid); if (session) { if (escaped) { const escapedData = session.shell?.endsWith('cmd.exe') @@ -257,12 +237,11 @@ export function newWindow( // Same deal as above, grabbing the window titlebar when the window // is maximized on Windows results in unmaximize, without hitting any // app buttons - const onGeometryChange = () => rpc.emit('windowGeometry change', {isMaximized: window.isMaximized()}); - window.on('maximize', onGeometryChange); - window.on('unmaximize', onGeometryChange); - window.on('minimize', onGeometryChange); - window.on('restore', onGeometryChange); - + for (const ev of ['maximize', 'unmaximize', 'minimize', 'restore'] as any) { + window.on(ev, () => { + rpc.emit('windowGeometry change', {isMaximized: window.isMaximized()}); + }); + } window.on('move', () => { const position = window.getPosition(); rpc.emit('move', {bounds: {x: position[0], y: position[1]}}); @@ -276,10 +255,10 @@ export function newWindow( }); // pass on the full screen events from the window to react rpc.win.on('enter-full-screen', () => { - rpc.emit('enter full screen'); + rpc.emit('enter full screen', {}); }); rpc.win.on('leave-full-screen', () => { - rpc.emit('leave full screen'); + rpc.emit('leave full screen', {}); }); const deleteSessions = () => { sessions.forEach((session, key) => { @@ -297,33 +276,22 @@ export function newWindow( } }); - const handleDroppedURL = (url: string) => { + const handleDrop = (event: Event, url: string) => { const protocol = typeof url === 'string' && new URL(url).protocol; if (protocol === 'file:') { + event.preventDefault(); const path = fileURLToPath(url); - return {uid: null, data: path, escaped: true}; + rpc.emit('session data send', {data: path, escaped: true}); } else if (protocol === 'http:' || protocol === 'https:') { - return {uid: null, data: url}; + event.preventDefault(); + rpc.emit('session data send', {data: url}); } }; // If file is dropped onto the terminal window, navigate and new-window events are prevented - // and it's path is added to active session. - window.webContents.on('will-navigate', (event, url) => { - const data = handleDroppedURL(url); - if (data) { - event.preventDefault(); - rpc.emit('session data send', data); - } - }); - window.webContents.setWindowOpenHandler(({url}) => { - const data = handleDroppedURL(url); - if (data) { - rpc.emit('session data send', data); - return {action: 'deny'}; - } - return {action: 'allow'}; - }); + // and his path is added to active session. + window.webContents.on('will-navigate', handleDrop); + window.webContents.on('new-window', handleDrop); // expose internals to extension authors window.rpc = rpc; diff --git a/app/updater.ts b/app/updater.ts index c7d07d4d..39ba2870 100644 --- a/app/updater.ts +++ b/app/updater.ts @@ -1,15 +1,12 @@ // Packages -import electron, {app} from 'electron'; -import type {BrowserWindow, AutoUpdater} from 'electron'; - -import retry from 'async-retry'; +import electron, {app, BrowserWindow, AutoUpdater} from 'electron'; import ms from 'ms'; +import retry from 'async-retry'; // Utilities -import autoUpdaterLinux from './auto-updater-linux'; -import {getDefaultProfile} from './config'; import {version} from './package.json'; import {getDecoratedConfig} from './plugins'; +import autoUpdaterLinux from './auto-updater-linux'; const {platform} = process; const isLinux = platform === 'linux'; @@ -18,7 +15,7 @@ const autoUpdater: AutoUpdater = isLinux ? autoUpdaterLinux : electron.autoUpdat const getDecoratedConfigWithRetry = async () => { return await retry(() => { - const content = getDecoratedConfig(getDefaultProfile()); + const content = getDecoratedConfig(); if (!content) { throw new Error('No config content loaded'); } @@ -72,23 +69,28 @@ async function init() { isInit = true; } -const updater = (win: BrowserWindow) => { +export default (win: BrowserWindow) => { if (!isInit) { void init(); } const {rpc} = win; - const onupdate = (ev: Event, releaseNotes: string, releaseName: string, date: Date, updateUrl: string) => { + const onupdate = ( + ev: Event, + releaseNotes: string, + releaseName: string, + date: Date, + updateUrl: string, + onQuitAndInstall: any + ) => { const releaseUrl = updateUrl || `https://github.com/vercel/hyper/releases/tag/${releaseName}`; - rpc.emit('update available', {releaseNotes, releaseName, releaseUrl, canInstall: !isLinux}); + rpc.emit('update available', {releaseNotes, releaseName, releaseUrl, canInstall: !!onQuitAndInstall}); }; - if (isLinux) { - autoUpdater.on('update-available', onupdate); - } else { - autoUpdater.on('update-downloaded', onupdate); - } + const eventName: any = isLinux ? 'update-available' : 'update-downloaded'; + + autoUpdater.on(eventName, onupdate); rpc.once('quit and install', () => { autoUpdater.quitAndInstall(); @@ -109,12 +111,6 @@ const updater = (win: BrowserWindow) => { }); win.on('close', () => { - if (isLinux) { - autoUpdater.removeListener('update-available', onupdate); - } else { - autoUpdater.removeListener('update-downloaded', onupdate); - } + autoUpdater.removeListener(eventName, onupdate); }); }; - -export default updater; diff --git a/app/utils/cli-install.ts b/app/utils/cli-install.ts index 3a5b9930..02cc7894 100644 --- a/app/utils/cli-install.ts +++ b/app/utils/cli-install.ts @@ -1,23 +1,20 @@ -import {existsSync, readlink, symlink} from 'fs'; +import pify from 'pify'; +import fs from 'fs'; import path from 'path'; -import {promisify} from 'util'; - -import {clipboard, dialog} from 'electron'; - -import {mkdirpSync} from 'fs-extra'; +import notify from '../notify'; +import {cliScriptPath, cliLinkPath} from '../config/paths'; import * as Registry from 'native-reg'; import type {ValueType} from 'native-reg'; import sudoPrompt from 'sudo-prompt'; +import {clipboard, dialog} from 'electron'; +import {mkdirpSync} from 'fs-extra'; -import {cliScriptPath, cliLinkPath} from '../config/paths'; -import notify from '../notify'; - -const readLink = promisify(readlink); -const symLink = promisify(symlink); -const sudoExec = promisify(sudoPrompt.exec); +const readlink = pify(fs.readlink); +const symlink = pify(fs.symlink); +const sudoExec = pify(sudoPrompt.exec, {multiArgs: true}); const checkInstall = () => { - return readLink(cliLinkPath) + return readlink(cliLinkPath) .then((link) => link === cliScriptPath) .catch((err) => { if (err.code === 'ENOENT') { @@ -35,14 +32,14 @@ const addSymlink = async (silent: boolean) => { return; } console.log('Linking HyperCLI'); - if (!existsSync(path.dirname(cliLinkPath))) { + if (!fs.existsSync(path.dirname(cliLinkPath))) { try { mkdirpSync(path.dirname(cliLinkPath)); } catch (err) { throw `Failed to create directory ${path.dirname(cliLinkPath)} - ${err}`; } } - await symLink(cliScriptPath, cliLinkPath); + await symlink(cliScriptPath, cliLinkPath); } catch (_err) { const err = _err as {code: string}; // 'EINVAL' is returned by readlink, diff --git a/app/utils/map-keys.ts b/app/utils/map-keys.ts index 4bcb9a1d..e9c79423 100644 --- a/app/utils/map-keys.ts +++ b/app/utils/map-keys.ts @@ -11,7 +11,7 @@ const generatePrefixedCommand = (command: string, shortcuts: string[]) => { return result; }; -const mapKeys = (config: Record) => { +export default (config: Record) => { return Object.keys(config).reduce((keymap: Record, command: string) => { if (!command) { return keymap; @@ -39,5 +39,3 @@ const mapKeys = (config: Record) => { return keymap; }, {}); }; - -export default mapKeys; diff --git a/app/utils/shell-fallback.ts b/app/utils/shell-fallback.ts deleted file mode 100644 index 0eb7df4b..00000000 --- a/app/utils/shell-fallback.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const getFallBackShellConfig = ( - shell: string, - shellArgs: string[], - defaultShell: string, - defaultShellArgs: string[] -): { - shell: string; - shellArgs: string[]; -} | null => { - if (shellArgs.length > 0) { - return { - shell, - shellArgs: [] - }; - } - - if (shell != defaultShell) { - return { - shell: defaultShell, - shellArgs: defaultShellArgs - }; - } - - return null; -}; diff --git a/app/utils/system-context-menu.ts b/app/utils/system-context-menu.ts index 80522d0c..e0c2b94e 100644 --- a/app/utils/system-context-menu.ts +++ b/app/utils/system-context-menu.ts @@ -9,7 +9,7 @@ const regKeys = [ ]; const regParts = [ {key: 'command', name: '', value: `${appPath} "%V"`}, - {name: '', value: 'Open &Hyper here'}, + {name: '', value: 'Open Hyper here'}, {name: 'Icon', value: `${appPath}`} ]; diff --git a/app/utils/to-electron-background-color.ts b/app/utils/to-electron-background-color.ts index a758f528..fc4a9799 100644 --- a/app/utils/to-electron-background-color.ts +++ b/app/utils/to-electron-background-color.ts @@ -4,7 +4,7 @@ import Color from 'color'; // returns a background color that's in hex // format including the alpha channel (e.g.: `#00000050`) // input can be any css value (rgb, hsl, stringâ€Ļ) -const toElectronBackgroundColor = (bgColor: string) => { +export default (bgColor: string) => { const color = Color(bgColor); if (color.alpha() === 1) { @@ -15,5 +15,3 @@ const toElectronBackgroundColor = (bgColor: string) => { const alphaHex = Math.round(color.alpha() * 255).toString(16); return `#${alphaHex}${color.hex().toString().slice(1)}`; }; - -export default toElectronBackgroundColor; diff --git a/app/yarn.lock b/app/yarn.lock index 8e654f1d..00ff05c9 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -2,15 +2,15 @@ # yarn lockfile v1 -"@babel/parser@7.24.4": - version "7.24.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" - integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== +"@babel/parser@7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" + integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== -"@electron/remote@2.1.2": - version "2.1.2" - resolved "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz#52a97c8faa5b769155b649ef262f2f8c851776e6" - integrity sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA== +"@electron/remote@2.0.9": + version "2.0.9" + resolved "https://registry.npmjs.org/@electron/remote/-/remote-2.0.9.tgz#092ff085407bc907f45b89a72c36faa773ccf2d9" + integrity sha512-LR0W0ID6WAKHaSs0x5LX9aiG+5pFBNAJL6eQAJfGkCuZPUa6nZz+czZLdlTDETG45CgF/0raSvCtYOYUpr6c+A== "@types/semver@^7.3.8": version "7.3.8" @@ -47,10 +47,20 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -ast-types@^0.16.1: - version "0.16.1" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz#7a9da1617c9081bc121faafe91711b4c8bb81da2" - integrity sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg== +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" + +ast-types@0.15.2: + version "0.15.2" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" + integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== dependencies: tslib "^2.0.1" @@ -66,24 +76,16 @@ atomically@^1.7.0: resolved "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe" integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w== -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== binary-extensions@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - braces@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -91,10 +93,18 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -chokidar@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -134,11 +144,6 @@ color@4.2.3: color-convert "^2.0.1" color-string "^1.9.0" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - conf@^10.2.0: version "10.2.0" resolved "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz#838e757be963f1a2386dfe048a98f8f69f7b55d6" @@ -155,11 +160,6 @@ conf@^10.2.0: pkg-up "^3.1.0" semver "^7.3.5" -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -192,6 +192,14 @@ default-shell@1.0.1, default-shell@^1.0.1: resolved "https://registry.npmjs.org/default-shell/-/default-shell-1.0.1.tgz#752304bddc6174f49eb29cb988feea0b8813c8bc" integrity sha1-dSMEvdxhdPSespy5iP7qC4gTyLw= +define-properties@^1.1.3: + version "1.1.4" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + dot-prop@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" @@ -199,16 +207,6 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" -electron-devtools-installer@3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz#acc48d24eb7033fe5af284a19667e73b78d406d0" - integrity sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ== - dependencies: - rimraf "^3.0.2" - semver "^7.2.1" - tslib "^2.1.0" - unzip-crx-3 "^0.2.0" - electron-fetch@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.9.1.tgz#e28bfe78d467de3f2dec884b1d72b8b05322f30f" @@ -221,10 +219,10 @@ electron-is-dev@2.0.0: resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz#833487a069b8dad21425c67a19847d9064ab19bd" integrity sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA== -electron-store@8.2.0: - version "8.2.0" - resolved "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz#114e6e453e8bb746ab4ccb542424d8c881ad2ca1" - integrity sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw== +electron-store@8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/electron-store/-/electron-store-8.1.0.tgz#46a398f2bd9aa83c4a9daaae28380e2b3b9c7597" + integrity sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA== dependencies: conf "^10.2.0" type-fest "^2.17.0" @@ -248,6 +246,11 @@ env-paths@^2.2.1: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== + esprima@~4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -300,25 +303,41 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -fs-extra@11.2.0: - version "11.2.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" - integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +fs-extra@11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -350,23 +369,44 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" + get-intrinsic "^1.1.3" graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.4" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -379,20 +419,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@~2.0.3: +inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -402,6 +429,14 @@ invert-kv@^3.0.0: resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" integrity sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw== +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" @@ -414,11 +449,23 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -426,6 +473,14 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -446,10 +501,16 @@ is-stream@^2.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +is-typed-array@^1.1.10, is-typed-array@^1.1.3: + version "1.1.10" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" isexe@^2.0.0: version "2.0.0" @@ -480,16 +541,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jszip@^3.1.0: - version "3.10.1" - resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" - integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== - dependencies: - lie "~3.3.0" - pako "~1.0.2" - readable-stream "~2.3.6" - setimmediate "^1.0.5" - lcid@^3.0.0: version "3.1.1" resolved "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0" @@ -497,13 +548,6 @@ lcid@^3.0.0: dependencies: invert-kv "^3.0.0" -lie@~3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" - integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== - dependencies: - immediate "~3.0.5" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -562,24 +606,10 @@ mimic-fn@^3.0.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== -minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mkdirp@^0.5.1: - version "0.5.6" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" +mkdirp@1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== ms@2.1.3: version "2.1.3" @@ -620,10 +650,10 @@ node-gyp-build@4: resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-pty@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd" - integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA== +node-pty@0.11.0-beta27: + version "0.11.0-beta27" + resolved "https://registry.npmjs.org/node-pty/-/node-pty-0.11.0-beta27.tgz#13ebfb687aff23af3b38991e676ac0fb92accb82" + integrity sha512-HkP1lHsgOgXroEMuFt/2hNkyI+roSSKIMKkyaW3g/NVNY3j5gpYD2YM05EbYlJbjaUFAWqbJzoLIVs2g+A+B8A== dependencies: nan "^2.17.0" @@ -646,10 +676,28 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -once@^1.3.0, once@^1.3.1, once@^1.4.0: +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" @@ -703,11 +751,6 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pako@~1.0.2: - version "1.0.11" - resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - parse-path@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" @@ -727,11 +770,6 @@ path-exists@^3.0.0: resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -747,6 +785,11 @@ picomatch@^2.0.4, picomatch@^2.2.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +pify@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -754,11 +797,6 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - protocols@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -784,33 +822,22 @@ queue@6.0.2: dependencies: inherits "~2.0.3" -react-dom@18.2.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== +react-dom@17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== dependencies: loose-envify "^1.1.0" - scheduler "^0.23.0" + object-assign "^4.1.1" + scheduler "^0.20.2" -react@18.2.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== +react@17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== dependencies: loose-envify "^1.1.0" - -readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + object-assign "^4.1.1" readdirp@~3.6.0: version "3.6.0" @@ -819,15 +846,15 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -recast@0.23.6: - version "0.23.6" - resolved "https://registry.npmjs.org/recast/-/recast-0.23.6.tgz#198fba74f66143a30acc81929302d214ce4e3bfa" - integrity sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ== +recast@0.22.0: + version "0.22.0" + resolved "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz#1dd3bf1b86e5eb810b044221a1a734234ed3e9c0" + integrity sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ== dependencies: - ast-types "^0.16.1" + assert "^2.0.0" + ast-types "0.15.2" esprima "~4.0.0" source-map "~0.6.1" - tiny-invariant "^1.3.3" tslib "^2.0.1" require-from-string@^2.0.2: @@ -840,34 +867,23 @@ retry@0.13.1: resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" + object-assign "^4.1.1" -semver@7.6.0, semver@^7.2.1, semver@^7.3.5: - version "7.6.0" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== +semver@7.3.8, semver@^7.3.5: + version "7.3.8" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -876,11 +892,6 @@ semver@^5.5.0, semver@^5.6.0: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -936,13 +947,6 @@ source-map@~0.6.1: resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -965,11 +969,6 @@ sudo-prompt@^9.2.1: resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== -tiny-invariant@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" - integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -977,10 +976,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tslib@^2.0.1, tslib@^2.1.0: - version "2.6.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" - integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== +tslib@^2.0.1: + version "2.4.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== type-fest@^2.17.0: version "2.18.0" @@ -992,15 +991,6 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unzip-crx-3@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz#d5324147b104a8aed9ae8639c95521f6f7cda292" - integrity sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ== - dependencies: - jszip "^3.1.0" - mkdirp "^0.5.1" - yaku "^0.16.6" - uri-js@^4.2.2: version "4.4.0" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" @@ -1008,15 +998,33 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util@^0.12.0: + version "0.12.5" + resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" -uuid@9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uuid@9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +which-typed-array@^1.1.2: + version "1.1.9" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" which@^1.2.9: version "1.3.1" @@ -1037,11 +1045,6 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -yaku@^0.16.6: - version "0.16.7" - resolved "https://registry.npmjs.org/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e" - integrity sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" diff --git a/bin/notarize.js b/bin/notarize.js index 8cce22a3..f13093e5 100644 --- a/bin/notarize.js +++ b/bin/notarize.js @@ -1,4 +1,4 @@ -const { notarize } = require("@electron/notarize"); +const { notarize } = require("electron-notarize"); exports.default = async function notarizing(context) { const { electronPlatformName, appOutDir } = context; diff --git a/bin/snapshot-libs.js b/bin/snapshot-libs.js index 791e8ec8..40dd79b6 100644 --- a/bin/snapshot-libs.js +++ b/bin/snapshot-libs.js @@ -7,6 +7,10 @@ require('normalize-url'); require('parse-url'); require('php-escape-shell'); require('plist'); +require('react-deep-force-update'); +require('react-dom'); +require('react-redux'); +require('react'); require('redux-thunk'); require('redux'); require('reselect'); @@ -18,14 +22,10 @@ if (false) { require('args'); require('mousetrap'); require('open'); - require('react-dom'); - require('react-redux'); - require('react'); require('xterm-addon-fit'); - require('xterm-addon-image'); + require('xterm-addon-ligatures'); require('xterm-addon-search'); require('xterm-addon-web-links'); require('xterm-addon-webgl'); - require('xterm-addon-canvas'); require('xterm'); } diff --git a/build/win/installer.nsh b/build/win/installer.nsh index 2a0b0a67..48d68816 100644 --- a/build/win/installer.nsh +++ b/build/win/installer.nsh @@ -1,15 +1,15 @@ !macro customInstall - WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper" "" "Open &Hyper here" - WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper" "Icon" `"$appExe"` - WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper\command" "" `"$appExe" "%V"` + WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper" "" "Open Hyper here" + WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper" "Icon" "$appExe" + WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper\command" "" `$appExe "%V"` - WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper" "" "Open &Hyper here" - WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper" "Icon" `"$appExe"` - WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper\command" "" `"$appExe" "%V"` + WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper" "" "Open Hyper here" + WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper" "Icon" "$appExe" + WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper\command" "" `$appExe "%V"` - WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper" "" "Open &Hyper here" - WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper" "Icon" `"$appExe"` - WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper\command" "" `"$appExe" "%V"` + WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper" "" "Open Hyper here" + WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper" "Icon" "$appExe" + WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper\command" "" `$appExe "%V"` !macroend !macro customUnInstall @@ -25,4 +25,4 @@ !macro customInit IfFileExists $LOCALAPPDATA\Hyper\Update.exe 0 +2 nsExec::Exec '"$LOCALAPPDATA\Hyper\Update.exe" --uninstall -s' -!macroend +!macroend \ No newline at end of file diff --git a/cli/api.ts b/cli/api.ts index 6c2a4347..cac531a3 100644 --- a/cli/api.ts +++ b/cli/api.ts @@ -2,20 +2,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ import fs from 'fs'; import os from 'os'; -import path from 'path'; - import got from 'got'; import registryUrlModule from 'registry-url'; - const registryUrl = registryUrlModule(); +import path from 'path'; // If the user defines XDG_CONFIG_HOME they definitely want their config there, // otherwise use the home directory in linux/mac and userdata in windows const applicationDirectory = process.env.XDG_CONFIG_HOME ? path.join(process.env.XDG_CONFIG_HOME, 'Hyper') : process.platform === 'win32' - ? path.join(process.env.APPDATA!, 'Hyper') - : path.join(os.homedir(), '.config', 'Hyper'); + ? path.join(process.env.APPDATA!, 'Hyper') + : path.join(os.homedir(), '.config', 'Hyper'); const devConfigFileName = path.join(__dirname, `../hyper.json`); @@ -32,7 +30,7 @@ const fileName = function memoize any>(fn: T): T { let hasResult = false; let result: any; - return ((...args: Parameters) => { + return ((...args: any[]) => { if (!hasResult) { result = fn(...args); hasResult = true; diff --git a/cli/index.ts b/cli/index.ts index 86dbc414..fe6c4637 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -1,20 +1,16 @@ // This is a CLI tool, using console is OK /* eslint no-console: 0 */ -import {spawn, exec} from 'child_process'; -import type {SpawnOptions} from 'child_process'; -import {existsSync} from 'fs'; +import {spawn, exec, SpawnOptions} from 'child_process'; import {isAbsolute, resolve} from 'path'; -import {promisify} from 'util'; - +import {existsSync} from 'fs'; +import {version} from '../app/package.json'; +import pify from 'pify'; import args from 'args'; import chalk from 'chalk'; +import open from 'open'; import _columnify from 'columnify'; import got from 'got'; -import open from 'open'; import ora from 'ora'; - -import {version} from '../app/package.json'; - import * as api from './api'; let commandPromise: Promise | undefined; @@ -195,10 +191,8 @@ const main = (argv: string[]) => { version: false, mri: { boolean: ['v', 'verbose'] - }, - mainColor: 'yellow', - subColor: 'dim' - }); + } + } as any); if (commandPromise) { return commandPromise; @@ -238,7 +232,7 @@ const main = (argv: string[]) => { const opts = { env }; - return promisify(exec)(cmd, opts); + return pify(exec)(cmd, opts); } } diff --git a/electron-builder-linux-ci.json b/electron-builder-linux-ci.json deleted file mode 100644 index a29fc86a..00000000 --- a/electron-builder-linux-ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/electron-builder", - "extends": "electron-builder.json", - "afterSign": null, - "npmRebuild": false -} diff --git a/electron-builder.json b/electron-builder.json index e5f0d691..a2ad003a 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -17,25 +17,42 @@ ] } ], - "artifactName": "${productName}-${version}-${arch}.${ext}", "linux": { "category": "TerminalEmulator", "target": [ - "deb", - "AppImage", - "rpm", - "snap", - "pacman" + { + "target": "deb", + "arch": [ + "x64", + "arm64" + ] + }, + { + "target": "AppImage", + "arch": [ + "x64", + "arm64" + ] + }, + { + "target": "rpm", + "arch": [ + "x64", + "arm64" + ] + }, + { + "target": "snap", + "arch": [ + "x64" + ] + } ] }, "win": { - "target": { - "target": "nsis", - "arch": [ - "x64", - "arm64" - ] - }, + "target": [ + "nsis" + ], "rfc3161TimeStampServer": "http://timestamp.comodoca.com" }, "nsis": { diff --git a/lib/actions/config.ts b/lib/actions/config.ts index 7d1110cd..4c68302e 100644 --- a/lib/actions/config.ts +++ b/lib/actions/config.ts @@ -1,6 +1,6 @@ -import type {configOptions} from '../../typings/config'; -import {CONFIG_LOAD, CONFIG_RELOAD} from '../../typings/constants/config'; -import type {HyperActions} from '../../typings/hyper'; +import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config'; +import {HyperActions} from '../hyper'; +import {configOptions} from '../config'; export function loadConfig(config: configOptions): HyperActions { return { diff --git a/lib/actions/header.ts b/lib/actions/header.ts index 771efa03..9cc52cef 100644 --- a/lib/actions/header.ts +++ b/lib/actions/header.ts @@ -1,15 +1,14 @@ -import {CLOSE_TAB, CHANGE_TAB} from '../../typings/constants/tabs'; +import {CLOSE_TAB, CHANGE_TAB} from '../constants/tabs'; import { UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE, UI_OPEN_HAMBURGER_MENU, UI_WINDOW_MINIMIZE, UI_WINDOW_CLOSE -} from '../../typings/constants/ui'; -import type {HyperDispatch} from '../../typings/hyper'; +} from '../constants/ui'; import rpc from '../rpc'; - import {userExitTermGroup, setActiveGroup} from './term-groups'; +import {HyperDispatch} from '../hyper'; export function closeTab(uid: string) { return (dispatch: HyperDispatch) => { @@ -40,7 +39,7 @@ export function maximize() { dispatch({ type: UI_WINDOW_MAXIMIZE, effect() { - rpc.emit('maximize'); + rpc.emit('maximize', null); } }); }; @@ -51,7 +50,7 @@ export function unmaximize() { dispatch({ type: UI_WINDOW_UNMAXIMIZE, effect() { - rpc.emit('unmaximize'); + rpc.emit('unmaximize', null); } }); }; @@ -73,7 +72,7 @@ export function minimize() { dispatch({ type: UI_WINDOW_MINIMIZE, effect() { - rpc.emit('minimize'); + rpc.emit('minimize', null); } }); }; @@ -84,7 +83,7 @@ export function close() { dispatch({ type: UI_WINDOW_CLOSE, effect() { - rpc.emit('close'); + rpc.emit('close', null); } }); }; diff --git a/lib/actions/index.ts b/lib/actions/index.ts index 192dd18b..68b89fbf 100644 --- a/lib/actions/index.ts +++ b/lib/actions/index.ts @@ -1,6 +1,6 @@ -import {INIT} from '../../typings/constants'; -import type {HyperDispatch} from '../../typings/hyper'; import rpc from '../rpc'; +import {INIT} from '../constants'; +import {HyperDispatch} from '../hyper'; export default function init() { return (dispatch: HyperDispatch) => { diff --git a/lib/actions/notifications.ts b/lib/actions/notifications.ts index 8433671f..71a1b9cc 100644 --- a/lib/actions/notifications.ts +++ b/lib/actions/notifications.ts @@ -1,5 +1,5 @@ -import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../../typings/constants/notifications'; -import type {HyperActions} from '../../typings/hyper'; +import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications'; +import {HyperActions} from '../hyper'; export function dismissNotification(id: string): HyperActions { return { diff --git a/lib/actions/sessions.ts b/lib/actions/sessions.ts index 4cb53f9d..92b07eb1 100644 --- a/lib/actions/sessions.ts +++ b/lib/actions/sessions.ts @@ -1,4 +1,6 @@ -import type {Session} from '../../typings/common'; +import rpc from '../rpc'; +import {keys} from '../utils/object'; +import findBySession from '../utils/term-groups'; import { SESSION_ADD, SESSION_RESIZE, @@ -12,13 +14,10 @@ import { SESSION_USER_DATA, SESSION_SET_XTERM_TITLE, SESSION_SEARCH -} from '../../typings/constants/sessions'; -import type {HyperState, HyperDispatch, HyperActions} from '../../typings/hyper'; -import rpc from '../rpc'; -import {keys} from '../utils/object'; -import findBySession from '../utils/term-groups'; +} from '../constants/sessions'; +import {HyperState, session, HyperDispatch, HyperActions} from '../hyper'; -export function addSession({uid, shell, pid, cols = null, rows = null, splitDirection, activeUid, profile}: Session) { +export function addSession({uid, shell, pid, cols, rows, splitDirection, activeUid}: session) { return (dispatch: HyperDispatch, getState: () => HyperState) => { const {sessions} = getState(); const now = Date.now(); @@ -31,20 +30,20 @@ export function addSession({uid, shell, pid, cols = null, rows = null, splitDire rows, splitDirection, activeUid: activeUid ? activeUid : sessions.activeUid, - now, - profile + now }); }; } -export function requestSession(profile: string | undefined) { +export function requestSession() { return (dispatch: HyperDispatch, getState: () => HyperState) => { dispatch({ type: SESSION_REQUEST, effect: () => { const {ui} = getState(); - const {cwd} = ui; - rpc.emit('new', {cwd, profile}); + // the cols and rows from preview session maybe not accurate. so remove. + const {/*cols, rows,*/ cwd} = ui; + rpc.emit('new', {cwd}); } }); }; @@ -163,7 +162,7 @@ export function closeSearch(uid?: string, keyEvent?: any) { }; } -export function sendSessionData(uid: string | null, data: string, escaped?: boolean) { +export function sendSessionData(uid: string | null, data: any, escaped?: boolean | null) { return (dispatch: HyperDispatch, getState: () => HyperState) => { dispatch({ type: SESSION_USER_DATA, diff --git a/lib/actions/term-groups.ts b/lib/actions/term-groups.ts index 6beeedd9..e8b1ca67 100644 --- a/lib/actions/term-groups.ts +++ b/lib/actions/term-groups.ts @@ -1,32 +1,28 @@ -import {SESSION_REQUEST} from '../../typings/constants/sessions'; +import rpc from '../rpc'; import { DIRECTION, TERM_GROUP_RESIZE, TERM_GROUP_REQUEST, TERM_GROUP_EXIT, TERM_GROUP_EXIT_ACTIVE -} from '../../typings/constants/term-groups'; -import type {ITermState, ITermGroup, HyperState, HyperDispatch, HyperActions} from '../../typings/hyper'; -import rpc from '../rpc'; -import {getRootGroups} from '../selectors'; +} from '../constants/term-groups'; +import {SESSION_REQUEST} from '../constants/sessions'; import findBySession from '../utils/term-groups'; - +import {getRootGroups} from '../selectors'; import {setActiveSession, ptyExitSession, userExitSession} from './sessions'; +import {ITermState, ITermGroup, HyperState, HyperDispatch, HyperActions} from '../hyper'; function requestSplit(direction: 'VERTICAL' | 'HORIZONTAL') { - return (_activeUid: string | undefined, _profile: string | undefined) => + return (activeUid: string) => (dispatch: HyperDispatch, getState: () => HyperState): void => { dispatch({ type: SESSION_REQUEST, effect: () => { const {ui, sessions} = getState(); - const activeUid = _activeUid ? _activeUid : sessions.activeUid; - const profile = _profile ? _profile : activeUid ? sessions.sessions[activeUid].profile : window.profileName; rpc.emit('new', { splitDirection: direction, cwd: ui.cwd, - activeUid, - profile + activeUid: activeUid ? activeUid : sessions.activeUid }); } }); @@ -44,20 +40,17 @@ export function resizeTermGroup(uid: string, sizes: number[]): HyperActions { }; } -export function requestTermGroup(_activeUid: string | undefined, _profile: string | undefined) { +export function requestTermGroup(activeUid: string) { return (dispatch: HyperDispatch, getState: () => HyperState) => { dispatch({ type: TERM_GROUP_REQUEST, effect: () => { const {ui, sessions} = getState(); const {cwd} = ui; - const activeUid = _activeUid ? _activeUid : sessions.activeUid; - const profile = _profile ? _profile : activeUid ? sessions.sessions[activeUid].profile : window.profileName; rpc.emit('new', { isNewGroup: true, cwd, - activeUid, - profile + activeUid: activeUid ? activeUid : sessions.activeUid }); } }); diff --git a/lib/actions/ui.ts b/lib/actions/ui.ts index 92da4cee..a2fa6d75 100644 --- a/lib/actions/ui.ts +++ b/lib/actions/ui.ts @@ -1,9 +1,10 @@ -import {stat} from 'fs'; -import type {Stats} from 'fs'; - -import type parseUrl from 'parse-url'; import {php_escapeshellcmd as escapeShellCmd} from 'php-escape-shell'; - +import {isExecutable} from '../utils/file'; +import {getRootGroups} from '../selectors'; +import findBySession from '../utils/term-groups'; +import notify from '../utils/notify'; +import rpc from '../rpc'; +import {requestSession, sendSessionData, setActiveSession} from './sessions'; import { UI_FONT_SIZE_SET, UI_FONT_SIZE_INCR, @@ -23,18 +24,14 @@ import { UI_OPEN_SSH_URL, UI_CONTEXTMENU_OPEN, UI_COMMAND_EXEC -} from '../../typings/constants/ui'; -import type {HyperState, HyperDispatch, HyperActions, ITermGroups} from '../../typings/hyper'; -import rpc from '../rpc'; -import {getRootGroups} from '../selectors'; -import {isExecutable} from '../utils/file'; -import notify from '../utils/notify'; -import findBySession from '../utils/term-groups'; +} from '../constants/ui'; -import {requestSession, sendSessionData, setActiveSession} from './sessions'; import {setActiveGroup} from './term-groups'; +import parseUrl from 'parse-url'; +import {HyperState, HyperDispatch, HyperActions, ITermGroups} from '../hyper'; +import {stat, Stats} from 'fs'; -export function openContextMenu(uid: string, selection: string) { +export function openContextMenu(uid: string, selection: any) { return (dispatch: HyperDispatch, getState: () => HyperState) => { dispatch({ type: UI_CONTEXTMENU_OPEN, @@ -272,11 +269,11 @@ export function openFile(path: string) { } rpc.once('session add', ({uid}) => { rpc.once('session data', () => { - dispatch(sendSessionData(uid, command)); + dispatch(sendSessionData(uid, command, null)); }); }); } - dispatch(requestSession(undefined)); + dispatch(requestSession()); }); } }); @@ -295,11 +292,12 @@ export function leaveFullScreen(): HyperActions { }; } -export function openSSH(parsedUrl: ReturnType) { +export function openSSH(url: string) { return (dispatch: HyperDispatch) => { dispatch({ type: UI_OPEN_SSH_URL, effect() { + const parsedUrl = parseUrl(url, true); let command = `${parsedUrl.protocol} ${parsedUrl.user ? `${parsedUrl.user}@` : ''}${parsedUrl.resource}`; if (parsedUrl.port) command += ` -p ${parsedUrl.port}`; @@ -308,11 +306,11 @@ export function openSSH(parsedUrl: ReturnType) { rpc.once('session add', ({uid}) => { rpc.once('session data', () => { - dispatch(sendSessionData(uid, command)); + dispatch(sendSessionData(uid, command, null)); }); }); - dispatch(requestSession(undefined)); + dispatch(requestSession()); } }); }; diff --git a/lib/actions/updater.ts b/lib/actions/updater.ts index 45490f7e..17eef45b 100644 --- a/lib/actions/updater.ts +++ b/lib/actions/updater.ts @@ -1,12 +1,12 @@ -import {UPDATE_INSTALL, UPDATE_AVAILABLE} from '../../typings/constants/updater'; -import type {HyperActions} from '../../typings/hyper'; +import {UPDATE_INSTALL, UPDATE_AVAILABLE} from '../constants/updater'; import rpc from '../rpc'; +import {HyperActions} from '../hyper'; export function installUpdate(): HyperActions { return { type: UPDATE_INSTALL, effect: () => { - rpc.emit('quit and install'); + rpc.emit('quit and install', null); } }; } diff --git a/lib/command-registry.ts b/lib/command-registry.ts index f72c08c9..82f38633 100644 --- a/lib/command-registry.ts +++ b/lib/command-registry.ts @@ -1,7 +1,9 @@ -import type {HyperDispatch} from '../typings/hyper'; - +import {require as remoteRequire} from '@electron/remote'; +import {HyperDispatch} from './hyper'; import {closeSearch} from './actions/sessions'; -import {ipcRenderer} from './utils/ipc'; +// TODO: Should be updates to new async API https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31 + +const {getDecoratedKeymaps} = remoteRequire('./plugins') as typeof import('../app/plugins'); let commands: Record void> = { 'editor:search-close': (e, dispatch) => { @@ -10,8 +12,8 @@ let commands: Record void> = { } }; -export const getRegisteredKeys = async () => { - const keymaps = await ipcRenderer.invoke('getDecoratedKeymaps'); +export const getRegisteredKeys = () => { + const keymaps = getDecoratedKeymaps(); return Object.keys(keymaps).reduce((result: Record, actionName) => { const commandKeys = keymaps[actionName]; diff --git a/lib/components/header.tsx b/lib/components/header.tsx index 416ecbc5..faa131fb 100644 --- a/lib/components/header.tsx +++ b/lib/components/header.tsx @@ -1,27 +1,27 @@ -import React, {forwardRef, useState} from 'react'; +import React from 'react'; -import type {HeaderProps} from '../../typings/hyper'; import {decorate, getTabsProps} from '../utils/plugins'; import Tabs_ from './tabs'; +import {HeaderProps} from '../hyper'; const Tabs = decorate(Tabs_, 'Tabs'); -const Header = forwardRef((props, ref) => { - const [headerMouseDownWindowX, setHeaderMouseDownWindowX] = useState(0); - const [headerMouseDownWindowY, setHeaderMouseDownWindowY] = useState(0); +export default class Header extends React.PureComponent { + headerMouseDownWindowX!: number; + headerMouseDownWindowY!: number; - const onChangeIntent = (active: string) => { + onChangeIntent = (active: string) => { // we ignore clicks if they're a byproduct of a drag // motion to move the window - if (window.screenX !== headerMouseDownWindowX || window.screenY !== headerMouseDownWindowY) { + if (window.screenX !== this.headerMouseDownWindowX || window.screenY !== this.headerMouseDownWindowY) { return; } - props.onChangeTab(active); + this.props.onChangeTab(active); }; - const handleHeaderMouseDown = () => { + handleHeaderMouseDown = () => { // the hack of all hacks, this prevents the term // iframe from losing focus, for example, when // the user drags the nav around @@ -30,43 +30,43 @@ const Header = forwardRef((props, ref) => { // persist start positions of a potential drag motion // to differentiate dragging from clicking - setHeaderMouseDownWindowX(window.screenX); - setHeaderMouseDownWindowY(window.screenY); + this.headerMouseDownWindowX = window.screenX; + this.headerMouseDownWindowY = window.screenY; }; - const handleHamburgerMenuClick = (event: React.MouseEvent) => { + handleHamburgerMenuClick = (event: React.MouseEvent) => { let {right: x, bottom: y} = event.currentTarget.getBoundingClientRect(); x -= 15; // to compensate padding y -= 12; // ^ same - props.openHamburgerMenu({x, y}); + this.props.openHamburgerMenu({x, y}); }; - const handleMaximizeClick = () => { - if (props.maximized) { - props.unmaximize(); + handleMaximizeClick = () => { + if (this.props.maximized) { + this.props.unmaximize(); } else { - props.maximize(); + this.props.maximize(); } }; - const handleMinimizeClick = () => { - props.minimize(); + handleMinimizeClick = () => { + this.props.minimize(); }; - const handleCloseClick = () => { - props.close(); + handleCloseClick = () => { + this.props.close(); }; - const getWindowHeaderConfig = () => { - const {showHamburgerMenu, showWindowControls} = props; + getWindowHeaderConfig() { + const {showHamburgerMenu, showWindowControls} = this.props; const defaults = { - hambMenu: !props.isMac, // show by default on windows and linux - winCtrls: !props.isMac // show by default on Windows and Linux + hambMenu: !this.props.isMac, // show by default on windows and linux + winCtrls: !this.props.isMac // show by default on Windows and Linux }; // don't allow the user to change defaults on macOS - if (props.isMac) { + if (this.props.isMac) { return defaults; } @@ -74,187 +74,182 @@ const Header = forwardRef((props, ref) => { hambMenu: showHamburgerMenu === '' ? defaults.hambMenu : showHamburgerMenu, winCtrls: showWindowControls === '' ? defaults.winCtrls : showWindowControls }; - }; - - const {isMac} = props; - const {borderColor} = props; - let title = 'Hyper'; - if (props.tabs.length === 1 && props.tabs[0].title) { - // if there's only one tab we use its title as the window title - title = props.tabs[0].title; } - const {hambMenu, winCtrls} = getWindowHeaderConfig(); - const left = winCtrls === 'left'; - const maxButtonHref = props.maximized - ? './renderer/assets/icons.svg#restore-window' - : './renderer/assets/icons.svg#maximize-window'; - return ( -
window.focusActiveTerm()} - onDoubleClick={handleMaximizeClick} - ref={ref} - > - {!isMac && ( -
1 ? 'header_windowHeaderWithBorder' : ''}`} - style={{borderColor}} - > - {hambMenu && ( - - - - )} - {title} - {winCtrls && ( -
-
- - - + render() { + const {isMac} = this.props; + const props = getTabsProps(this.props, { + tabs: this.props.tabs, + borderColor: this.props.borderColor, + onClose: this.props.onCloseTab, + onChange: this.onChangeIntent, + fullScreen: this.props.fullScreen + }); + const {borderColor} = props; + let title = 'Hyper'; + if (props.tabs.length === 1 && props.tabs[0].title) { + // if there's only one tab we use its title as the window title + title = props.tabs[0].title; + } + const {hambMenu, winCtrls} = this.getWindowHeaderConfig(); + const left = winCtrls === 'left'; + const maxButtonHref = this.props.maximized + ? './renderer/assets/icons.svg#restore-window' + : './renderer/assets/icons.svg#maximize-window'; + + return ( +
window.focusActiveTerm()} + onDoubleClick={this.handleMaximizeClick} + > + {!isMac && ( +
1 ? 'header_windowHeaderWithBorder' : ''}`} + style={{borderColor}} + > + {hambMenu && ( + + + + )} + {title} + {winCtrls && ( +
+
+ + + +
+
+ + + +
+
+ + + +
-
- - - -
-
- - - -
-
- )} -
- )} - {props.customChildrenBefore} - - {props.customChildren} + )} +
+ )} + {this.props.customChildrenBefore} + + {this.props.customChildren} - -
- ); -}); - -Header.displayName = 'Header'; - -export default Header; + .header_closeWindow:active { + color: #fe354e; + } + `} + + ); + } +} diff --git a/lib/components/new-tab.tsx b/lib/components/new-tab.tsx deleted file mode 100644 index 3fe99543..00000000 --- a/lib/components/new-tab.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, {useRef, useState} from 'react'; - -import {VscChevronDown} from '@react-icons/all-files/vsc/VscChevronDown'; -import useClickAway from 'react-use/lib/useClickAway'; - -import type {configOptions} from '../../typings/config'; - -interface Props { - defaultProfile: string; - profiles: configOptions['profiles']; - openNewTab: (name: string) => void; - backgroundColor: string; - borderColor: string; - tabsVisible: boolean; -} -const isMac = /Mac/.test(navigator.userAgent); - -const DropdownButton = ({defaultProfile, profiles, openNewTab, backgroundColor, borderColor, tabsVisible}: Props) => { - const [dropdownOpen, setDropdownOpen] = useState(false); - const ref = useRef(null); - - const toggleDropdown = () => { - setDropdownOpen(!dropdownOpen); - }; - - useClickAway(ref, () => { - setDropdownOpen(false); - }); - - return ( -
e.stopPropagation()} - onBlur={() => setDropdownOpen(false)} - > - - - {dropdownOpen && ( -
    - {profiles.map((profile) => ( -
  • { - openNewTab(profile.name); - setDropdownOpen(false); - }} - className={`profile_dropdown_item ${ - profile.name === defaultProfile && profiles.length > 1 ? 'profile_dropdown_item_default' : '' - }`} - > - {profile.name} -
  • - ))} -
- )} - - -
- ); -}; - -export default DropdownButton; diff --git a/lib/components/notification.tsx b/lib/components/notification.tsx index 68b7a2df..ff5bd4ee 100644 --- a/lib/components/notification.tsx +++ b/lib/components/notification.tsx @@ -1,110 +1,115 @@ -import React, {forwardRef, useEffect, useRef, useState} from 'react'; +import React from 'react'; +import {NotificationProps, NotificationState} from '../hyper'; -import type {NotificationProps} from '../../typings/hyper'; +export default class Notification extends React.PureComponent { + dismissTimer!: NodeJS.Timeout; + constructor(props: NotificationProps) { + super(props); + this.state = { + dismissing: false + }; + } -const Notification = forwardRef>((props, ref) => { - const dismissTimer = useRef(undefined); - const [dismissing, setDismissing] = useState(false); + componentDidMount() { + if (this.props.dismissAfter) { + this.setDismissTimer(); + } + } - useEffect(() => { - setDismissTimer(); - }, []); - - useEffect(() => { + componentDidUpdate(prevProps: NotificationProps, prevState: NotificationState) { // if we have a timer going and the notification text // changed we reset the timer - resetDismissTimer(); - setDismissing(false); - }, [props.text]); + if (this.props.text !== prevProps.text) { + if (prevProps.dismissAfter) { + this.resetDismissTimer(); + } + if (prevState.dismissing) { + this.setState({dismissing: false}); + } + } + } - const handleDismiss = () => { - setDismissing(true); + handleDismiss = () => { + this.setState({dismissing: true}); }; - const onElement = (el: HTMLDivElement | null) => { + onElement = (el: HTMLDivElement | null) => { if (el) { el.addEventListener('webkitTransitionEnd', () => { - if (dismissing) { - props.onDismiss(); + if (this.state.dismissing) { + this.props.onDismiss(); } }); - const {backgroundColor} = props; + const {backgroundColor} = this.props; if (backgroundColor) { el.style.setProperty('background-color', backgroundColor, 'important'); } - - if (ref) { - if (typeof ref === 'function') ref(el); - else ref.current = el; - } } }; - const setDismissTimer = () => { - if (typeof props.dismissAfter === 'number') { - dismissTimer.current = setTimeout(() => { - handleDismiss(); - }, props.dismissAfter); - } - }; + setDismissTimer() { + this.dismissTimer = setTimeout(() => { + this.handleDismiss(); + }, this.props.dismissAfter); + } - const resetDismissTimer = () => { - clearTimeout(dismissTimer.current); - setDismissTimer(); - }; + resetDismissTimer() { + clearTimeout(this.dismissTimer); + this.setDismissTimer(); + } - useEffect(() => { - return () => { - clearTimeout(dismissTimer.current); - }; - }, []); + componentWillUnmount() { + clearTimeout(this.dismissTimer); + } - const {backgroundColor, color} = props; - const opacity = dismissing ? 0 : 1; - return ( -
- {props.customChildrenBefore} - {props.children || props.text} - {props.userDismissable ? ( - - [x] - - ) : null} - {props.customChildren} + render() { + const {backgroundColor, color} = this.props; + const opacity = this.state.dismissing ? 0 : 1; + return ( +
+ {this.props.customChildrenBefore} + {this.props.children || this.props.text} + {this.props.userDismissable ? ( + + [x] + + ) : null} + {this.props.customChildren} - -
- ); -}); - -Notification.displayName = 'Notification'; - -export default Notification; + .notification_dismissLink:hover, + .notification_dismissLink:focus { + font-weight: 900; + } + `} +
+ ); + } +} diff --git a/lib/components/notifications.tsx b/lib/components/notifications.tsx index 79e6ede3..a5b281ca 100644 --- a/lib/components/notifications.tsx +++ b/lib/components/notifications.tsx @@ -1,132 +1,132 @@ -import React, {forwardRef} from 'react'; +import React from 'react'; -import type {NotificationsProps} from '../../typings/hyper'; import {decorate} from '../utils/plugins'; import Notification_ from './notification'; +import {NotificationsProps} from '../hyper'; const Notification = decorate(Notification_, 'Notification'); -const Notifications = forwardRef((props, ref) => { - return ( -
- {props.customChildrenBefore} - {props.fontShowing && ( - - )} +export default class Notifications extends React.PureComponent { + render() { + return ( +
+ {this.props.customChildrenBefore} + {this.props.fontShowing && ( + + )} - {props.resizeShowing && ( - - )} + {this.props.resizeShowing && ( + + )} - {props.messageShowing && ( - - {props.messageURL ? ( - <> - {props.messageText} ( - { - void window.require('electron').shell.openExternal(ev.currentTarget.href); - ev.preventDefault(); - }} - href={props.messageURL} - > - more - - ) - - ) : null} - - )} - - {props.updateShowing && ( - - Version {props.updateVersion} ready. - {props.updateNote && ` ${props.updateNote.trim().replace(/\.$/, '')}`} ( - { - void window.require('electron').shell.openExternal(ev.currentTarget.href); - ev.preventDefault(); - }} - href={`https://github.com/vercel/hyper/releases/tag/${props.updateVersion}`} + {this.props.messageShowing && ( + - notes - - ).{' '} - {props.updateCanInstall ? ( + {this.props.messageURL + ? [ + this.props.messageText, + ' (', + { + void window.require('electron').shell.openExternal(ev.currentTarget.href); + ev.preventDefault(); + }} + href={this.props.messageURL} + > + more + , + ') ' + ] + : null} + + )} + + {this.props.updateShowing && ( + + Version {this.props.updateVersion} ready. + {this.props.updateNote && ` ${this.props.updateNote.trim().replace(/\.$/, '')}`} ( - Restart - - ) : ( - { void window.require('electron').shell.openExternal(ev.currentTarget.href); ev.preventDefault(); }} - href={props.updateReleaseUrl!} + href={`https://github.com/vercel/hyper/releases/tag/${this.props.updateVersion}`} > - Download + notes - )} - .{' '} - - )} - {props.customChildren} + ).{' '} + {this.props.updateCanInstall ? ( + + Restart + + ) : ( + { + void window.require('electron').shell.openExternal(ev.currentTarget.href); + ev.preventDefault(); + }} + href={this.props.updateReleaseUrl!} + > + Download + + )} + .{' '} + + )} + {this.props.customChildren} - -
- ); -}); - -Notifications.displayName = 'Notifications'; - -export default Notifications; + +
+ ); + } +} diff --git a/lib/components/searchBox.tsx b/lib/components/searchBox.tsx index 55a72f16..9fa93b6f 100644 --- a/lib/components/searchBox.tsx +++ b/lib/components/searchBox.tsx @@ -1,15 +1,13 @@ -import React, {useCallback, useRef, useEffect, forwardRef} from 'react'; - -import {VscArrowDown} from '@react-icons/all-files/vsc/VscArrowDown'; +import React, {useCallback} from 'react'; +import {SearchBoxProps} from '../hyper'; import {VscArrowUp} from '@react-icons/all-files/vsc/VscArrowUp'; -import {VscCaseSensitive} from '@react-icons/all-files/vsc/VscCaseSensitive'; +import {VscArrowDown} from '@react-icons/all-files/vsc/VscArrowDown'; import {VscClose} from '@react-icons/all-files/vsc/VscClose'; +import {VscCaseSensitive} from '@react-icons/all-files/vsc/VscCaseSensitive'; import {VscRegex} from '@react-icons/all-files/vsc/VscRegex'; import {VscWholeWord} from '@react-icons/all-files/vsc/VscWholeWord'; import clsx from 'clsx'; -import type {SearchBoxProps} from '../../typings/hyper'; - type SearchButtonColors = { foregroundColor: string; selectionColor: string; @@ -84,154 +82,179 @@ const SearchButton = ({ ); }; -const SearchBox = forwardRef((props, ref) => { - const { - caseSensitive, - wholeWord, - regex, - results, - toggleCaseSensitive, - toggleWholeWord, - toggleRegex, - next, - prev, - close, - backgroundColor, - foregroundColor, - borderColor, - selectionColor, - font - } = props; +class SearchBox extends React.PureComponent { + searchTerm: string; + input: HTMLInputElement | null = null; + searchButtonColors: SearchButtonColors; - const searchTermRef = useRef(''); - const inputRef = useRef(null); + constructor(props: SearchBoxProps) { + super(props); + this.searchTerm = ''; + this.searchButtonColors = { + backgroundColor: this.props.borderColor, + selectionColor: this.props.selectionColor, + foregroundColor: this.props.foregroundColor + }; + } - const handleChange = useCallback( - (event: React.KeyboardEvent) => { - searchTermRef.current = event.currentTarget.value; - if (event.shiftKey && event.key === 'Enter') { - prev(searchTermRef.current); - } else if (event.key === 'Enter') { - next(searchTermRef.current); - } - }, - [prev, next] - ); - - useEffect(() => { - inputRef.current?.focus(); - }, [inputRef.current]); - - const searchButtonColors: SearchButtonColors = { - backgroundColor: borderColor, - selectionColor, - foregroundColor + handleChange = (event: React.KeyboardEvent) => { + this.searchTerm = event.currentTarget.value; + if (event.shiftKey && event.key === 'Enter') { + this.props.prev(this.searchTerm); + } else if (event.key === 'Enter') { + this.props.next(this.searchTerm); + } }; - return ( -
-
- + componentDidMount(): void { + this.input?.focus(); + } - - - + render() { + const { + caseSensitive, + wholeWord, + regex, + results, + toggleCaseSensitive, + toggleWholeWord, + toggleRegex, + next, + prev, + close, + backgroundColor, + foregroundColor, + borderColor, + selectionColor, + font + } = this.props; - - - + return ( +
+
+ { + this.input = input; + }} + placeholder="Search" + > - - - -
+ + + - - {results === undefined - ? '' - : results.resultCount === 0 + + + + + + + +
+ + + {results === undefined + ? '' + : results.resultCount === 0 ? 'No results' : `${results.resultIndex + 1} of ${results.resultCount}`} - + -
- prev(searchTermRef.current)} - active={false} - title="Previous Match" - {...searchButtonColors} - > - - +
+ prev(this.searchTerm)} + active={false} + title="Previous Match" + {...this.searchButtonColors} + > + + - next(searchTermRef.current)} - active={false} - title="Next Match" - {...searchButtonColors} - > - - + next(this.searchTerm)} + active={false} + title="Next Match" + {...this.searchButtonColors} + > + + - - - + close()} active={false} title="Close" {...this.searchButtonColors}> + + +
+ +
- - -
- ); -}); - -SearchBox.displayName = 'SearchBox'; + ); + } +} export default SearchBox; diff --git a/lib/components/split-pane.tsx b/lib/components/split-pane.tsx index aea53f6f..8ca6cd8c 100644 --- a/lib/components/split-pane.tsx +++ b/lib/components/split-pane.tsx @@ -1,191 +1,219 @@ -import React, {useState, useEffect, useRef, forwardRef} from 'react'; +import React from 'react'; +import _ from 'lodash'; +import {SplitPaneProps} from '../hyper'; -import sum from 'lodash/sum'; +export default class SplitPane extends React.PureComponent { + dragPanePosition!: number; + dragTarget!: Element; + panes!: Element[]; + paneIndex!: number; + d1!: 'height' | 'width'; + d2!: 'top' | 'left'; + d3!: 'clientX' | 'clientY'; + panesSize!: number; + dragging!: boolean; + constructor(props: SplitPaneProps) { + super(props); + this.state = {dragging: false}; + } -import type {SplitPaneProps} from '../../typings/hyper'; + componentDidUpdate(prevProps: SplitPaneProps) { + if (this.state.dragging && prevProps.sizes !== this.props.sizes) { + // recompute positions for ongoing dragging + this.dragPanePosition = this.dragTarget.getBoundingClientRect()[this.d2]; + } + } -const SplitPane = forwardRef((props, ref) => { - const dragPanePosition = useRef(0); - const dragTarget = useRef(null); - const paneIndex = useRef(0); - const d1 = props.direction === 'horizontal' ? 'height' : 'width'; - const d2 = props.direction === 'horizontal' ? 'top' : 'left'; - const d3 = props.direction === 'horizontal' ? 'clientY' : 'clientX'; - const panesSize = useRef(null); - const [dragging, setDragging] = useState(false); + setupPanes(ev: any) { + this.panes = Array.from(ev.target.parentNode.childNodes); + this.paneIndex = this.panes.indexOf(ev.target); + this.paneIndex -= Math.ceil(this.paneIndex / 2); + } - const handleAutoResize = (ev: React.MouseEvent, index: number) => { + handleAutoResize = (ev: React.MouseEvent) => { ev.preventDefault(); - paneIndex.current = index; + this.setupPanes(ev); - const sizes_ = getSizes(); - sizes_[paneIndex.current] = 0; - sizes_[paneIndex.current + 1] = 0; + const sizes_ = this.getSizes(); + sizes_[this.paneIndex] = 0; + sizes_[this.paneIndex + 1] = 0; - const availableWidth = 1 - sum(sizes_); - sizes_[paneIndex.current] = availableWidth / 2; - sizes_[paneIndex.current + 1] = availableWidth / 2; + const availableWidth = 1 - _.sum(sizes_); + sizes_[this.paneIndex] = availableWidth / 2; + sizes_[this.paneIndex + 1] = availableWidth / 2; - props.onResize(sizes_); + this.props.onResize(sizes_); }; - const handleDragStart = (ev: React.MouseEvent, index: number) => { + handleDragStart = (ev: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call ev.preventDefault(); - setDragging(true); - window.addEventListener('mousemove', onDrag); - window.addEventListener('mouseup', onDragEnd); + this.setState({dragging: true}); + window.addEventListener('mousemove', this.onDrag); + window.addEventListener('mouseup', this.onDragEnd); - const target = ev.target as HTMLDivElement; - dragTarget.current = target; - dragPanePosition.current = dragTarget.current.getBoundingClientRect()[d2]; - panesSize.current = target.parentElement!.getBoundingClientRect()[d1]; - paneIndex.current = index; + // dimensions to consider + if (this.props.direction === 'horizontal') { + this.d1 = 'height'; + this.d2 = 'top'; + this.d3 = 'clientY'; + } else { + this.d1 = 'width'; + this.d2 = 'left'; + this.d3 = 'clientX'; + } + + this.dragTarget = ev.target; + this.dragPanePosition = this.dragTarget.getBoundingClientRect()[this.d2]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + this.panesSize = ev.target.parentNode.getBoundingClientRect()[this.d1]; + this.setupPanes(ev); }; - const getSizes = () => { - const {sizes} = props; + getSizes() { + const {sizes} = this.props; let sizes_: number[]; if (sizes) { sizes_ = [...sizes.asMutable()]; } else { - const total = props.children.length; + const total = (this.props.children as React.ReactNodeArray).length; const count = new Array(total).fill(1 / total); sizes_ = count; } return sizes_; - }; + } - const onDrag = (ev: MouseEvent) => { - const sizes_ = getSizes(); + onDrag = (ev: MouseEvent) => { + const sizes_ = this.getSizes(); - const i = paneIndex.current; - const pos = ev[d3]; - const d = Math.abs(dragPanePosition.current - pos) / panesSize.current!; - if (pos > dragPanePosition.current) { + const i = this.paneIndex; + const pos = ev[this.d3]; + const d = Math.abs(this.dragPanePosition - pos) / this.panesSize; + if (pos > this.dragPanePosition) { sizes_[i] += d; sizes_[i + 1] -= d; } else { sizes_[i] -= d; sizes_[i + 1] += d; } - props.onResize(sizes_); + this.props.onResize(sizes_); }; - const onDragEnd = () => { - window.removeEventListener('mousemove', onDrag); - window.removeEventListener('mouseup', onDragEnd); - setDragging(false); + onDragEnd = () => { + if (this.state.dragging) { + window.removeEventListener('mousemove', this.onDrag); + window.removeEventListener('mouseup', this.onDragEnd); + this.setState({dragging: false}); + } }; - useEffect(() => { - return () => { - onDragEnd(); - }; - }, []); - - const {children, direction, borderColor} = props; - const sizeProperty = direction === 'horizontal' ? 'height' : 'width'; - // workaround for the fact that if we don't specify - // sizes, sometimes flex fails to calculate the - // right height for the horizontal panes - const sizes = props.sizes || new Array(children.length).fill(1 / children.length); - return ( -
- {children.map((child, i) => { - const style = { - // flexBasis doesn't work for the first horizontal pane, height need to be specified - [sizeProperty]: `${sizes[i] * 100}%`, - flexBasis: `${sizes[i] * 100}%`, - flexGrow: 0 - }; - - return ( - -
+ render() { + const children = this.props.children as React.ReactNodeArray; + const {direction, borderColor} = this.props; + const sizeProperty = direction === 'horizontal' ? 'height' : 'width'; + // workaround for the fact that if we don't specify + // sizes, sometimes flex fails to calculate the + // right height for the horizontal panes + const sizes = this.props.sizes || new Array(children.length).fill(1 / children.length); + return ( +
+ {React.Children.map(children, (child, i) => { + const style = { + // flexBasis doesn't work for the first horizontal pane, height need to be specified + [sizeProperty]: `${sizes[i] * 100}%`, + flexBasis: `${sizes[i] * 100}%`, + flexGrow: 0 + }; + return [ +
{child} -
- {i < children.length - 1 ? ( +
, + i < children.length - 1 ? (
handleDragStart(e, i)} - onDoubleClick={(e) => handleAutoResize(e, i)} + key="divider" + onMouseDown={this.handleDragStart} + onDoubleClick={this.handleAutoResize} style={{backgroundColor: borderColor}} className={`splitpane_divider splitpane_divider_${direction}`} /> - ) : null} - - ); - })} -
+ ) : null + ]; + })} +
- -
- ); -}); + /* + this shim is used to make sure mousemove events + trigger in all the draggable area of the screen + this is not the case due to hterm's