UNPKG

terminal-kit

Version:

256 colors, keys and mouse, input field, progress bars, screen buffer (including 32-bit composition and image loading), text buffer, and many more... Whether you just need colors and styles, build a simple interactive command line tool or a complexe termi

1,674 lines (1,308 loc) 2.13 MB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.TerminalKit = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ /* Terminal Kit Copyright (c) 2009 - 2022 Cédric Ronvel The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ "use strict" ; const termkit = require( './termkit.js' ) ; /* Custom 256-colors palette for ScreenBuffer, each color is 24 bits. Enhance ScreenBuffer without relying on ScreenBufferHD. The original 6x6x6 colors cube shipped with terminal software is rather boring and miss the spot. Lot of useless colors, lot of over-saturated hideous colors, and useful colors cannot be shaded/hilighted easily. It also wastes plenty of color registers with 24 its grayscale colors... way too much details on this area that is unlikely to be used. This new custom palette have 4 regions: * The first 16 colors are reserved, it always maps to ANSI colors and depends on the terminal settings entirely. * Then comes a 216-color adaptive palette based upon 12 user-provided colors that represent 12 hue of the wheel, with auto-generated variation of shades, tint and desaturation. That is: 12 colors * 6 level of tint/shade * 3 level of desaturation = 216 colors. For maximum reliance, they are computed using the HCL (Lab) colorspace. This provides some good hilight and dim effect. * Then comes 13 extra colors fully defined by the user, they must be precise and beautiful colors that are missing in the 216-colors part, and that have great added values. * And finally comes 11 gray scale colors (going 0%, 10%, 20%, ..., 100% of HCL lightness) */ /* Color names scheme: * ansi colors: the ANSI color, without prefix or suffix * adaptatives colors: '@' + color name + '~'{0,2} (desaturation level) + '+'{0,2} or '-'{0,3} (lightness or brightness level) * extra colors: '*' + color name * grayscale colors: '@' + color name */ const defaultAdaptivePaletteDef = [ { names: [ 'red' ] , code: '#e32322' } , { names: [ 'orange' ] , code: '#f18e1c' } , { names: [ 'gold' , 'yellow-orange' , 'amber' ] , code: '#fdc60b' } , { names: [ 'yellow' ] , code: '#f4e500' } , { names: [ 'chartreuse' , 'yellow-green' ] , code: '#8cbb26' } , { names: [ 'green' ] , code: '#25ad28' } , { names: [ 'turquoise' , 'turquoise-green' ] , code: '#1bc17d' } , { names: [ 'cyan' , 'turquoise-blue' ] , code: '#0dc0cd' } , { names: [ 'blue' ] , code: '#2a60b0' } , { names: [ 'indigo' ] , code: '#3b3ba2' } , { names: [ 'violet' , 'purple' ] , code: '#713795' } , { names: [ 'magenta' ] , code: '#bd0a7d' } ] ; // 13 extra colors const defaultExtraPaletteDef = [ { names: [ 'crimson' ] , code: '#dc143c' } , { names: [ 'vermilion' , 'cinnabar' ] , code: '#e34234' } , { names: [ 'brown' ] , code: '#a52a2a' } , { names: [ 'bronze' ] , code: '#cd7f32' } , { names: [ 'coquelicot' ] , code: '#ff3800' } , //{ names: [ 'flame' ] , code: '#e25822' } , //{ names: [ 'salmon' ] , code: '#ff8c69' } , { names: [ 'coral-pink' ] , code: '#f88379' } , { names: [ 'see-green' ] , code: '#2e8b57' } , { names: [ 'medium-spring-green' ] , code: '#00fa9a' } , { names: [ 'olivine' ] , code: '#9ab973' } , { names: [ 'royal-blue' ] , code: '#4169e1' } , { names: [ 'purple' ] , code: '#800080' } , //{ names: [ 'tyrian-purple' ] , code: '#66023c' } , //{ names: [ 'purple-heart' ] , code: '#69359c' } , { names: [ 'lavender-purple' ] , code: '#967bb6' } , //{ names: [ 'classic-rose' , 'light-pink' ] , code: '#fbcce7' } , { names: [ 'pink' ] , code: '#ffc0cb' } //{ names: [ 'lime' , 'lemon-lime' ] , code: '#bfff00' } , ] ; const ansiColorIndex = { black: 0 , red: 1 , green: 2 , yellow: 3 , blue: 4 , magenta: 5 , violet: 5 , cyan: 6 , white: 7 , grey: 8 , gray: 8 , 'bright-black': 8 , 'bright-red': 9 , 'bright-green': 10 , 'bright-yellow': 11 , 'bright-blue': 12 , 'bright-magenta': 13 , 'bright-violet': 13 , 'bright-cyan': 14 , 'bright-white': 15 } ; function Palette( options = {} ) { this.term = options.term || termkit.terminal ; this.system = !! options.system ; this.adaptivePaletteDef = this.system ? null : options.adaptivePaletteDef || defaultAdaptivePaletteDef ; this.extraPaletteDef = this.system ? null : options.extraPaletteDef || defaultExtraPaletteDef ; this.escape = [] ; this.bgEscape = [] ; this.chromaColors = [] ; this.colorIndex = {} ; // Because that function is often passed as argument... easier to bind it here once for all this.colorNameToIndex = this.colorNameToIndex.bind( this ) ; this.generate() ; } module.exports = Palette ; Palette.prototype.colorNameToIndex = function( name ) { name = name.toLowerCase() ; return this.colorIndex[ name ] || termkit.colorNameToIndex( name ) ; } ; Palette.prototype.generate = function() { this.generateDefaultMapping() ; this.generateAnsiColorNames() ; this.generateAdaptive() ; this.generateExtra() ; this.generateGrayscale() ; } ; // It just generates default terminal mapping for 256 colors Palette.prototype.generateDefaultMapping = function() { var register ; for ( register = 0 ; register < 256 ; register ++ ) { this.escape[ register ] = this.term.str.color256( register ) ; this.bgEscape[ register ] = this.term.str.bgColor256( register ) ; } } ; // It just generates default terminal mapping for 256 colors Palette.prototype.generateAnsiColorNames = function() { var name , strippedName ; for ( name in ansiColorIndex ) { strippedName = name.replace( /-/g , '' ) ; this.colorIndex[ name ] = ansiColorIndex[ name ] ; if ( strippedName !== name ) { this.colorIndex[ strippedName ] = ansiColorIndex[ name ] ; } } } ; Palette.prototype.generateAdaptive = function() { if ( this.system ) { return ; } var i , j , z , register , baseChromaColors , chromaColor , saturationMark , lightnessMark , suffix ; baseChromaColors = this.adaptivePaletteDef.map( color => termkit.chroma( color.code ) ) ; register = 16 ; for ( z = 0 ; z >= -2 ; z -- ) { if ( z > 0 ) { saturationMark = '!'.repeat( z ) ; } else if ( z < 0 ) { saturationMark = '~'.repeat( -z ) ; } else { saturationMark = '' ; } for ( j = 2 ; j >= -3 ; j -- ) { if ( j > 0 ) { lightnessMark = '+'.repeat( j ) ; } else if ( j < 0 ) { lightnessMark = '-'.repeat( -j ) ; } else { lightnessMark = '' ; } suffix = saturationMark + lightnessMark ; for ( i = 0 ; i < 12 ; i ++ ) { chromaColor = this.clStep( baseChromaColors[ i ] , z , j ) ; this.addColor( register , chromaColor , this.adaptivePaletteDef[ i ].names , '@' , suffix ) ; register ++ ; } } } } ; Palette.prototype.generateExtra = function() { if ( this.system ) { return ; } var i , register ; register = 232 ; for ( i = 0 ; i < 13 && i < this.extraPaletteDef.length ; i ++ ) { this.addColor( register , termkit.chroma( this.extraPaletteDef[ i ].code ) , this.extraPaletteDef[ i ].names , '*' ) ; register ++ ; } } ; const grayscaleNames = [ [ 'black' ] , [ 'darkest-gray' ] , [ 'darker-gray' ] , [ 'dark-gray' ] , [ 'dark-medium-gray' ] , [ 'medium-gray' , 'gray' ] , [ 'light-medium-gray' ] , [ 'light-gray' ] , [ 'lighter-gray' ] , [ 'lightest-gray' ] , [ 'white' ] ] ; Palette.prototype.generateGrayscale = function() { if ( this.system ) { return ; } var i , register , chromaColor ; register = 245 ; for ( i = 0 ; i <= 10 ; i ++ ) { chromaColor = termkit.chroma( 0 , 0 , 10 * i , 'hcl' ) ; this.addColor( register , chromaColor , grayscaleNames[ i ] , '@' ) ; register ++ ; } } ; Palette.prototype.getRgb = function( register ) { var chromaColor = this.chromaColors[ register ] ; if ( ! chromaColor ) { return null ; } var [ r , g , b ] = chromaColor.rgb() ; return { r , g , b } ; } ; Palette.prototype.addColor = function( register , chromaColor , names , prefix = '' , suffix = '' ) { var targetRegister , [ r , g , b ] = chromaColor.rgb() ; this.chromaColors[ register ] = chromaColor ; if ( this.term.support.trueColor ) { this.escape[ register ] = this.term.str.colorRgb( r , g , b ) ; this.bgEscape[ register ] = this.term.str.bgColorRgb( r , g , b ) ; } else if ( this.term.support['256colors'] ) { targetRegister = this.term.registerForRgb( { r , g , b } , r === g && g === b ? 232 : 0 , // minRegister is the start of the grayscale if r=g=b 255 ) ; this.escape[ register ] = this.term.str.color256( targetRegister ) ; this.bgEscape[ register ] = this.term.str.bgColor256( targetRegister ) ; } else { targetRegister = this.term.registerForRgb( { r , g , b } , 0 , 15 ) ; this.escape[ register ] = this.term.str.color256( targetRegister ) ; this.bgEscape[ register ] = this.term.str.bgColor256( targetRegister ) ; } names.forEach( name => { var strippedName = prefix + name.replace( /-/g , '' ) + suffix ; name = prefix + name + suffix ; this.colorIndex[ name ] = register ; if ( strippedName !== name ) { this.colorIndex[ strippedName ] = register ; } } ) ; } ; const FIX_STEP = 1.1 ; Palette.prototype.clStep = function( chromaColor , cAdjust , lAdjust , fixRgb = true ) { var c , l , rgb , avg , sortedChannels , preserveLOverC ; if ( ! cAdjust && ! lAdjust ) { return chromaColor ; } c = chromaColor.get( 'hcl.c' ) ; l = chromaColor.get( 'hcl.l' ) ; /* c += c * cAdjust / 3 ; l += l * lAdjust / 4 ; //*/ c *= ( cAdjust > 0 ? 1.6 : 1.7 ) ** cAdjust ; l *= ( lAdjust > 0 ? 1.2 : 1.35 ) ** lAdjust ; chromaColor = chromaColor.set( 'hcl.c' , c ).set( 'hcl.l' , l ) ; if ( ! fixRgb || ! chromaColor.clipped ) { return chromaColor ; } // RGB is clipped and should be fixed. // The most critical part is when the hue get changed, since it's arguably the most important information. // Lightness is somewhat important too, but less than hue and a bit more than the Chroma. // Chroma will be preserved if the adjustement is greater on it than on lightness. //preserveLOverC = Math.abs( lAdjust ) >= Math.abs( cAdjust ) ; preserveLOverC = Math.abs( lAdjust ) >= cAdjust ; for ( ;; ) { // chromaColor.clipped is not reliable since integer rounding counts as clipping... rgb = chromaColor._rgb._unclipped ; rgb.length = 3 ; if ( rgb.every( channel => channel > -5 && channel < 260 ) ) { return chromaColor ; } sortedChannels = [ ... rgb ].sort( ( a , b ) => a - b ) ; //console.log( "Clipped!" , rgb , chromaColor.rgb() ) ; if ( sortedChannels[ 2 ] >= 256 ) { // Clipping will affect hue! avg = ( sortedChannels[ 0 ] + sortedChannels[ 1 ] + sortedChannels[ 2 ] ) / 3 ; if ( preserveLOverC ) { // Desaturate a bit and retry c = chromaColor.get( 'hcl.c' ) ; c /= FIX_STEP ; chromaColor = chromaColor.set( 'hcl.c' , c ) ; } else { // Darken a bit and retry l = chromaColor.get( 'hcl.l' ) ; l /= FIX_STEP ; chromaColor = chromaColor.set( 'hcl.l' , l ) ; } // It was too bright anyway, let it be clipped if ( avg > 255 ) { return chromaColor ; } } else if ( sortedChannels[ 1 ] < 0 ) { // Clipping will affect hue! avg = ( sortedChannels[ 0 ] + sortedChannels[ 1 ] + sortedChannels[ 2 ] ) / 3 ; if ( preserveLOverC ) { // Desaturate a bit and retry c = chromaColor.get( 'hcl.c' ) ; c /= FIX_STEP ; chromaColor = chromaColor.set( 'hcl.c' , c ) ; } else { // Lighten a bit and retry l = chromaColor.get( 'hcl.l' ) ; l *= FIX_STEP ; chromaColor = chromaColor.set( 'hcl.l' , l ) ; } // It was too dark anyway, let it be clipped if ( avg < 0 ) { return chromaColor ; } } else { // This clipping (lowest channel below 0) will not affect hue, only lightness, let it be clipped return chromaColor ; } } } ; },{"./termkit.js":56}],2:[function(require,module,exports){ /* Terminal Kit Copyright (c) 2009 - 2022 Cédric Ronvel The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ "use strict" ; const termkit = require( './termkit.js' ) ; /* Rect: rectangular region, clipping, iterators for blitters, etc... new Rect( xmin , ymin , xmax , ymax ) new Rect( object ) having properties: xmin , ymin , xmax , ymax new Rect( Terminal ) new Rect( ScreenBuffer ) */ function Rect( xmin , ymin , xmax , ymax ) { var src = xmin ; this.xmin = 0 ; this.xmax = 0 ; this.ymin = 0 ; this.ymax = 0 ; this.width = 0 ; this.height = 0 ; this.isNull = true ; if ( src && ( typeof src === 'object' || typeof src === 'function' ) ) { if ( src instanceof termkit.Terminal ) { this.set( { xmin: 1 , ymin: 1 , xmax: src.width , ymax: src.height } ) ; } else if ( src instanceof termkit.ScreenBuffer ) { this.set( { xmin: 0 , ymin: 0 , xmax: src.width - 1 , ymax: src.height - 1 } ) ; } else if ( src instanceof termkit.TextBuffer ) { this.set( { xmin: 0 , ymin: 0 , xmax: src.width - 1 , ymax: src.height - 1 } ) ; } else if ( src instanceof Rect ) { this.set( src ) ; } else if ( src.xmin !== undefined || src.ymin !== undefined || src.xmax !== undefined || src.ymax !== undefined ) { this.set( { xmin: src.xmin !== undefined ? src.xmin : 0 , ymin: src.ymin !== undefined ? src.ymin : 0 , xmax: src.xmax !== undefined ? src.xmax : 1 , ymax: src.ymax !== undefined ? src.ymax : 1 } ) ; } else if ( src.x !== undefined || src.y !== undefined || src.width !== undefined || src.height !== undefined ) { this.set( { xmin: src.x !== undefined ? src.x : 0 , ymin: src.y !== undefined ? src.y : 0 , xmax: src.width !== undefined ? src.x + src.width - 1 : 1 , ymax: src.height !== undefined ? src.y + src.height - 1 : 1 } ) ; } } else { this.set( { xmin: xmin !== undefined ? xmin : 0 , ymin: ymin !== undefined ? ymin : 0 , xmax: xmax !== undefined ? xmax : 1 , ymax: ymax !== undefined ? ymax : 1 } ) ; } } module.exports = Rect ; // Backward compatibility Rect.create = ( ... args ) => new Rect( ... args ) ; Rect.prototype.set = function( data ) { if ( data.xmin !== undefined ) { this.xmin = Math.floor( data.xmin ) ; } if ( data.xmax !== undefined ) { this.xmax = Math.floor( data.xmax ) ; } if ( data.ymin !== undefined ) { this.ymin = Math.floor( data.ymin ) ; } if ( data.ymax !== undefined ) { this.ymax = Math.floor( data.ymax ) ; } this.width = this.xmax - this.xmin + 1 ; this.height = this.ymax - this.ymin + 1 ; this.isNull = this.xmin > this.xmax || this.ymin > this.ymax ; } ; // Set the reset size, keeping *min and adjusting *max Rect.prototype.setSize = function( data ) { if ( data.width !== undefined ) { this.width = Math.floor( data.width ) ; this.xmax = this.xmin + this.width - 1 ; } if ( data.height !== undefined ) { this.height = Math.floor( data.height ) ; this.ymax = this.ymin + this.height - 1 ; } this.isNull = this.xmin > this.xmax || this.ymin > this.ymax ; } ; Rect.prototype.isInside = function( x , y ) { return x >= this.xmin && x <= this.xmax && y >= this.ymin && y <= this.ymax ; } ; // Clip the src according to the dst, offset* are offsets of the srcRect relative to the dst coordinate system Rect.prototype.clip = function( dstRect , offsetX , offsetY , dstClipping ) { var srcRect = this ; offsetX = offsetX || 0 ; offsetY = offsetY || 0 ; srcRect.set( { xmin: Math.max( srcRect.xmin , dstRect.xmin - offsetX ) , ymin: Math.max( srcRect.ymin , dstRect.ymin - offsetY ) , xmax: Math.min( srcRect.xmax , dstRect.xmax - offsetX ) , ymax: Math.min( srcRect.ymax , dstRect.ymax - offsetY ) } ) ; if ( dstClipping ) { dstRect.set( { xmin: Math.max( dstRect.xmin , srcRect.xmin + offsetX ) , ymin: Math.max( dstRect.ymin , srcRect.ymin + offsetY ) , xmax: Math.min( dstRect.xmax , srcRect.xmax + offsetX ) , ymax: Math.min( dstRect.ymax , srcRect.ymax + offsetY ) } ) ; } return this ; } ; // Merge with another Rect, enlarge the current Rect so that it includes the Rect argument Rect.prototype.merge = function( rect ) { this.set( { xmin: Math.min( this.xmin , rect.xmin ) , ymin: Math.min( this.ymin , rect.ymin ) , xmax: Math.max( this.xmax , rect.xmax ) , ymax: Math.max( this.ymax , rect.ymax ) } ) ; return this ; } ; /* Given a srcRect, a dstRect, offsetX and offsetY, return an array of up to 4 objects consisting of the same properties found in entry, wrapping the src into the dst, i.e. the src is always fully visible in the dst, it is just as if the dst where circular Mandatory params: * dstRect * srcRect * offsetX * offsetY Optionnal params: * wrapOnly: 'x' , 'y' (only wrap along that axis) */ Rect.wrappingRect = function( p ) { var regions = [] , nw , ne , sw , se ; // Originate, North-West region nw = { srcRect: new Rect( p.srcRect ) , dstRect: new Rect( p.dstRect ) , offsetX: p.offsetX , offsetY: p.offsetY } ; // Modulate offsets so they are in-range if ( p.wrapOnly !== 'y' ) { nw.offsetX = nw.offsetX % p.dstRect.width ; if ( nw.offsetX < 0 ) { nw.offsetX += p.dstRect.width ; } } if ( p.wrapOnly !== 'x' ) { nw.offsetY = nw.offsetY % p.dstRect.height ; if ( nw.offsetY < 0 ) { nw.offsetY += p.dstRect.height ; } } // Mutual clipping nw.srcRect.clip( nw.dstRect , nw.offsetX , nw.offsetY , true ) ; if ( ! nw.srcRect.isNull ) { regions.push( nw ) ; } // Wrap-x North-Est region if ( nw.srcRect.width < p.srcRect.width && p.wrapOnly !== 'y' ) { ne = { srcRect: new Rect( p.srcRect ) , dstRect: new Rect( p.dstRect ) , offsetX: nw.offsetX - p.dstRect.width , offsetY: nw.offsetY } ; // Mutual clipping ne.srcRect.clip( ne.dstRect , ne.offsetX , ne.offsetY , true ) ; if ( ! ne.srcRect.isNull ) { regions.push( ne ) ; } } // Wrap-y South-West region if ( nw.srcRect.height < p.srcRect.height && p.wrapOnly !== 'x' ) { sw = { srcRect: new Rect( p.srcRect ) , dstRect: new Rect( p.dstRect ) , offsetX: nw.offsetX , offsetY: nw.offsetY - p.dstRect.height } ; // Mutual clipping sw.srcRect.clip( sw.dstRect , sw.offsetX , sw.offsetY , true ) ; if ( ! sw.srcRect.isNull ) { regions.push( sw ) ; } } // Wrap-x + wrap-y South-Est region, do it only if it has wrapped already if ( ne && sw ) { se = { srcRect: new Rect( p.srcRect ) , dstRect: new Rect( p.dstRect ) , offsetX: nw.offsetX - p.dstRect.width , offsetY: nw.offsetY - p.dstRect.height } ; // Mutual clipping se.srcRect.clip( se.dstRect , se.offsetX , se.offsetY , true ) ; if ( ! se.srcRect.isNull ) { regions.push( se ) ; } } return regions ; } ; /* This iterator generate synchronous line or cell for dst & src Rect. It is totally buffer agnostic. Buffer specificities should be added in p.context by the callee. Iterator. Mandatory params: * dstRect: Rect describing the dst geometry * srcRect: Rect describing the src geometry * type: 'line' or 'cell' Optionnal params: * context: an object that will be transmitted as is to the iterator * dstClipRect: a clipping Rect for the dst * srcClipRect: a clipping Rect for the src * offsetX: the X-offset of the origin of the srcRect relative to the dst coordinate system * offsetY: the Y-offset of the origin of the srcRect relative to the dst coordinate system * multiply: the byte size of a cell by which all offset should be multiplied */ Rect.regionIterator = function( p , iterator ) { var i , j , srcX , srcY , dstX , dstY , srcStart , dstStart , isFullWidth ; if ( ! p.multiply ) { p.multiply = 1 ; } if ( ! p.offsetX ) { p.offsetX = 0 ; } if ( ! p.offsetY ) { p.offsetY = 0 ; } if ( p.dstClipRect ) { p.dstClipRect.clip( p.dstRect ) ; } else { p.dstClipRect = new Rect( p.dstRect ) ; } if ( p.srcClipRect ) { p.srcClipRect.clip( p.srcRect ) ; } else { p.srcClipRect = new Rect( p.srcRect ) ; } // Mutual clipping p.srcClipRect.clip( p.dstClipRect , p.offsetX , p.offsetY , true ) ; // If out of bounds, or if everything is clipped away, return now if ( p.dstRect.isNull || p.srcClipRect.isNull || p.dstClipRect.isNull ) { return ; } switch ( p.type ) { case 'line' : for ( j = 0 ; j < p.srcClipRect.height ; j ++ ) { srcY = p.srcClipRect.ymin + j ; dstY = p.dstClipRect.ymin + j ; iterator( { context: p.context , srcXmin: p.srcClipRect.xmin , srcXmax: p.srcClipRect.xmax , srcY: srcY , srcStart: ( srcY * p.srcRect.width + p.srcClipRect.xmin ) * p.multiply , srcEnd: ( srcY * p.srcRect.width + p.srcClipRect.xmax + 1 ) * p.multiply , dstXmin: p.dstClipRect.xmin , dstXmax: p.dstClipRect.xmax , dstY: dstY , dstStart: ( dstY * p.dstRect.width + p.dstClipRect.xmin ) * p.multiply , dstEnd: ( dstY * p.dstRect.width + p.dstClipRect.xmax + 1 ) * p.multiply //, lastLine: j === p.srcClipRect.height - 1 } ) ; } break ; case 'reversedLine' : // Same than 'line' but start from the last line. // Useful for copying overlapping region of the same buffer, when the src rect has a lower Y-offset than the dst rect. for ( j = p.srcClipRect.height - 1 ; j >= 0 ; j -- ) { srcY = p.srcClipRect.ymin + j ; dstY = p.dstClipRect.ymin + j ; iterator( { context: p.context , srcXmin: p.srcClipRect.xmin , srcXmax: p.srcClipRect.xmax , srcY: srcY , srcStart: ( srcY * p.srcRect.width + p.srcClipRect.xmin ) * p.multiply , srcEnd: ( srcY * p.srcRect.width + p.srcClipRect.xmax + 1 ) * p.multiply , dstXmin: p.dstClipRect.xmin , dstXmax: p.dstClipRect.xmax , dstY: dstY , dstStart: ( dstY * p.dstRect.width + p.dstClipRect.xmin ) * p.multiply , dstEnd: ( dstY * p.dstRect.width + p.dstClipRect.xmax + 1 ) * p.multiply } ) ; } break ; case 'cell' : for ( j = 0 ; j < p.srcClipRect.height ; j ++ ) { for ( i = 0 ; i < p.srcClipRect.width ; i ++ ) { srcX = p.srcClipRect.xmin + i ; srcY = p.srcClipRect.ymin + j ; dstX = p.dstClipRect.xmin + i ; dstY = p.dstClipRect.ymin + j ; srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ; dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ; isFullWidth = iterator( { context: p.context , srcX: srcX , srcY: srcY , srcStart: srcStart , srcEnd: srcStart + p.multiply , dstX: dstX , dstY: dstY , dstStart: dstStart , dstEnd: dstStart + p.multiply , startOfBlitLine: ! i , endOfBlitLine: i === p.srcClipRect.width - 1 } ) ; if ( isFullWidth ) { i ++ ; } } } break ; } } ; /* This is the tile-variant of the regionIterator. Iterator. Mandatory params: * dstRect * srcRect * type: 'line' or 'cell' Optionnal params: * context: an object that will be transmitted as is to the iterator * dstClipRect * srcClipRect * offsetX * offsetY * multiply */ Rect.tileIterator = function( p , iterator ) { var srcI , srcJ , srcX , srcY , dstI , dstJ , dstX , dstY , streak , srcStart , dstStart ; if ( ! p.multiply ) { p.multiply = 1 ; } if ( ! p.offsetX ) { p.offsetX = 0 ; } if ( ! p.offsetY ) { p.offsetY = 0 ; } if ( p.dstClipRect ) { p.dstClipRect.clip( p.dstRect ) ; } else { p.dstClipRect = new Rect( p.dstRect ) ; } if ( p.srcClipRect ) { p.srcClipRect.clip( p.srcRect ) ; } else { p.srcClipRect = new Rect( p.srcRect ) ; } switch ( p.type ) { case 'cell' : for ( dstJ = 0 ; dstJ < p.dstClipRect.height ; dstJ ++ ) { srcJ = ( dstJ - p.offsetY ) % p.srcClipRect.height ; if ( srcJ < 0 ) { srcJ += p.srcClipRect.height ; } for ( dstI = 0 ; dstI < p.dstClipRect.width ; dstI ++ ) { srcI = ( dstI - p.offsetX ) % p.srcClipRect.width ; if ( srcI < 0 ) { srcI += p.srcClipRect.width ; } srcX = p.srcClipRect.xmin + srcI ; srcY = p.srcClipRect.ymin + srcJ ; dstX = p.dstClipRect.xmin + dstI ; dstY = p.dstClipRect.ymin + dstJ ; srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ; dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ; iterator( { context: p.context , srcX: srcX , srcY: srcY , srcStart: srcStart , srcEnd: srcStart + p.multiply , dstX: dstX , dstY: dstY , dstStart: dstStart , dstEnd: dstStart + p.multiply } ) ; } } break ; case 'line' : for ( dstJ = 0 ; dstJ < p.dstClipRect.height ; dstJ ++ ) { srcJ = ( dstJ - p.offsetY ) % p.srcClipRect.height ; if ( srcJ < 0 ) { srcJ += p.srcClipRect.height ; } dstI = 0 ; while ( dstI < p.dstClipRect.width ) { srcI = ( dstI - p.offsetX ) % p.srcClipRect.width ; if ( srcI < 0 ) { srcI += p.srcClipRect.width ; } streak = Math.min( p.srcClipRect.width - srcI , p.dstClipRect.width - dstI ) ; srcX = p.srcClipRect.xmin + srcI ; srcY = p.srcClipRect.ymin + srcJ ; dstX = p.dstClipRect.xmin + dstI ; dstY = p.dstClipRect.ymin + dstJ ; srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ; dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ; iterator( { context: p.context , srcXmin: srcX , srcXmax: srcX + streak - 1 , srcY: srcY , srcStart: srcStart , srcEnd: srcStart + streak * p.multiply , dstXmin: dstX , dstXmax: dstX + streak - 1 , dstY: dstY , dstStart: dstStart , dstEnd: dstStart + streak * p.multiply } ) ; dstI += streak ; } } break ; } } ; /* This is the wrap-variant of the regionIterator. Iterator. Mandatory params: * dstRect * srcRect * type: 'line' or 'cell' Optionnal params: * context: an object that will be transmitted as is to the iterator * dstClipRect * srcClipRect * offsetX * offsetY * multiply * wrapOnly: 'x' , 'y' (only wrap along that axis) */ Rect.wrapIterator = function( p , iterator ) { var i , regions ; regions = Rect.wrappingRect( { dstRect: p.dstClipRect , srcRect: p.srcClipRect , offsetX: p.offsetX , offsetY: p.offsetY , wrapOnly: p.wrap } ) ; for ( i = 0 ; i < regions.length ; i ++ ) { p.dstClipRect = regions[ i ].dstRect ; p.srcClipRect = regions[ i ].srcRect ; p.offsetX = regions[ i ].offsetX ; p.offsetY = regions[ i ].offsetY ; Rect.regionIterator( p , iterator ) ; } } ; },{"./termkit.js":56}],3:[function(require,module,exports){ (function (Buffer){(function (){ /* Terminal Kit Copyright (c) 2009 - 2022 Cédric Ronvel The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ "use strict" ; const misc = require( './misc.js' ) ; const fs = require( 'fs' ) ; const string = require( 'string-kit' ) ; const NextGenEvents = require( 'nextgen-events' ) ; /* options: * width: buffer width (default to dst.width) * height: buffer height (default to dst.height) * dst: writting destination * inline: for terminal dst only, draw inline instead of at some position (do not moveTo) * x: default position in the dst * y: default position in the dst * wrap: default wrapping behavior of .put() * noFill: do not call .fill() with default values at ScreenBuffer creation * blending: false/null or true or object (blending options): default blending params (can be overriden by .draw()) * palette: Palette instance */ function ScreenBuffer( options = {} ) { this.dst = options.dst ; // a terminal or another screenBuffer this.inline = !! options.inline ; // it's a terminal and we want to draw inline to it (no moveTo) this.width = Math.floor( options.width ) || ( options.dst ? options.dst.width : 1 ) ; this.height = Math.floor( options.height ) || ( options.dst ? options.dst.height : 1 ) ; this.x = options.x !== undefined ? options.x : ( options.dst && options.dst instanceof termkit.Terminal ? 1 : 0 ) ; // eslint-disable-line this.y = options.y !== undefined ? options.y : ( options.dst && options.dst instanceof termkit.Terminal ? 1 : 0 ) ; // eslint-disable-line this.cx = 0 ; this.cy = 0 ; this.ch = false ; // cursor hidden this.lastCh = null ; // cursor hidden on last terminal draw, avoid unecessary escape sequence output this.lastBuffer = null ; this.lastBufferUpToDate = false ; this.blending = options.blending || false ; this.wrap = !! options.wrap ; this.buffer = Buffer.allocUnsafe( this.width * this.height * this.ITEM_SIZE ) ; this.palette = options.palette || ( this.dst && this.dst.palette ) ; if ( ! options.noFill ) { this.fill() ; } } module.exports = ScreenBuffer ; ScreenBuffer.prototype = Object.create( NextGenEvents.prototype ) ; ScreenBuffer.prototype.constructor = ScreenBuffer ; ScreenBuffer.prototype.bitsPerColor = 8 ; // Backward compatibility ScreenBuffer.create = ( ... args ) => new ScreenBuffer( ... args ) ; const termkit = require( './termkit.js' ) ; const Rect = termkit.Rect ; /* options: * attr: attributes passed to .put() * transparencyChar: a char that is transparent * transparencyType: bit flags for the transparency char */ ScreenBuffer.createFromString = function( options , data ) { var x , y , length , attr , attrTrans , width , height , lineWidth , screenBuffer ; // Manage options if ( ! options ) { options = {} ; } if ( typeof data !== 'string' ) { if ( ! data.toString ) { throw new Error( '[terminal] ScreenBuffer.createFromDataString(): argument #1 should be a string or provide a .toString() method.' ) ; } data = data.toString() ; } // Transform the data into an array of lines data = termkit.stripControlChars( data , true ).split( '\n' ) ; // Compute the buffer size width = 0 ; height = data.length ; attr = options.attr !== undefined ? options.attr : ScreenBuffer.prototype.DEFAULT_ATTR ; if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = ScreenBuffer.object2attr( attr ) ; } attrTrans = attr ; if ( options.transparencyChar ) { if ( ! options.transparencyType ) { attrTrans |= TRANSPARENCY ; } else { attrTrans |= options.transparencyType & TRANSPARENCY ; } } // Compute the width of the screenBuffer for ( y = 0 ; y < data.length ; y ++ ) { lineWidth = string.unicode.width( data[ y ] ) ; if ( lineWidth > width ) { width = lineWidth ; } } // Create the buffer with the right width & height screenBuffer = new ScreenBuffer( { width: width , height: height } ) ; // Fill the buffer with data for ( y = 0 ; y < data.length ; y ++ ) { if ( ! options.transparencyChar ) { screenBuffer.put( { x: 0 , y: y , attr: attr } , data[ y ] ) ; } else { length = data[ y ].length ; for ( x = 0 ; x < length ; x ++ ) { if ( data[ y ][ x ] === options.transparencyChar ) { screenBuffer.put( { x: x , y: y , attr: attrTrans } , data[ y ][ x ] ) ; } else { screenBuffer.put( { x: x , y: y , attr: attr } , data[ y ][ x ] ) ; } } } } return screenBuffer ; } ; // Backward compatibility ScreenBuffer.createFromChars = ScreenBuffer.createFromString ; // Shared ScreenBuffer.prototype.setClearAttr = function( attr ) { this.CLEAR_ATTR = this.object2attr( attr ) ; this.CLEAR_BUFFER = Buffer.allocUnsafe( this.ITEM_SIZE ) ; if ( Buffer.isBuffer( this.CLEAR_ATTR ) ) { // ScreenBufferHD this.CLEAR_ATTR.copy( this.CLEAR_BUFFER ) ; } else { // if ( this.ATTR_SIZE === 4 ) { this.CLEAR_BUFFER.writeInt32BE( this.CLEAR_ATTR , 0 ) ; } this.CLEAR_BUFFER.write( ' \x00\x00\x00' , this.ATTR_SIZE ) ; // space } ; /* options: attr: optional, the attribute to fill (default to DEFAULT_ATTR) char: optional, the buffer will be filled with that char (default to space) region: optional, a Rect compliant object defining the region to fill, instead a filling the whole ScreenBuffer start: optional (internal), start offset end: optional (internal), end offset clearBuffer: optional (internal), a Buffer to use to clear (instead of char+attr) buffer: optional (internal), used when we want to clear a Buffer instance, not a ScreenBuffer instance */ // Shared ScreenBuffer.prototype.fill = function( options ) { var i , attr , char , start , end , region , srcRect , toRect , clearBuffer = this.CLEAR_BUFFER , buffer = this.buffer ; if ( options && typeof options === 'object' ) { if ( options.char || options.attr ) { clearBuffer = Buffer.allocUnsafe( this.ITEM_SIZE ) ; // Write the attributes attr = options.attr !== undefined ? options.attr : this.DEFAULT_ATTR ; if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = this.object2attr( attr ) ; } this.writeAttr( clearBuffer , attr , 0 ) ; // Write the character char = options.char && typeof options.char === 'string' ? options.char : ' ' ; //char = punycode.ucs2.encode( [ punycode.ucs2.decode( termkit.stripControlChars( char ) )[ 0 ] ] ) ; char = string.unicode.firstChar( termkit.stripControlChars( char ) ) ; //clearBuffer.write( char , this.ATTR_SIZE , this.CHAR_SIZE ) ; this.writeChar( clearBuffer , char , 0 ) ; } else if ( options.clearBuffer ) { clearBuffer = options.clearBuffer ; } // This option is used when we want to clear a Buffer instance, not a ScreenBuffer instance if ( options.buffer ) { buffer = options.buffer ; } start = options.start ? Math.floor( options.start / this.ITEM_SIZE ) : 0 ; end = options.end ? Math.floor( options.end / this.ITEM_SIZE ) : buffer.length / this.ITEM_SIZE ; region = options.region ? options.region : null ; } else { start = 0 ; end = buffer.length / this.ITEM_SIZE ; } if ( region ) { srcRect = new Rect( 0 , 0 , 0 , 0 ) ; toRect = new Rect( region ) ; toRect.clip( new Rect( this ) ) ; if ( toRect.isNull ) { return ; } // We use the blitter to fill the region Rect.tileIterator( { type: 'line' , context: { srcBuffer: clearBuffer , dstBuffer: this.buffer } , srcRect: srcRect , dstRect: new Rect( this ) , dstClipRect: toRect , multiply: this.ITEM_SIZE } , this.blitterLineIterator.bind( this ) ) ; } else { for ( i = start ; i < end ; i ++ ) { clearBuffer.copy( buffer , i * this.ITEM_SIZE ) ; } } } ; // Clear the buffer: fill it with blank // Shared ScreenBuffer.prototype.clear = ScreenBuffer.prototype.fill ; ScreenBuffer.prototype.preserveMarkupFormat = misc.preserveMarkupFormat ; ScreenBuffer.prototype.parseMarkup = string.markupMethod.bind( misc.markupOptions ) ; /* put( options , str ) put( options , format , [arg1] , [arg2] , ... ) options: * x: bypass this.cx * y: bypass this.cy * markup: boolean or 'ansi' or 'legacyAnsi', true if the text contains markup that should be interpreted, 'ansi' if it contains ansi code, 'legacyAnsi' is bold is bright fg and blink is bright bg * attr: standard attributes * resumeAttr: (internal) attr code to resume to * wrap: text wrapping, when the cursor move beyond the last column, it is moved to the begining of the next line * newLine: if true, then \r and \n produce new lines, false by default: .put() does not manage lines * clip: if set, it is object describing the clipping area, nothing is written outside of it, and if the 'wrap' option is set, wrapping is done on its boundary. Properties: * x * y * width * height * clipChar: a char that is display just before clipping something * direction: 'right' (default), 'left', 'up', 'down' or 'none'/null (do not move after puting a char) * dx: x increment after each character (default: 1) * dy: y increment after each character (default: 0) */ // Shared ScreenBuffer.prototype.put = function( options , str , ... args ) { var startX , startY , x , y , dx , dy , baseAttr , attr , attrObject , wrap , lastValidOffset = -1 , xmin = 0 , ymin = 0 , xmax = this.width - 1 , ymax = this.height - 1 ; // Manage options if ( ! options ) { options = {} ; } wrap = options.wrap !== undefined ? options.wrap : this.wrap ; startX = x = Math.floor( options.x !== undefined ? options.x : this.cx ) ; startY = y = Math.floor( options.y !== undefined ? options.y : this.cy ) ; if ( options.clip ) { xmin = options.clip.x ; ymin = options.clip.y ; xmax = xmin + options.clip.width - 1 ; ymax = ymin + options.clip.height - 1 ; } // Process directions/increments switch ( options.direction ) { //case 'right' : // not needed, use the default dx & dy case 'left' : dx = -1 ; break ; case 'up' : dx = 0 ; dy = -1 ; break ; case 'down' : dx = 0 ; dy = 1 ; break ; case null : case 'none' : dx = 0 ; dy = 0 ; break ; case 'right' : default : dx = 1 ; dy = 0 ; break ; } // Overide if ( typeof options.dx === 'number' ) { dx = options.dx ; } if ( typeof options.dy === 'number' ) { dy = options.dy ; } // Process attributes attr = options.attr !== undefined ? options.attr : this.DEFAULT_ATTR ; if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = this.object2attr( attr ) ; } baseAttr = attr ; // It's already in the correct format if ( options.resumeAttr !== undefined ) { attr = options.resumeAttr ; } // Process the input string if ( typeof str !== 'string' ) { if ( str.toString ) { str = str.toString() ; } else { return ; } } if ( args.length ) { str = options.markup === true ? this.preserveMarkupFormat( str , ... args ) : string.format( str , ... args ) ; } // The processing of raw chunk of text var processRaw = part => { //part = termkit.stripControlChars( part ) ; var characters = string.unicode.toArray( part ) , iMax = characters.length ; for ( let i = 0 ; i < iMax ; i ++ ) { let offset = ( y * this.width + x ) * this.ITEM_SIZE ; let char = characters[ i ] ; let charCode = char.charCodeAt( 0 ) ; if ( charCode < 0x20 || charCode === 0x7f ) { if ( options.newLine && ( charCode === 0x0a || charCode === 0x0d ) ) { if ( dx ) { x = startX ; y ++ ; } else { y = startY ; x ++ ; } continue ; } else { char = ' ' ; // Space charCode = 0x20 ; } } let isFullWidth = string.unicode.isFullWidth( char ) ; let inBounds = false ; if ( y >= ymin && y <= ymax ) { if ( isFullWidth ) { if ( x + isFullWidth * dx >= xmin && x + isFullWidth * ( dx || 1 ) <= xmax ) { // This is a full-width char! Needs extra care! if ( dx < 0 ) { offset -= this.ITEM_SIZE ; } lastValidOffset = offset ; // Check if we are writing on a fullwidth char if ( this.hasTrailingFullWidth( this.buffer , offset ) && x ) { this.removeFullWidth( this.buffer , offset - this.ITEM_SIZE ) ; } // Write the attributes this.writeAttr( this.buffer , attr , offset , this.LEADING_FULLWIDTH ) ; // Write the character this.writeChar( this.buffer , char , offset ) ; offset += this.ITEM_SIZE ; // Check if we are writing on a fullwidth char if ( this.hasLeadingFullWidth( this.buffer , offset ) && x < xmax ) { this.removeFullWidth( this.buffer , offset + this.ITEM_SIZE ) ; } // Write the attributes this.writeAttr( this.buffer , attr , offset , this.TRAILING_FULLWIDTH ) ; // Write a blank character this.writeChar( this.buffer , ' ' , offset ) ; inBounds = true ; } } else { if ( x >= xmin && x <= xmax ) { // Check if we are writing on a fullwidth char if ( this.hasLeadingFullWidth( this.buffer , offset ) && x < xmax ) { this.removeFullWidth( this.buffer , offset + this.ITEM_SIZE ) ; } else if ( this.hasTrailingFullWidth( this.buffer , offset ) && x ) { this.removeFullWidth( this.buffer , offset - this.ITEM_SIZE ) ; } // Write the attributes this.writeAttr( this.buffer , attr , offset ) ; // Write the character this.writeChar( this.buffer , char , offset ) ; lastValidOffset = offset ; inBounds = true ; } } } else { // Optimization : early out when there is no chance anymore char would be put... if ( ( y > ymax && dy >= 0 ) || ( y < ymin && dy < 0 ) ) { return true ; } } if ( ! inBounds && lastValidOffset >= 0 && options.clipChar ) { // Check if we are writing on a fullwidth char if ( this.hasLeadingFullWidth( this.buffer , lastValidOffset ) && x < xmax ) { this.removeFullWidth( this.buffer , lastValidOffset + this.ITEM_SIZE ) ; } else if ( this.hasTrailingFullWidth( this.buffer , lastValidOffset ) && x ) { this.removeFullWidth( this.buffer , lastValidOffset - this.ITEM_SIZE ) ; } // Write the character this.writeChar( this.buffer , options.clipChar , lastValidOffset ) ; lastValidOffset = -1 ; } x += dx * ( 1 + isFullWidth ) ; y += dy ; if ( wrap ) { if ( x < xmin ) { x = xmax ; y -- ; } else if ( x > xmax ) { x = xmin ; y ++ ; } } } } ; if ( ! options.markup ) { processRaw( str ) ; } else { const defaultAttrObject = this.attr2object( this.DEFAULT_ATTR ) ; const baseAttrObject = this.attr2object( baseAttr ) ; let legacyColor = false ; let parts = null ; switch ( options.markup ) { case 'ansi' : parts = string.ansi.parse( str ) ; break ; case 'legacyAnsi' : parts = string.ansi.parse( str ) ; legacyColor = true ; break ; case true : parts = this.parseMarkup( str ) ; break ; } for ( let part of parts ) { attrObject = Object.assign( {} , part.specialReset ? defaultAttrObject : baseAttrObject , part ) ; delete attrObject.text ; // Remove incompatible flags if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; } if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; } attr = this.object2attr( attrObject , undefined , legacyColor ) ; if ( part.text ) { if ( processRaw( part.text ) ) { break ; } } } } this.cx = x ; this.cy = y ; return attr ; } ; /* options: * x: bypass this.cx * y: bypass this.cy */ // Shared ScreenBuffer.prototype.get = function( options ) { var x , y , offset ; // Manage options if ( ! options ) { options = {} ; } x = options.x !== undefined ? options.x : this.cx ; y = options.y !== undefined ? options.y : this.cy ; if ( typeof x !== 'number' || x < 0 || x >= this.width ) { return null ; } x = Math.floor( x ) ; if ( typeof y !== 'number' || y < 0 || y >= this.height ) { return null ; } y = Math.floor( y ) ; offset = ( y * this.width + x ) * this.ITEM_SIZE ; return { attr: this.attr2object( this.readAttr( this.buffer , offset ) ) , char: this.readChar( this.buffer , offset ) } ; } ; // Resize a screenBuffer, using a Rect // Shared ScreenBuffer.prototype.resize = function( fromRect ) { // Do not reference directly the userland variable, clone it fromRect = new Rect( fromRect ) ; var offsetX = -fromRect.xmin , offsetY = -fromRect.ymin ; // Create the toRect region var toRect = new Rect( { xmin: 0 , ymin: 0 , xmax: fromRect.width - 1 , ymax: fromRect.height - 1 } ) ; fromRect.clip( new Rect( this ) ) ; if ( toRect.isNull ) { return false ; } // Generate a new buffer var resizedBuffer = Buffer.allocUnsafe( toRect.width * toRect.height * this.ITEM_SIZE ) ; this.fill( { buffer: resizedBuffer } ) ; // We use the blitter to reconstruct the buffer geometry Rect.regionIterator( { type: 'line' , context: { srcBuffer: this.buffer , dstBuffer: resizedBuffer } , dstRect: toRect , dstClipRect: new Rect( toRect ) , srcRect: new Rect( this ) , srcClipRect: fromRect , offsetX: offsetX , offsetY: offsetY , multiply: this.ITEM_SIZE } , this.blitterLineIterator.bind( this ) ) ; // Now, we have to replace the old buffer with the new, and set the width & height this.width = toRect.width ; this.height = toRect.height ; this.buffer = resizedBuffer ; // Disable the lastBuffer, so `draw( { delta: true } )` will not be bugged this.lastBuffer = null ; // This exists to improve compatibilities with the Terminal object this.emit( 'resize' , this.width , this.height ) ; return true ; } ; // Shared ScreenBuffer.prototype.draw = function( options ) { if ( ! options || typeof options !== 'object' ) { options = {} ; } // Transmitted options (do not edit the user provided options, clone them) var tr = { dst: options.dst || this.dst , inline: options.inline !== undefined ? !! options.inline : this.inline , offsetX: options.x !== undefined ? Math.floor( options.x ) : Math.floor( this.x ) , offsetY: options.y !== undefined ? Math.floor( options.y ) : Math.floor( this.y ) , dstClipRect: options.dstClipRect ? new Rect( options.dstClipRect ) : undefined , srcClipRect: options.srcClipRect ? new Rect( options.srcClipRect ) : undefined , delta: options.delta , blending: options.blending !== undefined ? options.blending : this.blending , wrap: options.wrap , tile: options.tile } ; if ( tr.dst instanceof ScreenBuffer ) { return this.blitter( tr ) ; } else if ( tr.dst instanceof termkit.Terminal ) { return this.terminalBlitter( tr ) ; } } ; // Shared ScreenBuffer.prototype.moveTo = function( x , y ) { this.cx = Math.max( 0 , Math.min( x , this.width - 1 ) ) ; this.cy = Math.max( 0 , Math.min( y , this.height - 1 ) ) ; } ; // Shared ScreenBuffer.prototype.drawCursor = function( options ) { if ( ! options || typeof options !== 'object' ) { options = {} ; } var dst = options.dst || this.dst ; if ( dst instanceof ScreenBuffer ) { if ( this.ch ) { dst.ch = true ; } else { dst.ch = false ; dst.moveTo( this.cx + this.x , this.cy + this.y ) ; } } else if ( dst instanceof termkit.Terminal ) { if ( this.ch ) { if ( this.ch !== this.lastCh ) { dst.hideCursor() ; } } else { if ( this.ch !== this.lastCh ) { dst.hideCursor( false ) ; } dst.moveTo( Math.max( 1 , Math.min( this.cx + this.x , dst.width ) ) , Math.max( 1 , Math.min( this.cy + this.y , dst.height ) ) ) ; } this.lastCh = this.ch ; } } ; // Shared ScreenBuffer.prototype.blitter = function( p ) { var tr , iterator , iteratorCallback ; // Default options & iterator tr = { type: 'line' , context: { srcBuffer: this.buffer , dstBuffer: p.dst.buffer , blending: p.blending } , dstRect: new Rect( p.dst ) , srcRect: new Rect( this ) , dstClip