projects/desktop/src/app/components/apps/terminal/terminal.component.ts

116 lines
3.4 KiB
TypeScript

import { Component, ElementRef, ViewChild, AfterViewInit, AfterViewChecked, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-terminal',
standalone: true,
imports: [CommonModule],
templateUrl: './terminal.component.html',
styleUrl: './terminal.component.scss',
})
export class TerminalComponent implements AfterViewInit, AfterViewChecked {
@ViewChild('terminalBody') terminalBody!: ElementRef<HTMLDivElement>;
@ViewChild('termInput') termInput!: ElementRef<HTMLInputElement>;
/** Command history for up/down arrow navigation. */
private history: string[] = [];
private historyIndex = -1;
/** Lines of terminal output. */
readonly lines = signal<string[]>([
'Butterfly Terminal v0.1',
'Type "help" for available commands.',
'',
]);
ngAfterViewInit(): void {
this.termInput?.nativeElement.focus();
}
onInput(event: Event): void {
// Just keep focus.
}
onKeyDown(event: KeyboardEvent): void {
if (event.key === 'Enter') {
const input = this.termInput.nativeElement;
const cmd = input.value.trim();
input.value = '';
if (cmd) {
this.history.push(cmd);
this.historyIndex = this.history.length;
this.executeCommand(cmd);
}
this.lines.update((l) => [...l, `> ${cmd}`]);
} else if (event.key === 'ArrowUp') {
event.preventDefault();
if (this.historyIndex > 0) {
this.historyIndex--;
this.termInput.nativeElement.value = this.history[this.historyIndex];
}
} else if (event.key === 'ArrowDown') {
event.preventDefault();
if (this.historyIndex < this.history.length - 1) {
this.historyIndex++;
this.termInput.nativeElement.value = this.history[this.historyIndex];
} else {
this.historyIndex = this.history.length;
this.termInput.nativeElement.value = '';
}
}
}
private executeCommand(cmd: string): void {
const parts = cmd.split(/\s+/);
const command = parts[0].toLowerCase();
switch (command) {
case 'help':
this.addOutput('Available commands: help, clear, echo, date, uptime, whoami, about');
break;
case 'clear':
this.lines.set([]);
return;
case 'echo':
this.addOutput(parts.slice(1).join(' '));
break;
case 'date':
this.addOutput(new Date().toString());
break;
case 'uptime':
this.addOutput(`System uptime: ${Math.floor(performance.now() / 1000)}s`);
break;
case 'whoami':
this.addOutput('butterfly-user');
break;
case 'about':
this.addOutput('Butterfly Desktop Environment v0.1');
this.addOutput('A browser-based remote desktop environment.');
break;
default:
this.addOutput(`Unknown command: ${command}. Type "help" for available commands.`);
}
}
private addOutput(text: string): void {
this.lines.update((l) => [...l, text]);
}
/** Auto-scroll to bottom whenever the view updates. */
ngAfterViewChecked(): void {
this.scrollToBottom();
}
/** Focus the input when the terminal is clicked. */
onClick(): void {
this.termInput?.nativeElement.focus();
}
/** Called after view updates to scroll to bottom. */
scrollToBottom(): void {
if (this.terminalBody) {
this.terminalBody.nativeElement.scrollTop = this.terminalBody.nativeElement.scrollHeight;
}
}
}