console-toolkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +92 -0
  3. package/package.json +59 -0
  4. package/src/alphanumeric/arrows.js +31 -0
  5. package/src/alphanumeric/fractions.js +82 -0
  6. package/src/alphanumeric/number-formatters.js +131 -0
  7. package/src/alphanumeric/roman.js +86 -0
  8. package/src/alphanumeric/unicode-cultural-numbers.js +68 -0
  9. package/src/alphanumeric/unicode-letters.js +63 -0
  10. package/src/alphanumeric/unicode-numbers.js +75 -0
  11. package/src/alphanumeric/utils.js +44 -0
  12. package/src/ansi/csi.js +67 -0
  13. package/src/ansi/index.js +20 -0
  14. package/src/ansi/sgr-constants.js +99 -0
  15. package/src/ansi/sgr-state.js +398 -0
  16. package/src/ansi/sgr.js +214 -0
  17. package/src/box.js +253 -0
  18. package/src/charts/bars/block-frac-grouped.js +6 -0
  19. package/src/charts/bars/block-frac.js +36 -0
  20. package/src/charts/bars/block-grouped.js +6 -0
  21. package/src/charts/bars/block.js +43 -0
  22. package/src/charts/bars/draw-grouped.js +39 -0
  23. package/src/charts/bars/draw-stacked.js +24 -0
  24. package/src/charts/bars/frac-grouped.js +33 -0
  25. package/src/charts/bars/plain-grouped.js +6 -0
  26. package/src/charts/bars/plain.js +63 -0
  27. package/src/charts/columns/block-frac-grouped.js +6 -0
  28. package/src/charts/columns/block-frac.js +30 -0
  29. package/src/charts/columns/block-grouped.js +6 -0
  30. package/src/charts/columns/block.js +37 -0
  31. package/src/charts/columns/draw-grouped.js +48 -0
  32. package/src/charts/columns/draw-stacked.js +31 -0
  33. package/src/charts/columns/frac-grouped.js +27 -0
  34. package/src/charts/columns/plain-grouped.js +6 -0
  35. package/src/charts/columns/plain.js +39 -0
  36. package/src/charts/themes/default.js +12 -0
  37. package/src/charts/themes/rainbow-reversed.js +5 -0
  38. package/src/charts/themes/rainbow.js +8 -0
  39. package/src/charts/utils.js +75 -0
  40. package/src/draw-block-frac.js +33 -0
  41. package/src/draw-block.js +55 -0
  42. package/src/meta.js +41 -0
  43. package/src/output/show.js +40 -0
  44. package/src/output/updater.js +82 -0
  45. package/src/output/writer.js +131 -0
  46. package/src/panel.js +748 -0
  47. package/src/plot/bitmap.js +108 -0
  48. package/src/plot/draw-line.js +26 -0
  49. package/src/plot/draw-rect.js +216 -0
  50. package/src/plot/index.js +24 -0
  51. package/src/plot/to-quads.js +32 -0
  52. package/src/spinner/index.js +8 -0
  53. package/src/spinner/spin.js +51 -0
  54. package/src/spinner/spinner.js +75 -0
  55. package/src/spinner/spinners.js +65 -0
  56. package/src/strings.js +72 -0
  57. package/src/style.js +620 -0
  58. package/src/symbols.js +131 -0
  59. package/src/table/draw-borders.js +87 -0
  60. package/src/table/index.js +7 -0
  61. package/src/table/table.js +330 -0
  62. package/src/themes/blocks/unicode-half.js +9 -0
  63. package/src/themes/blocks/unicode-thin.js +9 -0
  64. package/src/themes/lines/ascii-compact.js +11 -0
  65. package/src/themes/lines/ascii-dots.js +9 -0
  66. package/src/themes/lines/ascii-girder.js +9 -0
  67. package/src/themes/lines/ascii-github.js +11 -0
  68. package/src/themes/lines/ascii-reddit.js +11 -0
  69. package/src/themes/lines/ascii-rounded.js +11 -0
  70. package/src/themes/lines/ascii.js +11 -0
  71. package/src/themes/lines/unicode-bold.js +15 -0
  72. package/src/themes/lines/unicode-rounded.js +15 -0
  73. package/src/themes/lines/unicode.js +15 -0
  74. package/src/themes/utils.js +38 -0
  75. package/src/turtle/draw-line-art.js +46 -0
  76. package/src/turtle/draw-unicode.js +33 -0
  77. package/src/turtle/index.js +12 -0
  78. package/src/turtle/turtle.js +286 -0
