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 ```js
// features/apps/terminal/commands/touch.js // features/apps/terminal/commands/touch.js
export const touch = new Command("touch") export const touch = new Command()
.setRequireArgs(true) .setRequireArgs(true)
.setManual({ .setManual({
purpose: "Change file timestamps", purpose: "Change file timestamps",

25
package-lock.json generated
View file

@ -31,6 +31,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"babel-plugin-wildcard": "^7.0.0",
"eslint-plugin-jsdoc": "^46.4.6", "eslint-plugin-jsdoc": "^46.4.6",
"gh-pages": "^5.0.0" "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", "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==" "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": { "node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", "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 // Get options
const 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("--")) { if (option.startsWith("--")) {
const longOption = option.substring(2).toLowerCase(); const longOption = option.substring(2).toLowerCase();
if (!options.includes(longOption)) addOption(longOption);
options.push(longOption);
} else { } else {
const shortOptions = option.substring(1).split(""); const shortOptions = option.substring(1).split("");
shortOptions.forEach((shortOption) => { shortOptions.forEach((shortOption) => {
if (!options.includes(shortOption)) addOption(shortOption);
options.push(shortOption);
}); });
} }
@ -143,7 +157,8 @@ export function Terminal({ startPath, setTitle, close: exit, active }) {
hostname: HOSTNAME, hostname: HOSTNAME,
rawInputValue, rawInputValue,
options, options,
exit exit,
inputs
}); });
if (response == null) if (response == null)
@ -214,8 +229,10 @@ export function Terminal({ startPath, setTitle, close: exit, active }) {
submitInput(value); submitInput(value);
setInputKey((previousKey) => previousKey + 1); setInputKey((previousKey) => previousKey + 1);
} else if (key === "ArrowUp") { } else if (key === "ArrowUp") {
event.preventDefault();
updateHistoryIndex(1); updateHistoryIndex(1);
} else if (key === "ArrowDown") { } else if (key === "ArrowDown") {
event.preventDefault();
updateHistoryIndex(-1); updateHistoryIndex(-1);
} else if (!stream && (event.ctrlKey || event.metaKey) && key === "c") { } else if (!stream && (event.ctrlKey || event.metaKey) && key === "c") {
setInputValue((value) => value + "^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.rawInputValue
* @param {string[]} options.options * @param {string[]} options.options
* @param {Function} options.exit * @param {Function} options.exit
* @param {[]} options.inputs
* @returns {string|{ blank: boolean }} * @returns {string|{ blank: boolean }}
*/ */
/**
* @typedef {object} optionType
* @property {string} long
* @property {string} short
* @property {boolean} isInput
*/
export default class Command { export default class Command {
/** @type {string} */ /** @type {string} */
name; name;
/** @type {optionType[]} */
options = [];
/** @type {executeType} */ /** @type {executeType} */
execute = () => {}; execute = () => {};
/**
* @param {string} name
* @param {executeType} execute
*/
constructor(name, execute) {
this.name = name;
this.execute = execute;
}
/** /**
* @param {string} name * @param {string} name
* @returns {Command} * @returns {Command}
@ -78,4 +80,28 @@ export default class Command {
this.manual = { purpose, usage, description, options }; this.manual = { purpose, usage, description, options };
return this; 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 Command from "./command.js";
import { cat } from "./commands/cat.js";
import { cd } from "./commands/cd.js"; // Dynamically import commands
import { clear } from "./commands/clear.js"; const context = require.context("./commands", false, /\.js$/);
import { compgen } from "./commands/compgen.js"; const commands = [];
import { cowsay } from "./commands/cowsay.js"; context.keys().forEach((key) => {
import { dir } from "./commands/dir.js"; const commandModule = context(key);
import { echo } from "./commands/echo.js"; const commandName = Object.keys(commandModule)[0];
import { exit } from "./commands/exit.js"; const command = commandModule[commandName];
import { fortune } from "./commands/fortune.js"; command.setName(commandName.toLowerCase());
import { help } from "./commands/help.js"; commands.push(command);
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";
export default class CommandsManager { export default class CommandsManager {
/** /**
@ -52,31 +38,5 @@ export default class CommandsManager {
return matches; return matches;
} }
static COMMANDS = [ static COMMANDS = 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,
];
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
import Command from "../command.js"; import Command from "../command.js";
export const exit = new Command("exit") export const exit = new Command()
.setManual({ .setManual({
purpose: "Quit terminal interface" 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 * Source: https://github.com/shlomif/fortune-mod/blob/master/fortune-mod/datfiles/fortunes
*/ */
const FORTUNES = [ const FORTUNES = [
"A day for firm decisions!!!!! Or is it?", "Do not be afraid of competition.",
"Just to have it is enough.", "An exciting opportunity lies ahead of you.",
"Keep emotionally active. Cater to your favorite neurosis.", "You love peace.",
"Keep it short for pithy sake.", "Get your mind set…confidence will lead you on.",
"Lady Luck brings added income today. Lady friend takes it away tonight.", "You will always be surrounded by true friends.",
"Learn to pause -- or nothing worthwhile can catch up to you.", "Sell your ideas-they have exceptional merit.",
"Let me put it this way: today is going to be a learning experience.", "You should be able to undertake and complete anything.",
"Life is to you a dashing and bold adventure.", "You are kind and friendly.",
"\"Life, loathe it or ignore it, you can't like it.\"\n\n -- Marvin, \"Hitchhiker's Guide to the Galaxy\"", "You are wise beyond your years.",
"Live in a world of your own, but always welcome visitors.", "Your ability to juggle many tasks will take you far.",
"Living your life is a task so difficult, it has never been attempted before.", "A routine task will turn into an enchanting adventure.",
"Long life is in store for you.", "Beware of all enterprises that require new clothes.",
"Look afar and see the end from the beginning.", "Be true to your work, your word, and your friend.",
"Love is in the offing. Be affectionate to one who adores you.", "Goodness is the only investment that never fails.",
"Make a wish, it might come true.", "A journey of a thousand miles begins with a single step.",
"Many changes of mind and mood; do not hesitate too long.", "Forget injuries; never forget kindnesses.",
"Never be led astray onto the path of virtue.", "Respect yourself and others will respect you.",
"Never commit yourself! Let someone else commit you.", "A man cannot be comfortable without his own approval.",
"Never give an inch!", "Always do right. This will gratify some people and astonish the rest.",
"Never look up when dragons fly overhead.", "It is easier to stay out than to get out.",
"Never reveal your best argument.", "Sing everyday and chase the mean blues away.",
"Next Friday will not be your lucky day. As a matter of fact, you don't have a lucky day this year.", "You will receive money from an unexpected source.",
"Of course you have a purpose -- to find a purpose.", "Attitude is a little thing that makes a big difference.",
"People are beginning to notice you. Try dressing before you leave the house.", "Plan for many pleasures ahead.",
"Perfect day for scrubbing the floor and other exciting things.", "Experience is the best teacher.",
"Questionable day.\n\nAsk somebody something.", "You will be happy with your spouse.",
"Reply hazy, ask again later.", "Expect the unexpected.",
"Save energy: be apathetic.", "Stay healthy. Walk a mile.",
"Ships are safe in harbor, but they were never meant to stay there.", "The family that plays together stays together.",
"Slow day. Practice crawling.", "Eat chocolate to have a sweeter life.",
"Snow Day -- stay home.", "Once you make a decision the universe conspires to make it happen.",
"So this is it. We're going to die.", "Make yourself necessary to someone.",
"So you're back... about time...", "The only way to have a friend is to be one.",
"Someone is speaking well of you.", "Nothing great was ever achieved without enthusiasm.",
"Someone is speaking well of you.\n\nHow unusual!", "Dance as if no one is watching.",
"Someone whom you reject today, will reject you tomorrow.", "Live this day as if it were your last.",
"Stay away from flying saucers today.", "Your life will be happy and peaceful.",
"Stay away from hurricanes for a while.", "Reach for joy and all else will follow.",
"Stay the curse.", "Move in the direction of your dreams.",
"That secret you've been guarding, isn't.", "Bloom where you are planted.",
"The time is right to make new friends.", "Appreciate. Appreciate. Appreciate.",
"The whole world is a tuxedo and you are a pair of brown shoes.\n\n -- George Gobel", "Happy News is on its way.",
"There is a 20% chance of tomorrow.", "A closed mouth gathers no feet.",
"There is a fly on your nose.", "He who throws dirt is losing ground.",
"There was a phone call for you.", "Borrow money from a pessimist. They don't expect it back.",
"You're not my type. For that matter, you're not even my species!!!", "Life is what happens to you while you are busy making other plans.",
"You're ugly and your mother dresses you funny.", "Help! I'm being held prisoner in a fortune cookie factory.",
"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.",
]; ];
export const fortune = new Command("fortune") export const fortune = new Command()
.setManual({ .setManual({
purpose: "Tell fortune" purpose: "Tell fortune"
}) })

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
import { reloadViewport } from "../../../_utils/browser.utils.js"; import { reloadViewport } from "../../../_utils/browser.utils.js";
import Command from "../command.js"; import Command from "../command.js";
export const reboot = new Command("reboot") export const reboot = new Command()
.setManual({ .setManual({
purpose: "Reboot the system" 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 { VirtualFile } from "../../../virtual-drive/file/virtualFile.js";
import Command from "../command.js"; import Command from "../command.js";
export const rm = new Command("rm") export const rm = new Command()
.setRequireArgs(true) .setRequireArgs(true)
.setManual({ .setManual({
purpose: "Remove a file" purpose: "Remove a file"

View file

@ -1,6 +1,6 @@
import Command from "../command.js"; import Command from "../command.js";
export const rmdir = new Command("rmdir") export const rmdir = new Command()
.setRequireArgs(true) .setRequireArgs(true)
.setManual({ .setManual({
purpose: "Remove a directory" 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 = [ const LOCOMOTIVE_TOP = [
" ==== ________ ___________ ", " ==== ________ ___________",
" _D _| |_______/ \\__I_I_____===__|_________| ", " _D _| |_______/ \\__I_I_____===__|_________|",
" |(_)--- | H\\________/ | | =|___ ___| ", " |(_)--- | H\\________/ | | =|___ ___| ",
" / | | H | | | | ||_| |_|| ", " / | | H | | | | ||_| |_|| ",
" | | | H |__--------------------| [___] | ", " | | | H |__--------------------| [___] | ",
" | ________|___H__/__|_____/[][]~\\_______| | ", " | ________|___H__/__|_____/[][]~\\_______| | ",
" |/ | |-----------I_____I [][] [] D |=======|__ ", " |/ | |-----------I_____I [][] [] D |=======|__",
]; ];
const LOCOMOTIVE_BOTTOM = [ const LOCOMOTIVE_BOTTOM = [
[ [
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ", "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ", " |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\O=====O=====O=====O_/ \\_/ ", " \\_/ \\O=====O=====O=====O_/ \\_/ ",
], ],
[ [
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ", "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|=O=====O=====O=====O |_____/~\\___/ ", " |/-=|___|=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 |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= O=====O=====O=====O|_____/~\\___/ ", " |/-=|___|= O=====O=====O=====O|_____/~\\___/ ",
" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ", " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ",
], ],
[ [
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ", "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__",
" |/-=|___|= || || || |_____/~\\___/ ", " |/-=|___|= || || || |_____/~\\___/ ",
" \\_/ \\_O=====O=====O=====O/ \\_/ ", " \\_/ \\_O=====O=====O=====O/ \\_/ ",
] ]
].reverse(); ].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 smoke = LOCOMOTIVE_SMOKE[Math.round(frame / 6) % LOCOMOTIVE_SMOKE.length];
const top = LOCOMOTIVE_TOP; const top = LOCOMOTIVE_TOP;
const bottom = LOCOMOTIVE_BOTTOM[frame % LOCOMOTIVE_BOTTOM.length]; const bottom = LOCOMOTIVE_BOTTOM[frame % LOCOMOTIVE_BOTTOM.length];
const distance = 50 - frame; 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) { if (distance === 0) {
return line; return line;
} else if (distance > 0) { } else if (distance > 0) {
@ -83,13 +104,31 @@ function getLocomotive(frame) {
return `\n${locomotive}\n`; return `\n${locomotive}\n`;
} }
export const sl = new Command("sl") export const sl = new Command()
.setExecute(function() { .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(); const stream = new Stream();
let frame = 0; let frame = 0;
const interval = setInterval(() => { const interval = setInterval(() => {
const text = getLocomotive(frame); const text = getLocomotive(frame, wagonCount);
stream.send(text); stream.send(text);
frame++; frame++;

View file

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

View file

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

View file

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

View file

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