- Instalación
- Objetivos del proyecto
- Extensiones
- Keymaps
- Mapas de teclado por defecto:
- Configuración
- Localización de la configuración
- API de extensiones
- Carga de módulos
- Observación
- Localización de los plugins
- Decoración de componentes
- Acciones y Efectos
- La terminal subyacente
- Apis adicionales
- Tema de ejemplo: Hyperyellow
- Extensión de ejemplo: Hyperpower
Instalación
última versión: 3.0.2
64 bits | |
macOS (.app) | 3.0.2 |
Windows (.exe) | 3.0.2 |
Debian (.deb) | 3.0.2 |
Fedora (.rpm) | 3.0.2 |
Otras distros Linux (.AppImage) | 3.0.2 |
Objetivos del proyecto
El objetivo del proyecto es crear una experiencia hermosa y extensible para los usuarios de la interfaz de línea de comandos, construida sobre estándares web abiertos. Al principio, nuestro enfoque será principalmente en torno a la velocidad, la estabilidad y el desarrollo de la API correcta para los autores de extensiones.
En el futuro, anticipamos que la comunidad vendrá con adiciones innovadoras para mejorar lo que podría ser la interfaz más simple, más potente y bien probada para la productividad.
Extensiones
Las extensiones están disponibles en npm. Animamos a todos a incluir hyper
en el campo keywords
en package.json
.
$ npm search hyper
Entonces edite .hyper.js
y añádalo a plugins
module.exports = { config: { /*... */ }, plugins: };
Hyper
mostrará una notificación cuando sus módulos sean instalados a .hyper_plugins
.
Keymaps
Todas las claves de comando pueden ser cambiadas. Para cambiarlas, edite .hyper.js
y añada el cambio que desee a keymaps
.
Entonces Hyper cambiará el valor por defecto con su cambio personalizado.
Ejemplo: 'window:devtools': 'Cmd+Alt+O'
module.exports = { config: { /*... */ }, keymaps: { 'window:devtools': 'cmd+alt+o' }};
Mapas de teclado por defecto:
Configuración
Localización de la configuración
macOS | ~/Library/Application Support/Hyper/.hyper.js |
Windows | $Env:AppData/Hyper/.hyper.js |
Linux | ~/.config/Hyper/.hyper.js |
Nota: config en ~/.hyper.js
todavía se admite, pero será ignorado, si config en el directorio de la aplicación presente. De lo contrario, se moverá al directorio de la aplicación en la primera ejecución.
El objeto config
visto arriba en .hyper.js
admite lo siguiente
Propiedad | Por defecto | Descripción |
updateChannel |
«estable» | El canal de actualización para recibir las actualizaciones de |
fontSize |
12 | El tamaño por defecto en píxeles para el terminal |
fontFamily |
«Menlo, DejaVu Sans Mono, Lucida Console, monospace» | La familia de fuentes a utilizar con fallbacks opcionales |
uiFontFamily |
«-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, …» | La familia de fuentes a utilizar para la UI con fallbacks opcionales |
fontWeight |
«normal» | El peso de la fuente por defecto: «normal» o «bold» |
fontWeightBold |
«bold» | El peso de la fuente para los caracteres en negrita: «normal» o «negrita» |
cursorColor |
«rgba(248,28,229,0.8)» | El color del caret en el terminal |
cursorAccentColor |
«#000» | El color del texto bajo el cursor BLOCK |
cursorShape |
«BLOCK» | La forma del caret en el terminal. Las opciones disponibles son: ‘BEAM’, ‘UNDERLINE’, ‘BLOCK’ |
cursorBlink |
«false» | Si es verdadero, el cursor parpadeará |
foregroundColor |
«#fff» | El color del texto principal del terminal |
backgroundColor |
«#000» | El color y la opacidad del fondo de la ventana y del terminal principal |
selectionColor |
«rgba(248,28,229,0.3)» | El color de fondo/opacidad de la selección de texto en el terminal |
borderColor |
«#333» | El color del borde de la ventana principal y de la barra de pestañas |
css |
«» | CSS personalizado para incluir en la ventana principal |
padding |
«12px 14px» | Valores de relleno de CSS para el espacio alrededor de cada término |
colors |
{ negro: «#000000», rojo: «#ff0000», … } | Una lista de anulaciones para la paleta de colores. Los nombres de las teclas representan el «ANSI 16», que todos pueden ser vistos en la configuración por defecto. |
shell |
«» | Una ruta a un shell personalizado para ejecutar cuando Hyper inicia una nueva sesión |
shellArgs |
«» | Una matriz de argumentos del shell |
env |
{} | Un objeto de variables de entorno a establecer antes de lanzar el shell |
windowSize |
La anchura/altura por defecto en píxeles de una nueva ventana | |
scrollback |
1000 | El número de filas que deben persistir en el buffer del terminal para el desplazamiento |
copyOnSelect |
false | Si es verdadero, el texto seleccionado se copiará automáticamente en el portapapeles |
quickEdit |
falso | Si es cierto, al hacer clic con el botón derecho se copiará el texto seleccionado o se pegará si no hay selección (verdadero por defecto en Windows) |
defaultSSHApp |
true | Si es verdadero, Hyper se establecerá como cliente de protocolo por defecto para SSH |
modifierKeys |
{altIsMeta: false} | Cambiar el comportamiento de las teclas modificadoras para que actúen como meta tecla |
showHamburgerMenu |
true en Linux/Windows, false en macOS | Cambiar la visibilidad del menú hamburguesa. Las opciones disponibles son: true, false |
showWindowControls |
« | Cambiar la posición/visibilidad de los controles de la ventana. Las opciones disponibles son: true, false, «left» |
API de extensiones
Las extensiones son módulos universales de Node.js cargados tanto por Electron como por el proceso de renderizado.
El sistema de extensiones está diseñado en torno a la composición de las APIs que utilizamos para construir el terminal: React
componentes y Redux
acciones.
En lugar de exponer un método o parámetro personalizado de la API para cada punto de personalización posible, ¡permitimos interceptar y componer cada trozo de funcionalidad!
El único conocimiento que se requiere, por tanto, para extender con éxito Hyper
es el de sus bibliotecas de código abierto subyacentes.
Puede encontrar detalles adicionales sobre el desarrollo de plugins en el repositorio Hyper.
Su módulo tiene que exponer al menos uno de estos métodos:
Método | Invocado desde | Descripción | |||||
onApp |
Electrón |
Invocado cuando la aplicación se carga por primera vez. Si un plugin se recarga, se invoca de nuevo con la app existente. Parámetros:
|
|||||
onWindow |
Electron |
Se invoca cuando se crea cada ventana. Si un plugin se recarga, se invoca de nuevo con las ventanas existentes. Parámetros:
|
|||||
onUnload |
Electrón |
Se invoca cuando un plugin es eliminado por el usuario. Parámetros:
|
|||||
decorateConfig |
Electron / Renderer |
v0.5.0+. Permite decorar la configuración del usuario. Parámetros:
|
|||||
decorateEnv |
Electron |
environment |
El objeto environment |
decorateMenu
Invocado con la plantilla Menu
de Electron. Si se recarga un plugin, se llama de nuevo y se refresca el menú.
Parámetros:
menu |
El objeto plantilla del menú |
decorateBrowserOptions
Permite decorar las opciones de BrowserWindow
Electron cuando se crea una nueva ventana.
Parámetros:
options |
El objeto de opciones BrowserWindow . |
onRendererWindow
Se invoca cuando un plugin se carga por primera vez o se recarga posteriormente en cada ventana.
Parámetros:
window |
El objeto ventana |
middleware
Un middleware Redux personalizado que puede interceptar cualquier acción. Posteriormente invocamos el thunk
middleware, lo que significa que su middleware puedenext
thunks.
reduceUI
reduceSessions
reduceTermGroups
Un reductor personalizado para la forma de estado ui
, sessions
o termgroups
.
state |
El objeto de estado Redux |
action |
El objeto de acción |
getTabsProps
Pasa los props de <Tabs>
al componente <Header>
. Debe devolver el objeto props compuesto.
parentProps |
|
props |
Las propiedades existentes que se pasarán al componente. |
getTabProps
Pasa las props de <Tab>
al componente <Tabs>
. Debe devolver el objeto props compuesto.
uid |
Tab / Term uid |
parentProps |
Props del componente padre. |
props |
getTermGroupProps
<Terms>
al componente <TermGroup>
. Debe devolver el objeto props compuesto.
uid |
TermGroup uid |
parentProps |
Props del componente padre. |
props |
getTermProps
<TermGroup>
al componente <Term>
. Debe devolver el objeto props compuesto.
uid |
Término uid |
parentProps |
Props del componente padre. |
props |
mapHyperState
mapTermsState
mapHeaderState
mapNotificationsState
Un mapeador personalizado para las propiedades de estado que reciben los componentes contenedores. Tenga en cuenta que para que los componentes hijos obtengan estas propiedades, tiene que pasarlas hacia abajo usando los métodos correspondientes (como getTermProps
).
Debe devolver un objeto extendido del mapa pasado.
state |
El Redux estado global |
map |
El mapa de propiedades existente que se pasará al componente. |
mapHyperDispatch
mapTermsDispatch
mapHeaderDispatch
mapNotificationsDispatch
Un mapeador personalizado para las propiedades de envío. Debe devolver un objeto extendido del mapa pasado.
dispatch |
La función de envío de Redux |
map |
El mapa de propiedades existente que se pasará al componente. |
decorateHyper
decorateNotifications
decorateNotification
decorateHeader
decorateTabs
decorateTab
decorateTerms
decorateTermGroup
decorateSplitPane
decorateTerm
Invocado con el React
Component
para decorar. Debe devolver un componente de orden superior.
Parámetros:
Hyper |
El React Component constructor. |
env |
Una colección de referencias de módulos útiles para construir componentes. Ver más abajo |
Carga de módulos
El usuario puede cargar y recargar plugins en caliente pulsando Comando + R (refrescar). Por favor, esfuércese por hacer plugins que no requieran un reinicio completo de la aplicación para que funcionen.
Observación
Los plugins que afecten a la `BrowserWindow` tendrán el efecto en las nuevas ventanas después de la recarga en caliente.
En el futuro podríamos hacer esto automáticamente.
Cuando desarrolle, puede añadir su plugin a .hyper_plugins/local
y luego especificarlo bajo la matriz localPlugins
en .hyper.js
. Cargamos nuevos plugins:
- Periódicamente (cada pocas horas)
- Cuando se realizan cambios en el archivo de configuración (
plugins
olocalPlugins
) - Cuando el usuario hace clic en Plugins > Actualizar todo ahora
El proceso de recarga implica
- Ejecutar
npm prune
ynpm install
en.hyper_plugins
. - Ejecutar el
require.cache
tanto en electron como en el proceso de renderizado - Invocar métodos
on*
en las instancias existentes y volver a renderizar los componentes con las decoraciones frescas en su lugar.
Localización de los plugins
macOS | ~/Library/Application Support/Hyper/.hyper_plugins |
Windows | $Env:AppData/Hyper/.hyper_plugins |
Linux | ~/.config/Hyper/.hyper_plugins |
Nota: plugins en ~/.hyper_plugins
todavía se admite, pero será ignorado, si los plugins en el directorio de la aplicación presente. De lo contrario, se moverán al directorio de la aplicación en la primera ejecución.
Nota: en el proceso principal, los plugins se registran tan pronto como sea posible (disparamos onLoad
). En el navegador, es el usuario el que desencadena su carga pulsando comando+R. Ponemos al usuario en control de la carga de esta manera para evitar que pierdan trabajo crítico por extensiones que resetean el estado o no lo preservan correctamente.
Decoración de componentes
Le damos la capacidad de proporcionar un componente de orden superior para cada pieza de la Hyper
UI.
Su estructura es la siguiente:
<Hyper> <Header> <Tabs> <Tab /> ... </Tabs> </Header> <Terms> <TermGroup> <SplitPane> <TermGroup> <Term /> ... </TermGroup> <TermGroup> <Term /> ... </TermGroup> </SplitPane> </TermGroup> </Terms> <Notifications> <Notification /> ... </Notifications></Hyper>
Todos los métodos decorate*
reciben las siguientes referencias en un objeto pasado como segundo parámetro:
React |
Todo el espacio de nombres de React. |
notify |
Una función de ayuda que muestra una notificación de escritorio. El primer parámetro es el título, el segundo es el cuerpo opcional de la notificación, y el tercero es otro parámetro opcional que puede utilizarse para registrar detalles en la consola de desarrollo. Para pasar estos detalles, basta con proporcionar y objeto con una propiedad |
Notification |
El componente Notification por si se quiere reutilizar. |
Todos los componentes aceptan las siguientes dos propiedades para ampliar su marcado:
customChildren |
Un array de Element o un únicoElement para insertar en la parte inferior del componente. |
customChildrenBefore |
Lo mismo que la propiedad anterior, pero insertado como primer elemento hijo del componente. |
Su componente de orden superior puede suministrar una propiedad onDecorated
al componente decorado para obtener una referencia a su instancia.
Su componente de orden superior Term puede suministrar una propiedad onCursorMove
handler que se llama cuando el cursor se ha movido con un parámetro de objeto que representa su posición relativa al origen Term:
x |
Posición horizontal en píxeles |
y |
Posición vertical en píxeles |
width |
Ancho del cursor en píxeles |
height |
Altura del cursor en píxeles |
col |
Posición horizontal en columnas |
row |
Posición vertical en filas |
Le animamos a mantener la compatibilidad con otros decoradores. Ya que se pueden establecer muchos, no asumas que el tuyo es el único.
Por ejemplo, si estás pasando hijos, compón los posibles valores existentes:
render () { const customChildren = Array.from(this.props.customChildren) .concat(<p>My new child</p>); return <Tab {...this.props} customChildren={customChildren} />}
O si usas la propiedad onDecorated
onDecorated (term) { this.term = term; if (this.props.onDecorated) { this.props.onDecorated(term); }}
Acciones y Efectos
Todas las acciones de Redux están disponibles para que las manejes a través de tus middleware y reductores. Para un ejemplo, consulte el plugin de referencia Hyperpower.
Los efectos secundarios ocurren en dos formas fundamentales:
- Algunas acciones despachan otras acciones basadas en el estado.
- Algunas acciones realizan un trabajo asíncrono comunicándose a través del canal RPC con el proceso principal
En todos los casos, el efecto secundario se pasa como la clave effect
en la acción y posteriormente es manejado por nuestro middleware.
¡Esto significa que puedes anular, componer o eliminar completamente los efectos! En otras palabras, así es como puedes cambiar la funcionalidad o el comportamiento por defecto de la app.
Como ejemplo, considera la acción que utilizamos para aumentar el tamaño de la letra cuando pulsas Command+=
:
export function increaseFontSize () { return (dispatch, getState) => { dispatch({ type: UI_FONT_SIZE_INCR, effect () { const state = getState(); const old = state.ui.fontSizeOverride || state.ui.fontSize; const value = old + 1; dispatch({ type: UI_FONT_SIZE_SET, value }); } }); };}
La terminal subyacente
Hyper
consigue gran parte de su velocidad y funcionalidad gracias a la potencia de xterm.js
Apis adicionales
Los objetos Electron app
se extienden con las siguientes propiedades:
config |
Un Object con el bloque config de .hyper.js . |
plugins |
|
getWindows |
Un Function que devuelve un Set de todas las ventanas abiertas. |
createWindow |
Un Function que creará una nueva ventana. Acepta un callback opcional que se pasará como callback de la nueva ventana. |
Los objetos Electron BrowserWindow
se extienden con los siguientes parámetros:
rpc |
Un EventEmitter que permite la comunicación con el proceso de la ventana. |
sessions |
Un Map objeto de Session comunicación con el pty. de cada término. |
Las ventanas del renderizador se extienden de forma similar con:
rpc |
Un EventEmitter que permite la comunicación con el proceso de la ventana. |
store |
El objeto Redux Store . Permite acceder a las acciones de dispatch o leer el estado global con getState . |
El objeto rpc
es simétrico entre el navegador y el proceso de renderizado. La API es la misma que la de Node.js, con la excepción de que sólo admite un único objeto como sus parámetros:
window.rpc.emit('hi there', { some: 'payload', any: });
Tema de ejemplo: Hyperyellow
¡La siguiente extensión simplemente altera la configuración para añadir CSS y colores amarillos! Aquí está el código.
¡Los temas son simplemente plugins! Sólo se necesita un gancho, decorateConfig
:
exports.decorateConfig = (config) => { return Object.assign({}, config, { borderColor: 'yellow', cursorColor: 'yellow', css: ` ${config.css || ''} .tabs_nav .tabs_list .tab_text { color: yellow; } .tabs_nav .tabs_title { color: yellow; } ` });}
Agarré los nombres de las clases inspeccionando el término con Devtools, que puedes activar desde View -> Toggle Developer Tools
. Cuando lo hagas, observa que algunas clases se generan automáticamente y van seguidas de un nonce aleatorio (por ejemplo: term_13hv8io
). Ignóralos: ¡cambian con cada nueva ventana!
Nota el énfasis en jugar bien con otras extensiones. En concreto, creamos un nuevo objeto, extendemos sólo las claves que nos interesan, y componemos el CSS para preservar la configuración del usuario y la de otros autores:
return Object.assign({}, config, { css: ` ${config.css || ''} /* your css here */ `});
Extensión de ejemplo: Hyperpower
La siguiente extensión renderiza partículas a medida que el caret se mueve:
Vamos a recorrer su código.
Primero, interceptamos la acción Redux SESSION_ADD_DATA
. Puedes encontrar la lista completa de acciones en el repositorio.
exports.middleware = (store) => (next) => (action) => { if ('SESSION_ADD_DATA' === action.type) { const { data } = action; if (/bash: wow: command not found/.test(data)) { store.dispatch({ type: 'WOW_MODE_TOGGLE' }); } else { next(action); } } else { next(action); }};
Nota que no re-despachamos la acción, lo que significa que nunca renderizamos la salida del comando a la terminal. En su lugar, despachamos una acción propia, que agarramos en el uiReducer
y luego mapeamos:
exports.reduceUI = (state, action) => { switch (action.type) { case 'WOW_MODE_TOGGLE': return state.set('wowMode', !state.wowMode); } return state;};exports.mapTermsState = (state, map) => { return Object.assign(map, { wowMode: state.ui.wowMode });};
Entonces queremos decorar el componente <Term>
para poder acceder al caret subyacente.
Sin embargo, <Term>
no es un contenedor al que podamos mapear props. Así que usamos getTermProps
para pasar la propiedad más abajo:
exports.getTermProps = (uid, parentProps, props) => { return Object.assign(props, { wowMode: parentProps.wowMode });}
La extensión devuelve entonces un componente de orden superior para envolver <Term>
. Fíjate en que pasamos la propiedad onDecorated
para acceder al componente base de Term y su ref. DOM, y la propiedad onCursorMove
para usar la API de Hyper cursor:
render () { return React.createElement(Term, Object.assign({}, this.props, { onDecorated: this._onDecorated, onCursorMove: this._onCursorMove }));}