mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-13 04:28:41 -09:00
add config types
This commit is contained in:
parent
b099bb1218
commit
6debd1e7f2
11 changed files with 147 additions and 58 deletions
|
|
@ -5,12 +5,13 @@ import _openConfig from './config/open';
|
||||||
import win from './config/windows';
|
import win from './config/windows';
|
||||||
import {cfgPath, cfgDir} from './config/paths';
|
import {cfgPath, cfgDir} from './config/paths';
|
||||||
import {getColorMap} from './utils/colors';
|
import {getColorMap} from './utils/colors';
|
||||||
|
import {parsedConfig, configOptions} from '../lib/config';
|
||||||
|
|
||||||
const watchers: any[] = [];
|
const watchers: any[] = [];
|
||||||
let cfg: Record<string, any> = {};
|
let cfg: parsedConfig = {} as any;
|
||||||
let _watcher: fs.FSWatcher;
|
let _watcher: fs.FSWatcher;
|
||||||
|
|
||||||
export const getDeprecatedCSS = (config: Record<string, any>) => {
|
export const getDeprecatedCSS = (config: configOptions) => {
|
||||||
const deprecated: string[] = [];
|
const deprecated: string[] = [];
|
||||||
const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection'];
|
const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection'];
|
||||||
deprecatedCSS.forEach((css) => {
|
deprecatedCSS.forEach((css) => {
|
||||||
|
|
@ -124,15 +125,15 @@ export const getWin = win.get;
|
||||||
export const winRecord = win.recordState;
|
export const winRecord = win.recordState;
|
||||||
export const windowDefaults = win.defaults;
|
export const windowDefaults = win.defaults;
|
||||||
|
|
||||||
export const fixConfigDefaults = (decoratedConfig: any) => {
|
export const fixConfigDefaults = (decoratedConfig: configOptions) => {
|
||||||
const defaultConfig = getDefaultConfig()?.config;
|
const defaultConfig = getDefaultConfig().config!;
|
||||||
decoratedConfig.colors = getColorMap(decoratedConfig.colors) || {};
|
decoratedConfig.colors = getColorMap(decoratedConfig.colors) || {};
|
||||||
// We must have default colors for xterm css.
|
// We must have default colors for xterm css.
|
||||||
decoratedConfig.colors = Object.assign({}, defaultConfig.colors, decoratedConfig.colors);
|
decoratedConfig.colors = {...defaultConfig.colors, ...decoratedConfig.colors};
|
||||||
return decoratedConfig;
|
return decoratedConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const htermConfigTranslate = (config: Record<string, any>) => {
|
export const htermConfigTranslate = (config: configOptions) => {
|
||||||
const cssReplacements: Record<string, string> = {
|
const cssReplacements: Record<string, string> = {
|
||||||
'x-screen x-row([ {.[])': '.xterm-rows > div$1',
|
'x-screen x-row([ {.[])': '.xterm-rows > div$1',
|
||||||
'.cursor-node([ {.[])': '.terminal-cursor$1',
|
'.cursor-node([ {.[])': '.terminal-cursor$1',
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ import {sync as mkdirpSync} from 'mkdirp';
|
||||||
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
|
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
|
||||||
import {_init, _extractDefault} from './init';
|
import {_init, _extractDefault} from './init';
|
||||||
import notify from '../notify';
|
import notify from '../notify';
|
||||||
|
import {rawConfig} from '../../lib/config';
|
||||||
|
|
||||||
let defaultConfig: Record<string, any> | undefined;
|
let defaultConfig: rawConfig;
|
||||||
|
|
||||||
const _write = (path: string, data: any) => {
|
const _write = (path: string, data: any) => {
|
||||||
// This method will take text formatted as Unix line endings and transform it
|
// This method will take text formatted as Unix line endings and transform it
|
||||||
|
|
@ -92,41 +93,46 @@ const _importConf = () => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultCfgRaw = '';
|
||||||
try {
|
try {
|
||||||
const defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
|
defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
|
||||||
const _defaultCfg = _extractDefault(defaultCfgRaw);
|
|
||||||
// Importing platform specific keymap
|
|
||||||
try {
|
|
||||||
const content = readFileSync(defaultPlatformKeyPath(), 'utf8');
|
|
||||||
const mapping = JSON.parse(content) as Record<string, string | string[]>;
|
|
||||||
_defaultCfg.keymaps = mapping;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import user config
|
|
||||||
try {
|
|
||||||
const userCfg = readFileSync(cfgPath, 'utf8');
|
|
||||||
return {userCfg, defaultCfg: _defaultCfg};
|
|
||||||
} catch (err) {
|
|
||||||
_write(cfgPath, defaultCfgRaw);
|
|
||||||
return {userCfg: defaultCfgRaw, defaultCfg: _defaultCfg};
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
const _defaultCfg = _extractDefault(defaultCfgRaw) as rawConfig;
|
||||||
|
|
||||||
|
// Importing platform specific keymap
|
||||||
|
let content = '{}';
|
||||||
|
try {
|
||||||
|
content = readFileSync(defaultPlatformKeyPath(), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
const mapping = JSON.parse(content) as Record<string, string | string[]>;
|
||||||
|
_defaultCfg.keymaps = mapping;
|
||||||
|
|
||||||
|
// Import user config
|
||||||
|
let userCfg: string;
|
||||||
|
try {
|
||||||
|
userCfg = readFileSync(cfgPath, 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
_write(cfgPath, defaultCfgRaw);
|
||||||
|
userCfg = defaultCfgRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {userCfg, defaultCfg: _defaultCfg};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const _import = () => {
|
export const _import = () => {
|
||||||
const imported = _importConf();
|
const imported = _importConf();
|
||||||
defaultConfig = imported?.defaultCfg;
|
defaultConfig = imported.defaultCfg;
|
||||||
const result = _init(imported!);
|
const result = _init(imported!);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDefaultConfig = () => {
|
export const getDefaultConfig = () => {
|
||||||
if (!defaultConfig) {
|
if (!defaultConfig) {
|
||||||
defaultConfig = _importConf()?.defaultCfg;
|
defaultConfig = _importConf().defaultCfg;
|
||||||
}
|
}
|
||||||
return defaultConfig;
|
return defaultConfig;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import vm from 'vm';
|
import vm from 'vm';
|
||||||
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> = {};
|
||||||
|
|
@ -24,22 +25,24 @@ const _extractDefault = (cfg: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// init config
|
// init config
|
||||||
const _init = (cfg: {userCfg: string; defaultCfg: Record<string, any>}) => {
|
const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => {
|
||||||
const script = _syntaxValidation(cfg.userCfg);
|
const script = _syntaxValidation(cfg.userCfg);
|
||||||
if (script) {
|
const _cfg = script && (_extract(script) as rawConfig);
|
||||||
const _cfg = _extract(script);
|
return {
|
||||||
if (!_cfg.config) {
|
config: (() => {
|
||||||
notify('Error reading configuration: `config` key is missing');
|
if (_cfg?.config) {
|
||||||
return cfg.defaultCfg;
|
return _cfg.config;
|
||||||
}
|
} else {
|
||||||
|
notify('Error reading configuration: `config` key is missing');
|
||||||
|
return cfg.defaultCfg.config || ({} as configOptions);
|
||||||
|
}
|
||||||
|
})(),
|
||||||
// Merging platform specific keymaps with user defined keymaps
|
// Merging platform specific keymaps with user defined keymaps
|
||||||
_cfg.keymaps = mapKeys(Object.assign({}, cfg.defaultCfg.keymaps, _cfg.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
|
||||||
_cfg.plugins = (_cfg.plugins && _cfg.plugins.filter(Boolean)) || [];
|
plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [],
|
||||||
_cfg.localPlugins = (_cfg.localPlugins && _cfg.localPlugins.filter(Boolean)) || [];
|
localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || []
|
||||||
return _cfg;
|
};
|
||||||
}
|
|
||||||
return cfg.defaultCfg;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {_init, _extractDefault};
|
export {_init, _extractDefault};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {availableExtensions} from './plugins/extensions';
|
||||||
import {install} from './plugins/install';
|
import {install} from './plugins/install';
|
||||||
import {plugs} from './config/paths';
|
import {plugs} from './config/paths';
|
||||||
import mapKeys from './utils/map-keys';
|
import mapKeys from './utils/map-keys';
|
||||||
|
import {configOptions} from '../lib/config';
|
||||||
|
|
||||||
// local storage
|
// local storage
|
||||||
const cache = new Config();
|
const cache = new Config();
|
||||||
|
|
@ -187,10 +188,7 @@ if (cache.get('hyper.plugins') !== id || process.env.HYPER_FORCE_UPDATE) {
|
||||||
const baseConfig = config.getConfig();
|
const baseConfig = config.getConfig();
|
||||||
if (baseConfig['autoUpdatePlugins']) {
|
if (baseConfig['autoUpdatePlugins']) {
|
||||||
// otherwise update plugins every 5 hours
|
// otherwise update plugins every 5 hours
|
||||||
setInterval(
|
setInterval(updatePlugins, ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : baseConfig['autoUpdatePlugins']));
|
||||||
updatePlugins,
|
|
||||||
ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : (baseConfig['autoUpdatePlugins'] as string))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
@ -372,7 +370,7 @@ function decorateEntity(base: any, key: string, type: 'object' | 'function') {
|
||||||
return decorated;
|
return decorated;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decorateObject(base: any, key: string) {
|
function decorateObject<T>(base: T, key: string): T {
|
||||||
return decorateEntity(base, key, 'object');
|
return decorateEntity(base, key, 'object');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -381,14 +379,14 @@ function decorateClass(base: any, key: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDeprecatedConfig = () => {
|
export const getDeprecatedConfig = () => {
|
||||||
const deprecated: Record<string, any> = {};
|
const deprecated: Record<string, {css: string[]}> = {};
|
||||||
const baseConfig = config.getConfig();
|
const baseConfig = config.getConfig();
|
||||||
modules.forEach((plugin) => {
|
modules.forEach((plugin) => {
|
||||||
if (!plugin.decorateConfig) {
|
if (!plugin.decorateConfig) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We need to clone config in case of plugin modifies config directly.
|
// We need to clone config in case of plugin modifies config directly.
|
||||||
let configTmp;
|
let configTmp: configOptions;
|
||||||
try {
|
try {
|
||||||
configTmp = plugin.decorateConfig(JSON.parse(JSON.stringify(baseConfig)));
|
configTmp = plugin.decorateConfig(JSON.parse(JSON.stringify(baseConfig)));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
|
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
|
||||||
import {HyperActions} from '../hyper';
|
import {HyperActions} from '../hyper';
|
||||||
|
import {configOptions} from '../config';
|
||||||
|
|
||||||
export function loadConfig(config: any): HyperActions {
|
export function loadConfig(config: configOptions): HyperActions {
|
||||||
return {
|
return {
|
||||||
type: CONFIG_LOAD,
|
type: CONFIG_LOAD,
|
||||||
config
|
config
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reloadConfig(config: any): HyperActions {
|
export function reloadConfig(config: configOptions): HyperActions {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
return {
|
return {
|
||||||
type: CONFIG_RELOAD,
|
type: CONFIG_RELOAD,
|
||||||
|
|
|
||||||
77
lib/config.d.ts
vendored
Normal file
77
lib/config.d.ts
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import {FontWeight} from 'xterm';
|
||||||
|
|
||||||
|
export type configOptions = {
|
||||||
|
autoUpdatePlugins: boolean | string;
|
||||||
|
backgroundColor: string;
|
||||||
|
bell: string;
|
||||||
|
bellSound: string | null;
|
||||||
|
bellSoundURL: string | null;
|
||||||
|
borderColor: string;
|
||||||
|
colors: {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
copyOnSelect: boolean;
|
||||||
|
css: string;
|
||||||
|
cursorAccentColor: string;
|
||||||
|
cursorBlink: boolean;
|
||||||
|
cursorColor: string;
|
||||||
|
cursorShape: 'BEAM' | 'UNDERLINE' | 'BLOCK';
|
||||||
|
defaultSSHApp: 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;
|
||||||
|
quickEdit: boolean;
|
||||||
|
scrollback: number;
|
||||||
|
selectionColor: string;
|
||||||
|
shell: string;
|
||||||
|
shellArgs: string[];
|
||||||
|
showHamburgerMenu: boolean | '';
|
||||||
|
showWindowControls: string;
|
||||||
|
termCSS: string;
|
||||||
|
uiFontFamily: string;
|
||||||
|
updateChannel: 'stable' | 'canary';
|
||||||
|
useConpty: boolean;
|
||||||
|
webGLRenderer: boolean;
|
||||||
|
windowSize: [number, number];
|
||||||
|
};
|
||||||
|
|
||||||
|
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,15 +1,17 @@
|
||||||
|
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';
|
||||||
|
|
||||||
export interface ConfigLoadAction {
|
export interface ConfigLoadAction {
|
||||||
type: typeof CONFIG_LOAD;
|
type: typeof CONFIG_LOAD;
|
||||||
config: any;
|
config: configOptions;
|
||||||
now?: number;
|
now?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigReloadAction {
|
export interface ConfigReloadAction {
|
||||||
type: typeof CONFIG_RELOAD;
|
type: typeof CONFIG_RELOAD;
|
||||||
config: any;
|
config: configOptions;
|
||||||
now: number;
|
now: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
lib/hyper.d.ts
vendored
2
lib/hyper.d.ts
vendored
|
|
@ -96,7 +96,7 @@ export type uiState = {
|
||||||
rows: number | null;
|
rows: number | null;
|
||||||
scrollback: number;
|
scrollback: number;
|
||||||
selectionColor: string;
|
selectionColor: string;
|
||||||
showHamburgerMenu: string;
|
showHamburgerMenu: boolean | '';
|
||||||
showWindowControls: string;
|
showWindowControls: string;
|
||||||
termCSS: string;
|
termCSS: string;
|
||||||
uiFontFamily: string;
|
uiFontFamily: string;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {addNotificationMessage} from './actions/notifications';
|
||||||
import {loadConfig, reloadConfig} from './actions/config';
|
import {loadConfig, reloadConfig} from './actions/config';
|
||||||
import HyperContainer from './containers/hyper';
|
import HyperContainer from './containers/hyper';
|
||||||
import configureStore from './store/configure-store';
|
import configureStore from './store/configure-store';
|
||||||
|
import {configOptions} from './config';
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -31,8 +32,8 @@ Object.defineProperty(window, 'rpc', {get: () => rpc});
|
||||||
Object.defineProperty(window, 'config', {get: () => config});
|
Object.defineProperty(window, 'config', {get: () => config});
|
||||||
Object.defineProperty(window, 'plugins', {get: () => plugins});
|
Object.defineProperty(window, 'plugins', {get: () => plugins});
|
||||||
|
|
||||||
const fetchFileData = (configData: any) => {
|
const fetchFileData = (configData: configOptions) => {
|
||||||
const configInfo = Object.assign({}, configData, {bellSound: null});
|
const configInfo: configOptions = {...configData, bellSound: null};
|
||||||
if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) {
|
if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) {
|
||||||
store_.dispatch(reloadConfig(configInfo));
|
store_.dispatch(reloadConfig(configInfo));
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ const reducer = (state = initial, action: HyperActions) => {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -363,10 +363,10 @@ const loadModules = () => {
|
||||||
})
|
})
|
||||||
.filter((mod: any) => Boolean(mod));
|
.filter((mod: any) => Boolean(mod));
|
||||||
|
|
||||||
const deprecatedPlugins: Record<string, any> = plugins.getDeprecatedConfig();
|
const deprecatedPlugins = plugins.getDeprecatedConfig();
|
||||||
Object.keys(deprecatedPlugins).forEach((name) => {
|
Object.keys(deprecatedPlugins).forEach((name) => {
|
||||||
const {css} = deprecatedPlugins[name];
|
const {css} = deprecatedPlugins[name];
|
||||||
if (css) {
|
if (css.length > 0) {
|
||||||
console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`);
|
console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue