Compare commits

..

7 Commits

17 changed files with 367 additions and 164 deletions

32
package-lock.json generated
View File

@@ -1195,9 +1195,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.48.4",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.4.tgz",
"integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==",
"version": "2.50.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.50.2.tgz",
"integrity": "sha512-875hTUkEbz+MyJIxWbQjfMaekqdmEKUUfR7JyKcpfMRZqcGyrO9Gd+iS1D/Dx8LpE5FEtutWGOtlAh4ReSAiOA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1207,13 +1207,13 @@
"@types/cookie": "^0.6.0",
"acorn": "^8.14.1",
"cookie": "^0.6.0",
"devalue": "^5.3.2",
"devalue": "^5.6.2",
"esm-env": "^1.2.2",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"set-cookie-parser": "^3.0.0",
"sirv": "^3.0.0"
},
"bin": {
@@ -1226,11 +1226,15 @@
"@opentelemetry/api": "^1.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"typescript": "^5.3.3",
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
@@ -2401,9 +2405,9 @@
}
},
"node_modules/devalue": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.4.2.tgz",
"integrity": "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==",
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
"integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
"dev": true,
"license": "MIT"
},
@@ -3042,9 +3046,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4156,9 +4160,9 @@
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz",
"integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==",
"dev": true,
"license": "MIT"
},

View File

@@ -33,7 +33,7 @@
},
"Interactible": false,
"Func": null,
"Parent": null
"Parent": 1
},
"2": {
"Inode": 2,

View File

@@ -105,6 +105,10 @@ export class Bash {
return this._terminal.getTerminalWidth();
}
getTerminalFontSize(): number {
return this._terminal.getFontSize();
}
hasSudoPerms(uid: number): boolean {
return this._group[1].members.includes(uid);
}

View File

@@ -0,0 +1,15 @@
import { ExitCode, type Bash } from "../bash";
import type { CommandArgs, ICommand, Result } from "../static";
export const cmd_clear = function(this: Bash, args: CommandArgs): Result {
let result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd() };
result.exitCode = ExitCode.SUCCESS;
return result;
}
export const clear: ICommand = {
method: cmd_clear,
flags: [] as string[],
help: 'PATH TO HELP.md',
root: false
};

View File

