@@ -8,8 +8,13 @@ import {
88 Inject
99} from '@angular/core' ;
1010import { DOCUMENT } from '@angular/common' ;
11- import * as AnsiUp from 'ansi_up' ;
12- import { SlimScrollEvent , ISlimScrollOptions } from 'ngx-slimscroll' ;
11+ import * as hterm from 'hterm-umdjs' ;
12+
13+ const terminalColorPallete = [ 'rgb(40, 42, 54)' , 'rgb(255, 85, 85)' , 'rgb(80, 250, 123)' ,
14+ 'rgb(243, 251, 151)' , 'rgb(189, 147, 249)' , 'rgb(255, 121, 198)' , 'rgb(139, 233, 253)' ,
15+ 'rgb(187, 187, 187)' , 'rgb(85, 85, 85)' , 'rgb(255, 85, 85)' , 'rgb(80, 250, 123)' ,
16+ 'rgb(243, 251, 151)' , 'rgb(189, 147, 249)' , 'rgb(255, 121, 198)' , 'rgb(139, 233, 253)' ,
17+ 'rgb(255, 255, 255)' ] ;
1318
1419@Component ( {
1520 selector : 'app-terminal' ,
@@ -18,183 +23,81 @@ import { SlimScrollEvent, ISlimScrollOptions } from 'ngx-slimscroll';
1823export class AppTerminalComponent implements OnInit {
1924 @Input ( ) data : any ;
2025 @Input ( ) options : { size : 'normal' | 'large' } ;
21- au : any ;
22- commands : { command : string , visible : boolean , output : string , time : string } [ ] ;
23- noData : boolean ;
24- initScroll : boolean ;
25- scrollOptions : ISlimScrollOptions ;
26- scrollEvents : EventEmitter < SlimScrollEvent > ;
26+ hterm : hterm . Terminal ;
27+ terminalReady : boolean ;
28+ unwritenChanges : string ;
2729
2830 constructor (
2931 private elementRef : ElementRef ,
3032 @Inject ( DOCUMENT ) private document : any
3133 ) {
32- this . scrollOptions = {
33- barBackground : '#666' ,
34- gridBackground : '#000' ,
35- barBorderRadius : '10' ,
36- barWidth : '7' ,
37- gridWidth : '7' ,
38- barMargin : '2px 5px' ,
39- gridMargin : '2px 5px' ,
40- gridBorderRadius : '10' ,
41- alwaysVisible : false
42- } ;
43-
44- this . scrollEvents = new EventEmitter < SlimScrollEvent > ( ) ;
34+ hterm . hterm . defaultStorage = new hterm . lib . Storage . Local ( ) ;
35+ this . hterm = new hterm . hterm . Terminal ( ) ;
36+ this . terminalReady = false ;
37+ this . unwritenChanges = '' ;
4538 }
4639
4740 ngOnInit ( ) {
48- this . au = new AnsiUp . default ( ) ;
49- this . au . use_classes = true ;
50- this . commands = [ ] ;
51- this . noData = true ;
41+ this . hterm . onVTKeystroke = ( ) => { } ;
42+ this . hterm . showOverlay = ( ) => { } ;
43+ this . hterm . onTerminalReady = ( ) => {
44+ this . hterm . setWindowTitle = ( ) => { } ;
45+ this . hterm . prefs_ . set ( 'cursor-color' , 'transparent' ) ;
46+ this . hterm . prefs_ . set ( 'font-family' , 'monaco, menlo, monospace' ) ;
47+ this . hterm . prefs_ . set ( 'font-size' , 11 ) ;
48+ this . hterm . prefs_ . set ( 'audible-bell-sound' , '' ) ;
49+ this . hterm . prefs_ . set ( 'font-smoothing' , 'subpixel-antialiased' ) ;
50+ this . hterm . prefs_ . set ( 'enable-bold' , false ) ;
51+ this . hterm . prefs_ . set ( 'backspace-sends-backspace' , true ) ;
52+ this . hterm . prefs_ . set ( 'cursor-blink' , false ) ;
53+ this . hterm . prefs_ . set ( 'receive-encoding' , 'raw' ) ;
54+ this . hterm . prefs_ . set ( 'send-encoding' , 'raw' ) ;
55+ this . hterm . prefs_ . set ( 'alt-sends-what' , 'browser-key' ) ;
56+ this . hterm . prefs_ . set ( 'scrollbar-visible' , false ) ;
57+ this . hterm . prefs_ . set ( 'enable-clipboard-notice' , false ) ;
58+ this . hterm . prefs_ . set ( 'background-color' , '#000000' ) ;
59+ this . hterm . prefs_ . set ( 'foreground-color' , '#f8f8f2' ) ;
60+ hterm . lib . colors . stockColorPalette . splice ( 0 , terminalColorPallete . length )
61+ hterm . lib . colors . stockColorPalette = terminalColorPallete . concat (
62+ hterm . lib . colors . stockColorPalette ) ;
63+ this . hterm . prefs_ . set ( 'color-palette-overrides' , terminalColorPallete ) ;
64+
65+ this . terminalReady = true ;
66+ if ( this . unwritenChanges ) {
67+ this . printToTerminal ( this . unwritenChanges ) ;
68+ this . unwritenChanges = '' ;
69+ }
70+ } ;
71+
72+ this . hterm . decorate ( this . document . querySelector ( '.window-terminal-container' ) ) ;
73+ this . hterm . installKeyboard ( null ) ;
5274 }
5375
5476 ngOnChanges ( changes : SimpleChange ) {
5577 if ( ! this . data ) {
5678 return ;
5779 }
5880
59- this . noData = false ;
60-
6181 if ( typeof this . data . clear !== 'undefined' ) {
62- this . commands = [ ] ;
63- } else {
64- let output : string = this . au . ansi_to_html ( this . data ) ;
65- const regex = / = = [ & g t ; | > ] ( .* ) / g;
66- let match ;
67- let commands : string [ ] = [ ] ;
68-
69- if ( output . match ( regex ) ) {
70- while ( match = regex . exec ( output ) ) {
71- commands . push ( match [ 0 ] ) ;
72- }
73-
74- if ( commands . length > 1 ) {
75- this . commands = [ ] ;
76- }
77-
78- let retime = new RegExp ( '\\[exectime\\]: \\d*' , 'igm' ) ;
79- let times = [ ] ;
80- while ( match = retime . exec ( output ) ) {
81- let t = match [ 0 ] . replace ( / \[ e x e c t i m e \] : / igm, '' ) ;
82- times . push ( ( t / 10 ) . toFixed ( 0 ) ) ;
83- }
84-
85- this . commands = commands . reduce ( ( acc , curr , i ) => {
86- let next = commands [ i + 1 ] || '' ;
87- next = next . replace ( / [ \- \[ \] \/ \{ \} \( \) \* \+ \? \. \\ \^ \$ \| ] / g, '\\$&' ) ;
88- const c = curr . replace ( / [ \- \[ \] \/ \{ \} \( \) \* \+ \? \. \\ \^ \$ \| ] / g, '\\$&' ) ;
89- let re = new RegExp ( '(' + c + ')(' + '[\\s\\S]+' + ')(' + next + ')' ) ;
90- if ( ! output . match ( re ) ) {
91- re = new RegExp ( '(' + c + ')' + '[\\s\\S]+' ) ;
92- }
93- let time = times [ i ] ? Number ( times [ i ] ) : null ;
94- let out = output . match ( re ) && output . match ( re ) [ 2 ] ? output . match ( re ) [ 2 ] . trim ( ) : '' ;
95- out = out . replace ( retime , '' ) ;
96-
97- out = out . replace ( / ( \[ s u c c e s s \] : .* ) / igm, '<span class="ansi-green-fg">$1</span>' ) ;
98- out = out . replace ( / ( \[ e r r o r \] : .* ) / igm, '<span class="ansi-red-fg">$1</span>' ) ;
99- if ( output . includes ( '[exectime]: stopped' ) ) {
100- out = out . replace ( 'stopped' , '' ) ;
101- }
102-
103- return acc . concat ( {
104- command : curr . replace ( '==>' , '' ) . trim ( ) ,
105- visible : i === commands . length - 1 ? true : false ,
106- output : out ,
107- time : time ? this . getDuration ( time ) : ''
108- } ) ;
109- } , this . commands ) ;
110- } else {
111- if ( output . includes ( '[exectime]' ) ) {
112- if ( output !== '[exectime]: stopped' ) {
113- let retime = new RegExp ( '\\[exectime\]: \\d*' , 'igm' ) ;
114- let match = output . match ( retime ) ;
115- let time = Number ( ( Number ( match [ 0 ] . replace ( '[exectime]: ' , '' ) ) / 10 ) . toFixed ( 0 ) ) ;
116-
117- if ( this . commands [ this . commands . length - 1 ] ) {
118- this . commands [ this . commands . length - 1 ] . time = time ? this . getDuration ( time ) : '0ms' ;
119- }
120- }
121- } else {
122- output = output . replace ( / ( \[ s u c c e s s \] : .* ) / igm, '<span class="ansi-green-fg">$1</span>' ) ;
123- output = output . replace ( / ( \[ e r r o r \] : .* ) / igm, '<span class="ansi-red-fg">$1</span>' ) ;
124-
125- if ( this . commands [ this . commands . length - 1 ] ) {
126- this . commands [ this . commands . length - 1 ] . output += output ;
127- }
128- }
129- }
130-
131- if ( output . includes ( '[exectime]: stopped' ) ) {
132- if ( this . commands [ this . commands . length - 1 ] ) {
133- this . commands [ this . commands . length - 1 ] . time = 'stopped' ;
134- }
135-
136- this . commands . push ( {
137- command : 'Execution stopped, entered in debug mode.' ,
138- visible : true ,
139- output : '' ,
140- time : '...'
141- } ) ;
142- }
143-
144- if ( this . commands && this . commands . length ) {
145- this . commands = this . commands . map ( ( cmd , i ) => {
146- const v = i === this . commands . length - 1 || cmd . visible ;
147- cmd . visible = v ? true : false ;
148- return cmd ;
149- } ) ;
150- } else {
151- this . commands . push ( { command : output , visible : true , time : '.' , output : '' } ) ;
152- }
82+ this . hterm . keyboard . terminal . wipeContents ( ) ;
83+ return ;
15384 }
15485
155- setTimeout ( ( ) => {
156- const ev : SlimScrollEvent = {
157- type : 'scrollToBottom' ,
158- easing : 'linear' ,
159- duration : 50
160- } ;
161- this . scrollEvents . emit ( ev ) ;
162- } , 50 ) ;
163- }
164-
165- toggleCommand ( index : number ) {
166- this . commands [ index ] . visible = ! this . commands [ index ] . visible ;
167- setTimeout ( ( ) => this . recalculate ( ) ) ;
168- }
169-
170- recalculate ( ) : void {
171- const event : SlimScrollEvent = {
172- type : 'recalculate' ,
173- easing : 'linear'
174- } ;
86+ console . log ( this . data ) ;
17587
176- this . scrollEvents . emit ( event ) ;
88+ if ( ! this . terminalReady ) {
89+ this . unwritenChanges += this . data ;
90+ } else {
91+ this . printToTerminal ( this . data ) ;
92+ }
17793 }
17894
179- getDuration ( millis : number ) : string {
180- const dur = { } ;
181- const units = [
182- { label : 'ms' , mod : 100 } , // millis
183- { label : 'sec' , mod : 60 } ,
184- { label : 'min' , mod : 60 } ,
185- { label : 'h' , mod : 24 } ,
186- { label : 'd' , mod : 31 }
187- ] ;
188- units . forEach ( u => millis = ( millis - ( dur [ u . label ] = ( millis % u . mod ) ) ) / u . mod ) ;
189- const nonZero = ( u ) => { return dur [ u . label ] ; } ;
190- dur . toString = ( ) => {
191- return units
192- . reverse ( )
193- . filter ( nonZero )
194- . map ( u => dur [ u . label ] + u . label )
195- . join ( ', ' ) ;
196- } ;
197-
198- return dur . toString ( ) ;
95+ printToTerminal ( data : string ) {
96+ this . hterm . io . print ( this . data ) ;
97+ if ( this . hterm . keyboard . terminal
98+ && this . hterm . keyboard . terminal . scrollPort_
99+ && this . hterm . keyboard . terminal . scrollPort_ . isScrolledEnd ) {
100+ this . hterm . keyboard . terminal . scrollEnd ( ) ;
101+ }
199102 }
200103}
0 commit comments