Skip to content

Commit

Permalink
refactorings on terminal fullscreen - remove duplicated code
Browse files Browse the repository at this point in the history
  • Loading branch information
Pop John committed Oct 22, 2024
1 parent 11bbc82 commit f134c04
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 413 deletions.
Original file line number Diff line number Diff line change
@@ -1,84 +1,16 @@
<!-- Copyright 2024 Cisco Systems, Inc. and its affiliates
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0 -->

<ms-dialog>
<div class="ms-terminal-wrapper">
<div class="top-terminal-section">
<div class="heading-section-title mb-1 flex items-center">Terminal</div>
<div class="buttons-wrapper">
<button
mat-icon-button
color="primary"
matTooltip="Search terminal"
[msTooltipPanel]="searchTooltipContent"
[closeOnBackdropClick]="false"
[showCloseButton]="true"
position="bottom"
[allowCloseOnEscPress]="false"
(closed)="disposeSearch(); searchFormControl.setValue('')"
#tooltipPanel="msTooltipPanel">
<mat-icon fontSet="ms" fontIcon="icon-MagnifyingGlass"></mat-icon>
</button>

<button
mat-icon-button
color="primary"
matTooltip="Terminal history"
(click)="openTerminalMessagesHistoryDialog(); tooltipPanel.closePanel()">
<mat-icon>history</mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Scroll to top" (click)="scrollToTopTerminal()">
<mat-icon fontSet="ms" fontIcon="icon-ArrowUp"></mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Scroll to bottom" (click)="scrollToBottomTerminal()">
<mat-icon fontSet="ms" fontIcon="icon-ArrowDown"></mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Clear terminal" (click)="clearTerminal()">
<mat-icon> clear_all</mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Exit fullscreen" (click)="closeDialog()">
<mat-icon>{{ 'fullscreen_exit' }}</mat-icon>
</button>

<button
mat-icon-button
color="warn"
matTooltip="Stop current process"
[disabled]="!isScriptActive"
(click)="ctaStopScript()">
<mat-icon fontSet="ms" fontIcon="icon-X" class="mat-error"></mat-icon>
</button>
</div>
</div>
<div class="terminal">
<div class="terminal-wrapper" #terminal></div>
<ms-terminal-toolbar
(clearTerminal)="terminal.clearTerminal()"
(scrollToTopTerminal)="terminal.scrollToTop()"
(scrollToBottomTerminal)="terminal.scrollToBottom()"
(searchTerminal)="terminal.search($event)"
(disposeSearch)="terminal.clearSearch()"
[isFullscreen]="true"
(exitFullscreen)="closeDialog()"></ms-terminal-toolbar>
</div>
<ms-terminal-xterm #terminal></ms-terminal-xterm>
</div>
</ms-dialog>

<ng-template #searchTooltipContent>
<div class="form-field-container dark">
<mat-label> Search... </mat-label>
<mat-form-field subscriptSizing="dynamic">
<input msErrorDisplay matInput [formControl]="searchFormControl" />
<mat-icon fontSet="ms" fontIcon="icon-MagnifyingGlass" matSuffix></mat-icon>
</mat-form-field>
</div>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -15,212 +15,24 @@
// SPDX-License-Identifier: Apache-2.0

