Skip to content
This repository has been archived by the owner on Mar 4, 2022. It is now read-only.

Customizable layouts #59

Merged
merged 7 commits into from
Feb 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bin/nodejs-dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ program.option("-e, --eventdelay [ms]",
"Minimum threshold for event loop reporting, default 10ms",
config.BLOCKED_THRESHOLD);

program.option("-i, --interleave, default false",
"Interleave stderr/stdout output",
config.INTERLEAVE);
program.option("-l, --layouts [file]",
"Path to file with layouts",
config.LAYOUTS);

program.option("-p, --port [port]",
"Socket listener port",
Expand Down Expand Up @@ -67,7 +67,7 @@ var dashboard = new Dashboard({
appName: appName,
program: program,
scrollback: program.scrollback,
interleave: program.interleave
layoutsFile: program.layouts
});

server.on("connection", function (socket) {
Expand Down
2 changes: 1 addition & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ module.exports = {
BLOCKED_THRESHOLD: 10,
BLOCKED_THRESHOLD_KEY: pkg.name + "_BLOCKED_THRESHOLD",
SCROLLBACK: 1000,
INTERLEAVE: false
LAYOUTS: ""
};
61 changes: 31 additions & 30 deletions lib/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var EventLoopView = require("./views/eventloop-view");
var MemoryView = require("./views/memory-view");
var CpuView = require("./views/cpu-view");
var HelpView = require("./views/help");
var layouts = require("./layouts");
var generateLayouts = require("./generate-layouts");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file doesn't seem to exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Missed it.


var Dashboard = function Dashboard(options) {
this.options = options || {};
Expand All @@ -25,36 +25,36 @@ var Dashboard = function Dashboard(options) {
};

Dashboard.prototype._createViews = function () {
var config = layouts[0];
this.layouts = generateLayouts(this.options.layoutsFile);
var config = this.layouts[0];

// container prevents stream view scrolling from interfering with side views
this.container = blessed.box();
this.screen.append(this.container);

if (this.options.interleave) {
this.views.stdouterr = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stdout", "stderr"],
color: "light-blue",
layoutConfig: config.stdouterr
});
} else {
this.views.stdout = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stdout"],
color: "green",
layoutConfig: config.stdout
});
this.views.stderr = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stderr"],
color: "red",
layoutConfig: config.stderr
});
}
this.views.stdouterr = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stdout", "stderr"],
color: "light-blue",
layoutConfig: config.stdouterr
});

this.views.stdout = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stdout"],
color: "green",
layoutConfig: config.stdout
});

this.views.stderr = new StreamView({
parent: this.container,
scrollback: this.options.scrollback,
events: ["stderr"],
color: "red",
layoutConfig: config.stderr
});

