Hass.io: Show ANSI color codes in logs (#2155)

* Hass.io: Show ANSI color codes in logs

* Use innerHTML and color classes

* Refactor ANSI function

* Readability

* Make green text black in supervisor logs

* Use assigning while loop
This commit is contained in:
Otto Winter 2018-12-02 10:26:32 +01:00 committed by Paulus Schoutsen
parent f3b8d66f4f
commit f7e3f4a828
3 changed files with 230 additions and 11 deletions

View File

@ -2,6 +2,7 @@ import "@polymer/paper-button/paper-button";
import "@polymer/paper-card/paper-card";
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { ANSI_HTML_STYLE, parseTextToColoredPre } from "../ansi-to-html";
import "../../../src/resources/ha-style";
@ -15,10 +16,13 @@ class HassioAddonLogs extends PolymerElement {
}
pre {
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
${ANSI_HTML_STYLE}
<paper-card heading="Log">
<div class="card-content"><pre>[[log]]</pre></div>
<div class="card-content" id="content"></div>
<div class="card-actions">
<paper-button on-click="refresh">Refresh</paper-button>
</div>
@ -33,7 +37,6 @@ class HassioAddonLogs extends PolymerElement {
type: String,
observer: "addonSlugChanged",
},
log: String,
};
}
@ -51,8 +54,11 @@ class HassioAddonLogs extends PolymerElement {
refresh() {
this.hass
.callApi("get", `hassio/addons/${this.addonSlug}/logs`)
.then((info) => {
this.log = info;
.then((text) => {
while (this.$.content.lastChild) {
this.$.content.removeChild(this.$.content.lastChild);
}
this.$.content.appendChild(parseTextToColoredPre(text));
});
}
}

203
hassio/src/ansi-to-html.js Normal file
View File

@ -0,0 +1,203 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
export const ANSI_HTML_STYLE = html`
<style>
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
.underline {
text-decoration: underline;
}
.strikethrough {
text-decoration: line-through;
}
.underline.strikethrough {
text-decoration: underline line-through;
}
.fg-red {
color: rgb(222, 56, 43);
}
.fg-green {
color: rgb(57, 181, 74);
}
.fg-yellow {
color: rgb(255, 199, 6);
}
.fg-blue {
color: rgb(0, 111, 184);
}
.fg-magenta {
color: rgb(118, 38, 113);
}
.fg-cyan {
color: rgb(44, 181, 233);
}
.fg-white {
color: rgb(204, 204, 204);
}
.bg-black {
background-color: rgb(0, 0, 0);
}
.bg-red {
background-color: rgb(222, 56, 43);
}
.bg-green {
background-color: rgb(57, 181, 74);
}
.bg-yellow {
background-color: rgb(255, 199, 6);
}
.bg-blue {
background-color: rgb(0, 111, 184);
}
.bg-magenta {
background-color: rgb(118, 38, 113);
}
.bg-cyan {
background-color: rgb(44, 181, 233);
}
.bg-white {
background-color: rgb(204, 204, 204);
}
</style>
`;
export function parseTextToColoredPre(text) {
const pre = document.createElement("pre");
const re = /\033(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g;
let i = 0;
const state = {
bold: false,
italic: false,
underline: false,
strikethrough: false,
foregroundColor: null,
backgroundColor: null,
};
const addSpan = (content) => {
const span = document.createElement("span");
if (state.bold) span.classList.add("bold");
if (state.italic) span.classList.add("italic");
if (state.underline) span.classList.add("underline");
if (state.strikethrough) span.classList.add("strikethrough");
if (state.foregroundColor !== null)
span.classList.add(`fg-${state.foregroundColor}`);
if (state.backgroundColor !== null)
span.classList.add(`bg-${state.backgroundColor}`);
span.appendChild(document.createTextNode(content));
pre.appendChild(span);
};
/* eslint-disable no-cond-assign */
let match;
while ((match = re.exec(text)) !== null) {
const j = match.index;
addSpan(text.substring(i, j));
i = j + match[0].length;
if (match[1] === undefined) continue;
for (const colorCode of match[1].split(";")) {
switch (parseInt(colorCode)) {
case 0:
// reset
state.bold = false;
state.italic = false;
state.underline = false;
state.strikethrough = false;
state.foregroundColor = null;
state.backgroundColor = null;
break;
case 1:
state.bold = true;
break;
case 3:
state.italic = true;
break;
case 4:
state.underline = true;
break;
case 9:
state.strikethrough = true;
break;
case 22:
state.bold = false;
break;
case 23:
state.italic = false;
break;
case 24:
state.underline = false;
break;
case 29:
state.strikethrough = false;
break;
case 30:
// foreground black
state.foregroundColor = null;
break;
case 31:
state.foregroundColor = "red";
break;
case 32:
state.foregroundColor = "green";
break;
case 33:
state.foregroundColor = "yellow";
break;
case 34:
state.foregroundColor = "blue";
break;
case 35:
state.foregroundColor = "magenta";
break;
case 36:
state.foregroundColor = "cyan";
break;
case 37:
state.foregroundColor = "white";
break;
case 39:
// foreground reset
state.foregroundColor = null;
break;
case 40:
state.backgroundColor = "black";
break;
case 41:
state.backgroundColor = "red";
break;
case 42:
state.backgroundColor = "green";
break;
case 43:
state.backgroundColor = "yellow";
break;
case 44:
state.backgroundColor = "blue";
break;
case 45:
state.backgroundColor = "magenta";
break;
case 46:
state.backgroundColor = "cyan";
break;
case 47:
state.backgroundColor = "white";
break;
case 49:
// background reset
state.backgroundColor = null;
break;
}
}
}
addSpan(text.substring(i));
return pre;
}

View File

@ -2,6 +2,7 @@ import "@polymer/paper-button/paper-button";
import "@polymer/paper-card/paper-card";
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { ANSI_HTML_STYLE, parseTextToColoredPre } from "../ansi-to-html";
class HassioSupervisorLog extends PolymerElement {
static get template() {
@ -12,12 +13,18 @@ class HassioSupervisorLog extends PolymerElement {
}
pre {
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: break-word;
}
.fg-green {
color: var(--primary-text-color) !important;
}
</style>
${ANSI_HTML_STYLE}
<paper-card>
<div class="card-content"><pre>[[log]]</pre></div>
<div class="card-content" id="content"></div>
<div class="card-actions">
<paper-button on-click="refreshTapped">Refresh</paper-button>
<paper-button on-click="refresh">Refresh</paper-button>
</div>
</paper-card>
`;
@ -26,7 +33,6 @@ class HassioSupervisorLog extends PolymerElement {
static get properties() {
return {
hass: Object,
log: String,
};
}
@ -37,16 +43,20 @@ class HassioSupervisorLog extends PolymerElement {
loadData() {
this.hass.callApi("get", "hassio/supervisor/logs").then(
(info) => {
this.log = info;
(text) => {
while (this.$.content.lastChild) {
this.$.content.removeChild(this.$.content.lastChild);
}
this.$.content.appendChild(parseTextToColoredPre(text));
},
() => {
this.log = "Error fetching logs";
this.$.content.innerHTML =
'<span class="fg-red bold">Error fetching logs</span>';
}
);
}
refreshTapped() {
refresh() {
this.loadData();
}
}