mirror of
https://github.com/embarklabs/neo-blessed.git
synced 2025-02-22 15:48:07 +00:00
Image and Terminal element.
This commit is contained in:
parent
267e77a089
commit
982e075a90
23
README.md
23
README.md
@ -871,24 +871,37 @@ A radio button which can be used in a form element.
|
||||
|
||||
#### Terminal (from Box)
|
||||
|
||||
A box which spins up a pseudo terminal and renders the output. (Requires
|
||||
term.js to be installed).
|
||||
A box which spins up a pseudo terminal and renders the output. Useful for
|
||||
writing a terminal multiplexer, or something similar to an mc-like file
|
||||
manager. Requires term.js and pty.js to be installed. See
|
||||
`example/multiplex.js` for an example terminal multiplexer.
|
||||
|
||||
##### Options:
|
||||
|
||||
- inherits all from Box.
|
||||
- **handler** - handler for input data.
|
||||
- **shell** - name of shell. `$SHELL` by default.
|
||||
- **args** - args for shell.
|
||||
- **cursor** - can be `line`, `underline`, and `block`.
|
||||
- Other options similar to term.js'.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- inherits all from Box.
|
||||
- **term** - reference to the headless term.js terminal.
|
||||
- **pty** - reference to the pty.js pseudo terminal.
|
||||
|
||||
##### Events:
|
||||
|
||||
- inherits all from Box.
|
||||
- **title** - window title from terminal.
|
||||
- Other events similar to ScrollableBox.
|
||||
|
||||
##### Methods:
|
||||
|
||||
- inherits all from Box.
|
||||
- **write(data)** - write data to the terminal.
|
||||
- Other methods similar to ScrollableBox.
|
||||
|
||||
|
||||
#### Image (from Box)
|
||||
@ -900,8 +913,9 @@ terminals.
|
||||
##### Options:
|
||||
|
||||
- inherits all from Box.
|
||||
- **file** - path to image
|
||||
- **w3m** - path to w3mimgdisplay
|
||||
- **file** - path to image.
|
||||
- **w3m** - path to w3mimgdisplay. if a proper w3mimgdisplay path is not given,
|
||||
blessed will search the entire disk for the binary.
|
||||
|
||||
##### Properties:
|
||||
|
||||
@ -918,6 +932,7 @@ terminals.
|
||||
- **clearImage(callback)** - clear the current image.
|
||||
- **imageSize(img, callback)** - get the size of an image file in pixels.
|
||||
- **termSize(callback)** - get the size of the terminal in pixels.
|
||||
- **getPixelRatio(callback)** - get the pixel to cell ratio for the terminal.
|
||||
|
||||
|
||||
### Positioning
|
||||
|
@ -14,6 +14,9 @@ var blessed = require('blessed')
|
||||
|
||||
var left = blessed.terminal({
|
||||
parent: screen,
|
||||
cursor: 'line',
|
||||
cursorBlink: true,
|
||||
screenKeys: false,
|
||||
left: 0,
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
@ -32,6 +35,9 @@ var left = blessed.terminal({
|
||||
|
||||
var right = blessed.terminal({
|
||||
parent: screen,
|
||||
cursor: 'block',
|
||||
cursorBlink: true,
|
||||
screenKeys: false,
|
||||
right: 2,
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
|
249
lib/widget.js
249
lib/widget.js
@ -5364,7 +5364,7 @@ ProgressBar.prototype.render = function() {
|
||||
yi = yi + ((yl - yi) - (((yl - yi) * (this.filled / 100)) | 0));
|
||||
}
|
||||
|
||||
dattr = this.sattr(this, this.style.bar.fg, this.style.bar.bg);
|
||||
dattr = this.sattr(this.style.bar, this.style.bar.fg, this.style.bar.bg);
|
||||
|
||||
this.screen.fillRegion(dattr, this.ch, xi, xl, yi, yl);
|
||||
|
||||
@ -6481,7 +6481,7 @@ function Terminal(options) {
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
options.scrollable = true;
|
||||
options.scrollable = false;
|
||||
|
||||
Box.call(this, options);
|
||||
|
||||
@ -6489,6 +6489,10 @@ function Terminal(options) {
|
||||
this.shell = options.shell || process.env.SHELL || 'sh';
|
||||
this.args = options.args || [];
|
||||
|
||||
this.cursor = this.options.cursor;
|
||||
this.cursorBlink = this.options.cursorBlink;
|
||||
this.screenKeys = this.options.screenKeys;
|
||||
|
||||
this.style = this.style || {};
|
||||
this.style.bg = this.style.bg || 'default';
|
||||
this.style.fg = this.style.fg || 'default';
|
||||
@ -6535,14 +6539,15 @@ Terminal.prototype.bootstrap = function() {
|
||||
element.offsetParent = element;
|
||||
|
||||
this.term = require('term.js')({
|
||||
//termName: this.screen.program.terminal,
|
||||
termName: 'xterm',
|
||||
cols: this.width - this.iwidth,
|
||||
rows: this.height - this.iheight,
|
||||
context: element,
|
||||
document: element,
|
||||
body: element,
|
||||
parent: element
|
||||
parent: element,
|
||||
cursorBlink: this.cursorBlink,
|
||||
screenKeys: this.screenKeys
|
||||
});
|
||||
|
||||
this.term.refresh = function() {
|
||||
@ -6623,15 +6628,11 @@ Terminal.prototype.bootstrap = function() {
|
||||
});
|
||||
|
||||
this.on('focus', function() {
|
||||
if (self.term.sendFocus) {
|
||||
self.handler('\x1b[I');
|
||||
}
|
||||
self.term.focus();
|
||||
});
|
||||
|
||||
this.on('blur', function() {
|
||||
if (self.term.sendFocus) {
|
||||
self.handler('\x1b[O');
|
||||
}
|
||||
self.term.blur();
|
||||
});
|
||||
|
||||
this.term.on('title', function(title) {
|
||||
@ -6650,7 +6651,6 @@ Terminal.prototype.bootstrap = function() {
|
||||
});
|
||||
|
||||
this.pty = require('pty.js').fork(this.shell, this.args, {
|
||||
//name: this.screen.program.terminal,
|
||||
name: 'xterm',
|
||||
cols: this.width - this.iwidth,
|
||||
rows: this.height - this.iheight,
|
||||
@ -6687,7 +6687,7 @@ Terminal.prototype.render = function() {
|
||||
var ret = this._render();
|
||||
if (!ret) return;
|
||||
|
||||
this.dattr = this.sattr(this, this.style.fg, this.style.bg);
|
||||
this.dattr = this.sattr(this.style, this.style.fg, this.style.bg);
|
||||
|
||||
var xi = ret.xi + this.ileft
|
||||
, xl = ret.xl - this.iright
|
||||
@ -6702,7 +6702,7 @@ Terminal.prototype.render = function() {
|
||||
if (!line || !this.term.lines[scrollback + y - yi]) break;
|
||||
|
||||
if (y === yi + this.term.y
|
||||
// && this.term.cursorState
|
||||
&& this.term.cursorState
|
||||
&& this.screen.focused === this
|
||||
&& (this.term.ydisp === this.term.ybase || this.term.selectMode)
|
||||
&& !this.term.cursorHidden) {
|
||||
@ -6713,21 +6713,42 @@ Terminal.prototype.render = function() {
|
||||
|
||||
for (var x = xi; x < xl; x++) {
|
||||
if (!line[x] || !this.term.lines[scrollback + y - yi][x - xi]) break;
|
||||
|
||||
if (x === cursor) {
|
||||
line[x][0] = 7;
|
||||
if (this.cursor === 'line') {
|
||||
line[x][0] = this.dattr;
|
||||
line[x][1] = '\u2502';
|
||||
} else if (this.cursor === 'underline') {
|
||||
line[x][0] = this.dattr;
|
||||
line[x][1] = '_';
|
||||
} else if (this.cursor === 'block' || !this.cursor) {
|
||||
line[x][0] = this.dattr | (8 << 18);
|
||||
line[x][1] = ' ';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
line[x][0] = this.term.lines[scrollback + y - yi][x - xi][0];
|
||||
// default foreground = 257
|
||||
if (((line[x][0] >> 9) & 0x1ff) === 257 && !((line[x][0] >> 18) & 8)) {
|
||||
line[x][0] = (line[x][0] << 18) | (((this.dattr >> 9) & 0x1ff) << 9) | (line[x][0] & 0x1ff);
|
||||
}
|
||||
// default background = 256
|
||||
if ((line[x][0] & 0x1ff) === 256 && !((line[x][0] >> 18) & 8)) {
|
||||
line[x][0] = (line[x][0] << 18) | (((line[x][0] >> 9) & 0x1ff) << 9) | (this.dattr & 0x1ff);
|
||||
}
|
||||
line[x][1] = this.term.lines[scrollback + y - yi][x - xi][1];
|
||||
|
||||
// default foreground = 257
|
||||
if (((line[x][0] >> 9) & 0x1ff) === 257) {
|
||||
if (!((line[x][0] >> 18) & 8)) {
|
||||
line[x][0] &= ~(0x1ff << 9);
|
||||
line[x][0] |= ((this.dattr >> 9) & 0x1ff) << 9;
|
||||
}
|
||||
}
|
||||
|
||||
// default background = 256
|
||||
if ((line[x][0] & 0x1ff) === 256) {
|
||||
if (!((line[x][0] >> 18) & 8)) {
|
||||
line[x][0] &= ~0x1ff;
|
||||
line[x][0] |= this.dattr & 0x1ff;
|
||||
// } else {
|
||||
// line[x][0] &= ~(0x1ff << 9);
|
||||
// line[x][0] |= ((this.dattr >> 9) & 0x1ff) << 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line.dirty = true;
|
||||
@ -6747,6 +6768,7 @@ Terminal.prototype._isMouse = function(buf) {
|
||||
}
|
||||
}
|
||||
return (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d)
|
||||
|| /^\x1b\[M([\x00\u0020-\uffff]{3})/.test(s)
|
||||
|| /^\x1b\[(\d+;\d+;\d+)M/.test(s)
|
||||
|| /^\x1b\[<(\d+;\d+;\d+)([mM])/.test(s)
|
||||
|| /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.test(s)
|
||||
@ -6754,6 +6776,39 @@ Terminal.prototype._isMouse = function(buf) {
|
||||
|| /^\x1b\[(O|I)/.test(s);
|
||||
};
|
||||
|
||||
Terminal.prototype.setScroll =
|
||||
Terminal.prototype.scrollTo = function(offset, always) {
|
||||
this.term.ydisp = offset;
|
||||
return this.emit('scroll');
|
||||
};
|
||||
|
||||
Terminal.prototype.getScroll = function() {
|
||||
return this.term.ydisp;
|
||||
};
|
||||
|
||||
Terminal.prototype.scroll = function(offset, always) {
|
||||
this.term.scrollDisp(offset);
|
||||
return this.emit('scroll');
|
||||
};
|
||||
|
||||
Terminal.prototype.resetScroll = function() {
|
||||
this.term.ydisp = 0;
|
||||
this.term.ybase = 0;
|
||||
return this.emit('scroll');
|
||||
};
|
||||
|
||||
Terminal.prototype.getScrollHeight = function() {
|
||||
return this.term.rows - 1;
|
||||
};
|
||||
|
||||
Terminal.prototype.getScrollPerc = function(s) {
|
||||
return (this.term.ydisp / this.term.ybase) * 100;
|
||||
};
|
||||
|
||||
Terminal.prototype.setScrollPerc = function(i) {
|
||||
return this.setScroll((i / 100) * this.term.ybase | 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Image
|
||||
* Good example of w3mimgdisplay commands:
|
||||
@ -6771,11 +6826,43 @@ function Image(options) {
|
||||
|
||||
Box.call(this, options);
|
||||
|
||||
if (options.w3m) {
|
||||
Image.w3mdisplay = options.w3m;
|
||||
}
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
if (fs.existsSync(Image.w3mdisplay)) {
|
||||
Image.hasW3MDisplay = true;
|
||||
} else if (options.search !== false) {
|
||||
var file = findFile('/', 'w3mimgdisplay');
|
||||
if (file) {
|
||||
Image.hasW3MDisplay = true;
|
||||
Image.w3mdisplay = file;
|
||||
} else {
|
||||
Image.hasW3MDisplay = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.on('hide', function() {
|
||||
self._lastFile = self.file;
|
||||
self.clearImage();
|
||||
});
|
||||
|
||||
this.on('show', function() {
|
||||
if (!self._lastFile) return;
|
||||
self.setImage(self._lastFile);
|
||||
});
|
||||
|
||||
this.on('detach', function() {
|
||||
self._lastFile = self.file;
|
||||
self.clearImage();
|
||||
});
|
||||
|
||||
this.on('attach', function() {
|
||||
if (!self._lastFile) return;
|
||||
self.setImage(self._lastFile);
|
||||
});
|
||||
|
||||
if (this.options.file || this.options.img) {
|
||||
this.setImage(this.options.file || this.options.img);
|
||||
@ -6822,12 +6909,6 @@ Image.prototype.spawn = function(file, args, opt, callback) {
|
||||
Image.prototype.setImage = function(img, callback) {
|
||||
var self = this;
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
return this._waitForW3MDisplay(function() {
|
||||
return self.setImage(img, callback);
|
||||
});
|
||||
}
|
||||
|
||||
if (Image.hasW3MDisplay === false) {
|
||||
if (!callback) return;
|
||||
return callback(new Error('W3M Image Display not available.'));
|
||||
@ -6840,9 +6921,6 @@ Image.prototype.setImage = function(img, callback) {
|
||||
|
||||
this.file = img;
|
||||
|
||||
// NOTE: Fall back to screen.displayImage() instead of
|
||||
// setImage() if w3mimgdisplay is not present?
|
||||
|
||||
function renderImage(ratio, callback) {
|
||||
// clearImage unsets these:
|
||||
var _file = self.file;
|
||||
@ -6953,12 +7031,6 @@ Image.prototype.setImage = function(img, callback) {
|
||||
Image.prototype.clearImage = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
return this._waitForW3MDisplay(function() {
|
||||
return self.clearImage(callback);
|
||||
});
|
||||
}
|
||||
|
||||
if (Image.hasW3MDisplay === false) {
|
||||
if (!callback) return;
|
||||
return callback(new Error('W3M Image Display not available.'));
|
||||
@ -7007,12 +7079,6 @@ Image.prototype.imageSize = function(callback) {
|
||||
var self = this;
|
||||
var img = this.file;
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
return this._waitForW3MDisplay(function() {
|
||||
return self.imageSize(callback);
|
||||
});
|
||||
}
|
||||
|
||||
if (Image.hasW3MDisplay === false) {
|
||||
if (!callback) return;
|
||||
return callback(new Error('W3M Image Display not available.'));
|
||||
@ -7063,12 +7129,6 @@ Image.prototype.imageSize = function(callback) {
|
||||
Image.prototype.termSize = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
return this._waitForW3MDisplay(function() {
|
||||
return self.termSize(callback);
|
||||
});
|
||||
}
|
||||
|
||||
if (Image.hasW3MDisplay === false) {
|
||||
if (!callback) return;
|
||||
return callback(new Error('W3M Image Display not available.'));
|
||||
@ -7099,7 +7159,8 @@ Image.prototype.termSize = function(callback) {
|
||||
if (!callback) return;
|
||||
|
||||
if (!buf.trim()) {
|
||||
// XXX Bug
|
||||
// Bug: w3mimgdisplay will sometimes
|
||||
// output nothing. Try again:
|
||||
return self.termSize(callback);
|
||||
}
|
||||
|
||||
@ -7140,57 +7201,6 @@ Image.prototype.displayImage = function(callback) {
|
||||
return this.screen.displayImage(this.file, callback);
|
||||
};
|
||||
|
||||
Image.prototype._waitForW3MDisplay = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (Image.hasW3MDisplay == null) {
|
||||
if (Image._checkingW3MDisplay) {
|
||||
setTimeout(function() {
|
||||
return self._waitForW3MDisplay(callback);
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
Image._checkingW3MDisplay = true;
|
||||
|
||||
var ps = this.screen.spawn('find',
|
||||
['/', '-name', 'w3mimgdisplay'],
|
||||
{ stdio: ['ignore', 'pipe', 'ignore'] });
|
||||
|
||||
var buf = '';
|
||||
|
||||
ps.stdout.setEncoding('utf8');
|
||||
ps.stdout.on('data', function(data) {
|
||||
buf += data;
|
||||
});
|
||||
|
||||
ps.on('error', function(err) {
|
||||
Image.hasW3MDisplay = false;
|
||||
Image._checkingW3MDisplay = false;
|
||||
if (!callback) return;
|
||||
return callback(err);
|
||||
});
|
||||
|
||||
ps.on('exit', function(code) {
|
||||
if (!buf.trim()) {
|
||||
Image.hasW3MDisplay = false;
|
||||
Image._checkingW3MDisplay = false;
|
||||
if (!callback) return;
|
||||
return callback(new Error('find failed.'));
|
||||
}
|
||||
Image._checkingW3MDisplay = false;
|
||||
buf = buf.trim().split('\n')[0].trim();
|
||||
Image.w3mdisplay = buf;
|
||||
Image.hasW3MDisplay = true;
|
||||
return self._waitForW3MDisplay(callback);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) callback(null, Image.w3mdisplay);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
@ -7263,6 +7273,43 @@ var wideChars = new RegExp('(['
|
||||
+ '\\uffe8-\\uffee'
|
||||
+ '])', 'g');
|
||||
|
||||
function findFile(start, target) {
|
||||
return (function read(dir) {
|
||||
var files, file, stat, out;
|
||||
|
||||
if (dir === '/dev' || dir === '/sys' || dir === '/proc') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
files = fs.readdirSync(dir);
|
||||
} catch (e) {
|
||||
files = [];
|
||||
}
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
file = files[i];
|
||||
|
||||
if (file === target) {
|
||||
return (dir === '/' ? '' : dir) + '/' + file;
|
||||
}
|
||||
|
||||
try {
|
||||
stat = fs.statSync((dir === '/' ? '' : dir) + '/' + file);
|
||||
} catch (e) {
|
||||
stat = null;
|
||||
}
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
out = read((dir === '/' ? '' : dir) + '/' + file);
|
||||
if (out) return out;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
})(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
@ -7,7 +7,10 @@ screen = blessed.screen({
|
||||
});
|
||||
|
||||
// To ensure our w3mimgdisplay search works:
|
||||
blessed.image.w3mdisplay = '/does/not/exist';
|
||||
if (process.argv[2] === 'find') {
|
||||
blessed.image.w3mdisplay = '/does/not/exist';
|
||||
process.argv.length = 2;
|
||||
}
|
||||
|
||||
var file = process.argv[2] || __dirname + '/test-image.png';
|
||||
|
||||
@ -35,6 +38,14 @@ setTimeout(function() {
|
||||
image.rtop = 2;
|
||||
image.rleft = 7;
|
||||
screen.render();
|
||||
setTimeout(function() {
|
||||
image.detach();
|
||||
screen.render();
|
||||
setTimeout(function() {
|
||||
screen.append(image);
|
||||
screen.render();
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
}, 5000);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user