UNPKG

22.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var math = require('@aryth/math');
6var enumDataTypes = require('@typen/enum-data-types');
7var nullish = require('@typen/nullish');
8var readline = require('readline');
9
10function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
12var readline__default = /*#__PURE__*/_interopDefaultLegacy(readline);
13
14const trailZero = num => {
15 if (!num) return '0';
16 const tx = '' + math.roundD2(num);
17 let i = tx.indexOf('.');
18
19 if (!~i) {
20 return tx + '.00';
21 }
22
23 let df = tx.length - i;
24
25 if (df === 3) {
26 return tx;
27 }
28
29 if (df === 2) {
30 return tx + '0';
31 }
32
33 if (df === 1) {
34 return tx + '00';
35 }
36
37 return tx;
38};
39const base3ToScale = (base3, dec) => {
40 if (base3 === 0) return 'B'; //
41
42 if (base3 === 1) return dec ? 'K' : 'KB'; // Kilo
43
44 if (base3 === 2) return dec ? 'M' : 'MB'; // Mega
45
46 if (base3 === 3) return dec ? 'G' : 'GB'; // Giga
47
48 if (base3 === 4) return dec ? 'T' : 'TB'; // Tera
49
50 if (base3 === 5) return dec ? 'P' : 'PB'; // Peta
51
52 if (base3 === 6) return dec ? 'E' : 'EB'; // Exa
53
54 if (base3 === 7) return dec ? 'Z' : 'ZB'; // Zetta
55
56 if (base3 === 8) return dec ? 'Y' : 'YB'; // Yotta
57};
58
59const DEFAULT_SENTENCE = 'progress [{bar}] {progress}% | ETA: {eta}s | {value}/{total}';
60class Layout {
61 /**
62 * @param {object} [options]
63 * @param {number} [options.size] size of the progressbar in chars
64 * @param {string[]|string} [options.char]
65 * @param {string} [options.glue]
66 * @param {string} [options.sentence]
67 * @param {boolean} [options.autoZero = false] autoZero - false
68 * @param {function(State):string} [options.bar]
69 * @param {function(State):string} [options.degree]
70 * @param {function(State,object):string} [options.formatter]
71 */
72 constructor(options) {
73 var _options$size, _options$glue, _options$autoZero, _options$sentence;
74
75 const char = typeof options.char === enumDataTypes.STR ? [options.char, ' '] : Array.isArray(options.char) ? options.char : ['=', '-'];
76 const [x, y] = char;
77 this.size = (_options$size = options.size) !== null && _options$size !== void 0 ? _options$size : 24;
78 this.chars = [x.repeat(this.size + 1), y.repeat(this.size + 1)];
79 this.glue = (_options$glue = options.glue) !== null && _options$glue !== void 0 ? _options$glue : '';
80 this.autoZero = (_options$autoZero = options.autoZero) !== null && _options$autoZero !== void 0 ? _options$autoZero : false; // autoZero - false
81
82 this.sentence = (_options$sentence = options.sentence) !== null && _options$sentence !== void 0 ? _options$sentence : DEFAULT_SENTENCE;
83 if (options.bar) this.bar = options.bar.bind(this);
84 if (options.degree) this.degree = options.degree.bind(this);
85 if (options.formatter) this.formatter = options.formatter.bind(this);
86 }
87
88 static build(options) {
89 return new Layout(options);
90 }
91
92 loadFormatter(formatter) {
93 this.formatter = formatter.bind(this);
94 return this;
95 }
96
97 bar(state) {
98 const {
99 progress
100 } = state;
101 const {
102 chars,
103 glue,
104 size
105 } = this;
106 const lenX = math.round(progress * size),
107 lenY = size - lenX;
108 const [x, y] = chars; // generate bar string by stripping the pre-rendered strings
109
110 return x.slice(0, lenX) + glue + y.slice(0, lenY);
111 }
112
113 get fullBar() {
114 return this.chars[0].slice(0, this.size);
115 }
116
117 get zeroBar() {
118 return this.chars[1].slice(0, this.size);
119 }
120
121 degree(state) {
122 let {
123 value,
124 total
125 } = state;
126 const {
127 base3 = true,
128 decimal = true
129 } = this;
130 if (!base3) return `${math.round(value)}/${total}`;
131 const thousand = decimal ? 1000 : 1024;
132 let base3Level = 0;
133
134 while (total > thousand) {
135 // base3Level <= base3
136 total /= thousand;
137 value /= thousand;
138 base3Level++;
139 }
140
141 const totalText = trailZero(total);
142 const valueText = trailZero(value).padStart(totalText.length); // return { value: valueText, total: totalText, scale: base3ToScale(base3, dec) }
143
144 return `${valueText}/${totalText} ${base3ToScale(base3Level, decimal)}`;
145 }
146 /**
147 *
148 * @param {State|object} state
149 * @returns {string}
150 */
151
152
153 formatter(state) {
154 var _this$sentence;
155
156 return (_this$sentence = this.sentence) === null || _this$sentence === void 0 ? void 0 : _this$sentence.replace(/\{(\w+)\}/g, (match, key) => {
157 if (key === 'bar') return this.bar(state);
158 if (key === 'degree') return this.degree(state);
159 if (key in state) return state[key];
160 const payload = state.payload;
161 if (payload && key in payload) return payload[key];
162 return match;
163 });
164 }
165
166}
167
168class Config {
169 /**
170 *
171 * @param {object} config
172 *
173 * @param {WriteStream} [config.stream = process.stderr] the output stream to write on
174 * @param {number} [config.fps = 12] the max update rate in fps (redraw will only triggered on value change)
175 * @param {Terminal|any} [config.terminal = null] external terminal provided ?
176 * @param {boolean} [config.autoClear = false] clear on finish ?
177 * @param {boolean} [config.autoStop = false] stop on finish ?
178 * @param {boolean} [config.hideCursor = false] hide the cursor ?
179 * @param {boolean} [config.lineWrap = false] allow or disable setLineWrap ?
180 * @param {string} [config.sentence = DEFAULT_FORMAT] the bar sentence
181 * @param {function} [config.formatTime = null] external time-sentence provided ?
182 * @param {function} [config.formatValue = null] external value-sentence provided ?
183 * @param {function} [config.formatBar = null] external bar-sentence provided ?
184 * @param {boolean} [config.syncUpdate = true] allow synchronous updates ?
185 * @param {boolean} [config.noTTYOutput = false] noTTY mode
186 * @param {number} [config.notTTYSchedule = 2000] schedule - 2s
187 * @param {boolean} [config.forceRedraw = false] force bar redraw even if progress did not change
188 *
189 * @param {object} [config.eta] eta config
190 * @param {boolean} [config.eta.on = false] switch to turn on eta
191 * @param {number} [config.eta.capacity = 10] the number of results to average ETA over
192 * @param {boolean} [config.eta.autoUpdate = false] automatic eta updates based on fps
193 * @returns {Config}
194 */
195 constructor(config) {
196 var _config$fps, _config$stream, _config$eta$capacity, _config$eta, _config$eta$autoUpdat, _config$eta2, _config$terminal, _config$autoClear, _config$autoStop, _config$hideCursor, _config$lineWrap, _config$syncUpdate, _config$noTTYOutput, _config$notTTYSchedul, _config$forceRedraw;
197
198 // merge layout
199 // the max update rate in fps (redraw will only triggered on value change)
200 this.throttle = 1000 / ((_config$fps = config.fps) !== null && _config$fps !== void 0 ? _config$fps : 10); // the output stream to write on
201
202 this.stream = (_config$stream = config.stream) !== null && _config$stream !== void 0 ? _config$stream : process.stderr;
203 this.eta = config.eta ? {
204 capacity: (_config$eta$capacity = (_config$eta = config.eta) === null || _config$eta === void 0 ? void 0 : _config$eta.capacity) !== null && _config$eta$capacity !== void 0 ? _config$eta$capacity : 10,
205 // the number of results to average ETA over
206 autoUpdate: (_config$eta$autoUpdat = (_config$eta2 = config.eta) === null || _config$eta2 === void 0 ? void 0 : _config$eta2.autoUpdate) !== null && _config$eta$autoUpdat !== void 0 ? _config$eta$autoUpdat : false // automatic eta updates based on fps
207
208 } : null;
209 this.terminal = (_config$terminal = config.terminal) !== null && _config$terminal !== void 0 ? _config$terminal : null; // external terminal provided ?
210
211 this.autoClear = (_config$autoClear = config.autoClear) !== null && _config$autoClear !== void 0 ? _config$autoClear : false; // clear on finish ?
212
213 this.autoStop = (_config$autoStop = config.autoStop) !== null && _config$autoStop !== void 0 ? _config$autoStop : false; // stop on finish ?
214
215 this.hideCursor = (_config$hideCursor = config.hideCursor) !== null && _config$hideCursor !== void 0 ? _config$hideCursor : false; // hide the cursor ?
216
217 this.lineWrap = (_config$lineWrap = config.lineWrap) !== null && _config$lineWrap !== void 0 ? _config$lineWrap : false; // disable setLineWrap ?
218
219 this.syncUpdate = (_config$syncUpdate = config.syncUpdate) !== null && _config$syncUpdate !== void 0 ? _config$syncUpdate : true; // allow synchronous updates ?
220
221 this.noTTYOutput = (_config$noTTYOutput = config.noTTYOutput) !== null && _config$noTTYOutput !== void 0 ? _config$noTTYOutput : false; // noTTY mode
222
223 this.notTTYSchedule = (_config$notTTYSchedul = config.notTTYSchedule) !== null && _config$notTTYSchedul !== void 0 ? _config$notTTYSchedul : 2000; // schedule - 2s
224
225 this.forceRedraw = (_config$forceRedraw = config.forceRedraw) !== null && _config$forceRedraw !== void 0 ? _config$forceRedraw : false; // force bar redraw even if progress did not change
226
227 return this;
228 }
229
230 static build(config) {
231 return new Config(config);
232 }
233
234}
235
236// ETA calculation
237class ETA {
238 constructor(capacity, initTime, initValue) {
239 // size of eta buffer
240 this.capacity = capacity || 100; // eta buffer with initial values
241
242 this.valueSeries = [initValue];
243 this.timeSeries = [initTime]; // eta time value
244
245 this.eta = '0';
246 } // add new values to calculation buffer
247
248
249 update(time, {
250 value,
251 total
252 }) {
253 this.valueSeries.push(value);
254 this.timeSeries.push(time); // trigger recalculation
255
256 this.calculate(total - value);
257 } // fetch estimated time
258
259
260 get value() {
261 return this.eta;
262 } // eta calculation - request number of remaining events
263
264
265 calculate(remaining) {
266 // get number of samples in eta buffer
267 const consumed = this.valueSeries.length;
268 const gap = Math.min(this.capacity, consumed);
269 const dValue = this.valueSeries[consumed - 1] - this.valueSeries[consumed - gap];
270 const dTime = this.timeSeries[consumed - 1] - this.timeSeries[consumed - gap]; // get progress per ms
271
272 const marginalRate = dValue / dTime; // strip past elements
273
274 this.valueSeries = this.valueSeries.slice(-this.capacity);
275 this.timeSeries = this.timeSeries.slice(-this.capacity); // eq: vt_rate *x = total
276
277 const eta = Math.ceil(remaining / marginalRate / 1000); // check values
278
279 return this.eta = isNaN(eta) ? 'NULL' : !isFinite(eta) ? 'INF' // +/- Infinity --- NaN already handled
280 : eta > 1e7 ? 'INF' // > 10M s ? - set upper display limit ~115days (1e7/60/60/24)
281 : eta < 0 ? 0 // negative ?
282 : eta; // assign
283 }
284
285}
286
287class State {
288 constructor(total, value, payload, eta) {
289 var _eta$capacity;
290
291 this.value = value !== null && value !== void 0 ? value : 0;
292 this.total = total !== null && total !== void 0 ? total : 100;
293 this.start = Date.now(); // store start time for duration+eta calculation
294
295 this.end = null; // reset stop time for 're-start' scenario (used for duration calculation)
296
297 this.calETA = eta ? new ETA((_eta$capacity = eta.capacity) !== null && _eta$capacity !== void 0 ? _eta$capacity : 64, this.start, this.value) : null; // initialize eta buffer
298
299 this.payload = payload !== null && payload !== void 0 ? payload : {};
300 return this;
301 }
302
303 static build(values) {
304 const {
305 total,
306 value,
307 payload,
308 eta
309 } = values;
310 return new State(total, value, payload, eta);
311 }
312
313 initialize(total, value, payload, eta) {
314 return Object.assign(this, new State(total, value, payload, eta));
315 }
316
317 get eta() {
318 return this.calETA.eta;
319 }
320
321 get reachLimit() {
322 return this.value >= this.total;
323 }
324
325 get progress() {
326 const progress = this.value / this.total;
327 return isNaN(progress) ? 0 // this.preset?.autoZero ? 0.0 : 1.0
328 : math.constraint(progress, 0, 1);
329 }
330
331 update(value, payload) {
332 var _this$calETA;
333
334 // if (payload) for (let key in payload) this[key] = payload[key]
335 this.value = value;
336 if (payload) this.payload = payload;
337 (_this$calETA = this.calETA) === null || _this$calETA === void 0 ? void 0 : _this$calETA.update(Date.now(), this); // add new value; recalculate eta
338
339 if (this.reachLimit) this.end = Date.now();
340 return this;
341 }
342
343 stop(value, payload) {
344 this.end = Date.now();
345 if (nullish.valid(value)) this.value = value;
346 if (payload) this.payload = payload;
347 }
348
349 get elapsed() {
350 var _this$end;
351
352 return math.round((((_this$end = this.end) !== null && _this$end !== void 0 ? _this$end : Date.now()) - this.start) / 1000);
353 }
354
355 get percent() {
356 return ~~(this.progress * 100);
357 }
358
359}
360
361class Escape {
362 constructor(conf) {
363 var _conf$ctx, _conf$arg, _conf$instant;
364
365 this.ctx = (_conf$ctx = conf.ctx) !== null && _conf$ctx !== void 0 ? _conf$ctx : {};
366 this.arg = (_conf$arg = conf.arg) !== null && _conf$arg !== void 0 ? _conf$arg : null;
367 this.fn = conf.fn.bind(this.ctx, this.arg);
368 this.instant = (_conf$instant = conf.instant) !== null && _conf$instant !== void 0 ? _conf$instant : true;
369 this.timer = null;
370 this.logs = [];
371 }
372
373 static build(conf) {
374 return new Escape(conf);
375 }
376
377 get active() {
378 return nullish.valid(this.timer);
379 }
380
381 loop(ms) {
382 if (typeof this.timer === enumDataTypes.OBJ) this.stop();
383 if (!this.fn) return void 0;
384 if (this.instant) this.fn();
385 this.timer = setInterval(this.fn, ms);
386 }
387
388 stop() {
389 clearTimeout(this.timer);
390 return this.timer = null;
391 }
392
393}
394
395class Terminal {
396 stream = null;
397 /** @type {boolean} line wrapping enabled */
398
399 lineWrap = true;
400 /** @type {number} current, relative y position */
401
402 dy = 0;
403 /**
404 *
405 * @param {Config|object} configs
406 * @param {node::WriteStream} configs.stream
407 * @param {boolean} [configs.lineWrap]
408 * @param {number} [configs.dy]
409 *
410 */
411
412 constructor(configs) {
413 var _configs$lineWrap, _configs$dy;
414
415 this.stream = configs.stream;
416 this.lineWrap = (_configs$lineWrap = configs.lineWrap) !== null && _configs$lineWrap !== void 0 ? _configs$lineWrap : true;
417 this.dy = (_configs$dy = configs.dy) !== null && _configs$dy !== void 0 ? _configs$dy : 0;
418 } // tty environment ?
419
420
421 get isTTY() {
422 return this.stream.isTTY;
423 } // get terminal width
424
425
426 get width() {
427 var _this$stream$columns;
428
429 return (_this$stream$columns = this.stream.columns) !== null && _this$stream$columns !== void 0 ? _this$stream$columns : this.stream.isTTY ? 80 : 200; // set max width to 80 in tty-mode and 200 in noTTY-mode
430 } // write content to output stream
431 // @TODO use string-width to strip length
432
433
434 write(tx) {
435 this.stream.write(tx); // this.stream.write(this.lineWrap ? tx.slice(0, this.width) : tx)
436 }
437
438 cleanWrite(tx) {
439 this.cursorTo(0, null); // set cursor to start of line
440
441 this.stream.write(tx); // write output
442
443 this.clearRight(); // clear to the right from cursor
444 } // save cursor position + settings
445
446
447 saveCursor() {
448 if (!this.isTTY) return void 0;
449 this.stream.write('\x1B7'); // save position
450 } // restore last cursor position + settings
451
452
453 restoreCursor() {
454 if (!this.isTTY) return void 0;
455 this.stream.write('\x1B8'); // restore cursor
456 } // show/hide cursor
457
458
459 showCursor(enabled) {
460 if (!this.isTTY) return void 0;
461 enabled ? this.stream.write('\x1B[?25h') : this.stream.write('\x1B[?25l');
462 } // change cursor position
463
464
465 cursorTo(x, y) {
466 if (!this.isTTY) return void 0;
467 readline__default["default"].cursorTo(this.stream, x, y); // move cursor absolute
468 } // change relative cursor position
469
470
471 moveCursor(dx, dy) {
472 if (!this.isTTY) return void 0;
473 if (dy) this.dy += dy; // store current position
474
475 if (dx || dy) readline__default["default"].moveCursor(this.stream, dx, dy); // move cursor relative
476 } // reset relative cursor
477
478
479 resetCursor() {
480 if (!this.isTTY) return void 0;
481 readline__default["default"].moveCursor(this.stream, 0, -this.dy); // move cursor to initial line
482
483 readline__default["default"].cursorTo(this.stream, 0, null); // first char
484
485 this.dy = 0; // reset counter
486 } // clear to the right from cursor
487
488
489 clearRight() {
490 if (!this.isTTY) return void 0;
491 readline__default["default"].clearLine(this.stream, 1);
492 } // clear the full line
493
494
495 clearLine() {
496 if (!this.isTTY) return void 0;
497 readline__default["default"].clearLine(this.stream, 0);
498 } // clear everything beyond the current line
499
500
501 clearDown() {
502 if (!this.isTTY) return void 0;
503 readline__default["default"].clearScreenDown(this.stream);
504 } // add new line; increment counter
505
506
507 newline() {
508 this.stream.write('\n');
509 this.dy++;
510 } // control line wrapping
511
512
513 setLineWrap(enabled) {
514 if (!this.isTTY) return void 0;
515 this.lineWrap = enabled; // store state
516
517 enabled ? this.stream.write('\x1B[?7h') : this.stream.write('\x1B[?7l');
518 }
519
520}
521
522class Baro {
523 /** @type {Config|object} store config */
524 config;
525 /** @type {Terminal|object} store terminal instance */
526
527 terminal;
528 /** @type {boolean} progress bar active ? */
529
530 active = false;
531 /** @type {function} use default formatter or custom one ? */
532
533 formatter;
534 /** @type {[State]} */
535
536 states;
537 /**
538 *
539 * @param {Config} config
540 * @param {Layout} layout
541 */
542
543 constructor(config, layout) {
544 var _this$config$terminal;
545
546 this.config = config;
547 this.layout = layout;
548 this.states = [];
549 this.escape = Escape.build({
550 fn: this.#renderStates,
551 ctx: this,
552 arg: this.states
553 });
554 this.terminal = (_this$config$terminal = this.config.terminal) !== null && _this$config$terminal !== void 0 ? _this$config$terminal : new Terminal(this.config);
555 }
556
557 static build(config, layout) {
558 config = Config.build(config);
559 layout = Layout.build(layout);
560 return new Baro(config, layout);
561 }
562
563 get active() {
564 return !!this.escape.timer;
565 }
566
567 get forceRedraw() {
568 return this.config.forceRedraw || this.config.noTTYOutput && !this.terminal.isTTY; // force redraw in noTTY-mode!
569 }
570
571 get noTTY() {
572 return this.config.noTTYOutput && !this.terminal.isTTY;
573 }
574
575 boot() {
576 if (this.config.hideCursor) this.terminal.showCursor(false); // hide the cursor ?
577
578 if (!this.config.lineWrap) this.terminal.setLineWrap(false); // disable line wrapping ?
579
580 this.escape.loop(this.config.throttle); // initialize update timer
581 }
582 /**
583 * add a new bar to the stack
584 * @param total
585 * @param value
586 * @param payload
587 * @returns {State}
588 */
589
590
591 create(total, value, payload) {
592 // progress updates are only visible in TTY mode!
593 if (this.noTTY) return void 0;
594 const state = new State(total, value, payload, true);
595 state.last = Number.NEGATIVE_INFINITY;
596 this.states.push(state);
597 if (!this.escape.active && this.states.length) this.boot();
598 return state;
599 } // remove a bar from the stack
600
601
602 remove(state) {
603 const index = this.states.indexOf(state); // find element
604
605 if (index < 0) {
606 return false;
607 } // element found ?
608
609
610 this.states.splice(index, 1); // remove element
611
612 this.terminal.newline();
613 this.terminal.clearDown();
614 return true;
615 }
616
617 stop() {
618 this.escape.stop(); // stop timer
619
620 if (this.config.hideCursor) {
621 this.terminal.showCursor(true);
622 } // cursor hidden ?
623
624
625 if (!this.config.lineWrap) {
626 this.terminal.setLineWrap(true);
627 } // re-enable line wrapping ?
628
629
630 if (this.config.autoClear) {
631 this.terminal.resetCursor(); // reset cursor
632
633 this.terminal.clearDown();
634 } // clear all bars or show final progress
635 else {
636 for (let state of this.states) {
637 state.stop();
638 }
639
640 this.#renderStates(this.states);
641 }
642 }
643
644 #renderStates(states) {
645 this.terminal.resetCursor(); // reset cursor
646
647 for (let i = 0, hi = states.length; i < hi; i++) {
648 const state = states[i]; // update each bar
649
650 if (this.forceRedraw || state.value !== state.last) {
651 this.terminal.cleanWrite(this.layout.formatter(state));
652 } // string updated, only trigger redraw on change
653
654
655 this.terminal.newline();
656 state.last = state.value;
657 }
658
659 if (this.noTTY) {
660 this.terminal.newline();
661 this.terminal.newline();
662 } // add new line in noTTY mode
663
664
665 if (this.config.autoStop && states.every(state => state.reachLimit)) {
666 this.stop();
667 } // stop if autoStop and all bars stopped
668
669 }
670
671}
672
673class Spin {
674 max;
675 value;
676 width;
677 step;
678
679 constructor(max, width, step) {
680 this.max = max;
681 this.width = width;
682 this.step = step;
683 this.value = 0;
684 }
685
686 static build(max, width, step) {
687 return new Spin(max, width, step);
688 }
689
690 next() {
691 this.value += this.step;
692 if (this.value >= this.max) this.value -= this.max;
693 return this;
694 }
695
696 get sequel() {
697 let next = this.value + this.width;
698 if (next > this.max) next -= this.max;
699 return next;
700 }
701
702 get record() {
703 const {
704 value,
705 sequel
706 } = this;
707
708 if (value <= 0 || value >= this.max) {
709 const x = this.width;
710 const y = this.max - this.width;
711 return [0, 0, x, y];
712 } else {
713 if (value < sequel) {
714 const x = value - 1;
715 const y = this.width;
716 const z = this.max - this.width - value + 1;
717 return [0, x, y, z];
718 } else {
719 const x = sequel;
720 const y = value - sequel - 1;
721 const z = this.max - value + 1;
722 return [x, y, z, 0];
723 }
724 }
725 }
726
727 renderBar([bar, spc]) {
728 const {
729 value,
730 sequel
731 } = this;
732
733 if (value <= 0 || value >= this.max) {
734 const x = this.width;
735 const y = this.max - this.width;
736 return bar.slice(0, x) + spc.slice(0, y);
737 } else {
738 if (value < sequel) {
739 const x = value;
740 const y = this.width;
741 const z = this.max - this.width - value;
742 return spc.slice(0, x) + bar.slice(0, y) + spc.slice(0, z);
743 } else {
744 const x = sequel;
745 const y = value - sequel;
746 const z = this.max - value;
747 return bar.slice(0, x) + spc.slice(0, y) + bar.slice(0, z);
748 }
749 }
750 }
751
752}
753
754const CHARSET_SHADE = [`█`, `░`]; // \u2588 \u2591
755
756const CHARSET_RECT = [`■`, ` `]; // \u25A0
757
758const CHARSET_LEGACY = [`=`, `-`];
759
760exports.Baro = Baro;
761exports.CHARSET_LEGACY = CHARSET_LEGACY;
762exports.CHARSET_RECT = CHARSET_RECT;
763exports.CHARSET_SHADE = CHARSET_SHADE;
764exports.Config = Config;
765exports.ETA = ETA;
766exports.Escape = Escape;
767exports.Layout = Layout;
768exports.Spin = Spin;
769exports.State = State;