this.views.cpu = new CpuView({
parent: this.container,
Expand Down Expand Up @@ -82,7 +82,7 @@ Dashboard.prototype._configureKeys = function () {

this.screen.key(["left", "right"], function (ch, key) {
var delta = key.name === "left" ? -1 : 1;
var target = (this.currentLayout + delta + layouts.length) % layouts.length;
var target = (this.currentLayout + delta + this.layouts.length) % this.layouts.length;
this._showLayout(target);
}.bind(this));

Expand Down Expand Up @@ -116,17 +116,18 @@ Dashboard.prototype._showLayout = function (id) {
return;
}
_.each(this.views, function (v) {
v.node.hide();
if (v.setLayout) {
v.setLayout();
}
});
_.each(layouts[id], function (config, viewName) {
_.each(this.layouts[id], function (config, viewName) {
var view = this.views[viewName];
if (!view) {
return;
}
if (view.setLayout) {
view.setLayout(config);
}
view.node.show();
}.bind(this));
this.currentLayout = id;
this.screen.render();
Expand Down
102 changes: 102 additions & 0 deletions lib/default-layout-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use strict";

module.exports = [
[
{
position: {
grow: 3
},
views: [
{
name: "stdout"
},
{
name: "stderr"
}
]
},
{
views: [
{
name: "cpu",
limit: 30
},
{
name: "eventLoop",
limit: 30
},
{
name: "memory",
position: {
size: 15
}
}
]
}
],
[
{
position: {
grow: 3
},
views: [
{
name: "stdouterr"
}
]
},
{
views: [
{
name: "cpu",
limit: 30
},
{
name: "eventLoop",
limit: 30
},
{
name: "memory",
position: {
size: 15
}
}
]
}
],
[
{
views: [
{
name: "cpu",
limit: 30
},
{
name: "eventLoop",
limit: 30
}
]
}
],
[
{
views: [
{
name: "stdout"
},
{
name: "stderr"
}
]
}
],
[
{
views: [
{
name: "stdouterr"
}
]
}
]
];
131 changes: 131 additions & 0 deletions lib/generate-layouts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"use strict";
var _ = require("lodash");
var assert = require("assert");
var path = require("path");
var defaultLayoutConfig = require("./default-layout-config");
var validate = require("jsonschema").validate;
var layoutConfigSchema = require("./layout-config-schema.json");
/* eslint-disable no-magic-numbers */

// Each layout consists of vertical panels, that contains its position and horizontal views.
// Flex-like positions of panels and views defined by 'grow' and 'size' parameters.
// View or panel with 'size' has exactly <size> height or width respectively.
// View or panel with 'grow' fills <grow> part of the residuary space (it works like flex-grow).
// By default, position = { grow: 1 }

var normalizePosition = function (position) {
if (!_.has(position, "grow") && !_.has(position, "size")) {
position = { grow: 1 };
}

return position;
};

var concatPosition = function (position1, position2) {
position1 = normalizePosition(position1);
position2 = normalizePosition(position2);

return {
grow: (position1.grow || 0) + (position2.grow || 0),
size: (position1.size || 0) + (position2.size || 0)
};
};

var getSummaryPosition = function (items) {
return items.map(function (item) { return item.position; })
.reduce(concatPosition, { grow: 0, size: 0 });
};

var getSize = function (parentSize, itemPosition) {
var position = normalizePosition(itemPosition.position);
if (_.has(position, "size")) {
return position.size;
}

// Prevent last growing view from overflowing screen
var round = itemPosition.offset.grow + position.grow === itemPosition.summary.grow ?
Math.floor : Math.ceil;

return round(
(parentSize - itemPosition.summary.size) * position.grow / itemPosition.summary.grow
);
};

var getOffset = function (parentSize, itemPosition) {
return itemPosition.summary.grow ? Math.ceil(
itemPosition.offset.size +
(parentSize - itemPosition.summary.size) * itemPosition.offset.grow / itemPosition.summary.grow
) : 0;
};

var createViewLayout = function (view, viewPosition, panelPosition) {
return {
name: view.name,
limit: view.limit,
getPosition: function (parent) {
return {
width: getSize(parent.width, panelPosition),
height: getSize(parent.height, viewPosition),
left: getOffset(parent.width, panelPosition),
top: getOffset(parent.height, viewPosition)
};
}
};
};

var createPanelLayout = function (panelPosition, views) {
var viewSummaryPosition = getSummaryPosition(views);
var offsetPosition = { size: 0, grow: 0 };

return views.map(function (view) {
var viewPosition = {
summary: viewSummaryPosition,
offset: offsetPosition,
position: view.position
};

offsetPosition = concatPosition(view.position, offsetPosition);

return createViewLayout(view, viewPosition, panelPosition);
});
};

var createLayout = function (panelsConfig) {
var panelSummaryPosition = getSummaryPosition(panelsConfig);
var offsetPosition = { size: 0, grow: 0 };

return panelsConfig.reduce(function (layouts, panelConfig) {
var panelPosition = {
summary: panelSummaryPosition,
offset: offsetPosition,
position: panelConfig.position
};

var viewLayouts = createPanelLayout(panelPosition, panelConfig.views);
_.each(viewLayouts, function (viewLayout) {
layouts[viewLayout.name] = {
getPosition: viewLayout.getPosition,
limit: viewLayout.limit
};
});

offsetPosition = concatPosition(panelConfig.position, offsetPosition);

return layouts;
}, {});
};

module.exports = function generateLayouts(layoutsFile) {
var layoutConfig = defaultLayoutConfig;
if (layoutsFile) {
// eslint-disable-next-line global-require
layoutConfig = require(path.resolve(process.cwd(), layoutsFile));
var validationResult = validate(layoutConfig, layoutConfigSchema);
assert(
validationResult.valid,
"Layout config is invalid:\n\n * " + validationResult.errors.join("\n * ") + "\n"
);
}

return layoutConfig.map(createLayout);
};
Loading