Files
gornodox.dev/src/modules/Terminal.svelte

169 lines
4.7 KiB
Svelte
Executable File

<script lang="ts">
import { Terminal, type PrintData } from '$lib/stores/terminal/terminal';
import { onMount } from 'svelte';
import Input from './terminal/Input.svelte';
import { initTerminal, isInitializing } from '$lib/stores/terminal/init.svelte';
import { clear, print } from '$lib/stores/terminal/stdio/io';
import type { User } from '$lib/stores/bash/etc/userData';
const clearTerminal = (): void => clear();
const printOutput = (e: HTMLElement, d: PrintData): void => print(e, d);
function updateTerminal() {
username = terminal!.getUser().username;
cwd = terminal!.getCwd();
}
function getWidth() {
const e = document.getElementById('cout');
if(!e){
throw new Error('cant get width of the teminal element. Its null');
}
//gets an int from padding property value (which is a string) by cutting last 2 letters "px" and parsing to int
const padding: number = parseInt(window.getComputedStyle(e, null).getPropertyValue('padding').slice(0, -2));
return e.clientWidth - (padding * 2);
}
function getFontSize() {
const e = document.getElementById('cout');
if(!e) {
throw new Error('cant get font size of the terminal element. its null');
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
ctx.font = `${window.getComputedStyle(e, null).getPropertyValue('font-size')} 'JetBrains Mono', monospace;`;
return ctx.measureText('M').width;
}
function inputHandler(event: KeyboardEvent) {
switch (event.key) {
case 'Enter': {
terminal.executeCommand(inputValue);
updateTerminal();
break;
}
case 'ArrowRight': {
//TODO: Move cursor visually
}
case 'ArrowLeft': {
//TODO: Move cursor visually
}
case 'ArrowUp': {
//TODO: Make a traverse history function with up/down args
}
case 'ArrowDown': {
//TODO: Make a traverse history function with up/down args
}
}
const elem = document.getElementById('cout');
if(!elem){
throw new Error('cant scroll to bottom, element is null');
}
elem.scrollTop = elem.scrollHeight;
}
//Callback initializer
const callbackInit = {
print: (data: any) => {
const e = document.getElementById('outputWrapper');
if (!e) return;
printOutput(e, data);
},
clear: clearTerminal,
getWidth: getWidth,
getFontSize: getFontSize
};
//Test user with basic data so the bash can run
let testUser: User = {
username: 'kamil',
passwd: '123',
uid: 0,
gid: 0,
home: '/home/kamil',
history: []
};
//Empty terminal variable where the terminal class instance will be stored
let terminal: Terminal;
let username: string = $state(testUser.username);
let cwd: string = $state(testUser.home);
let inputValue = $state<string>('');
onMount(async () => {
try {
terminal = await initTerminal({ username: 'root', password: '123'}, callbackInit, 'en_US');
updateTerminal();
} catch (error) {
console.error('onMount trycatch failed', error);
}
});
</script>
<label for="input" onkeydowncapture={(event) => inputHandler(event)} class="w-11/12">
<div id="terminal" class="terminal-window shadow-() h-full w-full rounded-md shadow-bg">
<div
class="terminal-bar flex h-9 w-full flex-row items-center rounded-t-md bg-bg-dark text-center font-terminal text-sm font-bold text-primary-dark light:bg-bg-dark-light light:text-primary-light"
>
<div class="dots-wrapper mx-2.5 flex h-full flex-row items-center justify-center gap-2.5">
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
</div>
<div class=" flex mr-2 grow">
<h5>{username}</h5>
<!-- prettier-ignore -->
<h5 class=" mr-2">@terminal: </h5>
<h5>{cwd}</h5>
</div>
</div>
<div
class="inner-content scroll-hidden h-7/8 origin-top overflow-y-auto rounded-b-md bg-bg-light-dark p-4 text-text-dark shadow-subtle light:bg-bg-lighter-light light:text-text-light"
id="cout"
>
<div id="outputWrapper"></div>
<Input {cwd} bind:inputValue />
</div>
</div>
</label>
<style>
* {
transition: var(--transition-standard);
}
.dots-wrapper {
& > button {
border: none;
&:hover {
transform: translateY(-0.1rem);
}
&:active {
transform: translate(0);
}
&:nth-child(1) {
background-color: rgb(255, 0, 0);
&:active {
background-color: rgb(255, 100, 100);
}
}
&:nth-child(2) {
background-color: rgb(255, 165, 0);
&:active {
background-color: rgb(255, 215, 50);
}
}
&:nth-child(3) {
background-color: rgb(50, 205, 50);
&:active {
background-color: rgb(100, 255, 100);
}
}
}
}
</style>