Updated terminal app

This commit is contained in:
Prozilla 2023-12-19 11:42:51 +01:00
parent d20c1f6b80
commit ed80c17dfa
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
26 changed files with 209 additions and 139 deletions

View file

@ -6,7 +6,6 @@ import { faCaretLeft, faCaretRight, faHome, faRotateRight } from "@fortawesome/f
import { HOME_URL, SEARCH_URL } from "../../../constants/applications/browser.js"; import { HOME_URL, SEARCH_URL } from "../../../constants/applications/browser.js";
import { isValidUrl } from "../../../features/utils/browser.js"; import { isValidUrl } from "../../../features/utils/browser.js";
import { useHistory } from "../../../hooks/utils/history.js"; import { useHistory } from "../../../hooks/utils/history.js";
import { TITLE_SEPARATOR } from "../../../constants/windows.js";
/** @type {import("../../windows/WindowView.jsx").windowProps} */ /** @type {import("../../windows/WindowView.jsx").windowProps} */
export function Browser({ startUrl, focus }) { export function Browser({ startUrl, focus }) {

View file

@ -134,7 +134,7 @@ export function Terminal({ setTitle, close: exit }) {
}); });
if (output) if (output)
promptOutput(output); promptOutput(`${output}\n`);
}; };
const updateHistoryIndex = (delta) => { const updateHistoryIndex = (delta) => {

View file

@ -1,5 +1,4 @@
import Command from "./command.js"; import Command from "./command.js";
import { blow } from "./commands/blow.js";
import { cat } from "./commands/cat.js"; import { cat } from "./commands/cat.js";
import { cd } from "./commands/cd.js"; import { cd } from "./commands/cd.js";
import { clear } from "./commands/clear.js"; import { clear } from "./commands/clear.js";
@ -9,21 +8,21 @@ import { dir } from "./commands/dir.js";
import { echo } from "./commands/echo.js"; import { echo } from "./commands/echo.js";
import { exit } from "./commands/exit.js"; import { exit } from "./commands/exit.js";
import { fortune } from "./commands/fortune.js"; import { fortune } from "./commands/fortune.js";
import { help } from "./commands/help.js";
import { hostname } from "./commands/hostname.js"; import { hostname } from "./commands/hostname.js";
import { ls } from "./commands/ls.js"; import { ls } from "./commands/ls.js";
import { make } from "./commands/make.js"; import { make } from "./commands/make.js";
import { man } from "./commands/man.js"; import { man } from "./commands/man.js";
import { mkdir } from "./commands/mkdir.js"; import { mkdir } from "./commands/mkdir.js";
import { neofetch } from "./commands/neofetch.js"; import { neofetch } from "./commands/neofetch.js";
import { nice } from "./commands/nice.js";
import { pwd } from "./commands/pwd.js"; import { pwd } from "./commands/pwd.js";
import { reboot } from "./commands/reboot.js"; import { reboot } from "./commands/reboot.js";
import { rm } from "./commands/rm.js"; import { rm } from "./commands/rm.js";
import { rmdir } from "./commands/rmdir.js"; import { rmdir } from "./commands/rmdir.js";
import { touch } from "./commands/touch.js"; import { touch } from "./commands/touch.js";
import { uptime } from "./commands/uptime.js";
import { whatis } from "./commands/whatis.js"; import { whatis } from "./commands/whatis.js";
import { whoami } from "./commands/whoami.js"; import { whoami } from "./commands/whoami.js";
import { world } from "./commands/world.js";
export default class CommandsManager { export default class CommandsManager {
/** /**
@ -67,9 +66,6 @@ export default class CommandsManager {
neofetch, neofetch,
fortune, fortune,
cowsay, cowsay,
world,
nice,
blow,
make, make,
cat, cat,
man, man,
@ -78,5 +74,7 @@ export default class CommandsManager {
whoami, whoami,
whatis, whatis,
exit, exit,
help,
uptime,
]; ];
} }

View file

@ -1,6 +0,0 @@
import Command from "../command.js";
export const blow = new Command("%blow")
.setExecute(function() {
return `fg: ${this.name}: No such job`;
});

View file

@ -3,7 +3,7 @@ import Command from "../command.js";
export const cat = new Command("cat") export const cat = new Command("cat")
.setRequireArgs(true) .setRequireArgs(true)
.setManual({ .setManual({
purpose: "Concetenate files and print on the standard output", purpose: "Concetenate files and display on the terminal screen",
usage: "cat [OPTION]... [FILE]...", usage: "cat [OPTION]... [FILE]...",
description: "Concetenate FILE(s) to standard output." description: "Concetenate FILE(s) to standard output."
}) })

View file

@ -3,7 +3,7 @@ import Command from "../command.js";
export const cd = new Command("cd") export const cd = new Command("cd")
.setRequireArgs(true) .setRequireArgs(true)
.setManual({ .setManual({
purpose: "Change directory", purpose: "Change the current directory",
usage: "cd path", usage: "cd path",
description: "Change working directory to given path (the home directory by default)." description: "Change working directory to given path (the home directory by default)."
}) })

View file

@ -1,10 +1,14 @@
import Command from "../command.js"; import Command from "../command.js";
export const dir = new Command("dir", (args, { currentDirectory }) => { export const dir = new Command("dir")
const folderNames = currentDirectory.subFolders.map((folder) => folder.id); .setManual({
purpose: "List all directories in the current directory"
if (folderNames.length === 0) })
return { blank: true }; .setExecute((args, { currentDirectory }) => {
const folderNames = currentDirectory.subFolders.map((folder) => folder.id);
return folderNames.sort((nameA, nameB) => nameA.localeCompare(nameB)).join(" ");
}); if (folderNames.length === 0)
return { blank: true };
return folderNames.sort((nameA, nameB) => nameA.localeCompare(nameB)).join(" ");
});

View file

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

View file

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

View file

@ -94,6 +94,10 @@ const FORTUNES = [
"Your true value depends entirely on what you are compared with.", "Your true value depends entirely on what you are compared with.",
]; ];
export const fortune = new Command("fortune", () => { export const fortune = new Command("fortune")
return randomFromArray(FORTUNES); .setManual({
}); purpose: "Tell fortune"
})
.setExecute(() => {
return randomFromArray(FORTUNES);
});

View file

@ -0,0 +1,26 @@
import Command from "../command.js";
import CommandsManager from "../commands.js";
export const help = new Command("help")
.setExecute((args) => {
if (args.length === 0) {
return CommandsManager.COMMANDS.map((command) => {
if (command.manual?.purpose) {
return `${command.name} - ${command.manual.purpose}`;
} else {
return command.name;
}
}).sort().join("\n");
}
const commandName = args[0].toLowerCase();
const command = CommandsManager.find(commandName);
if (!command)
return `${this.name}: ${commandName}: Command not found`;
if (!command.manual?.purpose)
return `${this.name}: ${commandName}: No manual found`;
return command.manual.purpose;
});

View file

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

View file

@ -2,10 +2,9 @@ import Command from "../command.js";
export const ls = new Command("ls") export const ls = new Command("ls")
.setManual({ .setManual({
purpose: "list directory contents", purpose: "List directory contents",
usage: "ls [OPTION]... [FILE]...", usage: "ls [OPTION]... [FILE]...",
description: "List information about the FILEs (the current directory by default).\n" description: "List information about the FILEs (the current directory by default)."
+ "Sort entries alphabetically if none of -cftuvSUX nor --sort is specified."
}) })
.setExecute(function(args, { currentDirectory }) { .setExecute(function(args, { currentDirectory }) {
let directory = currentDirectory; let directory = currentDirectory;

View file

@ -1,6 +1,7 @@
import Command from "../command.js"; import Command from "../command.js";
export const make = new Command("make") export const make = new Command("make")
.setRequireArgs(true)
.setExecute(function(args) { .setExecute(function(args) {
if (args[0] === "love") if (args[0] === "love")
return `${this.name}: *** No rule to make target 'love'. Stop.`; return `${this.name}: *** No rule to make target 'love'. Stop.`;

View file

@ -18,14 +18,14 @@ export const man = new Command("man")
.setExecute(function(args, { options }) { .setExecute(function(args, { options }) {
// Search function // Search function
if (options.includes("k")) { if (options.includes("k")) {
const commands = CommandsManager.search(args[0]); const commands = CommandsManager.search(args[0].toLowerCase());
return commands.map((command) => { return commands.map((command) => {
if (command.manual?.purpose) { if (command.manual?.purpose) {
return `${command.name} - ${command.manual.purpose}`; return `${command.name} - ${command.manual.purpose}`;
} else { } else {
return command.name; return command.name;
} }
}).join("\n"); }).sort().join("\n");
} }
const commandName = args[0].toLowerCase(); const commandName = args[0].toLowerCase();

View file

@ -1,11 +1,16 @@
import Command from "../command.js"; import Command from "../command.js";
export const mkdir = new Command("mkdir", (args, { currentDirectory }) => { export const mkdir = new Command("mkdir")
const name = args[0]; .setManual({
purpose: "Create directory"
if (currentDirectory.findSubFolder(name)) })
.setRequireArgs(true)
.setExecute((args, { currentDirectory }) => {
const name = args[0];
if (currentDirectory.findSubFolder(name))
return { blank: true };
currentDirectory.createFolder(name);
return { blank: true }; return { blank: true };
});
currentDirectory.createFolder(name);
return { blank: true };
}).setRequireArgs(true);

View file

@ -5,53 +5,57 @@ import { formatRelativeTime } from "../../../utils/date.js";
import AppsManager from "../../applications.js"; import AppsManager from "../../applications.js";
import Command from "../command.js"; import Command from "../command.js";
export const neofetch = new Command("neofetch", (args, { username, hostname }) => { export const neofetch = new Command("neofetch")
const leftColumn = ASCII_LOGO.split("\n"); .setManual({
const rightColumnWidth = username.length + hostname.length + 1; purpose: "Fetch system information"
})
.setExecute((args, { username, hostname }) => {
const leftColumn = ASCII_LOGO.split("\n");
const rightColumnWidth = username.length + hostname.length + 1;
const userAgent = navigator.userAgent; const userAgent = navigator.userAgent;
// Check for the browser name using regular expressions // Check for the browser name using regular expressions
let browserName; let browserName;
if (userAgent.match(/Firefox\//)) { if (userAgent.match(/Firefox\//)) {
browserName = "Mozilla Firefox"; browserName = "Mozilla Firefox";
} else if (userAgent.match(/Edg\//)) { } else if (userAgent.match(/Edg\//)) {
browserName = "Microsoft Edge"; browserName = "Microsoft Edge";
} else if (userAgent.match(/Chrome\//)) { } else if (userAgent.match(/Chrome\//)) {
browserName = "Google Chrome"; browserName = "Google Chrome";
} else if (userAgent.match(/Safari\//)) { } else if (userAgent.match(/Safari\//)) {
browserName = "Apple Safari"; browserName = "Apple Safari";
} else {
browserName = "Unknown";
}
const rightColumn = [
`${username}@${hostname}`,
"-".repeat(rightColumnWidth),
`OS: ${NAME}`,
`UPTIME: ${formatRelativeTime(START_DATE, 2, false)}`,
`RESOLUTION: ${window.innerWidth}x${window.innerHeight}`,
"THEME: default",
"ICONS: Font Awesome",
`TERMINAL: ${AppsManager.getApp(APPS.TERMINAL)?.name ?? "Unknown"}`,
`BROWSER: ${browserName}`,
`PLATFORM: ${navigator.platform}`,
`LANGUAGE: ${navigator.language}`,
];
const combined = [];
for (let i = 1; i < leftColumn.length; i++) {
let line = `${leftColumn[i]} `;
if (i <= rightColumn.length) {
line += rightColumn[i - 1];
} else { } else {
// This fixes a weird display bug on Safari mobile browserName = "Unknown";
line += " ".repeat(rightColumnWidth);
} }
combined.push(line); const rightColumn = [
} `${username}@${hostname}`,
"-".repeat(rightColumnWidth),
`OS: ${NAME}`,
`UPTIME: ${formatRelativeTime(START_DATE, 2, false)}`,
`RESOLUTION: ${window.innerWidth}x${window.innerHeight}`,
"THEME: default",
"ICONS: Font Awesome",
`TERMINAL: ${AppsManager.getApp(APPS.TERMINAL)?.name ?? "Unknown"}`,
`BROWSER: ${browserName}`,
`PLATFORM: ${navigator.platform}`,
`LANGUAGE: ${navigator.language}`,
];
return combined.join("\n"); const combined = [];
}); for (let i = 1; i < leftColumn.length; i++) {
let line = `${leftColumn[i]} `;
if (i <= rightColumn.length) {
line += rightColumn[i - 1];
} else {
// This fixes a weird display bug on Safari mobile
line += " ".repeat(rightColumnWidth);
}
combined.push(line);
}
return combined.join("\n");
});

View file

@ -1,7 +0,0 @@
import Command from "../command.js";
export const nice = new Command("nice")
.setExecute(function(args) {
if (args[0] === "man" && args[1] === "woman")
return `${this.name}: No manual entry for woman`;
});

View file

@ -1,9 +1,13 @@
import Command from "../command.js"; import Command from "../command.js";
export const pwd = new Command("pwd", (args, { currentDirectory }) => { export const pwd = new Command("pwd")
if (currentDirectory.root) { .setManual({
return "/"; purpose: "Display path of the current directory"
} else { })
return currentDirectory.absolutePath; .setExecute((args, { currentDirectory }) => {
} if (currentDirectory.root) {
}); return "/";
} else {
return currentDirectory.absolutePath;
}
});

View file

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

View file

@ -1,12 +1,17 @@
import Command from "../command.js"; import Command from "../command.js";
export const rm = new Command("rm", (args, { currentDirectory }) => { export const rm = new Command("rm")
const [name, extension] = args[0].split("."); .setRequireArgs(true)
const file = currentDirectory.findFile(name, extension); .setManual({
purpose: "Remove a file"
if (!file) })
return `${this.name}: ${args[0]}: No such file`; .setExecute((args, { currentDirectory }) => {
const [name, extension] = args[0].split(".");
const file = currentDirectory.findFile(name, extension);
file.delete(); if (!file)
return { blank: true }; return `${this.name}: ${args[0]}: No such file`;
}).setRequireArgs(true);
file.delete();
return { blank: true };
});

View file

@ -1,12 +1,17 @@
import Command from "../command.js"; import Command from "../command.js";
export const rmdir = new Command("rmdir", (args, { currentDirectory }) => { export const rmdir = new Command("rmdir")
const name = args[0]; .setRequireArgs(true)
const folder = currentDirectory.findSubFolder(name); .setManual({
purpose: "Remove a directory"
if (!folder) })
return `${this.name}: ${args[0]}: No such directory`; .setExecute((args, { currentDirectory }) => {
const name = args[0];
const folder = currentDirectory.findSubFolder(name);
folder.delete(); if (!folder)
return { blank: true }; return `${this.name}: ${args[0]}: No such directory`;
}).setRequireArgs(true);
folder.delete();
return { blank: true };
});

View file

@ -0,0 +1,12 @@
import { START_DATE } from "../../../../index.js";
import { formatRelativeTime } from "../../../utils/date.js";
import Command from "../command.js";
export const uptime = new Command("uptime")
.setManual({
purpose: "Displays the current uptime of the system"
})
.setExecute(() => {
return `Uptime: ${formatRelativeTime(START_DATE, 2, false)}`;
});

View file

@ -3,6 +3,9 @@ import CommandsManager from "../commands.js";
export const whatis = new Command("whatis") export const whatis = new Command("whatis")
.setRequireArgs(true) .setRequireArgs(true)
.setManual({
purpose: "Show information about a command"
})
.setExecute(function(args) { .setExecute(function(args) {
const commandName = args[0].toLowerCase(); const commandName = args[0].toLowerCase();
const command = CommandsManager.find(commandName); const command = CommandsManager.find(commandName);

View file

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

View file

@ -1,6 +0,0 @@
import Command from "../command.js";
export const world = new Command("world")
.setExecute(function() {
return `${this.name}: Not found`;
});