1 | export class ColoredConsole {
|
2 | constructor(targetElement) {
|
3 | this.targetElement = targetElement;
|
4 | this.state = {
|
5 | bold: false,
|
6 | italic: false,
|
7 | underline: false,
|
8 | strikethrough: false,
|
9 | foregroundColor: null,
|
10 | backgroundColor: null,
|
11 | carriageReturn: false,
|
12 | secret: false,
|
13 | };
|
14 | }
|
15 | logs() {
|
16 | return this.targetElement.innerText;
|
17 | }
|
18 | addLine(line) {
|
19 |
|
20 | const re = /(?:\033|\\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g;
|
21 | let i = 0;
|
22 | if (this.state.carriageReturn) {
|
23 | if (line !== "\n") {
|
24 |
|
25 | this.targetElement.removeChild(this.targetElement.lastChild);
|
26 | }
|
27 | this.state.carriageReturn = false;
|
28 | }
|
29 | if (line.includes("\r")) {
|
30 | this.state.carriageReturn = true;
|
31 | }
|
32 | const lineSpan = document.createElement("span");
|
33 | lineSpan.classList.add("line");
|
34 | this.targetElement.appendChild(lineSpan);
|
35 | const addSpan = (content) => {
|
36 | if (content === "")
|
37 | return;
|
38 | const span = document.createElement("span");
|
39 | if (this.state.bold)
|
40 | span.classList.add("log-bold");
|
41 | if (this.state.italic)
|
42 | span.classList.add("log-italic");
|
43 | if (this.state.underline)
|
44 | span.classList.add("log-underline");
|
45 | if (this.state.strikethrough)
|
46 | span.classList.add("log-strikethrough");
|
47 | if (this.state.secret)
|
48 | span.classList.add("log-secret");
|
49 | if (this.state.foregroundColor !== null)
|
50 | span.classList.add(`log-fg-${this.state.foregroundColor}`);
|
51 | if (this.state.backgroundColor !== null)
|
52 | span.classList.add(`log-bg-${this.state.backgroundColor}`);
|
53 | span.appendChild(document.createTextNode(content));
|
54 | lineSpan.appendChild(span);
|
55 | if (this.state.secret) {
|
56 | const redacted = document.createElement("span");
|
57 | redacted.classList.add("log-secret-redacted");
|
58 | redacted.appendChild(document.createTextNode("[redacted]"));
|
59 | lineSpan.appendChild(redacted);
|
60 | }
|
61 | };
|
62 | while (true) {
|
63 | const match = re.exec(line);
|
64 | if (match === null)
|
65 | break;
|
66 | const j = match.index;
|
67 | addSpan(line.substring(i, j));
|
68 | i = j + match[0].length;
|
69 | if (match[1] === undefined)
|
70 | continue;
|
71 | for (const colorCode of match[1].split(";")) {
|
72 | switch (parseInt(colorCode)) {
|
73 | case 0:
|
74 |
|
75 | this.state.bold = false;
|
76 | this.state.italic = false;
|
77 | this.state.underline = false;
|
78 | this.state.strikethrough = false;
|
79 | this.state.foregroundColor = null;
|
80 | this.state.backgroundColor = null;
|
81 | this.state.secret = false;
|
82 | break;
|
83 | case 1:
|
84 | this.state.bold = true;
|
85 | break;
|
86 | case 3:
|
87 | this.state.italic = true;
|
88 | break;
|
89 | case 4:
|
90 | this.state.underline = true;
|
91 | break;
|
92 | case 5:
|
93 | this.state.secret = true;
|
94 | break;
|
95 | case 6:
|
96 | this.state.secret = false;
|
97 | break;
|
98 | case 9:
|
99 | this.state.strikethrough = true;
|
100 | break;
|
101 | case 22:
|
102 | this.state.bold = false;
|
103 | break;
|
104 | case 23:
|
105 | this.state.italic = false;
|
106 | break;
|
107 | case 24:
|
108 | this.state.underline = false;
|
109 | break;
|
110 | case 29:
|
111 | this.state.strikethrough = false;
|
112 | break;
|
113 | case 30:
|
114 | this.state.foregroundColor = "black";
|
115 | break;
|
116 | case 31:
|
117 | this.state.foregroundColor = "red";
|
118 | break;
|
119 | case 32:
|
120 | this.state.foregroundColor = "green";
|
121 | break;
|
122 | case 33:
|
123 | this.state.foregroundColor = "yellow";
|
124 | break;
|
125 | case 34:
|
126 | this.state.foregroundColor = "blue";
|
127 | break;
|
128 | case 35:
|
129 | this.state.foregroundColor = "magenta";
|
130 | break;
|
131 | case 36:
|
132 | this.state.foregroundColor = "cyan";
|
133 | break;
|
134 | case 37:
|
135 | this.state.foregroundColor = "white";
|
136 | break;
|
137 | case 39:
|
138 | this.state.foregroundColor = null;
|
139 | break;
|
140 | case 41:
|
141 | this.state.backgroundColor = "red";
|
142 | break;
|
143 | case 42:
|
144 | this.state.backgroundColor = "green";
|
145 | break;
|
146 | case 43:
|
147 | this.state.backgroundColor = "yellow";
|
148 | break;
|
149 | case 44:
|
150 | this.state.backgroundColor = "blue";
|
151 | break;
|
152 | case 45:
|
153 | this.state.backgroundColor = "magenta";
|
154 | break;
|
155 | case 46:
|
156 | this.state.backgroundColor = "cyan";
|
157 | break;
|
158 | case 47:
|
159 | this.state.backgroundColor = "white";
|
160 | break;
|
161 | case 40:
|
162 | case 49:
|
163 | this.state.backgroundColor = null;
|
164 | break;
|
165 | }
|
166 | }
|
167 | }
|
168 | const atBottom = this.targetElement.scrollTop >
|
169 | this.targetElement.scrollHeight - this.targetElement.offsetHeight - 50;
|
170 | addSpan(line.substring(i));
|
171 |
|
172 | if (atBottom) {
|
173 | this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
174 | }
|
175 | }
|
176 | }
|
177 | export const coloredConsoleStyles = `
|
178 | .log {
|
179 | flex: 1;
|
180 | background-color: #1c1c1c;
|
181 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier,
|
182 | monospace;
|
183 | font-size: 12px;
|
184 | padding: 16px;
|
185 | overflow: auto;
|
186 | line-height: 1.45;
|
187 | border-radius: 3px;
|
188 | white-space: pre-wrap;
|
189 | overflow-wrap: break-word;
|
190 | color: #ddd;
|
191 | }
|
192 |
|
193 | .log-bold {
|
194 | font-weight: bold;
|
195 | }
|
196 | .log-italic {
|
197 | font-style: italic;
|
198 | }
|
199 | .log-underline {
|
200 | text-decoration: underline;
|
201 | }
|
202 | .log-strikethrough {
|
203 | text-decoration: line-through;
|
204 | }
|
205 | .log-underline.log-strikethrough {
|
206 | text-decoration: underline line-through;
|
207 | }
|
208 | .log-secret {
|
209 | -webkit-user-select: none;
|
210 | -moz-user-select: none;
|
211 | -ms-user-select: none;
|
212 | user-select: none;
|
213 | }
|
214 | .log-secret-redacted {
|
215 | opacity: 0;
|
216 | width: 1px;
|
217 | font-size: 1px;
|
218 | }
|
219 | .log-fg-black {
|
220 | color: rgb(128, 128, 128);
|
221 | }
|
222 | .log-fg-red {
|
223 | color: rgb(255, 0, 0);
|
224 | }
|
225 | .log-fg-green {
|
226 | color: rgb(0, 255, 0);
|
227 | }
|
228 | .log-fg-yellow {
|
229 | color: rgb(255, 255, 0);
|
230 | }
|
231 | .log-fg-blue {
|
232 | color: rgb(0, 0, 255);
|
233 | }
|
234 | .log-fg-magenta {
|
235 | color: rgb(255, 0, 255);
|
236 | }
|
237 | .log-fg-cyan {
|
238 | color: rgb(0, 255, 255);
|
239 | }
|
240 | .log-fg-white {
|
241 | color: rgb(187, 187, 187);
|
242 | }
|
243 | .log-bg-black {
|
244 | background-color: rgb(0, 0, 0);
|
245 | }
|
246 | .log-bg-red {
|
247 | background-color: rgb(255, 0, 0);
|
248 | }
|
249 | .log-bg-green {
|
250 | background-color: rgb(0, 255, 0);
|
251 | }
|
252 | .log-bg-yellow {
|
253 | background-color: rgb(255, 255, 0);
|
254 | }
|
255 | .log-bg-blue {
|
256 | background-color: rgb(0, 0, 255);
|
257 | }
|
258 | .log-bg-magenta {
|
259 | background-color: rgb(255, 0, 255);
|
260 | }
|
261 | .log-bg-cyan {
|
262 | background-color: rgb(0, 255, 255);
|
263 | }
|
264 | .log-bg-white {
|
265 | background-color: rgb(255, 255, 255);
|
266 | }
|
267 | `;
|