Skip to content

Commit a363671

Browse files
committed
add plugin control and setting of prompt symbol per instance
1 parent a5d9753 commit a363671

File tree

7 files changed

+356
-99
lines changed

7 files changed

+356
-99
lines changed

PLUGINS.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Terminal in React Plugins
2+
3+
## Table of contents
4+
5+
* [Basic Structure](#basic-structure)
6+
7+
* [Plugin Setup](#plugin-setup)
8+
9+
* [Plugin API](#plugin-api)
10+
11+
* [Taking Control](#taking-control)
12+
13+
* [Releasing Control](#releasing-control)
14+
15+
## Basic structure
16+
17+
Each plugin for the terminal should inherit from the plugin base class.
18+
19+
```javascript
20+
import PluginBase from 'terminal-in-react/lib/js/components/Plugin';
21+
22+
class MyPlugin extends PluginBase {
23+
...
24+
}
25+
```
26+
27+
This gives you the required plugin structure, but there are attributes that can be
28+
and those that should be overwritten.
29+
30+
The required overrides are (static displayName, static version):
31+
32+
```javascript
33+
class MyPlugin extends PluginBase {
34+
static displayName = 'MyPlugin';
35+
static version = '1.0.0';
36+
}
37+
```
38+
39+
### Static attributes
40+
41+
| Attribute | Type | Default | Description |
42+
|:---------------- |:---------------------------:|:-------:|:-----------:|
43+
| **displayName** | static string | '' | Used so other plugins can access data and methods as such should be unique |
44+
| **version** | static string | '1.0.0' | Used so other plugins can check that your plugin is a certain version |
45+
| **defaultData** | static any | '' | Used for data storage that other plugins can get access to |
46+
| **commands** | static commands | {} | Commands that are simple and don't need access to the full plugin api |
47+
| **descriptions** | static command descriptions | {} | Descriptions for your static commands |
48+
| **shortcuts** | static shortcuts | {} | Simple shortcuts that call only existing commands or any static plugin command |
49+
50+
### Instance attributes and methods
51+
52+
| Attribute | Type | Default | Description |
53+
|:--------------------:|:--------------------:|:----------:|:-----------:|
54+
| **commands** | commands | {} | Commands that need access to the full plugin api |
55+
| **descriptions** | command descriptions | {} | Descriptions for your commands |
56+
| **shortcuts** | shortcuts | {} | Shortcuts that call more complicated methods |
57+
| **getPublicMethods** | <object>function | () => ({}) | A method to return the public methods your plugin exposes to other plugins |
58+
59+
## Plugin Setup
60+
If you need to use the class constructor you need pass all inputs to `super`.
61+
62+
```javascript
63+
import PluginBase from 'terminal-in-react/lib/js/components/Plugin';
64+
65+
class MyPlugin extends PluginBase {
66+
static displayName = 'MyPlugin';
67+
static version = '1.0.0';
68+
69+
constructor(api, config) {
70+
super(api, config);
71+
}
72+
}
73+
```
74+
75+
## Plugin API
76+
77+
The plugin api will be available in all plugin instance methods as `this.api`.
78+
79+
| Key | Params | Description |
80+
|:---------------------:|:---------------------:|:-----------:|
81+
| **printLine** | content:any | Used to add a new line to the output, can be of any type. |
82+
| **removeLine** | lineNumber:integer:-1 | Used to remove a line from output. If -1 will remove last line. |
83+
| **runCommand** | cmdText:string, force:bool:false | Used to run a command based on the text. `force` is used when a plugin has taken control. |
84+
| **setCanScroll** | canScroll:bool | Used to turn on and off scroll |
85+
| **setScrollPosition** | position:float | Set scroll top of the terminal |
86+
| **focusInput** | | Used to focus the input |
87+
| **setPromptPrefix** | promptPrefix:string | Used to set the prompt prefix of the current tab |
88+
| **setPromptSymbol** | promptSymbol:string | Used to set the prompt symbol ie '>' or '$' |
89+
| **getPluginMethod** | pluginName:string, methodName:string | Used to get a public method from another plugin |
90+
| **takeControl** | controller:object, newPromptSymbol:string, newPromptPrefix:string | Used to take full control over the terminal |
91+
| **releaseControl** | | Used to release full control |
92+
| **getData** | | Used to get the plugin's public data object |
93+
| **setData** | data:any | Used to set the plugin's public data object |
94+
| **checkVersion** | comparator:string, version:string | Used to check if the Terminal version meets certain criteria. ['=', '!=', '>', '<', '<=', '>='] ie ('>=', '3.2.0') |
95+
| **version** | NOT A FUNCTION | The Terminal's version |
96+
| **os** | NOT A FUNCTION | The os of the current user |
97+
98+
## Taking Control
99+
100+
One of the things a Plugin can do is take "full" control of the Terminal. If done
101+
none of the defualt commands or other plugin's commands will work.
102+
103+
To take "control" use:
104+
105+
```javascript
106+
this.api.takeControl(controller);
107+
```
108+
109+
### The controller object
110+
111+
- **shortcuts** : Shortcuts that only work in this mode [Optional]
112+
- **history** : If the inputs by are user should be saved to the default input history. Defaults to `false` [Optional]
113+
- **onKeyPress** : A function to handle the key press event. Params are the key object that was pressed [Optional]
114+
- **runCommand** : A function to take the input text and run commands with. [Optional]
115+
- **commands** : A object of commands that can be run in this mode. Can't be used along with `runCommand`. `runCommand` will take precedence. [Optional]
116+
117+
### Using `runCommand` controller
118+
119+
This allows you to "fully" control all commands that are run even if other
120+
plugins call `this.api.runCommand`. This is mostly true there exists a option on
121+
the api `runCommand` method to bypass the controller. That is the full params for
122+
`this.api.runCommand` are (`inputText`, `force`). If `force` is set to `true` then
123+
the controller will not be called. It is suggested that only a controller call
124+
`this.api.runCommand` with `force` set to `true` for instances where the built in
125+
commands want to be called ie: `this.api.runCommand('clear', true)` to clear
126+
the screen.
127+
128+
## Releasing Control
129+
130+
To do so just call `this.api.releaseControl()`

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,9 @@ Let's take an another example -
232232
<img src="https://i.gyazo.com/ef2427464989b1ce14bc44bb4fc94689.gif" />
233233
</p>
234234

235-
## Using plugins 🔥 [WIP]
235+
## Using plugins 🔥
236+
237+
[Plugin Documentation](PLUGINS.md).
236238

237239
We have also developed a plugin system for the `<Terminal />` component which helps you develop custom plugins. Here is one example of plugin which creates a fake file system called [terminal-in-react-pseudo-file-system-plugin](https://github.com/jcgertig/terminal-in-react-pseudo-file-system-plugin).
238240

@@ -409,6 +411,7 @@ Use
409411
| Props | Type | Default |
410412
| ------------- |:-------------:| -----:|
411413
| **color** | string | 'green' |
414+
| **outputColor** | string | props.color |
412415
| **backgroundColor** | string | 'black' |
413416
| **prompt** | string | 'green' |
414417
| **barColor** | string | 'black' |
@@ -437,7 +440,7 @@ Use
437440

438441
* Embed it as a toy on your website
439442
* For showcasing
440-
* Explain any of your project using this terminal component
443+
* Explain any of your projects using this terminal component
441444
* or just play with it
442445

443446
## You want a X feature

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "terminal-in-react",
3-
"version": "4.2.1",
3+
"version": "4.3.0",
44
"description": "A component for making a terminal in React",
55
"main": "lib/js/index.js",
66
"module": "src/index.js",

src/js/components/Content/index.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@ class Content extends Component {
1313
static propTypes = {
1414
id: PropTypes.string,
1515
oldData: PropTypes.object, // eslint-disable-line
16+
prompt: PropTypes.string,
1617
register: PropTypes.func,
1718
handleChange: PropTypes.func,
1819
handlerKeyPress: PropTypes.func.isRequired,
1920
};
2021

2122
static defaultProps = {
23+
prompt: '>',
2224
oldData: {},
2325
};
2426

2527
static contextTypes = {
26-
symbol: PropTypes.string,
2728
maximise: PropTypes.bool,
2829
instances: PropTypes.array,
2930
activeTab: PropTypes.string,
@@ -34,18 +35,22 @@ class Content extends Component {
3435
state = {
3536
summary: [],
3637
promptPrefix: '',
38+
prompt: '>',
3739
history: [],
3840
historyCounter: 0,
3941
input: [],
4042
keyInputs: [],
4143
canScroll: true,
44+
controller: null,
4245
};
4346

4447
componentWillMount = () => {
4548
const data = this.context.instances.find(i => i.index === this.props.id);
49+
let state = { prompt: this.props.prompt };
4650
if (data) {
47-
this.setState(data.oldData);
51+
state = { ...state, ...data.oldData };
4852
}
53+
this.setState(state);
4954
};
5055

5156
componentDidMount = () => {
@@ -106,7 +111,7 @@ class Content extends Component {
106111
render() {
107112
const { id } = this.props;
108113
const {
109-
symbol, maximise, activeTab, barShowing, tabsShowing,
114+
maximise, activeTab, barShowing, tabsShowing,
110115
} = this.context;
111116

112117
if (id !== activeTab) {
@@ -117,7 +122,17 @@ class Content extends Component {
117122
if (typeof content === 'string' && content.length === 0) {
118123
return <OutputLine key={i}>&nbsp;</OutputLine>;
119124
}
120-
return <PreOutputLine key={i}>{content}</PreOutputLine>;
125+
return (
126+
<PreOutputLine key={i}>
127+
{
128+
Array.isArray(content) ?
129+
content.map((cont, key) => (
130+
<span style={{ marginRight: 5 }} key={`inner-${key}`}>{cont}</span>
131+
)) :
132+
content
133+
}
134+
</PreOutputLine>
135+
);
121136
});
122137

123138
let toSubtract = 30;
@@ -150,7 +165,7 @@ class Content extends Component {
150165
innerRef={(elm) => { this.inputWrapper = elm; }}
151166
>
152167
<Prompt>
153-
{this.state.promptPrefix + symbol}
168+
{this.state.promptPrefix + this.state.prompt}
154169
</Prompt>
155170
<MainInput
156171
type="text"

0 commit comments

Comments
 (0)