mirror of
https://gitlab.silvrtree.co.uk/martind2000/nci.git
synced 2025-02-10 17:39:15 +00:00
some terminal improvements
This commit is contained in:
parent
e1b1d5e688
commit
8c8b600ab8
@ -50,7 +50,7 @@ steps:
|
||||
- cmd: cat 1.txt 2.txt
|
||||
- shell: /bin/bash
|
||||
cmd: >
|
||||
for i in {1..10}; do
|
||||
for i in {1..1000}; do
|
||||
echo "tick $i";
|
||||
sleep 0.3;
|
||||
done;
|
||||
|
8
db.js
8
db.js
@ -94,6 +94,14 @@ exports.init = function(dbPath, params, callback) {
|
||||
value: function(logLine) {
|
||||
return _(logLine).pick('number', 'text');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: {
|
||||
buildId: 1
|
||||
},
|
||||
value: function(logLine) {
|
||||
return _(logLine).pick('number', 'text');
|
||||
}
|
||||
}
|
||||
],
|
||||
withUniqueId: false
|
||||
|
@ -49,28 +49,27 @@ exports.init = function(app, callback) {
|
||||
}
|
||||
var buildDataResource = app.dataio.resource('build' + buildId);
|
||||
buildDataResource.on('connection', function(client) {
|
||||
var callback = this.async(),
|
||||
buildLogPath = getBuildLogPath(buildId);
|
||||
|
||||
var stream = fs.createReadStream(buildLogPath, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
stream
|
||||
.on('readable', function() {
|
||||
var data = stream.read();
|
||||
while (data) {
|
||||
client.emit('sync', 'data', {lines: [{text: data}]});
|
||||
data = stream.read();
|
||||
var callback = this.async();
|
||||
Steppy(
|
||||
function() {
|
||||
db.logLines.find({
|
||||
start: {buildId: buildId, numberStr: 0},
|
||||
}, this.slot());
|
||||
},
|
||||
function(err, lines) {
|
||||
client.emit('sync', 'data', {lines: lines});
|
||||
this.pass(true);
|
||||
},
|
||||
function(err) {
|
||||
if (err) {
|
||||
logger.error(
|
||||
'error during read log for "' + buildId + '":',
|
||||
err.stack || err
|
||||
);
|
||||
}
|
||||
})
|
||||
.on('end', callback)
|
||||
.on('error', function(err) {
|
||||
logger.error(
|
||||
'Error during read "' + buildLogPath + '":',
|
||||
err.stack || err
|
||||
);
|
||||
});
|
||||
callback();
|
||||
}
|
||||
);
|
||||
});
|
||||
buildDataResourcesHash[buildId] = buildDataResource;
|
||||
};
|
||||
@ -105,16 +104,16 @@ exports.init = function(app, callback) {
|
||||
var buildLogLineNumbersHash = {};
|
||||
|
||||
distributor.on('buildData', function(build, data) {
|
||||
if (!/\n$/.test(data)) {
|
||||
data += '\n';
|
||||
}
|
||||
var lines = data.trim().split('\n'),
|
||||
logLineNumber = buildLogLineNumbersHash[build.id] || 0;
|
||||
|
||||
if (buildLogLineNumbersHash[build.id]) {
|
||||
buildLogLineNumbersHash[build.id]++;
|
||||
} else {
|
||||
buildLogLineNumbersHash[build.id] = 1;
|
||||
}
|
||||
var logLineNumber = buildLogLineNumbersHash[build.id];
|
||||
lines = _(lines).map(function(line, index) {
|
||||
return {
|
||||
number: logLineNumber + index,
|
||||
text: line
|
||||
};
|
||||
});
|
||||
buildLogLineNumbersHash[build.id] = logLineNumber + lines.length;
|
||||
|
||||
var filePath = getBuildLogPath(build.id);
|
||||
|
||||
@ -134,24 +133,23 @@ exports.init = function(app, callback) {
|
||||
|
||||
app.dataio.resource('build' + build.id).clientEmitSync(
|
||||
'data',
|
||||
{lines: [{number: logLineNumber, text: data}]}
|
||||
{lines: lines}
|
||||
);
|
||||
|
||||
// write build logs to db
|
||||
|
||||
db.logLines.put({
|
||||
buildId: build.id,
|
||||
number: logLineNumber,
|
||||
text: data
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
logger.error(
|
||||
'Error during write log line "' + logLineNumber +
|
||||
'" for build "' + build.id + '":',
|
||||
err.stack || err
|
||||
);
|
||||
}
|
||||
});
|
||||
_(lines).each(function(line) {
|
||||
db.logLines.put(_({
|
||||
buildId: build.id,
|
||||
}).extend(line), function(err) {
|
||||
if (err) {
|
||||
logger.error(
|
||||
'Error during write log line "' + logLineNumber +
|
||||
'" for build "' + build.id + '":',
|
||||
err.stack || err
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
callback(null, distributor);
|
||||
|
@ -1,25 +1,63 @@
|
||||
.terminal {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&_header {
|
||||
|
||||
}
|
||||
|
||||
&_code {
|
||||
height: 420px;
|
||||
padding: 8px 12px;
|
||||
box-sizing: border-box;
|
||||
clear: left;
|
||||
height: 500px;
|
||||
min-height: 42px;
|
||||
padding: 15px 0;
|
||||
color: #F1F1F1;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
background-color: #2a2a2a;
|
||||
counter-reset: line-numbering;
|
||||
margin-top: 0;
|
||||
overflow-y: scroll;
|
||||
border: none;
|
||||
color: white;
|
||||
background-color: black;
|
||||
line-height: 0.8;
|
||||
font-size: 13px;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
}
|
||||
|
||||
&_footer {
|
||||
|
||||
&_newline {
|
||||
display: block;
|
||||
margin: 0.8em 0;
|
||||
white-space: pre;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-line {
|
||||
position: relative;
|
||||
padding: 0 15px 0 55px;
|
||||
margin: 0;
|
||||
min-height: 16px;
|
||||
|
||||
&_counter {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
min-width: 40px;
|
||||
margin-left: -33px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: darken(@gray-lighter, 15%);
|
||||
&:before {
|
||||
content: counter(line-numbering);
|
||||
counter-increment: line-numbering;
|
||||
padding-right: 1em;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: @gray-lighter;
|
||||
}
|
||||
}
|
||||
|
||||
&_body {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,6 @@
|
||||
.terminal
|
||||
.terminal_code(ref="code", onScroll=this.onScroll)!= this.state.data
|
||||
pre.terminal_code(ref="code")
|
||||
each row, index in this.state.data
|
||||
.code-line(key=index)
|
||||
span.code-line_counter
|
||||
.code-line_body!= row
|
||||
|
@ -10,48 +10,36 @@ define([
|
||||
], function(_, React, Reflux, terminalStore, ansiUp, template) {
|
||||
var Component = React.createClass({
|
||||
mixins: [Reflux.ListenerMixin],
|
||||
scrollOnData: true,
|
||||
shouldScrollBottom: true,
|
||||
ignoreScrollEvent: false,
|
||||
componentDidMount: function() {
|
||||
this.listenTo(terminalStore, this.updateItems);
|
||||
},
|
||||
ensureScrollPosition: function() {
|
||||
if (this.scrollOnData) {
|
||||
var codeNode = this.refs.code.getDOMNode();
|
||||
this.ignoreScrollEvent = true;
|
||||
codeNode.scrollTop = codeNode.scrollHeight - codeNode.offsetHeight;
|
||||
}
|
||||
},
|
||||
onScroll: function() {
|
||||
if (!this.ignoreScrollEvent) {
|
||||
var codeNode = this.refs.code.getDOMNode();
|
||||
if (codeNode.offsetHeight + codeNode.scrollTop >= codeNode.scrollHeight) {
|
||||
this.scrollOnData = true;
|
||||
} else {
|
||||
this.scrollOnData = false;
|
||||
}
|
||||
}
|
||||
this.ignoreScrollEvent = false;
|
||||
},
|
||||
prepareOutput: function(output) {
|
||||
var text = output.replace(
|
||||
/(.*)\n/gi,
|
||||
'<span class="terminal_code_newline">$1</span>'
|
||||
);
|
||||
return ansiUp.ansi_to_html(text);
|
||||
return output.map(function(row) {
|
||||
return ansiUp.ansi_to_html(row.text);
|
||||
});
|
||||
},
|
||||
componentWillUpdate: function() {
|
||||
var node = this.refs.code.getDOMNode();
|
||||
this.shouldScrollBottom = node.scrollTop + node.offsetHeight >= node.scrollHeight;
|
||||
},
|
||||
componentDidUpdate: function() {
|
||||
if (this.shouldScrollBottom) {
|
||||
var node = this.refs.code.getDOMNode();
|
||||
node.scrollTop = node.scrollHeight;
|
||||
}
|
||||
},
|
||||
updateItems: function(build) {
|
||||
// listen just our console update
|
||||
if (build.buildId === this.props.build) {
|
||||
this.setState({data: this.prepareOutput(build.data)});
|
||||
_.defer(this.ensureScrollPosition);
|
||||
this.ensureScrollPosition();
|
||||
}
|
||||
},
|
||||
render: template,
|
||||
getInitialState: function() {
|
||||
return {
|
||||
data: ''
|
||||
data: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
3
static/js/app/components/terminal/test/index.jade
Normal file
3
static/js/app/components/terminal/test/index.jade
Normal file
@ -0,0 +1,3 @@
|
||||
p 123
|
||||
|
||||
Terminal(lines=this.state.lines)
|
19
static/js/app/components/terminal/test/index.js
Normal file
19
static/js/app/components/terminal/test/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'react',
|
||||
'../terminal',
|
||||
'templates/app/components/terminal/test/index'
|
||||
], function(React, TerminalComponent, template) {
|
||||
template = template.locals({
|
||||
Terminal: TerminalComponent
|
||||
});
|
||||
return React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
lines: [1, 2, 3]
|
||||
};
|
||||
},
|
||||
render: template
|
||||
});
|
||||
});
|
@ -16,7 +16,7 @@ define([
|
||||
|
||||
onReadTerminalOutput: function(build) {
|
||||
var self = this,
|
||||
output = '',
|
||||
output = [],
|
||||
resourceName = 'build' + build.id;
|
||||
|
||||
var connectToBuildDataResource = function() {
|
||||
@ -29,7 +29,7 @@ define([
|
||||
}
|
||||
|
||||
connect.resource(resourceName).subscribe('data', function(data) {
|
||||
output += _(data.lines).pluck('text').join('');
|
||||
output = output.concat(data.lines);
|
||||
|
||||
self.trigger({
|
||||
buildId: build.id,
|
||||
|
Loading…
Reference in New Issue
Block a user