Init rebase to Svelte and TS
This commit is contained in:
10
src/lib/assets/deFlag.svg
Executable file
10
src/lib/assets/deFlag.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#464655;" d="M473.655,88.276H38.345C17.167,88.276,0,105.443,0,126.621v73.471h512v-73.471
|
||||
C512,105.443,494.833,88.276,473.655,88.276z"/>
|
||||
<path style="fill:#FFE15A;" d="M0,385.379c0,21.177,17.167,38.345,38.345,38.345h435.31c21.177,0,38.345-17.167,38.345-38.345
|
||||
v-73.471H0V385.379z"/>
|
||||
<rect y="200.09" style="fill:#FF4B55;" width="512" height="111.81"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 668 B |
2
src/lib/assets/enFlag.svg
Executable file
2
src/lib/assets/enFlag.svg
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"><path fill="#00247D" d="M0 9.059V13h5.628zM4.664 31H13v-5.837zM23 25.164V31h8.335zM0 23v3.941L5.63 23zM31.337 5H23v5.837zM36 26.942V23h-5.631zM36 13V9.059L30.371 13zM13 5H4.664L13 10.837z"></path><path fill="#CF1B2B" d="M25.14 23l9.712 6.801a3.977 3.977 0 0 0 .99-1.749L28.627 23H25.14zM13 23h-2.141l-9.711 6.8c.521.53 1.189.909 1.938 1.085L13 23.943V23zm10-10h2.141l9.711-6.8a3.988 3.988 0 0 0-1.937-1.085L23 12.057V13zm-12.141 0L1.148 6.2a3.994 3.994 0 0 0-.991 1.749L7.372 13h3.487z"></path><path fill="#EEE" d="M36 21H21v10h2v-5.836L31.335 31H32a3.99 3.99 0 0 0 2.852-1.199L25.14 23h3.487l7.215 5.052c.093-.337.158-.686.158-1.052v-.058L30.369 23H36v-2zM0 21v2h5.63L0 26.941V27c0 1.091.439 2.078 1.148 2.8l9.711-6.8H13v.943l-9.914 6.941c.294.07.598.116.914.116h.664L13 25.163V31h2V21H0zM36 9a3.983 3.983 0 0 0-1.148-2.8L25.141 13H23v-.943l9.915-6.942A4.001 4.001 0 0 0 32 5h-.663L23 10.837V5h-2v10h15v-2h-5.629L36 9.059V9zM13 5v5.837L4.664 5H4a3.985 3.985 0 0 0-2.852 1.2l9.711 6.8H7.372L.157 7.949A3.968 3.968 0 0 0 0 9v.059L5.628 13H0v2h15V5h-2z"></path><path fill="#CF1B2B" d="M21 15V5h-6v10H0v6h15v10h6V21h15v-6z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/lib/assets/favicon.svg
Normal file
1
src/lib/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
10
src/lib/assets/frFlag.svg
Executable file
10
src/lib/assets/frFlag.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#41479B;" d="M38.345,88.273C17.167,88.273,0,105.44,0,126.618v258.759c0,21.177,17.167,38.345,38.345,38.345
|
||||
h132.322V88.273H38.345z"/>
|
||||
<rect x="170.67" y="88.277" style="fill:#F5F5F5;" width="170.67" height="335.45"/>
|
||||
<path style="fill:#FF4B55;" d="M473.655,88.273H341.333v335.448h132.322c21.177,0,38.345-17.167,38.345-38.345V126.618
|
||||
C512,105.44,494.833,88.273,473.655,88.273z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 695 B |
1509
src/lib/assets/fs/fs.json
Executable file
1509
src/lib/assets/fs/fs.json
Executable file
File diff suppressed because it is too large
Load Diff
9
src/lib/assets/jaFlag.svg
Executable file
9
src/lib/assets/jaFlag.svg
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#F5F5F5;" d="M473.655,88.275H38.345C17.167,88.275,0,105.442,0,126.62V385.38
|
||||
c0,21.177,17.167,38.345,38.345,38.345h435.31c21.177,0,38.345-17.167,38.345-38.345V126.62
|
||||
C512,105.442,494.833,88.275,473.655,88.275z"/>
|
||||
<circle style="fill:#FF4B55;" cx="256" cy="255.999" r="97.1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 620 B |
2
src/lib/assets/plFlag.svg
Executable file
2
src/lib/assets/plFlag.svg
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"><path fill="#EEE" d="M32 5H4a4 4 0 0 0-4 4v9h36V9a4 4 0 0 0-4-4z"></path><path fill="#DC143C" d="M0 27a4 4 0 0 0 4 4h28a4 4 0 0 0 4-4v-9H0v9z"></path></svg>
|
||||
|
After Width: | Height: | Size: 506 B |
BIN
src/lib/assets/quan.ttf
Normal file
BIN
src/lib/assets/quan.ttf
Normal file
Binary file not shown.
1
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
152
src/lib/stores/bash/bash.ts
Normal file
152
src/lib/stores/bash/bash.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { command } from '$app/server';
|
||||
import { COMMANDS, GROUP, HELP_ARGS, PASSWD, type CommandArg, type ICommand } from './static';
|
||||
import { VirtualFS } from './fs';
|
||||
import { Terminal, type PrintData } from '../terminal';
|
||||
import { Stack } from '../stack';
|
||||
import path from 'path';
|
||||
|
||||
export interface Permission {
|
||||
r: boolean;
|
||||
w: boolean;
|
||||
x: boolean;
|
||||
}
|
||||
|
||||
export interface BashInitArgs {
|
||||
stdio?: Terminal;
|
||||
user: User;
|
||||
fs: any;
|
||||
}
|
||||
|
||||
// TODO: Finish this
|
||||
export enum ExitCode {
|
||||
SUCCESS = 0,
|
||||
ERROR = 1
|
||||
}
|
||||
|
||||
export interface User {
|
||||
username: string;
|
||||
passwd: string; //HASHED PASSWORD
|
||||
uid: number; // Normal user 1000+ System user 1-999 root - 0
|
||||
gid: number; // Primary group | 'Users' 1000 - Others - 1000+ root - 0
|
||||
home: string;
|
||||
history: string[];
|
||||
cwd?: string[];
|
||||
pwd?: string[];
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
groupname: string;
|
||||
gid: number; // Primary group 'Users' 1000 - Others - 1000+ root - 0
|
||||
members: string[];
|
||||
}
|
||||
|
||||
export class Bash {
|
||||
private vfs: VirtualFS;
|
||||
private _passwd: User[];
|
||||
private _instances: Stack<User>;
|
||||
private _group: Group[];
|
||||
private _terminal!: Terminal;
|
||||
private user: User;
|
||||
private _helpArgs: CommandArg[];
|
||||
private _commands: Record<string, ICommand>;
|
||||
|
||||
constructor(args: BashInitArgs) {
|
||||
this.user = args.user;
|
||||
this._helpArgs = HELP_ARGS;
|
||||
this._commands = COMMANDS;
|
||||
this._passwd = PASSWD;
|
||||
this._group = GROUP;
|
||||
this._terminal = args.stdio!;
|
||||
this._instances = new Stack<User>();
|
||||
|
||||
this.vfs = new VirtualFS({ fs: args.fs, user: args.user });
|
||||
|
||||
console.log(this._commands);
|
||||
}
|
||||
|
||||
updateHistory(input: string): void {
|
||||
if ((this.user.history.length = 255)) {
|
||||
this.user.history.unshift(...this.user.history.splice(-1));
|
||||
this.user.history[0] = input;
|
||||
} else {
|
||||
this.user.history.push(input);
|
||||
this.user.history.unshift(...this.user.history.splice(-1));
|
||||
}
|
||||
}
|
||||
|
||||
getCwd(): string[] {
|
||||
return this.vfs.cwd;
|
||||
}
|
||||
|
||||
getPwd(): string[] {
|
||||
return this.vfs.pwd;
|
||||
}
|
||||
|
||||
getUser(): User {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
getFs(): VirtualFS {
|
||||
return this.vfs;
|
||||
}
|
||||
|
||||
changeUser(user: User) {
|
||||
this.user = user;
|
||||
this.vfs.home = this.vfs._splitPathString(user.home);
|
||||
this.vfs.cwd = user.cwd ? user.cwd : this.vfs._splitPathString(user.home);
|
||||
this.vfs.pwd = user.pwd ? user.pwd : this.vfs._splitPathString(user.home);
|
||||
}
|
||||
|
||||
executeCommand(commandName: string, ...args: string[]): void {
|
||||
const command = this._commands[commandName];
|
||||
if (!command) this.throwError(ExitCode.ERROR);
|
||||
if (command.root) {
|
||||
if (this._group[1].members.includes(this.user.username)) {
|
||||
let out: ExitCode = command.method.call(this, ...args);
|
||||
this.throwError(out);
|
||||
}
|
||||
this.throwError(ExitCode.ERROR);
|
||||
}
|
||||
|
||||
let out: ExitCode = command.method.call(this, ...args);
|
||||
this.throwError(out);
|
||||
}
|
||||
|
||||
throwError(code: ExitCode, data?: any): void {
|
||||
//TODO: Make data some interface format or smh.
|
||||
switch (code) {
|
||||
default:
|
||||
this.appendNewResult(this.vfs.pwd, 'Success!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
userLogin(username: string, passwd: string): ExitCode {
|
||||
const user: User | undefined = this._passwd.find((u) => u.username === username);
|
||||
if (user === undefined) return ExitCode.ERROR;
|
||||
|
||||
if (user.passwd === passwd) {
|
||||
this._instances.push(user);
|
||||
this.changeUser(user);
|
||||
return ExitCode.ERROR; //TODO: Make it return the exitcode of changeUser() if needed
|
||||
} else return ExitCode.ERROR;
|
||||
}
|
||||
|
||||
userLogout() {
|
||||
this._instances.pop();
|
||||
if (this._instances.size() === 0) {
|
||||
//TODO: Implement system logout
|
||||
} else {
|
||||
this.changeUser(this._instances.peek()!);
|
||||
}
|
||||
}
|
||||
|
||||
appendNewResult(path: string[], output: any) {
|
||||
const data: PrintData = {
|
||||
path: this.vfs.formatPath(this.vfs.pathArrayToString(path)),
|
||||
output: output
|
||||
};
|
||||
console.log('NEW RESULT - ', data);
|
||||
this._terminal.PrintOutput(data);
|
||||
}
|
||||
}
|
||||
130
src/lib/stores/bash/fs.ts
Normal file
130
src/lib/stores/bash/fs.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { Permission, User } from './bash';
|
||||
|
||||
export enum Type {
|
||||
Directory = 16384,
|
||||
File = 32768
|
||||
}
|
||||
|
||||
type NodePerms = {
|
||||
user: Permission;
|
||||
group: Permission;
|
||||
other: Permission;
|
||||
};
|
||||
|
||||
export interface FsInitArgs {
|
||||
fs: any;
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface TreeNode {
|
||||
name: string;
|
||||
type: Type;
|
||||
readonly: boolean;
|
||||
interactible: boolean;
|
||||
func: any;
|
||||
children: TreeNode[];
|
||||
content: string; // Path to the content of the file
|
||||
link: string[]; // Symlink
|
||||
permission: NodePerms;
|
||||
owner: string;
|
||||
group: string;
|
||||
modtime: Date;
|
||||
}
|
||||
|
||||
export class VirtualFS {
|
||||
private root: TreeNode; // TODO make this the correct type
|
||||
|
||||
home: string[];
|
||||
cwd: string[];
|
||||
pwd: string[];
|
||||
|
||||
constructor(args: FsInitArgs) {
|
||||
this.root = args.fs;
|
||||
this.home = this._splitPathString(args.user.home);
|
||||
this.cwd = args.user.cwd ? args.user.cwd : this.home;
|
||||
this.pwd = args.user.pwd ? args.user.pwd : this.cwd;
|
||||
|
||||
console.log(this.home);
|
||||
console.log(this.cwd);
|
||||
console.log(this.pwd);
|
||||
|
||||
console.log('VFS INIT ', this._getNodeByPathArray(['/', 'home', 'kamil']));
|
||||
}
|
||||
|
||||
_splitPathString(path: string): string[] {
|
||||
if (path === '/') return ['/'];
|
||||
|
||||
const raw: string[] = path.split('/');
|
||||
const parts: string[] = [];
|
||||
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
if (raw[i].length > 0) parts.push(raw[i]);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
_isAbsolutePath = (path: string): boolean => {
|
||||
return typeof path === 'string' && path.startsWith('/');
|
||||
};
|
||||
|
||||
pathArrayToString(path: string[]): string {
|
||||
if (path.length === 1 && path[0] === '/') return '/';
|
||||
return '/' + path.join('/');
|
||||
}
|
||||
|
||||
formatPath(path: string): string {
|
||||
console.log('FORMAT PATH ', path);
|
||||
const prefix = this.pathArrayToString(this.home);
|
||||
if (path.startsWith(prefix)) {
|
||||
return path.replace(prefix, '~');
|
||||
} else return path;
|
||||
}
|
||||
|
||||
resolvePath(path: string): string[] {
|
||||
if (path === '' || path === undefined || path === null) return this.cwd.slice();
|
||||
if (path.startsWith('/') && path.length === 1) return [];
|
||||
|
||||
if (path.startsWith('~')) {
|
||||
const trail: string = path === '~' ? '' : path.slice(1);
|
||||
const home: string = this.pathArrayToString(this.home);
|
||||
path = home + (trail ? (trail.startsWith('/') ? '' : '/') + trail : '');
|
||||
}
|
||||
|
||||
const start = this._isAbsolutePath(path) ? [] : this.cwd.slice();
|
||||
console.log('START', start);
|
||||
const parts = this._splitPathString(path);
|
||||
console.log('PARTS', parts);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const seg = parts[i];
|
||||
|
||||
if (seg === '.' || seg === '') continue;
|
||||
if (seg === '..') {
|
||||
if (start.length > 1) start.pop();
|
||||
continue;
|
||||
}
|
||||
start.push(seg);
|
||||
}
|
||||
|
||||
if (start.length === 0) return [];
|
||||
console.log('OUTPUT', start);
|
||||
return start;
|
||||
}
|
||||
|
||||
_getNodeByPathArray(path: string[]): TreeNode {
|
||||
if (path.length === 1 && path[0] === '/') return this.root;
|
||||
|
||||
let node: TreeNode = this.root;
|
||||
const parts: string[] = path.slice(path[0] === '/' ? 1 : 0);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const seg: string = parts[i];
|
||||
|
||||
if (node.type === Type.File) return node;
|
||||
const newNode = node.children.find((child) => child.name === seg);
|
||||
if (newNode !== undefined) node = newNode;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
229
src/lib/stores/bash/static.ts
Normal file
229
src/lib/stores/bash/static.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
import { Bash, ExitCode, type Group, type User } from './bash';
|
||||
import { Type, type TreeNode } from './fs';
|
||||
|
||||
export type CommandArg = `-${string}`;
|
||||
|
||||
export interface ICommand {
|
||||
method: (this: Bash, ...args: any[]) => ExitCode;
|
||||
args: CommandArg[] | string[] | null;
|
||||
help: string;
|
||||
root: boolean;
|
||||
}
|
||||
|
||||
export const GROUP: Group[] = [
|
||||
{
|
||||
groupname: 'sudo',
|
||||
gid: 69,
|
||||
members: ['root', 'admin']
|
||||
},
|
||||
{
|
||||
groupname: 'users',
|
||||
gid: 1000,
|
||||
members: ['admin', 'user']
|
||||
}
|
||||
];
|
||||
|
||||
export const PASSWD: User[] = [
|
||||
{
|
||||
username: 'root',
|
||||
passwd: '123',
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
home: '/',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
},
|
||||
{
|
||||
username: 'admin',
|
||||
passwd: '456',
|
||||
uid: 1001,
|
||||
gid: 1000,
|
||||
home: '/home/admin',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
},
|
||||
{
|
||||
username: 'user',
|
||||
passwd: '789',
|
||||
uid: 1002,
|
||||
gid: 1000,
|
||||
home: '/home/user',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
}
|
||||
];
|
||||
|
||||
export const HELP_ARGS: CommandArg[] = ['-h', '--help'];
|
||||
|
||||
export const cmd_return = function (this: Bash, ...args: string[]): ExitCode {
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const cmd_cd = function (this: Bash, ...args: string[]): ExitCode {
|
||||
const path = args[0];
|
||||
let targetNode: TreeNode;
|
||||
|
||||
if (args.length > 1) return ExitCode.ERROR; // Too many args
|
||||
|
||||
// if no args cd into home dir
|
||||
|
||||
if (args.length === 0) {
|
||||
this.getFs().cwd = this.getFs().home;
|
||||
return ExitCode.SUCCESS;
|
||||
}
|
||||
|
||||
// if the arg is - cd make your current dir the prev dir and vice versa
|
||||
|
||||
if (args[0] === '-') {
|
||||
[this.getFs().cwd, this.getFs().pwd] = [this.getFs().pwd, this.getFs().cwd];
|
||||
return ExitCode.SUCCESS;
|
||||
}
|
||||
|
||||
// Change the input STRING path from relative to absolute by replacing ~ with the home directory path
|
||||
|
||||
//TODO: Change that to a global function inside fs class to parse all possible path formats????? already exists, need to verify
|
||||
|
||||
let resolvedPath = path.startsWith('~')
|
||||
? path.replace('~', this.getFs().pathArrayToString(this.getFs().home))
|
||||
: path;
|
||||
|
||||
this.getFs().pwd = this.getFs().cwd;
|
||||
targetNode = this.getFs()._getNodeByPathArray(this.getFs().resolvePath(resolvedPath)); // Conversion from STRING path to ARRAY
|
||||
|
||||
if (!targetNode) return ExitCode.ERROR;
|
||||
if (targetNode.type !== Type.Directory) return ExitCode.ERROR;
|
||||
//if () return ExitCode.ERROR; // Check for read permissions on node and user
|
||||
|
||||
this.getFs().cwd = this.getFs().resolvePath(resolvedPath); // CD was successfull, change current dir to the verified target dir
|
||||
return ExitCode.SUCCESS;
|
||||
};
|
||||
|
||||
export const COMMANDS = {
|
||||
return: {
|
||||
method: cmd_return,
|
||||
args: [] as CommandArg[],
|
||||
help: 'PATH TO HELP.MD',
|
||||
root: false
|
||||
},
|
||||
cd: {
|
||||
method: cmd_cd,
|
||||
args: [] as string[],
|
||||
help: 'PATH TO HELP.MD',
|
||||
root: false
|
||||
}
|
||||
} satisfies Record<string, ICommand>;
|
||||
|
||||
/* //export const commands {
|
||||
return: {
|
||||
method: this.cmd_return,
|
||||
flags: [],
|
||||
help: "Help about this command",
|
||||
},
|
||||
ls: {
|
||||
method: this.cmd_ls,
|
||||
flags: [],
|
||||
help: "./help/ls.md",
|
||||
},
|
||||
echo: {
|
||||
method: this.cmd_echo,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
touch: {
|
||||
method: this.cmd_touch,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
mkdir: {
|
||||
method: this.cmd_mkdir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
pwd: {
|
||||
method: this.cmd_pwd,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cd: {
|
||||
method: this.cmd_cd,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
exit: {
|
||||
method: this.cmd_exit,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cp: {
|
||||
method: this.cmd_cp,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
mv: {
|
||||
method: this.cmd_mv,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
rmdir: {
|
||||
method: this.cmd_rmdir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cat: {
|
||||
method: this.cmd_cat,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
dir: {
|
||||
method: this.cmd_dir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
less: {
|
||||
method: this.cmd_less,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
chown: {
|
||||
method: this.cmd_chown,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
chmod: {
|
||||
method: this.cmd_chmod,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
reboot: {
|
||||
method: this.cmd_reboot,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
help: {
|
||||
method: this.cmd_help,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
whoami: {
|
||||
method: this.cmd_whoami,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
rm: {
|
||||
method: this.cmd_rm,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
sudo: {
|
||||
method: this.cmd_sudo,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
su: {
|
||||
method: this.cmd_su,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
clear: {
|
||||
method: this.cmd_clear,
|
||||
flags: [],
|
||||
help: "",
|
||||
}
|
||||
} */
|
||||
13
src/lib/stores/lang.ts
Normal file
13
src/lib/stores/lang.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import plFlag from '$lib/assets/plFlag.svg';
|
||||
import enFlag from '$lib/assets/enFlag.svg';
|
||||
import deFlag from '$lib/assets/deFlag.svg';
|
||||
import frFlag from '$lib/assets/frFlag.svg';
|
||||
import jaFlag from '$lib/assets/jaFlag.svg';
|
||||
|
||||
export const langs = {
|
||||
pl: {},
|
||||
en: {},
|
||||
de: {},
|
||||
ja: {},
|
||||
fr: {}
|
||||
};
|
||||
31
src/lib/stores/stack.ts
Normal file
31
src/lib/stores/stack.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export class Stack<T> {
|
||||
private items: T[] = [];
|
||||
|
||||
push(element: T): void {
|
||||
this.items.push(element);
|
||||
}
|
||||
|
||||
pop(): T | undefined {
|
||||
return this.items.pop();
|
||||
}
|
||||
|
||||
peek(): T | undefined {
|
||||
return this.items[this.items.length - 1];
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
return [...this.items];
|
||||
}
|
||||
}
|
||||
95
src/lib/stores/terminal.ts
Normal file
95
src/lib/stores/terminal.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { FileOutput } from '@lucide/svelte';
|
||||
import Cursor from '../../modules/terminal/Cursor.svelte';
|
||||
import { Bash, ExitCode, type BashInitArgs, type User } from './bash/bash';
|
||||
import { Stack } from './stack';
|
||||
import type { VirtualFS } from './bash/fs';
|
||||
|
||||
export interface TerminalMode {}
|
||||
|
||||
export interface TermInitArgs {
|
||||
bash: BashInitArgs;
|
||||
}
|
||||
|
||||
export interface ParsedInput {
|
||||
command: string;
|
||||
args: string[];
|
||||
}
|
||||
|
||||
export interface PrintData {
|
||||
path: string;
|
||||
output: any; // TODO: Make this be any predefined format of outputs like ls, ls w/ flags and so on;
|
||||
}
|
||||
|
||||
export interface PageCallbacks {
|
||||
print: (data: PrintData) => void;
|
||||
}
|
||||
|
||||
export class Terminal {
|
||||
private bash: Bash;
|
||||
private callbacks: Partial<PageCallbacks> = {};
|
||||
|
||||
constructor(args: TermInitArgs) {
|
||||
args.bash.stdio = this;
|
||||
this.bash = new Bash(args.bash);
|
||||
}
|
||||
|
||||
private _parseInput(input: string): ParsedInput {
|
||||
const result: ParsedInput = { command: '', args: [] };
|
||||
let current: string = '';
|
||||
let inQuotes: boolean = false;
|
||||
let quoteChar: Stack<string> = new Stack<string>();
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input[i];
|
||||
|
||||
if ((char === '"' || char === "'") && !inQuotes) {
|
||||
inQuotes = true;
|
||||
quoteChar.push(char);
|
||||
continue;
|
||||
} else if (char === quoteChar.peek() && inQuotes) {
|
||||
inQuotes = false;
|
||||
quoteChar.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === ' ' && !inQuotes) {
|
||||
if (current !== '') {
|
||||
result.command = current;
|
||||
current = '';
|
||||
}
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
}
|
||||
if (current !== '') result.args.push(current);
|
||||
return result;
|
||||
}
|
||||
|
||||
executeCommand(input: string): void {
|
||||
this.bash.updateHistory(input);
|
||||
const parsed: ParsedInput = this._parseInput(input);
|
||||
this.bash.executeCommand(parsed.command, ...parsed.args);
|
||||
}
|
||||
|
||||
registerCallbacks(callbacks: PageCallbacks): void {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
getUser(): User {
|
||||
return this.bash.getUser();
|
||||
}
|
||||
|
||||
getCwd(): string {
|
||||
const fs: VirtualFS = this.bash.getFs();
|
||||
let temp: string = fs.formatPath(fs.pathArrayToString(this.bash.getCwd()));
|
||||
return temp;
|
||||
}
|
||||
|
||||
userLogin(username: string, passwd: string): ExitCode {
|
||||
return this.bash.userLogin(username, passwd);
|
||||
}
|
||||
|
||||
PrintOutput(data: PrintData) {
|
||||
this.callbacks.print?.(data);
|
||||
}
|
||||
}
|
||||
17
src/lib/stores/theme.ts
Normal file
17
src/lib/stores/theme.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
function getInitalTheme(): string {
|
||||
if (typeof window === 'undefined') return 'dark';
|
||||
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
if (savedTheme === 'dark' || savedTheme === 'light') return savedTheme;
|
||||
|
||||
const sysPrefTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
return sysPrefTheme ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
export const theme = writable(getInitalTheme());
|
||||
|
||||
theme.subscribe((value) => {
|
||||
if (typeof window !== 'undefined') localStorage.setItem('theme', value);
|
||||
});
|
||||
Reference in New Issue
Block a user