Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20a4ecf95f | ||
|
|
c80cd5426a | ||
|
|
9073401c6c | ||
|
|
f9e19161b3 | ||
|
|
44d25cd528 | ||
|
|
7bf6e3b6df | ||
|
|
7cc1615cec | ||
|
|
ef5419b520 |
126 changed files with 5195 additions and 8148 deletions
|
|
@ -11,4 +11,3 @@ bin
|
||||||
dist
|
dist
|
||||||
target
|
target
|
||||||
cache
|
cache
|
||||||
schema.json
|
|
||||||
102
.eslintrc.json
102
.eslintrc.json
|
|
@ -3,9 +3,7 @@
|
||||||
"react",
|
"react",
|
||||||
"prettier",
|
"prettier",
|
||||||
"@typescript-eslint",
|
"@typescript-eslint",
|
||||||
"eslint-comments",
|
"eslint-comments"
|
||||||
"lodash",
|
|
||||||
"import"
|
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
|
|
@ -15,6 +13,7 @@
|
||||||
],
|
],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 8,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"jsx": true,
|
"jsx": true,
|
||||||
|
|
@ -34,11 +33,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
"version": "detect"
|
"version": "detect"
|
||||||
},
|
}
|
||||||
"import/resolver": {
|
|
||||||
"typescript": {}
|
|
||||||
},
|
|
||||||
"import/internal-regex": "^(electron|react)$"
|
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"func-names": [
|
"func-names": [
|
||||||
|
|
@ -65,18 +60,31 @@
|
||||||
"bracketSameLine": false
|
"bracketSameLine": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"eslint-comments/no-unused-disable": "error",
|
"eslint-comments/no-unused-disable": "error"
|
||||||
"react/no-unknown-property":[
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"ignore": [
|
|
||||||
"jsx",
|
|
||||||
"global"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"app/config/config-default.js",
|
||||||
|
".hyper.js"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"semi": true,
|
||||||
|
"useTabs": false,
|
||||||
|
"parser": "babel",
|
||||||
|
"bracketSameLine": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"**.ts",
|
"**.ts",
|
||||||
|
|
@ -98,63 +106,7 @@
|
||||||
"@typescript-eslint/no-shadow": ["error"],
|
"@typescript-eslint/no-shadow": ["error"],
|
||||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||||
"@typescript-eslint/restrict-template-expressions": "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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"extends": [
|
|
||||||
"plugin:jsonc/recommended-with-json",
|
|
||||||
"plugin:json-schema-validator/recommended"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"*.json"
|
|
||||||
],
|
|
||||||
"parser": "jsonc-eslint-parser",
|
|
||||||
"plugins": [
|
|
||||||
"jsonc",
|
|
||||||
"json-schema-validator"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"jsonc/array-element-newline": [
|
|
||||||
"error",
|
|
||||||
"consistent"
|
|
||||||
],
|
|
||||||
"jsonc/array-bracket-newline": [
|
|
||||||
"error",
|
|
||||||
"consistent"
|
|
||||||
],
|
|
||||||
"jsonc/indent": [
|
|
||||||
"error",
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"prettier/prettier": "off",
|
|
||||||
"json-schema-validator/no-invalid": "error"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -26,7 +26,7 @@ assignees: ''
|
||||||
|
|
||||||
- **OS version and name**: <!-- Replace with version + name -->
|
- **OS version and name**: <!-- Replace with version + name -->
|
||||||
- **Hyper.app version**: <!-- Replace with version -->
|
- **Hyper.app version**: <!-- Replace with version -->
|
||||||
- **Link of a [Gist](https://gist.github.com/) with the contents of your hyper.json**: <!-- Gist Link Here -->
|
- **Link of a [Gist](https://gist.github.com/) with the contents of your .hyper.js**: <!-- Gist Link Here -->
|
||||||
- **Relevant information from devtools** _(CMD+ALT+I on macOS, CTRL+SHIFT+I elsewhere)_: <!-- Replace with info if applicable, or N/A -->
|
- **Relevant information from devtools** _(CMD+ALT+I on macOS, CTRL+SHIFT+I elsewhere)_: <!-- Replace with info if applicable, or N/A -->
|
||||||
- **The issue is reproducible in vanilla Hyper.app**: <!-- Replace with info if applicable, or `Is Vanilla`. (Vanilla means Hyper.app without any add-ons or extras. Straight out of the box.) -->
|
- **The issue is reproducible in vanilla Hyper.app**: <!-- Replace with info if applicable, or `Is Vanilla`. (Vanilla means Hyper.app without any add-ons or extras. Straight out of the box.) -->
|
||||||
|
|
||||||
|
|
|
||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -35,11 +35,11 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# 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).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v3
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
@ -64,4 +64,4 @@ jobs:
|
||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|
|
||||||
6
.github/workflows/e2e_comment.yml
vendored
6
.github/workflows/e2e_comment.yml
vendored
|
|
@ -14,14 +14,14 @@ jobs:
|
||||||
WORKFLOW_RUN_INFO: ${{ toJSON(github.event.workflow_run) }}
|
WORKFLOW_RUN_INFO: ${{ toJSON(github.event.workflow_run) }}
|
||||||
run: echo "$WORKFLOW_RUN_INFO"
|
run: echo "$WORKFLOW_RUN_INFO"
|
||||||
- name: Download Artifacts
|
- name: Download Artifacts
|
||||||
uses: dawidd6/action-download-artifact@v3.1.4
|
uses: dawidd6/action-download-artifact@v2.24.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
workflow: nodejs.yml
|
workflow: nodejs.yml
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
name: e2e
|
name: e2e
|
||||||
- name: Get PR number
|
- name: Get PR number
|
||||||
uses: dawidd6/action-download-artifact@v3.1.4
|
uses: dawidd6/action-download-artifact@v2.24.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
workflow: nodejs.yml
|
workflow: nodejs.yml
|
||||||
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
name: pr_num
|
name: pr_num
|
||||||
- name: Read the pr_num file
|
- name: Read the pr_num file
|
||||||
id: pr_num_reader
|
id: pr_num_reader
|
||||||
uses: juliangruber/read-file-action@v1.1.7
|
uses: juliangruber/read-file-action@v1.1.6
|
||||||
with:
|
with:
|
||||||
path: ./pr_num.txt
|
path: ./pr_num.txt
|
||||||
- name: List images
|
- name: List images
|
||||||
|
|
|
||||||
6
.github/workflows/nodejs.yml
vendored
6
.github/workflows/nodejs.yml
vendored
|
|
@ -32,7 +32,7 @@ jobs:
|
||||||
- name: Get yarn cache directory path
|
- name: Get yarn cache directory path
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
- uses: actions/cache/restore@v4
|
- uses: actions/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }}
|
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }}
|
||||||
|
|
@ -108,7 +108,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: pr_num
|
name: pr_num
|
||||||
path: ./pr_num.txt
|
path: ./pr_num.txt
|
||||||
- uses: actions/cache/save@v4
|
- uses: actions/cache/save@v3
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
|
@ -138,7 +138,7 @@ jobs:
|
||||||
- name: Get yarn cache directory path
|
- name: Get yarn cache directory path
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
- uses: actions/cache/restore@v4
|
- uses: actions/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }}
|
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock', 'app/yarn.lock') }}
|
||||||
|
|
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -13,9 +13,8 @@ npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|
||||||
# optional dev config file and plugins directory
|
# optional dev config file and plugins directory
|
||||||
hyper.json
|
.hyper.js
|
||||||
schema.json
|
.hyper_plugins
|
||||||
plugins
|
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ Prerequisites and steps are described in the ["Contributing" section of our READ
|
||||||
Be sure to use the `canary` branch.
|
Be sure to use the `canary` branch.
|
||||||
|
|
||||||
### Create a dev config file
|
### Create a dev config file
|
||||||
Copy your config file `hyper.json` to the root of your cloned repository. Hyper, in dev mode, will use this copied config file. That means that you can continue to use your main installation of Hyper with your day-to-day configuration.
|
Copy your config file `.hyper.js` to the root of your cloned repository. Hyper, in dev mode, will use this copied config file. That means that you can continue to use your main installation of Hyper with your day-to-day configuration.
|
||||||
After the first run, Hyper, in dev mode, will have created a new `plugins` directory in your repository directory.
|
After the first run, Hyper, in dev mode, will have created a new `.hyper_plugins` directory in your repository directory.
|
||||||
|
|
||||||
### Setup your plugin
|
### Setup your plugin
|
||||||
Go to your recently created `<repository_root>/plugins/local` directory and create/clone your plugin repo. An even better method on macOS/Linux is to add a symlink to your plugin directory.
|
Go to your recently created `<repository_root>/.hyper_plugins/local` directory and create/clone your plugin repo. An even better method on macOS/Linux is to add a symlink to your plugin directory.
|
||||||
|
|
||||||
Edit your dev config file, and add your plugin name (directory name in your `local` directory) in the `localPlugins` array.
|
Edit your dev config file, and add your plugin name (directory name in your `local` directory) in the `localPlugins` array.
|
||||||
```js
|
```js
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {EventEmitter} from 'events';
|
|
||||||
|
|
||||||
import fetch from 'electron-fetch';
|
import fetch from 'electron-fetch';
|
||||||
|
import {EventEmitter} from 'events';
|
||||||
|
|
||||||
class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
|
class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
|
||||||
updateURL!: string;
|
updateURL!: string;
|
||||||
|
|
@ -27,12 +26,13 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
|
||||||
this.emit('update-not-available');
|
this.emit('update-not-available');
|
||||||
return;
|
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.
|
// Only name is mandatory, needed to construct release URL.
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new Error('Malformed server response: release name is missing.');
|
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);
|
this.emit('update-available', {}, notes, name, date);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -47,6 +47,4 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoUpdaterLinux = new AutoUpdater();
|
export default new AutoUpdater();
|
||||||
|
|
||||||
export default autoUpdaterLinux;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import {app, Menu} from 'electron';
|
import {app, Menu, BrowserWindow} from 'electron';
|
||||||
import type {BrowserWindow} from 'electron';
|
|
||||||
|
|
||||||
import {openConfig, getConfig} from './config';
|
import {openConfig, getConfig} from './config';
|
||||||
import {updatePlugins} from './plugins';
|
import {updatePlugins} from './plugins';
|
||||||
import {installCLI} from './utils/cli-install';
|
import {installCLI} from './utils/cli-install';
|
||||||
|
|
@ -146,22 +144,6 @@ const commands: Record<string, (focusedWindow?: BrowserWindow) => 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) => {
|
export const execCommand = (command: string, focusedWindow?: BrowserWindow) => {
|
||||||
const fn = commands[command];
|
const fn = commands[command];
|
||||||
if (fn) {
|
if (fn) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import {app} from 'electron';
|
|
||||||
|
|
||||||
import chokidar from 'chokidar';
|
import chokidar from 'chokidar';
|
||||||
|
import notify from './notify';
|
||||||
import type {parsedConfig, configOptions} from '../typings/config';
|
|
||||||
|
|
||||||
import {_import, getDefaultConfig} from './config/import';
|
import {_import, getDefaultConfig} from './config/import';
|
||||||
import _openConfig from './config/open';
|
import _openConfig from './config/open';
|
||||||
import {cfgPath, cfgDir} from './config/paths';
|
import {cfgPath, cfgDir} from './config/paths';
|
||||||
import notify from './notify';
|
|
||||||
import {getColorMap} from './utils/colors';
|
import {getColorMap} from './utils/colors';
|
||||||
|
import {parsedConfig, configOptions} from '../lib/config';
|
||||||
|
import {app} from 'electron';
|
||||||
|
|
||||||
const watchers: Function[] = [];
|
const watchers: Function[] = [];
|
||||||
let cfg: parsedConfig = {} as any;
|
let cfg: parsedConfig = {} as any;
|
||||||
|
|
@ -81,30 +78,8 @@ export const getConfigDir = () => {
|
||||||
return cfgDir;
|
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 = () => {
|
export const getConfig = () => {
|
||||||
return getProfileConfig(getDefaultProfile());
|
return cfg.config;
|
||||||
};
|
|
||||||
|
|
||||||
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};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openConfig = () => {
|
export const openConfig = () => {
|
||||||
|
|
|
||||||
189
app/config/config-default.js
Normal file
189
app/config/config-default.js
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// Future versions of Hyper may add additional config options,
|
||||||
|
// which will not automatically be merged into this file.
|
||||||
|
// See https://hyper.is#cfg for all currently supported options.
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
config: {
|
||||||
|
// choose either `'stable'` for receiving highly polished,
|
||||||
|
// or `'canary'` for less polished but more frequent updates
|
||||||
|
updateChannel: 'stable',
|
||||||
|
|
||||||
|
// default font size in pixels for all tabs
|
||||||
|
fontSize: 12,
|
||||||
|
|
||||||
|
// font family with optional fallbacks
|
||||||
|
fontFamily: 'Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace',
|
||||||
|
|
||||||
|
// default font weight: 'normal' or 'bold'
|
||||||
|
fontWeight: 'normal',
|
||||||
|
|
||||||
|
// font weight for bold characters: 'normal' or 'bold'
|
||||||
|
fontWeightBold: 'bold',
|
||||||
|
|
||||||
|
// line height as a relative unit
|
||||||
|
lineHeight: 1,
|
||||||
|
|
||||||
|
// letter spacing as a relative unit
|
||||||
|
letterSpacing: 0,
|
||||||
|
|
||||||
|
// terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)
|
||||||
|
cursorColor: 'rgba(248,28,229,0.8)',
|
||||||
|
|
||||||
|
// terminal text color under BLOCK cursor
|
||||||
|
cursorAccentColor: '#000',
|
||||||
|
|
||||||
|
// `'BEAM'` for |, `'UNDERLINE'` for _, `'BLOCK'` for █
|
||||||
|
cursorShape: 'BLOCK',
|
||||||
|
|
||||||
|
// set to `true` (without backticks and without quotes) for blinking cursor
|
||||||
|
cursorBlink: false,
|
||||||
|
|
||||||
|
// color of the text
|
||||||
|
foregroundColor: '#fff',
|
||||||
|
|
||||||
|
// terminal background color
|
||||||
|
// opacity is only supported on macOS
|
||||||
|
backgroundColor: '#000',
|
||||||
|
|
||||||
|
// terminal selection color
|
||||||
|
selectionColor: 'rgba(248,28,229,0.3)',
|
||||||
|
|
||||||
|
// border color (window, tabs)
|
||||||
|
borderColor: '#333',
|
||||||
|
|
||||||
|
// custom CSS to embed in the main window
|
||||||
|
css: '',
|
||||||
|
|
||||||
|
// custom CSS to embed in the terminal window
|
||||||
|
termCSS: '',
|
||||||
|
|
||||||
|
// set custom startup directory (must be an absolute path)
|
||||||
|
workingDirectory: '',
|
||||||
|
|
||||||
|
// if you're using a Linux setup which show native menus, set to false
|
||||||
|
// default: `true` on Linux, `true` on Windows, ignored on macOS
|
||||||
|
showHamburgerMenu: '',
|
||||||
|
|
||||||
|
// set to `false` (without backticks and without quotes) if you want to hide the minimize, maximize and close buttons
|
||||||
|
// additionally, set to `'left'` if you want them on the left, like in Ubuntu
|
||||||
|
// default: `true` (without backticks and without quotes) on Windows and Linux, ignored on macOS
|
||||||
|
showWindowControls: '',
|
||||||
|
|
||||||
|
// custom padding (CSS format, i.e.: `top right bottom left`)
|
||||||
|
padding: '12px 14px',
|
||||||
|
|
||||||
|
// the full list. if you're going to provide the full color palette,
|
||||||
|
// including the 6 x 6 color cubes and the grayscale map, just provide
|
||||||
|
// an array here instead of a color map object
|
||||||
|
colors: {
|
||||||
|
black: '#000000',
|
||||||
|
red: '#C51E14',
|
||||||
|
green: '#1DC121',
|
||||||
|
yellow: '#C7C329',
|
||||||
|
blue: '#0A2FC4',
|
||||||
|
magenta: '#C839C5',
|
||||||
|
cyan: '#20C5C6',
|
||||||
|
white: '#C7C7C7',
|
||||||
|
lightBlack: '#686868',
|
||||||
|
lightRed: '#FD6F6B',
|
||||||
|
lightGreen: '#67F86F',
|
||||||
|
lightYellow: '#FFFA72',
|
||||||
|
lightBlue: '#6A76FB',
|
||||||
|
lightMagenta: '#FD7CFC',
|
||||||
|
lightCyan: '#68FDFE',
|
||||||
|
lightWhite: '#FFFFFF',
|
||||||
|
limeGreen: '#32CD32',
|
||||||
|
lightCoral: '#F08080',
|
||||||
|
},
|
||||||
|
|
||||||
|
// the shell to run when spawning a new session (i.e. /usr/local/bin/fish)
|
||||||
|
// if left empty, your system's login shell will be used by default
|
||||||
|
//
|
||||||
|
// Windows
|
||||||
|
// - Make sure to use a full path if the binary name doesn't work
|
||||||
|
// - Remove `--login` in shellArgs
|
||||||
|
//
|
||||||
|
// Windows Subsystem for Linux (WSL) - previously Bash on Windows
|
||||||
|
// - Example: `C:\\Windows\\System32\\wsl.exe`
|
||||||
|
//
|
||||||
|
// Git-bash on Windows
|
||||||
|
// - Example: `C:\\Program Files\\Git\\bin\\bash.exe`
|
||||||
|
//
|
||||||
|
// PowerShell on Windows
|
||||||
|
// - Example: `C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
|
||||||
|
//
|
||||||
|
// Cygwin
|
||||||
|
// - Example: `C:\\cygwin64\\bin\\bash.exe`
|
||||||
|
shell: '',
|
||||||
|
|
||||||
|
// for setting shell arguments (i.e. for using interactive shellArgs: `['-i']`)
|
||||||
|
// by default `['--login']` will be used
|
||||||
|
shellArgs: ['--login'],
|
||||||
|
|
||||||
|
// for environment variables
|
||||||
|
env: {},
|
||||||
|
|
||||||
|
// Supported Options:
|
||||||
|
// 1. 'SOUND' -> Enables the bell as a sound
|
||||||
|
// 2. false: turns off the bell
|
||||||
|
bell: 'SOUND',
|
||||||
|
|
||||||
|
// An absolute file path to a sound file on the machine.
|
||||||
|
// bellSoundURL: '/path/to/sound/file',
|
||||||
|
|
||||||
|
// if `true` (without backticks and without quotes), selected text will automatically be copied to the clipboard
|
||||||
|
copyOnSelect: false,
|
||||||
|
|
||||||
|
// if `true` (without backticks and without quotes), hyper will be set as the default protocol client for SSH
|
||||||
|
defaultSSHApp: true,
|
||||||
|
|
||||||
|
// if `true` (without backticks and without quotes), on right click selected text will be copied or pasted if no
|
||||||
|
// selection is present (`true` by default on Windows and disables the context menu feature)
|
||||||
|
quickEdit: false,
|
||||||
|
|
||||||
|
// choose either `'vertical'`, if you want the column mode when Option key is hold during selection (Default)
|
||||||
|
// or `'force'`, if you want to force selection regardless of whether the terminal is in mouse events mode
|
||||||
|
// (inside tmux or vim with mouse mode enabled for example).
|
||||||
|
macOptionSelectionMode: 'vertical',
|
||||||
|
|
||||||
|
// Whether to use the WebGL renderer. Set it to false to use canvas-based
|
||||||
|
// rendering (slower, but supports transparent backgrounds)
|
||||||
|
webGLRenderer: true,
|
||||||
|
|
||||||
|
// keypress required for weblink activation: [ctrl|alt|meta|shift]
|
||||||
|
// todo: does not pick up config changes automatically, need to restart terminal :/
|
||||||
|
webLinksActivationKey: '',
|
||||||
|
|
||||||
|
// if `false` (without backticks and without quotes), Hyper will use ligatures provided by some fonts
|
||||||
|
disableLigatures: true,
|
||||||
|
|
||||||
|
// set to true to disable auto updates
|
||||||
|
disableAutoUpdates: false,
|
||||||
|
|
||||||
|
// set to true to enable screen reading apps (like NVDA) to read the contents of the terminal
|
||||||
|
screenReaderMode: false,
|
||||||
|
|
||||||
|
// set to true to preserve working directory when creating splits or tabs
|
||||||
|
preserveCWD: true,
|
||||||
|
|
||||||
|
// for advanced config flags please refer to https://hyper.is/#cfg
|
||||||
|
},
|
||||||
|
|
||||||
|
// a list of plugins to fetch and install from npm
|
||||||
|
// format: [@org/]project[#version]
|
||||||
|
// examples:
|
||||||
|
// `hyperpower`
|
||||||
|
// `@company/project`
|
||||||
|
// `project#1.0.1`
|
||||||
|
plugins: [],
|
||||||
|
|
||||||
|
// in development, you can create a directory under
|
||||||
|
// `~/.hyper_plugins/local/` and include it here
|
||||||
|
// to load it and avoid it being `npm install`ed
|
||||||
|
localPlugins: [],
|
||||||
|
|
||||||
|
keymaps: {
|
||||||
|
// Example
|
||||||
|
// 'window:devtools': 'cmd+alt+o',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "./schema.json",
|
|
||||||
"config": {
|
|
||||||
"updateChannel": "stable",
|
|
||||||
"fontSize": 12,
|
|
||||||
"fontFamily": "Menlo, \"DejaVu Sans Mono\", Consolas, \"Lucida Console\", monospace",
|
|
||||||
"fontWeight": "normal",
|
|
||||||
"fontWeightBold": "bold",
|
|
||||||
"lineHeight": 1,
|
|
||||||
"letterSpacing": 0,
|
|
||||||
"scrollback": 1000,
|
|
||||||
"cursorColor": "rgba(248,28,229,0.8)",
|
|
||||||
"cursorAccentColor": "#000",
|
|
||||||
"cursorShape": "BLOCK",
|
|
||||||
"cursorBlink": false,
|
|
||||||
"foregroundColor": "#fff",
|
|
||||||
"backgroundColor": "#000",
|
|
||||||
"selectionColor": "rgba(248,28,229,0.3)",
|
|
||||||
"borderColor": "#333",
|
|
||||||
"css": "",
|
|
||||||
"termCSS": "",
|
|
||||||
"workingDirectory": "",
|
|
||||||
"showHamburgerMenu": "",
|
|
||||||
"showWindowControls": "",
|
|
||||||
"padding": "12px 14px",
|
|
||||||
"colors": {
|
|
||||||
"black": "#000000",
|
|
||||||
"red": "#C51E14",
|
|
||||||
"green": "#1DC121",
|
|
||||||
"yellow": "#C7C329",
|
|
||||||
"blue": "#0A2FC4",
|
|
||||||
"magenta": "#C839C5",
|
|
||||||
"cyan": "#20C5C6",
|
|
||||||
"white": "#C7C7C7",
|
|
||||||
"lightBlack": "#686868",
|
|
||||||
"lightRed": "#FD6F6B",
|
|
||||||
"lightGreen": "#67F86F",
|
|
||||||
"lightYellow": "#FFFA72",
|
|
||||||
"lightBlue": "#6A76FB",
|
|
||||||
"lightMagenta": "#FD7CFC",
|
|
||||||
"lightCyan": "#68FDFE",
|
|
||||||
"lightWhite": "#FFFFFF",
|
|
||||||
"limeGreen": "#32CD32",
|
|
||||||
"lightCoral": "#F08080"
|
|
||||||
},
|
|
||||||
"shell": "",
|
|
||||||
"shellArgs": [
|
|
||||||
"--login"
|
|
||||||
],
|
|
||||||
"env": {},
|
|
||||||
"bell": "SOUND",
|
|
||||||
"bellSound": null,
|
|
||||||
"bellSoundURL": null,
|
|
||||||
"copyOnSelect": false,
|
|
||||||
"defaultSSHApp": true,
|
|
||||||
"quickEdit": false,
|
|
||||||
"macOptionSelectionMode": "vertical",
|
|
||||||
"webGLRenderer": false,
|
|
||||||
"webLinksActivationKey": "",
|
|
||||||
"disableLigatures": true,
|
|
||||||
"disableAutoUpdates": false,
|
|
||||||
"autoUpdatePlugins": true,
|
|
||||||
"preserveCWD": true,
|
|
||||||
"screenReaderMode": false,
|
|
||||||
"imageSupport": true,
|
|
||||||
"defaultProfile": "default",
|
|
||||||
"profiles": [
|
|
||||||
{
|
|
||||||
"name": "default",
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": [],
|
|
||||||
"localPlugins": [],
|
|
||||||
"keymaps": {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +1,100 @@
|
||||||
import {readFileSync, mkdirpSync} from 'fs-extra';
|
import {moveSync, copySync, existsSync, writeFileSync, readFileSync, lstatSync} from 'fs-extra';
|
||||||
|
import {sync as mkdirpSync} from 'mkdirp';
|
||||||
import type {rawConfig} from '../../typings/config';
|
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
|
||||||
|
import {_init, _extractDefault} from './init';
|
||||||
import notify from '../notify';
|
import notify from '../notify';
|
||||||
|
import {rawConfig} from '../../lib/config';
|
||||||
import {_init} from './init';
|
|
||||||
import {migrateHyper3Config} from './migrate';
|
|
||||||
import {defaultCfg, cfgPath, plugs, defaultPlatformKeyPath} from './paths';
|
|
||||||
|
|
||||||
let defaultConfig: rawConfig;
|
let defaultConfig: rawConfig;
|
||||||
|
|
||||||
|
const _write = (path: string, data: string) => {
|
||||||
|
// This method will take text formatted as Unix line endings and transform it
|
||||||
|
// to text formatted with DOS line endings. We do this because the default
|
||||||
|
// text editor on Windows (notepad) doesn't Deal with LF files. Still. In 2017.
|
||||||
|
const crlfify = (str: string) => {
|
||||||
|
return str.replace(/\r?\n/g, '\r\n');
|
||||||
|
};
|
||||||
|
const format = process.platform === 'win32' ? crlfify(data.toString()) : data;
|
||||||
|
writeFileSync(path, format, 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Saves a file as backup by appending '.backup' or '.backup2', '.backup3', etc.
|
||||||
|
// so as to not override any existing files
|
||||||
|
const saveAsBackup = (src: string) => {
|
||||||
|
let attempt = 1;
|
||||||
|
while (attempt < 100) {
|
||||||
|
const backupPath = `${src}.backup${attempt === 1 ? '' : attempt}`;
|
||||||
|
if (!existsSync(backupPath)) {
|
||||||
|
moveSync(src, backupPath);
|
||||||
|
return backupPath;
|
||||||
|
}
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
throw new Error('Failed to create backup for config file. Too many backups');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Migrate Hyper2 config to Hyper3 but only if the user hasn't manually
|
||||||
|
// touched the new config and if the old config is not a symlink
|
||||||
|
const migrateHyper2Config = () => {
|
||||||
|
if (cfgPath === legacyCfgPath) {
|
||||||
|
// No need to migrate
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!existsSync(legacyCfgPath)) {
|
||||||
|
// Already migrated or user never used Hyper 2
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const existsNew = existsSync(cfgPath);
|
||||||
|
if (lstatSync(legacyCfgPath).isSymbolicLink() || (existsNew && lstatSync(cfgPath).isSymbolicLink())) {
|
||||||
|
// One of the files is a symlink, there could be a number of complications
|
||||||
|
// in this case so let's avoid those and not do automatic migration
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsNew) {
|
||||||
|
const cfg1 = readFileSync(defaultCfg, 'utf8').replace(/\r|\n/g, '');
|
||||||
|
const cfg2 = readFileSync(cfgPath, 'utf8').replace(/\r|\n/g, '');
|
||||||
|
const hasNewConfigBeenTouched = cfg1 !== cfg2;
|
||||||
|
if (hasNewConfigBeenTouched) {
|
||||||
|
// Assume the user has migrated manually but rename old config to .backup so
|
||||||
|
// we don't keep trying to migrate on every launch
|
||||||
|
const backupPath = saveAsBackup(legacyCfgPath);
|
||||||
|
notify(
|
||||||
|
'Hyper 3',
|
||||||
|
`Settings location has changed to ${cfgPath}.\nWe've backed up your old Hyper config to ${backupPath}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate
|
||||||
|
copySync(legacyCfgPath, cfgPath);
|
||||||
|
saveAsBackup(legacyCfgPath);
|
||||||
|
|
||||||
|
notify(
|
||||||
|
'Hyper 3',
|
||||||
|
`Settings location has changed to ${cfgPath}.\nWe've automatically migrated your existing config!\nPlease restart Hyper now`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const _importConf = () => {
|
const _importConf = () => {
|
||||||
// init plugin directories if not present
|
// init plugin directories if not present
|
||||||
mkdirpSync(plugs.base);
|
mkdirpSync(plugs.base);
|
||||||
mkdirpSync(plugs.local);
|
mkdirpSync(plugs.local);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
migrateHyper3Config();
|
migrateHyper2Config();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultCfgRaw = '{}';
|
let defaultCfgRaw = '';
|
||||||
try {
|
try {
|
||||||
defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
|
defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
const _defaultCfg = JSON.parse(defaultCfgRaw) as rawConfig;
|
const _defaultCfg = _extractDefault(defaultCfgRaw) as rawConfig;
|
||||||
|
|
||||||
// Importing platform specific keymap
|
// Importing platform specific keymap
|
||||||
let content = '{}';
|
let content = '{}';
|
||||||
|
|
@ -39,12 +107,12 @@ const _importConf = () => {
|
||||||
_defaultCfg.keymaps = mapping;
|
_defaultCfg.keymaps = mapping;
|
||||||
|
|
||||||
// Import user config
|
// Import user config
|
||||||
let userCfg: rawConfig;
|
let userCfg: string;
|
||||||
try {
|
try {
|
||||||
userCfg = JSON.parse(readFileSync(cfgPath, 'utf8'));
|
userCfg = readFileSync(cfgPath, 'utf8');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify("Couldn't parse config file. Using default config instead.");
|
_write(cfgPath, defaultCfgRaw);
|
||||||
userCfg = JSON.parse(defaultCfgRaw);
|
userCfg = defaultCfgRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {userCfg, defaultCfg: _defaultCfg};
|
return {userCfg, defaultCfg: _defaultCfg};
|
||||||
|
|
@ -53,7 +121,7 @@ const _importConf = () => {
|
||||||
export const _import = () => {
|
export const _import = () => {
|
||||||
const imported = _importConf();
|
const imported = _importConf();
|
||||||
defaultConfig = imported.defaultCfg;
|
defaultConfig = imported.defaultCfg;
|
||||||
const result = _init(imported.userCfg, imported.defaultCfg);
|
const result = _init(imported);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import vm from 'vm';
|
import vm from 'vm';
|
||||||
|
|
||||||
import merge from 'lodash/merge';
|
|
||||||
|
|
||||||
import type {parsedConfig, rawConfig, configOptions} from '../../typings/config';
|
|
||||||
import notify from '../notify';
|
import notify from '../notify';
|
||||||
import mapKeys from '../utils/map-keys';
|
import mapKeys from '../utils/map-keys';
|
||||||
|
import {parsedConfig, rawConfig, configOptions} from '../../lib/config';
|
||||||
|
|
||||||
const _extract = (script?: vm.Script): Record<string, any> => {
|
const _extract = (script?: vm.Script): Record<string, any> => {
|
||||||
const module: Record<string, any> = {};
|
const module: Record<string, any> = {};
|
||||||
script?.runInNewContext({module}, {displayErrors: true});
|
script?.runInNewContext({module});
|
||||||
if (!module.exports) {
|
if (!module.exports) {
|
||||||
throw new Error('Error reading configuration: `module.exports` not set');
|
throw new Error('Error reading configuration: `module.exports` not set');
|
||||||
}
|
}
|
||||||
|
|
@ -18,10 +15,10 @@ const _extract = (script?: vm.Script): Record<string, any> => {
|
||||||
|
|
||||||
const _syntaxValidation = (cfg: string) => {
|
const _syntaxValidation = (cfg: string) => {
|
||||||
try {
|
try {
|
||||||
return new vm.Script(cfg, {filename: '.hyper.js'});
|
return new vm.Script(cfg, {filename: '.hyper.js', displayErrors: true});
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
const err = _err as {name: string};
|
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});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -30,33 +27,23 @@ const _extractDefault = (cfg: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// init config
|
// init config
|
||||||
const _init = (userCfg: rawConfig, defaultCfg: rawConfig): parsedConfig => {
|
const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => {
|
||||||
|
const script = _syntaxValidation(cfg.userCfg);
|
||||||
|
const _cfg = script && (_extract(script) as rawConfig);
|
||||||
return {
|
return {
|
||||||
config: (() => {
|
config: (() => {
|
||||||
if (userCfg?.config) {
|
if (_cfg?.config) {
|
||||||
const conf = userCfg.config;
|
return _cfg.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);
|
|
||||||
} else {
|
} else {
|
||||||
notify('Error reading configuration: `config` key is missing');
|
notify('Error reading configuration: `config` key is missing');
|
||||||
return defaultCfg.config || ({} as configOptions);
|
return cfg.defaultCfg.config || ({} as configOptions);
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
// Merging platform specific keymaps with user defined keymaps
|
// Merging platform specific keymaps with user defined keymaps
|
||||||
keymaps: mapKeys({...defaultCfg.keymaps, ...userCfg?.keymaps}),
|
keymaps: mapKeys({...cfg.defaultCfg.keymaps, ..._cfg?.keymaps}),
|
||||||
// Ignore undefined values in plugin and localPlugins array Issue #1862
|
// Ignore undefined values in plugin and localPlugins array Issue #1862
|
||||||
plugins: userCfg?.plugins?.filter(Boolean) || [],
|
plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [],
|
||||||
localPlugins: userCfg?.localPlugins?.filter(Boolean) || []
|
localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || []
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
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 * as babelParser from 'recast/parsers/babel';
|
|
||||||
|
|
||||||
import notify from '../notify';
|
|
||||||
|
|
||||||
import {_extractDefault} from './init';
|
|
||||||
import {cfgDir, cfgPath, defaultCfg, legacyCfgPath, plugs, schemaFile, schemaPath} from './paths';
|
|
||||||
|
|
||||||
// function to remove all json serializable entries from an array expression
|
|
||||||
function removeElements(node: namedTypes.ArrayExpression): namedTypes.ArrayExpression {
|
|
||||||
const newElements = node.elements.filter((element) => {
|
|
||||||
if (namedTypes.ObjectExpression.check(element)) {
|
|
||||||
const newElement = removeProperties(element);
|
|
||||||
if (newElement.properties.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (namedTypes.ArrayExpression.check(element)) {
|
|
||||||
const newElement = removeElements(element);
|
|
||||||
if (newElement.elements.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (namedTypes.Literal.check(element)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
return {...node, elements: newElements};
|
|
||||||
}
|
|
||||||
|
|
||||||
// function to remove all json serializable properties from an object expression
|
|
||||||
function removeProperties(node: namedTypes.ObjectExpression): namedTypes.ObjectExpression {
|
|
||||||
const newProperties = node.properties.filter((property) => {
|
|
||||||
if (
|
|
||||||
namedTypes.ObjectProperty.check(property) &&
|
|
||||||
(namedTypes.Literal.check(property.key) || namedTypes.Identifier.check(property.key)) &&
|
|
||||||
!property.computed
|
|
||||||
) {
|
|
||||||
if (namedTypes.ObjectExpression.check(property.value)) {
|
|
||||||
const newValue = removeProperties(property.value);
|
|
||||||
if (newValue.properties.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (namedTypes.ArrayExpression.check(property.value)) {
|
|
||||||
const newValue = removeElements(property.value);
|
|
||||||
if (newValue.elements.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (namedTypes.Literal.check(property.value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
return {...node, properties: newProperties};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function configToPlugin(code: string): string {
|
|
||||||
const ast: namedTypes.File = parse(code, {
|
|
||||||
parser: babelParser
|
|
||||||
});
|
|
||||||
const statements = ast.program.body;
|
|
||||||
let moduleExportsNode: namedTypes.AssignmentExpression | null = null;
|
|
||||||
let configNode: ExpressionKind | null = null;
|
|
||||||
|
|
||||||
for (const statement of statements) {
|
|
||||||
if (namedTypes.ExpressionStatement.check(statement)) {
|
|
||||||
const expression = statement.expression;
|
|
||||||
if (
|
|
||||||
namedTypes.AssignmentExpression.check(expression) &&
|
|
||||||
expression.operator === '=' &&
|
|
||||||
namedTypes.MemberExpression.check(expression.left) &&
|
|
||||||
namedTypes.Identifier.check(expression.left.object) &&
|
|
||||||
expression.left.object.name === 'module' &&
|
|
||||||
namedTypes.Identifier.check(expression.left.property) &&
|
|
||||||
expression.left.property.name === 'exports'
|
|
||||||
) {
|
|
||||||
moduleExportsNode = expression;
|
|
||||||
if (namedTypes.ObjectExpression.check(expression.right)) {
|
|
||||||
const properties = expression.right.properties;
|
|
||||||
for (const property of properties) {
|
|
||||||
if (
|
|
||||||
namedTypes.ObjectProperty.check(property) &&
|
|
||||||
namedTypes.Identifier.check(property.key) &&
|
|
||||||
property.key.name === 'config'
|
|
||||||
) {
|
|
||||||
configNode = property.value as ExpressionKind;
|
|
||||||
if (namedTypes.ObjectExpression.check(property.value)) {
|
|
||||||
configNode = removeProperties(property.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
configNode = builders.memberExpression(moduleExportsNode.right, builders.identifier('config'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!moduleExportsNode) {
|
|
||||||
console.log('No module.exports found in config');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (!configNode) {
|
|
||||||
console.log('No config field found in module.exports');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (namedTypes.ObjectExpression.check(configNode) && configNode.properties.length === 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleExportsNode.right = builders.objectExpression([
|
|
||||||
builders.property(
|
|
||||||
'init',
|
|
||||||
builders.identifier('decorateConfig'),
|
|
||||||
builders.arrowFunctionExpression(
|
|
||||||
[builders.identifier('_config')],
|
|
||||||
builders.callExpression(
|
|
||||||
builders.memberExpression(builders.identifier('Object'), builders.identifier('assign')),
|
|
||||||
[builders.objectExpression([]), builders.identifier('_config'), configNode]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return prettyPrint(ast, {tabWidth: 2}).code;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const _write = (path: string, data: string) => {
|
|
||||||
// This method will take text formatted as Unix line endings and transform it
|
|
||||||
// to text formatted with DOS line endings. We do this because the default
|
|
||||||
// text editor on Windows (notepad) doesn't Deal with LF files. Still. In 2017.
|
|
||||||
const crlfify = (str: string) => {
|
|
||||||
return str.replace(/\r?\n/g, '\r\n');
|
|
||||||
};
|
|
||||||
const format = process.platform === 'win32' ? crlfify(data.toString()) : data;
|
|
||||||
writeFileSync(path, format, 'utf8');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Migrate Hyper3 config to Hyper4 but only if the user hasn't manually
|
|
||||||
// touched the new config and if the old config is not a symlink
|
|
||||||
export const migrateHyper3Config = () => {
|
|
||||||
copy(schemaPath, resolve(cfgDir, schemaFile), (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existsSync(cfgPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existsSync(legacyCfgPath)) {
|
|
||||||
copySync(defaultCfg, cfgPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate
|
|
||||||
copySync(resolve(dirname(legacyCfgPath), '.hyper_plugins', 'local'), plugs.local);
|
|
||||||
|
|
||||||
const defaultCfgData = JSON.parse(readFileSync(defaultCfg, 'utf8'));
|
|
||||||
let newCfgData;
|
|
||||||
try {
|
|
||||||
const legacyCfgRaw = readFileSync(legacyCfgPath, 'utf8');
|
|
||||||
const legacyCfgData = _extractDefault(legacyCfgRaw);
|
|
||||||
newCfgData = merge({}, defaultCfgData, legacyCfgData);
|
|
||||||
|
|
||||||
const pluginCode = configToPlugin(legacyCfgRaw);
|
|
||||||
if (pluginCode) {
|
|
||||||
const pluginPath = resolve(plugs.local, 'migrated-hyper3-config.js');
|
|
||||||
newCfgData.localPlugins = ['migrated-hyper3-config', ...(newCfgData.localPlugins || [])];
|
|
||||||
_write(pluginPath, pluginCode);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
notify(
|
|
||||||
'Hyper 4',
|
|
||||||
`Failed to migrate your config from Hyper 3.\nDefault config will be created instead at ${cfgPath}`
|
|
||||||
);
|
|
||||||
newCfgData = defaultCfgData;
|
|
||||||
}
|
|
||||||
_write(cfgPath, JSON.stringify(newCfgData, null, 2));
|
|
||||||
|
|
||||||
notify('Hyper 4', `Settings location and format has changed to ${cfgPath}`);
|
|
||||||
};
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import {exec} from 'child_process';
|
|
||||||
|
|
||||||
import {shell} from 'electron';
|
import {shell} from 'electron';
|
||||||
|
|
||||||
import * as Registry from 'native-reg';
|
|
||||||
|
|
||||||
import {cfgPath} from './paths';
|
import {cfgPath} from './paths';
|
||||||
|
import * as Registry from 'native-reg';
|
||||||
|
import {exec} from 'child_process';
|
||||||
|
|
||||||
const getUserChoiceKey = () => {
|
const getUserChoiceKey = () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -60,7 +57,7 @@ const openNotepad = (file: string) =>
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const openConfig = () => {
|
export default () => {
|
||||||
// Windows opens .js files with WScript.exe by 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 the user hasn't set up an editor for .js files, we fallback to notepad.
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
|
|
@ -76,5 +73,3 @@ const openConfig = () => {
|
||||||
}
|
}
|
||||||
return shell.openPath(cfgPath).then((error) => error === '');
|
return shell.openPath(cfgPath).then((error) => error === '');
|
||||||
};
|
};
|
||||||
|
|
||||||
export default openConfig;
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,26 @@
|
||||||
// This module exports paths, names, and other metadata that is referenced
|
// This module exports paths, names, and other metadata that is referenced
|
||||||
import {statSync} from 'fs';
|
|
||||||
import {homedir} from 'os';
|
import {homedir} from 'os';
|
||||||
import {resolve, join} from 'path';
|
|
||||||
|
|
||||||
import {app} from 'electron';
|
import {app} from 'electron';
|
||||||
|
import {statSync} from 'fs';
|
||||||
|
import {resolve, join} from 'path';
|
||||||
import isDev from 'electron-is-dev';
|
import isDev from 'electron-is-dev';
|
||||||
|
|
||||||
const cfgFile = 'hyper.json';
|
const cfgFile = '.hyper.js';
|
||||||
const defaultCfgFile = 'config-default.json';
|
const defaultCfgFile = 'config-default.js';
|
||||||
const schemaFile = 'schema.json';
|
|
||||||
const homeDirectory = homedir();
|
const homeDirectory = homedir();
|
||||||
|
|
||||||
// If the user defines XDG_CONFIG_HOME they definitely want their config there,
|
// 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
|
// otherwise use the home directory in linux/mac and userdata in windows
|
||||||
let cfgDir = process.env.XDG_CONFIG_HOME
|
const applicationDirectory =
|
||||||
? join(process.env.XDG_CONFIG_HOME, 'Hyper')
|
|
||||||
: process.platform === 'win32'
|
|
||||||
? app.getPath('userData')
|
|
||||||
: join(homeDirectory, '.config', 'Hyper');
|
|
||||||
|
|
||||||
const legacyCfgPath = join(
|
|
||||||
process.env.XDG_CONFIG_HOME !== undefined
|
process.env.XDG_CONFIG_HOME !== undefined
|
||||||
? join(process.env.XDG_CONFIG_HOME, 'hyper')
|
? join(process.env.XDG_CONFIG_HOME, 'hyper')
|
||||||
: process.platform == 'win32'
|
: process.platform == 'win32'
|
||||||
? app.getPath('userData')
|
? app.getPath('userData')
|
||||||
: homedir(),
|
: homedir();
|
||||||
'.hyper.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
let cfgPath = join(cfgDir, cfgFile);
|
let cfgDir = applicationDirectory;
|
||||||
const schemaPath = resolve(__dirname, schemaFile);
|
let cfgPath = join(applicationDirectory, cfgFile);
|
||||||
|
const legacyCfgPath = join(homeDirectory, cfgFile); // Hyper 2 config location
|
||||||
|
|
||||||
const devDir = resolve(__dirname, '../..');
|
const devDir = resolve(__dirname, '../..');
|
||||||
const devCfg = join(devDir, cfgFile);
|
const devCfg = join(devDir, cfgFile);
|
||||||
|
|
@ -48,8 +38,10 @@ if (isDev) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins = resolve(cfgDir, 'plugins');
|
const plugins = resolve(cfgDir, '.hyper_plugins');
|
||||||
const plugs = {
|
const plugs = {
|
||||||
|
legacyBase: resolve(homeDirectory, '.hyper_plugins'),
|
||||||
|
legacyLocal: resolve(homeDirectory, '.hyper_plugins', 'local'),
|
||||||
base: plugins,
|
base: plugins,
|
||||||
local: resolve(plugins, 'local'),
|
local: resolve(plugins, 'local'),
|
||||||
cache: resolve(plugins, 'cache')
|
cache: resolve(plugins, 'cache')
|
||||||
|
|
@ -90,7 +82,5 @@ export {
|
||||||
yarn,
|
yarn,
|
||||||
cliScriptPath,
|
cliScriptPath,
|
||||||
cliLinkPath,
|
cliLinkPath,
|
||||||
homeDirectory,
|
homeDirectory
|
||||||
schemaFile,
|
|
||||||
schemaPath
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,756 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"definitions": {
|
|
||||||
"FontWeight": {
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"enum": [
|
|
||||||
"100",
|
|
||||||
"200",
|
|
||||||
"300",
|
|
||||||
"400",
|
|
||||||
"500",
|
|
||||||
"600",
|
|
||||||
"700",
|
|
||||||
"800",
|
|
||||||
"900",
|
|
||||||
"bold",
|
|
||||||
"normal"
|
|
||||||
],
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "A string or number representing text font weight."
|
|
||||||
},
|
|
||||||
"Partial<profileConfigOptions>": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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<profileConfigOptions>",
|
|
||||||
"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": [
|
|
||||||
{
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"description": "Example\n'window:devtools': 'cmd+alt+o',",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"localPlugins": {
|
|
||||||
"description": "in development, you can create a directory under\n`plugins/local/` and include it here\nto load it and avoid it being `npm install`ed",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"description": "a list of plugins to fetch and install from npm\nformat: [@org/]project[#version]\nexamples:\n `hyperpower`\n `@company/project`\n `project#1.0.1`",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import type {BrowserWindow} from 'electron';
|
|
||||||
|
|
||||||
import Config from 'electron-store';
|
import Config from 'electron-store';
|
||||||
|
import {BrowserWindow} from 'electron';
|
||||||
|
|
||||||
export const defaults = {
|
export const defaults = {
|
||||||
windowPosition: [50, 50] as [number, number],
|
windowPosition: [50, 50] as [number, number],
|
||||||
|
|
|
||||||
8
app/ext-modules.d.ts
vendored
Normal file
8
app/ext-modules.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
declare module 'git-describe' {
|
||||||
|
export function gitDescribe(...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'default-shell' {
|
||||||
|
const val: string;
|
||||||
|
export default val;
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import type {Server} from '../app/rpc';
|
import type {Server} from './rpc';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Electron {
|
namespace Electron {
|
||||||
interface App {
|
interface App {
|
||||||
config: typeof import('../app/config');
|
config: typeof import('./config');
|
||||||
plugins: typeof import('../app/plugins');
|
plugins: typeof import('./plugins');
|
||||||
getWindows: () => Set<BrowserWindow>;
|
getWindows: () => Set<BrowserWindow>;
|
||||||
getLastFocusedWindow: () => BrowserWindow | null;
|
getLastFocusedWindow: () => BrowserWindow | null;
|
||||||
windowCallback?: (win: BrowserWindow) => void;
|
windowCallback?: (win: BrowserWindow) => void;
|
||||||
createWindow: (
|
createWindow: (
|
||||||
fn?: (win: BrowserWindow) => void,
|
fn?: (win: BrowserWindow) => void,
|
||||||
options?: {size?: [number, number]; position?: [number, number]},
|
options?: {size?: [number, number]; position?: [number, number]}
|
||||||
profileName?: string
|
|
||||||
) => BrowserWindow;
|
) => BrowserWindow;
|
||||||
setVersion: (version: string) => void;
|
setVersion: (version: string) => void;
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +22,6 @@ declare global {
|
||||||
focusTime: number;
|
focusTime: number;
|
||||||
clean: () => void;
|
clean: () => void;
|
||||||
rpc: Server;
|
rpc: Server;
|
||||||
profileName: string;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
app/index.d.ts
vendored
Normal file
1
app/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
// Dummy file, required by tsc
|
||||||
47
app/index.ts
47
app/index.ts
|
|
@ -1,41 +1,37 @@
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import {cfgPath} from './config/paths';
|
|
||||||
|
|
||||||
// Print diagnostic information for a few arguments instead of running Hyper.
|
// Print diagnostic information for a few arguments instead of running Hyper.
|
||||||
if (['--help', '-v', '--version'].includes(process.argv[1])) {
|
if (['--help', '-v', '--version'].includes(process.argv[1])) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const {version} = require('./package');
|
const {version} = require('./package');
|
||||||
|
const configLocation = process.platform === 'win32' ? `${process.env.userprofile}\\.hyper.js` : '~/.hyper.js';
|
||||||
console.log(`Hyper version ${version}`);
|
console.log(`Hyper version ${version}`);
|
||||||
console.log('Hyper does not accept any command line arguments. Please modify the config file instead.');
|
console.log('Hyper does not accept any command line arguments. Please modify the config file instead.');
|
||||||
console.log(`Hyper configuration file located at: ${cfgPath}`);
|
console.log(`Hyper configuration file located at: ${configLocation}`);
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable remote module
|
// Enable remote module
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import {initialize as remoteInitialize} from '@electron/remote/main';
|
import {initialize as remoteInitialize} from '@electron/remote/main';
|
||||||
remoteInitialize();
|
remoteInitialize();
|
||||||
|
|
||||||
// set up config
|
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import * as config from './config';
|
|
||||||
config.setup();
|
|
||||||
|
|
||||||
// Native
|
// Native
|
||||||
import {resolve} from 'path';
|
import {resolve} from 'path';
|
||||||
|
|
||||||
// Packages
|
// Packages
|
||||||
import {app, BrowserWindow, Menu, screen} from 'electron';
|
import {app, BrowserWindow, Menu, screen} from 'electron';
|
||||||
|
|
||||||
import isDev from 'electron-is-dev';
|
|
||||||
import {gitDescribe} from 'git-describe';
|
import {gitDescribe} from 'git-describe';
|
||||||
import parseUrl from 'parse-url';
|
import isDev from 'electron-is-dev';
|
||||||
|
import * as config from './config';
|
||||||
|
|
||||||
import * as AppMenu from './menus/menu';
|
// set up config
|
||||||
|
config.setup();
|
||||||
|
|
||||||
|
import notify from './notify';
|
||||||
import * as plugins from './plugins';
|
import * as plugins from './plugins';
|
||||||
import {newWindow} from './ui/window';
|
|
||||||
import {installCLI} from './utils/cli-install';
|
import {installCLI} from './utils/cli-install';
|
||||||
|
import * as AppMenu from './menus/menu';
|
||||||
|
import {newWindow} from './ui/window';
|
||||||
import * as windowUtils from './utils/window-utils';
|
import * as windowUtils from './utils/window-utils';
|
||||||
|
import parseUrl from 'parse-url';
|
||||||
|
|
||||||
const windowSet = new Set<BrowserWindow>([]);
|
const windowSet = new Set<BrowserWindow>([]);
|
||||||
|
|
||||||
|
|
@ -62,7 +58,7 @@ if (isDev) {
|
||||||
console.log('running in dev mode');
|
console.log('running in dev mode');
|
||||||
|
|
||||||
// Override default appVersion which is set from package.json
|
// 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) {
|
if (!error) {
|
||||||
app.setVersion(gitInfo.raw);
|
app.setVersion(gitInfo.raw);
|
||||||
}
|
}
|
||||||
|
|
@ -78,13 +74,15 @@ async function installDevExtensions(isDev_: boolean) {
|
||||||
if (!isDev_) {
|
if (!isDev_) {
|
||||||
return [];
|
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);
|
const forceDownload = Boolean(process.env.UPGRADE_EXTENSIONS);
|
||||||
|
|
||||||
return Promise.all(
|
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 +92,9 @@ app.on('ready', () =>
|
||||||
.then(() => {
|
.then(() => {
|
||||||
function createWindow(
|
function createWindow(
|
||||||
fn?: (win: BrowserWindow) => void,
|
fn?: (win: BrowserWindow) => void,
|
||||||
options: {size?: [number, number]; position?: [number, number]} = {},
|
options: {size?: [number, number]; position?: [number, number]} = {}
|
||||||
profileName: string = config.getDefaultProfile()
|
|
||||||
) {
|
) {
|
||||||
const cfg = plugins.getDecoratedConfig(profileName);
|
const cfg = plugins.getDecoratedConfig();
|
||||||
|
|
||||||
const winSet = config.getWin();
|
const winSet = config.getWin();
|
||||||
let [startX, startY] = winSet.position;
|
let [startX, startY] = winSet.position;
|
||||||
|
|
@ -139,7 +136,7 @@ app.on('ready', () =>
|
||||||
[startX, startY] = config.windowDefaults.windowPosition;
|
[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);
|
windowSet.add(hwin);
|
||||||
void hwin.loadURL(url);
|
void hwin.loadURL(url);
|
||||||
|
|
||||||
|
|
@ -238,6 +235,10 @@ app.on('open-file', (_event, path) => {
|
||||||
|
|
||||||
app.on('open-url', (_event, sshUrl) => {
|
app.on('open-url', (_event, sshUrl) => {
|
||||||
GetWindow((win: BrowserWindow) => {
|
GetWindow((win: BrowserWindow) => {
|
||||||
|
try {
|
||||||
win.rpc.emit('open ssh', parseUrl(sshUrl));
|
win.rpc.emit('open ssh', parseUrl(sshUrl));
|
||||||
|
} catch (e) {
|
||||||
|
notify('Invalid ssh url', 'Please check your ssh url and try again.', {error: e});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
// Packages
|
// Packages
|
||||||
import {app, dialog, Menu} from 'electron';
|
import {app, dialog, Menu, BrowserWindow} from 'electron';
|
||||||
import type {BrowserWindow} from 'electron';
|
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
import {execCommand} from '../commands';
|
|
||||||
import {getConfig} from '../config';
|
import {getConfig} from '../config';
|
||||||
import {icon} from '../config/paths';
|
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 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 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 appName = app.name;
|
||||||
const appVersion = app.getVersion();
|
const appVersion = app.getVersion();
|
||||||
|
|
@ -63,11 +61,7 @@ export const createMenu = (
|
||||||
};
|
};
|
||||||
const menu = [
|
const menu = [
|
||||||
...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []),
|
...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []),
|
||||||
shellMenu(
|
shellMenu(commandKeys, execCommand),
|
||||||
commandKeys,
|
|
||||||
execCommand,
|
|
||||||
getConfig().profiles.map((p) => p.name)
|
|
||||||
),
|
|
||||||
editMenu(commandKeys, execCommand),
|
editMenu(commandKeys, execCommand),
|
||||||
viewMenu(commandKeys, execCommand),
|
viewMenu(commandKeys, execCommand),
|
||||||
toolsMenu(commandKeys, execCommand),
|
toolsMenu(commandKeys, execCommand),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
// This menu label is overrided by OSX to be the appName
|
// This menu label is overrided by OSX to be the appName
|
||||||
// The label is set to appName here so it matches actual behavior
|
// The label is set to appName here so it matches actual behavior
|
||||||
import {app} from 'electron';
|
import {app, BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
|
||||||
|
|
||||||
const darwinMenu = (
|
export default (
|
||||||
commandKeys: Record<string, string>,
|
commandKeys: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void,
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void,
|
||||||
showAbout: () => void
|
showAbout: () => void
|
||||||
|
|
@ -55,5 +54,3 @@ const darwinMenu = (
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default darwinMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
const editMenu = (
|
export default (
|
||||||
commandKeys: Record<string, string>,
|
commandKeys: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -147,5 +147,3 @@ const editMenu = (
|
||||||
submenu
|
submenu
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default editMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import {release} from 'os';
|
import {release} from 'os';
|
||||||
|
import {app, shell, MenuItemConstructorOptions, dialog, clipboard} from 'electron';
|
||||||
import {app, shell, dialog, clipboard} from 'electron';
|
|
||||||
import type {MenuItemConstructorOptions} from 'electron';
|
|
||||||
|
|
||||||
import {getConfig, getPlugins} from '../../config';
|
import {getConfig, getPlugins} from '../../config';
|
||||||
|
const {arch, env, platform, versions} = process;
|
||||||
import {version} from '../../package.json';
|
import {version} from '../../package.json';
|
||||||
|
|
||||||
const {arch, env, platform, versions} = process;
|
export default (commands: Record<string, string>, showAbout: () => void): MenuItemConstructorOptions => {
|
||||||
|
|
||||||
const helpMenu = (commands: Record<string, string>, showAbout: () => void): MenuItemConstructorOptions => {
|
|
||||||
const submenu: MenuItemConstructorOptions[] = [
|
const submenu: MenuItemConstructorOptions[] = [
|
||||||
{
|
{
|
||||||
label: `${app.name} Website`,
|
label: `${app.name} Website`,
|
||||||
|
|
@ -43,12 +39,12 @@ const helpMenu = (commands: Record<string, string>, showAbout: () => void): Menu
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- hyper.json config -->
|
<!-- ~/.hyper.js config -->
|
||||||
- **${app.name} version**: ${env.TERM_PROGRAM_VERSION} "${app.getVersion()}"
|
- **${app.name} version**: ${env.TERM_PROGRAM_VERSION} "${app.getVersion()}"
|
||||||
- **OS ARCH VERSION:** ${platform} ${arch} ${release()}
|
- **OS ARCH VERSION:** ${platform} ${arch} ${release()}
|
||||||
- **Electron:** ${versions.electron} **LANG:** ${env.LANG}
|
- **Electron:** ${versions.electron} **LANG:** ${env.LANG}
|
||||||
- **SHELL:** ${env.SHELL} **TERM:** ${env.TERM}
|
- **SHELL:** ${env.SHELL} **TERM:** ${env.TERM}
|
||||||
<details><summary><strong>hyper.json contents</strong></summary>
|
<details><summary><strong>.hyper.js contents</strong></summary>
|
||||||
|
|
||||||
\`\`\`json
|
\`\`\`json
|
||||||
${JSON.stringify(getConfig(), null, 2)}
|
${JSON.stringify(getConfig(), null, 2)}
|
||||||
|
|
@ -110,5 +106,3 @@ ${JSON.stringify(getPlugins(), null, 2)}
|
||||||
submenu
|
submenu
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default helpMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
const shellMenu = (
|
export default (
|
||||||
commandKeys: Record<string, string>,
|
commandKeys: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void,
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
||||||
profiles: string[]
|
|
||||||
): MenuItemConstructorOptions => {
|
): MenuItemConstructorOptions => {
|
||||||
const isMac = process.platform === 'darwin';
|
const isMac = process.platform === 'darwin';
|
||||||
|
|
||||||
|
|
@ -44,47 +43,6 @@ const shellMenu = (
|
||||||
{
|
{
|
||||||
type: 'separator'
|
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',
|
label: 'Close',
|
||||||
accelerator: commandKeys['pane:close'],
|
accelerator: commandKeys['pane:close'],
|
||||||
|
|
@ -100,5 +58,3 @@ const shellMenu = (
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default shellMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
const toolsMenu = (
|
export default (
|
||||||
commands: Record<string, string>,
|
commands: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
||||||
): MenuItemConstructorOptions => {
|
): MenuItemConstructorOptions => {
|
||||||
|
|
@ -45,5 +45,3 @@ const toolsMenu = (
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default toolsMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
const viewMenu = (
|
export default (
|
||||||
commandKeys: Record<string, string>,
|
commandKeys: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
||||||
): MenuItemConstructorOptions => {
|
): MenuItemConstructorOptions => {
|
||||||
|
|
@ -55,5 +55,3 @@ const viewMenu = (
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default viewMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
const windowMenu = (
|
export default (
|
||||||
commandKeys: Record<string, string>,
|
commandKeys: Record<string, string>,
|
||||||
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
|
||||||
): MenuItemConstructorOptions => {
|
): MenuItemConstructorOptions => {
|
||||||
|
|
@ -94,5 +94,3 @@ const windowMenu = (
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default windowMenu;
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import type {BrowserWindow} from 'electron';
|
|
||||||
|
|
||||||
import fetch from 'electron-fetch';
|
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import fetch from 'electron-fetch';
|
||||||
import {version} from './package.json';
|
import {version} from './package.json';
|
||||||
|
import {BrowserWindow} from 'electron';
|
||||||
|
|
||||||
const NEWS_URL = 'https://hyper-news.now.sh';
|
const NEWS_URL = 'https://hyper-news.now.sh';
|
||||||
|
|
||||||
|
|
@ -24,7 +22,7 @@ export default function fetchNotifications(win: BrowserWindow) {
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const message: {text: string; url: string; dismissable: boolean} | '' = data.message || '';
|
const {message} = data || {};
|
||||||
if (typeof message !== 'object' && message !== '') {
|
if (typeof message !== 'object' && message !== '') {
|
||||||
throw new Error('Bad response');
|
throw new Error('Bad response');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import {app, Notification} from 'electron';
|
import {app, Notification} from 'electron';
|
||||||
|
|
||||||
import {icon} from './config/paths';
|
import {icon} from './config/paths';
|
||||||
|
|
||||||
export default function notify(title: string, body = '', details: {error?: any} = {}) {
|
export default function notify(title: string, body = '', details: {error?: any} = {}) {
|
||||||
|
|
|
||||||
|
|
@ -2,44 +2,39 @@
|
||||||
"name": "hyper",
|
"name": "hyper",
|
||||||
"productName": "Hyper",
|
"productName": "Hyper",
|
||||||
"description": "A terminal built on web technologies",
|
"description": "A terminal built on web technologies",
|
||||||
"version": "4.0.0-canary.5",
|
"version": "3.4.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "ZEIT, Inc.",
|
"name": "ZEIT, Inc.",
|
||||||
"email": "team@zeit.co"
|
"email": "team@zeit.co"
|
||||||
},
|
},
|
||||||
"repository": "zeit/hyper",
|
"repository": "zeit/hyper",
|
||||||
"scripts": {
|
|
||||||
"postinstall": "npx patch-package"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "7.24.4",
|
"@electron/remote": "2.0.9",
|
||||||
"@electron/remote": "2.1.2",
|
|
||||||
"ast-types": "^0.16.1",
|
|
||||||
"async-retry": "1.3.3",
|
"async-retry": "1.3.3",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.5.3",
|
||||||
"color": "4.2.3",
|
"color": "4.2.3",
|
||||||
"default-shell": "1.0.1",
|
"default-shell": "1.0.1",
|
||||||
"electron-devtools-installer": "3.2.0",
|
|
||||||
"electron-fetch": "1.9.1",
|
"electron-fetch": "1.9.1",
|
||||||
"electron-is-dev": "2.0.0",
|
"electron-is-dev": "2.0.0",
|
||||||
"electron-store": "8.2.0",
|
"electron-store": "8.1.0",
|
||||||
"fs-extra": "11.2.0",
|
"fs-extra": "11.1.0",
|
||||||
"git-describe": "4.1.1",
|
"git-describe": "4.1.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
|
"mkdirp": "1.0.4",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"native-process-working-directory": "^1.0.2",
|
"native-process-working-directory": "^1.0.2",
|
||||||
"node-pty": "1.0.0",
|
"node-pty": "0.10.1",
|
||||||
"os-locale": "5.0.0",
|
"os-locale": "5.0.0",
|
||||||
"parse-url": "8.1.0",
|
"parse-url": "8.1.0",
|
||||||
|
"pify": "5.0.0",
|
||||||
"queue": "6.0.2",
|
"queue": "6.0.2",
|
||||||
"react": "18.2.0",
|
"react": "17.0.2",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "17.0.2",
|
||||||
"recast": "0.23.6",
|
"semver": "7.3.8",
|
||||||
"semver": "7.6.0",
|
|
||||||
"shell-env": "3.0.1",
|
"shell-env": "3.0.1",
|
||||||
"sudo-prompt": "^9.2.1",
|
"sudo-prompt": "^9.2.1",
|
||||||
"uuid": "9.0.1"
|
"uuid": "9.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"native-reg": "1.1.1"
|
"native-reg": "1.1.1"
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,28 +1,22 @@
|
||||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import {exec, execFile} from 'child_process';
|
import {app, dialog, BrowserWindow, App, ipcMain} from 'electron';
|
||||||
import {writeFileSync} from 'fs';
|
|
||||||
import {resolve, basename} from 'path';
|
import {resolve, basename} from 'path';
|
||||||
import {promisify} from 'util';
|
import {writeFileSync} from 'fs';
|
||||||
|
|
||||||
import {app, dialog, ipcMain as _ipcMain} from 'electron';
|
|
||||||
import type {BrowserWindow, App, MenuItemConstructorOptions} from 'electron';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Config from 'electron-store';
|
import Config from 'electron-store';
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import React from 'react';
|
||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
|
|
||||||
import type {IpcMainWithCommands} from '../typings/common';
|
|
||||||
import type {configOptions} from '../typings/config';
|
|
||||||
|
|
||||||
import * as config from './config';
|
import * as config from './config';
|
||||||
import {plugs} from './config/paths';
|
|
||||||
import notify from './notify';
|
import notify from './notify';
|
||||||
import {availableExtensions} from './plugins/extensions';
|
import {availableExtensions} from './plugins/extensions';
|
||||||
import {install} from './plugins/install';
|
import {install} from './plugins/install';
|
||||||
|
import {plugs} from './config/paths';
|
||||||
import mapKeys from './utils/map-keys';
|
import mapKeys from './utils/map-keys';
|
||||||
|
import {configOptions} from '../lib/config';
|
||||||
|
import {promisify} from 'util';
|
||||||
|
import {exec, execFile} from 'child_process';
|
||||||
|
|
||||||
// local storage
|
// local storage
|
||||||
const cache = new Config();
|
const cache = new Config();
|
||||||
|
|
@ -208,7 +202,7 @@ function syncPackageJSON() {
|
||||||
const dependencies = toDependencies(plugins);
|
const dependencies = toDependencies(plugins);
|
||||||
const pkg = {
|
const pkg = {
|
||||||
name: 'hyper-plugins',
|
name: 'hyper-plugins',
|
||||||
description: 'Auto-generated from `hyper.json`!',
|
description: 'Auto-generated from `~/.hyper.js`!',
|
||||||
private: true,
|
private: true,
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
repository: 'vercel/hyper',
|
repository: 'vercel/hyper',
|
||||||
|
|
@ -282,7 +276,7 @@ function requirePlugins(): any[] {
|
||||||
const {plugins: plugins_, localPlugins} = paths;
|
const {plugins: plugins_, localPlugins} = paths;
|
||||||
|
|
||||||
const load = (path_: string) => {
|
const load = (path_: string) => {
|
||||||
let mod: Record<string, any>;
|
let mod: any;
|
||||||
try {
|
try {
|
||||||
mod = require(path_);
|
mod = require(path_);
|
||||||
const exposed = mod && Object.keys(mod).some((key) => availableExtensions.has(key));
|
const exposed = mod && Object.keys(mod).some((key) => availableExtensions.has(key));
|
||||||
|
|
@ -312,13 +306,10 @@ function requirePlugins(): any[] {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return plugins_
|
||||||
...localPlugins.filter((p) => basename(p) === 'migrated-hyper3-config'),
|
|
||||||
...plugins_,
|
|
||||||
...localPlugins.filter((p) => basename(p) !== 'migrated-hyper3-config')
|
|
||||||
]
|
|
||||||
.map(load)
|
.map(load)
|
||||||
.filter((v): v is Record<string, any> => Boolean(v));
|
.concat(localPlugins.map(load))
|
||||||
|
.filter((v) => Boolean(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const onApp = (app_: App) => {
|
export const onApp = (app_: App) => {
|
||||||
|
|
@ -421,7 +412,7 @@ export const getDeprecatedConfig = () => {
|
||||||
return deprecated;
|
return deprecated;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decorateMenu = (tpl: MenuItemConstructorOptions[]) => {
|
export const decorateMenu = (tpl: any) => {
|
||||||
return decorateObject(tpl, 'decorateMenu');
|
return decorateObject(tpl, 'decorateMenu');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -429,8 +420,8 @@ export const getDecoratedEnv = (baseEnv: Record<string, string>) => {
|
||||||
return decorateObject(baseEnv, 'decorateEnv');
|
return decorateObject(baseEnv, 'decorateEnv');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDecoratedConfig = (profile: string) => {
|
export const getDecoratedConfig = () => {
|
||||||
const baseConfig = config.getProfileConfig(profile);
|
const baseConfig = config.getConfig();
|
||||||
const decoratedConfig = decorateObject(baseConfig, 'decorateConfig');
|
const decoratedConfig = decorateObject(baseConfig, 'decorateConfig');
|
||||||
const fixedConfig = config.fixConfigDefaults(decoratedConfig);
|
const fixedConfig = config.fixConfigDefaults(decoratedConfig);
|
||||||
const translatedConfig = config.htermConfigTranslate(fixedConfig);
|
const translatedConfig = config.htermConfigTranslate(fixedConfig);
|
||||||
|
|
@ -462,19 +453,12 @@ export const decorateSessionClass = <T>(Session: T): T => {
|
||||||
|
|
||||||
export {toDependencies as _toDependencies};
|
export {toDependencies as _toDependencies};
|
||||||
|
|
||||||
const ipcMain = _ipcMain as IpcMainWithCommands;
|
ipcMain.handle('child_process.exec', (event, args) => {
|
||||||
|
const {command, options} = args;
|
||||||
ipcMain.handle('child_process.exec', (event, command, options) => {
|
|
||||||
return promisify(exec)(command, options);
|
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);
|
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());
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
|
|
||||||
import ms from 'ms';
|
|
||||||
import queue from 'queue';
|
import queue from 'queue';
|
||||||
|
import ms from 'ms';
|
||||||
import {yarn, plugs} from '../config/paths';
|
import {yarn, plugs} from '../config/paths';
|
||||||
|
|
||||||
export const install = (fn: (err: string | null) => void) => {
|
export const install = (fn: (err: string | null) => void) => {
|
||||||
|
|
|
||||||
46
app/rpc.ts
46
app/rpc.ts
|
|
@ -1,22 +1,15 @@
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
|
import {ipcMain, BrowserWindow} from 'electron';
|
||||||
import {ipcMain} from 'electron';
|
|
||||||
import type {BrowserWindow, IpcMainEvent} from 'electron';
|
|
||||||
|
|
||||||
import {v4 as uuidv4} from 'uuid';
|
import {v4 as uuidv4} from 'uuid';
|
||||||
|
|
||||||
import type {TypedEmitter, MainEvents, RendererEvents, FilterNever} from '../typings/common';
|
export class Server extends EventEmitter {
|
||||||
|
|
||||||
export class Server {
|
|
||||||
emitter: TypedEmitter<MainEvents>;
|
|
||||||
destroyed = false;
|
destroyed = false;
|
||||||
win: BrowserWindow;
|
win: BrowserWindow;
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
constructor(win: BrowserWindow) {
|
constructor(win: BrowserWindow) {
|
||||||
this.emitter = new EventEmitter();
|
super();
|
||||||
this.win = win;
|
this.win = win;
|
||||||
this.emit = this.emit.bind(this);
|
this.ipcListener = this.ipcListener.bind(this);
|
||||||
|
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -25,13 +18,14 @@ export class Server {
|
||||||
const uid = uuidv4();
|
const uid = uuidv4();
|
||||||
this.id = uid;
|
this.id = uid;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
ipcMain.on(uid, this.ipcListener);
|
ipcMain.on(uid, this.ipcListener);
|
||||||
|
|
||||||
// we intentionally subscribe to `on` instead of `once`
|
// we intentionally subscribe to `on` instead of `once`
|
||||||
// to support reloading the window and re-initializing
|
// to support reloading the window and re-initializing
|
||||||
// the channel
|
// the channel
|
||||||
this.wc.on('did-finish-load', () => {
|
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;
|
return this.win.webContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcListener = <U extends keyof MainEvents>(event: IpcMainEvent, {ev, data}: {ev: U; data: MainEvents[U]}) =>
|
ipcListener(event: any, {ev, data}: {ev: string; data: any}) {
|
||||||
this.emitter.emit(ev, data);
|
super.emit(ev, data);
|
||||||
|
}
|
||||||
|
|
||||||
on = <U extends keyof MainEvents>(ev: U, fn: (arg0: MainEvents[U]) => void) => {
|
emit(ch: string, data: any = {}): any {
|
||||||
this.emitter.on(ev, fn);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
once = <U extends keyof MainEvents>(ev: U, fn: (arg0: MainEvents[U]) => void) => {
|
|
||||||
this.emitter.once(ev, fn);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
emit<U extends Exclude<keyof RendererEvents, FilterNever<RendererEvents>>>(ch: U): boolean;
|
|
||||||
emit<U extends FilterNever<RendererEvents>>(ch: U, data: RendererEvents[U]): boolean;
|
|
||||||
emit<U extends keyof RendererEvents>(ch: U, data?: RendererEvents[U]) {
|
|
||||||
// This check is needed because data-batching can cause extra data to be
|
// This check is needed because data-batching can cause extra data to be
|
||||||
// emitted after the window has already closed
|
// emitted after the window has already closed
|
||||||
if (!this.win.isDestroyed()) {
|
if (!this.win.isDestroyed()) {
|
||||||
this.wc.send(this.id, {ch, data});
|
this.wc.send(this.id, {ch, data});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.emitter.removeAllListeners();
|
this.removeAllListeners();
|
||||||
this.wc.removeAllListeners();
|
this.wc.removeAllListeners();
|
||||||
if (this.id) {
|
if (this.id) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
ipcMain.removeListener(this.id, this.ipcListener);
|
ipcMain.removeListener(this.id, this.ipcListener);
|
||||||
} else {
|
} else {
|
||||||
// mark for `genUid` in constructor
|
// mark for `genUid` in constructor
|
||||||
|
|
@ -76,8 +58,6 @@ export class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createRPC = (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
return new Server(win);
|
return new Server(win);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createRPC;
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import {dirname} from 'path';
|
|
||||||
import {StringDecoder} from 'string_decoder';
|
import {StringDecoder} from 'string_decoder';
|
||||||
|
|
||||||
import defaultShell from 'default-shell';
|
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 {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 = () =>
|
const createNodePtyError = () =>
|
||||||
new Error(
|
new Error(
|
||||||
|
|
@ -26,6 +23,7 @@ try {
|
||||||
throw createNodePtyError();
|
throw createNodePtyError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const envFromConfig = config.getConfig().env || {};
|
||||||
const useConpty = config.getConfig().useConpty;
|
const useConpty = config.getConfig().useConpty;
|
||||||
|
|
||||||
// Max duration to batch session data before sending it to the renderer process.
|
// Max duration to batch session data before sending it to the renderer process.
|
||||||
|
|
@ -58,7 +56,7 @@ class DataBatcher extends EventEmitter {
|
||||||
this.timeout = null;
|
this.timeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
write(chunk: Buffer | string) {
|
write(chunk: Buffer) {
|
||||||
if (this.data.length + chunk.length >= BATCH_MAX_SIZE) {
|
if (this.data.length + chunk.length >= BATCH_MAX_SIZE) {
|
||||||
// We've reached the max batch size. Flush it and start another one
|
// We've reached the max batch size. Flush it and start another one
|
||||||
if (this.timeout) {
|
if (this.timeout) {
|
||||||
|
|
@ -68,7 +66,7 @@ class DataBatcher extends EventEmitter {
|
||||||
this.flush();
|
this.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data += typeof chunk === 'string' ? chunk : this.decoder.write(chunk);
|
this.data += this.decoder.write(chunk);
|
||||||
|
|
||||||
if (!this.timeout) {
|
if (!this.timeout) {
|
||||||
this.timeout = setTimeout(() => this.flush(), BATCH_DURATION_MS);
|
this.timeout = setTimeout(() => this.flush(), BATCH_DURATION_MS);
|
||||||
|
|
@ -86,12 +84,11 @@ class DataBatcher extends EventEmitter {
|
||||||
|
|
||||||
interface SessionOptions {
|
interface SessionOptions {
|
||||||
uid: string;
|
uid: string;
|
||||||
rows?: number;
|
rows: number;
|
||||||
cols?: number;
|
cols: number;
|
||||||
cwd?: string;
|
cwd: string;
|
||||||
shell?: string;
|
shell: string;
|
||||||
shellArgs?: string[];
|
shellArgs: string[];
|
||||||
profile: string;
|
|
||||||
}
|
}
|
||||||
export default class Session extends EventEmitter {
|
export default class Session extends EventEmitter {
|
||||||
pty: IPty | null;
|
pty: IPty | null;
|
||||||
|
|
@ -99,7 +96,6 @@ export default class Session extends EventEmitter {
|
||||||
shell: string | null;
|
shell: string | null;
|
||||||
ended: boolean;
|
ended: boolean;
|
||||||
initTimestamp: number;
|
initTimestamp: number;
|
||||||
profile!: string;
|
|
||||||
constructor(options: SessionOptions) {
|
constructor(options: SessionOptions) {
|
||||||
super();
|
super();
|
||||||
this.pty = null;
|
this.pty = null;
|
||||||
|
|
@ -110,25 +106,22 @@ export default class Session extends EventEmitter {
|
||||||
this.init(options);
|
this.init(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
init({uid, rows, cols, cwd, shell: _shell, shellArgs: _shellArgs, profile}: SessionOptions) {
|
init({uid, rows, cols: columns, cwd, shell: _shell, shellArgs: _shellArgs}: SessionOptions) {
|
||||||
this.profile = profile;
|
|
||||||
const envFromConfig = config.getProfileConfig(profile).env || {};
|
|
||||||
const defaultShellArgs = ['--login'];
|
|
||||||
|
|
||||||
const shell = _shell || defaultShell;
|
|
||||||
const shellArgs = _shellArgs || defaultShellArgs;
|
|
||||||
|
|
||||||
const cleanEnv =
|
const cleanEnv =
|
||||||
process.env['APPIMAGE'] && process.env['APPDIR'] ? shellEnv.sync(_shell || defaultShell) : process.env;
|
process.env['APPIMAGE'] && process.env['APPDIR'] ? shellEnv.sync(_shell || defaultShell) : process.env;
|
||||||
const baseEnv: Record<string, string> = {
|
const baseEnv = Object.assign(
|
||||||
...cleanEnv,
|
{},
|
||||||
|
cleanEnv,
|
||||||
|
{
|
||||||
LANG: `${osLocale.sync().replace(/-/, '_')}.UTF-8`,
|
LANG: `${osLocale.sync().replace(/-/, '_')}.UTF-8`,
|
||||||
TERM: 'xterm-256color',
|
TERM: 'xterm-256color',
|
||||||
COLORTERM: 'truecolor',
|
COLORTERM: 'truecolor',
|
||||||
TERM_PROGRAM: productName,
|
TERM_PROGRAM: productName,
|
||||||
TERM_PROGRAM_VERSION: version,
|
TERM_PROGRAM_VERSION: version
|
||||||
...envFromConfig
|
},
|
||||||
};
|
envFromConfig
|
||||||
|
);
|
||||||
|
|
||||||
// path to AppImage mount point is added to PATH environment variable automatically
|
// path to AppImage mount point is added to PATH environment variable automatically
|
||||||
// which conflicts with the cli
|
// which conflicts with the cli
|
||||||
if (baseEnv['APPIMAGE'] && baseEnv['APPDIR']) {
|
if (baseEnv['APPIMAGE'] && baseEnv['APPDIR']) {
|
||||||
|
|
@ -144,8 +137,10 @@ export default class Session extends EventEmitter {
|
||||||
delete baseEnv.GOOGLE_API_KEY;
|
delete baseEnv.GOOGLE_API_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultShellArgs = ['--login'];
|
||||||
|
|
||||||
const options: IWindowsPtyForkOptions = {
|
const options: IWindowsPtyForkOptions = {
|
||||||
cols,
|
cols: columns,
|
||||||
rows,
|
rows,
|
||||||
cwd,
|
cwd,
|
||||||
env: getDecoratedEnv(baseEnv)
|
env: getDecoratedEnv(baseEnv)
|
||||||
|
|
@ -156,6 +151,9 @@ export default class Session extends EventEmitter {
|
||||||
options.useConpty = useConpty;
|
options.useConpty = useConpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shell = _shell || defaultShell;
|
||||||
|
const shellArgs = _shellArgs || defaultShellArgs;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.pty = spawn(shell, shellArgs, options);
|
this.pty = spawn(shell, shellArgs, options);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
|
|
@ -172,7 +170,7 @@ export default class Session extends EventEmitter {
|
||||||
if (this.ended) {
|
if (this.ended) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.batcher?.write(chunk);
|
this.batcher?.write(chunk as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.batcher.on('flush', (data: string) => {
|
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
|
// this will inform users in case there are errors in the config instead of instant exit
|
||||||
const runDuration = new Date().getTime() - this.initTimestamp;
|
const runDuration = new Date().getTime() - this.initTimestamp;
|
||||||
if (e.exitCode > 0 && runDuration < 1000) {
|
if (e.exitCode > 0 && runDuration < 1000) {
|
||||||
const fallBackShellConfig = getFallBackShellConfig(shell, shellArgs, defaultShell, defaultShellArgs);
|
const defaultShellConfig = {shell: defaultShell, shellArgs: defaultShellArgs};
|
||||||
if (fallBackShellConfig) {
|
|
||||||
const msg = `
|
const msg = `
|
||||||
shell exited in ${runDuration} ms with exit code ${e.exitCode}
|
shell exited in ${runDuration} ms with exit code ${e.exitCode}
|
||||||
please check the shell config: ${JSON.stringify({shell, shellArgs}, undefined, 2)}
|
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);
|
console.warn(msg);
|
||||||
this.batcher?.write(msg.replace(/\n/g, '\r\n'));
|
this.batcher?.write(msg.replace(/\n/g, '\r\n') as any);
|
||||||
this.init({
|
this.init({uid, rows, cols: columns, cwd, ...defaultShellConfig});
|
||||||
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'));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.ended = true;
|
this.ended = true;
|
||||||
this.emit('exit');
|
this.emit('exit');
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./**/*",
|
"./**/*",
|
||||||
"./package.json",
|
"./package.json"
|
||||||
"../typings/extend-electron.d.ts",
|
|
||||||
"../typings/ext-modules.d.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 editMenu from '../menus/menus/edit';
|
||||||
import shellMenu from '../menus/menus/shell';
|
import shellMenu from '../menus/menus/shell';
|
||||||
|
import {execCommand} from '../commands';
|
||||||
import {getDecoratedKeymaps} from '../plugins';
|
import {getDecoratedKeymaps} from '../plugins';
|
||||||
|
import {MenuItemConstructorOptions, BrowserWindow} from 'electron';
|
||||||
const separator: MenuItemConstructorOptions = {type: 'separator'};
|
const separator: MenuItemConstructorOptions = {type: 'separator'};
|
||||||
|
|
||||||
const getCommandKeys = (keymaps: Record<string, string[]>): Record<string, string> =>
|
const getCommandKeys = (keymaps: Record<string, string[]>): Record<string, string> =>
|
||||||
|
|
@ -23,20 +20,14 @@ const filterCutCopy = (selection: string, menuItem: MenuItemConstructorOptions)
|
||||||
return menuItem;
|
return menuItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextMenuTemplate = (
|
export default (
|
||||||
createWindow: (fn?: (win: BrowserWindow) => void, options?: Record<string, any>) => BrowserWindow,
|
createWindow: (fn?: (win: BrowserWindow) => void, options?: Record<string, any>) => BrowserWindow,
|
||||||
selection: string
|
selection: string
|
||||||
) => {
|
) => {
|
||||||
const commandKeys = getCommandKeys(getDecoratedKeymaps());
|
const commandKeys = getCommandKeys(getDecoratedKeymaps());
|
||||||
const _shell = shellMenu(
|
const _shell = shellMenu(commandKeys, execCommand).submenu as MenuItemConstructorOptions[];
|
||||||
commandKeys,
|
|
||||||
execCommand,
|
|
||||||
getProfiles().map((p) => p.name)
|
|
||||||
).submenu as MenuItemConstructorOptions[];
|
|
||||||
const _edit = editMenu(commandKeys, execCommand).submenu.filter(filterCutCopy.bind(null, selection));
|
const _edit = editMenu(commandKeys, execCommand).submenu.filter(filterCutCopy.bind(null, selection));
|
||||||
return _edit
|
return _edit
|
||||||
.concat(separator, _shell)
|
.concat(separator, _shell)
|
||||||
.filter((menuItem) => !Object.prototype.hasOwnProperty.call(menuItem, 'enabled') || menuItem.enabled);
|
.filter((menuItem) => !Object.prototype.hasOwnProperty.call(menuItem, 'enabled') || menuItem.enabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default contextMenuTemplate;
|
|
||||||
|
|
|
||||||
126
app/ui/window.ts
126
app/ui/window.ts
|
|
@ -1,36 +1,27 @@
|
||||||
import {existsSync} from 'fs';
|
import {app, BrowserWindow, shell, Menu, BrowserWindowConstructorOptions, Event} from 'electron';
|
||||||
import {isAbsolute, normalize, sep} from 'path';
|
import {isAbsolute, normalize, sep} from 'path';
|
||||||
import {URL, fileURLToPath} from 'url';
|
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 {v4 as uuidv4} from 'uuid';
|
||||||
|
import isDev from 'electron-is-dev';
|
||||||
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 updater from '../updater';
|
import updater from '../updater';
|
||||||
import {setRendererType, unsetRendererType} from '../utils/renderer-utils';
|
|
||||||
import toElectronBackgroundColor from '../utils/to-electron-background-color';
|
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 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';
|
||||||
|
|
||||||
export function newWindow(
|
export function newWindow(
|
||||||
options_: BrowserWindowConstructorOptions,
|
options_: BrowserWindowConstructorOptions,
|
||||||
cfg: configOptions,
|
cfg: configOptions,
|
||||||
fn?: (win: BrowserWindow) => void,
|
fn?: (win: BrowserWindow) => void
|
||||||
profileName: string = getDefaultProfile()
|
|
||||||
): BrowserWindow {
|
): BrowserWindow {
|
||||||
const classOpts = Object.assign({uid: uuidv4()});
|
const classOpts = Object.assign({uid: uuidv4()});
|
||||||
app.plugins.decorateWindowClass(classOpts);
|
app.plugins.decorateWindowClass(classOpts);
|
||||||
|
|
@ -56,8 +47,6 @@ export function newWindow(
|
||||||
};
|
};
|
||||||
const window = new BrowserWindow(app.plugins.getDecoratedBrowserOptions(winOpts));
|
const window = new BrowserWindow(app.plugins.getDecoratedBrowserOptions(winOpts));
|
||||||
|
|
||||||
window.profileName = profileName;
|
|
||||||
|
|
||||||
// Enable remote module on this window
|
// Enable remote module on this window
|
||||||
remoteEnable(window.webContents);
|
remoteEnable(window.webContents);
|
||||||
|
|
||||||
|
|
@ -70,13 +59,28 @@ export function newWindow(
|
||||||
const sessions = new Map<string, Session>();
|
const sessions = new Map<string, Session>();
|
||||||
|
|
||||||
const updateBackgroundColor = () => {
|
const updateBackgroundColor = () => {
|
||||||
const cfg_ = app.plugins.getDecoratedConfig(profileName);
|
const cfg_ = app.plugins.getDecoratedConfig();
|
||||||
window.setBackgroundColor(toElectronBackgroundColor(cfg_.backgroundColor || '#000'));
|
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
|
// config changes
|
||||||
const cfgUnsubscribe = app.config.subscribe(() => {
|
const cfgUnsubscribe = app.config.subscribe(() => {
|
||||||
const cfg_ = app.plugins.getDecoratedConfig(profileName);
|
const cfg_ = app.plugins.getDecoratedConfig();
|
||||||
|
|
||||||
// notify renderer
|
// notify renderer
|
||||||
window.webContents.send('config change');
|
window.webContents.send('config change');
|
||||||
|
|
@ -119,43 +123,22 @@ export function newWindow(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function createSession(extraOptions: sessionExtraOptions = {}) {
|
function createSession(extraOptions: any = {}) {
|
||||||
const uid = uuidv4();
|
const uid = uuidv4();
|
||||||
const extraOptionsFiltered: sessionExtraOptions = {};
|
const extraOptionsFiltered: any = {};
|
||||||
Object.keys(extraOptions).forEach((key) => {
|
Object.keys(extraOptions).forEach((key) => {
|
||||||
if (extraOptions[key] !== undefined) extraOptionsFiltered[key] = extraOptions[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 = '';
|
let cwd = '';
|
||||||
if (cfg.preserveCWD !== false && activeSession && activeSession.profile === profile) {
|
if (cfg.preserveCWD === undefined || cfg.preserveCWD) {
|
||||||
const activePID = activeSession.pty?.pid;
|
const activePID = extraOptionsFiltered.activeUid && sessions.get(extraOptionsFiltered.activeUid)?.pty?.pid;
|
||||||
if (activePID !== undefined) {
|
|
||||||
try {
|
try {
|
||||||
cwd = getWorkingDirectoryFromPID(activePID) || '';
|
cwd = activePID && getWorkingDirectoryFromPID(activePID);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
cwd = cwd && isAbsolute(cwd) ? cwd : '';
|
||||||
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
|
// remove the rows and cols, the wrong value of them will break layout when init create
|
||||||
|
|
@ -163,14 +146,11 @@ export function newWindow(
|
||||||
{
|
{
|
||||||
cwd: cwd || workingDirectory,
|
cwd: cwd || workingDirectory,
|
||||||
splitDirection: undefined,
|
splitDirection: undefined,
|
||||||
shell: profileCfg.shell,
|
shell: cfg.shell,
|
||||||
shellArgs: profileCfg.shellArgs && Array.from(profileCfg.shellArgs)
|
shellArgs: cfg.shellArgs && Array.from(cfg.shellArgs)
|
||||||
},
|
},
|
||||||
extraOptionsFiltered,
|
extraOptionsFiltered,
|
||||||
{
|
{uid}
|
||||||
profile: extraOptionsFiltered.profile || profileName,
|
|
||||||
uid
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
const options = decorateSessionOptions(defaultOptions);
|
const options = decorateSessionOptions(defaultOptions);
|
||||||
const DecoratedSession = decorateSessionClass(Session);
|
const DecoratedSession = decorateSessionClass(Session);
|
||||||
|
|
@ -190,8 +170,7 @@ export function newWindow(
|
||||||
splitDirection: options.splitDirection,
|
splitDirection: options.splitDirection,
|
||||||
shell: session.shell,
|
shell: session.shell,
|
||||||
pid: session.pty ? session.pty.pid : null,
|
pid: session.pty ? session.pty.pid : null,
|
||||||
activeUid: options.activeUid ?? undefined,
|
activeUid: options.activeUid
|
||||||
profile: options.profile
|
|
||||||
});
|
});
|
||||||
|
|
||||||
session.on('data', (data: string) => {
|
session.on('data', (data: string) => {
|
||||||
|
|
@ -226,8 +205,8 @@ export function newWindow(
|
||||||
session.resize({cols, rows});
|
session.resize({cols, rows});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
rpc.on('data', ({uid, data, escaped}) => {
|
rpc.on('data', ({uid, data, escaped}: {uid: string; data: string; escaped: boolean}) => {
|
||||||
const session = uid && sessions.get(uid);
|
const session = sessions.get(uid);
|
||||||
if (session) {
|
if (session) {
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
const escapedData = session.shell?.endsWith('cmd.exe')
|
const escapedData = session.shell?.endsWith('cmd.exe')
|
||||||
|
|
@ -257,12 +236,11 @@ export function newWindow(
|
||||||
// Same deal as above, grabbing the window titlebar when the window
|
// Same deal as above, grabbing the window titlebar when the window
|
||||||
// is maximized on Windows results in unmaximize, without hitting any
|
// is maximized on Windows results in unmaximize, without hitting any
|
||||||
// app buttons
|
// app buttons
|
||||||
const onGeometryChange = () => rpc.emit('windowGeometry change', {isMaximized: window.isMaximized()});
|
for (const ev of ['maximize', 'unmaximize', 'minimize', 'restore'] as any) {
|
||||||
window.on('maximize', onGeometryChange);
|
window.on(ev, () => {
|
||||||
window.on('unmaximize', onGeometryChange);
|
rpc.emit('windowGeometry change', {isMaximized: window.isMaximized()});
|
||||||
window.on('minimize', onGeometryChange);
|
});
|
||||||
window.on('restore', onGeometryChange);
|
}
|
||||||
|
|
||||||
window.on('move', () => {
|
window.on('move', () => {
|
||||||
const position = window.getPosition();
|
const position = window.getPosition();
|
||||||
rpc.emit('move', {bounds: {x: position[0], y: position[1]}});
|
rpc.emit('move', {bounds: {x: position[0], y: position[1]}});
|
||||||
|
|
@ -276,10 +254,10 @@ export function newWindow(
|
||||||
});
|
});
|
||||||
// pass on the full screen events from the window to react
|
// pass on the full screen events from the window to react
|
||||||
rpc.win.on('enter-full-screen', () => {
|
rpc.win.on('enter-full-screen', () => {
|
||||||
rpc.emit('enter full screen');
|
rpc.emit('enter full screen', {});
|
||||||
});
|
});
|
||||||
rpc.win.on('leave-full-screen', () => {
|
rpc.win.on('leave-full-screen', () => {
|
||||||
rpc.emit('leave full screen');
|
rpc.emit('leave full screen', {});
|
||||||
});
|
});
|
||||||
const deleteSessions = () => {
|
const deleteSessions = () => {
|
||||||
sessions.forEach((session, key) => {
|
sessions.forEach((session, key) => {
|
||||||
|
|
@ -301,9 +279,9 @@ export function newWindow(
|
||||||
const protocol = typeof url === 'string' && new URL(url).protocol;
|
const protocol = typeof url === 'string' && new URL(url).protocol;
|
||||||
if (protocol === 'file:') {
|
if (protocol === 'file:') {
|
||||||
const path = fileURLToPath(url);
|
const path = fileURLToPath(url);
|
||||||
return {uid: null, data: path, escaped: true};
|
return {data: path, escaped: true};
|
||||||
} else if (protocol === 'http:' || protocol === 'https:') {
|
} else if (protocol === 'http:' || protocol === 'https:') {
|
||||||
return {uid: null, data: url};
|
return {data: url};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
// Packages
|
// Packages
|
||||||
import electron, {app} from 'electron';
|
import electron, {app, BrowserWindow, AutoUpdater} from 'electron';
|
||||||
import type {BrowserWindow, AutoUpdater} from 'electron';
|
|
||||||
|
|
||||||
import retry from 'async-retry';
|
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import retry from 'async-retry';
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
import autoUpdaterLinux from './auto-updater-linux';
|
|
||||||
import {getDefaultProfile} from './config';
|
|
||||||
import {version} from './package.json';
|
import {version} from './package.json';
|
||||||
import {getDecoratedConfig} from './plugins';
|
import {getDecoratedConfig} from './plugins';
|
||||||
|
import autoUpdaterLinux from './auto-updater-linux';
|
||||||
|
|
||||||
const {platform} = process;
|
const {platform} = process;
|
||||||
const isLinux = platform === 'linux';
|
const isLinux = platform === 'linux';
|
||||||
|
|
@ -18,7 +15,7 @@ const autoUpdater: AutoUpdater = isLinux ? autoUpdaterLinux : electron.autoUpdat
|
||||||
|
|
||||||
const getDecoratedConfigWithRetry = async () => {
|
const getDecoratedConfigWithRetry = async () => {
|
||||||
return await retry(() => {
|
return await retry(() => {
|
||||||
const content = getDecoratedConfig(getDefaultProfile());
|
const content = getDecoratedConfig();
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new Error('No config content loaded');
|
throw new Error('No config content loaded');
|
||||||
}
|
}
|
||||||
|
|
@ -72,23 +69,28 @@ async function init() {
|
||||||
isInit = true;
|
isInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updater = (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
if (!isInit) {
|
if (!isInit) {
|
||||||
void init();
|
void init();
|
||||||
}
|
}
|
||||||
|
|
||||||
const {rpc} = win;
|
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}`;
|
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) {
|
const eventName: any = isLinux ? 'update-available' : 'update-downloaded';
|
||||||
autoUpdater.on('update-available', onupdate);
|
|
||||||
} else {
|
autoUpdater.on(eventName, onupdate);
|
||||||
autoUpdater.on('update-downloaded', onupdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc.once('quit and install', () => {
|
rpc.once('quit and install', () => {
|
||||||
autoUpdater.quitAndInstall();
|
autoUpdater.quitAndInstall();
|
||||||
|
|
@ -109,12 +111,6 @@ const updater = (win: BrowserWindow) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
win.on('close', () => {
|
win.on('close', () => {
|
||||||
if (isLinux) {
|
autoUpdater.removeListener(eventName, onupdate);
|
||||||
autoUpdater.removeListener('update-available', onupdate);
|
|
||||||
} else {
|
|
||||||
autoUpdater.removeListener('update-downloaded', onupdate);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default updater;
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
import {existsSync, readlink, symlink} from 'fs';
|
import pify from 'pify';
|
||||||
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {promisify} from 'util';
|
import notify from '../notify';
|
||||||
|
import {cliScriptPath, cliLinkPath} from '../config/paths';
|
||||||
import {clipboard, dialog} from 'electron';
|
|
||||||
|
|
||||||
import {mkdirpSync} from 'fs-extra';
|
|
||||||
import * as Registry from 'native-reg';
|
import * as Registry from 'native-reg';
|
||||||
import type {ValueType} from 'native-reg';
|
import type {ValueType} from 'native-reg';
|
||||||
import sudoPrompt from 'sudo-prompt';
|
import sudoPrompt from 'sudo-prompt';
|
||||||
|
import {clipboard, dialog} from 'electron';
|
||||||
|
import {mkdirpSync} from 'fs-extra';
|
||||||
|
|
||||||
import {cliScriptPath, cliLinkPath} from '../config/paths';
|
const readlink = pify(fs.readlink);
|
||||||
import notify from '../notify';
|
const symlink = pify(fs.symlink);
|
||||||
|
const sudoExec = pify(sudoPrompt.exec, {multiArgs: true});
|
||||||
const readLink = promisify(readlink);
|
|
||||||
const symLink = promisify(symlink);
|
|
||||||
const sudoExec = promisify(sudoPrompt.exec);
|
|
||||||
|
|
||||||
const checkInstall = () => {
|
const checkInstall = () => {
|
||||||
return readLink(cliLinkPath)
|
return readlink(cliLinkPath)
|
||||||
.then((link) => link === cliScriptPath)
|
.then((link) => link === cliScriptPath)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
|
|
@ -35,14 +32,14 @@ const addSymlink = async (silent: boolean) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Linking HyperCLI');
|
console.log('Linking HyperCLI');
|
||||||
if (!existsSync(path.dirname(cliLinkPath))) {
|
if (!fs.existsSync(path.dirname(cliLinkPath))) {
|
||||||
try {
|
try {
|
||||||
mkdirpSync(path.dirname(cliLinkPath));
|
mkdirpSync(path.dirname(cliLinkPath));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw `Failed to create directory ${path.dirname(cliLinkPath)} - ${err}`;
|
throw `Failed to create directory ${path.dirname(cliLinkPath)} - ${err}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await symLink(cliScriptPath, cliLinkPath);
|
await symlink(cliScriptPath, cliLinkPath);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
const err = _err as {code: string};
|
const err = _err as {code: string};
|
||||||
// 'EINVAL' is returned by readlink,
|
// 'EINVAL' is returned by readlink,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const generatePrefixedCommand = (command: string, shortcuts: string[]) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapKeys = (config: Record<string, string[] | string>) => {
|
export default (config: Record<string, string[] | string>) => {
|
||||||
return Object.keys(config).reduce((keymap: Record<string, string[]>, command: string) => {
|
return Object.keys(config).reduce((keymap: Record<string, string[]>, command: string) => {
|
||||||
if (!command) {
|
if (!command) {
|
||||||
return keymap;
|
return keymap;
|
||||||
|
|
@ -39,5 +39,3 @@ const mapKeys = (config: Record<string, string[] | string>) => {
|
||||||
return keymap;
|
return keymap;
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mapKeys;
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
@ -9,7 +9,7 @@ const regKeys = [
|
||||||
];
|
];
|
||||||
const regParts = [
|
const regParts = [
|
||||||
{key: 'command', name: '', value: `${appPath} "%V"`},
|
{key: 'command', name: '', value: `${appPath} "%V"`},
|
||||||
{name: '', value: 'Open &Hyper here'},
|
{name: '', value: 'Open Hyper here'},
|
||||||
{name: 'Icon', value: `${appPath}`}
|
{name: 'Icon', value: `${appPath}`}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Color from 'color';
|
||||||
// returns a background color that's in hex
|
// returns a background color that's in hex
|
||||||
// format including the alpha channel (e.g.: `#00000050`)
|
// format including the alpha channel (e.g.: `#00000050`)
|
||||||
// input can be any css value (rgb, hsl, string…)
|
// input can be any css value (rgb, hsl, string…)
|
||||||
const toElectronBackgroundColor = (bgColor: string) => {
|
export default (bgColor: string) => {
|
||||||
const color = Color(bgColor);
|
const color = Color(bgColor);
|
||||||
|
|
||||||
if (color.alpha() === 1) {
|
if (color.alpha() === 1) {
|
||||||
|
|
@ -15,5 +15,3 @@ const toElectronBackgroundColor = (bgColor: string) => {
|
||||||
const alphaHex = Math.round(color.alpha() * 255).toString(16);
|
const alphaHex = Math.round(color.alpha() * 255).toString(16);
|
||||||
return `#${alphaHex}${color.hex().toString().slice(1)}`;
|
return `#${alphaHex}${color.hex().toString().slice(1)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default toElectronBackgroundColor;
|
|
||||||
|
|
|
||||||
326
app/yarn.lock
326
app/yarn.lock
|
|
@ -2,15 +2,10 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@babel/parser@7.24.4":
|
"@electron/remote@2.0.9":
|
||||||
version "7.24.4"
|
version "2.0.9"
|
||||||
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88"
|
resolved "https://registry.npmjs.org/@electron/remote/-/remote-2.0.9.tgz#092ff085407bc907f45b89a72c36faa773ccf2d9"
|
||||||
integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==
|
integrity sha512-LR0W0ID6WAKHaSs0x5LX9aiG+5pFBNAJL6eQAJfGkCuZPUa6nZz+czZLdlTDETG45CgF/0raSvCtYOYUpr6c+A==
|
||||||
|
|
||||||
"@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==
|
|
||||||
|
|
||||||
"@types/semver@^7.3.8":
|
"@types/semver@^7.3.8":
|
||||||
version "7.3.8"
|
version "7.3.8"
|
||||||
|
|
@ -47,13 +42,6 @@ anymatch@~3.1.2:
|
||||||
normalize-path "^3.0.0"
|
normalize-path "^3.0.0"
|
||||||
picomatch "^2.0.4"
|
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==
|
|
||||||
dependencies:
|
|
||||||
tslib "^2.0.1"
|
|
||||||
|
|
||||||
async-retry@1.3.3:
|
async-retry@1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
|
resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
|
||||||
|
|
@ -66,24 +54,11 @@ atomically@^1.7.0:
|
||||||
resolved "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe"
|
resolved "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe"
|
||||||
integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==
|
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==
|
|
||||||
|
|
||||||
binary-extensions@^2.0.0:
|
binary-extensions@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
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:
|
braces@~3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||||
|
|
@ -91,10 +66,10 @@ braces@~3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
chokidar@^3.6.0:
|
chokidar@^3.5.3:
|
||||||
version "3.6.0"
|
version "3.5.3"
|
||||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch "~3.1.2"
|
anymatch "~3.1.2"
|
||||||
braces "~3.0.2"
|
braces "~3.0.2"
|
||||||
|
|
@ -134,11 +109,6 @@ color@4.2.3:
|
||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
color-string "^1.9.0"
|
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:
|
conf@^10.2.0:
|
||||||
version "10.2.0"
|
version "10.2.0"
|
||||||
resolved "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz#838e757be963f1a2386dfe048a98f8f69f7b55d6"
|
resolved "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz#838e757be963f1a2386dfe048a98f8f69f7b55d6"
|
||||||
|
|
@ -155,11 +125,6 @@ conf@^10.2.0:
|
||||||
pkg-up "^3.1.0"
|
pkg-up "^3.1.0"
|
||||||
semver "^7.3.5"
|
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:
|
cross-spawn@^6.0.0:
|
||||||
version "6.0.5"
|
version "6.0.5"
|
||||||
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||||
|
|
@ -199,16 +164,6 @@ dot-prop@^6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-obj "^2.0.0"
|
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:
|
electron-fetch@1.9.1:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.9.1.tgz#e28bfe78d467de3f2dec884b1d72b8b05322f30f"
|
resolved "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.9.1.tgz#e28bfe78d467de3f2dec884b1d72b8b05322f30f"
|
||||||
|
|
@ -221,10 +176,10 @@ electron-is-dev@2.0.0:
|
||||||
resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz#833487a069b8dad21425c67a19847d9064ab19bd"
|
resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz#833487a069b8dad21425c67a19847d9064ab19bd"
|
||||||
integrity sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA==
|
integrity sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA==
|
||||||
|
|
||||||
electron-store@8.2.0:
|
electron-store@8.1.0:
|
||||||
version "8.2.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz#114e6e453e8bb746ab4ccb542424d8c881ad2ca1"
|
resolved "https://registry.npmjs.org/electron-store/-/electron-store-8.1.0.tgz#46a398f2bd9aa83c4a9daaae28380e2b3b9c7597"
|
||||||
integrity sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==
|
integrity sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA==
|
||||||
dependencies:
|
dependencies:
|
||||||
conf "^10.2.0"
|
conf "^10.2.0"
|
||||||
type-fest "^2.17.0"
|
type-fest "^2.17.0"
|
||||||
|
|
@ -248,11 +203,6 @@ env-paths@^2.2.1:
|
||||||
resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||||
|
|
||||||
esprima@~4.0.0:
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
|
||||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
|
||||||
|
|
||||||
execa@^1.0.0:
|
execa@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
|
resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
|
||||||
|
|
@ -300,20 +250,15 @@ find-up@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path "^3.0.0"
|
locate-path "^3.0.0"
|
||||||
|
|
||||||
fs-extra@11.2.0:
|
fs-extra@11.1.0:
|
||||||
version "11.2.0"
|
version "11.1.0"
|
||||||
resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
|
resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed"
|
||||||
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
|
integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs "^4.2.0"
|
graceful-fs "^4.2.0"
|
||||||
jsonfile "^6.0.1"
|
jsonfile "^6.0.1"
|
||||||
universalify "^2.0.0"
|
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:
|
fsevents@~2.3.2:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||||
|
|
@ -350,18 +295,6 @@ glob-parent@~5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
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==
|
|
||||||
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"
|
|
||||||
|
|
||||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||||
version "4.2.4"
|
version "4.2.4"
|
||||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||||
|
|
@ -379,20 +312,7 @@ iconv-lite@^0.6.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||||
|
|
||||||
immediate@~3.0.5:
|
inherits@~2.0.3:
|
||||||
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:
|
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
@ -446,11 +366,6 @@ is-stream@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||||
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
|
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==
|
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
|
|
@ -480,16 +395,6 @@ jsonfile@^6.0.1:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs "^4.1.6"
|
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:
|
lcid@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0"
|
resolved "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0"
|
||||||
|
|
@ -497,13 +402,6 @@ lcid@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
invert-kv "^3.0.0"
|
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:
|
locate-path@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||||
|
|
@ -562,31 +460,17 @@ mimic-fn@^3.0.0:
|
||||||
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
|
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
|
||||||
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
|
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
|
||||||
|
|
||||||
minimatch@^3.1.1:
|
mkdirp@1.0.4:
|
||||||
version "3.1.2"
|
version "1.0.4"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||||
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"
|
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
nan@^2.17.0:
|
nan@^2.14.0:
|
||||||
version "2.17.0"
|
version "2.17.0"
|
||||||
resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
||||||
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
|
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
|
||||||
|
|
@ -620,12 +504,12 @@ node-gyp-build@4:
|
||||||
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||||
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
||||||
|
|
||||||
node-pty@1.0.0:
|
node-pty@0.10.1:
|
||||||
version "1.0.0"
|
version "0.10.1"
|
||||||
resolved "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
|
resolved "https://registry.npmjs.org/node-pty/-/node-pty-0.10.1.tgz#cd05d03a2710315ec40221232ec04186f6ac2c6d"
|
||||||
integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
|
integrity sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==
|
||||||
dependencies:
|
dependencies:
|
||||||
nan "^2.17.0"
|
nan "^2.14.0"
|
||||||
|
|
||||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
|
|
@ -646,10 +530,15 @@ npm-run-path@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key "^3.0.0"
|
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=
|
||||||
|
|
||||||
|
once@^1.3.1, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
|
|
@ -703,11 +592,6 @@ p-try@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
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:
|
parse-path@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b"
|
resolved "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b"
|
||||||
|
|
@ -727,11 +611,6 @@ path-exists@^3.0.0:
|
||||||
resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
||||||
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
|
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:
|
path-key@^2.0.0, path-key@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||||
|
|
@ -747,6 +626,11 @@ picomatch@^2.0.4, picomatch@^2.2.1:
|
||||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
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:
|
pkg-up@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||||
|
|
@ -754,11 +638,6 @@ pkg-up@^3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up "^3.0.0"
|
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:
|
protocols@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86"
|
resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86"
|
||||||
|
|
@ -784,33 +663,22 @@ queue@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
inherits "~2.0.3"
|
inherits "~2.0.3"
|
||||||
|
|
||||||
react-dom@18.2.0:
|
react-dom@17.0.2:
|
||||||
version "18.2.0"
|
version "17.0.2"
|
||||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.0"
|
object-assign "^4.1.1"
|
||||||
|
scheduler "^0.20.2"
|
||||||
|
|
||||||
react@18.2.0:
|
react@17.0.2:
|
||||||
version "18.2.0"
|
version "17.0.2"
|
||||||
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
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"
|
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
readdirp@~3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
|
|
@ -819,17 +687,6 @@ readdirp@~3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
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==
|
|
||||||
dependencies:
|
|
||||||
ast-types "^0.16.1"
|
|
||||||
esprima "~4.0.0"
|
|
||||||
source-map "~0.6.1"
|
|
||||||
tiny-invariant "^1.3.3"
|
|
||||||
tslib "^2.0.1"
|
|
||||||
|
|
||||||
require-from-string@^2.0.2:
|
require-from-string@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||||
|
|
@ -840,34 +697,23 @@ retry@0.13.1:
|
||||||
resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
||||||
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
|
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":
|
"safer-buffer@>= 2.1.2 < 3.0.0":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
scheduler@^0.23.0:
|
scheduler@^0.20.2:
|
||||||
version "0.23.0"
|
version "0.20.2"
|
||||||
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
semver@7.6.0, semver@^7.2.1, semver@^7.3.5:
|
semver@7.3.8, semver@^7.3.5:
|
||||||
version "7.6.0"
|
version "7.3.8"
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||||
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache "^6.0.0"
|
lru-cache "^6.0.0"
|
||||||
|
|
||||||
|
|
@ -876,11 +722,6 @@ semver@^5.5.0, semver@^5.6.0:
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
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:
|
shebang-command@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||||
|
|
@ -931,18 +772,6 @@ simple-swizzle@^0.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.3.1"
|
is-arrayish "^0.3.1"
|
||||||
|
|
||||||
source-map@~0.6.1:
|
|
||||||
version "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:
|
strip-ansi@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||||
|
|
@ -965,11 +794,6 @@ sudo-prompt@^9.2.1:
|
||||||
resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
|
resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
|
||||||
integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==
|
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:
|
to-regex-range@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||||
|
|
@ -977,11 +801,6 @@ to-regex-range@^5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
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==
|
|
||||||
|
|
||||||
type-fest@^2.17.0:
|
type-fest@^2.17.0:
|
||||||
version "2.18.0"
|
version "2.18.0"
|
||||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz#fdef3a74e0a9e68ebe46054836650fb91ac3881e"
|
resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz#fdef3a74e0a9e68ebe46054836650fb91ac3881e"
|
||||||
|
|
@ -992,15 +811,6 @@ universalify@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||||
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
|
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:
|
uri-js@^4.2.2:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
|
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
|
||||||
|
|
@ -1008,15 +818,10 @@ uri-js@^4.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
util-deprecate@~1.0.1:
|
uuid@9.0.0:
|
||||||
version "1.0.2"
|
version "9.0.0"
|
||||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
|
||||||
|
|
||||||
uuid@9.0.1:
|
|
||||||
version "9.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
|
||||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
|
||||||
|
|
||||||
which@^1.2.9:
|
which@^1.2.9:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
|
|
@ -1037,11 +842,6 @@ wrappy@1:
|
||||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
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:
|
yallist@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||||
|
|
|
||||||
29
assets/search-icons.svg
Normal file
29
assets/search-icons.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<symbol id="left-arrow" viewBox="0 0 10 10">
|
||||||
|
<title>left arrow</title>
|
||||||
|
<g stroke-linecap="round">
|
||||||
|
<line x1="0.5" y1="5" x2="8.5" y2="5" stroke="#000" />
|
||||||
|
<line x1="0.5" y1="5" x2="3.5" y2="2" stroke="#000" />
|
||||||
|
<line x1="0.5" y1="5" x2="3.5" y2="8" stroke="#000" />
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="right-arrow" viewBox="0 0 10 10">
|
||||||
|
<title>right arrow</title>
|
||||||
|
<g stroke-linecap="round">
|
||||||
|
<line x1="1.5" y1="5" x2="9.5" y2="5" stroke="#000" />
|
||||||
|
<line x1="9.5" y1="5" x2="6.5" y2="2" stroke="#000" />
|
||||||
|
<line x1="9.5" y1="5" x2="6.5" y2="8" stroke="#000" />
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="cancel" viewBox="0 0 10 10">
|
||||||
|
<title>cancel</title>
|
||||||
|
<g stroke-linecap="round">
|
||||||
|
<line x1="5" y1="5" x2="8" y2="8" stroke="#000" />
|
||||||
|
<line x1="5" y1="5" x2="8" y2="2" stroke="#000" />
|
||||||
|
<line x1="5" y1="5" x2="2" y2="2" stroke="#000" />
|
||||||
|
<line x1="5" y1="5" x2="2" y2="8" stroke="#000" />
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
10
bin/snapshot-libs.js
vendored
10
bin/snapshot-libs.js
vendored
|
|
@ -7,6 +7,10 @@ require('normalize-url');
|
||||||
require('parse-url');
|
require('parse-url');
|
||||||
require('php-escape-shell');
|
require('php-escape-shell');
|
||||||
require('plist');
|
require('plist');
|
||||||
|
require('react-deep-force-update');
|
||||||
|
require('react-dom');
|
||||||
|
require('react-redux');
|
||||||
|
require('react');
|
||||||
require('redux-thunk');
|
require('redux-thunk');
|
||||||
require('redux');
|
require('redux');
|
||||||
require('reselect');
|
require('reselect');
|
||||||
|
|
@ -18,14 +22,10 @@ if (false) {
|
||||||
require('args');
|
require('args');
|
||||||
require('mousetrap');
|
require('mousetrap');
|
||||||
require('open');
|
require('open');
|
||||||
require('react-dom');
|
|
||||||
require('react-redux');
|
|
||||||
require('react');
|
|
||||||
require('xterm-addon-fit');
|
require('xterm-addon-fit');
|
||||||
require('xterm-addon-image');
|
require('xterm-addon-ligatures');
|
||||||
require('xterm-addon-search');
|
require('xterm-addon-search');
|
||||||
require('xterm-addon-web-links');
|
require('xterm-addon-web-links');
|
||||||
require('xterm-addon-webgl');
|
require('xterm-addon-webgl');
|
||||||
require('xterm-addon-canvas');
|
|
||||||
require('xterm');
|
require('xterm');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
!macro customInstall
|
!macro customInstall
|
||||||
WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper" "" "Open &Hyper here"
|
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" "Icon" "$appExe"
|
||||||
WriteRegStr HKCU "Software\Classes\Directory\Background\shell\Hyper\command" "" `"$appExe" "%V"`
|
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" "" "Open Hyper here"
|
||||||
WriteRegStr HKCU "Software\Classes\Directory\shell\Hyper" "Icon" `"$appExe"`
|
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\command" "" `$appExe "%V"`
|
||||||
|
|
||||||
WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper" "" "Open &Hyper here"
|
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" "Icon" "$appExe"
|
||||||
WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper\command" "" `"$appExe" "%V"`
|
WriteRegStr HKCU "Software\Classes\Drive\shell\Hyper\command" "" `$appExe "%V"`
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro customUnInstall
|
!macro customUnInstall
|
||||||
|
|
|
||||||
70
cli/api.ts
70
cli/api.ts
|
|
@ -2,27 +2,28 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
import registryUrlModule from 'registry-url';
|
import registryUrlModule from 'registry-url';
|
||||||
|
|
||||||
const registryUrl = registryUrlModule();
|
const registryUrl = registryUrlModule();
|
||||||
|
import pify from 'pify';
|
||||||
|
import * as recast from 'recast';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
// If the user defines XDG_CONFIG_HOME they definitely want their config there,
|
// 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
|
// otherwise use the home directory in linux/mac and userdata in windows
|
||||||
const applicationDirectory = process.env.XDG_CONFIG_HOME
|
const applicationDirectory =
|
||||||
? path.join(process.env.XDG_CONFIG_HOME, 'Hyper')
|
process.env.XDG_CONFIG_HOME !== undefined
|
||||||
: process.platform === 'win32'
|
? path.join(process.env.XDG_CONFIG_HOME, 'hyper')
|
||||||
|
: process.platform == 'win32'
|
||||||
? path.join(process.env.APPDATA!, 'Hyper')
|
? path.join(process.env.APPDATA!, 'Hyper')
|
||||||
: path.join(os.homedir(), '.config', 'Hyper');
|
: os.homedir();
|
||||||
|
|
||||||
const devConfigFileName = path.join(__dirname, `../hyper.json`);
|
const devConfigFileName = path.join(__dirname, `../.hyper.js`);
|
||||||
|
|
||||||
const fileName =
|
const fileName =
|
||||||
process.env.NODE_ENV !== 'production' && fs.existsSync(devConfigFileName)
|
process.env.NODE_ENV !== 'production' && fs.existsSync(devConfigFileName)
|
||||||
? devConfigFileName
|
? devConfigFileName
|
||||||
: path.join(applicationDirectory, 'hyper.json');
|
: path.join(applicationDirectory, '.hyper.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We need to make sure the file reading and parsing is lazy so that failure to
|
* We need to make sure the file reading and parsing is lazy so that failure to
|
||||||
|
|
@ -32,7 +33,7 @@ const fileName =
|
||||||
function memoize<T extends (...args: any[]) => any>(fn: T): T {
|
function memoize<T extends (...args: any[]) => any>(fn: T): T {
|
||||||
let hasResult = false;
|
let hasResult = false;
|
||||||
let result: any;
|
let result: any;
|
||||||
return ((...args: Parameters<T>) => {
|
return ((...args: any[]) => {
|
||||||
if (!hasResult) {
|
if (!hasResult) {
|
||||||
result = fn(...args);
|
result = fn(...args);
|
||||||
hasResult = true;
|
hasResult = true;
|
||||||
|
|
@ -42,12 +43,34 @@ function memoize<T extends (...args: any[]) => any>(fn: T): T {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFileContents = memoize(() => {
|
const getFileContents = memoize(() => {
|
||||||
|
try {
|
||||||
return fs.readFileSync(fileName, 'utf8');
|
return fs.readFileSync(fileName, 'utf8');
|
||||||
|
} catch (_err) {
|
||||||
|
const err = _err as {code: string};
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
// ENOENT === !exists()
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const getParsedFile = memoize(() => JSON.parse(getFileContents()));
|
const getParsedFile = memoize(() => recast.parse(getFileContents()!));
|
||||||
|
|
||||||
const getPluginsByKey = (key: string): any[] => getParsedFile()[key] || [];
|
const getProperties = memoize(
|
||||||
|
(): any[] =>
|
||||||
|
((getParsedFile()?.program?.body as any[]) || []).find(
|
||||||
|
(bodyItem) =>
|
||||||
|
bodyItem.type === 'ExpressionStatement' &&
|
||||||
|
bodyItem.expression.type === 'AssignmentExpression' &&
|
||||||
|
bodyItem.expression.left.object.name === 'module' &&
|
||||||
|
bodyItem.expression.left.property.name === 'exports' &&
|
||||||
|
bodyItem.expression.right.type === 'ObjectExpression'
|
||||||
|
)?.expression?.right?.properties || []
|
||||||
|
);
|
||||||
|
|
||||||
|
const getPluginsByKey = (key: string): any[] =>
|
||||||
|
getProperties().find((property) => property?.key?.name === key)?.value?.elements || [];
|
||||||
|
|
||||||
const getPlugins = memoize(() => {
|
const getPlugins = memoize(() => {
|
||||||
return getPluginsByKey('plugins');
|
return getPluginsByKey('plugins');
|
||||||
|
|
@ -64,13 +87,13 @@ function exists() {
|
||||||
function isInstalled(plugin: string, locally?: boolean) {
|
function isInstalled(plugin: string, locally?: boolean) {
|
||||||
const array = locally ? getLocalPlugins() : getPlugins();
|
const array = locally ? getLocalPlugins() : getPlugins();
|
||||||
if (array && Array.isArray(array)) {
|
if (array && Array.isArray(array)) {
|
||||||
return array.includes(plugin);
|
return array.some((entry) => entry.value === plugin);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save(config: any) {
|
function save() {
|
||||||
return fs.writeFileSync(fileName, JSON.stringify(config, null, 2), 'utf8');
|
return pify(fs.writeFile)(fileName, recast.print(getParsedFile()).code, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPackageName(plugin: string) {
|
function getPackageName(plugin: string) {
|
||||||
|
|
@ -112,25 +135,26 @@ function install(plugin: string, locally?: boolean) {
|
||||||
return Promise.reject(`${plugin} is already installed`);
|
return Promise.reject(`${plugin} is already installed`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = getParsedFile();
|
array.push(recast.types.builders.literal(plugin));
|
||||||
config[locally ? 'localPlugins' : 'plugins'] = [...array, plugin];
|
return save();
|
||||||
save(config);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uninstall(plugin: string) {
|
function uninstall(plugin: string) {
|
||||||
if (!isInstalled(plugin)) {
|
if (!isInstalled(plugin)) {
|
||||||
return Promise.reject(`${plugin} is not installed`);
|
return Promise.reject(`${plugin} is not installed`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = getParsedFile();
|
const index = getPlugins().findIndex((entry) => entry.value === plugin);
|
||||||
config.plugins = getPlugins().filter((p) => p !== plugin);
|
getPlugins().splice(index, 1);
|
||||||
save(config);
|
return save();
|
||||||
}
|
}
|
||||||
|
|
||||||
function list() {
|
function list() {
|
||||||
if (getPlugins().length > 0) {
|
if (getPlugins().length > 0) {
|
||||||
return getPlugins().join('\n');
|
return getPlugins()
|
||||||
|
.map((plugin) => plugin.value)
|
||||||
|
.join('\n');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
cli/index.ts
22
cli/index.ts
|
|
@ -1,20 +1,16 @@
|
||||||
// This is a CLI tool, using console is OK
|
// This is a CLI tool, using console is OK
|
||||||
/* eslint no-console: 0 */
|
/* eslint no-console: 0 */
|
||||||
import {spawn, exec} from 'child_process';
|
import {spawn, exec, SpawnOptions} from 'child_process';
|
||||||
import type {SpawnOptions} from 'child_process';
|
|
||||||
import {existsSync} from 'fs';
|
|
||||||
import {isAbsolute, resolve} from 'path';
|
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 args from 'args';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import open from 'open';
|
||||||
import _columnify from 'columnify';
|
import _columnify from 'columnify';
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
import open from 'open';
|
|
||||||
import ora from 'ora';
|
import ora from 'ora';
|
||||||
|
|
||||||
import {version} from '../app/package.json';
|
|
||||||
|
|
||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
|
|
||||||
let commandPromise: Promise<void> | undefined;
|
let commandPromise: Promise<void> | undefined;
|
||||||
|
|
@ -195,10 +191,8 @@ const main = (argv: string[]) => {
|
||||||
version: false,
|
version: false,
|
||||||
mri: {
|
mri: {
|
||||||
boolean: ['v', 'verbose']
|
boolean: ['v', 'verbose']
|
||||||
},
|
}
|
||||||
mainColor: 'yellow',
|
} as any);
|
||||||
subColor: 'dim'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (commandPromise) {
|
if (commandPromise) {
|
||||||
return commandPromise;
|
return commandPromise;
|
||||||
|
|
@ -238,7 +232,7 @@ const main = (argv: string[]) => {
|
||||||
const opts = {
|
const opts = {
|
||||||
env
|
env
|
||||||
};
|
};
|
||||||
return promisify(exec)(cmd, opts);
|
return pify(exec)(cmd, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type {configOptions} from '../../typings/config';
|
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
|
||||||
import {CONFIG_LOAD, CONFIG_RELOAD} from '../../typings/constants/config';
|
import {HyperActions} from '../hyper';
|
||||||
import type {HyperActions} from '../../typings/hyper';
|
import {configOptions} from '../config';
|
||||||
|
|
||||||
export function loadConfig(config: configOptions): HyperActions {
|
export function loadConfig(config: configOptions): HyperActions {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
import {CLOSE_TAB, CHANGE_TAB} from '../../typings/constants/tabs';
|
import {CLOSE_TAB, CHANGE_TAB} from '../constants/tabs';
|
||||||
import {
|
import {
|
||||||
UI_WINDOW_MAXIMIZE,
|
UI_WINDOW_MAXIMIZE,
|
||||||
UI_WINDOW_UNMAXIMIZE,
|
UI_WINDOW_UNMAXIMIZE,
|
||||||
UI_OPEN_HAMBURGER_MENU,
|
UI_OPEN_HAMBURGER_MENU,
|
||||||
UI_WINDOW_MINIMIZE,
|
UI_WINDOW_MINIMIZE,
|
||||||
UI_WINDOW_CLOSE
|
UI_WINDOW_CLOSE
|
||||||
} from '../../typings/constants/ui';
|
} from '../constants/ui';
|
||||||
import type {HyperDispatch} from '../../typings/hyper';
|
|
||||||
import rpc from '../rpc';
|
import rpc from '../rpc';
|
||||||
|
|
||||||
import {userExitTermGroup, setActiveGroup} from './term-groups';
|
import {userExitTermGroup, setActiveGroup} from './term-groups';
|
||||||
|
import {HyperDispatch} from '../hyper';
|
||||||
|
|
||||||
export function closeTab(uid: string) {
|
export function closeTab(uid: string) {
|
||||||
return (dispatch: HyperDispatch) => {
|
return (dispatch: HyperDispatch) => {
|
||||||
|
|
@ -40,7 +39,7 @@ export function maximize() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_WINDOW_MAXIMIZE,
|
type: UI_WINDOW_MAXIMIZE,
|
||||||
effect() {
|
effect() {
|
||||||
rpc.emit('maximize');
|
rpc.emit('maximize', null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -51,7 +50,7 @@ export function unmaximize() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_WINDOW_UNMAXIMIZE,
|
type: UI_WINDOW_UNMAXIMIZE,
|
||||||
effect() {
|
effect() {
|
||||||
rpc.emit('unmaximize');
|
rpc.emit('unmaximize', null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -73,7 +72,7 @@ export function minimize() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_WINDOW_MINIMIZE,
|
type: UI_WINDOW_MINIMIZE,
|
||||||
effect() {
|
effect() {
|
||||||
rpc.emit('minimize');
|
rpc.emit('minimize', null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -84,7 +83,7 @@ export function close() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_WINDOW_CLOSE,
|
type: UI_WINDOW_CLOSE,
|
||||||
effect() {
|
effect() {
|
||||||
rpc.emit('close');
|
rpc.emit('close', null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {INIT} from '../../typings/constants';
|
|
||||||
import type {HyperDispatch} from '../../typings/hyper';
|
|
||||||
import rpc from '../rpc';
|
import rpc from '../rpc';
|
||||||
|
import {INIT} from '../constants';
|
||||||
|
import {HyperDispatch} from '../hyper';
|
||||||
|
|
||||||
export default function init() {
|
export default function init() {
|
||||||
return (dispatch: HyperDispatch) => {
|
return (dispatch: HyperDispatch) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../../typings/constants/notifications';
|
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications';
|
||||||
import type {HyperActions} from '../../typings/hyper';
|
import {HyperActions} from '../hyper';
|
||||||
|
|
||||||
export function dismissNotification(id: string): HyperActions {
|
export function dismissNotification(id: string): HyperActions {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
import {
|
||||||
SESSION_ADD,
|
SESSION_ADD,
|
||||||
SESSION_RESIZE,
|
SESSION_RESIZE,
|
||||||
|
|
@ -12,13 +14,10 @@ import {
|
||||||
SESSION_USER_DATA,
|
SESSION_USER_DATA,
|
||||||
SESSION_SET_XTERM_TITLE,
|
SESSION_SET_XTERM_TITLE,
|
||||||
SESSION_SEARCH
|
SESSION_SEARCH
|
||||||
} from '../../typings/constants/sessions';
|
} from '../constants/sessions';
|
||||||
import type {HyperState, HyperDispatch, HyperActions} from '../../typings/hyper';
|
import {HyperState, session, HyperDispatch, HyperActions} from '../hyper';
|
||||||
import rpc from '../rpc';
|
|
||||||
import {keys} from '../utils/object';
|
|
||||||
import findBySession from '../utils/term-groups';
|
|
||||||
|
|
||||||
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) => {
|
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
||||||
const {sessions} = getState();
|
const {sessions} = getState();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
@ -31,20 +30,20 @@ export function addSession({uid, shell, pid, cols = null, rows = null, splitDire
|
||||||
rows,
|
rows,
|
||||||
splitDirection,
|
splitDirection,
|
||||||
activeUid: activeUid ? activeUid : sessions.activeUid,
|
activeUid: activeUid ? activeUid : sessions.activeUid,
|
||||||
now,
|
now
|
||||||
profile
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestSession(profile: string | undefined) {
|
export function requestSession() {
|
||||||
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SESSION_REQUEST,
|
type: SESSION_REQUEST,
|
||||||
effect: () => {
|
effect: () => {
|
||||||
const {ui} = getState();
|
const {ui} = getState();
|
||||||
const {cwd} = ui;
|
// the cols and rows from preview session maybe not accurate. so remove.
|
||||||
rpc.emit('new', {cwd, profile});
|
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) => {
|
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SESSION_USER_DATA,
|
type: SESSION_USER_DATA,
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,28 @@
|
||||||
import {SESSION_REQUEST} from '../../typings/constants/sessions';
|
import rpc from '../rpc';
|
||||||
import {
|
import {
|
||||||
DIRECTION,
|
DIRECTION,
|
||||||
TERM_GROUP_RESIZE,
|
TERM_GROUP_RESIZE,
|
||||||
TERM_GROUP_REQUEST,
|
TERM_GROUP_REQUEST,
|
||||||
TERM_GROUP_EXIT,
|
TERM_GROUP_EXIT,
|
||||||
TERM_GROUP_EXIT_ACTIVE
|
TERM_GROUP_EXIT_ACTIVE
|
||||||
} from '../../typings/constants/term-groups';
|
} from '../constants/term-groups';
|
||||||
import type {ITermState, ITermGroup, HyperState, HyperDispatch, HyperActions} from '../../typings/hyper';
|
import {SESSION_REQUEST} from '../constants/sessions';
|
||||||
import rpc from '../rpc';
|
|
||||||
import {getRootGroups} from '../selectors';
|
|
||||||
import findBySession from '../utils/term-groups';
|
import findBySession from '../utils/term-groups';
|
||||||
|
import {getRootGroups} from '../selectors';
|
||||||
import {setActiveSession, ptyExitSession, userExitSession} from './sessions';
|
import {setActiveSession, ptyExitSession, userExitSession} from './sessions';
|
||||||
|
import {ITermState, ITermGroup, HyperState, HyperDispatch, HyperActions} from '../hyper';
|
||||||
|
|
||||||
function requestSplit(direction: 'VERTICAL' | 'HORIZONTAL') {
|
function requestSplit(direction: 'VERTICAL' | 'HORIZONTAL') {
|
||||||
return (_activeUid: string | undefined, _profile: string | undefined) =>
|
return (activeUid: string) =>
|
||||||
(dispatch: HyperDispatch, getState: () => HyperState): void => {
|
(dispatch: HyperDispatch, getState: () => HyperState): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SESSION_REQUEST,
|
type: SESSION_REQUEST,
|
||||||
effect: () => {
|
effect: () => {
|
||||||
const {ui, sessions} = getState();
|
const {ui, sessions} = getState();
|
||||||
const activeUid = _activeUid ? _activeUid : sessions.activeUid;
|
|
||||||
const profile = _profile ? _profile : activeUid ? sessions.sessions[activeUid].profile : window.profileName;
|
|
||||||
rpc.emit('new', {
|
rpc.emit('new', {
|
||||||
splitDirection: direction,
|
splitDirection: direction,
|
||||||
cwd: ui.cwd,
|
cwd: ui.cwd,
|
||||||
activeUid,
|
activeUid: activeUid ? activeUid : sessions.activeUid
|
||||||
profile
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -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) => {
|
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TERM_GROUP_REQUEST,
|
type: TERM_GROUP_REQUEST,
|
||||||
effect: () => {
|
effect: () => {
|
||||||
const {ui, sessions} = getState();
|
const {ui, sessions} = getState();
|
||||||
const {cwd} = ui;
|
const {cwd} = ui;
|
||||||
const activeUid = _activeUid ? _activeUid : sessions.activeUid;
|
|
||||||
const profile = _profile ? _profile : activeUid ? sessions.sessions[activeUid].profile : window.profileName;
|
|
||||||
rpc.emit('new', {
|
rpc.emit('new', {
|
||||||
isNewGroup: true,
|
isNewGroup: true,
|
||||||
cwd,
|
cwd,
|
||||||
activeUid,
|
activeUid: activeUid ? activeUid : sessions.activeUid
|
||||||
profile
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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 {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 {
|
import {
|
||||||
UI_FONT_SIZE_SET,
|
UI_FONT_SIZE_SET,
|
||||||
UI_FONT_SIZE_INCR,
|
UI_FONT_SIZE_INCR,
|
||||||
|
|
@ -23,18 +24,14 @@ import {
|
||||||
UI_OPEN_SSH_URL,
|
UI_OPEN_SSH_URL,
|
||||||
UI_CONTEXTMENU_OPEN,
|
UI_CONTEXTMENU_OPEN,
|
||||||
UI_COMMAND_EXEC
|
UI_COMMAND_EXEC
|
||||||
} from '../../typings/constants/ui';
|
} from '../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';
|
|
||||||
|
|
||||||
import {requestSession, sendSessionData, setActiveSession} from './sessions';
|
|
||||||
import {setActiveGroup} from './term-groups';
|
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) => {
|
return (dispatch: HyperDispatch, getState: () => HyperState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_CONTEXTMENU_OPEN,
|
type: UI_CONTEXTMENU_OPEN,
|
||||||
|
|
@ -272,11 +269,11 @@ export function openFile(path: string) {
|
||||||
}
|
}
|
||||||
rpc.once('session add', ({uid}) => {
|
rpc.once('session add', ({uid}) => {
|
||||||
rpc.once('session data', () => {
|
rpc.once('session data', () => {
|
||||||
dispatch(sendSessionData(uid, command));
|
dispatch(sendSessionData(uid, command, null));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dispatch(requestSession(undefined));
|
dispatch(requestSession());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -300,6 +297,12 @@ export function openSSH(parsedUrl: ReturnType<typeof parseUrl>) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UI_OPEN_SSH_URL,
|
type: UI_OPEN_SSH_URL,
|
||||||
effect() {
|
effect() {
|
||||||
|
const resourseIsValid = /^(?!-)[\w.-]+$/.test(parsedUrl.resource);
|
||||||
|
if (!resourseIsValid) {
|
||||||
|
notify('Invalid ssh url', 'Please check your ssh url and try again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let command = `${parsedUrl.protocol} ${parsedUrl.user ? `${parsedUrl.user}@` : ''}${parsedUrl.resource}`;
|
let command = `${parsedUrl.protocol} ${parsedUrl.user ? `${parsedUrl.user}@` : ''}${parsedUrl.resource}`;
|
||||||
|
|
||||||
if (parsedUrl.port) command += ` -p ${parsedUrl.port}`;
|
if (parsedUrl.port) command += ` -p ${parsedUrl.port}`;
|
||||||
|
|
@ -308,11 +311,11 @@ export function openSSH(parsedUrl: ReturnType<typeof parseUrl>) {
|
||||||
|
|
||||||
rpc.once('session add', ({uid}) => {
|
rpc.once('session add', ({uid}) => {
|
||||||
rpc.once('session data', () => {
|
rpc.once('session data', () => {
|
||||||
dispatch(sendSessionData(uid, command));
|
dispatch(sendSessionData(uid, command, null));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(requestSession(undefined));
|
dispatch(requestSession());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import {UPDATE_INSTALL, UPDATE_AVAILABLE} from '../../typings/constants/updater';
|
import {UPDATE_INSTALL, UPDATE_AVAILABLE} from '../constants/updater';
|
||||||
import type {HyperActions} from '../../typings/hyper';
|
|
||||||
import rpc from '../rpc';
|
import rpc from '../rpc';
|
||||||
|
import {HyperActions} from '../hyper';
|
||||||
|
|
||||||
export function installUpdate(): HyperActions {
|
export function installUpdate(): HyperActions {
|
||||||
return {
|
return {
|
||||||
type: UPDATE_INSTALL,
|
type: UPDATE_INSTALL,
|
||||||
effect: () => {
|
effect: () => {
|
||||||
rpc.emit('quit and install');
|
rpc.emit('quit and install', null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {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<string, (event: any, dispatch: HyperDispatch) => void> = {
|
let commands: Record<string, (event: any, dispatch: HyperDispatch) => void> = {
|
||||||
'editor:search-close': (e, dispatch) => {
|
'editor:search-close': (e, dispatch) => {
|
||||||
|
|
@ -10,8 +12,8 @@ let commands: Record<string, (event: any, dispatch: HyperDispatch) => void> = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRegisteredKeys = async () => {
|
export const getRegisteredKeys = () => {
|
||||||
const keymaps = await ipcRenderer.invoke('getDecoratedKeymaps');
|
const keymaps = getDecoratedKeymaps();
|
||||||
|
|
||||||
return Object.keys(keymaps).reduce((result: Record<string, string>, actionName) => {
|
return Object.keys(keymaps).reduce((result: Record<string, string>, actionName) => {
|
||||||
const commandKeys = keymaps[actionName];
|
const commandKeys = keymaps[actionName];
|
||||||
|
|
|
||||||
|
|
@ -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 {decorate, getTabsProps} from '../utils/plugins';
|
||||||
|
|
||||||
import Tabs_ from './tabs';
|
import Tabs_ from './tabs';
|
||||||
|
import {HeaderProps} from '../hyper';
|
||||||
|
|
||||||
const Tabs = decorate(Tabs_, 'Tabs');
|
const Tabs = decorate(Tabs_, 'Tabs');
|
||||||
|
|
||||||
const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
export default class Header extends React.PureComponent<HeaderProps> {
|
||||||
const [headerMouseDownWindowX, setHeaderMouseDownWindowX] = useState<number>(0);
|
headerMouseDownWindowX!: number;
|
||||||
const [headerMouseDownWindowY, setHeaderMouseDownWindowY] = useState<number>(0);
|
headerMouseDownWindowY!: number;
|
||||||
|
|
||||||
const onChangeIntent = (active: string) => {
|
onChangeIntent = (active: string) => {
|
||||||
// we ignore clicks if they're a byproduct of a drag
|
// we ignore clicks if they're a byproduct of a drag
|
||||||
// motion to move the window
|
// motion to move the window
|
||||||
if (window.screenX !== headerMouseDownWindowX || window.screenY !== headerMouseDownWindowY) {
|
if (window.screenX !== this.headerMouseDownWindowX || window.screenY !== this.headerMouseDownWindowY) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
props.onChangeTab(active);
|
this.props.onChangeTab(active);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHeaderMouseDown = () => {
|
handleHeaderMouseDown = () => {
|
||||||
// the hack of all hacks, this prevents the term
|
// the hack of all hacks, this prevents the term
|
||||||
// iframe from losing focus, for example, when
|
// iframe from losing focus, for example, when
|
||||||
// the user drags the nav around
|
// the user drags the nav around
|
||||||
|
|
@ -30,43 +30,43 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
|
|
||||||
// persist start positions of a potential drag motion
|
// persist start positions of a potential drag motion
|
||||||
// to differentiate dragging from clicking
|
// to differentiate dragging from clicking
|
||||||
setHeaderMouseDownWindowX(window.screenX);
|
this.headerMouseDownWindowX = window.screenX;
|
||||||
setHeaderMouseDownWindowY(window.screenY);
|
this.headerMouseDownWindowY = window.screenY;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHamburgerMenuClick = (event: React.MouseEvent) => {
|
handleHamburgerMenuClick = (event: React.MouseEvent) => {
|
||||||
let {right: x, bottom: y} = event.currentTarget.getBoundingClientRect();
|
let {right: x, bottom: y} = event.currentTarget.getBoundingClientRect();
|
||||||
x -= 15; // to compensate padding
|
x -= 15; // to compensate padding
|
||||||
y -= 12; // ^ same
|
y -= 12; // ^ same
|
||||||
props.openHamburgerMenu({x, y});
|
this.props.openHamburgerMenu({x, y});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMaximizeClick = () => {
|
handleMaximizeClick = () => {
|
||||||
if (props.maximized) {
|
if (this.props.maximized) {
|
||||||
props.unmaximize();
|
this.props.unmaximize();
|
||||||
} else {
|
} else {
|
||||||
props.maximize();
|
this.props.maximize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMinimizeClick = () => {
|
handleMinimizeClick = () => {
|
||||||
props.minimize();
|
this.props.minimize();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseClick = () => {
|
handleCloseClick = () => {
|
||||||
props.close();
|
this.props.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWindowHeaderConfig = () => {
|
getWindowHeaderConfig() {
|
||||||
const {showHamburgerMenu, showWindowControls} = props;
|
const {showHamburgerMenu, showWindowControls} = this.props;
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
hambMenu: !props.isMac, // show by default on windows and linux
|
hambMenu: !this.props.isMac, // show by default on windows and linux
|
||||||
winCtrls: !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
|
// don't allow the user to change defaults on macOS
|
||||||
if (props.isMac) {
|
if (this.props.isMac) {
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,28 +74,35 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
hambMenu: showHamburgerMenu === '' ? defaults.hambMenu : showHamburgerMenu,
|
hambMenu: showHamburgerMenu === '' ? defaults.hambMenu : showHamburgerMenu,
|
||||||
winCtrls: showWindowControls === '' ? defaults.winCtrls : showWindowControls
|
winCtrls: showWindowControls === '' ? defaults.winCtrls : showWindowControls
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
const {isMac} = props;
|
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;
|
const {borderColor} = props;
|
||||||
let title = 'Hyper';
|
let title = 'Hyper';
|
||||||
if (props.tabs.length === 1 && props.tabs[0].title) {
|
if (props.tabs.length === 1 && props.tabs[0].title) {
|
||||||
// if there's only one tab we use its title as the window title
|
// if there's only one tab we use its title as the window title
|
||||||
title = props.tabs[0].title;
|
title = props.tabs[0].title;
|
||||||
}
|
}
|
||||||
const {hambMenu, winCtrls} = getWindowHeaderConfig();
|
const {hambMenu, winCtrls} = this.getWindowHeaderConfig();
|
||||||
const left = winCtrls === 'left';
|
const left = winCtrls === 'left';
|
||||||
const maxButtonHref = props.maximized
|
const maxButtonHref = this.props.maximized
|
||||||
? './renderer/assets/icons.svg#restore-window'
|
? './renderer/assets/icons.svg#restore-window'
|
||||||
: './renderer/assets/icons.svg#maximize-window';
|
: './renderer/assets/icons.svg#maximize-window';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={`header_header ${isMac && 'header_headerRounded'}`}
|
className={`header_header ${isMac && 'header_headerRounded'}`}
|
||||||
onMouseDown={handleHeaderMouseDown}
|
onMouseDown={this.handleHeaderMouseDown}
|
||||||
onMouseUp={() => window.focusActiveTerm()}
|
onMouseUp={() => window.focusActiveTerm()}
|
||||||
onDoubleClick={handleMaximizeClick}
|
onDoubleClick={this.handleMaximizeClick}
|
||||||
ref={ref}
|
|
||||||
>
|
>
|
||||||
{!isMac && (
|
{!isMac && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -105,7 +112,7 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
{hambMenu && (
|
{hambMenu && (
|
||||||
<svg
|
<svg
|
||||||
className={`header_shape ${left ? 'header_hamburgerMenuRight' : 'header_hamburgerMenuLeft'}`}
|
className={`header_shape ${left ? 'header_hamburgerMenuRight' : 'header_hamburgerMenuLeft'}`}
|
||||||
onClick={handleHamburgerMenuClick}
|
onClick={this.handleHamburgerMenuClick}
|
||||||
>
|
>
|
||||||
<use xlinkHref="./renderer/assets/icons.svg#hamburger-menu" />
|
<use xlinkHref="./renderer/assets/icons.svg#hamburger-menu" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -113,17 +120,20 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
<span className="header_appTitle">{title}</span>
|
<span className="header_appTitle">{title}</span>
|
||||||
{winCtrls && (
|
{winCtrls && (
|
||||||
<div className={`header_windowControls ${left ? 'header_windowControlsLeft' : ''}`}>
|
<div className={`header_windowControls ${left ? 'header_windowControlsLeft' : ''}`}>
|
||||||
<div className={`${left ? 'header_minimizeWindowLeft' : ''}`} onClick={handleMinimizeClick}>
|
<div className={`${left ? 'header_minimizeWindowLeft' : ''}`} onClick={this.handleMinimizeClick}>
|
||||||
<svg className="header_shape">
|
<svg className="header_shape">
|
||||||
<use xlinkHref="./renderer/assets/icons.svg#minimize-window" />
|
<use xlinkHref="./renderer/assets/icons.svg#minimize-window" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${left ? 'header_maximizeWindowLeft' : ''}`} onClick={handleMaximizeClick}>
|
<div className={`${left ? 'header_maximizeWindowLeft' : ''}`} onClick={this.handleMaximizeClick}>
|
||||||
<svg className="header_shape">
|
<svg className="header_shape">
|
||||||
<use xlinkHref={maxButtonHref} />
|
<use xlinkHref={maxButtonHref} />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className={`header_closeWindow ${left ? 'header_closeWindowLeft' : ''}`} onClick={handleCloseClick}>
|
<div
|
||||||
|
className={`header_closeWindow ${left ? 'header_closeWindowLeft' : ''}`}
|
||||||
|
onClick={this.handleCloseClick}
|
||||||
|
>
|
||||||
<svg className="header_shape">
|
<svg className="header_shape">
|
||||||
<use xlinkHref="./renderer/assets/icons.svg#close-window" />
|
<use xlinkHref="./renderer/assets/icons.svg#close-window" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -132,21 +142,9 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
<Tabs
|
<Tabs {...props} />
|
||||||
{...getTabsProps(props, {
|
{this.props.customChildren}
|
||||||
tabs: props.tabs,
|
|
||||||
borderColor: props.borderColor,
|
|
||||||
backgroundColor: props.backgroundColor,
|
|
||||||
onClose: props.onCloseTab,
|
|
||||||
onChange: onChangeIntent,
|
|
||||||
fullScreen: props.fullScreen,
|
|
||||||
defaultProfile: props.defaultProfile,
|
|
||||||
profiles: props.profiles.asMutable({deep: true}),
|
|
||||||
openNewTab: props.openNewTab
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
{props.customChildren}
|
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.header_header {
|
.header_header {
|
||||||
|
|
@ -253,8 +251,5 @@ const Header = forwardRef<HTMLElement, HeaderProps>((props, ref) => {
|
||||||
`}</style>
|
`}</style>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Header.displayName = 'Header';
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
title="New Tab"
|
|
||||||
className={`new_tab ${tabsVisible ? 'tabs_visible' : 'tabs_hidden'}`}
|
|
||||||
onClick={toggleDropdown}
|
|
||||||
onDoubleClick={(e) => e.stopPropagation()}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
<VscChevronDown style={{verticalAlign: 'middle'}} />
|
|
||||||
|
|
||||||
{dropdownOpen && (
|
|
||||||
<ul
|
|
||||||
key="dropdown"
|
|
||||||
className="profile_dropdown"
|
|
||||||
style={{
|
|
||||||
borderColor,
|
|
||||||
backgroundColor
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{profiles.map((profile) => (
|
|
||||||
<li
|
|
||||||
key={profile.name}
|
|
||||||
onClick={() => {
|
|
||||||
openNewTab(profile.name);
|
|
||||||
setDropdownOpen(false);
|
|
||||||
}}
|
|
||||||
className={`profile_dropdown_item ${
|
|
||||||
profile.name === defaultProfile && profiles.length > 1 ? 'profile_dropdown_item_default' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{profile.name}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<style jsx>{`
|
|
||||||
.profile_dropdown {
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-bottom-width: 0px;
|
|
||||||
border-right-width: 0px;
|
|
||||||
position: absolute;
|
|
||||||
top: 33px;
|
|
||||||
right: 0px;
|
|
||||||
z-index: 1000;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
list-style-type: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile_dropdown_item {
|
|
||||||
padding: 0px 20px;
|
|
||||||
height: 34px;
|
|
||||||
line-height: 34px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: transparent;
|
|
||||||
border-width: 0px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
border-bottom-color: ${borderColor};
|
|
||||||
text-align: start;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile_dropdown_item:hover {
|
|
||||||
background-color: ${borderColor};
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile_dropdown_item_default {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new_tab {
|
|
||||||
background: transparent;
|
|
||||||
color: #fff;
|
|
||||||
border-left: 1px;
|
|
||||||
border-bottom: 1px;
|
|
||||||
border-left-style: solid;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
border-left-width: 1px;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
height: 34px;
|
|
||||||
line-height: 34px;
|
|
||||||
padding: 0 16px;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
${isMac ? '-webkit-app-region: drag;' : ''}
|
|
||||||
top: '0px';
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs_visible {
|
|
||||||
border-color: ${borderColor};
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs_hidden {
|
|
||||||
border-color: transparent;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs_hidden:hover {
|
|
||||||
border-color: ${borderColor};
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DropdownButton;
|
|
||||||
|
|
@ -1,76 +1,84 @@
|
||||||
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<NotificationProps, NotificationState> {
|
||||||
|
dismissTimer!: NodeJS.Timeout;
|
||||||
|
constructor(props: NotificationProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
dismissing: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const Notification = forwardRef<HTMLDivElement, React.PropsWithChildren<NotificationProps>>((props, ref) => {
|
componentDidMount() {
|
||||||
const dismissTimer = useRef<NodeJS.Timeout | undefined>(undefined);
|
if (this.props.dismissAfter) {
|
||||||
const [dismissing, setDismissing] = useState(false);
|
this.setDismissTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
componentDidUpdate(prevProps: NotificationProps, prevState: NotificationState) {
|
||||||
setDismissTimer();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// if we have a timer going and the notification text
|
// if we have a timer going and the notification text
|
||||||
// changed we reset the timer
|
// changed we reset the timer
|
||||||
resetDismissTimer();
|
if (this.props.text !== prevProps.text) {
|
||||||
setDismissing(false);
|
if (prevProps.dismissAfter) {
|
||||||
}, [props.text]);
|
this.resetDismissTimer();
|
||||||
|
}
|
||||||
|
if (prevState.dismissing) {
|
||||||
|
this.setState({dismissing: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleDismiss = () => {
|
handleDismiss = () => {
|
||||||
setDismissing(true);
|
this.setState({dismissing: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onElement = (el: HTMLDivElement | null) => {
|
onElement = (el: HTMLDivElement | null) => {
|
||||||
if (el) {
|
if (el) {
|
||||||
el.addEventListener('webkitTransitionEnd', () => {
|
el.addEventListener('webkitTransitionEnd', () => {
|
||||||
if (dismissing) {
|
if (this.state.dismissing) {
|
||||||
props.onDismiss();
|
this.props.onDismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const {backgroundColor} = props;
|
const {backgroundColor} = this.props;
|
||||||
if (backgroundColor) {
|
if (backgroundColor) {
|
||||||
el.style.setProperty('background-color', backgroundColor, 'important');
|
el.style.setProperty('background-color', backgroundColor, 'important');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref) {
|
|
||||||
if (typeof ref === 'function') ref(el);
|
|
||||||
else ref.current = el;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setDismissTimer = () => {
|
setDismissTimer() {
|
||||||
if (typeof props.dismissAfter === 'number') {
|
this.dismissTimer = setTimeout(() => {
|
||||||
dismissTimer.current = setTimeout(() => {
|
this.handleDismiss();
|
||||||
handleDismiss();
|
}, this.props.dismissAfter);
|
||||||
}, props.dismissAfter);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const resetDismissTimer = () => {
|
resetDismissTimer() {
|
||||||
clearTimeout(dismissTimer.current);
|
clearTimeout(this.dismissTimer);
|
||||||
setDismissTimer();
|
this.setDismissTimer();
|
||||||
};
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
componentWillUnmount() {
|
||||||
return () => {
|
clearTimeout(this.dismissTimer);
|
||||||
clearTimeout(dismissTimer.current);
|
}
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const {backgroundColor, color} = props;
|
render() {
|
||||||
const opacity = dismissing ? 0 : 1;
|
const {backgroundColor, color} = this.props;
|
||||||
|
const opacity = this.state.dismissing ? 0 : 1;
|
||||||
return (
|
return (
|
||||||
<div ref={onElement} style={{opacity, backgroundColor, color}} className="notification_indicator">
|
<div ref={this.onElement} style={{opacity, backgroundColor, color}} className="notification_indicator">
|
||||||
{props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
{props.children || props.text}
|
{this.props.children || this.props.text}
|
||||||
{props.userDismissable ? (
|
{this.props.userDismissable ? (
|
||||||
<a className="notification_dismissLink" onClick={handleDismiss} style={{color: props.userDismissColor}}>
|
<a
|
||||||
|
className="notification_dismissLink"
|
||||||
|
onClick={this.handleDismiss}
|
||||||
|
style={{color: this.props.userDismissColor}}
|
||||||
|
>
|
||||||
[x]
|
[x]
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
{props.customChildren}
|
{this.props.customChildren}
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.notification_indicator {
|
.notification_indicator {
|
||||||
|
|
@ -103,8 +111,5 @@ const Notification = forwardRef<HTMLDivElement, React.PropsWithChildren<Notifica
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Notification.displayName = 'Notification';
|
|
||||||
|
|
||||||
export default Notification;
|
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,99 @@
|
||||||
import React, {forwardRef} from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type {NotificationsProps} from '../../typings/hyper';
|
|
||||||
import {decorate} from '../utils/plugins';
|
import {decorate} from '../utils/plugins';
|
||||||
|
|
||||||
import Notification_ from './notification';
|
import Notification_ from './notification';
|
||||||
|
import {NotificationsProps} from '../hyper';
|
||||||
|
|
||||||
const Notification = decorate(Notification_, 'Notification');
|
const Notification = decorate(Notification_, 'Notification');
|
||||||
|
|
||||||
const Notifications = forwardRef<HTMLDivElement, NotificationsProps>((props, ref) => {
|
export default class Notifications extends React.PureComponent<NotificationsProps> {
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="notifications_view" ref={ref}>
|
<div className="notifications_view">
|
||||||
{props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
{props.fontShowing && (
|
{this.props.fontShowing && (
|
||||||
<Notification
|
<Notification
|
||||||
key="font"
|
key="font"
|
||||||
backgroundColor="rgba(255, 255, 255, .2)"
|
backgroundColor="rgba(255, 255, 255, .2)"
|
||||||
text={`${props.fontSize}px`}
|
text={`${this.props.fontSize}px`}
|
||||||
userDismissable={false}
|
userDismissable={false}
|
||||||
onDismiss={props.onDismissFont}
|
onDismiss={this.props.onDismissFont}
|
||||||
dismissAfter={1000}
|
dismissAfter={1000}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.resizeShowing && (
|
{this.props.resizeShowing && (
|
||||||
<Notification
|
<Notification
|
||||||
key="resize"
|
key="resize"
|
||||||
backgroundColor="rgba(255, 255, 255, .2)"
|
backgroundColor="rgba(255, 255, 255, .2)"
|
||||||
text={`${props.cols}x${props.rows}`}
|
text={`${this.props.cols}x${this.props.rows}`}
|
||||||
userDismissable={false}
|
userDismissable={false}
|
||||||
onDismiss={props.onDismissResize}
|
onDismiss={this.props.onDismissResize}
|
||||||
dismissAfter={1000}
|
dismissAfter={1000}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.messageShowing && (
|
{this.props.messageShowing && (
|
||||||
<Notification
|
<Notification
|
||||||
key="message"
|
key="message"
|
||||||
backgroundColor="#FE354E"
|
backgroundColor="#FE354E"
|
||||||
color="#fff"
|
color="#fff"
|
||||||
text={props.messageText}
|
text={this.props.messageText}
|
||||||
onDismiss={props.onDismissMessage}
|
onDismiss={this.props.onDismissMessage}
|
||||||
userDismissable={props.messageDismissable}
|
userDismissable={this.props.messageDismissable}
|
||||||
>
|
>
|
||||||
{props.messageURL ? (
|
{this.props.messageURL
|
||||||
<>
|
? [
|
||||||
{props.messageText} (
|
this.props.messageText,
|
||||||
|
' (',
|
||||||
<a
|
<a
|
||||||
|
key="link"
|
||||||
style={{color: '#fff'}}
|
style={{color: '#fff'}}
|
||||||
onClick={(ev) => {
|
onClick={(ev) => {
|
||||||
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}}
|
}}
|
||||||
href={props.messageURL}
|
href={this.props.messageURL}
|
||||||
>
|
>
|
||||||
more
|
more
|
||||||
</a>
|
</a>,
|
||||||
)
|
') '
|
||||||
</>
|
]
|
||||||
) : null}
|
: null}
|
||||||
</Notification>
|
</Notification>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.updateShowing && (
|
{this.props.updateShowing && (
|
||||||
<Notification
|
<Notification
|
||||||
key="update"
|
key="update"
|
||||||
backgroundColor="#18E179"
|
backgroundColor="#18E179"
|
||||||
color="#000"
|
color="#000"
|
||||||
text={`Version ${props.updateVersion} ready`}
|
text={`Version ${this.props.updateVersion} ready`}
|
||||||
onDismiss={props.onDismissUpdate}
|
onDismiss={this.props.onDismissUpdate}
|
||||||
userDismissable
|
userDismissable
|
||||||
>
|
>
|
||||||
Version <b>{props.updateVersion}</b> ready.
|
Version <b>{this.props.updateVersion}</b> ready.
|
||||||
{props.updateNote && ` ${props.updateNote.trim().replace(/\.$/, '')}`} (
|
{this.props.updateNote && ` ${this.props.updateNote.trim().replace(/\.$/, '')}`} (
|
||||||
<a
|
<a
|
||||||
style={{color: '#000'}}
|
style={{color: '#000'}}
|
||||||
onClick={(ev) => {
|
onClick={(ev) => {
|
||||||
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}}
|
}}
|
||||||
href={`https://github.com/vercel/hyper/releases/tag/${props.updateVersion}`}
|
href={`https://github.com/vercel/hyper/releases/tag/${this.props.updateVersion}`}
|
||||||
>
|
>
|
||||||
notes
|
notes
|
||||||
</a>
|
</a>
|
||||||
).{' '}
|
).{' '}
|
||||||
{props.updateCanInstall ? (
|
{this.props.updateCanInstall ? (
|
||||||
<a
|
<a
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
}}
|
}}
|
||||||
onClick={props.onUpdateInstall}
|
onClick={this.props.onUpdateInstall}
|
||||||
>
|
>
|
||||||
Restart
|
Restart
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -106,7 +109,7 @@ const Notifications = forwardRef<HTMLDivElement, NotificationsProps>((props, ref
|
||||||
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
void window.require('electron').shell.openExternal(ev.currentTarget.href);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}}
|
}}
|
||||||
href={props.updateReleaseUrl!}
|
href={this.props.updateReleaseUrl!}
|
||||||
>
|
>
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -114,7 +117,7 @@ const Notifications = forwardRef<HTMLDivElement, NotificationsProps>((props, ref
|
||||||
.{' '}
|
.{' '}
|
||||||
</Notification>
|
</Notification>
|
||||||
)}
|
)}
|
||||||
{props.customChildren}
|
{this.props.customChildren}
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.notifications_view {
|
.notifications_view {
|
||||||
|
|
@ -125,8 +128,5 @@ const Notifications = forwardRef<HTMLDivElement, NotificationsProps>((props, ref
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Notifications.displayName = 'Notifications';
|
|
||||||
|
|
||||||
export default Notifications;
|
|
||||||
|
|
|
||||||
|
|
@ -1,237 +1,80 @@
|
||||||
import React, {useCallback, useRef, useEffect, forwardRef} from 'react';
|
import React from 'react';
|
||||||
|
import {SearchBoxProps} from '../hyper';
|
||||||
|
|
||||||
import {VscArrowDown} from '@react-icons/all-files/vsc/VscArrowDown';
|
const searchBoxStyling: React.CSSProperties = {
|
||||||
import {VscArrowUp} from '@react-icons/all-files/vsc/VscArrowUp';
|
float: 'right',
|
||||||
import {VscCaseSensitive} from '@react-icons/all-files/vsc/VscCaseSensitive';
|
height: '28px',
|
||||||
import {VscClose} from '@react-icons/all-files/vsc/VscClose';
|
backgroundColor: 'white',
|
||||||
import {VscRegex} from '@react-icons/all-files/vsc/VscRegex';
|
position: 'absolute',
|
||||||
import {VscWholeWord} from '@react-icons/all-files/vsc/VscWholeWord';
|
right: '10px',
|
||||||
import clsx from 'clsx';
|
top: '0px',
|
||||||
|
width: '224px',
|
||||||
import type {SearchBoxProps} from '../../typings/hyper';
|
zIndex: 9999
|
||||||
|
|
||||||
type SearchButtonColors = {
|
|
||||||
foregroundColor: string;
|
|
||||||
selectionColor: string;
|
|
||||||
backgroundColor: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type SearchButtonProps = React.PropsWithChildren<
|
const enterKey = 13;
|
||||||
{
|
|
||||||
onClick: () => void;
|
|
||||||
active: boolean;
|
|
||||||
title: string;
|
|
||||||
} & SearchButtonColors
|
|
||||||
>;
|
|
||||||
|
|
||||||
const SearchButton = ({
|
export default class SearchBox extends React.PureComponent<SearchBoxProps> {
|
||||||
onClick,
|
searchTerm: string;
|
||||||
active,
|
constructor(props: SearchBoxProps) {
|
||||||
title,
|
super(props);
|
||||||
foregroundColor,
|
this.searchTerm = '';
|
||||||
backgroundColor,
|
|
||||||
selectionColor,
|
|
||||||
children
|
|
||||||
}: SearchButtonProps) => {
|
|
||||||
const handleKeyUp = useCallback(
|
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
|
||||||
onClick();
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[onClick]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
handleChange = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
this.searchTerm = event.currentTarget.value;
|
||||||
|
if (event.keyCode === enterKey) {
|
||||||
|
this.props.search(event.currentTarget.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div style={searchBoxStyling}>
|
||||||
onClick={onClick}
|
<input type="text" className="search-box" onKeyUp={this.handleChange} ref={(input) => input?.focus()} />
|
||||||
className={clsx('search-button', {'search-button-active': active})}
|
<svg className="search-button" onClick={() => this.props.prev(this.searchTerm)}>
|
||||||
tabIndex={0}
|
<use xlinkHref="./renderer/assets/search-icons.svg#left-arrow" />
|
||||||
onKeyUp={handleKeyUp}
|
</svg>
|
||||||
title={title}
|
<svg className="search-button" onClick={() => this.props.next(this.searchTerm)}>
|
||||||
>
|
<use xlinkHref="./renderer/assets/search-icons.svg#right-arrow" />
|
||||||
{children}
|
</svg>
|
||||||
|
<svg className="search-button" onClick={() => this.props.close()}>
|
||||||
|
<use xlinkHref="./renderer/assets/search-icons.svg#cancel" />
|
||||||
|
</svg>
|
||||||
<style jsx>
|
<style jsx>
|
||||||
{`
|
{`
|
||||||
.search-button {
|
|
||||||
cursor: pointer;
|
|
||||||
color: ${foregroundColor};
|
|
||||||
padding: 2px;
|
|
||||||
margin: 4px 0px;
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button:focus {
|
|
||||||
outline: ${selectionColor} solid 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button:hover {
|
|
||||||
background-color: ${backgroundColor};
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button-active {
|
|
||||||
background-color: ${selectionColor};
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button-active:hover {
|
|
||||||
background-color: ${selectionColor};
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const SearchBox = forwardRef<HTMLDivElement, SearchBoxProps>((props, ref) => {
|
|
||||||
const {
|
|
||||||
caseSensitive,
|
|
||||||
wholeWord,
|
|
||||||
regex,
|
|
||||||
results,
|
|
||||||
toggleCaseSensitive,
|
|
||||||
toggleWholeWord,
|
|
||||||
toggleRegex,
|
|
||||||
next,
|
|
||||||
prev,
|
|
||||||
close,
|
|
||||||
backgroundColor,
|
|
||||||
foregroundColor,
|
|
||||||
borderColor,
|
|
||||||
selectionColor,
|
|
||||||
font
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const searchTermRef = useRef<string>('');
|
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
||||||
|
|
||||||
const handleChange = useCallback(
|
|
||||||
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex-row search-container" ref={ref}>
|
|
||||||
<div className="flex-row search-box">
|
|
||||||
<input className="search-input" type="text" onKeyDown={handleChange} ref={inputRef} placeholder="Search" />
|
|
||||||
|
|
||||||
<SearchButton onClick={toggleCaseSensitive} active={caseSensitive} title="Match Case" {...searchButtonColors}>
|
|
||||||
<VscCaseSensitive size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
|
|
||||||
<SearchButton onClick={toggleWholeWord} active={wholeWord} title="Match Whole Word" {...searchButtonColors}>
|
|
||||||
<VscWholeWord size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
|
|
||||||
<SearchButton onClick={toggleRegex} active={regex} title="Use Regular Expression" {...searchButtonColors}>
|
|
||||||
<VscRegex size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span style={{minWidth: '60px', marginLeft: '4px'}}>
|
|
||||||
{results === undefined
|
|
||||||
? ''
|
|
||||||
: results.resultCount === 0
|
|
||||||
? 'No results'
|
|
||||||
: `${results.resultIndex + 1} of ${results.resultCount}`}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className="flex-row">
|
|
||||||
<SearchButton
|
|
||||||
onClick={() => prev(searchTermRef.current)}
|
|
||||||
active={false}
|
|
||||||
title="Previous Match"
|
|
||||||
{...searchButtonColors}
|
|
||||||
>
|
|
||||||
<VscArrowUp size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
|
|
||||||
<SearchButton
|
|
||||||
onClick={() => next(searchTermRef.current)}
|
|
||||||
active={false}
|
|
||||||
title="Next Match"
|
|
||||||
{...searchButtonColors}
|
|
||||||
>
|
|
||||||
<VscArrowDown size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
|
|
||||||
<SearchButton onClick={close} active={false} title="Close" {...searchButtonColors}>
|
|
||||||
<VscClose size="14px" />
|
|
||||||
</SearchButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style jsx>
|
|
||||||
{`
|
|
||||||
.search-container {
|
|
||||||
background-color: ${backgroundColor};
|
|
||||||
border: 1px solid ${borderColor};
|
|
||||||
border-radius: 2px;
|
|
||||||
position: absolute;
|
|
||||||
right: 13px;
|
|
||||||
top: 4px;
|
|
||||||
z-index: 10;
|
|
||||||
padding: 4px;
|
|
||||||
font-family: ${font};
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
outline: none;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
color: ${foregroundColor};
|
|
||||||
align-self: stretch;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
.search-box {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
width: 152px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 2px;
|
float: left;
|
||||||
outline: ${borderColor} solid 1px;
|
|
||||||
background-color: ${backgroundColor};
|
|
||||||
color: ${foregroundColor};
|
|
||||||
padding: 0px 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input::placeholder {
|
.search-box:focus {
|
||||||
color: ${foregroundColor};
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box:focus-within {
|
.search-button {
|
||||||
outline: ${selectionColor} solid 2px;
|
background-color: #ffffff;
|
||||||
|
color: black;
|
||||||
|
padding: 7px 5.5px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
transition-duration: 0.4s;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 27px;
|
||||||
|
width: 24px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.search-button:hover {
|
||||||
|
background-color: #e7e7e7;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
</style>
|
</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
SearchBox.displayName = 'SearchBox';
|
|
||||||
|
|
||||||
export default SearchBox;
|
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,148 @@
|
||||||
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<SplitPaneProps, {dragging: boolean}> {
|
||||||
|
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<HTMLDivElement, SplitPaneProps>((props, ref) => {
|
setupPanes(ev: any) {
|
||||||
const dragPanePosition = useRef<number>(0);
|
this.panes = Array.from(ev.target.parentNode.childNodes);
|
||||||
const dragTarget = useRef<HTMLDivElement | null>(null);
|
this.paneIndex = this.panes.indexOf(ev.target);
|
||||||
const paneIndex = useRef<number>(0);
|
this.paneIndex -= Math.ceil(this.paneIndex / 2);
|
||||||
const d1 = props.direction === 'horizontal' ? 'height' : 'width';
|
}
|
||||||
const d2 = props.direction === 'horizontal' ? 'top' : 'left';
|
|
||||||
const d3 = props.direction === 'horizontal' ? 'clientY' : 'clientX';
|
|
||||||
const panesSize = useRef<number | null>(null);
|
|
||||||
const [dragging, setDragging] = useState(false);
|
|
||||||
|
|
||||||
const handleAutoResize = (ev: React.MouseEvent<HTMLDivElement>, index: number) => {
|
handleAutoResize = (ev: React.MouseEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
paneIndex.current = index;
|
this.setupPanes(ev);
|
||||||
|
|
||||||
const sizes_ = getSizes();
|
const sizes_ = this.getSizes();
|
||||||
sizes_[paneIndex.current] = 0;
|
sizes_[this.paneIndex] = 0;
|
||||||
sizes_[paneIndex.current + 1] = 0;
|
sizes_[this.paneIndex + 1] = 0;
|
||||||
|
|
||||||
const availableWidth = 1 - sum(sizes_);
|
const availableWidth = 1 - _.sum(sizes_);
|
||||||
sizes_[paneIndex.current] = availableWidth / 2;
|
sizes_[this.paneIndex] = availableWidth / 2;
|
||||||
sizes_[paneIndex.current + 1] = availableWidth / 2;
|
sizes_[this.paneIndex + 1] = availableWidth / 2;
|
||||||
|
|
||||||
props.onResize(sizes_);
|
this.props.onResize(sizes_);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragStart = (ev: React.MouseEvent<HTMLDivElement>, index: number) => {
|
handleDragStart = (ev: any) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
setDragging(true);
|
this.setState({dragging: true});
|
||||||
window.addEventListener('mousemove', onDrag);
|
window.addEventListener('mousemove', this.onDrag);
|
||||||
window.addEventListener('mouseup', onDragEnd);
|
window.addEventListener('mouseup', this.onDragEnd);
|
||||||
|
|
||||||
const target = ev.target as HTMLDivElement;
|
// dimensions to consider
|
||||||
dragTarget.current = target;
|
if (this.props.direction === 'horizontal') {
|
||||||
dragPanePosition.current = dragTarget.current.getBoundingClientRect()[d2];
|
this.d1 = 'height';
|
||||||
panesSize.current = target.parentElement!.getBoundingClientRect()[d1];
|
this.d2 = 'top';
|
||||||
paneIndex.current = index;
|
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 = () => {
|
getSizes() {
|
||||||
const {sizes} = props;
|
const {sizes} = this.props;
|
||||||
let sizes_: number[];
|
let sizes_: number[];
|
||||||
|
|
||||||
if (sizes) {
|
if (sizes) {
|
||||||
sizes_ = [...sizes.asMutable()];
|
sizes_ = [...sizes.asMutable()];
|
||||||
} else {
|
} else {
|
||||||
const total = props.children.length;
|
const total = (this.props.children as React.ReactNodeArray).length;
|
||||||
const count = new Array<number>(total).fill(1 / total);
|
const count = new Array<number>(total).fill(1 / total);
|
||||||
|
|
||||||
sizes_ = count;
|
sizes_ = count;
|
||||||
}
|
}
|
||||||
return sizes_;
|
return sizes_;
|
||||||
};
|
}
|
||||||
|
|
||||||
const onDrag = (ev: MouseEvent) => {
|
onDrag = (ev: MouseEvent) => {
|
||||||
const sizes_ = getSizes();
|
const sizes_ = this.getSizes();
|
||||||
|
|
||||||
const i = paneIndex.current;
|
const i = this.paneIndex;
|
||||||
const pos = ev[d3];
|
const pos = ev[this.d3];
|
||||||
const d = Math.abs(dragPanePosition.current - pos) / panesSize.current!;
|
const d = Math.abs(this.dragPanePosition - pos) / this.panesSize;
|
||||||
if (pos > dragPanePosition.current) {
|
if (pos > this.dragPanePosition) {
|
||||||
sizes_[i] += d;
|
sizes_[i] += d;
|
||||||
sizes_[i + 1] -= d;
|
sizes_[i + 1] -= d;
|
||||||
} else {
|
} else {
|
||||||
sizes_[i] -= d;
|
sizes_[i] -= d;
|
||||||
sizes_[i + 1] += d;
|
sizes_[i + 1] += d;
|
||||||
}
|
}
|
||||||
props.onResize(sizes_);
|
this.props.onResize(sizes_);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = () => {
|
onDragEnd = () => {
|
||||||
window.removeEventListener('mousemove', onDrag);
|
if (this.state.dragging) {
|
||||||
window.removeEventListener('mouseup', onDragEnd);
|
window.removeEventListener('mousemove', this.onDrag);
|
||||||
setDragging(false);
|
window.removeEventListener('mouseup', this.onDragEnd);
|
||||||
|
this.setState({dragging: false});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
render() {
|
||||||
return () => {
|
const children = this.props.children as React.ReactNodeArray;
|
||||||
onDragEnd();
|
const {direction, borderColor} = this.props;
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const {children, direction, borderColor} = props;
|
|
||||||
const sizeProperty = direction === 'horizontal' ? 'height' : 'width';
|
const sizeProperty = direction === 'horizontal' ? 'height' : 'width';
|
||||||
// workaround for the fact that if we don't specify
|
// workaround for the fact that if we don't specify
|
||||||
// sizes, sometimes flex fails to calculate the
|
// sizes, sometimes flex fails to calculate the
|
||||||
// right height for the horizontal panes
|
// right height for the horizontal panes
|
||||||
const sizes = props.sizes || new Array<number>(children.length).fill(1 / children.length);
|
const sizes = this.props.sizes || new Array<number>(children.length).fill(1 / children.length);
|
||||||
return (
|
return (
|
||||||
<div className={`splitpane_panes splitpane_panes_${direction}`} ref={ref}>
|
<div className={`splitpane_panes splitpane_panes_${direction}`}>
|
||||||
{children.map((child, i) => {
|
{React.Children.map(children, (child, i) => {
|
||||||
const style = {
|
const style = {
|
||||||
// flexBasis doesn't work for the first horizontal pane, height need to be specified
|
// flexBasis doesn't work for the first horizontal pane, height need to be specified
|
||||||
[sizeProperty]: `${sizes[i] * 100}%`,
|
[sizeProperty]: `${sizes[i] * 100}%`,
|
||||||
flexBasis: `${sizes[i] * 100}%`,
|
flexBasis: `${sizes[i] * 100}%`,
|
||||||
flexGrow: 0
|
flexGrow: 0
|
||||||
};
|
};
|
||||||
|
return [
|
||||||
return (
|
<div key="pane" className="splitpane_pane" style={style}>
|
||||||
<React.Fragment key={i}>
|
|
||||||
<div className="splitpane_pane" style={style}>
|
|
||||||
{child}
|
{child}
|
||||||
</div>
|
</div>,
|
||||||
{i < children.length - 1 ? (
|
i < children.length - 1 ? (
|
||||||
<div
|
<div
|
||||||
onMouseDown={(e) => handleDragStart(e, i)}
|
key="divider"
|
||||||
onDoubleClick={(e) => handleAutoResize(e, i)}
|
onMouseDown={this.handleDragStart}
|
||||||
|
onDoubleClick={this.handleAutoResize}
|
||||||
style={{backgroundColor: borderColor}}
|
style={{backgroundColor: borderColor}}
|
||||||
className={`splitpane_divider splitpane_divider_${direction}`}
|
className={`splitpane_divider splitpane_divider_${direction}`}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null
|
||||||
</React.Fragment>
|
];
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
<div style={{display: dragging ? 'block' : 'none'}} className="splitpane_shim" />
|
<div style={{display: this.state.dragging ? 'block' : 'none'}} className="splitpane_shim" />
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.splitpane_panes {
|
.splitpane_panes {
|
||||||
|
|
@ -184,8 +208,12 @@ const SplitPane = forwardRef<HTMLDivElement, SplitPaneProps>((props, ref) => {
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
SplitPane.displayName = 'SplitPane';
|
componentWillUnmount() {
|
||||||
|
// ensure drag end
|
||||||
export default SplitPane;
|
if (this.dragging) {
|
||||||
|
this.onDragEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,142 @@
|
||||||
import React, {forwardRef} from 'react';
|
import React from 'react';
|
||||||
|
import {StyleSheetProps} from '../hyper';
|
||||||
|
|
||||||
import type {StyleSheetProps} from '../../typings/hyper';
|
export default class StyleSheet extends React.PureComponent<StyleSheetProps> {
|
||||||
|
render() {
|
||||||
const StyleSheet = forwardRef<HTMLStyleElement, StyleSheetProps>((props, ref) => {
|
const {backgroundColor, fontFamily, foregroundColor, borderColor} = this.props;
|
||||||
const {borderColor} = props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<style jsx global ref={ref}>{`
|
<style jsx global>{`
|
||||||
|
.xterm {
|
||||||
|
font-family: ${fontFamily};
|
||||||
|
font-feature-settings: 'liga' 0;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.focus,
|
||||||
|
.xterm:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helpers {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
/**
|
||||||
|
* The z-index of the helpers must be higher than the canvases in order for
|
||||||
|
* IMEs to appear on top.
|
||||||
|
*/
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helper-textarea {
|
||||||
|
/*
|
||||||
|
* HACK: to fix IE's blinking cursor
|
||||||
|
* Move textarea out of the screen to the far left, so that the cursor is not visible.
|
||||||
|
*/
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
left: -9999em;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
z-index: -10;
|
||||||
|
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view {
|
||||||
|
/* TODO: Composition position got messed up somewhere */
|
||||||
|
background: ${backgroundColor};
|
||||||
|
color: ${foregroundColor};
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-viewport {
|
||||||
|
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||||
|
background-color: ${backgroundColor};
|
||||||
|
overflow-y: scroll;
|
||||||
|
cursor: default;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-screen {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm canvas {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-scroll-area {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-char-measure-element {
|
||||||
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
left: -9999em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.enable-mouse-events {
|
||||||
|
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.xterm-cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.xterm-cursor-crosshair {
|
||||||
|
/* Column selection mode */
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-accessibility,
|
||||||
|
.xterm .xterm-message {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-accessibility-tree:focus [id^='xterm-active-item-'] {
|
||||||
|
outline: 1px solid #f80;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .live-region {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
}
|
}
|
||||||
|
|
@ -20,8 +150,5 @@ const StyleSheet = forwardRef<HTMLStyleElement, StyleSheetProps>((props, ref) =>
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
StyleSheet.displayName = 'StyleSheet';
|
|
||||||
|
|
||||||
export default StyleSheet;
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,55 @@
|
||||||
import React, {forwardRef} from 'react';
|
import React from 'react';
|
||||||
|
import {TabProps} from '../hyper';
|
||||||
|
|
||||||
import type {TabProps} from '../../typings/hyper';
|
export default class Tab extends React.PureComponent<TabProps> {
|
||||||
|
constructor(props: TabProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
const Tab = forwardRef<HTMLLIElement, TabProps>((props, ref) => {
|
handleClick = (event: React.MouseEvent) => {
|
||||||
const handleClick = (event: React.MouseEvent) => {
|
|
||||||
const isLeftClick = event.nativeEvent.which === 1;
|
const isLeftClick = event.nativeEvent.which === 1;
|
||||||
|
|
||||||
if (isLeftClick && !props.isActive) {
|
if (isLeftClick && !this.props.isActive) {
|
||||||
props.onSelect();
|
this.props.onSelect();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = (event: React.MouseEvent) => {
|
handleMouseUp = (event: React.MouseEvent) => {
|
||||||
const isMiddleClick = event.nativeEvent.which === 2;
|
const isMiddleClick = event.nativeEvent.which === 2;
|
||||||
|
|
||||||
if (isMiddleClick) {
|
if (isMiddleClick) {
|
||||||
props.onClose();
|
this.props.onClose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const {isActive, isFirst, isLast, borderColor, hasActivity} = props;
|
render() {
|
||||||
|
const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<React.Fragment>
|
||||||
<li
|
<li
|
||||||
onClick={props.onClick}
|
onClick={this.props.onClick}
|
||||||
style={{borderColor}}
|
style={{borderColor}}
|
||||||
className={`tab_tab ${isFirst ? 'tab_first' : ''} ${isActive ? 'tab_active' : ''} ${
|
className={`tab_tab ${isFirst ? 'tab_first' : ''} ${isActive ? 'tab_active' : ''} ${
|
||||||
isFirst && isActive ? 'tab_firstActive' : ''
|
isFirst && isActive ? 'tab_firstActive' : ''
|
||||||
} ${hasActivity ? 'tab_hasActivity' : ''}`}
|
} ${hasActivity ? 'tab_hasActivity' : ''}`}
|
||||||
ref={ref}
|
|
||||||
>
|
>
|
||||||
{props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
<span
|
<span
|
||||||
className={`tab_text ${isLast ? 'tab_textLast' : ''} ${isActive ? 'tab_textActive' : ''}`}
|
className={`tab_text ${isLast ? 'tab_textLast' : ''} ${isActive ? 'tab_textActive' : ''}`}
|
||||||
onClick={handleClick}
|
onClick={this.handleClick}
|
||||||
onMouseUp={handleMouseUp}
|
onMouseUp={this.handleMouseUp}
|
||||||
>
|
>
|
||||||
<span title={props.text} className="tab_textInner">
|
<span title={this.props.text} className="tab_textInner">
|
||||||
{props.text}
|
{this.props.text}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<i className="tab_icon" onClick={props.onClose}>
|
<i className="tab_icon" onClick={this.props.onClose}>
|
||||||
<svg className="tab_shape">
|
<svg className="tab_shape">
|
||||||
<use xlinkHref="./renderer/assets/icons.svg#close-tab" />
|
<use xlinkHref="./renderer/assets/icons.svg#close-tab" />
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
{props.customChildren}
|
{this.props.customChildren}
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
|
|
@ -114,11 +117,7 @@ const Tab = forwardRef<HTMLLIElement, TabProps>((props, ref) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab_icon {
|
.tab_icon {
|
||||||
transition:
|
transition: opacity 0.2s ease, color 0.2s ease, transform 0.25s ease, background-color 0.1s ease;
|
||||||
opacity 0.2s ease,
|
|
||||||
color 0.2s ease,
|
|
||||||
transform 0.25s ease,
|
|
||||||
background-color 0.1s ease;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 7px;
|
right: 7px;
|
||||||
|
|
@ -159,10 +158,7 @@ const Tab = forwardRef<HTMLLIElement, TabProps>((props, ref) => {
|
||||||
shape-rendering: crispEdges;
|
shape-rendering: crispEdges;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Tab.displayName = 'Tab';
|
|
||||||
|
|
||||||
export default Tab;
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import React, {forwardRef} from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type {TabsProps} from '../../typings/hyper';
|
|
||||||
import {decorate, getTabProps} from '../utils/plugins';
|
import {decorate, getTabProps} from '../utils/plugins';
|
||||||
|
|
||||||
import DropdownButton from './new-tab';
|
|
||||||
import Tab_ from './tab';
|
import Tab_ from './tab';
|
||||||
|
import {TabsProps} from '../hyper';
|
||||||
|
|
||||||
const Tab = decorate(Tab_, 'Tab');
|
const Tab = decorate(Tab_, 'Tab');
|
||||||
const isMac = /Mac/.test(navigator.userAgent);
|
const isMac = /Mac/.test(navigator.userAgent);
|
||||||
|
|
||||||
const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
export default class Tabs extends React.PureComponent<TabsProps> {
|
||||||
const {tabs = [], borderColor, onChange, onClose, fullScreen} = props;
|
render() {
|
||||||
|
const {tabs = [], borderColor, onChange, onClose, fullScreen} = this.props;
|
||||||
|
|
||||||
const hide = !isMac && tabs.length === 1;
|
const hide = !isMac && tabs.length === 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={`tabs_nav ${hide ? 'tabs_hiddenNav' : ''}`} ref={ref}>
|
<nav className={`tabs_nav ${hide ? 'tabs_hiddenNav' : ''}`}>
|
||||||
{props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
{tabs.length === 1 && isMac ? <div className="tabs_title">{tabs[0].title}</div> : null}
|
{tabs.length === 1 && isMac ? <div className="tabs_title">{tabs[0].title}</div> : null}
|
||||||
{tabs.length > 1 ? (
|
{tabs.length > 1
|
||||||
<>
|
? [
|
||||||
<ul key="list" className={`tabs_list ${fullScreen && isMac ? 'tabs_fullScreen' : ''}`}>
|
<ul key="list" className={`tabs_list ${fullScreen && isMac ? 'tabs_fullScreen' : ''}`}>
|
||||||
{tabs.map((tab, i) => {
|
{tabs.map((tab, i) => {
|
||||||
const {uid, title, isActive, hasActivity} = tab;
|
const {uid, title, isActive, hasActivity} = tab;
|
||||||
const tabProps = getTabProps(tab, props, {
|
const props = getTabProps(tab, this.props, {
|
||||||
text: title === '' ? 'Shell' : title,
|
text: title === '' ? 'Shell' : title,
|
||||||
isFirst: i === 0,
|
isFirst: i === 0,
|
||||||
isLast: tabs.length - 1 === i,
|
isLast: tabs.length - 1 === i,
|
||||||
|
|
@ -33,20 +33,19 @@ const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
||||||
onSelect: onChange.bind(null, uid),
|
onSelect: onChange.bind(null, uid),
|
||||||
onClose: onClose.bind(null, uid)
|
onClose: onClose.bind(null, uid)
|
||||||
});
|
});
|
||||||
return <Tab key={`tab-${uid}`} {...tabProps} />;
|
return <Tab key={`tab-${uid}`} {...props} />;
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>,
|
||||||
{isMac && (
|
isMac && (
|
||||||
<div
|
<div
|
||||||
key="shim"
|
key="shim"
|
||||||
style={{borderColor}}
|
style={{borderColor}}
|
||||||
className={`tabs_borderShim ${fullScreen ? 'tabs_borderShimUndo' : ''}`}
|
className={`tabs_borderShim ${fullScreen ? 'tabs_borderShimUndo' : ''}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)
|
||||||
</>
|
]
|
||||||
) : null}
|
: null}
|
||||||
<DropdownButton {...props} tabsVisible={tabs.length > 1} />
|
{this.props.customChildren}
|
||||||
{props.customChildren}
|
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.tabs_nav {
|
.tabs_nav {
|
||||||
|
|
@ -60,8 +59,6 @@ const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-webkit-app-region: ${isMac ? 'drag' : ''};
|
-webkit-app-region: ${isMac ? 'drag' : ''};
|
||||||
top: ${isMac ? '0px' : '34px'};
|
top: ${isMac ? '0px' : '34px'};
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs_hiddenNav {
|
.tabs_hiddenNav {
|
||||||
|
|
@ -76,7 +73,6 @@ const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding-left: 76px;
|
padding-left: 76px;
|
||||||
padding-right: 76px;
|
padding-right: 76px;
|
||||||
flex-grow: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs_list {
|
.tabs_list {
|
||||||
|
|
@ -84,7 +80,6 @@ const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
margin-left: ${isMac ? '76px' : '0'};
|
margin-left: ${isMac ? '76px' : '0'};
|
||||||
flex-grow: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs_fullScreen {
|
.tabs_fullScreen {
|
||||||
|
|
@ -106,8 +101,5 @@ const Tabs = forwardRef<HTMLElement, TabsProps>((props, ref) => {
|
||||||
`}</style>
|
`}</style>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Tabs.displayName = 'Tabs';
|
|
||||||
|
|
||||||
export default Tabs;
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import type {HyperState, HyperDispatch, TermGroupProps, TermGroupOwnProps} from '../../typings/hyper';
|
|
||||||
import {resizeTermGroup} from '../actions/term-groups';
|
|
||||||
import {decorate, getTermProps, getTermGroupProps} from '../utils/plugins';
|
import {decorate, getTermProps, getTermGroupProps} from '../utils/plugins';
|
||||||
|
import {resizeTermGroup} from '../actions/term-groups';
|
||||||
import SplitPane_ from './split-pane';
|
|
||||||
import Term_ from './term';
|
import Term_ from './term';
|
||||||
|
import SplitPane_ from './split-pane';
|
||||||
|
import {HyperState, HyperDispatch, TermGroupProps, TermGroupOwnProps} from '../hyper';
|
||||||
|
|
||||||
const Term = decorate(Term_, 'Term');
|
const Term = decorate(Term_, 'Term');
|
||||||
const SplitPane = decorate(SplitPane_, 'SplitPane');
|
const SplitPane = decorate(SplitPane_, 'SplitPane');
|
||||||
|
|
@ -85,6 +82,7 @@ class TermGroup_ extends React.PureComponent<TermGroupProps> {
|
||||||
letterSpacing: this.props.letterSpacing,
|
letterSpacing: this.props.letterSpacing,
|
||||||
modifierKeys: this.props.modifierKeys,
|
modifierKeys: this.props.modifierKeys,
|
||||||
padding: this.props.padding,
|
padding: this.props.padding,
|
||||||
|
url: session.url,
|
||||||
cleared: session.cleared,
|
cleared: session.cleared,
|
||||||
search: session.search,
|
search: session.search,
|
||||||
cols: session.cols,
|
cols: session.cols,
|
||||||
|
|
@ -108,8 +106,6 @@ class TermGroup_ extends React.PureComponent<TermGroupProps> {
|
||||||
macOptionSelectionMode: this.props.macOptionSelectionMode,
|
macOptionSelectionMode: this.props.macOptionSelectionMode,
|
||||||
disableLigatures: this.props.disableLigatures,
|
disableLigatures: this.props.disableLigatures,
|
||||||
screenReaderMode: this.props.screenReaderMode,
|
screenReaderMode: this.props.screenReaderMode,
|
||||||
windowsPty: this.props.windowsPty,
|
|
||||||
imageSupport: this.props.imageSupport,
|
|
||||||
uid
|
uid
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,20 @@
|
||||||
import {clipboard, shell} from 'electron';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {Terminal, ITerminalOptions, IDisposable} from 'xterm';
|
||||||
import Color from 'color';
|
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
import pickBy from 'lodash/pickBy';
|
|
||||||
import {Terminal} from 'xterm';
|
|
||||||
import type {ITerminalOptions, IDisposable} from 'xterm';
|
|
||||||
import {CanvasAddon} from 'xterm-addon-canvas';
|
|
||||||
import {FitAddon} from 'xterm-addon-fit';
|
import {FitAddon} from 'xterm-addon-fit';
|
||||||
import {ImageAddon} from 'xterm-addon-image';
|
|
||||||
import {LigaturesAddon} from 'xterm-addon-ligatures';
|
|
||||||
import {SearchAddon} from 'xterm-addon-search';
|
|
||||||
import type {ISearchDecorationOptions} from 'xterm-addon-search';
|
|
||||||
import {Unicode11Addon} from 'xterm-addon-unicode11';
|
|
||||||
import {WebLinksAddon} from 'xterm-addon-web-links';
|
import {WebLinksAddon} from 'xterm-addon-web-links';
|
||||||
|
import {SearchAddon} from 'xterm-addon-search';
|
||||||
import {WebglAddon} from 'xterm-addon-webgl';
|
import {WebglAddon} from 'xterm-addon-webgl';
|
||||||
|
import {LigaturesAddon} from 'xterm-addon-ligatures';
|
||||||
import type {TermProps} from '../../typings/hyper';
|
import {Unicode11Addon} from 'xterm-addon-unicode11';
|
||||||
|
import {clipboard, shell} from 'electron';
|
||||||
|
import Color from 'color';
|
||||||
import terms from '../terms';
|
import terms from '../terms';
|
||||||
import processClipboard from '../utils/paste';
|
import processClipboard from '../utils/paste';
|
||||||
import {decorate} from '../utils/plugins';
|
import SearchBox from './searchBox';
|
||||||
|
import {TermProps} from '../hyper';
|
||||||
|
import {ObjectTypedKeys} from '../utils/object';
|
||||||
|
|
||||||
import _SearchBox from './searchBox';
|
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(navigator.platform);
|
||||||
|
|
||||||
import 'xterm/css/xterm.css';
|
|
||||||
|
|
||||||
const SearchBox = decorate(_SearchBox, 'SearchBox');
|
|
||||||
|
|
||||||
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(navigator.platform) || process.platform === 'win32';
|
|
||||||
|
|
||||||
// map old hterm constants to xterm.js
|
// map old hterm constants to xterm.js
|
||||||
const CURSOR_STYLES = {
|
const CURSOR_STYLES = {
|
||||||
|
|
@ -51,7 +38,7 @@ const isWebgl2Supported = (() => {
|
||||||
const getTermOptions = (props: TermProps): ITerminalOptions => {
|
const getTermOptions = (props: TermProps): ITerminalOptions => {
|
||||||
// Set a background color only if it is opaque
|
// Set a background color only if it is opaque
|
||||||
const needTransparency = Color(props.backgroundColor).alpha() < 1;
|
const needTransparency = Color(props.backgroundColor).alpha() < 1;
|
||||||
const backgroundColor = needTransparency ? 'rgba(0,0,0,0)' : props.backgroundColor;
|
const backgroundColor = needTransparency ? 'transparent' : props.backgroundColor;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
macOptionIsMeta: props.modifierKeys.altIsMeta,
|
macOptionIsMeta: props.modifierKeys.altIsMeta,
|
||||||
|
|
@ -66,14 +53,14 @@ const getTermOptions = (props: TermProps): ITerminalOptions => {
|
||||||
letterSpacing: props.letterSpacing,
|
letterSpacing: props.letterSpacing,
|
||||||
allowTransparency: needTransparency,
|
allowTransparency: needTransparency,
|
||||||
macOptionClickForcesSelection: props.macOptionSelectionMode === 'force',
|
macOptionClickForcesSelection: props.macOptionSelectionMode === 'force',
|
||||||
|
bellStyle: props.bell === 'SOUND' ? 'sound' : 'none',
|
||||||
windowsMode: isWindows,
|
windowsMode: isWindows,
|
||||||
...(isWindows && props.windowsPty && {windowsPty: props.windowsPty}),
|
|
||||||
theme: {
|
theme: {
|
||||||
foreground: props.foregroundColor,
|
foreground: props.foregroundColor,
|
||||||
background: backgroundColor,
|
background: backgroundColor,
|
||||||
cursor: props.cursorColor,
|
cursor: props.cursorColor,
|
||||||
cursorAccent: props.cursorAccentColor,
|
cursorAccent: props.cursorAccentColor,
|
||||||
selectionBackground: props.selectionColor,
|
selection: props.selectionColor,
|
||||||
black: props.colors.black,
|
black: props.colors.black,
|
||||||
red: props.colors.red,
|
red: props.colors.red,
|
||||||
green: props.colors.green,
|
green: props.colors.green,
|
||||||
|
|
@ -91,50 +78,22 @@ const getTermOptions = (props: TermProps): ITerminalOptions => {
|
||||||
brightCyan: props.colors.lightCyan,
|
brightCyan: props.colors.lightCyan,
|
||||||
brightWhite: props.colors.lightWhite
|
brightWhite: props.colors.lightWhite
|
||||||
},
|
},
|
||||||
screenReaderMode: props.screenReaderMode,
|
screenReaderMode: props.screenReaderMode
|
||||||
overviewRulerWidth: 20,
|
|
||||||
allowProposedApi: true
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Term extends React.PureComponent<
|
export default class Term extends React.PureComponent<TermProps> {
|
||||||
TermProps,
|
|
||||||
{
|
|
||||||
searchOptions: {
|
|
||||||
caseSensitive: boolean;
|
|
||||||
wholeWord: boolean;
|
|
||||||
regex: boolean;
|
|
||||||
};
|
|
||||||
searchResults:
|
|
||||||
| {
|
|
||||||
resultIndex: number;
|
|
||||||
resultCount: number;
|
|
||||||
}
|
|
||||||
| undefined;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
termRef: HTMLElement | null;
|
termRef: HTMLElement | null;
|
||||||
termWrapperRef: HTMLElement | null;
|
termWrapperRef: HTMLElement | null;
|
||||||
termOptions: ITerminalOptions;
|
termOptions: ITerminalOptions;
|
||||||
disposableListeners: IDisposable[];
|
disposableListeners: IDisposable[];
|
||||||
defaultBellSound: HTMLAudioElement | null;
|
termDefaultBellSound: string | null;
|
||||||
bellSound: HTMLAudioElement | null;
|
|
||||||
fitAddon: FitAddon;
|
fitAddon: FitAddon;
|
||||||
searchAddon: SearchAddon;
|
searchAddon: SearchAddon;
|
||||||
static rendererTypes: Record<string, string>;
|
static rendererTypes: Record<string, string>;
|
||||||
term!: Terminal;
|
term!: Terminal;
|
||||||
resizeObserver!: ResizeObserver;
|
resizeObserver!: ResizeObserver;
|
||||||
resizeTimeout!: NodeJS.Timeout;
|
resizeTimeout!: NodeJS.Timeout;
|
||||||
searchDecorations: ISearchDecorationOptions;
|
|
||||||
state = {
|
|
||||||
searchOptions: {
|
|
||||||
caseSensitive: false,
|
|
||||||
wholeWord: false,
|
|
||||||
regex: false
|
|
||||||
},
|
|
||||||
searchResults: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: TermProps) {
|
constructor(props: TermProps) {
|
||||||
super(props);
|
super(props);
|
||||||
props.ref_(props.uid, this);
|
props.ref_(props.uid, this);
|
||||||
|
|
@ -142,17 +101,9 @@ export default class Term extends React.PureComponent<
|
||||||
this.termWrapperRef = null;
|
this.termWrapperRef = null;
|
||||||
this.termOptions = {};
|
this.termOptions = {};
|
||||||
this.disposableListeners = [];
|
this.disposableListeners = [];
|
||||||
this.defaultBellSound = null;
|
this.termDefaultBellSound = null;
|
||||||
this.bellSound = null;
|
|
||||||
this.fitAddon = new FitAddon();
|
this.fitAddon = new FitAddon();
|
||||||
this.searchAddon = new SearchAddon();
|
this.searchAddon = new SearchAddon();
|
||||||
this.searchDecorations = {
|
|
||||||
activeMatchColorOverviewRuler: Color(this.props.cursorColor).hex(),
|
|
||||||
matchOverviewRuler: Color(this.props.borderColor).hex(),
|
|
||||||
activeMatchBackground: Color(this.props.cursorColor).hex(),
|
|
||||||
activeMatchBorder: Color(this.props.cursorColor).hex(),
|
|
||||||
matchBorder: Color(this.props.cursorColor).hex()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The main process shows this in the About dialog
|
// The main process shows this in the About dialog
|
||||||
|
|
@ -170,14 +121,7 @@ export default class Term extends React.PureComponent<
|
||||||
|
|
||||||
this.termOptions = getTermOptions(props);
|
this.termOptions = getTermOptions(props);
|
||||||
this.term = props.term || new Terminal(this.termOptions);
|
this.term = props.term || new Terminal(this.termOptions);
|
||||||
this.defaultBellSound = new Audio(
|
this.termDefaultBellSound = this.term.getOption('bellSound');
|
||||||
// Source: https://freesound.org/people/altemark/sounds/45759/
|
|
||||||
// This sound is released under the Creative Commons Attribution 3.0 Unported
|
|
||||||
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
|
|
||||||
// made, apart from the conversion to base64.
|
|
||||||
'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
|
|
||||||
);
|
|
||||||
this.setBellSound(props.bell, props.bellSound);
|
|
||||||
|
|
||||||
// The parent element for the terminal is attached and removed manually so
|
// The parent element for the terminal is attached and removed manually so
|
||||||
// that we can preserve it across mounts and unmounts of the component
|
// that we can preserve it across mounts and unmounts of the component
|
||||||
|
|
@ -205,9 +149,9 @@ export default class Term extends React.PureComponent<
|
||||||
}
|
}
|
||||||
Term.reportRenderer(props.uid, useWebGL ? 'WebGL' : 'Canvas');
|
Term.reportRenderer(props.uid, useWebGL ? 'WebGL' : 'Canvas');
|
||||||
|
|
||||||
const shallActivateWebLink = (event: MouseEvent): boolean => {
|
const shallActivateWebLink = (event: Record<string, any> | undefined): boolean => {
|
||||||
if (!event) return false;
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return props.webLinksActivationKey ? event[`${props.webLinksActivationKey}Key`] : true;
|
return event && (!props.webLinksActivationKey || event[`${props.webLinksActivationKey}Key`]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
|
@ -215,46 +159,35 @@ export default class Term extends React.PureComponent<
|
||||||
this.term.loadAddon(this.fitAddon);
|
this.term.loadAddon(this.fitAddon);
|
||||||
this.term.loadAddon(this.searchAddon);
|
this.term.loadAddon(this.searchAddon);
|
||||||
this.term.loadAddon(
|
this.term.loadAddon(
|
||||||
new WebLinksAddon((event, uri) => {
|
new WebLinksAddon(
|
||||||
|
(event: MouseEvent | undefined, uri: string) => {
|
||||||
if (shallActivateWebLink(event)) void shell.openExternal(uri);
|
if (shallActivateWebLink(event)) void shell.openExternal(uri);
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
// prevent default electron link handling to allow selection, e.g. via double-click
|
||||||
|
willLinkActivate: (event: MouseEvent | undefined) => {
|
||||||
|
event?.preventDefault();
|
||||||
|
return shallActivateWebLink(event);
|
||||||
|
},
|
||||||
|
priority: Date.now()
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.term.open(this.termRef);
|
this.term.open(this.termRef);
|
||||||
|
|
||||||
if (useWebGL) {
|
if (useWebGL) {
|
||||||
const webglAddon = new WebglAddon();
|
this.term.loadAddon(new WebglAddon());
|
||||||
this.term.loadAddon(webglAddon);
|
|
||||||
webglAddon.onContextLoss(() => {
|
|
||||||
console.warn('WebGL context lost. Falling back to canvas-based rendering.');
|
|
||||||
webglAddon.dispose();
|
|
||||||
this.term.loadAddon(new CanvasAddon());
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.term.loadAddon(new CanvasAddon());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.disableLigatures !== true && !useWebGL) {
|
if (props.disableLigatures !== true && !useWebGL) {
|
||||||
this.term.loadAddon(new LigaturesAddon());
|
this.term.loadAddon(new LigaturesAddon());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.term.loadAddon(new Unicode11Addon());
|
this.term.loadAddon(new Unicode11Addon());
|
||||||
this.term.unicode.activeVersion = '11';
|
this.term.unicode.activeVersion = '11';
|
||||||
|
|
||||||
if (props.imageSupport) {
|
|
||||||
this.term.loadAddon(new ImageAddon());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// get the cached plugins
|
// get the cached plugins
|
||||||
this.fitAddon = props.fitAddon!;
|
this.fitAddon = props.fitAddon!;
|
||||||
this.searchAddon = props.searchAddon!;
|
this.searchAddon = props.searchAddon!;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
this.term.element!.style.padding = props.padding;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fitAddon.fit();
|
this.fitAddon.fit();
|
||||||
|
|
||||||
if (this.props.isTermActive) {
|
if (this.props.isTermActive) {
|
||||||
|
|
@ -276,10 +209,6 @@ export default class Term extends React.PureComponent<
|
||||||
this.disposableListeners.push(this.term.onData(props.onData));
|
this.disposableListeners.push(this.term.onData(props.onData));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.term.onBell(() => {
|
|
||||||
this.ringBell();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (props.onResize) {
|
if (props.onResize) {
|
||||||
this.disposableListeners.push(
|
this.disposableListeners.push(
|
||||||
this.term.onResize(({cols, rows}) => {
|
this.term.onResize(({cols, rows}) => {
|
||||||
|
|
@ -307,15 +236,6 @@ export default class Term extends React.PureComponent<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.disposableListeners.push(
|
|
||||||
this.searchAddon.onDidChangeResults((results) => {
|
|
||||||
this.setState((state) => ({
|
|
||||||
...state,
|
|
||||||
searchResults: results
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
window.addEventListener('paste', this.onWindowPaste, {
|
window.addEventListener('paste', this.onWindowPaste, {
|
||||||
capture: true
|
capture: true
|
||||||
});
|
});
|
||||||
|
|
@ -364,9 +284,9 @@ export default class Term extends React.PureComponent<
|
||||||
this.term.write(data);
|
this.term.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
focus = () => {
|
focus() {
|
||||||
this.term.focus();
|
this.term.focus();
|
||||||
};
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.term.clear();
|
this.term.clear();
|
||||||
|
|
@ -376,28 +296,20 @@ export default class Term extends React.PureComponent<
|
||||||
this.term.reset();
|
this.term.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
search = (searchTerm = '') => {
|
||||||
|
this.searchAddon.findNext(searchTerm);
|
||||||
|
};
|
||||||
|
|
||||||
searchNext = (searchTerm: string) => {
|
searchNext = (searchTerm: string) => {
|
||||||
this.searchAddon.findNext(searchTerm, {
|
this.searchAddon.findNext(searchTerm);
|
||||||
...this.state.searchOptions,
|
|
||||||
decorations: this.searchDecorations
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
searchPrevious = (searchTerm: string) => {
|
searchPrevious = (searchTerm: string) => {
|
||||||
this.searchAddon.findPrevious(searchTerm, {
|
this.searchAddon.findPrevious(searchTerm);
|
||||||
...this.state.searchOptions,
|
|
||||||
decorations: this.searchDecorations
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
closeSearchBox = () => {
|
closeSearchBox = () => {
|
||||||
this.props.onCloseSearch();
|
this.props.onCloseSearch();
|
||||||
this.searchAddon.clearDecorations();
|
|
||||||
this.searchAddon.clearActiveDecoration();
|
|
||||||
this.setState((state) => ({
|
|
||||||
...state,
|
|
||||||
searchResults: undefined
|
|
||||||
}));
|
|
||||||
this.term.focus();
|
this.term.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -421,18 +333,6 @@ export default class Term extends React.PureComponent<
|
||||||
return !e.catched;
|
return !e.catched;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBellSound(bell: 'SOUND' | false, sound: string | null) {
|
|
||||||
if (bell && bell.toUpperCase() === 'SOUND') {
|
|
||||||
this.bellSound = sound ? new Audio(sound) : this.defaultBellSound;
|
|
||||||
} else {
|
|
||||||
this.bellSound = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ringBell() {
|
|
||||||
void this.bellSound?.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: TermProps) {
|
componentDidUpdate(prevProps: TermProps) {
|
||||||
if (!prevProps.cleared && this.props.cleared) {
|
if (!prevProps.cleared && this.props.cleared) {
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
@ -440,28 +340,43 @@ export default class Term extends React.PureComponent<
|
||||||
|
|
||||||
const nextTermOptions = getTermOptions(this.props);
|
const nextTermOptions = getTermOptions(this.props);
|
||||||
|
|
||||||
if (prevProps.bell !== this.props.bell || prevProps.bellSound !== this.props.bellSound) {
|
// Use bellSound in nextProps if it exists
|
||||||
this.setBellSound(this.props.bell, this.props.bellSound);
|
// otherwise use the default sound found in xterm.
|
||||||
}
|
nextTermOptions.bellSound = this.props.bellSound || this.termDefaultBellSound!;
|
||||||
|
|
||||||
if (prevProps.search && !this.props.search) {
|
if (!prevProps.search && this.props.search) {
|
||||||
this.closeSearchBox();
|
this.search();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update only options that have changed.
|
// Update only options that have changed.
|
||||||
this.term.options = pickBy(
|
ObjectTypedKeys(nextTermOptions)
|
||||||
nextTermOptions,
|
.filter((option) => option !== 'theme' && nextTermOptions[option] !== this.termOptions[option])
|
||||||
(value, key) => !isEqual(this.termOptions[key as keyof ITerminalOptions], value)
|
.forEach((option) => {
|
||||||
|
try {
|
||||||
|
this.term.setOption(option, nextTermOptions[option]);
|
||||||
|
} catch (_e) {
|
||||||
|
const e = _e as {message: string};
|
||||||
|
if (/The webgl renderer only works with the webgl char atlas/i.test(e.message)) {
|
||||||
|
// Ignore this because the char atlas will also be changed
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Do we need to update theme?
|
||||||
|
const shouldUpdateTheme =
|
||||||
|
!this.termOptions.theme ||
|
||||||
|
nextTermOptions.rendererType !== this.termOptions.rendererType ||
|
||||||
|
ObjectTypedKeys(nextTermOptions.theme!).some(
|
||||||
|
(option) => nextTermOptions.theme![option] !== this.termOptions.theme![option]
|
||||||
);
|
);
|
||||||
|
if (shouldUpdateTheme) {
|
||||||
|
this.term.setOption('theme', nextTermOptions.theme);
|
||||||
|
}
|
||||||
|
|
||||||
this.termOptions = nextTermOptions;
|
this.termOptions = nextTermOptions;
|
||||||
|
|
||||||
try {
|
|
||||||
this.term.element!.style.padding = this.props.padding;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.props.fontSize !== prevProps.fontSize ||
|
this.props.fontSize !== prevProps.fontSize ||
|
||||||
this.props.fontFamily !== prevProps.fontFamily ||
|
this.props.fontFamily !== prevProps.fontFamily ||
|
||||||
|
|
@ -512,44 +427,24 @@ export default class Term extends React.PureComponent<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={`term_fit ${this.props.isTermActive ? 'term_active' : ''}`} onMouseUp={this.onMouseUp}>
|
<div
|
||||||
|
className={`term_fit ${this.props.isTermActive ? 'term_active' : ''}`}
|
||||||
|
style={{padding: this.props.padding}}
|
||||||
|
onMouseUp={this.onMouseUp}
|
||||||
|
>
|
||||||
{this.props.customChildrenBefore}
|
{this.props.customChildrenBefore}
|
||||||
<div ref={this.onTermWrapperRef} className="term_fit term_wrapper" />
|
<div ref={this.onTermWrapperRef} className="term_fit term_wrapper" />
|
||||||
{this.props.customChildren}
|
{this.props.customChildren}
|
||||||
{this.props.search ? (
|
{this.props.search ? (
|
||||||
<SearchBox
|
<SearchBox
|
||||||
|
search={this.search}
|
||||||
next={this.searchNext}
|
next={this.searchNext}
|
||||||
prev={this.searchPrevious}
|
prev={this.searchPrevious}
|
||||||
close={this.closeSearchBox}
|
close={this.closeSearchBox}
|
||||||
caseSensitive={this.state.searchOptions.caseSensitive}
|
|
||||||
wholeWord={this.state.searchOptions.wholeWord}
|
|
||||||
regex={this.state.searchOptions.regex}
|
|
||||||
results={this.state.searchResults}
|
|
||||||
toggleCaseSensitive={() =>
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
searchOptions: {...this.state.searchOptions, caseSensitive: !this.state.searchOptions.caseSensitive}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
toggleWholeWord={() =>
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
searchOptions: {...this.state.searchOptions, wholeWord: !this.state.searchOptions.wholeWord}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
toggleRegex={() =>
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
searchOptions: {...this.state.searchOptions, regex: !this.state.searchOptions.regex}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
selectionColor={this.props.selectionColor}
|
|
||||||
backgroundColor={this.props.backgroundColor}
|
|
||||||
foregroundColor={this.props.foregroundColor}
|
|
||||||
borderColor={this.props.borderColor}
|
|
||||||
font={this.props.uiFontFamily}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
.term_fit {
|
.term_fit {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type {TermsProps, HyperDispatch} from '../../typings/hyper';
|
|
||||||
import {registerCommandHandlers} from '../command-registry';
|
|
||||||
import {ObjectTypedKeys} from '../utils/object';
|
|
||||||
import {decorate, getTermGroupProps} from '../utils/plugins';
|
import {decorate, getTermGroupProps} from '../utils/plugins';
|
||||||
|
import {registerCommandHandlers} from '../command-registry';
|
||||||
import StyleSheet_ from './style-sheet';
|
|
||||||
import type Term from './term';
|
|
||||||
import TermGroup_ from './term-group';
|
import TermGroup_ from './term-group';
|
||||||
|
import StyleSheet_ from './style-sheet';
|
||||||
|
import {TermsProps, HyperDispatch} from '../hyper';
|
||||||
|
import Term from './term';
|
||||||
|
import {ObjectTypedKeys} from '../utils/object';
|
||||||
|
|
||||||
const TermGroup = decorate(TermGroup_, 'TermGroup');
|
const TermGroup = decorate(TermGroup_, 'TermGroup');
|
||||||
const StyleSheet = decorate(StyleSheet_, 'StyleSheet');
|
const StyleSheet = decorate(StyleSheet_, 'StyleSheet');
|
||||||
|
|
||||||
const isMac = /Mac/.test(navigator.userAgent);
|
const isMac = /Mac/.test(navigator.userAgent);
|
||||||
|
|
||||||
export default class Terms extends React.Component<React.PropsWithChildren<TermsProps>> {
|
export default class Terms extends React.Component<TermsProps> {
|
||||||
terms: Record<string, Term>;
|
terms: Record<string, Term>;
|
||||||
registerCommands: (cmds: Record<string, (e: any, dispatch: HyperDispatch) => void>) => void;
|
registerCommands: (cmds: Record<string, (e: any, dispatch: HyperDispatch) => void>) => void;
|
||||||
constructor(props: TermsProps, context: any) {
|
constructor(props: TermsProps, context: any) {
|
||||||
|
|
@ -121,8 +119,6 @@ export default class Terms extends React.Component<React.PropsWithChildren<Terms
|
||||||
macOptionSelectionMode: this.props.macOptionSelectionMode,
|
macOptionSelectionMode: this.props.macOptionSelectionMode,
|
||||||
disableLigatures: this.props.disableLigatures,
|
disableLigatures: this.props.disableLigatures,
|
||||||
screenReaderMode: this.props.screenReaderMode,
|
screenReaderMode: this.props.screenReaderMode,
|
||||||
windowsPty: this.props.windowsPty,
|
|
||||||
imageSupport: this.props.imageSupport,
|
|
||||||
parentProps: this.props
|
parentProps: this.props
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
84
lib/config.d.ts
vendored
Normal file
84
lib/config.d.ts
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import {FontWeight} from 'xterm';
|
||||||
|
|
||||||
|
export type ColorMap = {
|
||||||
|
black: string;
|
||||||
|
blue: string;
|
||||||
|
cyan: string;
|
||||||
|
green: string;
|
||||||
|
lightBlack: string;
|
||||||
|
lightBlue: string;
|
||||||
|
lightCyan: string;
|
||||||
|
lightGreen: string;
|
||||||
|
lightMagenta: string;
|
||||||
|
lightRed: string;
|
||||||
|
lightWhite: string;
|
||||||
|
lightYellow: string;
|
||||||
|
magenta: string;
|
||||||
|
red: string;
|
||||||
|
white: string;
|
||||||
|
yellow: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type configOptions = {
|
||||||
|
autoUpdatePlugins: boolean | string;
|
||||||
|
backgroundColor: string;
|
||||||
|
bell: string;
|
||||||
|
bellSound: string | null;
|
||||||
|
bellSoundURL: string | null;
|
||||||
|
borderColor: string;
|
||||||
|
colors: ColorMap;
|
||||||
|
copyOnSelect: boolean;
|
||||||
|
css: string;
|
||||||
|
cursorAccentColor: string;
|
||||||
|
cursorBlink: boolean;
|
||||||
|
cursorColor: string;
|
||||||
|
cursorShape: 'BEAM' | 'UNDERLINE' | 'BLOCK';
|
||||||
|
defaultSSHApp: boolean;
|
||||||
|
disableAutoUpdates: boolean;
|
||||||
|
disableLigatures: boolean;
|
||||||
|
env: Record<string, string>;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: number;
|
||||||
|
fontWeight: FontWeight;
|
||||||
|
fontWeightBold: FontWeight;
|
||||||
|
foregroundColor: string;
|
||||||
|
letterSpacing: number;
|
||||||
|
lineHeight: number;
|
||||||
|
macOptionSelectionMode: string;
|
||||||
|
modifierKeys: {
|
||||||
|
altIsMeta: boolean;
|
||||||
|
cmdIsMeta: boolean;
|
||||||
|
};
|
||||||
|
padding: string;
|
||||||
|
preserveCWD: boolean;
|
||||||
|
quickEdit: boolean;
|
||||||
|
screenReaderMode: boolean;
|
||||||
|
scrollback: number;
|
||||||
|
selectionColor: string;
|
||||||
|
shell: string;
|
||||||
|
shellArgs: string[];
|
||||||
|
showHamburgerMenu: boolean | '';
|
||||||
|
showWindowControls: string;
|
||||||
|
termCSS: string;
|
||||||
|
uiFontFamily: string;
|
||||||
|
updateChannel: 'stable' | 'canary';
|
||||||
|
useConpty: boolean;
|
||||||
|
webGLRenderer: boolean;
|
||||||
|
webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift';
|
||||||
|
windowSize: [number, number];
|
||||||
|
workingDirectory: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type rawConfig = {
|
||||||
|
config?: configOptions;
|
||||||
|
plugins?: string[];
|
||||||
|
localPlugins?: string[];
|
||||||
|
keymaps?: Record<string, string | string[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type parsedConfig = {
|
||||||
|
config: configOptions;
|
||||||
|
plugins: string[];
|
||||||
|
localPlugins: string[];
|
||||||
|
keymaps: Record<string, string[]>;
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type {configOptions} from '../config';
|
import {configOptions} from '../config';
|
||||||
|
|
||||||
export const CONFIG_LOAD = 'CONFIG_LOAD';
|
export const CONFIG_LOAD = 'CONFIG_LOAD';
|
||||||
export const CONFIG_RELOAD = 'CONFIG_RELOAD';
|
export const CONFIG_RELOAD = 'CONFIG_RELOAD';
|
||||||
|
|
@ -8,6 +8,8 @@ export const SESSION_USER_EXIT = 'SESSION_USER_EXIT';
|
||||||
export const SESSION_SET_ACTIVE = 'SESSION_SET_ACTIVE';
|
export const SESSION_SET_ACTIVE = 'SESSION_SET_ACTIVE';
|
||||||
export const SESSION_CLEAR_ACTIVE = 'SESSION_CLEAR_ACTIVE';
|
export const SESSION_CLEAR_ACTIVE = 'SESSION_CLEAR_ACTIVE';
|
||||||
export const SESSION_USER_DATA = 'SESSION_USER_DATA';
|
export const SESSION_USER_DATA = 'SESSION_USER_DATA';
|
||||||
|
export const SESSION_URL_SET = 'SESSION_URL_SET';
|
||||||
|
export const SESSION_URL_UNSET = 'SESSION_URL_UNSET';
|
||||||
export const SESSION_SET_XTERM_TITLE = 'SESSION_SET_XTERM_TITLE';
|
export const SESSION_SET_XTERM_TITLE = 'SESSION_SET_XTERM_TITLE';
|
||||||
export const SESSION_SET_CWD = 'SESSION_SET_CWD';
|
export const SESSION_SET_CWD = 'SESSION_SET_CWD';
|
||||||
export const SESSION_SEARCH = 'SESSION_SEARCH';
|
export const SESSION_SEARCH = 'SESSION_SEARCH';
|
||||||
|
|
@ -22,7 +24,6 @@ export interface SessionAddAction {
|
||||||
splitDirection?: 'HORIZONTAL' | 'VERTICAL';
|
splitDirection?: 'HORIZONTAL' | 'VERTICAL';
|
||||||
activeUid: string | null;
|
activeUid: string | null;
|
||||||
now: number;
|
now: number;
|
||||||
profile: string;
|
|
||||||
}
|
}
|
||||||
export interface SessionResizeAction {
|
export interface SessionResizeAction {
|
||||||
type: typeof SESSION_RESIZE;
|
type: typeof SESSION_RESIZE;
|
||||||
|
|
@ -40,7 +41,6 @@ export interface SessionAddDataAction {
|
||||||
}
|
}
|
||||||
export interface SessionPtyDataAction {
|
export interface SessionPtyDataAction {
|
||||||
type: typeof SESSION_PTY_DATA;
|
type: typeof SESSION_PTY_DATA;
|
||||||
data: string;
|
|
||||||
uid: string;
|
uid: string;
|
||||||
now: number;
|
now: number;
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +62,14 @@ export interface SessionClearActiveAction {
|
||||||
export interface SessionUserDataAction {
|
export interface SessionUserDataAction {
|
||||||
type: typeof SESSION_USER_DATA;
|
type: typeof SESSION_USER_DATA;
|
||||||
}
|
}
|
||||||
|
export interface SessionUrlSetAction {
|
||||||
|
type: typeof SESSION_URL_SET;
|
||||||
|
uid: string;
|
||||||
|
}
|
||||||
|
export interface SessionUrlUnsetAction {
|
||||||
|
type: typeof SESSION_URL_UNSET;
|
||||||
|
uid: string;
|
||||||
|
}
|
||||||
export interface SessionSetXtermTitleAction {
|
export interface SessionSetXtermTitleAction {
|
||||||
type: typeof SESSION_SET_XTERM_TITLE;
|
type: typeof SESSION_SET_XTERM_TITLE;
|
||||||
uid: string;
|
uid: string;
|
||||||
|
|
@ -88,6 +96,8 @@ export type SessionActions =
|
||||||
| SessionSetActiveAction
|
| SessionSetActiveAction
|
||||||
| SessionClearActiveAction
|
| SessionClearActiveAction
|
||||||
| SessionUserDataAction
|
| SessionUserDataAction
|
||||||
|
| SessionUrlSetAction
|
||||||
|
| SessionUrlUnsetAction
|
||||||
| SessionSetXtermTitleAction
|
| SessionSetXtermTitleAction
|
||||||
| SessionSetCwdAction
|
| SessionSetCwdAction
|
||||||
| SessionSearchAction;
|
| SessionSearchAction;
|
||||||
|
|
@ -2,10 +2,10 @@ export const TERM_GROUP_REQUEST = 'TERM_GROUP_REQUEST';
|
||||||
export const TERM_GROUP_EXIT = 'TERM_GROUP_EXIT';
|
export const TERM_GROUP_EXIT = 'TERM_GROUP_EXIT';
|
||||||
export const TERM_GROUP_RESIZE = 'TERM_GROUP_RESIZE';
|
export const TERM_GROUP_RESIZE = 'TERM_GROUP_RESIZE';
|
||||||
export const TERM_GROUP_EXIT_ACTIVE = 'TERM_GROUP_EXIT_ACTIVE';
|
export const TERM_GROUP_EXIT_ACTIVE = 'TERM_GROUP_EXIT_ACTIVE';
|
||||||
export enum DIRECTION {
|
export const DIRECTION = {
|
||||||
HORIZONTAL = 'HORIZONTAL',
|
HORIZONTAL: 'HORIZONTAL',
|
||||||
VERTICAL = 'VERTICAL'
|
VERTICAL: 'VERTICAL'
|
||||||
}
|
} as const;
|
||||||
|
|
||||||
export interface TermGroupRequestAction {
|
export interface TermGroupRequestAction {
|
||||||
type: typeof TERM_GROUP_REQUEST;
|
type: typeof TERM_GROUP_REQUEST;
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import {createSelector} from 'reselect';
|
import {createSelector} from 'reselect';
|
||||||
|
|
||||||
import type {HyperState, HyperDispatch, ITab} from '../../typings/hyper';
|
|
||||||
import {closeTab, changeTab, maximize, openHamburgerMenu, unmaximize, minimize, close} from '../actions/header';
|
|
||||||
import {requestTermGroup} from '../actions/term-groups';
|
|
||||||
import Header from '../components/header';
|
import Header from '../components/header';
|
||||||
import {getRootGroups} from '../selectors';
|
import {closeTab, changeTab, maximize, openHamburgerMenu, unmaximize, minimize, close} from '../actions/header';
|
||||||
import {connect} from '../utils/plugins';
|
import {connect} from '../utils/plugins';
|
||||||
|
import {getRootGroups} from '../selectors';
|
||||||
|
import {HyperState, HyperDispatch, ITab} from '../hyper';
|
||||||
|
|
||||||
const isMac = /Mac/.test(navigator.userAgent);
|
const isMac = /Mac/.test(navigator.userAgent);
|
||||||
|
|
||||||
|
|
@ -39,9 +38,7 @@ const mapStateToProps = (state: HyperState) => {
|
||||||
maximized: state.ui.maximized,
|
maximized: state.ui.maximized,
|
||||||
fullScreen: state.ui.fullScreen,
|
fullScreen: state.ui.fullScreen,
|
||||||
showHamburgerMenu: state.ui.showHamburgerMenu,
|
showHamburgerMenu: state.ui.showHamburgerMenu,
|
||||||
showWindowControls: state.ui.showWindowControls,
|
showWindowControls: state.ui.showWindowControls
|
||||||
defaultProfile: state.ui.defaultProfile,
|
|
||||||
profiles: state.ui.profiles
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,10 +70,6 @@ const mapDispatchToProps = (dispatch: HyperDispatch) => {
|
||||||
|
|
||||||
close: () => {
|
close: () => {
|
||||||
dispatch(close());
|
dispatch(close());
|
||||||
},
|
|
||||||
|
|
||||||
openNewTab: (profile: string) => {
|
|
||||||
dispatch(requestTermGroup(undefined, profile));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,113 +1,122 @@
|
||||||
import React, {forwardRef, useEffect, useRef} from 'react';
|
import React from 'react';
|
||||||
|
import Mousetrap, {MousetrapInstance} from 'mousetrap';
|
||||||
|
|
||||||
import Mousetrap from 'mousetrap';
|
import {connect} from '../utils/plugins';
|
||||||
import type {MousetrapInstance} from 'mousetrap';
|
|
||||||
import stylis from 'stylis';
|
|
||||||
|
|
||||||
import type {HyperState, HyperProps, HyperDispatch} from '../../typings/hyper';
|
|
||||||
import * as uiActions from '../actions/ui';
|
import * as uiActions from '../actions/ui';
|
||||||
import {getRegisteredKeys, getCommandHandler, shouldPreventDefault} from '../command-registry';
|
import {getRegisteredKeys, getCommandHandler, shouldPreventDefault} from '../command-registry';
|
||||||
import type Terms from '../components/terms';
|
import stylis from 'stylis';
|
||||||
import {connect} from '../utils/plugins';
|
|
||||||
|
|
||||||
import {HeaderContainer} from './header';
|
import {HeaderContainer} from './header';
|
||||||
import NotificationsContainer from './notifications';
|
|
||||||
import TermsContainer from './terms';
|
import TermsContainer from './terms';
|
||||||
|
import NotificationsContainer from './notifications';
|
||||||
|
import {HyperState, HyperProps, HyperDispatch} from '../hyper';
|
||||||
|
import Terms from '../components/terms';
|
||||||
|
|
||||||
const isMac = /Mac/.test(navigator.userAgent);
|
const isMac = /Mac/.test(navigator.userAgent);
|
||||||
|
|
||||||
const Hyper = forwardRef<HTMLDivElement, HyperProps>((props, ref) => {
|
class Hyper extends React.PureComponent<HyperProps> {
|
||||||
const mousetrap = useRef<MousetrapInstance | null>(null);
|
mousetrap!: MousetrapInstance;
|
||||||
const terms = useRef<Terms | null>(null);
|
terms!: Terms;
|
||||||
|
constructor(props: HyperProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
componentDidUpdate(prev: HyperProps) {
|
||||||
void attachKeyListeners();
|
if (this.props.backgroundColor !== prev.backgroundColor) {
|
||||||
}, [props.lastConfigUpdate]);
|
// this can be removed when `setBackgroundColor` in electron
|
||||||
useEffect(() => {
|
// starts working again
|
||||||
handleFocusActive(props.activeSession);
|
document.body.style.backgroundColor = this.props.backgroundColor;
|
||||||
}, [props.activeSession]);
|
}
|
||||||
|
const {lastConfigUpdate} = this.props;
|
||||||
|
if (lastConfigUpdate && lastConfigUpdate !== prev.lastConfigUpdate) {
|
||||||
|
this.attachKeyListeners();
|
||||||
|
}
|
||||||
|
if (prev.activeSession !== this.props.activeSession) {
|
||||||
|
this.handleFocusActive(this.props.activeSession!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleFocusActive = (uid?: string | null) => {
|
handleFocusActive = (uid?: string) => {
|
||||||
const term = uid && terms.current?.getTermByUid(uid);
|
const term = uid && this.terms.getTermByUid(uid);
|
||||||
if (term) {
|
if (term) {
|
||||||
term.focus();
|
term.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAll = () => {
|
handleSelectAll = () => {
|
||||||
const term = terms.current?.getActiveTerm();
|
const term = this.terms.getActiveTerm();
|
||||||
if (term) {
|
if (term) {
|
||||||
term.selectAll();
|
term.selectAll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const attachKeyListeners = async () => {
|
attachKeyListeners() {
|
||||||
if (!mousetrap.current) {
|
if (!this.mousetrap) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
mousetrap.current = new (Mousetrap as any)(window, true);
|
this.mousetrap = new (Mousetrap as any)(window, true);
|
||||||
mousetrap.current!.stopCallback = () => {
|
this.mousetrap.stopCallback = () => {
|
||||||
// All events should be intercepted even if focus is in an input/textarea
|
// All events should be intercepted even if focus is in an input/textarea
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mousetrap.current.reset();
|
this.mousetrap.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = await getRegisteredKeys();
|
const keys = getRegisteredKeys();
|
||||||
Object.keys(keys).forEach((commandKeys) => {
|
Object.keys(keys).forEach((commandKeys) => {
|
||||||
mousetrap.current?.bind(
|
this.mousetrap.bind(
|
||||||
commandKeys,
|
commandKeys,
|
||||||
(e) => {
|
(e) => {
|
||||||
const command = keys[commandKeys];
|
const command = keys[commandKeys];
|
||||||
// We should tell xterm to ignore this event.
|
// We should tell to xterm that it should ignore this event.
|
||||||
(e as any).catched = true;
|
(e as any).catched = true;
|
||||||
props.execCommand(command, getCommandHandler(command), e);
|
this.props.execCommand(command, getCommandHandler(command), e);
|
||||||
shouldPreventDefault(command) && e.preventDefault();
|
shouldPreventDefault(command) && e.preventDefault();
|
||||||
},
|
},
|
||||||
'keydown'
|
'keydown'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
componentDidMount() {
|
||||||
void attachKeyListeners();
|
this.attachKeyListeners();
|
||||||
window.rpc.on('term selectAll', handleSelectAll);
|
window.rpc.on('term selectAll', this.handleSelectAll);
|
||||||
}, []);
|
}
|
||||||
|
|
||||||
const onTermsRef = (_terms: Terms | null) => {
|
onTermsRef = (terms: Terms) => {
|
||||||
terms.current = _terms;
|
this.terms = terms;
|
||||||
window.focusActiveTerm = (uid?: string) => {
|
window.focusActiveTerm = (uid?: string) => {
|
||||||
if (uid) {
|
if (uid) {
|
||||||
handleFocusActive(uid);
|
this.handleFocusActive(uid);
|
||||||
} else {
|
} else {
|
||||||
terms.current?.getActiveTerm()?.focus();
|
this.terms.getActiveTerm().focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
componentWillUnmount() {
|
||||||
return () => {
|
document.body.style.backgroundColor = 'inherit';
|
||||||
mousetrap.current?.reset();
|
this.mousetrap?.reset();
|
||||||
};
|
}
|
||||||
}, []);
|
|
||||||
|
|
||||||
const {isMac: isMac_, customCSS, uiFontFamily, borderColor, maximized, fullScreen} = props;
|
render() {
|
||||||
|
const {isMac: isMac_, customCSS, uiFontFamily, borderColor, maximized, fullScreen} = this.props;
|
||||||
const borderWidth = isMac_ ? '' : `${maximized ? '0' : '1'}px`;
|
const borderWidth = isMac_ ? '' : `${maximized ? '0' : '1'}px`;
|
||||||
stylis.set({prefix: false});
|
stylis.set({prefix: false});
|
||||||
return (
|
return (
|
||||||
<div id="hyper" ref={ref}>
|
<div id="hyper">
|
||||||
<div
|
<div
|
||||||
style={{fontFamily: uiFontFamily, borderColor, borderWidth}}
|
style={{fontFamily: uiFontFamily, borderColor, borderWidth}}
|
||||||
className={`hyper_main ${isMac_ && 'hyper_mainRounded'} ${fullScreen ? 'fullScreen' : ''}`}
|
className={`hyper_main ${isMac_ && 'hyper_mainRounded'} ${fullScreen ? 'fullScreen' : ''}`}
|
||||||
>
|
>
|
||||||
<HeaderContainer />
|
<HeaderContainer />
|
||||||
<TermsContainer ref_={onTermsRef} />
|
<TermsContainer ref_={this.onTermsRef} />
|
||||||
{props.customInnerChildren}
|
{this.props.customInnerChildren}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NotificationsContainer />
|
<NotificationsContainer />
|
||||||
|
|
||||||
{props.customChildren}
|
{this.props.customChildren}
|
||||||
|
|
||||||
<style jsx>
|
<style jsx>
|
||||||
{`
|
{`
|
||||||
|
|
@ -121,8 +130,7 @@ const Hyper = forwardRef<HTMLDivElement, HyperProps>((props, ref) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyper_mainRounded {
|
.hyper_mainRounded {
|
||||||
border-radius: 10.5px;
|
border-radius: 5px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -134,9 +142,8 @@ const Hyper = forwardRef<HTMLDivElement, HyperProps>((props, ref) => {
|
||||||
<style dangerouslySetInnerHTML={{__html: stylis('#hyper', customCSS)}} />
|
<style dangerouslySetInnerHTML={{__html: stylis('#hyper', customCSS)}} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
Hyper.displayName = 'Hyper';
|
|
||||||
|
|
||||||
const mapStateToProps = (state: HyperState) => {
|
const mapStateToProps = (state: HyperState) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type {HyperState, HyperDispatch} from '../../typings/hyper';
|
|
||||||
import {dismissNotification} from '../actions/notifications';
|
|
||||||
import {installUpdate} from '../actions/updater';
|
|
||||||
import Notifications from '../components/notifications';
|
import Notifications from '../components/notifications';
|
||||||
|
import {installUpdate} from '../actions/updater';
|
||||||
import {connect} from '../utils/plugins';
|
import {connect} from '../utils/plugins';
|
||||||
|
import {dismissNotification} from '../actions/notifications';
|
||||||
|
import {HyperState, HyperDispatch} from '../hyper';
|
||||||
|
|
||||||
const mapStateToProps = (state: HyperState) => {
|
const mapStateToProps = (state: HyperState) => {
|
||||||
const {ui} = state;
|
const {ui} = state;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type {HyperState, HyperDispatch} from '../../typings/hyper';
|
import Terms from '../components/terms';
|
||||||
|
import {connect} from '../utils/plugins';
|
||||||
import {
|
import {
|
||||||
resizeSession,
|
resizeSession,
|
||||||
sendSessionData,
|
sendSessionData,
|
||||||
|
|
@ -7,10 +8,10 @@ import {
|
||||||
openSearch,
|
openSearch,
|
||||||
closeSearch
|
closeSearch
|
||||||
} from '../actions/sessions';
|
} from '../actions/sessions';
|
||||||
|
|
||||||
import {openContextMenu} from '../actions/ui';
|
import {openContextMenu} from '../actions/ui';
|
||||||
import Terms from '../components/terms';
|
|
||||||
import {getRootGroups} from '../selectors';
|
import {getRootGroups} from '../selectors';
|
||||||
import {connect} from '../utils/plugins';
|
import {HyperState, HyperDispatch} from '../hyper';
|
||||||
|
|
||||||
const mapStateToProps = (state: HyperState) => {
|
const mapStateToProps = (state: HyperState) => {
|
||||||
const {sessions} = state.sessions;
|
const {sessions} = state.sessions;
|
||||||
|
|
@ -52,15 +53,13 @@ const mapStateToProps = (state: HyperState) => {
|
||||||
webLinksActivationKey: state.ui.webLinksActivationKey,
|
webLinksActivationKey: state.ui.webLinksActivationKey,
|
||||||
macOptionSelectionMode: state.ui.macOptionSelectionMode,
|
macOptionSelectionMode: state.ui.macOptionSelectionMode,
|
||||||
disableLigatures: state.ui.disableLigatures,
|
disableLigatures: state.ui.disableLigatures,
|
||||||
screenReaderMode: state.ui.screenReaderMode,
|
screenReaderMode: state.ui.screenReaderMode
|
||||||
windowsPty: state.ui.windowsPty,
|
|
||||||
imageSupport: state.ui.imageSupport
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: HyperDispatch) => {
|
const mapDispatchToProps = (dispatch: HyperDispatch) => {
|
||||||
return {
|
return {
|
||||||
onData(uid: string, data: string) {
|
onData(uid: string, data: any) {
|
||||||
dispatch(sendSessionData(uid, data));
|
dispatch(sendSessionData(uid, data));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -84,7 +83,7 @@ const mapDispatchToProps = (dispatch: HyperDispatch) => {
|
||||||
dispatch(closeSearch(uid));
|
dispatch(closeSearch(uid));
|
||||||
},
|
},
|
||||||
|
|
||||||
onContextMenu(uid: string, selection: string) {
|
onContextMenu(uid: string, selection: any) {
|
||||||
dispatch(setActiveSession(uid));
|
dispatch(setActiveSession(uid));
|
||||||
dispatch(openContextMenu(uid, selection));
|
dispatch(openContextMenu(uid, selection));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
lib/ext-modules.d.ts
vendored
Normal file
7
lib/ext-modules.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
declare module 'php-escape-shell' {
|
||||||
|
export function php_escapeshellcmd(path: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'react-deep-force-update' {
|
||||||
|
export default function (...args: any[]): any;
|
||||||
|
}
|
||||||
97
typings/hyper.d.ts → lib/hyper.d.ts
vendored
97
typings/hyper.d.ts → lib/hyper.d.ts
vendored
|
|
@ -1,14 +1,11 @@
|
||||||
// eslint-disable-next-line eslint-comments/disable-enable-pair
|
import {Immutable} from 'seamless-immutable';
|
||||||
/* eslint-disable import/order */
|
import Client from './utils/rpc';
|
||||||
import type {Immutable} from 'seamless-immutable';
|
|
||||||
import type Client from '../lib/utils/rpc';
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
__rpcId: string;
|
__rpcId: string;
|
||||||
rpc: Client;
|
rpc: Client;
|
||||||
focusActiveTerm: (uid?: string) => void;
|
focusActiveTerm: (uid?: string) => void;
|
||||||
profileName: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshotResult: {
|
const snapshotResult: {
|
||||||
|
|
@ -41,15 +38,15 @@ export type ITermState = Immutable<{
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type cursorShapes = 'BEAM' | 'UNDERLINE' | 'BLOCK';
|
export type cursorShapes = 'BEAM' | 'UNDERLINE' | 'BLOCK';
|
||||||
import type {FontWeight, IWindowsPty, Terminal} from 'xterm';
|
import {FontWeight, Terminal} from 'xterm';
|
||||||
import type {ColorMap, configOptions} from './config';
|
import {ColorMap} from './config';
|
||||||
|
|
||||||
export type uiState = Immutable<{
|
export type uiState = Immutable<{
|
||||||
_lastUpdate: number | null;
|
_lastUpdate: number | null;
|
||||||
activeUid: string | null;
|
activeUid: string | null;
|
||||||
activityMarkers: Record<string, boolean>;
|
activityMarkers: Record<string, boolean>;
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
bell: 'SOUND' | false;
|
bell: string;
|
||||||
bellSoundURL: string | null;
|
bellSoundURL: string | null;
|
||||||
bellSound: string | null;
|
bellSound: string | null;
|
||||||
borderColor: string;
|
borderColor: string;
|
||||||
|
|
@ -71,7 +68,6 @@ export type uiState = Immutable<{
|
||||||
fontWeightBold: FontWeight;
|
fontWeightBold: FontWeight;
|
||||||
foregroundColor: string;
|
foregroundColor: string;
|
||||||
fullScreen: boolean;
|
fullScreen: boolean;
|
||||||
imageSupport: boolean;
|
|
||||||
letterSpacing: number;
|
letterSpacing: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
macOptionSelectionMode: string;
|
macOptionSelectionMode: string;
|
||||||
|
|
@ -98,7 +94,7 @@ export type uiState = Immutable<{
|
||||||
scrollback: number;
|
scrollback: number;
|
||||||
selectionColor: string;
|
selectionColor: string;
|
||||||
showHamburgerMenu: boolean | '';
|
showHamburgerMenu: boolean | '';
|
||||||
showWindowControls: boolean | 'left' | '';
|
showWindowControls: string;
|
||||||
termCSS: string;
|
termCSS: string;
|
||||||
uiFontFamily: string;
|
uiFontFamily: string;
|
||||||
updateCanInstall: null | boolean;
|
updateCanInstall: null | boolean;
|
||||||
|
|
@ -106,10 +102,7 @@ export type uiState = Immutable<{
|
||||||
updateReleaseUrl: string | null;
|
updateReleaseUrl: string | null;
|
||||||
updateVersion: string | null;
|
updateVersion: string | null;
|
||||||
webGLRenderer: boolean;
|
webGLRenderer: boolean;
|
||||||
webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift' | '';
|
webLinksActivationKey: string;
|
||||||
windowsPty?: IWindowsPty;
|
|
||||||
defaultProfile: string;
|
|
||||||
profiles: configOptions['profiles'];
|
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type session = {
|
export type session = {
|
||||||
|
|
@ -122,11 +115,10 @@ export type session = {
|
||||||
shell: string | null;
|
shell: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
uid: string;
|
uid: string;
|
||||||
|
url: string | null;
|
||||||
splitDirection?: 'HORIZONTAL' | 'VERTICAL';
|
splitDirection?: 'HORIZONTAL' | 'VERTICAL';
|
||||||
activeUid?: string;
|
activeUid?: string;
|
||||||
profile: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type sessionState = Immutable<{
|
export type sessionState = Immutable<{
|
||||||
sessions: Record<string, session>;
|
sessions: Record<string, session>;
|
||||||
activeUid: string | null;
|
activeUid: string | null;
|
||||||
|
|
@ -139,7 +131,7 @@ export type IUiReducer = Reducer<uiState, HyperActions>;
|
||||||
|
|
||||||
export type ISessionReducer = Reducer<sessionState, HyperActions>;
|
export type ISessionReducer = Reducer<sessionState, HyperActions>;
|
||||||
|
|
||||||
import type {Middleware, Reducer} from 'redux';
|
import {Middleware, Reducer} from 'redux';
|
||||||
export type hyperPlugin = {
|
export type hyperPlugin = {
|
||||||
getTabProps: any;
|
getTabProps: any;
|
||||||
getTabsProps: any;
|
getTabsProps: any;
|
||||||
|
|
@ -169,14 +161,14 @@ export type HyperState = {
|
||||||
termGroups: ITermState;
|
termGroups: ITermState;
|
||||||
};
|
};
|
||||||
|
|
||||||
import type {UIActions} from './constants/ui';
|
import {UIActions} from './constants/ui';
|
||||||
import type {ConfigActions} from './constants/config';
|
import {ConfigActions} from './constants/config';
|
||||||
import type {SessionActions} from './constants/sessions';
|
import {SessionActions} from './constants/sessions';
|
||||||
import type {NotificationActions} from './constants/notifications';
|
import {NotificationActions} from './constants/notifications';
|
||||||
import type {UpdateActions} from './constants/updater';
|
import {UpdateActions} from './constants/updater';
|
||||||
import type {TermGroupActions} from './constants/term-groups';
|
import {TermGroupActions} from './constants/term-groups';
|
||||||
import type {InitActions} from './constants';
|
import {InitActions} from './constants';
|
||||||
import type {TabActions} from './constants/tabs';
|
import {TabActions} from './constants/tabs';
|
||||||
|
|
||||||
export type HyperActions = (
|
export type HyperActions = (
|
||||||
| UIActions
|
| UIActions
|
||||||
|
|
@ -189,10 +181,10 @@ export type HyperActions = (
|
||||||
| TabActions
|
| TabActions
|
||||||
) & {effect?: () => void};
|
) & {effect?: () => void};
|
||||||
|
|
||||||
import type configureStore from '../lib/store/configure-store';
|
import configureStore from './store/configure-store';
|
||||||
export type HyperDispatch = ReturnType<typeof configureStore>['dispatch'];
|
export type HyperDispatch = ReturnType<typeof configureStore>['dispatch'];
|
||||||
|
|
||||||
import type {ReactChild, ReactNode} from 'react';
|
import {ReactChild} from 'react';
|
||||||
type extensionProps = Partial<{
|
type extensionProps = Partial<{
|
||||||
customChildren: ReactChild | ReactChild[];
|
customChildren: ReactChild | ReactChild[];
|
||||||
customChildrenBefore: ReactChild | ReactChild[];
|
customChildrenBefore: ReactChild | ReactChild[];
|
||||||
|
|
@ -200,17 +192,17 @@ type extensionProps = Partial<{
|
||||||
customInnerChildren: ReactChild | ReactChild[];
|
customInnerChildren: ReactChild | ReactChild[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
import type {HeaderConnectedProps} from '../lib/containers/header';
|
import {HeaderConnectedProps} from './containers/header';
|
||||||
export type HeaderProps = HeaderConnectedProps & extensionProps;
|
export type HeaderProps = HeaderConnectedProps & extensionProps;
|
||||||
|
|
||||||
import type {HyperConnectedProps} from '../lib/containers/hyper';
|
import {HyperConnectedProps} from './containers/hyper';
|
||||||
export type HyperProps = HyperConnectedProps & extensionProps;
|
export type HyperProps = HyperConnectedProps & extensionProps;
|
||||||
|
|
||||||
import type {NotificationsConnectedProps} from '../lib/containers/notifications';
|
import {NotificationsConnectedProps} from './containers/notifications';
|
||||||
export type NotificationsProps = NotificationsConnectedProps & extensionProps;
|
export type NotificationsProps = NotificationsConnectedProps & extensionProps;
|
||||||
|
|
||||||
import type Terms from '../lib/components/terms';
|
import Terms from './components/terms';
|
||||||
import type {TermsConnectedProps} from '../lib/containers/terms';
|
import {TermsConnectedProps} from './containers/terms';
|
||||||
export type TermsProps = TermsConnectedProps & extensionProps & {ref_: (terms: Terms | null) => void};
|
export type TermsProps = TermsConnectedProps & extensionProps & {ref_: (terms: Terms | null) => void};
|
||||||
|
|
||||||
export type StyleSheetProps = {
|
export type StyleSheetProps = {
|
||||||
|
|
@ -242,13 +234,9 @@ export type ITab = {
|
||||||
export type TabsProps = {
|
export type TabsProps = {
|
||||||
tabs: ITab[];
|
tabs: ITab[];
|
||||||
borderColor: string;
|
borderColor: string;
|
||||||
backgroundColor: string;
|
|
||||||
onChange: (uid: string) => void;
|
onChange: (uid: string) => void;
|
||||||
onClose: (uid: string) => void;
|
onClose: (uid: string) => void;
|
||||||
fullScreen: boolean;
|
fullScreen: boolean;
|
||||||
defaultProfile: string;
|
|
||||||
profiles: configOptions['profiles'];
|
|
||||||
openNewTab: (profile: string) => void;
|
|
||||||
} & extensionProps;
|
} & extensionProps;
|
||||||
|
|
||||||
export type NotificationProps = {
|
export type NotificationProps = {
|
||||||
|
|
@ -261,15 +249,18 @@ export type NotificationProps = {
|
||||||
userDismissColor?: string;
|
userDismissColor?: string;
|
||||||
} & extensionProps;
|
} & extensionProps;
|
||||||
|
|
||||||
|
export type NotificationState = {
|
||||||
|
dismissing: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type SplitPaneProps = {
|
export type SplitPaneProps = {
|
||||||
borderColor: string;
|
borderColor: string;
|
||||||
direction: 'horizontal' | 'vertical';
|
direction: 'horizontal' | 'vertical';
|
||||||
onResize: (sizes: number[]) => void;
|
onResize: Function;
|
||||||
sizes?: Immutable<number[]> | null;
|
sizes?: Immutable<number[]> | null;
|
||||||
children: ReactNode[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
import type Term from '../lib/components/term';
|
import Term from './components/term';
|
||||||
|
|
||||||
export type TermGroupOwnProps = {
|
export type TermGroupOwnProps = {
|
||||||
cursorAccentColor?: string;
|
cursorAccentColor?: string;
|
||||||
|
|
@ -317,36 +308,23 @@ export type TermGroupOwnProps = {
|
||||||
| 'uiFontFamily'
|
| 'uiFontFamily'
|
||||||
| 'webGLRenderer'
|
| 'webGLRenderer'
|
||||||
| 'webLinksActivationKey'
|
| 'webLinksActivationKey'
|
||||||
| 'windowsPty'
|
|
||||||
| 'imageSupport'
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
import type {TermGroupConnectedProps} from '../lib/components/term-group';
|
import {TermGroupConnectedProps} from './components/term-group';
|
||||||
export type TermGroupProps = TermGroupConnectedProps & TermGroupOwnProps;
|
export type TermGroupProps = TermGroupConnectedProps & TermGroupOwnProps;
|
||||||
|
|
||||||
export type SearchBoxProps = {
|
export type SearchBoxProps = {
|
||||||
caseSensitive: boolean;
|
search: (searchTerm: string) => void;
|
||||||
wholeWord: boolean;
|
|
||||||
regex: boolean;
|
|
||||||
results: {resultIndex: number; resultCount: number} | undefined;
|
|
||||||
toggleCaseSensitive: () => void;
|
|
||||||
toggleWholeWord: () => void;
|
|
||||||
toggleRegex: () => void;
|
|
||||||
next: (searchTerm: string) => void;
|
next: (searchTerm: string) => void;
|
||||||
prev: (searchTerm: string) => void;
|
prev: (searchTerm: string) => void;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
backgroundColor: string;
|
|
||||||
foregroundColor: string;
|
|
||||||
borderColor: string;
|
|
||||||
selectionColor: string;
|
|
||||||
font: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
import type {FitAddon} from 'xterm-addon-fit';
|
import {FitAddon} from 'xterm-addon-fit';
|
||||||
import type {SearchAddon} from 'xterm-addon-search';
|
import {SearchAddon} from 'xterm-addon-search';
|
||||||
export type TermProps = {
|
export type TermProps = {
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
bell: 'SOUND' | false;
|
bell: string;
|
||||||
bellSound: string | null;
|
bellSound: string | null;
|
||||||
bellSoundURL: string | null;
|
bellSoundURL: string | null;
|
||||||
borderColor: string;
|
borderColor: string;
|
||||||
|
|
@ -366,7 +344,6 @@ export type TermProps = {
|
||||||
fontWeight: FontWeight;
|
fontWeight: FontWeight;
|
||||||
fontWeightBold: FontWeight;
|
fontWeightBold: FontWeight;
|
||||||
foregroundColor: string;
|
foregroundColor: string;
|
||||||
imageSupport: boolean;
|
|
||||||
isTermActive: boolean;
|
isTermActive: boolean;
|
||||||
letterSpacing: number;
|
letterSpacing: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
|
|
@ -391,9 +368,9 @@ export type TermProps = {
|
||||||
term: Terminal | null;
|
term: Terminal | null;
|
||||||
uid: string;
|
uid: string;
|
||||||
uiFontFamily: string;
|
uiFontFamily: string;
|
||||||
|
url: string | null;
|
||||||
webGLRenderer: boolean;
|
webGLRenderer: boolean;
|
||||||
webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift' | '';
|
webLinksActivationKey: string;
|
||||||
windowsPty?: IWindowsPty;
|
|
||||||
ref_: (uid: string, term: Term | null) => void;
|
ref_: (uid: string, term: Term | null) => void;
|
||||||
} & extensionProps;
|
} & extensionProps;
|
||||||
|
|
||||||
|
|
@ -1,25 +1,24 @@
|
||||||
import './v8-snapshot-util';
|
import './v8-snapshot-util';
|
||||||
import {webFrame} from 'electron';
|
import {webFrame} from 'electron';
|
||||||
import React from 'react';
|
import forceUpdate from 'react-deep-force-update';
|
||||||
|
|
||||||
import {createRoot} from 'react-dom/client';
|
|
||||||
import {Provider} from 'react-redux';
|
import {Provider} from 'react-redux';
|
||||||
|
import React from 'react';
|
||||||
|
import {render} from 'react-dom';
|
||||||
|
|
||||||
import type {configOptions} from '../typings/config';
|
import rpc from './rpc';
|
||||||
|
|
||||||
import {loadConfig, reloadConfig} from './actions/config';
|
|
||||||
import init from './actions/index';
|
import init from './actions/index';
|
||||||
import {addNotificationMessage} from './actions/notifications';
|
import * as config from './utils/config';
|
||||||
import * as sessionActions from './actions/sessions';
|
import * as plugins from './utils/plugins';
|
||||||
import * as termGroupActions from './actions/term-groups';
|
import {getBase64FileData} from './utils/file';
|
||||||
import * as uiActions from './actions/ui';
|
import * as uiActions from './actions/ui';
|
||||||
import * as updaterActions from './actions/updater';
|
import * as updaterActions from './actions/updater';
|
||||||
|
import * as sessionActions from './actions/sessions';
|
||||||
|
import * as termGroupActions from './actions/term-groups';
|
||||||
|
import {addNotificationMessage} from './actions/notifications';
|
||||||
|
import {loadConfig, reloadConfig} from './actions/config';
|
||||||
import HyperContainer from './containers/hyper';
|
import HyperContainer from './containers/hyper';
|
||||||
import rpc from './rpc';
|
|
||||||
import configureStore from './store/configure-store';
|
import configureStore from './store/configure-store';
|
||||||
import * as config from './utils/config';
|
import {configOptions} from './config';
|
||||||
import {getBase64FileData} from './utils/file';
|
|
||||||
import * as plugins from './utils/plugins';
|
|
||||||
|
|
||||||
// On Linux, the default zoom was somehow changed with Electron 3 (or maybe 2).
|
// On Linux, the default zoom was somehow changed with Electron 3 (or maybe 2).
|
||||||
// Setting zoom factor to 1.2 brings back the normal default size
|
// Setting zoom factor to 1.2 brings back the normal default size
|
||||||
|
|
@ -157,16 +156,16 @@ rpc.on('session search close', () => {
|
||||||
store_.dispatch(sessionActions.closeSearch());
|
store_.dispatch(sessionActions.closeSearch());
|
||||||
});
|
});
|
||||||
|
|
||||||
rpc.on('termgroup add req', ({activeUid, profile}) => {
|
rpc.on('termgroup add req', ({activeUid}) => {
|
||||||
store_.dispatch(termGroupActions.requestTermGroup(activeUid, profile));
|
store_.dispatch(termGroupActions.requestTermGroup(activeUid));
|
||||||
});
|
});
|
||||||
|
|
||||||
rpc.on('split request horizontal', ({activeUid, profile}) => {
|
rpc.on('split request horizontal', ({activeUid}) => {
|
||||||
store_.dispatch(termGroupActions.requestHorizontalSplit(activeUid, profile));
|
store_.dispatch(termGroupActions.requestHorizontalSplit(activeUid));
|
||||||
});
|
});
|
||||||
|
|
||||||
rpc.on('split request vertical', ({activeUid, profile}) => {
|
rpc.on('split request vertical', ({activeUid}) => {
|
||||||
store_.dispatch(termGroupActions.requestVerticalSplit(activeUid, profile));
|
store_.dispatch(termGroupActions.requestVerticalSplit(activeUid));
|
||||||
});
|
});
|
||||||
|
|
||||||
rpc.on('reset fontSize req', () => {
|
rpc.on('reset fontSize req', () => {
|
||||||
|
|
@ -233,14 +232,14 @@ rpc.on('leave full screen', () => {
|
||||||
store_.dispatch(uiActions.leaveFullScreen());
|
store_.dispatch(uiActions.leaveFullScreen());
|
||||||
});
|
});
|
||||||
|
|
||||||
const root = createRoot(document.getElementById('mount')!);
|
const app = render(
|
||||||
|
|
||||||
root.render(
|
|
||||||
<Provider store={store_}>
|
<Provider store={store_}>
|
||||||
<HyperContainer />
|
<HyperContainer />
|
||||||
</Provider>
|
</Provider>,
|
||||||
|
document.getElementById('mount')
|
||||||
);
|
);
|
||||||
|
|
||||||
rpc.on('reload', () => {
|
rpc.on('reload', () => {
|
||||||
plugins.reload();
|
plugins.reload();
|
||||||
|
forceUpdate(app);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import {combineReducers} from 'redux';
|
import {combineReducers, Reducer} from 'redux';
|
||||||
import type {Reducer} from 'redux';
|
import ui from './ui';
|
||||||
|
|
||||||
import type {HyperActions, HyperState} from '../../typings/hyper';
|
|
||||||
|
|
||||||
import sessions from './sessions';
|
import sessions from './sessions';
|
||||||
import termGroups from './term-groups';
|
import termGroups from './term-groups';
|
||||||
import ui from './ui';
|
import {HyperActions, HyperState} from '../hyper';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
ui,
|
ui,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Immutable from 'seamless-immutable';
|
import Immutable from 'seamless-immutable';
|
||||||
|
import {decorateSessionsReducer} from '../utils/plugins';
|
||||||
import {
|
import {
|
||||||
SESSION_ADD,
|
SESSION_ADD,
|
||||||
SESSION_PTY_EXIT,
|
SESSION_PTY_EXIT,
|
||||||
|
|
@ -11,9 +11,8 @@ import {
|
||||||
SESSION_SET_XTERM_TITLE,
|
SESSION_SET_XTERM_TITLE,
|
||||||
SESSION_SET_CWD,
|
SESSION_SET_CWD,
|
||||||
SESSION_SEARCH
|
SESSION_SEARCH
|
||||||
} from '../../typings/constants/sessions';
|
} from '../constants/sessions';
|
||||||
import type {sessionState, session, Mutable, ISessionReducer} from '../../typings/hyper';
|
import {sessionState, session, Mutable, ISessionReducer} from '../hyper';
|
||||||
import {decorateSessionsReducer} from '../utils/plugins';
|
|
||||||
|
|
||||||
const initialState: sessionState = Immutable<Mutable<sessionState>>({
|
const initialState: sessionState = Immutable<Mutable<sessionState>>({
|
||||||
sessions: {},
|
sessions: {},
|
||||||
|
|
@ -26,17 +25,17 @@ function Session(obj: Immutable.DeepPartial<session>) {
|
||||||
title: '',
|
title: '',
|
||||||
cols: null,
|
cols: null,
|
||||||
rows: null,
|
rows: null,
|
||||||
|
url: null,
|
||||||
cleared: false,
|
cleared: false,
|
||||||
search: false,
|
search: false,
|
||||||
shell: '',
|
shell: '',
|
||||||
pid: null,
|
pid: null
|
||||||
profile: ''
|
|
||||||
};
|
};
|
||||||
return Immutable(x).merge(obj);
|
return Immutable(x).merge(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSession(state: sessionState, uid: string) {
|
function deleteSession(state: sessionState, uid: string) {
|
||||||
return state.updateIn(['sessions'], (sessions: (typeof state)['sessions']) => {
|
return state.updateIn(['sessions'], (sessions: typeof state['sessions']) => {
|
||||||
const sessions_ = sessions.asMutable();
|
const sessions_ = sessions.asMutable();
|
||||||
delete sessions_[uid];
|
delete sessions_[uid];
|
||||||
return sessions_;
|
return sessions_;
|
||||||
|
|
@ -53,8 +52,7 @@ const reducer: ISessionReducer = (state = initialState, action) => {
|
||||||
rows: action.rows,
|
rows: action.rows,
|
||||||
uid: action.uid,
|
uid: action.uid,
|
||||||
shell: action.shell ? action.shell.split('/').pop() : null,
|
shell: action.shell ? action.shell.split('/').pop() : null,
|
||||||
pid: action.pid,
|
pid: action.pid
|
||||||
profile: action.profile
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import Immutable from 'seamless-immutable';
|
|
||||||
import type {Immutable as ImmutableType} from 'seamless-immutable';
|
|
||||||
import {v4 as uuidv4} from 'uuid';
|
import {v4 as uuidv4} from 'uuid';
|
||||||
|
import Immutable, {Immutable as ImmutableType} from 'seamless-immutable';
|
||||||
import {SESSION_ADD, SESSION_SET_ACTIVE} from '../../typings/constants/sessions';
|
import {TERM_GROUP_EXIT, TERM_GROUP_RESIZE} from '../constants/term-groups';
|
||||||
import type {SessionAddAction} from '../../typings/constants/sessions';
|
import {SESSION_ADD, SESSION_SET_ACTIVE, SessionAddAction} from '../constants/sessions';
|
||||||
import {TERM_GROUP_EXIT, TERM_GROUP_RESIZE} from '../../typings/constants/term-groups';
|
|
||||||
import type {ITermGroup, ITermState, ITermGroups, ITermGroupReducer, Mutable} from '../../typings/hyper';
|
|
||||||
import {decorateTermGroupsReducer} from '../utils/plugins';
|
|
||||||
import findBySession from '../utils/term-groups';
|
import findBySession from '../utils/term-groups';
|
||||||
|
import {decorateTermGroupsReducer} from '../utils/plugins';
|
||||||
|
import {ITermGroup, ITermState, ITermGroups, ITermGroupReducer, Mutable} from '../hyper';
|
||||||
|
|
||||||
const MIN_SIZE = 0.05;
|
const MIN_SIZE = 0.05;
|
||||||
const initialState: ITermState = Immutable<Mutable<ITermState>>({
|
const initialState: ITermState = Immutable<Mutable<ITermState>>({
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,6 @@
|
||||||
import {release} from 'os';
|
import Immutable, {Immutable as ImmutableType} from 'seamless-immutable';
|
||||||
|
import {decorateUIReducer} from '../utils/plugins';
|
||||||
import Immutable from 'seamless-immutable';
|
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
|
||||||
import type {Immutable as ImmutableType} from 'seamless-immutable';
|
|
||||||
|
|
||||||
import {CONFIG_LOAD, CONFIG_RELOAD} from '../../typings/constants/config';
|
|
||||||
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../../typings/constants/notifications';
|
|
||||||
import {
|
|
||||||
SESSION_ADD,
|
|
||||||
SESSION_RESIZE,
|
|
||||||
SESSION_PTY_DATA,
|
|
||||||
SESSION_PTY_EXIT,
|
|
||||||
SESSION_SET_ACTIVE,
|
|
||||||
SESSION_SET_CWD
|
|
||||||
} from '../../typings/constants/sessions';
|
|
||||||
import {
|
import {
|
||||||
UI_FONT_SIZE_SET,
|
UI_FONT_SIZE_SET,
|
||||||
UI_FONT_SIZE_RESET,
|
UI_FONT_SIZE_RESET,
|
||||||
|
|
@ -22,17 +10,23 @@ import {
|
||||||
UI_WINDOW_GEOMETRY_CHANGED,
|
UI_WINDOW_GEOMETRY_CHANGED,
|
||||||
UI_ENTER_FULLSCREEN,
|
UI_ENTER_FULLSCREEN,
|
||||||
UI_LEAVE_FULLSCREEN
|
UI_LEAVE_FULLSCREEN
|
||||||
} from '../../typings/constants/ui';
|
} from '../constants/ui';
|
||||||
import {UPDATE_AVAILABLE} from '../../typings/constants/updater';
|
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications';
|
||||||
import type {uiState, Mutable, IUiReducer} from '../../typings/hyper';
|
import {
|
||||||
import {decorateUIReducer} from '../utils/plugins';
|
SESSION_ADD,
|
||||||
|
SESSION_RESIZE,
|
||||||
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(navigator.platform) || process.platform === 'win32';
|
SESSION_PTY_DATA,
|
||||||
|
SESSION_PTY_EXIT,
|
||||||
|
SESSION_SET_ACTIVE,
|
||||||
|
SESSION_SET_CWD
|
||||||
|
} from '../constants/sessions';
|
||||||
|
import {UPDATE_AVAILABLE} from '../constants/updater';
|
||||||
|
import {uiState, Mutable, IUiReducer} from '../hyper';
|
||||||
|
|
||||||
const allowedCursorShapes = new Set(['BEAM', 'BLOCK', 'UNDERLINE']);
|
const allowedCursorShapes = new Set(['BEAM', 'BLOCK', 'UNDERLINE']);
|
||||||
const allowedCursorBlinkValues = new Set([true, false]);
|
const allowedCursorBlinkValues = new Set([true, false]);
|
||||||
const allowedBells = new Set(['SOUND', 'false', false]);
|
const allowedBells = new Set(['SOUND', 'false', false]);
|
||||||
const allowedHamburgerMenuValues = new Set([true, false, ''] as const);
|
const allowedHamburgerMenuValues = new Set([true, false]);
|
||||||
const allowedWindowControlsValues = new Set([true, false, 'left']);
|
const allowedWindowControlsValues = new Set([true, false, 'left']);
|
||||||
|
|
||||||
// Populate `config-default.js` from this :)
|
// Populate `config-default.js` from this :)
|
||||||
|
|
@ -56,7 +50,6 @@ const initial: uiState = Immutable<Mutable<uiState>>({
|
||||||
fontSmoothingOverride: 'antialiased',
|
fontSmoothingOverride: 'antialiased',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontWeightBold: 'bold',
|
fontWeightBold: 'bold',
|
||||||
imageSupport: true,
|
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
css: '',
|
css: '',
|
||||||
|
|
@ -115,9 +108,7 @@ const initial: uiState = Immutable<Mutable<uiState>>({
|
||||||
webLinksActivationKey: '',
|
webLinksActivationKey: '',
|
||||||
macOptionSelectionMode: 'vertical',
|
macOptionSelectionMode: 'vertical',
|
||||||
disableLigatures: true,
|
disableLigatures: true,
|
||||||
screenReaderMode: false,
|
screenReaderMode: false
|
||||||
defaultProfile: '',
|
|
||||||
profiles: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const reducer: IUiReducer = (state = initial, action) => {
|
const reducer: IUiReducer = (state = initial, action) => {
|
||||||
|
|
@ -219,7 +210,7 @@ const reducer: IUiReducer = (state = initial, action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowedBells.has(config.bell)) {
|
if (allowedBells.has(config.bell)) {
|
||||||
ret.bell = (config.bell as any) === 'false' ? false : config.bell;
|
ret.bell = config.bell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.bellSoundURL !== state.bellSoundURL) {
|
if (config.bellSoundURL !== state.bellSoundURL) {
|
||||||
|
|
@ -244,7 +235,7 @@ const reducer: IUiReducer = (state = initial, action) => {
|
||||||
ret.modifierKeys = config.modifierKeys;
|
ret.modifierKeys = config.modifierKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) {
|
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu as any)) {
|
||||||
ret.showHamburgerMenu = config.showHamburgerMenu;
|
ret.showHamburgerMenu = config.showHamburgerMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,27 +269,6 @@ const reducer: IUiReducer = (state = initial, action) => {
|
||||||
ret.screenReaderMode = config.screenReaderMode;
|
ret.screenReaderMode = config.screenReaderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildNumber = parseInt(release().split('.').at(-1) || '0', 10);
|
|
||||||
if (isWindows && !Number.isNaN(buildNumber) && buildNumber > 0) {
|
|
||||||
const useConpty = typeof config.useConpty === 'boolean' ? config.useConpty : buildNumber >= 18309;
|
|
||||||
ret.windowsPty = {
|
|
||||||
backend: useConpty ? 'conpty' : 'winpty',
|
|
||||||
buildNumber
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.imageSupport !== undefined) {
|
|
||||||
ret.imageSupport = config.imageSupport;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.defaultProfile !== undefined) {
|
|
||||||
ret.defaultProfile = config.defaultProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.profiles !== undefined) {
|
|
||||||
ret.profiles = config.profiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret._lastUpdate = now;
|
ret._lastUpdate = now;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -461,7 +431,13 @@ const reducer: IUiReducer = (state = initial, action) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.cols !== null && state.rows !== null && (state.rows !== state_.rows || state.cols !== state_.cols)) {
|
if (
|
||||||
|
typeof state.cols !== 'undefined' &&
|
||||||
|
state.cols !== null &&
|
||||||
|
typeof state.rows !== 'undefined' &&
|
||||||
|
state.rows !== null &&
|
||||||
|
(state.rows !== state_.rows || state.cols !== state_.cols)
|
||||||
|
) {
|
||||||
state_ = state_.merge({notifications: {resize: true}}, {deep: true});
|
state_ = state_.merge({notifications: {resize: true}}, {deep: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import RPC from './utils/rpc';
|
import RPC from './utils/rpc';
|
||||||
|
|
||||||
const rpc = new RPC();
|
export default new RPC();
|
||||||
|
|
||||||
export default rpc;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {createSelector} from 'reselect';
|
import {createSelector} from 'reselect';
|
||||||
|
import {HyperState} from './hyper';
|
||||||
import type {HyperState} from '../typings/hyper';
|
|
||||||
|
|
||||||
const getTermGroups = ({termGroups}: Pick<HyperState, 'termGroups'>) => termGroups.termGroups;
|
const getTermGroups = ({termGroups}: Pick<HyperState, 'termGroups'>) => termGroups.termGroups;
|
||||||
export const getRootGroups = createSelector(getTermGroups, (termGroups) =>
|
export const getRootGroups = createSelector(getTermGroups, (termGroups) =>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
import {composeWithDevTools} from '@redux-devtools/extension';
|
|
||||||
import {createStore, applyMiddleware} from 'redux';
|
import {createStore, applyMiddleware} from 'redux';
|
||||||
import _thunk from 'redux-thunk';
|
import _thunk, {ThunkMiddleware} from 'redux-thunk';
|
||||||
import type {ThunkMiddleware} from 'redux-thunk';
|
|
||||||
|
|
||||||
import type {HyperState, HyperActions} from '../../typings/hyper';
|
|
||||||
import rootReducer from '../reducers/index';
|
import rootReducer from '../reducers/index';
|
||||||
import effects from '../utils/effects';
|
import effects from '../utils/effects';
|
||||||
import * as plugins from '../utils/plugins';
|
import * as plugins from '../utils/plugins';
|
||||||
|
|
||||||
import writeMiddleware from './write-middleware';
|
import writeMiddleware from './write-middleware';
|
||||||
|
import {composeWithDevTools} from 'redux-devtools-extension';
|
||||||
|
import {HyperState, HyperActions} from '../hyper';
|
||||||
const thunk: ThunkMiddleware<HyperState, HyperActions> = _thunk;
|
const thunk: ThunkMiddleware<HyperState, HyperActions> = _thunk;
|
||||||
|
|
||||||
const configureStoreForDevelopment = () => {
|
export default () => {
|
||||||
const enhancer = composeWithDevTools(applyMiddleware(thunk, plugins.middleware, thunk, writeMiddleware, effects));
|
const enhancer = composeWithDevTools(applyMiddleware(thunk, plugins.middleware, thunk, writeMiddleware, effects));
|
||||||
|
|
||||||
return createStore(rootReducer, enhancer);
|
return createStore(rootReducer, enhancer);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default configureStoreForDevelopment;
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
import {createStore, applyMiddleware} from 'redux';
|
import {createStore, applyMiddleware} from 'redux';
|
||||||
import _thunk from 'redux-thunk';
|
import _thunk, {ThunkMiddleware} from 'redux-thunk';
|
||||||
import type {ThunkMiddleware} from 'redux-thunk';
|
|
||||||
|
|
||||||
import type {HyperState, HyperActions} from '../../typings/hyper';
|
|
||||||
import rootReducer from '../reducers/index';
|
import rootReducer from '../reducers/index';
|
||||||
import effects from '../utils/effects';
|
import effects from '../utils/effects';
|
||||||
import * as plugins from '../utils/plugins';
|
import * as plugins from '../utils/plugins';
|
||||||
|
|
||||||
import writeMiddleware from './write-middleware';
|
import writeMiddleware from './write-middleware';
|
||||||
|
import {HyperState, HyperActions} from '../hyper';
|
||||||
const thunk: ThunkMiddleware<HyperState, HyperActions> = _thunk;
|
const thunk: ThunkMiddleware<HyperState, HyperActions> = _thunk;
|
||||||
|
|
||||||
const configureStoreForProd = () =>
|
export default () =>
|
||||||
createStore(rootReducer, applyMiddleware(thunk, plugins.middleware, thunk, writeMiddleware, effects));
|
createStore(rootReducer, applyMiddleware(thunk, plugins.middleware, thunk, writeMiddleware, effects));
|
||||||
|
|
||||||
export default configureStoreForProd;
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue