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 5 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
223 changes: 223 additions & 0 deletions lib/generate-layouts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
"use strict";
var _ = require("lodash");
var fs = require("fs");
/* 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 DEFAULT_LAYOUT_CONFIG = [
Copy link
Contributor

Choose a reason for hiding this comment

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

The default config should live in a separate file, say ./layouts/default.js. Do you see any problem with using js instead of json?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, js is better here. I replaced fs with require - not completely sure if I require there in a right way, but it seems to work.

[
{
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"
}
]
}
]
];

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 = DEFAULT_LAYOUT_CONFIG;
if (layoutsFile) {
var json = fs.readFileSync(layoutsFile, { encoding: "utf8" });
layoutConfig = JSON.parse(json);
}

return layoutConfig.map(createLayout);
};
Loading