package/src/panel.js ADDED
@@ -0,0 +1,748 @@
1
+ import {matchCsi} from './ansi/csi.js';
2
+ import {
3
+ RESET_STATE,
4
+ combineStates,
5
+ commandsToState,
6
+ addCommandsToState,
7
+ stateTransition,
8
+ stateReverseTransition,
9
+ stringifyCommands,
10
+ optimize,
11
+ toState
12
+ } from './ansi/sgr-state.js';
13
+ import Box from './box.js';
14
+ import {addAliases} from './meta.js';
15
+
16
+ export class Panel {
17
+ constructor(width, height) {
18
+ this.box = new Array(height);
19
+ for (let i = 0; i < height; ++i) {
20
+ this.box[i] = new Array(width).fill(null);
21
+ }
22
+ }
23
+
24
+ get width() {
25
+ return this.box.length && this.box[0].length;
26
+ }
27
+
28
+ get height() {
29
+ return this.box.length;
30
+ }
31
+
32
+ static make(s, options) {
33
+ main: for (;;) {
34
+ switch (typeof s) {
35
+ case 'function':
36
+ for (let i = 0; i < 10 && typeof s == 'function'; ++i) s = s();
37
+ if (typeof s == 'function') s = String(s);
38
+ continue main;
39
+ case 'object':
40
+ if (s instanceof Panel) return s.clone();
41
+ if (typeof s?.toPanel == 'function') return s.toPanel(options);
42
+ break;
43
+ }
44
+ s = Box.make(s, options);
45
+ break main;
46
+ }
47
+
48
+ const {emptySymbol = '\x07'} = options || {},
49
+ panel = new Panel(s.width, s.height);
50
+
51
+ for (let i = 0, n = s.height; i < n; ++i) {
52
+ const row = s.box[i],
53
+ panelRow = panel.box[i];
54
+ let start = 0,
55
+ pos = 0,
56
+ state = {};
57
+ matchCsi.lastIndex = 0;
58
+ for (const match of row.matchAll(matchCsi)) {
59
+ const str = [...row.substring(start, match.index)];
60
+ for (let j = 0; j < str.length; ++j) {
61
+ panelRow[pos++] = str[j] === emptySymbol ? null : {symbol: str[j], state};
62
+ }
63
+ start = match.index + match[0].length;
64
+ if (match[3] !== 'm') continue;
65
+ state = addCommandsToState(state, match[1].split(';'));
66
+ }
67
+ const str = [...row.substring(start)];
68
+ for (let j = 0; j < str.length; ++j) {
69
+ panelRow[pos++] = str[j] === emptySymbol ? null : {symbol: str[j], state};
70
+ }
71
+ }
72
+
73
+ return panel;
74
+ }
75
+
76
+ toStrings({emptySymbol = ' ', emptyState = RESET_STATE} = {}) {
77
+ if (!this.height || !this.width) return Box.makeBlank(this.width, this.height);
78
+ emptyState = toState(emptyState);
79
+
80
+ const s = new Array(this.height),
81
+ emptyCell = {symbol: emptySymbol, state: emptyState};
82
+
83
+ for (let i = 0; i < this.height; ++i) {
84
+ const panelRow = this.box[i];
85
+ let row = '',
86
+ initState = {},
87
+ state = initState;
88
+ for (let j = 0; j < this.width; ++j) {
89
+ const cell = panelRow[j] || emptyCell,
90
+ newState = combineStates(state, cell.state),
91
+ commands = stateTransition(state, newState);
92
+ row += stringifyCommands(commands) + cell.symbol;
93
+ state = newState;
94
+ }
95
+ const commands = stateReverseTransition(initState, state);
96
+ s[i] = optimize(row + stringifyCommands(commands), initState);
97
+ }
98
+
99
+ return s;
100
+ }
101
+
102
+ toBox(options) {
103
+ return new Box(this.toStrings(options), true);
104
+ }
105
+
106
+ extract(x, y, width, height) {
107
+ // normalize arguments
108
+
109
+ if (typeof x != 'number') {
110
+ x = y = 0;
111
+ width = this.width;
112
+ height = this.height;
113
+ } else if (typeof y != 'number') {
114
+ y = 0;
115
+ width = this.width;
116
+ height = this.height;
117
+ } else if (typeof width != 'number') {
118
+ width = this.width;
119
+ height = this.height;
120
+ } else if (typeof height != 'number') {
121
+ height = this.height;
122
+ }
123
+
124
+ if (x < 0) x = 0;
125
+ if (x >= this.width) return new Panel(0, 0);
126
+ if (x + width > this.width) {
127
+ width = this.width - x;
128
+ }
129
+
130
+ if (y < 0) y = 0;
131
+ if (y >= this.height) return new Panel(0, 0);
132
+ if (y + height > this.height) {
133
+ height = this.height - y;
134
+ }
135
+
136
+ // create new instance
137
+ const panel = new Panel(width, height);
138
+
139
+ // copy cells
140
+ for (let i = 0; i < height; ++i) {
141
+ const panelRow = panel.box[i],
142
+ row = this.box[y + i];
143
+ for (let j = 0; j < width; ++j) {
144
+ const cell = row[x + j];
145
+ panelRow[j] = cell && {...cell};
146
+ }
147
+ }
148
+
149
+ return panel;
150
+ }
151
+
152
+ clone() {
153
+ return this.extract();
154
+ }
155
+
156
+ copyFrom(x, y, width, height, panel, x1 = 0, y1 = 0) {
157
+ // normalize arguments
158
+
159
+ if (x < 0) x = 0;
160
+ if (x >= this.width) return this;
161
+ if (x + width > this.width) {
162
+ width = this.width - x;
163
+ }
164
+ if (x1 < 0) x1 = 0;
165
+ if (x1 >= panel.width) return this;
166
+ width = Math.min(width, panel.width - x1);
167
+
168
+ if (y < 0) y = 0;
169
+ if (y >= this.height) return this;
170
+ if (y + height > this.height) {
171
+ height = this.height - y;
172
+ }
173
+ if (y1 < 0) y1 = 0;
174
+ if (y1 >= panel.height) return this;
175
+ height = Math.min(height, panel.height - y1);
176
+
177
+ // copy cells
178
+ for (let i = 0; i < height; ++i) {
179
+ const panelRow = panel.box[y1 + i],
180
+ row = this.box[y + i];
181
+ for (let j = 0; j < width; ++j) {
182
+ const cell = panelRow[x1 + j];
183
+ row[x + j] = cell && {...cell};
184
+ }
185
+ }
186
+
187
+ return this;
188
+ }
189
+
190
+ put(x, y, text, emptySymbol = '\x07') {
191
+ if (text instanceof Panel) return this.copyFrom(x, y, text.width, text.height, text);
192
+
193
+ // normalize arguments
194
+
195
+ const box = text instanceof Box ? text : new Box(text);
196
+ let height = box.height;
197
+ if (!height) return this;
198
+
199
+ if (x < 0) x = 0;
200
+ if (x >= this.width) return this;
201
+
202
+ if (y < 0) y = 0;
203
+ if (y >= this.height) return this;
204
+ if (y + height > this.height) height = this.height - y;
205
+
206
+ // copy characters
207
+ let state = {};
208
+ for (let i = 0; i < height; ++i) {
209
+ const row = this.box[y + i],
210
+ s = box.box[i];
211
+ let start = 0,
212
+ pos = 0;
213
+ matchCsi.lastIndex = 0;
214
+ for (const match of s.matchAll(matchCsi)) {
215
+ const str = [...s.substring(start, match.index)];
216
+ for (let j = 0; j < str.length; ++j, ++pos) {
217
+ if (x + pos >= row.length) break;
218
+ const cell = row[x + pos];
219
+ row[x + pos] =
220
+ str[j] === emptySymbol ? null : {symbol: str[j], state: cell ? combineStates(cell.state, state) : state};
221
+ }
222
+ start = match.index + match[0].length;
223
+ if (match[3] !== 'm') continue;
224
+ state = addCommandsToState(state, match[1].split(';'));
225
+ }
226
+ const str = [...s.substring(start)];
227
+ for (let j = 0; j < str.length; ++j, ++pos) {
228
+ if (x + pos >= row.length) break;
229
+ const cell = row[x + pos];
230
+ row[x + pos] =
231
+ str[j] === emptySymbol ? null : {symbol: str[j], state: cell ? combineStates(cell.state, state) : state};
232
+ }
233
+ if (x + pos < row.length) {
234
+ const cell = row[x + pos];
235
+ if (cell) row[x + pos] = {symbol: cell.symbol, state: combineStates(state, cell.state)};
236
+ }
237
+ }
238
+
239
+ return this;
240
+ }
241
+
242
+ applyFn(x, y, width, height, fn) {
243
+ // normalize arguments
244
+
245
+ if (typeof x == 'function') {
246
+ fn = x;
247
+ x = 0;
248
+ y = 0;
249
+ width = this.width;
250
+ height = this.height;
251
+ } else {
252
+ if (x < 0) x = 0;
253
+ if (x >= this.width) return this;
254
+ if (x + width > this.width) {
255
+ width = this.width - x;
256
+ }
257
+ if (y < 0) y = 0;
258
+ if (y >= this.height) return this;
259
+ if (y + height > this.height) {
260
+ height = this.height - y;
261
+ }
262
+ }
263
+
264
+ // fill cells
265
+ for (let i = 0; i < height; ++i) {
266
+ const row = this.box[y + i];
267
+ for (let j = 0; j < width; ++j) {
268
+ const cell = row[x + j],
269
+ newCell = fn(x + j, y + i, cell);
270
+ if (newCell !== undefined) row[x + j] = newCell;
271
+ }
272
+ }
273
+
274
+ return this;
275
+ }
276
+
277
+ fill(x, y, width, height, symbol, state = {}) {
278
+ if (typeof x === 'string') {
279
+ symbol = x;
280
+ state = y || {};
281
+ y = 0;
282
+ x = 0;
283
+ width = this.width;
284
+ height = this.height;
285
+ }
286
+ if (typeof state == 'string') {
287
+ state = commandsToState(state.split(';'));
288
+ } else if (Array.isArray(state)) {
289
+ state = commandsToState(state);
290
+ } else {
291
+ state = toState(state);
292
+ }
293
+ return this.applyFn(x, y, width, height, () => ({symbol, state}));
294
+ }
295
+
296
+ fillState(x, y, width, height, options) {
297
+ if (typeof x === 'object') {
298
+ options = x;
299
+ x = 0;
300
+ y = 0;
301
+ width = this.width;
302
+ height = this.height;
303
+ }
304
+ let {state = {}, emptySymbol = ' '} = options || {};
305
+ if (typeof state == 'string') {
306
+ state = commandsToState(state.split(';'));
307
+ } else if (Array.isArray(state)) {
308
+ state = commandsToState(state);
309
+ } else {
310
+ state = toState(state);
311
+ }
312
+ return this.applyFn(x, y, width, height, (x, y, cell) => ({symbol: cell ? cell.symbol : emptySymbol, state}));
313
+ }
314
+
315
+ fillNonEmptyState(x, y, width, height, options) {
316
+ if (typeof x === 'object') {
317
+ options = x;
318
+ x = 0;
319
+ y = 0;
320
+ width = this.width;
321
+ height = this.height;
322
+ }
323
+ let {state = {}} = options || {};
324
+ if (typeof state == 'string') {
325
+ state = commandsToState(state.split(';'));
326
+ } else if (Array.isArray(state)) {
327
+ state = commandsToState(state);
328
+ } else {
329
+ state = toState(state);
330
+ }
331
+ return this.applyFn(x, y, width, height, (x, y, cell) => cell && {symbol: cell.symbol, state});
332
+ }
333
+
334
+ combineStateBefore(x, y, width, height, options) {
335
+ if (typeof x === 'object') {
336
+ options = x;
337
+ x = 0;
338
+ y = 0;
339
+ width = this.width;
340
+ height = this.height;
341
+ }
342
+ let {state = {}, emptySymbol = ' ', emptyState = RESET_STATE} = options || {};
343
+ if (typeof state == 'string') {
344
+ state = commandsToState(state.split(';'));
345
+ } else if (Array.isArray(state)) {
346
+ state = commandsToState(state);
347
+ } else {
348
+ state = toState(state);
349
+ }
350
+ return this.applyFn(x, y, width, height, (x, y, cell) =>
351
+ cell
352
+ ? {symbol: cell.symbol, state: combineStates(state, cell.state)}
353
+ : {symbol: emptySymbol, state: combineStates(state, emptyState)}
354
+ );
355
+ }
356
+
357
+ combineStateAfter(x, y, width, height, options) {
358
+ if (typeof x === 'object') {
359
+ options = x;
360
+ x = 0;
361
+ y = 0;
362
+ width = this.width;
363
+ height = this.height;
364
+ }
365
+ let {state = {}, emptySymbol = ' ', emptyState = RESET_STATE} = options || {};
366
+ if (typeof state == 'string') {
367
+ state = commandsToState(state.split(';'));
368
+ } else if (Array.isArray(state)) {
369
+ state = commandsToState(state);
370
+ } else {
371
+ state = toState(state);
372
+ }
373
+ return this.applyFn(x, y, width, height, (x, y, cell) =>
374
+ cell
375
+ ? {symbol: cell.symbol, state: combineStates(cell.state, state)}
376
+ : {symbol: emptySymbol, state: combineStates(emptyState, state)}
377
+ );
378
+ }
379
+
380
+ clear(x, y, width, height) {
381
+ // normalize arguments
382
+ if (typeof x != 'number') {
383
+ x = y = 0;
384
+ width = this.width;
385
+ height = this.height;
386
+ } else if (typeof y != 'number') {
387
+ y = 0;
388
+ width = this.width;
389
+ height = this.height;
390
+ } else if (typeof width != 'number') {
391
+ width = this.width;
392
+ height = this.height;
393
+ } else if (typeof height != 'number') {
394
+ height = this.height;
395
+ }
396
+
397
+ return this.applyFn(x, y, width, height, () => null);
398
+ }
399
+
400
+ padLeft(n) {
401
+ if (n <= 0) return this;
402
+
403
+ for (let i = 0; i < this.height; ++i) {
404
+ const row = this.box[i],
405
+ padding = new Array(n).fill(null);
406
+ this.box[i] = row ? padding.concat(row) : padding;
407
+ }
408
+ return this;
409
+ }
410
+
411
+ padRight(n) {
412
+ if (n <= 0) return this;
413
+
414
+ for (let i = 0; i < this.height; ++i) {
415
+ const row = this.box[i],
416
+ padding = new Array(n).fill(null);
417
+ this.box[i] = row ? row.concat(padding) : padding;
418
+ }
419
+ return this;
420
+ }
421
+
422
+ padLeftRight(n, m) {
423
+ if (n <= 0) return this.padRight(m);
424
+ if (m <= 0) return this.padLeft(n);
425
+
426
+ for (let i = 0; i < this.height; ++i) {
427
+ const row = this.box[i];
428
+ this.box[i] = row ? new Array(n).fill(null).concat(row, new Array(m).fill(null)) : new Array(n + m).fill(null);
429
+ }
430
+ return this;
431
+ }
432
+
433
+ padTop(n) {
434
+ if (n <= 0) return this;
435
+
436
+ const pad = new Array(n);
437
+ for (let i = 0; i < n; ++i) pad[i] = new Array(this.width).fill(null);
438
+ this.box = pad.concat(this.box);
439
+
440
+ return this;
441
+ }
442
+
443
+ padBottom(n) {
444
+ if (n <= 0) return this;
445
+
446
+ const pad = new Array(n);
447
+ for (let i = 0; i < n; ++i) pad[i] = new Array(this.width).fill(null);
448
+ this.box = this.box.concat(pad);
449
+
450
+ return this;
451
+ }
452
+
453
+ padTopBottom(n, m) {
454
+ if (n <= 0) return this.padBottom(m);
455
+ if (m <= 0) return this.padTop(n);
456
+
457
+ const top = new Array(n);
458
+ for (let i = 0; i < n; ++i) {
459
+ top[i] = new Array(this.width).fill(null);
460
+ }
461
+ const bottom = new Array(m);
462
+ for (let i = 0; i < m; ++i) bottom[i] = new Array(this.width).fill(null);
463
+ this.box = top.concat(this.box, bottom);
464
+
465
+ return this;
466
+ }
467
+
468
+ pad(t, r, b, l) {
469
+ // use values according to CSS rules
470
+ if (typeof r != 'number') {
471
+ r = b = l = t;
472
+ } else if (typeof b != 'number') {
473
+ l = r;
474
+ b = t;
475
+ } else if (typeof l != 'number') {
476
+ l = r;
477
+ }
478
+
479
+ return this.padLeftRight(l, r).padTopBottom(t, b);
480
+ }
481
+
482
+ removeColumns(x, n) {
483
+ // normalize arguments
484
+ if (x < 0) {
485
+ if (x + n <= 0) return this;
486
+ n = Math.max(0, x + n);
487
+ x = 0;
488
+ }
489
+ if (n <= 0) return this;
490
+
491
+ for (const row of this.box) row.splice(x, n);
492
+ return this;
493
+ }
494
+
495
+ removeRows(y, n) {
496
+ // normalize arguments
497
+ if (y < 0) {
498
+ if (y + n <= 0) return this;
499
+ n = Math.max(0, y + n);
500
+ y = 0;
501
+ }
502
+ if (n <= 0) return this;
503
+
504
+ this.box.splice(y, n);
505
+ return this;
506
+ }
507
+
508
+ resizeH(newWidth, align = 'right') {
509
+ if (!this.width) return this.padRight(newWidth);
510
+
511
+ const diff = newWidth - this.width;
512
+ switch (align) {
513
+ case 'left':
514
+ case 'l':
515
+ return diff < 0 ? this.removeColumns(0, -diff) : this.padLeft(diff);
516
+ case 'center':
517
+ case 'c': {
518
+ const half = Math.abs(diff) >> 1;
519
+ return diff < 0
520
+ ? this.removeColumns(0, half).removeColumns(newWidth, half + 1)
521
+ : this.padLeft(half).padRight(diff - half);
522
+ }
523
+ }
524
+ // right
525
+ return diff < 0 ? this.removeColumns(newWidth, -diff) : this.padRight(diff);
526
+ }
527
+
528
+ resizeV(newHeight, align = 'bottom') {
529
+ if (!this.height) return this.padBottom(newHeight);
530
+
531
+ const diff = newHeight - this.height;
532
+ switch (align) {
533
+ case 'top':
534
+ case 't':
535
+ return diff < 0 ? this.removeRows(0, -diff) : this.padTop(diff);
536
+ case 'center':
537
+ case 'c': {
538
+ const half = Math.abs(diff) >> 1;
539
+ return diff < 0
540
+ ? this.removeRows(0, half).removeRows(newHeight, half + 1)
541
+ : this.padTop(half).padBottom(diff - half);
542
+ }
543
+ }
544
+ // bottom
545
+ return diff < 0 ? this.removeRows(newHeight, -diff) : this.padBottom(diff);
546
+ }
547
+
548
+ resize(newWidth, newHeight, horizontal = 'right', vertical = 'bottom') {
549
+ if (!this.height) return this.padBottom(newHeight).padRight(newWidth);
550
+
551
+ return newHeight < this.height
552
+ ? this.resizeV(newHeight, vertical).resizeH(newWidth, horizontal)
553
+ : this.resizeH(newWidth, horizontal).resizeV(newHeight, vertical);
554
+ }
555
+
556
+ insertColumns(x, n) {
557
+ // normalize arguments
558
+ if (n <= 0) return this;
559
+ if (x > this.width) x = this.width;
560
+ else if (x < 0) x = 0;
561
+
562
+ for (const row of this.box) row.splice(x, 0, ...new Array(n).fill(null));
563
+ return this;
564
+ }
565
+
566
+ insertRows(y, n) {
567
+ // normalize arguments
568
+ if (n <= 0) return this;
569
+ if (y > this.height) y = this.height;
570
+ else if (y < 0) y = 0;
571
+
572
+ this.box.splice(y, 0, ...new Array(n).fill(null));
573
+ for (let i = 0; i < n; ++i) this.box[y + i] = new Array(this.width).fill(null);
574
+ return this;
575
+ }
576
+
577
+ addBottom(panel, {align = 'left'} = {}) {
578
+ panel = panel.clone();
579
+
580
+ const diff = this.width - panel.width;
581
+
582
+ if (align === 'left' || align === 'l') {
583
+ if (diff >= 0) {
584
+ this.box.splice(this.height, 0, ...panel.box.map(row => row.concat(new Array(diff).fill(null))));
585
+ return this;
586
+ }
587
+ this.box = this.box.map(row => row.concat(new Array(-diff).fill(null))).concat(panel.box);
588
+ return this;
589
+ }
590
+
591
+ if (align === 'right' || align === 'r') {
592
+ if (diff >= 0) {
593
+ this.box.splice(this.height, 0, ...panel.box.map(row => new Array(diff).fill(null).concat(row)));
594
+ return this;
595
+ }
596
+ this.box = this.box.map(row => new Array(-diff).fill(null).concat(row)).concat(panel.box);
597
+ return this;
598
+ }
599
+
600
+ // align === 'center'
601
+
602
+ if (diff >= 0) {
603
+ const half = diff >> 1;
604
+ this.box.splice(
605
+ this.height,
606
+ 0,
607
+ ...panel.box.map(row => new Array(half).fill(null).concat(row, new Array(diff - half).fill(null)))
608
+ );
609
+ return this;
610
+ }
611
+
612
+ const half = -diff >> 1;
613
+ this.box = this.box
614
+ .map(row => new Array(half).fill(null).concat(row, new Array(-diff - half).fill(null)))
615
+ .concat(panel.box);
616
+ return this;
617
+ }
618
+
619
+ addRight(panel, {align = 'top'} = {}) {
620
+ panel = panel.clone();
621
+
622
+ const diff = this.height - panel.height;
623
+
624
+ if (align === 'bottom' || align === 'b') {
625
+ if (diff >= 0) {
626
+ for (let i = 0; i < diff; ++i) {
627
+ this.box[i] = this.box[i].concat(new Array(panel.width).fill(null));
628
+ }
629
+ for (let i = diff; i < this.height; ++i) {
630
+ this.box[i] = this.box[i].concat(panel.box[i - diff]);
631
+ }
632
+ return this;
633
+ }
634
+ const box = new Array(panel.height);
635
+ for (let i = 0, n = -diff; i < n; ++i) {
636
+ box[i] = new Array(this.width).fill(null).concat(panel.box[i]);
637
+ }
638
+ for (let i = -diff; i < panel.height; ++i) {
639
+ box[i] = this.box[i + diff].concat(panel.box[i]);
640
+ }
641
+ this.box = box;
642
+ return this;
643
+ }
644
+
645
+ if (align === 'top' || align === 't') {
646
+ if (diff >= 0) {
647
+ for (let i = 0; i < panel.height; ++i) {
648
+ this.box[i] = this.box[i].concat(panel.box[i]);
649
+ }
650
+ for (let i = panel.height; i < this.height; ++i) {
651
+ this.box[i] = this.box[i].concat(new Array(panel.width).fill(null));
652
+ }
653
+ return this;
654
+ }
655
+ const box = new Array(panel.height);
656
+ for (let i = 0; i < this.height; ++i) {
657
+ box[i] = this.box[i].concat(panel.box[i]);
658
+ }
659
+ for (let i = this.height; i < panel.height; ++i) {
660
+ box[i] = new Array(this.width).fill(null).concat(panel.box[i]);
661
+ }
662
+ this.box = box;
663
+ return this;
664
+ }
665
+
666
+ // align === 'center'
667
+
668
+ if (diff >= 0) {
669
+ const half = diff >> 1;
670
+ for (let i = 0; i < half; ++i) {
671
+ this.box[i] = this.box[i].concat(new Array(panel.width).fill(null));
672
+ }
673
+ for (let i = 0; i < panel.height; ++i) {
674
+ this.box[i + half] = this.box[i + half].concat(panel.box[i]);
675
+ }
676
+ for (let i = panel.height + half; i < this.height; ++i) {
677
+ this.box[i] = this.box[i].concat(new Array(panel.width).fill(null));
678
+ }
679
+ return this;
680
+ }
681
+
682
+ const half = -diff >> 1,
683
+ box = new Array(panel.height);
684
+ for (let i = 0; i < half; ++i) {
685
+ box[i] = new Array(this.width).fill(null).concat(panel.box[i]);
686
+ }
687
+ for (let i = 0; i < this.height; ++i) {
688
+ box[i + half] = this.box[i].concat(panel.box[i + half]);
689
+ }
690
+ for (let i = this.height + half; i < panel.height; ++i) {
691
+ box[i] = new Array(this.width).fill(null).concat(panel.box[i]);
692
+ }
693
+ this.box = box;
694
+ return this;
695
+ }
696
+
697
+ transpose() {
698
+ const panel = new Panel(this.height, this.width);
699
+ for (let i = 0; i < this.height; ++i) {
700
+ const row = this.box[i];
701
+ for (let j = 0; j < this.width; ++j) {
702
+ const cell = row[j];
703
+ panel.box[j][i] = cell && {...cell};
704
+ }
705
+ }
706
+ return panel;
707
+ }
708
+
709
+ rotateRight() {
710
+ const panel = new Panel(this.height, this.width);
711
+ for (let i = 0; i < this.height; ++i) {
712
+ const row = this.box[i];
713
+ for (let j = 0; j < this.width; ++j) {
714
+ const cell = row[j];
715
+ panel.box[j][this.height - i - 1] = cell && {...cell};
716
+ }
717
+ }
718
+ return panel;
719
+ }
720
+
721
+ rotateLeft() {
722
+ const panel = new Panel(this.height, this.width);
723
+ for (let i = 0; i < this.height; ++i) {
724
+ const row = this.box[i];
725
+ for (let j = 0; j < this.width; ++j) {
726
+ const cell = row[j];
727
+ panel.box[this.width - j - 1][i] = cell && {...cell};
728
+ }
729
+ }
730
+ return panel;
731
+ }
732
+
733
+ flipH() {
734
+ for (const row of this.box) row.reverse();
735
+ return this;
736
+ }
737
+
738
+ flipV() {
739
+ this.box.reverse();
740
+ return this;
741
+ }
742
+ }
743
+
744
+ addAliases(Panel, {combineState: 'combineStateAfter', toPanel: 'clone'});
745
+
746
+ export const toPanel = Panel.make;
747
+
748
+ export default Panel;