@@ -1,5 +1,5 @@
import { Bash, ExitCode, type Permission, type TimeStamps } from '../bash';
import { Type, type NodePerms, type TreeNode } from '../fs';
import { Type, VirtualFS, type NodePerms, type TreeNode } from '../fs';
import { Sort, SortNodeBy } from '../sort';
import type { CommandArgs, ICommand, Result, resultData } from '../static';
@@ -18,7 +18,7 @@ const LsEntryUtils = {
return Object.entries(entry)
.filter(([_, value]) => value !== '')
.map(([_, value]) => value)
.join(' ');
.join(' ').trimStart();
}
};
@@ -38,6 +38,7 @@ const months: readonly string[] = [
];
export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
const Fs = this.getFs();
const resultData: resultData = { cmd: 'ls', data: null, args: args };
const result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd(), data: resultData };
const nodes: TreeNode[] = [];
@@ -55,10 +56,10 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
this.throwError(result); //No such flag/s
}
if (args.args.length === 0) nodes.push(this.getFs().getNodeByINode(this.getFs().cwd));
if (args.args.length === 0) nodes.push(Fs.getNodeByINode(Fs.cwd));
for (let i = 0; i < args.args.length; i++) {
const node = this.getFs().resolvePath(args.args[i]);
const node = Fs.resolvePath(args.args[i]);
if (node === null) this.throwError(result); //no such path (i think this will never occur as backed methods have error cases implemented - which is wrong)
nodes.push(node);
@@ -71,15 +72,19 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
const dummysonoerror: HTMLElement = document.createElement('div');
const Fs: VirtualFS = this.getFs();
const flagInfo = checkFlags(args.flags);
const nodes: TreeNode[] = data;
let nodeIndex: number = 0;
const f_a: boolean = flagInfo.has('a') || flagInfo.has('all');
const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all');
const f_G: boolean = flagInfo.has('G') || flagInfo.has('no-group');
const f_h: boolean = flagInfo.has('h') || flagInfo.has('human-readable');
const f_r: boolean = flagInfo.has('r') || flagInfo.has('reverse');
const f_R: boolean = flagInfo.has('R') || flagInfo.has('recursive');
const f_Q: boolean = flagInfo.has('Q') || flagInfo.has('quote-name');
const f_n: boolean = flagInfo.has('n') || flagInfo.has('numeric-uid-gid');
const f_N: boolean = flagInfo.has('N') || flagInfo.has('literal');
@@ -96,138 +101,170 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
const f_g: boolean = flagInfo.has('g');
const f_o: boolean = flagInfo.has('o');
let shouldShift: boolean = false
const valuedArgs = args.flags.filter((flag: string) =>
flag.includes('=') && ls.flags.includes(flag.split('=')[0]));
if (f_l || f_g || f_o) {
const w: HTMLElement = document.createElement('div');
const w: HTMLElement = document.createElement('div');
for (const node of nodes) {
const elem: HTMLElement = document.createElement('div');
const children: TreeNode[] = node.children.map((child) => this.getFs().getNodeByINode(child));
if(f_R) {
const treeWalkResult: TreeNode[] = [];
const treeWalkCallback = (node: TreeNode) => { treeWalkResult.push(node); }
for(const node of nodes) {
treeWalkResult.push(node);
Fs.recursiveTraversalPre(node, treeWalkCallback);
}
nodes.length = 0;
nodes.push(...treeWalkResult.filter((node) => node.type != Type.File));
}
for (const node of nodes) {
const elem: HTMLElement = document.createElement('div');
const childrenMap: TreeNode[] = node.children.map((child) => Fs.getNodeByINode(child));
const children: TreeNode[] = (f_a || f_A)
? childrenMap
: childrenMap.filter((child) => !child.name.startsWith('.'));
const timeArg = valuedArgs.find((flag) => flag.startsWith('time'));
const shouldNamesShift: boolean = children.some((child) => child.name.match(/\s/) !== null);
let timestamp: SortNodeBy.ATIME | SortNodeBy.CTIME | SortNodeBy.MTIME = SortNodeBy.MTIME;
if(f_a && !f_A) {
const current: TreeNode = node;
current.name = '.';
const parent: TreeNode = Fs.getNodeByINode(node.parent);
parent.name = '..';
children.unshift(current, parent);
}
if(timeArg) {
let value: string = timeArg.split('=')[1];
if (value && isValidNodeTimestamp(value)) timestamp = value;
}
if (!f_U && !f_f) {
const sortArg = valuedArgs.find((flag) => flag.startsWith('sort'));
let sortBy: SortNodeBy = SortNodeBy.NAME;
if(f_t) sortBy = timestamp;
if(f_S) sortBy = SortNodeBy.SIZE;
if(f_X) sortBy = SortNodeBy.EXTENSION;
if(sortArg) {
let value = sortArg.split('=')[1];
if(value && isValidNodeSortMethod(value)) sortBy = value;
}
Sort.nodeArraySort.call(this, children, f_r, sortBy);
}
if (f_l || f_g || f_o) {
const rows: string[] = [];
const timeArg = valuedArgs.find((flag) => flag.startsWith('time'));
let timestamp: SortNodeBy.ATIME | SortNodeBy.CTIME | SortNodeBy.MTIME = SortNodeBy.MTIME;
if(timeArg) {
let value: string = timeArg.split('=')[1];
if (value && isValidNodeTimestamp(value)) {
timestamp = value;
console.log(timestamp);
}
}
if (!f_U && !f_f) {
const sortArg = valuedArgs.find((flag) => flag.startsWith('sort'));
let sortBy: SortNodeBy = SortNodeBy.NAME;
if(f_t) sortBy = timestamp;
if(f_S) sortBy = SortNodeBy.SIZE;
if(f_X) sortBy = SortNodeBy.EXTENSION;
if(sortArg) {
let value = sortArg.split('=')[1];
if(value && isValidNodeSortMethod(value)) {
sortBy = value;
console.log(sortBy, 'sortBy');
}
}
Sort.nodeArraySort.call(this, children, f_r, sortBy);
}
const sizes = children.map((child) => child.size);
const maxSizeWidth = Math.max(...sizes.map((size) => size));
const maxSizeWidth = Math.max(
...children.map((child) => child.size.toString().length));
for (const child of children) {
if (child.name.startsWith('.') && !(f_a || f_A)) continue;
const cols: LsEntry = {
const entry: LsEntry = {
inode: null,
perms: formatPermission(child),
children: formatChildren(child),
owners: formatOwners.call(this, child, flagInfo),
size: formatSize.call(this, f_h, child, maxSizeWidth, f_si),
modt: formatModtime(child, timestamp),
name: formatName(child, flagInfo, shouldShift)
name: formatName(child, flagInfo, shouldNamesShift)
};
if (f_i) cols.inode = child.inode;
if (f_i) entry.inode = child.inode;
rows.push(LsEntryUtils.toString(cols));
}
if (f_a && !f_A) {
const current: LsEntry = {
inode: null,
perms: formatPermission(node),
children: formatChildren(node),
owners: formatOwners.call(this, node, flagInfo),
size: formatSize.call(this, f_h, node, maxSizeWidth, f_si),
modt: formatModtime(node, timestamp),
name: '.'
};
let parent: LsEntry = {
...current,
name: '..'
};
if (node.parent) {
const parentNode: TreeNode = this.getFs().getNodeByINode(node.parent);
parent = {
inode: null,
perms: formatPermission(parentNode),
children: formatChildren(parentNode),
owners: formatOwners.call(this, parentNode, flagInfo),
size: formatSize.call(this, f_h, parentNode, maxSizeWidth, f_si),
modt: formatModtime(parentNode, timestamp),
name: '..'
};
}
if (f_i) {
current.inode = node.inode;
parent.inode = node.parent ? node.parent : node.inode;
}
if (f_r) {
rows.push(LsEntryUtils.toString(parent), LsEntryUtils.toString(current));
} else {
rows.unshift(LsEntryUtils.toString(current), LsEntryUtils.toString(parent));
}
rows.push(LsEntryUtils.toString(entry));
}
//TODO: Calculate the total size of contents in the node
rows.unshift('total ' + node.children.length.toString());
if (nodes.length > 1) {
const nodePath: string =
node.name === '/' ? '/:' : `${this.getFs().getPathByInode(node.inode).slice(1)}:`;
rows.unshift(nodePath);
rows.push('\n');
}
for(const row of rows) {
const name: string = row[row.length - 1];
if(!name.startsWith('"') || !name.startsWith("'"))
name.padStart(1, ' ');
else continue;
const nodePath: HTMLElement = document.createElement('p');
nodePath.innerText = `${Fs.getPathByInode(node.inode)}:`;
w.appendChild(nodePath);
if(nodeIndex +1 < nodes.length) elem.style.marginBottom = `${this.getTerminalFontSize() * 2}px`;
}
for (let i = 0; i < rows.length; i++) {
const p: HTMLElement = document.createElement('p');
p.innerText = rows[i];
elem.appendChild(p);
}
w.appendChild(elem);
}
return w;
}
else {
const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8);
return dummysonoerror; //TEMP SO NO ERROR CUZ RETURNS HTMLElement EVERY TIME, DELETE LATER
let columns: string[][] = [];
let colWidths: number[];
let lowBound: number = 1;
let highBound: number = children.length;
while(lowBound + 1 < highBound) {
const c = lowBound + (highBound - lowBound) / 2;
columns = [];
colWidths = [];
let fileIndex = 0;
for(let i = 0; i < c; i++) {
if(fileIndex >= children.length) break;
columns[i] = [];
for(let j = 0; j < Math.ceil(children.length / c); j++) {
if(fileIndex >= children.length) break;
columns[i].push(children[fileIndex].name);
fileIndex++;
}
colWidths.push(Math.max(...columns[i].map((name) => name.length)));
}
const calcWidth: number = colWidths.reduce((prev, curr) => prev + curr) + ((c-1) * 3);
if(calcWidth < maxWidth) lowBound = c + 1;
else if(calcWidth > maxWidth) highBound = c -1
}
const wrapper: HTMLElement = document.createElement('div');
wrapper.style.display = 'flex';
wrapper.style.marginBottom = `${this.getTerminalFontSize() * 2}px`;
let fileIndex = 0;
for(let i = 0; i < lowBound; i++) {
if(fileIndex >= children.length) break;
const col: HTMLElement = document.createElement('div');
for(let j = 0; j < Math.ceil(children.length / lowBound); j++) {
if(fileIndex >= children.length) break;
const entry: HTMLElement = document.createElement('p');
entry.innerText = formatName(children[fileIndex], flagInfo, shouldNamesShift);
col.appendChild(entry);
fileIndex++;
}
wrapper.appendChild(col);
}
if (nodes.length > 1) {
const nodePath: HTMLElement = document.createElement('p');
nodePath.innerText = `${Fs.getPathByInode(node.inode)}`;
w.appendChild(nodePath);
}
w.appendChild(wrapper);
}
nodeIndex++;
}
return w;
}
function isValidNodeSortMethod(value: string): value is SortNodeBy {
@@ -285,6 +322,7 @@ function formatChildren(node: TreeNode): string {
if (!node.children) throw new Error('children array is null on this node');
const c = node.children.length.toString();
console.log(c, "TEST TEST");
return c.length > 1 ? c : ` ${c}`;
}
@@ -321,17 +359,29 @@ function formatModtime(node: TreeNode, sortBy: SortNodeBy.ATIME | SortNodeBy.CTI
}
function formatName(node: TreeNode, flag: any, shouldShift: boolean) {
let name: string;
let name: string = node.name;
const char: string = flag.has('Q') ? '"' : "'";
if (/\s/.test(node.name)) {
name = `${char}${node.name}${char}`
shouldShift = true;
} else {
name = `${node.name}`;
if (/\s/.test(node.name)) name = `${char}${name}${char}`;
if(flag.has('p') && node.type === Type.Directory) name = `${name}/`
if((flag.has('l') || flag.has('g') || flag.has('o'))) {
// Is the ls in long format
if (!(/\s/.test(node.name))) {
//Shift non quoted names 1 char right to align if any names in group have a quote
name = `${shouldShift ? ' ' : ''}${name}`;
}
}
else {
if (!(/\s/.test(node.name))) {
name = ` ${name}`;
}
name = `${name} `;
}
return flag.has('p') && node.type === Type.Directory ? `${name}/` : name;
return name
}
const checkFlags = (pFlags: string[]) => {
@@ -358,6 +408,7 @@ export const ls: ICommand = {
't',
'S',
'r',
'R',
'Q',
'p',
'o',
@@ -374,6 +425,7 @@ export const ls: ICommand = {
'no-group',
'human-readable',
'reverse',
'recursive',
'quote-name',
'indicator-style',
'literal',

View File

@@ -19,7 +19,7 @@ export type FsInitArgs = {
export type TreeNode = {
inode: number;
parent?: number;
parent: number;
name: string;
type: Type;
size: number; //Size in Bytes
@@ -110,11 +110,11 @@ export class VirtualFS {
return typeof path === 'string' && path.startsWith('/');
};
getPathByInode(inode: number): string {
public getPathByInode(inode: number): string {
return this._iNodeToPathString(inode);
}
formatPath(path: string): string {
public formatPath(path: string): string {
console.log(path, 'formatPath');
const prefix = this._iNodeToPathString(this.home);
@@ -123,7 +123,7 @@ export class VirtualFS {
} else return path;
}
resolvePath(path: string): TreeNode {
public resolvePath(path: string): TreeNode {
if (path === '/') return this.getNodeByINode(this.rootINode);
let parsedPath: string = path;
@@ -144,22 +144,32 @@ export class VirtualFS {
return Node;
}
getNodeByINode(inode: number): TreeNode {
public getNodeByINode(inode: number): TreeNode {
const node: TreeNode | undefined = this.FsTable.get(inode);
if (!node) throw new Error('Could not get the node, no such i node exists');
if (!node) throw new Error(`Could not get the node, no such inode exists - ${inode}`);
return node;
}
/* private _getPathToNode(node: TreeNode): string[] {
const path: string[] = [];
let current = node;
path.push(node.name);
public recursiveTraversalPre(node: TreeNode, callback: (param: TreeNode) => void, childIndex?: number[], depthIndex?: number): void {
if(!depthIndex) depthIndex = 0;
if(!childIndex) childIndex = [0];
while (current.parent) {
current = current.parent;
path.unshift(current.name);
if(node.type != Type.File && node.children[childIndex[depthIndex]]) {
node = this.getNodeByINode(node.children[childIndex[depthIndex]]);
depthIndex++;
if(!childIndex[depthIndex]) childIndex[depthIndex] = 0;
callback(node);
}
else {
node = this.getNodeByINode(node.parent);
childIndex[depthIndex] = 0;
depthIndex--;
childIndex[depthIndex]++;
}
return path;
} */
if(depthIndex < 0) return;
this.recursiveTraversalPre(node, callback, childIndex, depthIndex);
}
}

View File

@@ -0,0 +1,78 @@
export class Posix {
private static readonly CharTable = {
// Space and punctuation (32-47)
' ': 32,
'!': 33,
'"': 34,
'#': 35,
'$': 36,
'%': 37,
'&': 38,
"'": 39,
'(': 40,
')': 41,
'*': 42,
'+': 43,
',': 44,
'-': 45,
'.': 46,
'/': 47,
// Numbers 0-9 (48-57)
'0': 48, '1': 49, '2': 50, '3': 51, '4': 52,
'5': 53, '6': 54, '7': 55, '8': 56, '9': 57,
// More punctuation (58-64)
':': 58,
';': 59,
'<': 60,
'=': 61,
'>': 62,
'?': 63,
'@': 64,
// LETTERS - INTERLEAVED AaBbCc... (65-116)
'A': 65, 'a': 66,
'B': 67, 'b': 68,
'C': 69, 'c': 70,
'D': 71, 'd': 72,
'E': 73, 'e': 74,
'F': 75, 'f': 76,
'G': 77, 'g': 78,
'H': 79, 'h': 80,
'I': 81, 'i': 82,
'J': 83, 'j': 84,
'K': 85, 'k': 86,
'L': 87, 'l': 88,
'M': 89, 'm': 90,
'N': 91, 'n': 92,
'O': 93, 'o': 94,
'P': 95, 'p': 96,
'Q': 97, 'q': 98,
'R': 99, 'r': 100,
'S': 101, 's': 102,
'T': 103, 't': 104,
'U': 105, 'u': 106,
'V': 107, 'v': 108,
'W': 109, 'w': 110,
'X': 111, 'x': 112,
'Y': 113, 'y': 114,
'Z': 115, 'z': 116,
// Specials after letters (now 117-126)
'[': 117,
'\\': 118,
']': 119,
'^': 120,
'_': 121,
'`': 122,
'{': 123,
'|': 124,
'}': 125,
'~': 126
} as const;
public static GetCharWeight(char: string): number {
return this.CharTable[char as keyof typeof this.CharTable];
}
}

View File

@@ -0,0 +1,7 @@
import type { Bash } from "./bash";
import type { TreeNode } from "./fs";
export class Search {
}

View File

@@ -1,5 +1,6 @@
import type { TreeNode } from './fs';
import type { Bash } from './bash';
import { Posix } from './posix';
export enum SortNodeBy {
NAME = 'name',
@@ -19,7 +20,7 @@ export class Sort {
reverse: boolean = false,
sortBy: SortNodeBy = SortNodeBy.NAME
): TreeNode[] {
if (nodes.length === 0) throw new Error('Tried to sort an empty node array!');
if (nodes.length === 0) {console.warn('Tried to sort an empty node array!'); return [];}
const parsedNodes: TreeNode[] = [];
if (typeof nodes[0] === 'number') {
@@ -85,17 +86,19 @@ export class Sort {
): number {
switch (sortBy) {
case SortNodeBy.NAME: {
const minLength = Math.min(a.name.length, b.name.length);
const nameA: string = a.name.startsWith('.') ? a.name.slice(1) : a.name;
const nameB: string = b.name.startsWith('.') ? b.name.slice(1) : b.name;
const minLength = Math.min(nameA.length, nameB.length);
for (let i = 0; i < minLength; i++) {
const charCodeA = a.name.charCodeAt(i);
const charCodeB = b.name.charCodeAt(i);
const charCodeA = Posix.GetCharWeight(nameA[i]);
const charCodeB = Posix.GetCharWeight(nameB[i]);
if (charCodeA !== charCodeB) {
return reverse ? charCodeB - charCodeA : charCodeA - charCodeB;
}
}
return reverse ? b.name.length - a.name.length : a.name.length - b.name.length;
return reverse ? nameB.length - nameA.length : nameA.length - nameB.length;
}
case SortNodeBy.MTIME:
case SortNodeBy.ATIME:

View File

@@ -1,6 +1,7 @@
import { Bash, ExitCode, type Group, type User } from './bash';
import { ls } from './commands/ls';
import { cd } from './commands/cd';
import { clear } from './commands/clear';
export type ICommand = {
method: (this: Bash, args: CommandArgs) => Result;
@@ -76,7 +77,8 @@ export const PASSWD: User[] = [
export const COMMANDS = {
cd,
ls
ls,
clear
} as const satisfies Record<string, ICommand>;
/* //export const commands {

View File

@@ -8,7 +8,7 @@ export function isInitializing(): boolean {
return initializing;
}
function jsonToNodeTable(data: any, parent?: number): Map<number, TreeNode> {
function jsonToNodeTable(data: any): Map<number, TreeNode> {
const FsTable: Map<number, TreeNode> = new Map<number, TreeNode>();
const entryList = Object.entries(data);

View File

@@ -35,7 +35,6 @@ export function print(e: HTMLElement, data: PrintData): void {
}
export function clear(): void {
for (const n of outputInstances) {
unmount(n);
}
console.log("outInstances", outputInstances);
unmount(outputInstances);
}

View File

@@ -22,7 +22,9 @@ export type PrintData = {
export type PageCallbacks = {
print: (data: PrintData) => void;
clear: () => void;
getWidth: () => number;
getFontSize: () => number;
};
export class Terminal {
@@ -106,10 +108,16 @@ export class Terminal {
const width = this.callbacks.getWidth?.();
if(!width) { throw new Error('somehow width is undefined still after all the checks'); }
console.log(width);
return width;
}
getFontSize(): number {
const size = this.callbacks.getFontSize?.();
if(!size) { throw new Error('somehow font size is undefined still after all the checks'); }
return size;
}
getCwd(): string {
const fs: VirtualFS = this.bash.getFs();
console.log(fs.getPathByInode(this.bash.getCwd()));

View File

@@ -20,13 +20,26 @@
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));
console.log(padding);
return e.clientWidth - (padding * 2);
}
function handleInput(e: KeyboardEvent) {
switch (e.key) {
function getFontSize() {
return 10;
/* 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(' ').width; */
}
function handleInput(event: KeyboardEvent) {
switch (event.key) {
case 'Enter': {
terminal.executeCommand(inputValue);
updateTerminal();
@@ -45,6 +58,13 @@
//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
@@ -54,7 +74,9 @@
if (!e) return;
printOutput(e, data);
},
getWidth: getWidth
clear: clear,
getWidth: getWidth,
getFontSize: getFontSize
};
//Test user with basic data so the bash can run
@@ -84,8 +106,8 @@
});
</script>
<label for="input" onkeydowncapture={(e) => handleInput(e)}>
<div id="terminal" class="terminal-window shadow-() size-full rounded-md shadow-bg">
<label for="input" onkeydowncapture={(e) => handleInput(e)} 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"
>
@@ -94,7 +116,7 @@
<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">
<div class=" flex mr-2 grow">
<h5>{username}</h5>
<!-- prettier-ignore -->
<h5 class=" mr-2">@terminal: </h5>
@@ -102,7 +124,7 @@
</div>
</div>
<div
class="inner-content scroll-hidden h-[860px] 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"
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>

View File

@@ -8,7 +8,7 @@
<span class=" pointer self-start pr-2">$</span>
<p>{cmd}</p>
</div>
<div style="white-space: nowrap;" class=" relative">
<div style="white-space: pre;" class=" relative">
{#if typeof output === 'string'}
{output}
{:else if output instanceof Element}

View File

@@ -6,7 +6,7 @@
</script>
<Settings></Settings>
<div class="h-dvh w-full p-24">
<div class="h-dvh w-full p-24 flex justify-center">
<TerminalModule />
</div>

View File

@@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
@@ -11,7 +11,6 @@ const config = {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter()
}
};