console-toolkit 1.2.8 → 1.2.10
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.
- package/README.md +70 -25
- package/package.json +26 -6
- package/src/alphanumeric/arrows.d.ts +48 -0
- package/src/alphanumeric/arrows.js +23 -0
- package/src/alphanumeric/fractions.d.ts +65 -0
- package/src/alphanumeric/fractions.js +49 -0
- package/src/alphanumeric/number-formatters.d.ts +91 -0
- package/src/alphanumeric/number-formatters.js +45 -1
- package/src/alphanumeric/roman.d.ts +15 -0
- package/src/alphanumeric/roman.js +12 -0
- package/src/alphanumeric/unicode-cultural-numbers.d.ts +65 -0
- package/src/alphanumeric/unicode-cultural-numbers.js +1 -0
- package/src/alphanumeric/unicode-letters.d.ts +32 -0
- package/src/alphanumeric/unicode-letters.js +8 -0
- package/src/alphanumeric/unicode-numbers.d.ts +44 -0
- package/src/alphanumeric/unicode-numbers.js +21 -0
- package/src/alphanumeric/utils.d.ts +45 -0
- package/src/alphanumeric/utils.js +26 -0
- package/src/ansi/csi.d.ts +141 -0
- package/src/ansi/csi.js +51 -2
- package/src/ansi/index.d.ts +26 -0
- package/src/ansi/sgr-constants.d.ts +173 -0
- package/src/ansi/sgr-state.d.ts +91 -0
- package/src/ansi/sgr-state.js +45 -0
- package/src/ansi/sgr.d.ts +587 -0
- package/src/ansi/sgr.js +426 -6
- package/src/box.d.ts +160 -0
- package/src/box.js +113 -12
- package/src/charts/bars/block-frac-grouped.d.ts +12 -0
- package/src/charts/bars/block-frac-grouped.js +6 -0
- package/src/charts/bars/block-frac.d.ts +34 -0
- package/src/charts/bars/block-frac.js +13 -0
- package/src/charts/bars/block-grouped.d.ts +12 -0
- package/src/charts/bars/block-grouped.js +6 -0
- package/src/charts/bars/block.d.ts +43 -0
- package/src/charts/bars/block.js +13 -0
- package/src/charts/bars/draw-grouped.d.ts +41 -0
- package/src/charts/bars/draw-grouped.js +4 -0
- package/src/charts/bars/draw-stacked.d.ts +47 -0
- package/src/charts/bars/draw-stacked.js +4 -0
- package/src/charts/bars/frac-grouped.d.ts +32 -0
- package/src/charts/bars/frac-grouped.js +13 -0
- package/src/charts/bars/plain-grouped.d.ts +12 -0
- package/src/charts/bars/plain-grouped.js +6 -0
- package/src/charts/bars/plain.d.ts +75 -0
- package/src/charts/bars/plain.js +27 -0
- package/src/charts/columns/block-frac-grouped.d.ts +12 -0
- package/src/charts/columns/block-frac-grouped.js +6 -0
- package/src/charts/columns/block-frac.d.ts +39 -0
- package/src/charts/columns/block-frac.js +13 -0
- package/src/charts/columns/block-grouped.d.ts +12 -0
- package/src/charts/columns/block-grouped.js +6 -0
- package/src/charts/columns/block.d.ts +43 -0
- package/src/charts/columns/block.js +13 -0
- package/src/charts/columns/draw-grouped.d.ts +41 -0
- package/src/charts/columns/draw-grouped.js +4 -0
- package/src/charts/columns/draw-stacked.d.ts +39 -0
- package/src/charts/columns/draw-stacked.js +4 -0
- package/src/charts/columns/frac-grouped.d.ts +37 -0
- package/src/charts/columns/frac-grouped.js +13 -0
- package/src/charts/columns/plain-grouped.d.ts +12 -0
- package/src/charts/columns/plain-grouped.js +6 -0
- package/src/charts/columns/plain.d.ts +32 -0
- package/src/charts/columns/plain.js +13 -0
- package/src/charts/themes/default.d.ts +6 -0
- package/src/charts/themes/default.js +1 -0
- package/src/charts/themes/rainbow-reversed.d.ts +6 -0
- package/src/charts/themes/rainbow-reversed.js +2 -1
- package/src/charts/themes/rainbow.d.ts +6 -0
- package/src/charts/themes/rainbow.js +1 -0
- package/src/charts/utils.d.ts +79 -0
- package/src/charts/utils.js +32 -4
- package/src/draw-block-frac.d.ts +16 -0
- package/src/draw-block-frac.js +14 -0
- package/src/draw-block.d.ts +53 -0
- package/src/draw-block.js +25 -1
- package/src/meta.d.ts +84 -0
- package/src/meta.js +64 -0
- package/src/output/show.d.ts +55 -0
- package/src/output/show.js +28 -0
- package/src/output/updater.d.ts +114 -0
- package/src/output/updater.js +58 -4
- package/src/output/writer.d.ts +87 -0
- package/src/output/writer.js +57 -5
- package/src/panel.d.ts +402 -0
- package/src/panel.js +219 -5
- package/src/plot/bitmap.d.ts +80 -0
- package/src/plot/bitmap.js +33 -4
- package/src/plot/draw-line.d.ts +13 -0
- package/src/plot/draw-line.js +8 -0
- package/src/plot/draw-rect.d.ts +13 -0
- package/src/plot/draw-rect.js +38 -30
- package/src/plot/index.d.ts +39 -0
- package/src/plot/index.js +22 -0
- package/src/plot/to-quads.d.ts +10 -0
- package/src/plot/to-quads.js +5 -0
- package/src/spinner/index.d.ts +4 -0
- package/src/spinner/index.js +0 -2
- package/src/spinner/spin.d.ts +13 -0
- package/src/spinner/spin.js +13 -2
- package/src/spinner/spinner.d.ts +69 -0
- package/src/spinner/spinner.js +30 -2
- package/src/spinner/spinners.d.ts +34 -0
- package/src/spinner/spinners.js +23 -9
- package/src/strings/clip.d.ts +21 -0
- package/src/strings/clip.js +10 -0
- package/src/strings/parse.d.ts +23 -0
- package/src/strings/parse.js +7 -0
- package/src/strings/split.d.ts +38 -0
- package/src/strings/split.js +15 -0
- package/src/strings.d.ts +44 -0
- package/src/strings.js +34 -4
- package/src/style.d.ts +462 -0
- package/src/style.js +58 -4
- package/src/symbols.d.ts +167 -0
- package/src/symbols.js +91 -7
- package/src/table/draw-borders.d.ts +38 -0
- package/src/table/draw-borders.js +10 -2
- package/src/table/index.d.ts +8 -0
- package/src/table/index.js +1 -0
- package/src/table/table.d.ts +234 -0
- package/src/table/table.js +59 -1
- package/src/themes/blocks/unicode-half.d.ts +6 -0
- package/src/themes/blocks/unicode-half.js +1 -0
- package/src/themes/blocks/unicode-thin.d.ts +6 -0
- package/src/themes/blocks/unicode-thin.js +1 -0
- package/src/themes/lines/ascii-compact.d.ts +6 -0
- package/src/themes/lines/ascii-compact.js +1 -0
- package/src/themes/lines/ascii-dots.d.ts +6 -0
- package/src/themes/lines/ascii-dots.js +1 -0
- package/src/themes/lines/ascii-girder.d.ts +6 -0
- package/src/themes/lines/ascii-girder.js +1 -0
- package/src/themes/lines/ascii-github.d.ts +6 -0
- package/src/themes/lines/ascii-github.js +1 -0
- package/src/themes/lines/ascii-reddit.d.ts +6 -0
- package/src/themes/lines/ascii-reddit.js +1 -0
- package/src/themes/lines/ascii-rounded.d.ts +6 -0
- package/src/themes/lines/ascii-rounded.js +1 -0
- package/src/themes/lines/ascii.d.ts +6 -0
- package/src/themes/lines/ascii.js +1 -0
- package/src/themes/lines/unicode-bold.d.ts +6 -0
- package/src/themes/lines/unicode-bold.js +1 -0
- package/src/themes/lines/unicode-rounded.d.ts +6 -0
- package/src/themes/lines/unicode-rounded.js +1 -0
- package/src/themes/lines/unicode.d.ts +6 -0
- package/src/themes/lines/unicode.js +1 -0
- package/src/themes/utils.d.ts +33 -0
- package/src/themes/utils.js +7 -0
- package/src/turtle/draw-line-art.d.ts +19 -0
- package/src/turtle/draw-line-art.js +7 -0
- package/src/turtle/draw-unicode.d.ts +19 -0
- package/src/turtle/draw-unicode.js +8 -0
- package/src/turtle/index.d.ts +21 -0
- package/src/turtle/index.js +8 -0
- package/src/turtle/turtle.d.ts +269 -0
- package/src/turtle/turtle.js +124 -4
package/src/panel.js
CHANGED
|
@@ -15,7 +15,15 @@ import split, {size} from './strings/split.js';
|
|
|
15
15
|
import Box from './box.js';
|
|
16
16
|
import {addAliases} from './meta.js';
|
|
17
17
|
|
|
18
|
+
/** A 2D array of cells, where each cell has a character symbol and an SGR state.
|
|
19
|
+
* Provides methods for manipulation, styling, geometric transforms, and conversion to Box/strings.
|
|
20
|
+
* @see {@link https://github.com/uhop/console-toolkit/wiki/Module:-panel}
|
|
21
|
+
*/
|
|
18
22
|
export class Panel {
|
|
23
|
+
/** Creates an empty Panel of the given dimensions.
|
|
24
|
+
* @param {number} width - Width in columns.
|
|
25
|
+
* @param {number} height - Height in rows.
|
|
26
|
+
*/
|
|
19
27
|
constructor(width, height) {
|
|
20
28
|
this.box = new Array(height);
|
|
21
29
|
for (let i = 0; i < height; ++i) {
|
|
@@ -23,14 +31,21 @@ export class Panel {
|
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
33
|
|
|
34
|
+
/** The width of the panel in columns. */
|
|
26
35
|
get width() {
|
|
27
36
|
return this.box.length && this.box[0].length;
|
|
28
37
|
}
|
|
29
38
|
|
|
39
|
+
/** The height of the panel in rows. */
|
|
30
40
|
get height() {
|
|
31
41
|
return this.box.length;
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
/** Creates a Panel from various input types (Panel, Box, string, string[], function).
|
|
45
|
+
* @param {import('./strings.js').StringsInput} s - Input data.
|
|
46
|
+
* @param {object} [options] - Options passed to Box.make() or put().
|
|
47
|
+
* @returns {Panel}
|
|
48
|
+
*/
|
|
34
49
|
static make(s, options) {
|
|
35
50
|
main: for (;;) {
|
|
36
51
|
switch (typeof s) {
|
|
@@ -52,6 +67,12 @@ export class Panel {
|
|
|
52
67
|
return panel;
|
|
53
68
|
}
|
|
54
69
|
|
|
70
|
+
/** Converts the panel to an array of strings with embedded ANSI escape sequences.
|
|
71
|
+
* @param {object} [options] - Options.
|
|
72
|
+
* @param {string} [options.emptySymbol=' '] - Character for empty cells.
|
|
73
|
+
* @param {object} [options.emptyState] - SGR state for empty cells.
|
|
74
|
+
* @returns {string[]}
|
|
75
|
+
*/
|
|
55
76
|
toStrings(options = {}) {
|
|
56
77
|
if (!this.height || !this.width) return Box.makeBlank(this.width, this.height);
|
|
57
78
|
|
|
@@ -81,10 +102,21 @@ export class Panel {
|
|
|
81
102
|
return s;
|
|
82
103
|
}
|
|
83
104
|
|
|
105
|
+
/** Converts the panel to a Box.
|
|
106
|
+
* @param {object} [options] - Options passed to toStrings().
|
|
107
|
+
* @returns {import('./box.js').Box}
|
|
108
|
+
*/
|
|
84
109
|
toBox(options) {
|
|
85
110
|
return new Box(this.toStrings(options), true);
|
|
86
111
|
}
|
|
87
112
|
|
|
113
|
+
/** Extracts a rectangular region as a new Panel.
|
|
114
|
+
* @param {number} [x=0] - Left column.
|
|
115
|
+
* @param {number} [y=0] - Top row.
|
|
116
|
+
* @param {number} [width] - Width (defaults to panel width).
|
|
117
|
+
* @param {number} [height] - Height (defaults to panel height).
|
|
118
|
+
* @returns {Panel}
|
|
119
|
+
*/
|
|
88
120
|
extract(x, y, width, height) {
|
|
89
121
|
// normalize arguments
|
|
90
122
|
|
|
@@ -131,10 +163,23 @@ export class Panel {
|
|
|
131
163
|
return panel;
|
|
132
164
|
}
|
|
133
165
|
|
|
166
|
+
/** Creates a deep copy of this panel.
|
|
167
|
+
* @returns {Panel}
|
|
168
|
+
*/
|
|
134
169
|
clone() {
|
|
135
170
|
return this.extract();
|
|
136
171
|
}
|
|
137
172
|
|
|
173
|
+
/** Copies cells from another panel into this panel.
|
|
174
|
+
* @param {number} x - Destination x.
|
|
175
|
+
* @param {number} y - Destination y.
|
|
176
|
+
* @param {number} width - Width to copy.
|
|
177
|
+
* @param {number} height - Height to copy.
|
|
178
|
+
* @param {Panel} panel - Source panel.
|
|
179
|
+
* @param {number} [x1=0] - Source x.
|
|
180
|
+
* @param {number} [y1=0] - Source y.
|
|
181
|
+
* @returns {this}
|
|
182
|
+
*/
|
|
138
183
|
copyFrom(x, y, width, height, panel, x1 = 0, y1 = 0) {
|
|
139
184
|
// normalize arguments
|
|
140
185
|
|
|
@@ -169,6 +214,14 @@ export class Panel {
|
|
|
169
214
|
return this;
|
|
170
215
|
}
|
|
171
216
|
|
|
217
|
+
/** Places text onto this panel at the given position. Characters matching `emptySymbol` (default: '\x07' BELL) are treated as empty cells.
|
|
218
|
+
* @param {number} x - Left column.
|
|
219
|
+
* @param {number} y - Top row.
|
|
220
|
+
* @param {import('./strings.js').StringsInput} text - Content to place.
|
|
221
|
+
* @param {object} [options] - Put options.
|
|
222
|
+
* @param {string} [options.emptySymbol='\x07'] - Character treated as an empty cell.
|
|
223
|
+
* @returns {this}
|
|
224
|
+
*/
|
|
172
225
|
put(x, y, text, options = {}) {
|
|
173
226
|
if (text instanceof Panel) return this.copyFrom(x, y, text.width, text.height, text);
|
|
174
227
|
|
|
@@ -228,6 +281,15 @@ export class Panel {
|
|
|
228
281
|
return this;
|
|
229
282
|
}
|
|
230
283
|
|
|
284
|
+
/** Applies a function to each cell in a rectangular region.
|
|
285
|
+
* @param {number|Function} x - Left column, or the function if called with a single argument.
|
|
286
|
+
* @param {number} [y] - Top row.
|
|
287
|
+
* @param {number} [width] - Width.
|
|
288
|
+
* @param {number} [height] - Height.
|
|
289
|
+
* @param {Function} [fn] - Function `(x, y, cell) => newCell`.
|
|
290
|
+
* @param {object} [options] - Options.
|
|
291
|
+
* @returns {this}
|
|
292
|
+
*/
|
|
231
293
|
applyFn(x, y, width, height, fn, options) {
|
|
232
294
|
// normalize arguments
|
|
233
295
|
|
|
@@ -274,6 +336,16 @@ export class Panel {
|
|
|
274
336
|
return this;
|
|
275
337
|
}
|
|
276
338
|
|
|
339
|
+
/** Fills a rectangular region with a symbol and state.
|
|
340
|
+
* @param {number|string} x - Left column, or symbol if filling the entire panel.
|
|
341
|
+
* @param {number} [y] - Top row.
|
|
342
|
+
* @param {number} [width] - Width.
|
|
343
|
+
* @param {number} [height] - Height.
|
|
344
|
+
* @param {string} [symbol] - Fill character.
|
|
345
|
+
* @param {object|string|string[]} [state={}] - SGR state.
|
|
346
|
+
* @param {object} [options] - Options.
|
|
347
|
+
* @returns {this}
|
|
348
|
+
*/
|
|
277
349
|
fill(x, y, width, height, symbol, state = {}, options) {
|
|
278
350
|
if (typeof x === 'string') {
|
|
279
351
|
symbol = x;
|
|
@@ -293,6 +365,14 @@ export class Panel {
|
|
|
293
365
|
return this.applyFn(x, y, width, height, () => ({symbol, state}), options);
|
|
294
366
|
}
|
|
295
367
|
|
|
368
|
+
/** Fills all cells with the given state, using `emptySymbol` for empty cells' symbol.
|
|
369
|
+
* @param {number|object} x - Left column, or options if filling the entire panel.
|
|
370
|
+
* @param {number} [y] - Top row.
|
|
371
|
+
* @param {number} [width] - Width.
|
|
372
|
+
* @param {number} [height] - Height.
|
|
373
|
+
* @param {object} [options] - Options with `state` and `emptySymbol`.
|
|
374
|
+
* @returns {this}
|
|
375
|
+
*/
|
|
296
376
|
fillState(x, y, width, height, options) {
|
|
297
377
|
if (typeof x === 'object') {
|
|
298
378
|
options = x;
|
|
@@ -319,6 +399,14 @@ export class Panel {
|
|
|
319
399
|
);
|
|
320
400
|
}
|
|
321
401
|
|
|
402
|
+
/** Fills state only for non-empty cells in a region.
|
|
403
|
+
* @param {number|object} x - Left column, or options if filling the entire panel.
|
|
404
|
+
* @param {number} [y] - Top row.
|
|
405
|
+
* @param {number} [width] - Width.
|
|
406
|
+
* @param {number} [height] - Height.
|
|
407
|
+
* @param {object} [options] - Options with `state`.
|
|
408
|
+
* @returns {this}
|
|
409
|
+
*/
|
|
322
410
|
fillNonEmptyState(x, y, width, height, options) {
|
|
323
411
|
if (typeof x === 'object') {
|
|
324
412
|
options = x;
|
|
@@ -338,6 +426,14 @@ export class Panel {
|
|
|
338
426
|
return this.applyFn(x, y, width, height, (x, y, cell) => cell && {symbol: cell.symbol, state}, options);
|
|
339
427
|
}
|
|
340
428
|
|
|
429
|
+
/** Combines a state before existing cell states (applied state acts as a base that cells override).
|
|
430
|
+
* @param {number|object} x - Left column, or options if filling the entire panel.
|
|
431
|
+
* @param {number} [y] - Top row.
|
|
432
|
+
* @param {number} [width] - Width.
|
|
433
|
+
* @param {number} [height] - Height.
|
|
434
|
+
* @param {object} [options] - Options with `state`, `emptySymbol`, `emptyState`.
|
|
435
|
+
* @returns {this}
|
|
436
|
+
*/
|
|
341
437
|
combineStateBefore(x, y, width, height, options) {
|
|
342
438
|
if (typeof x === 'object') {
|
|
343
439
|
options = x;
|
|
@@ -367,6 +463,14 @@ export class Panel {
|
|
|
367
463
|
);
|
|
368
464
|
}
|
|
369
465
|
|
|
466
|
+
/** Combines a state after existing cell states (applied state overrides cell properties).
|
|
467
|
+
* @param {number|object} x - Left column, or options if filling the entire panel.
|
|
468
|
+
* @param {number} [y] - Top row.
|
|
469
|
+
* @param {number} [width] - Width.
|
|
470
|
+
* @param {number} [height] - Height.
|
|
471
|
+
* @param {object} [options] - Options with `state`, `emptySymbol`, `emptyState`.
|
|
472
|
+
* @returns {this}
|
|
473
|
+
*/
|
|
370
474
|
combineStateAfter(x, y, width, height, options) {
|
|
371
475
|
if (typeof x === 'object') {
|
|
372
476
|
options = x;
|
|
@@ -396,6 +500,14 @@ export class Panel {
|
|
|
396
500
|
);
|
|
397
501
|
}
|
|
398
502
|
|
|
503
|
+
/** Clears (nullifies) cells in a rectangular region.
|
|
504
|
+
* @param {number} [x=0] - Left column.
|
|
505
|
+
* @param {number} [y=0] - Top row.
|
|
506
|
+
* @param {number} [width] - Width.
|
|
507
|
+
* @param {number} [height] - Height.
|
|
508
|
+
* @param {object} [options] - Options.
|
|
509
|
+
* @returns {this}
|
|
510
|
+
*/
|
|
399
511
|
clear(x, y, width, height, options) {
|
|
400
512
|
// normalize arguments
|
|
401
513
|
if (typeof x != 'number') {
|
|
@@ -416,6 +528,10 @@ export class Panel {
|
|
|
416
528
|
return this.applyFn(x, y, width, height, () => null, options);
|
|
417
529
|
}
|
|
418
530
|
|
|
531
|
+
/** Pads the left side with `n` empty columns.
|
|
532
|
+
* @param {number} n
|
|
533
|
+
* @returns {this}
|
|
534
|
+
*/
|
|
419
535
|
padLeft(n) {
|
|
420
536
|
if (n <= 0) return this;
|
|
421
537
|
|
|
@@ -427,6 +543,10 @@ export class Panel {
|
|
|
427
543
|
return this;
|
|
428
544
|
}
|
|
429
545
|
|
|
546
|
+
/** Pads the right side with `n` empty columns.
|
|
547
|
+
* @param {number} n
|
|
548
|
+
* @returns {this}
|
|
549
|
+
*/
|
|
430
550
|
padRight(n) {
|
|
431
551
|
if (n <= 0) return this;
|
|
432
552
|
|
|
@@ -438,6 +558,11 @@ export class Panel {
|
|
|
438
558
|
return this;
|
|
439
559
|
}
|
|
440
560
|
|
|
561
|
+
/** Pads left and right sides with empty cells.
|
|
562
|
+
* @param {number} n - Left columns.
|
|
563
|
+
* @param {number} m - Right columns.
|
|
564
|
+
* @returns {this}
|
|
565
|
+
*/
|
|
441
566
|
padLeftRight(n, m) {
|
|
442
567
|
if (n <= 0) return this.padRight(m);
|
|
443
568
|
if (m <= 0) return this.padLeft(n);
|
|
@@ -449,6 +574,10 @@ export class Panel {
|
|
|
449
574
|
return this;
|
|
450
575
|
}
|
|
451
576
|
|
|
577
|
+
/** Pads the top with `n` empty rows.
|
|
578
|
+
* @param {number} n
|
|
579
|
+
* @returns {this}
|
|
580
|
+
*/
|
|
452
581
|
padTop(n) {
|
|
453
582
|
if (n <= 0) return this;
|
|
454
583
|
|
|
@@ -459,6 +588,10 @@ export class Panel {
|
|
|
459
588
|
return this;
|
|
460
589
|
}
|
|
461
590
|
|
|
591
|
+
/** Pads the bottom with `n` empty rows.
|
|
592
|
+
* @param {number} n
|
|
593
|
+
* @returns {this}
|
|
594
|
+
*/
|
|
462
595
|
padBottom(n) {
|
|
463
596
|
if (n <= 0) return this;
|
|
464
597
|
|
|
@@ -469,6 +602,11 @@ export class Panel {
|
|
|
469
602
|
return this;
|
|
470
603
|
}
|
|
471
604
|
|
|
605
|
+
/** Pads top and bottom with empty rows.
|
|
606
|
+
* @param {number} n - Top rows.
|
|
607
|
+
* @param {number} m - Bottom rows.
|
|
608
|
+
* @returns {this}
|
|
609
|
+
*/
|
|
472
610
|
padTopBottom(n, m) {
|
|
473
611
|
if (n <= 0) return this.padBottom(m);
|
|
474
612
|
if (m <= 0) return this.padTop(n);
|
|
@@ -484,6 +622,13 @@ export class Panel {
|
|
|
484
622
|
return this;
|
|
485
623
|
}
|
|
486
624
|
|
|
625
|
+
/** Pads the panel using CSS-style shorthand (top, right, bottom, left).
|
|
626
|
+
* @param {number} t - Top (or all sides if only argument).
|
|
627
|
+
* @param {number} [r] - Right.
|
|
628
|
+
* @param {number} [b] - Bottom.
|
|
629
|
+
* @param {number} [l] - Left.
|
|
630
|
+
* @returns {this}
|
|
631
|
+
*/
|
|
487
632
|
pad(t, r, b, l) {
|
|
488
633
|
// use values according to CSS rules
|
|
489
634
|
if (typeof r != 'number') {
|
|
@@ -498,6 +643,11 @@ export class Panel {
|
|
|
498
643
|
return this.padLeftRight(l, r).padTopBottom(t, b);
|
|
499
644
|
}
|
|
500
645
|
|
|
646
|
+
/** Removes `n` columns starting at column `x`.
|
|
647
|
+
* @param {number} x - Start column.
|
|
648
|
+
* @param {number} n - Number of columns to remove.
|
|
649
|
+
* @returns {this}
|
|
650
|
+
*/
|
|
501
651
|
removeColumns(x, n) {
|
|
502
652
|
// normalize arguments
|
|
503
653
|
if (x < 0) {
|
|
@@ -511,6 +661,11 @@ export class Panel {
|
|
|
511
661
|
return this;
|
|
512
662
|
}
|
|
513
663
|
|
|
664
|
+
/** Removes `n` rows starting at row `y`.
|
|
665
|
+
* @param {number} y - Start row.
|
|
666
|
+
* @param {number} n - Number of rows to remove.
|
|
667
|
+
* @returns {this}
|
|
668
|
+
*/
|
|
514
669
|
removeRows(y, n) {
|
|
515
670
|
// normalize arguments
|
|
516
671
|
if (y < 0) {
|
|
@@ -524,6 +679,11 @@ export class Panel {
|
|
|
524
679
|
return this;
|
|
525
680
|
}
|
|
526
681
|
|
|
682
|
+
/** Resizes the panel horizontally.
|
|
683
|
+
* @param {number} newWidth - New width.
|
|
684
|
+
* @param {'left'|'center'|'right'} [align='right'] - Alignment when adding/removing columns.
|
|
685
|
+
* @returns {this}
|
|
686
|
+
*/
|
|
527
687
|
resizeH(newWidth, align = 'right') {
|
|
528
688
|
if (!this.width) return this.padRight(newWidth);
|
|
529
689
|
|
|
@@ -536,7 +696,7 @@ export class Panel {
|
|
|
536
696
|
case 'c': {
|
|
537
697
|
const half = Math.abs(diff) >> 1;
|
|
538
698
|
return diff < 0
|
|
539
|
-
? this.removeColumns(0, half).removeColumns(newWidth,
|
|
699
|
+
? this.removeColumns(0, half).removeColumns(newWidth, -diff - half)
|
|
540
700
|
: this.padLeft(half).padRight(diff - half);
|
|
541
701
|
}
|
|
542
702
|
}
|
|
@@ -544,6 +704,11 @@ export class Panel {
|
|
|
544
704
|
return diff < 0 ? this.removeColumns(newWidth, -diff) : this.padRight(diff);
|
|
545
705
|
}
|
|
546
706
|
|
|
707
|
+
/** Resizes the panel vertically.
|
|
708
|
+
* @param {number} newHeight - New height.
|
|
709
|
+
* @param {'top'|'center'|'bottom'} [align='bottom'] - Alignment when adding/removing rows.
|
|
710
|
+
* @returns {this}
|
|
711
|
+
*/
|
|
547
712
|
resizeV(newHeight, align = 'bottom') {
|
|
548
713
|
if (!this.height) return this.padBottom(newHeight);
|
|
549
714
|
|
|
@@ -556,7 +721,7 @@ export class Panel {
|
|
|
556
721
|
case 'c': {
|
|
557
722
|
const half = Math.abs(diff) >> 1;
|
|
558
723
|
return diff < 0
|
|
559
|
-
? this.removeRows(0, half).removeRows(newHeight,
|
|
724
|
+
? this.removeRows(0, half).removeRows(newHeight, -diff - half)
|
|
560
725
|
: this.padTop(half).padBottom(diff - half);
|
|
561
726
|
}
|
|
562
727
|
}
|
|
@@ -564,6 +729,13 @@ export class Panel {
|
|
|
564
729
|
return diff < 0 ? this.removeRows(newHeight, -diff) : this.padBottom(diff);
|
|
565
730
|
}
|
|
566
731
|
|
|
732
|
+
/** Resizes the panel in both dimensions.
|
|
733
|
+
* @param {number} newWidth - New width.
|
|
734
|
+
* @param {number} newHeight - New height.
|
|
735
|
+
* @param {'left'|'center'|'right'} [horizontal='right'] - Horizontal alignment.
|
|
736
|
+
* @param {'top'|'center'|'bottom'} [vertical='bottom'] - Vertical alignment.
|
|
737
|
+
* @returns {this}
|
|
738
|
+
*/
|
|
567
739
|
resize(newWidth, newHeight, horizontal = 'right', vertical = 'bottom') {
|
|
568
740
|
if (!this.height) return this.padBottom(newHeight).padRight(newWidth);
|
|
569
741
|
|
|
@@ -572,27 +744,45 @@ export class Panel {
|
|
|
572
744
|
: this.resizeH(newWidth, horizontal).resizeV(newHeight, vertical);
|
|
573
745
|
}
|
|
574
746
|
|
|
747
|
+
/** Inserts `n` empty columns at position `x`.
|
|
748
|
+
* @param {number} x - Insert position.
|
|
749
|
+
* @param {number} n - Number of columns.
|
|
750
|
+
* @returns {this}
|
|
751
|
+
*/
|
|
575
752
|
insertColumns(x, n) {
|
|
576
753
|
// normalize arguments
|
|
577
754
|
if (n <= 0) return this;
|
|
578
755
|
if (x > this.width) x = this.width;
|
|
579
756
|
else if (x < 0) x = 0;
|
|
580
757
|
|
|
581
|
-
|
|
758
|
+
const padding = new Array(n).fill(null);
|
|
759
|
+
for (const row of this.box) row.splice(x, 0, ...padding);
|
|
582
760
|
return this;
|
|
583
761
|
}
|
|
584
762
|
|
|
763
|
+
/** Inserts `n` empty rows at position `y`.
|
|
764
|
+
* @param {number} y - Insert position.
|
|
765
|
+
* @param {number} n - Number of rows.
|
|
766
|
+
* @returns {this}
|
|
767
|
+
*/
|
|
585
768
|
insertRows(y, n) {
|
|
586
769
|
// normalize arguments
|
|
587
770
|
if (n <= 0) return this;
|
|
588
771
|
if (y > this.height) y = this.height;
|
|
589
772
|
else if (y < 0) y = 0;
|
|
590
773
|
|
|
591
|
-
|
|
592
|
-
for (let i = 0; i < n; ++i)
|
|
774
|
+
const rows = new Array(n);
|
|
775
|
+
for (let i = 0; i < n; ++i) rows[i] = new Array(this.width).fill(null);
|
|
776
|
+
this.box.splice(y, 0, ...rows);
|
|
593
777
|
return this;
|
|
594
778
|
}
|
|
595
779
|
|
|
780
|
+
/** Appends another panel below this one.
|
|
781
|
+
* @param {Panel} panel - Panel to append.
|
|
782
|
+
* @param {object} [options] - Options.
|
|
783
|
+
* @param {'left'|'center'|'right'} [options.align='left'] - Horizontal alignment.
|
|
784
|
+
* @returns {this}
|
|
785
|
+
*/
|
|
596
786
|
addBottom(panel, {align = 'left'} = {}) {
|
|
597
787
|
panel = panel.clone();
|
|
598
788
|
|
|
@@ -635,6 +825,12 @@ export class Panel {
|
|
|
635
825
|
return this;
|
|
636
826
|
}
|
|
637
827
|
|
|
828
|
+
/** Appends another panel to the right of this one.
|
|
829
|
+
* @param {Panel} panel - Panel to append.
|
|
830
|
+
* @param {object} [options] - Options.
|
|
831
|
+
* @param {'top'|'center'|'bottom'} [options.align='top'] - Vertical alignment.
|
|
832
|
+
* @returns {this}
|
|
833
|
+
*/
|
|
638
834
|
addRight(panel, {align = 'top'} = {}) {
|
|
639
835
|
panel = panel.clone();
|
|
640
836
|
|
|
@@ -713,6 +909,9 @@ export class Panel {
|
|
|
713
909
|
return this;
|
|
714
910
|
}
|
|
715
911
|
|
|
912
|
+
/** Returns a new Panel that is the transpose of this one (rows become columns).
|
|
913
|
+
* @returns {Panel}
|
|
914
|
+
*/
|
|
716
915
|
transpose() {
|
|
717
916
|
const panel = new Panel(this.height, this.width);
|
|
718
917
|
for (let i = 0; i < this.height; ++i) {
|
|
@@ -725,6 +924,9 @@ export class Panel {
|
|
|
725
924
|
return panel;
|
|
726
925
|
}
|
|
727
926
|
|
|
927
|
+
/** Returns a new Panel rotated 90° clockwise.
|
|
928
|
+
* @returns {Panel}
|
|
929
|
+
*/
|
|
728
930
|
rotateRight() {
|
|
729
931
|
const panel = new Panel(this.height, this.width);
|
|
730
932
|
for (let i = 0; i < this.height; ++i) {
|
|
@@ -737,6 +939,9 @@ export class Panel {
|
|
|
737
939
|
return panel;
|
|
738
940
|
}
|
|
739
941
|
|
|
942
|
+
/** Returns a new Panel rotated 90° counter-clockwise.
|
|
943
|
+
* @returns {Panel}
|
|
944
|
+
*/
|
|
740
945
|
rotateLeft() {
|
|
741
946
|
const panel = new Panel(this.height, this.width);
|
|
742
947
|
for (let i = 0; i < this.height; ++i) {
|
|
@@ -749,11 +954,17 @@ export class Panel {
|
|
|
749
954
|
return panel;
|
|
750
955
|
}
|
|
751
956
|
|
|
957
|
+
/** Flips the panel horizontally (mirrors left-right) in place.
|
|
958
|
+
* @returns {this}
|
|
959
|
+
*/
|
|
752
960
|
flipH() {
|
|
753
961
|
for (const row of this.box) row.reverse();
|
|
754
962
|
return this;
|
|
755
963
|
}
|
|
756
964
|
|
|
965
|
+
/** Flips the panel vertically (mirrors top-bottom) in place.
|
|
966
|
+
* @returns {this}
|
|
967
|
+
*/
|
|
757
968
|
flipV() {
|
|
758
969
|
this.box.reverse();
|
|
759
970
|
return this;
|
|
@@ -762,6 +973,9 @@ export class Panel {
|
|
|
762
973
|
|
|
763
974
|
addAliases(Panel, {combineState: 'combineStateAfter', toPanel: 'clone'});
|
|
764
975
|
|
|
976
|
+
/** Alias for `Panel.make()`. Creates a Panel from various input types.
|
|
977
|
+
* @type {typeof Panel.make}
|
|
978
|
+
*/
|
|
765
979
|
export const toPanel = Panel.make;
|
|
766
980
|
|
|
767
981
|
export default Panel;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Box from '../box.js';
|
|
2
|
+
|
|
3
|
+
/** A bitmap for drawing pixels, stored as a compact bit-packed array.
|
|
4
|
+
* @see {@link https://github.com/uhop/console-toolkit/wiki/Module:-plot}
|
|
5
|
+
*/
|
|
6
|
+
export class Bitmap {
|
|
7
|
+
/** Width in pixels. */
|
|
8
|
+
width: number;
|
|
9
|
+
/** Height in pixels. */
|
|
10
|
+
height: number;
|
|
11
|
+
/** Width of each internal block (default: 5). */
|
|
12
|
+
blockWidth: number;
|
|
13
|
+
/** Height of each internal block (default: 5). */
|
|
14
|
+
blockHeight: number;
|
|
15
|
+
/** Number of words per line. */
|
|
16
|
+
lineSize: number;
|
|
17
|
+
/** Number of lines. */
|
|
18
|
+
lineCount: number;
|
|
19
|
+
/** The underlying bit-packed array. */
|
|
20
|
+
bitmap: number[];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param width - Width in pixels.
|
|
24
|
+
* @param height - Height in pixels.
|
|
25
|
+
* @param blockWidth - Width of each internal block (default: 5).
|
|
26
|
+
* @param blockHeight - Height of each internal block (default: 5).
|
|
27
|
+
*/
|
|
28
|
+
constructor(width: number, height: number, blockWidth?: number, blockHeight?: number);
|
|
29
|
+
|
|
30
|
+
/** Verifies that (x, y) is within bounds.
|
|
31
|
+
* @param x - X coordinate.
|
|
32
|
+
* @param y - Y coordinate.
|
|
33
|
+
* @returns This Bitmap.
|
|
34
|
+
* @throws If out of bounds.
|
|
35
|
+
*/
|
|
36
|
+
verifyPos(x: number, y: number): this;
|
|
37
|
+
/** Returns the word index for the given position.
|
|
38
|
+
* @param x - X coordinate.
|
|
39
|
+
* @param y - Y coordinate.
|
|
40
|
+
* @returns The word index.
|
|
41
|
+
*/
|
|
42
|
+
getWordIndex(x: number, y: number): number;
|
|
43
|
+
/** Returns the bit mask for the given position.
|
|
44
|
+
* @param x - X coordinate.
|
|
45
|
+
* @param y - Y coordinate.
|
|
46
|
+
* @returns The bit mask.
|
|
47
|
+
*/
|
|
48
|
+
getWordMask(x: number, y: number): number;
|
|
49
|
+
|
|
50
|
+
/** Gets the bit value at (x, y).
|
|
51
|
+
* @param x - X coordinate.
|
|
52
|
+
* @param y - Y coordinate.
|
|
53
|
+
* @returns Non-zero if the bit is set, 0 otherwise.
|
|
54
|
+
*/
|
|
55
|
+
getBit(x: number, y: number): number;
|
|
56
|
+
/** Sets the bit value at (x, y).
|
|
57
|
+
* @param x - X coordinate.
|
|
58
|
+
* @param y - Y coordinate.
|
|
59
|
+
* @param value - Bit value (default: 1).
|
|
60
|
+
* @returns This Bitmap.
|
|
61
|
+
*/
|
|
62
|
+
setBit(x: number, y: number, value?: number): this;
|
|
63
|
+
/** Alias for `setBit`. */
|
|
64
|
+
set: Bitmap['setBit'];
|
|
65
|
+
|
|
66
|
+
/** Clears the bitmap.
|
|
67
|
+
* @param value - If true, set all bits; if false, clear all (default: false).
|
|
68
|
+
* @returns This Bitmap.
|
|
69
|
+
*/
|
|
70
|
+
clear(value?: boolean): this;
|
|
71
|
+
|
|
72
|
+
/** Converts the bitmap to a Box using the given characters.
|
|
73
|
+
* @param one - Character for set bits (default: full block).
|
|
74
|
+
* @param zero - Character for clear bits (default: space).
|
|
75
|
+
* @returns A Box representation.
|
|
76
|
+
*/
|
|
77
|
+
toBox(one?: string, zero?: string): Box;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default Bitmap;
|
package/src/plot/bitmap.js
CHANGED
|
@@ -2,20 +2,29 @@ import Box from '../box.js';
|
|
|
2
2
|
import {addAlias} from '../meta.js';
|
|
3
3
|
import {fullBlock} from '../symbols.js';
|
|
4
4
|
|
|
5
|
+
/** A bitmap image using a compact bit-packed representation.
|
|
6
|
+
* Supports setting/getting individual bits and converting to a Box for console display.
|
|
7
|
+
*/
|
|
5
8
|
export class Bitmap {
|
|
9
|
+
/** Creates a new Bitmap.
|
|
10
|
+
* @param {number} width - The width in pixels.
|
|
11
|
+
* @param {number} height - The height in pixels.
|
|
12
|
+
* @param {number} [blockWidth=5] - The width of each internal block (blockWidth * blockHeight <= 32).
|
|
13
|
+
* @param {number} [blockHeight=5] - The height of each internal block.
|
|
14
|
+
*/
|
|
6
15
|
constructor(width, height, blockWidth = 5, blockHeight = 5) {
|
|
7
16
|
// parameters check
|
|
8
17
|
width = Math.round(width);
|
|
9
18
|
if (isNaN(width) || width <= 0) throw new Error(`Width should be a positive integer instead of "${width}"`);
|
|
10
19
|
height = Math.round(height);
|
|
11
|
-
if (isNaN(
|
|
20
|
+
if (isNaN(height) || height <= 0) throw new Error(`Height should be a positive integer instead of "${height}"`);
|
|
12
21
|
blockWidth = Math.round(blockWidth);
|
|
13
|
-
if (isNaN(
|
|
22
|
+
if (isNaN(blockWidth) || blockWidth <= 0)
|
|
14
23
|
throw new Error(`Block width should be a positive integer instead of "${blockWidth}"`);
|
|
15
24
|
blockHeight = Math.round(blockHeight);
|
|
16
|
-
if (isNaN(
|
|
25
|
+
if (isNaN(blockHeight) || blockHeight <= 0)
|
|
17
26
|
throw new Error(`Block height should be a positive integer instead of "${blockHeight}"`);
|
|
18
|
-
if (
|
|
27
|
+
if (blockWidth * blockHeight > 32)
|
|
19
28
|
throw new Error("Multiplication of 'blockWidth' and 'blockHeight' should be equal or less than 32");
|
|
20
29
|
|
|
21
30
|
this.width = width;
|
|
@@ -49,6 +58,11 @@ export class Bitmap {
|
|
|
49
58
|
return 1 << (shiftX + this.blockWidth * shiftY);
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
/** Gets the value of a bit at the given position.
|
|
62
|
+
* @param {number} x - The x coordinate.
|
|
63
|
+
* @param {number} y - The y coordinate.
|
|
64
|
+
* @returns {number} Non-zero if the bit is set, 0 otherwise.
|
|
65
|
+
*/
|
|
52
66
|
getBit(x, y) {
|
|
53
67
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return 0;
|
|
54
68
|
const index = this.getWordIndex(x, y),
|
|
@@ -56,6 +70,12 @@ export class Bitmap {
|
|
|
56
70
|
return this.bitmap[index] & mask;
|
|
57
71
|
}
|
|
58
72
|
|
|
73
|
+
/** Sets or clears a bit at the given position.
|
|
74
|
+
* @param {number} x - The x coordinate.
|
|
75
|
+
* @param {number} y - The y coordinate.
|
|
76
|
+
* @param {number} [value=1] - Positive to set, 0 to clear, negative to toggle.
|
|
77
|
+
* @returns {this}
|
|
78
|
+
*/
|
|
59
79
|
setBit(x, y, value = 1) {
|
|
60
80
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return this;
|
|
61
81
|
const index = this.getWordIndex(x, y),
|
|
@@ -65,6 +85,10 @@ export class Bitmap {
|
|
|
65
85
|
return this;
|
|
66
86
|
}
|
|
67
87
|
|
|
88
|
+
/** Clears the entire bitmap.
|
|
89
|
+
* @param {boolean} [value=false] - If true, sets all bits; if false, clears all bits.
|
|
90
|
+
* @returns {this}
|
|
91
|
+
*/
|
|
68
92
|
clear(value) {
|
|
69
93
|
this.bitmap.fill(value ? ~0 : 0);
|
|
70
94
|
return this;
|
|
@@ -81,6 +105,11 @@ export class Bitmap {
|
|
|
81
105
|
// return result;
|
|
82
106
|
// }
|
|
83
107
|
|
|
108
|
+
/** Converts the bitmap to a Box for console display.
|
|
109
|
+
* @param {string} [one=fullBlock] - Character for set bits.
|
|
110
|
+
* @param {string} [zero=' '] - Character for unset bits.
|
|
111
|
+
* @returns {import('../box.js').Box} A Box representation of the bitmap.
|
|
112
|
+
*/
|
|
84
113
|
toBox(one = fullBlock, zero = ' ') {
|
|
85
114
|
const result = [];
|
|
86
115
|
for (let k = 0, kBase = 0; k < this.lineCount; ++k, kBase += this.blockHeight) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {Bitmap} from './bitmap.js';
|
|
2
|
+
|
|
3
|
+
/** Draws a line on a Bitmap using Bresenham's algorithm.
|
|
4
|
+
* @param bmp - The target bitmap.
|
|
5
|
+
* @param x0 - Start X.
|
|
6
|
+
* @param y0 - Start Y.
|
|
7
|
+
* @param x1 - End X.
|
|
8
|
+
* @param y1 - End Y.
|
|
9
|
+
* @param value - Bit value to set (default: 1).
|
|
10
|
+
*/
|
|
11
|
+
export function drawLine(bmp: Bitmap, x0: number, y0: number, x1: number, y1: number, value?: number): void;
|
|
12
|
+
|
|
13
|
+
export default drawLine;
|
package/src/plot/draw-line.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// Drawing lines using Bresenham's line algorithm
|
|
2
2
|
// The canonic version from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
|
|
3
3
|
|
|
4
|
+
/** Draws a line on a Bitmap using Bresenham's line algorithm.
|
|
5
|
+
* @param {import('./bitmap.js').Bitmap} bmp - The bitmap to draw on.
|
|
6
|
+
* @param {number} x0 - Start x coordinate.
|
|
7
|
+
* @param {number} y0 - Start y coordinate.
|
|
8
|
+
* @param {number} x1 - End x coordinate.
|
|
9
|
+
* @param {number} y1 - End y coordinate.
|
|
10
|
+
* @param {number} [value=1] - Bit value: positive to set, 0 to clear, negative to toggle.
|
|
11
|
+
*/
|
|
4
12
|
export const drawLine = (bmp, x0, y0, x1, y1, value = 1) => {
|
|
5
13
|
const dx = Math.abs(x1 - x0),
|
|
6
14
|
sx = x0 < x1 ? 1 : -1,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {Bitmap} from './bitmap.js';
|
|
2
|
+
|
|
3
|
+
/** Draws a filled rectangle on a Bitmap.
|
|
4
|
+
* @param bmp - The target bitmap.
|
|
5
|
+
* @param x0 - First corner x coordinate.
|
|
6
|
+
* @param y0 - First corner y coordinate.
|
|
7
|
+
* @param x1 - Opposite corner x coordinate.
|
|
8
|
+
* @param y1 - Opposite corner y coordinate.
|
|
9
|
+
* @param value - Bit value to set (default: 1).
|
|
10
|
+
*/
|
|
11
|
+
export function drawRect(bmp: Bitmap, x0: number, y0: number, x1: number, y1: number, value?: number): void;
|
|
12
|
+
|
|
13
|
+
export default drawRect;
|