mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-12 20:18:41 -09:00
Migrate config to json
This commit is contained in:
parent
de9dd1433a
commit
bafa44845a
9 changed files with 725 additions and 133 deletions
69
app/config/config-default.json
Normal file
69
app/config/config-default.json
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"$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
|
||||
},
|
||||
"plugins": [],
|
||||
"localPlugins": [],
|
||||
"keymaps": {}
|
||||
}
|
||||
|
|
@ -1,9 +1,20 @@
|
|||
import {moveSync, copySync, existsSync, writeFileSync, readFileSync, lstatSync} from 'fs-extra';
|
||||
import {copySync, existsSync, writeFileSync, readFileSync, copy} from 'fs-extra';
|
||||
import {sync as mkdirpSync} from 'mkdirp';
|
||||
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
|
||||
import {
|
||||
defaultCfg,
|
||||
cfgPath,
|
||||
legacyCfgPath,
|
||||
plugs,
|
||||
defaultPlatformKeyPath,
|
||||
schemaPath,
|
||||
cfgDir,
|
||||
schemaFile
|
||||
} from './paths';
|
||||
import {_init, _extractDefault} from './init';
|
||||
import notify from '../notify';
|
||||
import {rawConfig} from '../../lib/config';
|
||||
import _ from 'lodash';
|
||||
import {resolve} from 'path';
|
||||
|
||||
let defaultConfig: rawConfig;
|
||||
|
||||
|
|
@ -18,62 +29,33 @@ const _write = (path: string, data: string) => {
|
|||
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
|
||||
// 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
|
||||
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
|
||||
const migrateHyper3Config = () => {
|
||||
copy(schemaPath, resolve(cfgDir, schemaFile), (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
if (existsSync(cfgPath)) {
|
||||
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;
|
||||
}
|
||||
if (!existsSync(legacyCfgPath)) {
|
||||
copySync(defaultCfg, cfgPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate
|
||||
copySync(legacyCfgPath, cfgPath);
|
||||
saveAsBackup(legacyCfgPath);
|
||||
const defaultCfgData = JSON.parse(readFileSync(defaultCfg, 'utf8'));
|
||||
const legacyCfgData = _extractDefault(readFileSync(legacyCfgPath, 'utf8'));
|
||||
const newCfgData = _.merge(defaultCfgData, legacyCfgData);
|
||||
_write(cfgPath, JSON.stringify(newCfgData, null, 2));
|
||||
|
||||
notify(
|
||||
'Hyper 3',
|
||||
`Settings location has changed to ${cfgPath}.\nWe've automatically migrated your existing config!\nPlease restart Hyper now`
|
||||
'Hyper 4',
|
||||
`Settings location and format has changed.\nWe've automatically migrated your existing config to ${cfgPath}`
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -83,18 +65,18 @@ const _importConf = () => {
|
|||
mkdirpSync(plugs.local);
|
||||
|
||||
try {
|
||||
migrateHyper2Config();
|
||||
migrateHyper3Config();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
let defaultCfgRaw = '';
|
||||
let defaultCfgRaw = '{}';
|
||||
try {
|
||||
defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
const _defaultCfg = _extractDefault(defaultCfgRaw) as rawConfig;
|
||||
const _defaultCfg = JSON.parse(defaultCfgRaw) as rawConfig;
|
||||
|
||||
// Importing platform specific keymap
|
||||
let content = '{}';
|
||||
|
|
@ -107,12 +89,12 @@ const _importConf = () => {
|
|||
_defaultCfg.keymaps = mapping;
|
||||
|
||||
// Import user config
|
||||
let userCfg: string;
|
||||
let userCfg: rawConfig;
|
||||
try {
|
||||
userCfg = readFileSync(cfgPath, 'utf8');
|
||||
userCfg = JSON.parse(readFileSync(cfgPath, 'utf8'));
|
||||
} catch (err) {
|
||||
_write(cfgPath, defaultCfgRaw);
|
||||
userCfg = defaultCfgRaw;
|
||||
userCfg = JSON.parse(defaultCfgRaw);
|
||||
}
|
||||
|
||||
return {userCfg, defaultCfg: _defaultCfg};
|
||||
|
|
@ -121,7 +103,7 @@ const _importConf = () => {
|
|||
export const _import = () => {
|
||||
const imported = _importConf();
|
||||
defaultConfig = imported.defaultCfg;
|
||||
const result = _init(imported);
|
||||
const result = _init(imported.userCfg, imported.defaultCfg);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import vm from 'vm';
|
|||
import notify from '../notify';
|
||||
import mapKeys from '../utils/map-keys';
|
||||
import {parsedConfig, rawConfig, configOptions} from '../../lib/config';
|
||||
import _ from 'lodash';
|
||||
|
||||
const _extract = (script?: vm.Script): Record<string, any> => {
|
||||
const module: Record<string, any> = {};
|
||||
|
|
@ -27,23 +28,21 @@ const _extractDefault = (cfg: string) => {
|
|||
};
|
||||
|
||||
// init config
|
||||
const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => {
|
||||
const script = _syntaxValidation(cfg.userCfg);
|
||||
const _cfg = script && (_extract(script) as rawConfig);
|
||||
const _init = (userCfg: rawConfig, defaultCfg: rawConfig): parsedConfig => {
|
||||
return {
|
||||
config: (() => {
|
||||
if (_cfg?.config) {
|
||||
return _cfg.config;
|
||||
if (userCfg?.config) {
|
||||
return _.merge(defaultCfg.config, userCfg.config);
|
||||
} else {
|
||||
notify('Error reading configuration: `config` key is missing');
|
||||
return cfg.defaultCfg.config || ({} as configOptions);
|
||||
return defaultCfg.config || ({} as configOptions);
|
||||
}
|
||||
})(),
|
||||
// Merging platform specific keymaps with user defined keymaps
|
||||
keymaps: mapKeys({...cfg.defaultCfg.keymaps, ..._cfg?.keymaps}),
|
||||
keymaps: mapKeys({...defaultCfg.keymaps, ...userCfg?.keymaps}),
|
||||
// Ignore undefined values in plugin and localPlugins array Issue #1862
|
||||
plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [],
|
||||
localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || []
|
||||
plugins: (userCfg?.plugins && userCfg.plugins.filter(Boolean)) || [],
|
||||
localPlugins: (userCfg?.localPlugins && userCfg.localPlugins.filter(Boolean)) || []
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,22 +5,30 @@ import {statSync} from 'fs';
|
|||
import {resolve, join} from 'path';
|
||||
import isDev from 'electron-is-dev';
|
||||
|
||||
const cfgFile = '.hyper.js';
|
||||
const defaultCfgFile = 'config-default.js';
|
||||
const cfgFile = 'hyper.json';
|
||||
const defaultCfgFile = 'config-default.json';
|
||||
const schemaFile = 'schema.json';
|
||||
const homeDirectory = homedir();
|
||||
|
||||
// If the user defines XDG_CONFIG_HOME they definitely want their config there,
|
||||
// otherwise use the home directory in linux/mac and userdata in windows
|
||||
const applicationDirectory =
|
||||
let cfgDir = process.env.XDG_CONFIG_HOME
|
||||
? 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
|
||||
? join(process.env.XDG_CONFIG_HOME, 'hyper')
|
||||
: process.platform == 'win32'
|
||||
? app.getPath('userData')
|
||||
: homedir();
|
||||
: homedir(),
|
||||
'.hyper.js'
|
||||
);
|
||||
|
||||
let cfgDir = applicationDirectory;
|
||||
let cfgPath = join(applicationDirectory, cfgFile);
|
||||
const legacyCfgPath = join(homeDirectory, cfgFile); // Hyper 2 config location
|
||||
let cfgPath = join(cfgDir, cfgFile);
|
||||
const schemaPath = resolve(__dirname, schemaFile);
|
||||
|
||||
const devDir = resolve(__dirname, '../..');
|
||||
const devCfg = join(devDir, cfgFile);
|
||||
|
|
@ -38,10 +46,8 @@ if (isDev) {
|
|||
}
|
||||
}
|
||||
|
||||
const plugins = resolve(cfgDir, '.hyper_plugins');
|
||||
const plugins = resolve(cfgDir, 'plugins');
|
||||
const plugs = {
|
||||
legacyBase: resolve(homeDirectory, '.hyper_plugins'),
|
||||
legacyLocal: resolve(homeDirectory, '.hyper_plugins', 'local'),
|
||||
base: plugins,
|
||||
local: resolve(plugins, 'local'),
|
||||
cache: resolve(plugins, 'cache')
|
||||
|
|
@ -82,5 +88,7 @@ export {
|
|||
yarn,
|
||||
cliScriptPath,
|
||||
cliLinkPath,
|
||||
homeDirectory
|
||||
homeDirectory,
|
||||
schemaFile,
|
||||
schemaPath
|
||||
};
|
||||
|
|
|
|||
427
app/config/schema.json
Normal file
427
app/config/schema.json
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"config": {
|
||||
"properties": {
|
||||
"autoUpdatePlugins": {
|
||||
"description": "if `true` (default), Hyper will update plugins every 5 hours\nyou can also set it to a custom time e.g. `1d` or `2h`",
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
},
|
||||
"backgroundColor": {
|
||||
"description": "terminal background color\n\nopacity is only supported on macOS",
|
||||
"type": "string"
|
||||
},
|
||||
"bell": {
|
||||
"description": "Supported Options:\n1. 'SOUND' -> Enables the bell as a sound\n2. false: turns off the bell",
|
||||
"type": "string"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"defaultSSHApp": {
|
||||
"description": "if `true` hyper will be set as the default protocol client for SSH",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableAutoUpdates": {
|
||||
"description": "if `true` hyper will not check for updates",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableLigatures": {
|
||||
"description": "if `false` Hyper will use ligatures provided by some fonts",
|
||||
"type": "boolean"
|
||||
},
|
||||
"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": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"100",
|
||||
"200",
|
||||
"300",
|
||||
"400",
|
||||
"500",
|
||||
"600",
|
||||
"700",
|
||||
"800",
|
||||
"900",
|
||||
"bold",
|
||||
"normal"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
],
|
||||
"description": "default font weight eg:'normal', '400', 'bold'"
|
||||
},
|
||||
"fontWeightBold": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"100",
|
||||
"200",
|
||||
"300",
|
||||
"400",
|
||||
"500",
|
||||
"600",
|
||||
"700",
|
||||
"800",
|
||||
"900",
|
||||
"bold",
|
||||
"normal"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
],
|
||||
"description": "font weight for bold characters eg:'normal', '600', 'bold'"
|
||||
},
|
||||
"foregroundColor": {
|
||||
"description": "color of the text",
|
||||
"type": "string"
|
||||
},
|
||||
"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 (i.e. /usr/local/bin/fish)\nif left empty, your system's login shell will be used by default\n\nWindows\n- Make sure to use a full path if the binary name doesn't work\n- Remove `--login` in shellArgs\n\nWindows Subsystem for Linux (WSL) - previously Bash on Windows\n- Example: `C:\\\\Windows\\\\System32\\\\wsl.exe`\n\nGit-bash on Windows\n- Example: `C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe`\n\nPowerShell on Windows\n- Example: `C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`\n\nCygwin\n- Example: `C:\\\\cygwin64\\\\bin\\\\bash.exe`\n\nGit Bash\n- Example: `C:\\\\Program Files\\\\Git\\\\git-cmd.exe`\nThen Add `--command=usr/bin/bash.exe` to shellArgs",
|
||||
"type": "string"
|
||||
},
|
||||
"shellArgs": {
|
||||
"description": "for setting shell arguments (i.e. 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"
|
||||
},
|
||||
"updateChannel": {
|
||||
"description": "choose either `'stable'` for receiving highly polished, or `'canary'` for less polished but more frequent updates",
|
||||
"enum": [
|
||||
"canary",
|
||||
"stable"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"useConpty": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"webGLRenderer": {
|
||||
"description": "Whether to use the WebGL renderer. Set it to false to use canvas-based\nrendering (slower, but supports transparent backgrounds)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"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": [
|
||||
"autoUpdatePlugins",
|
||||
"backgroundColor",
|
||||
"bell",
|
||||
"bellSound",
|
||||
"bellSoundURL",
|
||||
"borderColor",
|
||||
"colors",
|
||||
"copyOnSelect",
|
||||
"css",
|
||||
"cursorAccentColor",
|
||||
"cursorBlink",
|
||||
"cursorColor",
|
||||
"cursorShape",
|
||||
"defaultSSHApp",
|
||||
"disableAutoUpdates",
|
||||
"disableLigatures",
|
||||
"env",
|
||||
"fontFamily",
|
||||
"fontSize",
|
||||
"fontWeight",
|
||||
"fontWeightBold",
|
||||
"foregroundColor",
|
||||
"letterSpacing",
|
||||
"lineHeight",
|
||||
"macOptionSelectionMode",
|
||||
"padding",
|
||||
"preserveCWD",
|
||||
"quickEdit",
|
||||
"screenReaderMode",
|
||||
"scrollback",
|
||||
"selectionColor",
|
||||
"shell",
|
||||
"shellArgs",
|
||||
"showHamburgerMenu",
|
||||
"showWindowControls",
|
||||
"termCSS",
|
||||
"updateChannel",
|
||||
"webGLRenderer",
|
||||
"webLinksActivationKey",
|
||||
"workingDirectory"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ import {decorateSessionOptions, decorateSessionClass} from '../plugins';
|
|||
import {enable as remoteEnable} from '@electron/remote/main';
|
||||
import {configOptions} from '../../lib/config';
|
||||
import {getWorkingDirectoryFromPID} from 'native-process-working-directory';
|
||||
import {existsSync} from 'fs';
|
||||
|
||||
export function newWindow(
|
||||
options_: BrowserWindowConstructorOptions,
|
||||
|
|
@ -138,7 +139,7 @@ export function newWindow(
|
|||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
cwd = cwd && isAbsolute(cwd) ? cwd : '';
|
||||
cwd = cwd && isAbsolute(cwd) && existsSync(cwd) ? cwd : '';
|
||||
}
|
||||
|
||||
// remove the rows and cols, the wrong value of them will break layout when init create
|
||||
|
|
|
|||
68
cli/api.ts
68
cli/api.ts
|
|
@ -5,25 +5,22 @@ import os from 'os';
|
|||
import got from 'got';
|
||||
import registryUrlModule from 'registry-url';
|
||||
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,
|
||||
// otherwise use the home directory in linux/mac and userdata in windows
|
||||
const applicationDirectory =
|
||||
process.env.XDG_CONFIG_HOME !== undefined
|
||||
? path.join(process.env.XDG_CONFIG_HOME, 'hyper')
|
||||
: process.platform == 'win32'
|
||||
? path.join(process.env.APPDATA!, 'Hyper')
|
||||
: os.homedir();
|
||||
const applicationDirectory = process.env.XDG_CONFIG_HOME
|
||||
? path.join(process.env.XDG_CONFIG_HOME, 'Hyper')
|
||||
: process.platform === 'win32'
|
||||
? path.join(process.env.APPDATA!, 'Hyper')
|
||||
: path.join(os.homedir(), '.config', 'Hyper');
|
||||
|
||||
const devConfigFileName = path.join(__dirname, `../.hyper.js`);
|
||||
const devConfigFileName = path.join(__dirname, `../hyper.json`);
|
||||
|
||||
const fileName =
|
||||
process.env.NODE_ENV !== 'production' && fs.existsSync(devConfigFileName)
|
||||
? devConfigFileName
|
||||
: path.join(applicationDirectory, '.hyper.js');
|
||||
: path.join(applicationDirectory, 'hyper.json');
|
||||
|
||||
/**
|
||||
* We need to make sure the file reading and parsing is lazy so that failure to
|
||||
|
|
@ -43,34 +40,12 @@ function memoize<T extends (...args: any[]) => any>(fn: T): T {
|
|||
}
|
||||
|
||||
const getFileContents = memoize(() => {
|
||||
try {
|
||||
return fs.readFileSync(fileName, 'utf8');
|
||||
} catch (_err) {
|
||||
const err = _err as {code: string};
|
||||
if (err.code !== 'ENOENT') {
|
||||
// ENOENT === !exists()
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return fs.readFileSync(fileName, 'utf8');
|
||||
});
|
||||
|
||||
const getParsedFile = memoize(() => recast.parse(getFileContents()!));
|
||||
const getParsedFile = memoize(() => JSON.parse(getFileContents()));
|
||||
|
||||
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 getPluginsByKey = (key: string): any[] => getParsedFile()[key] || [];
|
||||
|
||||
const getPlugins = memoize(() => {
|
||||
return getPluginsByKey('plugins');
|
||||
|
|
@ -87,13 +62,13 @@ function exists() {
|
|||
function isInstalled(plugin: string, locally?: boolean) {
|
||||
const array = locally ? getLocalPlugins() : getPlugins();
|
||||
if (array && Array.isArray(array)) {
|
||||
return array.some((entry) => entry.value === plugin);
|
||||
return array.includes(plugin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function save() {
|
||||
return pify(fs.writeFile)(fileName, recast.print(getParsedFile()).code, 'utf8');
|
||||
function save(config: any) {
|
||||
return fs.writeFileSync(fileName, JSON.stringify(config, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
function getPackageName(plugin: string) {
|
||||
|
|
@ -135,26 +110,25 @@ function install(plugin: string, locally?: boolean) {
|
|||
return Promise.reject(`${plugin} is already installed`);
|
||||
}
|
||||
|
||||
array.push(recast.types.builders.literal(plugin));
|
||||
return save();
|
||||
const config = getParsedFile();
|
||||
config[locally ? 'localPlugins' : 'plugins'] = [...array, plugin];
|
||||
save(config);
|
||||
});
|
||||
}
|
||||
|
||||
function uninstall(plugin: string) {
|
||||
async function uninstall(plugin: string) {
|
||||
if (!isInstalled(plugin)) {
|
||||
return Promise.reject(`${plugin} is not installed`);
|
||||
}
|
||||
|
||||
const index = getPlugins().findIndex((entry) => entry.value === plugin);
|
||||
getPlugins().splice(index, 1);
|
||||
return save();
|
||||
const config = getParsedFile();
|
||||
config.plugins = getPlugins().filter((p) => p !== plugin);
|
||||
save(config);
|
||||
}
|
||||
|
||||
function list() {
|
||||
if (getPlugins().length > 0) {
|
||||
return getPlugins()
|
||||
.map((plugin) => plugin.value)
|
||||
.join('\n');
|
||||
return getPlugins().join('\n');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
148
lib/config.d.ts
vendored
148
lib/config.d.ts
vendored
|
|
@ -20,60 +20,192 @@ export type ColorMap = {
|
|||
};
|
||||
|
||||
export type configOptions = {
|
||||
/**
|
||||
* if `true` (default), Hyper will update plugins every 5 hours
|
||||
* you can also set it to a custom time e.g. `1d` or `2h`
|
||||
*/
|
||||
autoUpdatePlugins: boolean | string;
|
||||
/**
|
||||
* terminal background color
|
||||
*
|
||||
* opacity is only supported on macOS
|
||||
*/
|
||||
backgroundColor: string;
|
||||
/**
|
||||
* Supported Options:
|
||||
* 1. 'SOUND' -> Enables the bell as a sound
|
||||
* 2. false: turns off the bell
|
||||
*/
|
||||
bell: string;
|
||||
/**
|
||||
* base64 encoded string of the sound file to use for the bell
|
||||
* if null, the default bell will be used
|
||||
* @nullable
|
||||
*/
|
||||
bellSound: string | null;
|
||||
/**
|
||||
* An absolute file path to a sound file on the machine.
|
||||
* @nullable
|
||||
*/
|
||||
bellSoundURL: string | null;
|
||||
/** border color (window, tabs) */
|
||||
borderColor: string;
|
||||
/**
|
||||
* 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: ColorMap;
|
||||
/** if `true` selected text will automatically be copied to the clipboard */
|
||||
copyOnSelect: boolean;
|
||||
/** custom CSS to embed in the main window */
|
||||
css: string;
|
||||
/** terminal text color under BLOCK cursor */
|
||||
cursorAccentColor: string;
|
||||
/** set to `true` for blinking cursor */
|
||||
cursorBlink: boolean;
|
||||
/** terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk) */
|
||||
cursorColor: string;
|
||||
/** `'BEAM'` for |, `'UNDERLINE'` for _, `'BLOCK'` for █ */
|
||||
cursorShape: 'BEAM' | 'UNDERLINE' | 'BLOCK';
|
||||
/** if `true` hyper will be set as the default protocol client for SSH */
|
||||
defaultSSHApp: boolean;
|
||||
/** if `true` hyper will not check for updates */
|
||||
disableAutoUpdates: boolean;
|
||||
/** if `false` Hyper will use ligatures provided by some fonts */
|
||||
disableLigatures: boolean;
|
||||
env: Record<string, string>;
|
||||
/** for environment variables */
|
||||
env: {[k: string]: string};
|
||||
/** font family with optional fallbacks */
|
||||
fontFamily: string;
|
||||
/** default font size in pixels for all tabs */
|
||||
fontSize: number;
|
||||
/** default font weight eg:'normal', '400', 'bold' */
|
||||
fontWeight: FontWeight;
|
||||
/** font weight for bold characters eg:'normal', '600', 'bold' */
|
||||
fontWeightBold: FontWeight;
|
||||
/** color of the text */
|
||||
foregroundColor: string;
|
||||
/** letter spacing as a relative unit */
|
||||
letterSpacing: number;
|
||||
/** line height as a relative unit */
|
||||
lineHeight: number;
|
||||
/**
|
||||
* 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: string;
|
||||
modifierKeys: {
|
||||
modifierKeys?: {
|
||||
altIsMeta: boolean;
|
||||
cmdIsMeta: boolean;
|
||||
};
|
||||
/** custom padding (CSS format, i.e.: `top right bottom left` or `top horizontal bottom` or `vertical horizontal` or `all`) */
|
||||
padding: string;
|
||||
/**
|
||||
* set to true to preserve working directory when creating splits or tabs
|
||||
*/
|
||||
preserveCWD: boolean;
|
||||
/**
|
||||
* if `true` 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: boolean;
|
||||
/**
|
||||
* set to true to enable screen reading apps (like NVDA) to read the contents of the terminal
|
||||
*/
|
||||
screenReaderMode: boolean;
|
||||
scrollback: number;
|
||||
/** terminal selection color */
|
||||
selectionColor: string;
|
||||
/**
|
||||
* 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`
|
||||
*
|
||||
* Git Bash
|
||||
* - Example: `C:\\Program Files\\Git\\git-cmd.exe`
|
||||
* Then Add `--command=usr/bin/bash.exe` to shellArgs
|
||||
*/
|
||||
shell: string;
|
||||
/**
|
||||
* for setting shell arguments (i.e. for using interactive shellArgs: `['-i']`)
|
||||
* by default `['--login']` will be used
|
||||
*/
|
||||
shellArgs: string[];
|
||||
/**
|
||||
* 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: boolean | '';
|
||||
showWindowControls: string;
|
||||
/**
|
||||
* set to `false` 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` on Windows and Linux, ignored on macOS
|
||||
*/
|
||||
showWindowControls: boolean | 'left' | '';
|
||||
/** custom CSS to embed in the terminal window */
|
||||
termCSS: string;
|
||||
uiFontFamily: string;
|
||||
uiFontFamily?: string;
|
||||
/** choose either `'stable'` for receiving highly polished, or `'canary'` for less polished but more frequent updates */
|
||||
updateChannel: 'stable' | 'canary';
|
||||
useConpty: boolean;
|
||||
useConpty?: boolean;
|
||||
/**
|
||||
* Whether to use the WebGL renderer. Set it to false to use canvas-based
|
||||
* rendering (slower, but supports transparent backgrounds)
|
||||
*/
|
||||
webGLRenderer: boolean;
|
||||
webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift';
|
||||
windowSize: [number, number];
|
||||
// TODO: does not pick up config changes automatically, need to restart terminal
|
||||
/**
|
||||
* keypress required for weblink activation: [ctrl | alt | meta | shift]
|
||||
*/
|
||||
webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift' | '';
|
||||
/** Initial window size in pixels */
|
||||
windowSize?: [number, number];
|
||||
/** set custom startup directory (must be an absolute path) */
|
||||
workingDirectory: string;
|
||||
};
|
||||
|
||||
export type rawConfig = {
|
||||
config?: configOptions;
|
||||
/**
|
||||
* a list of plugins to fetch and install from npm
|
||||
* format: [@org/]project[#version]
|
||||
* examples:
|
||||
* `hyperpower`
|
||||
* `@company/project`
|
||||
* `project#1.0.1`
|
||||
*/
|
||||
plugins?: string[];
|
||||
/**
|
||||
* in development, you can create a directory under
|
||||
* `plugins/local/` and include it here
|
||||
* to load it and avoid it being `npm install`ed
|
||||
*/
|
||||
localPlugins?: string[];
|
||||
keymaps?: Record<string, string | string[]>;
|
||||
/**
|
||||
* Example
|
||||
* 'window:devtools': 'cmd+alt+o',
|
||||
*/
|
||||
keymaps?: {[k: string]: string | string[]};
|
||||
};
|
||||
|
||||
export type parsedConfig = {
|
||||
|
|
|
|||
2
lib/hyper.d.ts
vendored
2
lib/hyper.d.ts
vendored
|
|
@ -94,7 +94,7 @@ export type uiState = Immutable<{
|
|||
scrollback: number;
|
||||
selectionColor: string;
|
||||
showHamburgerMenu: boolean | '';
|
||||
showWindowControls: string;
|
||||
showWindowControls: boolean | 'left' | '';
|
||||
termCSS: string;
|
||||
uiFontFamily: string;
|
||||
updateCanInstall: null | boolean;
|
||||
|
|
|
|||
Loading…
Reference in a new issue