diff --git a/PLUGINS.md b/PLUGINS.md index 5d7c6134b0c8..3aa0abd8faca 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -40,6 +40,23 @@ When you put a `console.log()` in your plugin code, it will be displayed in the Almost all available API methods can be found on https://hyper.is. If there's any missing, let us know or submit a PR to document it! +### Xterm Addons + +Plugins can contribute xterm.js addons by exporting a `getXtermAddons` function that returns an array of addon instances. + +Example: + +```js +// In your plugin's main file +import { xyzAddon } from 'xterm-addon-xyz'; + +exports.getXtermAddons = () => { + return [new xyzAddon()]; +}; +``` + +These addons are loaded by Hyper and made available to the terminal. + ### Components You can decorate almost all Hyper components with a Higher-Order Component (HOC). To understand their architecture, the easiest way is to use React dev-tools to dig in to their hierarchy. diff --git a/app/plugins/extensions.ts b/app/plugins/extensions.ts index 5fd3cdfe2ed7..522644f06d4d 100644 --- a/app/plugins/extensions.ts +++ b/app/plugins/extensions.ts @@ -14,10 +14,10 @@ export const availableExtensions = new Set([ 'decorateBrowserOptions', 'decorateMenu', 'decorateTerm', + 'decorateTerms', 'decorateHyper', 'decorateHyperTerm', // for backwards compatibility with hyperterm 'decorateHeader', - 'decorateTerms', 'decorateTab', 'decorateNotification', 'decorateNotifications', @@ -31,6 +31,7 @@ export const availableExtensions = new Set([ 'getTabProps', 'getTabsProps', 'getTermGroupProps', + 'getXtermAddons', 'mapHyperTermState', 'mapTermsState', 'mapHeaderState', diff --git a/lib/components/term.tsx b/lib/components/term.tsx index 45c1464c97cc..29bd6ef795d5 100644 --- a/lib/components/term.tsx +++ b/lib/components/term.tsx @@ -19,7 +19,7 @@ import {WebglAddon} from 'xterm-addon-webgl'; import type {TermProps} from '../../typings/hyper'; import terms from '../terms'; import processClipboard from '../utils/paste'; -import {decorate} from '../utils/plugins'; +import {decorate, getXtermAddons} from '../utils/plugins'; import _SearchBox from './searchBox'; @@ -243,6 +243,16 @@ export default class Term extends React.PureComponent< if (props.imageSupport) { this.term.loadAddon(new ImageAddon()); } + + // Load xterm addons from plugins + const pluginAddons = getXtermAddons(); + for (const addon of pluginAddons) { + try { + this.term.loadAddon(addon); + } catch (err) { + console.warn(`Failed to load xterm addon from plugin:`, err); + } + } } else { // get the cached plugins this.fitAddon = props.fitAddon!; diff --git a/lib/utils/plugins.ts b/lib/utils/plugins.ts index dea23f915fdc..5997b4364ff4 100644 --- a/lib/utils/plugins.ts +++ b/lib/utils/plugins.ts @@ -69,6 +69,9 @@ let reducersDecorators: { reduceTermGroups: ITermGroupReducer[]; }; +// xterm addons from plugins +let xtermAddons: any[] = []; + // expose decorated component instance to the higher-order components function exposeDecorated
>( Component_: React.ComponentType
@@ -252,6 +255,7 @@ const loadModules = () => {
tabsPropsDecorators = [];
termPropsDecorators = [];
termGroupPropsDecorators = [];
+ xtermAddons = [];
propsDecorators = {
getTermProps: termPropsDecorators,
@@ -355,6 +359,15 @@ const loadModules = () => {
connectors.Notifications.dispatch.push(mod.mapNotificationsDispatch);
}
+ if (mod.getXtermAddons) {
+ const addons = mod.getXtermAddons();
+ if (Array.isArray(addons)) {
+ xtermAddons.push(...addons);
+ } else if (addons) {
+ xtermAddons.push(addons);
+ }
+ }
+
if (mod.getTermGroupProps) {
termGroupPropsDecorators.push(mod.getTermGroupProps);
}
@@ -581,3 +594,7 @@ export const middleware: Middleware<{}, HyperState, Dispatch