hyper/lib/reducers/ui.js

337 lines
8.9 KiB
JavaScript
Raw Normal View History

2016-07-13 12:44:24 -08:00
import Immutable from 'seamless-immutable';
import {decorateUIReducer} from '../utils/plugins';
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
import {
UI_FONT_SIZE_SET,
UI_FONT_SIZE_RESET,
UI_FONT_SMOOTHING_SET,
UI_WINDOW_MAXIMIZE,
UI_WINDOW_UNMAXIMIZE
} from '../constants/ui';
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications';
2016-07-13 12:44:24 -08:00
import {
SESSION_ADD,
SESSION_RESIZE,
SESSION_PTY_DATA,
SESSION_PTY_EXIT,
2016-07-19 09:45:28 -08:00
SESSION_SET_ACTIVE,
SESSION_SET_CWD
2016-07-13 12:44:24 -08:00
} from '../constants/sessions';
import {UPDATE_AVAILABLE} from '../constants/updater';
import {values} from '../utils/object';
2016-07-13 12:44:24 -08:00
const allowedCursorShapes = new Set(['BEAM', 'BLOCK', 'UNDERLINE']);
const allowedBells = new Set(['SOUND', false]);
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
const allowedHamburgerMenuValues = new Set([true, false]);
const allowedWindowControlsValues = new Set([true, false, 'left']);
// Populate `config-default.js` from this :)
2016-07-13 12:44:24 -08:00
const initial = Immutable({
cols: null,
rows: null,
activeUid: null,
cursorColor: '#F81CE5',
cursorShape: 'BLOCK',
2016-07-13 12:44:24 -08:00
borderColor: '#333',
fontSize: 12,
padding: '12px 14px',
fontFamily: 'Menlo, "DejaVu Sans Mono", "Lucida Console", monospace',
fontSizeOverride: null,
fontSmoothingOverride: 'antialiased',
2016-07-13 12:44:24 -08:00
css: '',
termCSS: '',
openAt: {},
resizeAt: 0,
colors: {
black: '#000000',
red: '#ff0000',
green: '#33ff00',
yellow: '#ffff00',
blue: '#0066ff',
magenta: '#cc00ff',
cyan: '#00ffff',
white: '#d0d0d0',
lightBlack: '#808080',
lightRed: '#ff0000',
lightGreen: '#33ff00',
lightYellow: '#ffff00',
lightBlue: '#0066ff',
lightMagenta: '#cc00ff',
lightCyan: '#00ffff',
lightWhite: '#ffffff'
},
2016-07-13 12:44:24 -08:00
activityMarkers: {},
notifications: {
font: false,
resize: false,
updates: false,
message: false
2016-07-13 12:44:24 -08:00
},
foregroundColor: '#fff',
backgroundColor: '#000',
maximized: false,
2016-07-13 12:44:24 -08:00
updateVersion: null,
updateNotes: null,
messageText: null,
messageURL: null,
messageDismissable: null,
bell: 'SOUND',
bellSoundURL: 'lib-resource:hterm/audio/bell',
copyOnSelect: false,
modifierKeys: {
altIsMeta: false,
cmdIsMeta: false
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
},
showHamburgerMenu: '',
showWindowControls: ''
2016-07-13 12:44:24 -08:00
});
const reducer = (state = initial, action) => {
let state_ = state;
switch (action.type) { // eslint-disable-line default-case
2016-07-13 12:44:24 -08:00
case CONFIG_LOAD:
case CONFIG_RELOAD: // eslint-disable-line no-case-declarations
const {config} = action;
2016-07-13 12:44:24 -08:00
state_ = state
// unset the user font size override if the
2016-07-29 12:40:46 -08:00
// font size changed from the config
.merge((() => {
const ret = {};
2016-07-13 12:44:24 -08:00
2016-07-29 12:40:46 -08:00
if (state.fontSizeOverride && config.fontSize !== state.fontSize) {
ret.fontSizeOverride = null;
}
2016-07-13 12:44:24 -08:00
if (config.fontSize) {
2016-07-29 12:40:46 -08:00
ret.fontSize = config.fontSize;
}
2016-07-13 12:44:24 -08:00
if (config.fontFamily) {
2016-07-29 12:40:46 -08:00
ret.fontFamily = config.fontFamily;
}
2016-07-13 12:44:24 -08:00
if (config.cursorColor) {
2016-07-29 12:40:46 -08:00
ret.cursorColor = config.cursorColor;
}
if (allowedCursorShapes.has(config.cursorShape)) {
2016-07-29 12:40:46 -08:00
ret.cursorShape = config.cursorShape;
}
2016-07-13 12:44:24 -08:00
if (config.borderColor) {
2016-07-29 12:40:46 -08:00
ret.borderColor = config.borderColor;
}
2016-07-13 12:44:24 -08:00
if (typeof (config.padding) !== 'undefined' &&
config.padding !== null) {
2016-07-29 12:40:46 -08:00
ret.padding = config.padding;
}
2016-07-13 12:44:24 -08:00
if (config.foregroundColor) {
2016-07-29 12:40:46 -08:00
ret.foregroundColor = config.foregroundColor;
}
2016-07-13 12:44:24 -08:00
if (config.backgroundColor) {
2016-07-29 12:40:46 -08:00
ret.backgroundColor = config.backgroundColor;
}
2016-07-13 12:44:24 -08:00
if (config.css) {
2016-07-29 12:40:46 -08:00
ret.css = config.css;
}
2016-07-13 12:44:24 -08:00
if (config.termCSS) {
2016-07-29 12:40:46 -08:00
ret.termCSS = config.termCSS;
}
if (allowedBells.has(config.bell)) {
ret.bell = config.bell;
}
if (config.bellSoundURL) {
ret.bellSoundURL = config.bellSoundURL || initial.bellSoundURL;
}
if (typeof (config.copyOnSelect) !== 'undefined' &&
config.copyOnSelect !== null) {
ret.copyOnSelect = config.copyOnSelect;
}
if (config.colors) {
2016-07-29 12:40:46 -08:00
if (Array.isArray(config.colors)) {
const stateColors = Array.isArray(state.colors) ?
state.colors :
values(state.colors);
2016-07-29 12:40:46 -08:00
if (stateColors.toString() !== config.colors.toString()) {
ret.colors = config.colors;
}
} else if (JSON.stringify(state.colors) !== JSON.stringify(config.colors)) {
ret.colors = config.colors;
}
2016-07-13 12:44:24 -08:00
}
if (config.modifierKeys) {
ret.modifierKeys = config.modifierKeys;
}
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) {
ret.showHamburgerMenu = config.showHamburgerMenu;
}
if (allowedWindowControlsValues.has(config.showWindowControls)) {
ret.showWindowControls = config.showWindowControls;
}
2016-07-29 12:40:46 -08:00
return ret;
})());
2016-07-13 12:44:24 -08:00
break;
case SESSION_ADD:
state_ = state.merge({
activeUid: action.uid,
2016-07-13 12:44:24 -08:00
openAt: {
[action.uid]: Date.now()
}
}, {deep: true});
2016-07-13 12:44:24 -08:00
break;
case SESSION_RESIZE:
// only care about the sizes
Split Panes (#693) * npm: add .npmrc with save-exact=true * split panes: create initial implementation This allows users to split their Hyperterm terms into multiple nested splits, both vertical and horizontal. Fixes #56 * split panes: suport closing tabs and individual panes * split panes: ensure new splits are placed at the correct index New split panes should be placed after the currently active pane, not at the end like they were previously. * split panes: add explicit dependency to uuid * split panes: implement split pane cycling This adds menu buttons for moving back and forward between open split panes in the currect terminal tab. Doesn't add a hotkey yet, needs some bikeshedding. * split panes: move activeSessionUid to its own object It made little sense to have so many objects with `activeSessionUid` set to `null` when it only mattered on the top level. Now it's an object mapping term-group `uid` to `sessionUid` instead. * split panes: make sure closing the last split pane exits the app * split panes: fix a crash after closing specific panes Sometimes the terminal would crash when a specific split pane was closed, because the `activeSessions` mapping wasn't updated correctly. * split panes: fix a bug that caused initial session sizing to be wrong * fix all our focus / blur issues in one fell swoop :O (famous last words) * get rid of react warning * hterm: make sure not to lose focus when VT listens on clicks * term: restore onactive callback * add missing `return` to override (just in case) * split pane: new split pane implementation * goodbye react-split-pane * added term group resizing action and reducer * terms: supply border color so that we can use it for splits * term-group: add resizing hook * term-groups: add resizing constant * remove split pane css side-effect * split panes: pass existing hterm instances to Term * split panes: add keybindings for split pane cycling * split panes: remove unused action * split panes: remove unused styling * split-pane: remove `console.log` * split-pane: remove `console.log` * split panes: rebalance sizes on insert/removal * split panes: pass existing hterm instances to Term * split panes: add keybindings for split pane cycling * split panes: remove unused action * split panes: remove unused styling * split panes: rebalance sizes on insert/removal * split panes: set a minimum size for resizing * split-pane: fix vertical splits * css :| * package: bump electron * split panes: attach onFocus listener to webviews * 1.4.1 and 1.4.2 are broken. they have the following regression: - open google.com on the main window - open a new tab - come back to previous tab. webview is gone :| * split panes: handle PTY exits * split panes: add linux friendly keybindings
2016-10-03 18:00:50 -08:00
// of standalone terms (i.e. not splits):
if (!action.isStandaloneTerm) {
break;
}
2016-07-13 12:44:24 -08:00
state_ = state.merge({
rows: action.rows,
cols: action.cols,
resizeAt: Date.now()
});
break;
case SESSION_PTY_EXIT:
state_ = state
.updateIn(['openAt'], times => {
2016-07-29 12:40:46 -08:00
const times_ = times.asMutable();
delete times_[action.uid];
return times_;
})
.updateIn(['activityMarkers'], markers => {
2016-07-29 12:40:46 -08:00
const markers_ = markers.asMutable();
delete markers_[action.uid];
return markers_;
});
2016-07-13 12:44:24 -08:00
break;
case SESSION_SET_ACTIVE:
state_ = state.merge({
activeUid: action.uid,
activityMarkers: {
[action.uid]: false
}
}, {deep: true});
2016-07-13 12:44:24 -08:00
break;
case SESSION_PTY_DATA: // eslint-disable-line no-case-declarations
2016-07-13 12:44:24 -08:00
// ignore activity markers for current tab
if (action.uid === state.activeUid) {
break;
}
2016-07-13 12:44:24 -08:00
// current time for comparisons
const now = Date.now();
2016-07-13 12:44:24 -08:00
// if first data events after open, ignore
if (now - state.openAt[action.uid] < 1000) {
break;
}
2016-07-13 12:44:24 -08:00
// ignore activity markers that are within
2016-07-13 12:44:24 -08:00
// proximity of a resize event, since we
// expect to get data packets from the resize
// of the ptys as a result
if (!state.resizeAt || now - state.resizeAt > 1000) {
state_ = state.merge({
activityMarkers: {
[action.uid]: true
}
}, {deep: true});
2016-07-13 12:44:24 -08:00
}
break;
2016-07-19 09:45:28 -08:00
case SESSION_SET_CWD:
state_ = state.set('cwd', action.cwd);
break;
2016-07-13 12:44:24 -08:00
case UI_FONT_SIZE_SET:
2016-07-13 21:37:46 -08:00
state_ = state.set('fontSizeOverride', action.value);
2016-07-13 12:44:24 -08:00
break;
case UI_FONT_SIZE_RESET:
state_ = state.set('fontSizeOverride', null);
break;
case UI_FONT_SMOOTHING_SET:
state_ = state.set('fontSmoothingOverride', action.fontSmoothing);
break;
case UI_WINDOW_MAXIMIZE:
state_ = state.set('maximized', true);
break;
case UI_WINDOW_UNMAXIMIZE:
state_ = state.set('maximized', false);
break;
2016-07-13 12:44:24 -08:00
case NOTIFICATION_DISMISS:
state_ = state.merge({
notifications: {
[action.id]: false
}
}, {deep: true});
2016-07-13 12:44:24 -08:00
break;
case NOTIFICATION_MESSAGE:
state_ = state.merge({
messageText: action.text,
2016-10-04 12:27:27 -08:00
messageURL: action.url,
messageDismissable: action.dismissable === true
});
break;
2016-07-13 12:44:24 -08:00
case UPDATE_AVAILABLE:
state_ = state.merge({
updateVersion: action.version,
updateNotes: action.notes || ''
});
break;
}
// Show a notification if any of the font size values have changed
2016-07-13 12:44:24 -08:00
if (CONFIG_LOAD !== action.type) {
if (state_.fontSize !== state.fontSize ||
state_.fontSizeOverride !== state.fontSizeOverride) {
state_ = state_.merge({notifications: {font: true}}, {deep: true});
2016-07-13 12:44:24 -08:00
}
}
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});
2016-07-13 12:44:24 -08:00
}
if (state.messageText !== state_.messageText || state.messageURL !== state_.messageURL) {
state_ = state_.merge({notifications: {message: true}}, {deep: true});
}
2016-07-13 12:44:24 -08:00
if (state.updateVersion !== state_.updateVersion) {
state_ = state_.merge({notifications: {updates: true}}, {deep: true});
2016-07-13 12:44:24 -08:00
}
return state_;
};
export default decorateUIReducer(reducer);