Implemented command options with input values

This commit is contained in:
Prozilla 2024-04-29 23:18:55 +02:00
parent 96a77f6f2e
commit 357792ec7d
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
31 changed files with 246 additions and 210 deletions

View file

@ -19,7 +19,7 @@ See [features/apps/terminal/commands.js](../../../../src/features/apps/terminal/
```js
// features/apps/terminal/commands/touch.js
export const touch = new Command("touch")
export const touch = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Change file timestamps",

25
package-lock.json generated
View file

@ -31,6 +31,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"babel-plugin-wildcard": "^7.0.0",
"eslint-plugin-jsdoc": "^46.4.6",
"gh-pages": "^5.0.0"
}
@ -5636,6 +5637,30 @@
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
"integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA=="
},
"node_modules/babel-plugin-wildcard": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-wildcard/-/babel-plugin-wildcard-7.0.0.tgz",
"integrity": "sha512-3duaRaULzxmmgr+YO9EPEE3m/irVyOWKvXCOz2S41tlp0g2UarSEtpM++D3IS+asK205OOqW1ajnjFeoRRqfZg==",
"dev": true,
"dependencies": {
"rimraf": "^2.6.2"
},
"bin": {
"bpwc": "index.js"
}
},
"node_modules/babel-plugin-wildcard/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",

View file

@ -106,16 +106,30 @@ export function Terminal({ startPath, setTitle, close: exit, active }) {
// Get options
const options = [];
args.filter((arg) => arg.startsWith("-")).forEach((option) => {
const inputs = {};
args.filter((arg) => arg.startsWith("-")).forEach((option, index) => {
const addOption = (key) => {
if (options.includes(key))
return;
options.push(key);
const commandOption = command.getOption(options[options.length - 1]);
if (commandOption?.isInput) {
const optionInput = args[args.indexOf(option) + 1];
inputs[commandOption.short] = optionInput;
removeFromArray(optionInput, args);
}
};
if (option.startsWith("--")) {
const longOption = option.substring(2).toLowerCase();
if (!options.includes(longOption))
options.push(longOption);
addOption(longOption);
} else {
const shortOptions = option.substring(1).split("");
shortOptions.forEach((shortOption) => {
if (!options.includes(shortOption))
options.push(shortOption);
addOption(shortOption);
});
}
@ -143,7 +157,8 @@ export function Terminal({ startPath, setTitle, close: exit, active }) {
hostname: HOSTNAME,
rawInputValue,
options,
exit
exit,
inputs
});
if (response == null)
@ -214,8 +229,10 @@ export function Terminal({ startPath, setTitle, close: exit, active }) {
submitInput(value);
setInputKey((previousKey) => previousKey + 1);
} else if (key === "ArrowUp") {
event.preventDefault();
updateHistoryIndex(1);
} else if (key === "ArrowDown") {
event.preventDefault();
updateHistoryIndex(-1);
} else if (!stream && (event.ctrlKey || event.metaKey) && key === "c") {
setInputValue((value) => value + "^C");

View file

@ -15,25 +15,27 @@ import { VirtualRoot } from "../../virtual-drive/root/virtualRoot.js";
* @param {string} options.rawInputValue
* @param {string[]} options.options
* @param {Function} options.exit
* @param {[]} options.inputs
* @returns {string|{ blank: boolean }}
*/
/**
* @typedef {object} optionType
* @property {string} long
* @property {string} short
* @property {boolean} isInput
*/
export default class Command {
/** @type {string} */
name;
/** @type {optionType[]} */
options = [];
/** @type {executeType} */
execute = () => {};
/**
* @param {string} name
* @param {executeType} execute
*/
constructor(name, execute) {
this.name = name;
this.execute = execute;
}
/**
* @param {string} name
* @returns {Command}
@ -78,4 +80,28 @@ export default class Command {
this.manual = { purpose, usage, description, options };
return this;
}
/**
* @param {optionType} option
* @returns {Command}
*/
addOption({ short, long, isInput }) {
this.options.push({ short, long, isInput });
return this;
}
/**
* @param {string} key
* @returns {optionType}
*/
getOption(key) {
let matchingOption = null;
this.options.forEach((option) => {
if (option.short === key || option.long === key)
matchingOption = option;
});
return matchingOption;
}
}

View file

@ -1,29 +1,15 @@
import Command from "./command.js";
import { cat } from "./commands/cat.js";
import { cd } from "./commands/cd.js";
import { clear } from "./commands/clear.js";
import { compgen } from "./commands/compgen.js";
import { cowsay } from "./commands/cowsay.js";
import { dir } from "./commands/dir.js";
import { echo } from "./commands/echo.js";
import { exit } from "./commands/exit.js";
import { fortune } from "./commands/fortune.js";
import { help } from "./commands/help.js";
import { hostname } from "./commands/hostname.js";
import { ls } from "./commands/ls.js";
import { make } from "./commands/make.js";
import { man } from "./commands/man.js";
import { mkdir } from "./commands/mkdir.js";
import { neofetch } from "./commands/neofetch.js";
import { pwd } from "./commands/pwd.js";
import { reboot } from "./commands/reboot.js";
import { rm } from "./commands/rm.js";
import { rmdir } from "./commands/rmdir.js";
import { sl } from "./commands/sl.js";
import { touch } from "./commands/touch.js";
import { uptime } from "./commands/uptime.js";
import { whatis } from "./commands/whatis.js";
import { whoami } from "./commands/whoami.js";
// Dynamically import commands
const context = require.context("./commands", false, /\.js$/);
const commands = [];
context.keys().forEach((key) => {
const commandModule = context(key);
const commandName = Object.keys(commandModule)[0];
const command = commandModule[commandName];
command.setName(commandName.toLowerCase());
commands.push(command);
});
export default class CommandsManager {
/**
@ -52,31 +38,5 @@ export default class CommandsManager {
return matches;
}
static COMMANDS = [
echo,
clear,
ls,
cd,
dir,
pwd,
touch,
mkdir,
rm,
rmdir,
hostname,
neofetch,
fortune,
cowsay,
make,
cat,
man,
reboot,
compgen,
whoami,
whatis,
exit,
help,
uptime,
sl,
];
static COMMANDS = commands;
}

View file

@ -1,7 +1,7 @@
import { VirtualFile } from "../../../virtual-drive/file/virtualFile.js";
import Command from "../command.js";
export const cat = new Command("cat")
export const cat = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Concetenate files and display on the terminal screen",

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const cd = new Command("cd")
export const cd = new Command()
.setManual({
purpose: "Change the current directory",
usage: "cd path",

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const clear = new Command("clear")
export const clear = new Command()
.setManual({
purpose: "Clear terminal screen",
usage: "clear",

View file

@ -1,7 +1,7 @@
import Command from "../command.js";
import CommandsManager from "../commands.js";
export const compgen = new Command("compgen")
export const compgen = new Command()
.setRequireOptions(true)
.setExecute(function(args, { options }) {
if (options.includes("c")) {

View file

@ -8,7 +8,7 @@ const COW = `
||----w |
|| ||`;
export const cowsay = new Command("cowsay")
export const cowsay = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Show a cow saying something",

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const dir = new Command("dir")
export const dir = new Command()
.setManual({
purpose: "List all directories in the current directory"
})

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const echo = new Command("echo")
export const echo = new Command()
.setManual({
purpose: "Display text on the terminal screen"
})

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const exit = new Command("exit")
export const exit = new Command()
.setManual({
purpose: "Quit terminal interface"
})

View file

@ -5,96 +5,56 @@ import Command from "../command.js";
* Source: https://github.com/shlomif/fortune-mod/blob/master/fortune-mod/datfiles/fortunes
*/
const FORTUNES = [
"A day for firm decisions!!!!! Or is it?",
"Just to have it is enough.",
"Keep emotionally active. Cater to your favorite neurosis.",
"Keep it short for pithy sake.",
"Lady Luck brings added income today. Lady friend takes it away tonight.",
"Learn to pause -- or nothing worthwhile can catch up to you.",
"Let me put it this way: today is going to be a learning experience.",
"Life is to you a dashing and bold adventure.",
"\"Life, loathe it or ignore it, you can't like it.\"\n\n -- Marvin, \"Hitchhiker's Guide to the Galaxy\"",
"Live in a world of your own, but always welcome visitors.",
"Living your life is a task so difficult, it has never been attempted before.",
"Long life is in store for you.",
"Look afar and see the end from the beginning.",
"Love is in the offing. Be affectionate to one who adores you.",
"Make a wish, it might come true.",
"Many changes of mind and mood; do not hesitate too long.",
"Never be led astray onto the path of virtue.",
"Never commit yourself! Let someone else commit you.",
"Never give an inch!",
"Never look up when dragons fly overhead.",
"Never reveal your best argument.",
"Next Friday will not be your lucky day. As a matter of fact, you don't have a lucky day this year.",
"Of course you have a purpose -- to find a purpose.",
"People are beginning to notice you. Try dressing before you leave the house.",
"Perfect day for scrubbing the floor and other exciting things.",
"Questionable day.\n\nAsk somebody something.",
"Reply hazy, ask again later.",
"Save energy: be apathetic.",
"Ships are safe in harbor, but they were never meant to stay there.",
"Slow day. Practice crawling.",
"Snow Day -- stay home.",
"So this is it. We're going to die.",
"So you're back... about time...",
"Someone is speaking well of you.",
"Someone is speaking well of you.\n\nHow unusual!",
"Someone whom you reject today, will reject you tomorrow.",
"Stay away from flying saucers today.",
"Stay away from hurricanes for a while.",
"Stay the curse.",
"That secret you've been guarding, isn't.",
"The time is right to make new friends.",
"The whole world is a tuxedo and you are a pair of brown shoes.\n\n -- George Gobel",
"There is a 20% chance of tomorrow.",
"There is a fly on your nose.",
"There was a phone call for you.",
"You're not my type. For that matter, you're not even my species!!!",
"You're ugly and your mother dresses you funny.",
"You're working under a slight handicap. You happen to be human.",
"You've been leading a dog's life. Stay off the furniture.",
"Your aim is high and to the right.",
"Your aims are high, and you are capable of much.",
"Your analyst has you mixed up with another patient. Don't believe a thing he tells you.",
"Your best consolation is the hope that the things you failed to get weren't really worth having.",
"Your boss climbed the corporate ladder, wrong by wrong.",
"Your boss is a few sandwiches short of a picnic.",
"Your boyfriend takes chocolate from strangers.",
"Your business will assume vast proportions.",
"Your business will go through a period of considerable expansion.",
"Your depth of comprehension may tend to make you lax in worldly ways.",
"Your domestic life may be harmonious.",
"Your fly might be open (but don't check it just now).",
"Your goose is cooked. (Your current chick is burned up too!)",
"Your heart is pure, and your mind clear, and your soul devout.",
"Your ignorance cramps my conversation.",
"Your life would be very empty if you had nothing to regret.",
"Your love life will be happy and harmonious.",
"Your love life will be... interesting.",
"Your lover will never wish to leave you.",
"Your lucky color has faded.",
"Your lucky number has been disconnected.",
"Your lucky number is 3552664958674928. Watch for it everywhere.",
"Your mode of life will be changed for the better because of good news soon.",
"Your mode of life will be changed for the better because of new developments.",
"Your motives for doing whatever good deed you may have in mind will be misinterpreted by somebody.",
"Your nature demands love and your happiness depends on it.",
"Your object is to save the world, while still leading a pleasant life.",
"Your own qualities will help prevent your advancement in the world.",
"Your present plans will be successful.",
"Your reasoning is excellent -- it's only your basic assumptions that are wrong.",
"Your reasoning powers are good, and you are a fairly good planner.",
"Your sister swims out to meet troop ships.",
"Your society will be sought by people of taste and refinement.",
"Your step will soil many countries.",
"Your supervisor is thinking about you.",
"Your talents will be recognized and suitably rewarded.",
"Your temporary financial embarrassment will be relieved in a surprising manner.",
"Your true value depends entirely on what you are compared with.",
"Do not be afraid of competition.",
"An exciting opportunity lies ahead of you.",
"You love peace.",
"Get your mind set…confidence will lead you on.",
"You will always be surrounded by true friends.",
"Sell your ideas-they have exceptional merit.",
"You should be able to undertake and complete anything.",
"You are kind and friendly.",
"You are wise beyond your years.",
"Your ability to juggle many tasks will take you far.",
"A routine task will turn into an enchanting adventure.",
"Beware of all enterprises that require new clothes.",
"Be true to your work, your word, and your friend.",
"Goodness is the only investment that never fails.",
"A journey of a thousand miles begins with a single step.",
"Forget injuries; never forget kindnesses.",
"Respect yourself and others will respect you.",
"A man cannot be comfortable without his own approval.",
"Always do right. This will gratify some people and astonish the rest.",
"It is easier to stay out than to get out.",
"Sing everyday and chase the mean blues away.",
"You will receive money from an unexpected source.",
"Attitude is a little thing that makes a big difference.",
"Plan for many pleasures ahead.",
"Experience is the best teacher.",
"You will be happy with your spouse.",
"Expect the unexpected.",
"Stay healthy. Walk a mile.",
"The family that plays together stays together.",
"Eat chocolate to have a sweeter life.",
"Once you make a decision the universe conspires to make it happen.",
"Make yourself necessary to someone.",
"The only way to have a friend is to be one.",
"Nothing great was ever achieved without enthusiasm.",
"Dance as if no one is watching.",
"Live this day as if it were your last.",
"Your life will be happy and peaceful.",
"Reach for joy and all else will follow.",
"Move in the direction of your dreams.",
"Bloom where you are planted.",
"Appreciate. Appreciate. Appreciate.",
"Happy News is on its way.",
"A closed mouth gathers no feet.",
"He who throws dirt is losing ground.",
"Borrow money from a pessimist. They don't expect it back.",
"Life is what happens to you while you are busy making other plans.",
"Help! I'm being held prisoner in a fortune cookie factory.",
];
export const fortune = new Command("fortune")
export const fortune = new Command()
.setManual({
purpose: "Tell fortune"
})

View file

@ -1,7 +1,7 @@
import Command from "../command.js";
import CommandsManager from "../commands.js";
export const help = new Command("help")
export const help = new Command()
.setExecute((args) => {
if (args.length === 0) {
return CommandsManager.COMMANDS.map((command) => {

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const hostname = new Command("hostname")
export const hostname = new Command()
.setManual({
purpose: "Display the hostname"
})

View file

@ -1,7 +1,7 @@
import { ANSI } from "../../../../config/apps/terminal.config.js";
import Command from "../command.js";
export const ls = new Command("ls")
export const ls = new Command()
.setManual({
purpose: "List directory contents",
usage: "ls [OPTION]... [FILE]...",

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const make = new Command("make")
export const make = new Command()
.setRequireArgs(true)
.setExecute(function(args) {
if (args[0] === "love")

View file

@ -3,7 +3,7 @@ import CommandsManager from "../commands.js";
const MARGIN = 5;
export const man = new Command("man")
export const man = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Show system reference manuals",

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const mkdir = new Command("mkdir")
export const mkdir = new Command()
.setManual({
purpose: "Create directory"
})

View file

@ -6,7 +6,7 @@ import { formatRelativeTime } from "../../../_utils/date.utils.js";
import AppsManager from "../../appsManager.js";
import Command from "../command.js";
export const neofetch = new Command("neofetch")
export const neofetch = new Command()
.setManual({
purpose: "Fetch system information"
})

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const pwd = new Command("pwd")
export const pwd = new Command()
.setManual({
purpose: "Display path of the current directory"
})

View file

@ -1,7 +1,7 @@
import { reloadViewport } from "../../../_utils/browser.utils.js";
import Command from "../command.js";
export const reboot = new Command("reboot")
export const reboot = new Command()
.setManual({
purpose: "Reboot the system"
})

View file

@ -0,0 +1,9 @@
import Command from "../command.js";
export const rev = new Command()
.setManual({
purpose: "Reverses text."
})
.setExecute((args, { rawInputValue }) => {
return rawInputValue.split("").reverse().join("");
});

View file

@ -1,7 +1,7 @@
import { VirtualFile } from "../../../virtual-drive/file/virtualFile.js";
import Command from "../command.js";
export const rm = new Command("rm")
export const rm = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Remove a file"

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const rmdir = new Command("rmdir")
export const rmdir = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Remove a directory"

View file

@ -11,7 +11,7 @@ const LOCOMOTIVE_SMOKE = [
" (@@@)",
],
[
" ( ) (@@) ( ) (@) () @@ o @ o",
" ( ) (@@) ( ) (@) () @@ o @ o @",
" (@@@)",
" ( )",
" (@@@@)",
@ -21,55 +21,76 @@ const LOCOMOTIVE_SMOKE = [
];
const LOCOMOTIVE_TOP = [
" ==== ________ ___________ ",
" _D _| |_______/ \\__I_I_____===__|_________| ",
" |(_)--- | H\\________/ | | =|___ ___| ",
" / | | H | | | | ||_| |_|| ",
" | | | H |__--------------------| [___] | ",
" | ________|___H__/__|_____/[][]~\\_______| | ",
" |/ | |-----------I_____I [][] [] D |=======|__ ",
" ==== ________ ___________",
" _D _| |_______/ \\__I_I_____===__|_________|",
" |(_)--- | H\\________/ | | =|___ ___| ",
" / | | H | | | | ||_| |_|| ",
" | | | H |__--------------------| [___] | ",
" | ________|___H__/__|_____/[][]~\\_______| | ",
" |/ | |-----------I_____I [][] [] D |=======|__",
];
const LOCOMOTIVE_BOTTOM = [
[
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\O=====O=====O=====O_/ \\_/ ",
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\O=====O=====O=====O_/ \\_/ ",
],
[
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ",
" |/-=|___|=O=====O=====O=====O |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|=O=====O=====O=====O |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
],
[
"__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ ",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
"__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
],
[
"__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ ",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
"__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
],
[
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ",
" |/-=|___|= O=====O=====O=====O|_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= O=====O=====O=====O|_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
],
[
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\_O=====O=====O=====O/ \\_/ ",
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\_O=====O=====O=====O/ \\_/ ",
]
].reverse();
function getLocomotive(frame) {
const WAGON = [
" _________________ ",
" _| \\_____A ",
" =| | ",
" -| | ",
"__|________________________|_",
"|__________________________|_",
" |_D__D__D_| |_D__D__D_| ",
" \\_/ \\_/ \\_/ \\_/ ",
];
function getLocomotive(frame, wagonCount = 1) {
const smokeHeight = LOCOMOTIVE_SMOKE[0].length;
const locomotiveHeight = LOCOMOTIVE_TOP.length + LOCOMOTIVE_BOTTOM[0].length;
const wagonHeight = WAGON.length;
const wagonStart = smokeHeight + locomotiveHeight - wagonHeight;
const smoke = LOCOMOTIVE_SMOKE[Math.round(frame / 6) % LOCOMOTIVE_SMOKE.length];
const top = LOCOMOTIVE_TOP;
const bottom = LOCOMOTIVE_BOTTOM[frame % LOCOMOTIVE_BOTTOM.length];
const distance = 50 - frame;
const locomotive = smoke.concat(top, bottom).map((line) => {
const locomotive = smoke.concat(top, bottom).map((line, index) => {
if (index >= wagonStart && wagonCount > 0) {
line += WAGON[index - wagonStart].repeat(wagonCount);
}
if (distance === 0) {
return line;
} else if (distance > 0) {
@ -83,13 +104,31 @@ function getLocomotive(frame) {
return `\n${locomotive}\n`;
}
export const sl = new Command("sl")
.setExecute(function() {
export const sl = new Command()
.setManual({
purpose: "Displays animations aimed to correct users who accidentally enter sl instead of ls. SL stands for Steam Locomotive."
})
.addOption({
short: "w",
long: "wagons",
isInput: true
})
.setExecute((args, { inputs }) => {
let wagonCount = 1;
if (inputs?.w) {
wagonCount = parseInt(inputs.w);
if (!wagonCount || wagonCount < 0) {
return `${this.name}: Please specify a valid amount of wagons`;
}
}
const stream = new Stream();
let frame = 0;
const interval = setInterval(() => {
const text = getLocomotive(frame);
const text = getLocomotive(frame, wagonCount);
stream.send(text);
frame++;

View file

@ -1,7 +1,7 @@
import { VirtualFile } from "../../../virtual-drive/file/virtualFile.js";
import Command from "../command.js";
export const touch = new Command("touch")
export const touch = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Change file timestamps",

View file

@ -3,7 +3,7 @@ import { formatRelativeTime } from "../../../_utils/date.utils.js";
import Command from "../command.js";
export const uptime = new Command("uptime")
export const uptime = new Command()
.setManual({
purpose: "Displays the current uptime of the system"
})

View file

@ -1,7 +1,7 @@
import Command from "../command.js";
import CommandsManager from "../commands.js";
export const whatis = new Command("whatis")
export const whatis = new Command()
.setRequireArgs(true)
.setManual({
purpose: "Show information about a command"

View file

@ -1,6 +1,6 @@
import Command from "../command.js";
export const whoami = new Command("whoami")
export const whoami = new Command()
.setManual({
purpose: "Display the username"
})