import { CommonModule } from '@angular/common';
import { Component, ElementRef, Inject, ViewChild } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatTooltipModule } from '@angular/material/tooltip';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { FitAddon } from '@xterm/addon-fit';
import { SearchAddon } from '@xterm/addon-search';
import { Terminal } from '@xterm/xterm';
import { delay, filter } from 'rxjs';
import { ScriptActions } from '../../../../../../state/core/script';
import { ScriptFacadeService } from '../../../../../core/services';
import { isScriptActive } from '../../../../../model-compression/models/enums/script-status.enum';
import { MsTooltipPanelDirective } from '../../../../directives/ms-tooltip-panel/ms-tooltip-panel.directive';
import { isNilOrEmptyString } from '../../../../shared.utils';
import {
DIALOG_DATA,
DialogConfig,
DialogRef,
DialogService,
DialogStatus,
MsDialogComponent
} from '../../../ms-dialog';
import { TerminalWebSocketService } from '../../services/terminal-websocket.service';
import { MsTerminalMessagesHistoryDialogComponent } from '../ms-terminal-messages-history-dialog/ms-terminal-messages-history-dialog.component';
import { MsTerminalComponent } from '../ms-terminal/ms-terminal.component';
import { Component } from '@angular/core';
import { UntilDestroy } from '@ngneat/until-destroy';
import { DialogRef, MsDialogComponent } from '../../../ms-dialog';
import { MsTerminalToolbarComponent } from '../ms-terminal-toolbar/ms-terminal-toolbar.component';
import { MsTerminalXtermComponent } from '../ms-terminal-xterm/ms-terminal-xterm.component';

@UntilDestroy({})
@Component({
selector: 'ms-terminal-fullscreen-dialog',
standalone: true,
imports: [
CommonModule,
MsDialogComponent,
MsTerminalComponent,
MatButtonModule,
MatTooltipModule,
MatIconModule,
MsTooltipPanelDirective,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule
],
imports: [CommonModule, MsDialogComponent, MsTerminalToolbarComponent, MsTerminalXtermComponent],
templateUrl: './ms-terminal-fullscreen-dialog.component.html',
styleUrl: './ms-terminal-fullscreen-dialog.component.scss'
})
export class MsTerminalFullscreenDialogComponent {
@ViewChild('terminal', { static: true }) terminalDiv!: ElementRef;
constructor(private dialogRef: DialogRef) {}

isScriptActive: boolean = false;

searchFormControl = new FormControl<string>('');

private terminal: Terminal = new Terminal({
cursorBlink: true,
theme: {
background: '#D0D4D9',
foreground: '#000000',
cursor: '#000000',
selectionBackground: '#FFDD00',
selectionForeground: '#000000'
},
allowProposedApi: true
});

private fitAddon: FitAddon = new FitAddon();
private searchAddon = new SearchAddon();
private resizeObserver?: ResizeObserver;

constructor(
@Inject(DIALOG_DATA) public dialogConfig: DialogConfig,
private dialogRef: DialogRef,
private dialogService: DialogService,
private terminalWebSocketService: TerminalWebSocketService,
private scriptFacadeService: ScriptFacadeService
) {
document.body.classList.add('no-scroll');
}

ngOnInit(): void {
this.listenToScriptStateChanges();
this.listenToSearchFormControlChanges();

this.initializeTerminal();
this.subscribeToWebSocketMessages();
}

private listenToSearchFormControlChanges(): void {
this.searchFormControl.valueChanges
.pipe(
untilDestroyed(this),
delay(300),
filter((value: string | null): value is string => !isNilOrEmptyString(value))
)
.subscribe((value: string) => {
this.search(value);
});
}

ngAfterViewInit(): void {
setTimeout(() => {
this.adjustHeightToParent();
}, 0);
this.fitTerminalToContainer();
}

closeDialog() {
this.dialogRef.close({ status: DialogStatus.CLOSE });
}

private listenToScriptStateChanges(): void {
this.scriptFacadeService.scriptStatus$.pipe(untilDestroyed(this)).subscribe((state) => {
this.isScriptActive = isScriptActive(state);
});
}

openTerminalMessagesHistoryDialog() {
this.dialogService.open(MsTerminalMessagesHistoryDialogComponent, {
title: 'Terminal history',
showSaveButton: false,
width: '60vw',
height: '75vh'
} as DialogConfig);
}

public search(value: string) {
this.searchAddon.findNext(value, {
decorations: {
matchBackground: '#FFFF00',
matchBorder: '#FFFF00',
matchOverviewRuler: '#FFFF00',
activeMatchBackground: '#FFFF00',
activeMatchBorder: '#FFFF00',
activeMatchColorOverviewRuler: '#FFFF00'
}
});
}

public disposeSearch() {
this.searchAddon.clearDecorations();
}

ctaStopScript() {
this.scriptFacadeService.dispatch(ScriptActions.stopScript());
}

private subscribeToWebSocketMessages(): void {
this.terminalWebSocketService.messages$.pipe(untilDestroyed(this)).subscribe((message: string) => {
this.terminal.write(message);
});
}

private initializeTerminal(): void {
this.terminal.loadAddon(this.fitAddon);
this.terminal.loadAddon(this.searchAddon);

this.terminal.open(this.terminalDiv.nativeElement);

this.setupResizeObserver();

this.terminal.onData((data) => {
this.terminalWebSocketService.sendMessage(data);
});
}

private setupResizeObserver(): void {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}

this.resizeObserver = new ResizeObserver(() => this.fitTerminalToContainer());
this.resizeObserver.observe(this.terminalDiv.nativeElement);
}

private fitTerminalToContainer(): void {
this.fitAddon.fit();
}

private adjustHeightToParent(): void {
const rightSideElement = this.terminalDiv.nativeElement.parentElement.parentElement.parentElement.parentElement;

if (rightSideElement) {
let finalHeight: number;
const heightCorrection = 100;
finalHeight = rightSideElement.offsetHeight - heightCorrection;
this.terminalDiv.nativeElement.style.height = `${finalHeight}px`;
}
}

clearTerminal() {
this.terminalWebSocketService.clearScreen();
}

scrollToTopTerminal() {
this.terminal.scrollToTop();
}

scrollToBottomTerminal() {
this.terminal.scrollToBottom();
}

ngOnDestroy(): void {
this.resizeObserver?.disconnect();
this.terminal.dispose();
closeDialog(): void {
this.dialogRef.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@
<mat-icon fontSet="ms" fontIcon="icon-ArrowDown"></mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Clear terminal" (click)="clearTerminal()">
<button mat-icon-button color="primary" matTooltip="Clear terminal" (click)="clearTerminalScreen()">
<mat-icon> clear_all</mat-icon>
</button>

<button mat-icon-button color="primary" matTooltip="Toggle fullscreen" (click)="openFullScreenMode()">
<mat-icon>{{ 'fullscreen' }}</mat-icon>
<mat-icon>fullscreen</mat-icon>
</button>

<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// SPDX-License-Identifier: Apache-2.0

import { CommonModule } from '@angular/common';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
Expand Down Expand Up @@ -54,16 +54,17 @@ import { MsTerminalMessagesHistoryDialogComponent } from '../ms-terminal-message
providers: [DialogService]
})
export class MsTerminalToolbarComponent implements OnInit {
isFullscreen: boolean = false;
isScriptActive: boolean = false;
@Input() isFullscreen = false;
@Input() isScriptActive = false;
@Output() clearTerminal = new EventEmitter<void>();
@Output() scrollToTopTerminal = new EventEmitter<void>();
@Output() scrollToBottomTerminal = new EventEmitter<void>();
@Output() searchTerminal = new EventEmitter<string>();
@Output() disposeSearch = new EventEmitter<void>();
@Output() exitFullscreen = new EventEmitter<void>();

searchFormControl = new FormControl<string>('');

@Output() scrollToTopTerminal = new EventEmitter();
@Output() scrollToBottomTerminal = new EventEmitter();
@Output() searchTerminal = new EventEmitter<string>();
@Output() disposeSearch = new EventEmitter();

constructor(
private scriptFacadeService: ScriptFacadeService,
private dialogService: DialogService,
Expand All @@ -75,7 +76,7 @@ export class MsTerminalToolbarComponent implements OnInit {
this.listenToSearchFormControlChanges();
}

public clearTerminal() {
public clearTerminalScreen() {
this.terminalWebSocketService.clearScreen();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- Copyright 2024 Cisco Systems, Inc. and its affiliates
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0 -->

<div class="terminal">
<div class="terminal-wrapper" #terminal></div>
</div>
Loading

0 comments on commit f134c04

Please sign in to comment.