2022-06-12 14:22:15 +02:00
|
|
|
import type { Diary, Project } from "../content/types";
|
2021-12-17 18:55:00 +01:00
|
|
|
import type { Command, Flag } from "./types";
|
2022-06-12 14:55:48 +02:00
|
|
|
import Color from "color";
|
2021-12-16 00:21:14 +01:00
|
|
|
|
2022-06-12 14:55:48 +02:00
|
|
|
function getCommandByName(name: string): Command | undefined {
|
2021-12-17 19:04:34 +01:00
|
|
|
return commandList.find(cmd => cmd.name === name);
|
|
|
|
}
|
|
|
|
|
2021-12-16 00:21:14 +01:00
|
|
|
function illegalUse(raw: string, cmd: Command): string[] {
|
|
|
|
return [
|
|
|
|
"Syntax error!",
|
|
|
|
`Cannot parse "${raw}"`,
|
|
|
|
""
|
|
|
|
].concat(printSyntax(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkFlags(flags: string[], cmd: Command): boolean {
|
|
|
|
if (!flags || flags === []) return true;
|
|
|
|
if (!cmd.flags) return false;
|
|
|
|
|
|
|
|
for (const flag of flags) {
|
2022-06-12 14:55:48 +02:00
|
|
|
const isLong = flag.substring(0, 2) === "--";
|
2021-12-17 20:02:42 +01:00
|
|
|
const flagObj = Object.values(cmd.flags).find(f => isLong ? f.long === flag.substring(2) : f.short === flag.substring(1));
|
2021-12-16 00:21:14 +01:00
|
|
|
if (!flagObj) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkSubcmd(subcmds: string[], cmd: Command): boolean {
|
|
|
|
if (!subcmds || subcmds === []) return true;
|
|
|
|
if (!cmd.subcommands) return false;
|
|
|
|
|
|
|
|
for (const sc of subcmds) {
|
2021-12-17 20:02:42 +01:00
|
|
|
const flagObj = Object.values(cmd.subcommands).find(s => s.name === sc);
|
2021-12-16 00:21:14 +01:00
|
|
|
if (!flagObj) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-17 18:55:00 +01:00
|
|
|
function checkFlagInclude(flagsProvided: string[], flag: Flag): boolean {
|
2021-12-17 20:02:42 +01:00
|
|
|
if (!flag) return false;
|
2021-12-17 18:55:00 +01:00
|
|
|
return flagsProvided.includes(`-${flag.short}`) || flagsProvided.includes(`--${flag.long}`);
|
|
|
|
}
|
|
|
|
|
2021-12-16 00:21:14 +01:00
|
|
|
export function printSyntax(cmd: Command): string[] {
|
|
|
|
let flagsOption = "";
|
|
|
|
let flagsDesc = [];
|
2021-12-17 20:02:42 +01:00
|
|
|
if (cmd.flags && Object.keys(cmd.flags).length > 0) {
|
2021-12-16 00:21:14 +01:00
|
|
|
flagsOption = " [";
|
2021-12-16 01:03:54 +01:00
|
|
|
flagsDesc.push("");
|
2021-12-16 00:21:14 +01:00
|
|
|
flagsDesc.push("Flags:");
|
2021-12-17 20:02:42 +01:00
|
|
|
Object.values(cmd.flags).forEach((flag => {
|
2021-12-16 00:21:14 +01:00
|
|
|
flagsOption += `-${flag.short} `;
|
|
|
|
flagsDesc.push(`\t-${flag.short}\t--${flag.long}\t${flag.desc}`);
|
|
|
|
}));
|
2022-06-12 14:55:48 +02:00
|
|
|
flagsOption = flagsOption.substring(0, flagsOption.length - 1) + "]";
|
2021-12-16 00:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let subcmdOption = "";
|
|
|
|
let subcmdDesc = [];
|
2021-12-17 20:02:42 +01:00
|
|
|
if (cmd.subcommands && Object.keys(cmd.subcommands).length > 0) {
|
2021-12-16 00:21:14 +01:00
|
|
|
subcmdOption = " [";
|
2021-12-16 01:03:54 +01:00
|
|
|
subcmdDesc.push("");
|
2021-12-17 19:04:34 +01:00
|
|
|
subcmdDesc.push("Arguments:");
|
2021-12-17 20:02:42 +01:00
|
|
|
Object.values(cmd.subcommands).forEach((subcmd => {
|
2021-12-16 00:21:14 +01:00
|
|
|
subcmdOption += `${subcmd.name}|`;
|
2021-12-17 20:02:42 +01:00
|
|
|
subcmdDesc.push(`\t${subcmd.name}\t${subcmd.desc}`);
|
2021-12-16 00:21:14 +01:00
|
|
|
}));
|
2022-06-12 14:55:48 +02:00
|
|
|
subcmdOption = subcmdOption.substring(0, subcmdOption.length - 1) + "]";
|
2021-12-16 00:21:14 +01:00
|
|
|
}
|
|
|
|
|
2022-06-12 14:55:48 +02:00
|
|
|
return [`Usage: ${cmd.name}${flagsOption}${subcmdOption}`].concat(flagsDesc).concat(subcmdDesc);
|
2021-12-16 00:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const about: Command = {
|
|
|
|
name: "about",
|
|
|
|
desc: "Show information about this page.",
|
2021-12-17 20:02:42 +01:00
|
|
|
execute: () => {
|
2021-12-16 00:21:14 +01:00
|
|
|
return [
|
|
|
|
"Hello there wanderer.",
|
|
|
|
"So you want to know what this is about?",
|
|
|
|
"",
|
|
|
|
"Well, the answer is pretty unspectecular:",
|
|
|
|
"This site presents some stuff that the user named C0ntroller created.",
|
|
|
|
"If you wander arround you will find various projects.",
|
|
|
|
"",
|
|
|
|
"The navigation is done via this console interface.",
|
|
|
|
"Even when you open a project page you don't need your mouse - just press Esc to close it.",
|
|
|
|
"",
|
|
|
|
"I hope you enjoy your stay here!",
|
2021-12-26 16:17:40 +01:00
|
|
|
"If you wanted more information about the page itself, type %{project this} or %{help -t}."
|
2021-12-16 00:21:14 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-17 18:55:00 +01:00
|
|
|
const help: Command = {
|
|
|
|
name: "help",
|
|
|
|
desc: "Shows helptext.",
|
2022-06-12 14:55:48 +02:00
|
|
|
flags: { more: { long: "this", short: "t", desc: "Show information about this site." } },
|
2021-12-17 20:02:42 +01:00
|
|
|
execute: (flags) => {
|
|
|
|
if (help.flags && checkFlagInclude(flags, help.flags.more)) {
|
2021-12-17 18:55:00 +01:00
|
|
|
return [
|
|
|
|
"Hello user!",
|
|
|
|
"What you see here should resemble an CLI. If you ever used Linux this should be pretty easy for you.",
|
|
|
|
"Everyone else: Have no fear. It is pretty simple. You just type in commands and the output is shown here or it does something on the webite.",
|
2022-02-06 15:07:16 +01:00
|
|
|
"To find out, which commands are available, you can type just %{help}.",
|
2021-12-17 18:55:00 +01:00
|
|
|
"",
|
|
|
|
"Have fun!"
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
const available = ["Available commands:"];
|
|
|
|
commandList.forEach(cmd => available.push(`\t${cmd.name}\t${cmd.desc}`));
|
|
|
|
available.concat([
|
|
|
|
"",
|
|
|
|
"Need more help? Type 'help -m'!"
|
|
|
|
]);
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-17 19:04:34 +01:00
|
|
|
const man: Command = {
|
|
|
|
name: "man",
|
|
|
|
desc: "Provides a manual for a command.",
|
2021-12-17 20:02:42 +01:00
|
|
|
subcommands: {
|
2022-06-12 14:55:48 +02:00
|
|
|
command: { name: "command", desc: "Name of a command" }
|
2021-12-17 20:02:42 +01:00
|
|
|
},
|
|
|
|
execute: (_flags, args) => {
|
2021-12-17 19:04:34 +01:00
|
|
|
if (args.length !== 1) {
|
|
|
|
return printSyntax(man);
|
|
|
|
} else {
|
|
|
|
const cmd = getCommandByName(args[0]);
|
|
|
|
if (!cmd) return [`Cannot find command '${args[0]}'.`];
|
|
|
|
else return printSyntax(cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-17 20:02:42 +01:00
|
|
|
const project: Command = {
|
|
|
|
name: "project",
|
|
|
|
desc: "Show information about a project.",
|
|
|
|
flags: {
|
2022-06-12 14:55:48 +02:00
|
|
|
minimal: { short: "m", long: "minimal", desc: "Only show minimal information." },
|
|
|
|
source: { short: "s", long: "source", desc: "Open git repository of project." },
|
|
|
|
list: { short: "l", long: "list", desc: "\tShow list of projects." }
|
2021-12-17 20:02:42 +01:00
|
|
|
},
|
2022-07-29 14:22:51 +02:00
|
|
|
subcommands: { name: { name: "name", desc: "Name of the project." }, page: {name: "page", desc: "Page of the diary (only for diaries; 0 is mainpage)."} },
|
2022-01-14 14:27:19 +01:00
|
|
|
execute: (flags, args, _raw, cmdIf) => {
|
2021-12-17 20:02:42 +01:00
|
|
|
if (project.flags && checkFlagInclude(flags, project.flags.list)) {
|
|
|
|
const result = ["Found the following projects:"];
|
2022-06-12 14:22:15 +02:00
|
|
|
|
|
|
|
const projects = cmdIf.content.filter(p => p.type === "project");
|
2022-06-12 14:55:48 +02:00
|
|
|
if (projects.length === 0) result.push("\tNo projects found.");
|
2022-06-12 14:22:15 +02:00
|
|
|
else projects.forEach(project => result.push(`\t${project.name}\t${project.short_desc}`));
|
|
|
|
|
2022-03-15 17:21:10 +01:00
|
|
|
result.push("And the following diaries:");
|
2022-06-12 14:22:15 +02:00
|
|
|
const diaries = cmdIf.content.filter(p => p.type === "diary");
|
2022-06-12 14:55:48 +02:00
|
|
|
if (diaries.length === 0) result.push("\tNo diaries found.");
|
2022-06-12 14:22:15 +02:00
|
|
|
else diaries.forEach(diary => result.push(`\t${diary.name}\t${diary.short_desc}`));
|
|
|
|
|
2021-12-17 20:02:42 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 14:22:51 +02:00
|
|
|
if (args.length < 1 || args.length > 2) return printSyntax(project);
|
2021-12-17 20:02:42 +01:00
|
|
|
|
|
|
|
if (args[0] === "this") args[0] = "homepage";
|
|
|
|
|
2022-06-12 14:55:48 +02:00
|
|
|
let [pjt] = [cmdIf.content.find(p => p.name === args[0]) as Project | Diary | undefined];
|
2021-12-17 20:02:42 +01:00
|
|
|
if (!pjt) return [
|
|
|
|
`Cannot find project ${args[0]}!`,
|
|
|
|
"You can see available projects using 'project -l'."
|
|
|
|
];
|
|
|
|
|
|
|
|
if (project.flags && checkFlagInclude(flags, project.flags.source)) {
|
|
|
|
try {
|
|
|
|
window && window.open(pjt.repo, "_blank");
|
|
|
|
return ["Opened repository in new tab."];
|
|
|
|
} catch {
|
|
|
|
return ["Sorry, no repository for this project."];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (project.flags && checkFlagInclude(flags, project.flags.minimal)) return pjt.desc;
|
|
|
|
|
2022-07-29 14:22:51 +02:00
|
|
|
const result: string[] = [];
|
|
|
|
|
|
|
|
let selectedPage: number|undefined = Number.parseInt(args[1] || "");
|
|
|
|
|
|
|
|
if (args[1]) {
|
|
|
|
if(Number.isNaN(selectedPage)) {
|
|
|
|
result.push("Invalid page number, ignoring it.");
|
|
|
|
selectedPage = undefined;
|
|
|
|
} else {
|
|
|
|
if (pjt.type !== "diary") {
|
|
|
|
result.push("This is not a diary, ignoring page.");
|
|
|
|
selectedPage = undefined;
|
|
|
|
} else {
|
|
|
|
if (selectedPage < 0 || selectedPage > pjt.entries.length) {
|
|
|
|
result.push("Invalid page number, ignoring it.");
|
|
|
|
selectedPage = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
selectedPage = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmdIf.callbacks?.setModalContent) cmdIf.callbacks.setModalContent(pjt, selectedPage);
|
2022-06-12 14:22:15 +02:00
|
|
|
if (cmdIf.callbacks?.setModalVisible) cmdIf.callbacks.setModalVisible(true);
|
2022-07-29 14:22:51 +02:00
|
|
|
return result;
|
2021-12-17 20:02:42 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-26 14:07:46 +01:00
|
|
|
const exitCmd: Command = {
|
|
|
|
name: "exit",
|
2021-12-26 15:37:08 +01:00
|
|
|
desc: "Tries to close this tab.",
|
2021-12-26 14:07:46 +01:00
|
|
|
execute: () => {
|
|
|
|
if (typeof window !== undefined) {
|
|
|
|
window.opener = null;
|
2022-01-05 17:52:20 +01:00
|
|
|
window.open("", "_self");
|
2021-12-26 14:07:46 +01:00
|
|
|
window.close();
|
|
|
|
}
|
|
|
|
return [
|
|
|
|
"If you can read this, closing the window did not work.",
|
2021-12-26 15:37:08 +01:00
|
|
|
"This is most likely because of a restriction in JavaScript.",
|
|
|
|
"#{Read more here|https://developer.mozilla.org/en-US/docs/Web/API/Window/close}."
|
2022-01-05 17:52:20 +01:00
|
|
|
];
|
2021-12-26 14:07:46 +01:00
|
|
|
}
|
2022-01-05 17:52:20 +01:00
|
|
|
};
|
2021-12-26 14:07:46 +01:00
|
|
|
|
2021-12-26 14:17:11 +01:00
|
|
|
const clear: Command = {
|
|
|
|
name: "clear",
|
|
|
|
desc: "Clears the output on screen.",
|
|
|
|
execute: () => []
|
2022-01-05 17:52:20 +01:00
|
|
|
};
|
2021-12-26 14:17:11 +01:00
|
|
|
|
2022-06-21 18:44:20 +02:00
|
|
|
const getColors = () => {
|
|
|
|
const replColor = window.document.documentElement.style.getPropertyValue("--repl-color") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color") || "rgb(24, 138, 24)";
|
|
|
|
const linkColor = window.document.documentElement.style.getPropertyValue("--repl-color-link") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color-link") || "rgb(31, 179, 31)";
|
|
|
|
const hintColor = window.document.documentElement.style.getPropertyValue("--repl-color-hint") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color-hint") || "rgba(24, 138, 24, 0.3)";
|
|
|
|
return [replColor, linkColor, hintColor];
|
|
|
|
};
|
|
|
|
|
|
|
|
const setColors = (color: Color) => {
|
|
|
|
window?.document.documentElement.style.setProperty("--repl-color", color.string());
|
|
|
|
window?.document.documentElement.style.setProperty("--repl-color-link", color.lighten(0.3).rgb().string());
|
|
|
|
window?.document.documentElement.style.setProperty("--repl-color-hint", color.fade(0.7).string());
|
|
|
|
};
|
|
|
|
|
2022-06-12 14:55:48 +02:00
|
|
|
const color: Command = {
|
|
|
|
name: "color",
|
|
|
|
desc: "Changes the color of the site.",
|
|
|
|
subcommands: {
|
|
|
|
reset: { name: "reset", desc: "Resets the color." },
|
2022-06-20 23:37:34 +02:00
|
|
|
value: { name: "value", desc: "Any valid (css) color value." },
|
2022-06-12 14:55:48 +02:00
|
|
|
},
|
2022-06-12 20:15:16 +02:00
|
|
|
execute: (_flags, args, _raw, cmdIf) => {
|
2022-06-12 14:55:48 +02:00
|
|
|
if (!window || !window.document) return [];
|
2022-06-21 18:44:20 +02:00
|
|
|
if (args.length === 0) {
|
|
|
|
const colors = getColors();
|
|
|
|
return [
|
|
|
|
"Current colors:",
|
|
|
|
`Text:\t\t${colors[0]}`,
|
|
|
|
`Links:\t\t${colors[1]}`,
|
|
|
|
`Completion:\t${colors[2]}`
|
|
|
|
];
|
|
|
|
}
|
2022-06-12 14:55:48 +02:00
|
|
|
if (args[0] === "reset") {
|
|
|
|
window.document.documentElement.style.removeProperty("--repl-color");
|
|
|
|
window.document.documentElement.style.removeProperty("--repl-color-link");
|
|
|
|
window.document.documentElement.style.removeProperty("--repl-color-hint");
|
|
|
|
return ["Color reset."];
|
|
|
|
} else {
|
2022-06-12 19:57:34 +02:00
|
|
|
let color: Color;
|
2022-06-12 14:55:48 +02:00
|
|
|
try {
|
2022-06-21 18:44:20 +02:00
|
|
|
color = Color(args.join(" ").trim());
|
2022-06-12 14:55:48 +02:00
|
|
|
} catch {
|
|
|
|
return ["Invalid color!"];
|
|
|
|
}
|
2022-06-21 18:44:20 +02:00
|
|
|
setColors(color);
|
2022-06-12 20:15:16 +02:00
|
|
|
|
|
|
|
switch(true) {
|
|
|
|
case color.hex().toLowerCase() === "#1f1e33": {
|
2022-06-21 18:47:42 +02:00
|
|
|
if (cmdIf.callbacks?.setModalHTML) cmdIf.callbacks?.setModalHTML('<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/w4U9S5eX3eY" title="YouTube video player" frameborder="0" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture" allowfullscreen></iframe>');
|
2022-06-12 20:15:16 +02:00
|
|
|
if (cmdIf.callbacks?.setModalVisible) cmdIf.callbacks?.setModalVisible(true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-12 19:57:34 +02:00
|
|
|
return ["Color set | #{Link|#} | %{help}"];
|
2022-06-12 14:55:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-21 18:44:20 +02:00
|
|
|
const save: Command = {
|
|
|
|
name: "save",
|
|
|
|
desc: "Saves the current color and command history to local storage.",
|
|
|
|
subcommands: {
|
|
|
|
clear: { name: "clear", desc: "Clear the saved data." },
|
|
|
|
confirm: { name: "confirm", desc: "You explicitly confirm, you allow saving to the local storage." }
|
|
|
|
},
|
|
|
|
execute: (_flags, args, _raw, cmdIf) => {
|
|
|
|
const defaultRet = [
|
|
|
|
"You can save the current color and command history to local storage.",
|
|
|
|
"To do so, use %{save confirm}.",
|
|
|
|
"By using this command above you agree on saving the non-functional data to local storage.",
|
|
|
|
"The data will never leave your computer!"
|
|
|
|
];
|
|
|
|
|
|
|
|
if (args.length === 0) {
|
|
|
|
return defaultRet;
|
|
|
|
} else if (args[0] === "clear") {
|
|
|
|
window.localStorage.clear();
|
|
|
|
return ["Colors and history removed from storage."];
|
|
|
|
} else if (args[0] === "confirm") {
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
const currentColors = getColors();
|
|
|
|
const color = new Color(currentColors[0]);
|
2022-06-22 22:07:46 +02:00
|
|
|
if(color.contrast(new Color("#000")) < 1.1 || color.alpha() < 0.1) result.push("Skipping saving the color because it's too dark.");
|
|
|
|
else {
|
|
|
|
window.localStorage.setItem("color", currentColors[0]);
|
|
|
|
result.push("Color saved to local storage.");
|
|
|
|
}
|
2022-06-21 18:44:20 +02:00
|
|
|
|
|
|
|
const history = cmdIf.callbacks?.getCmdHistory ? cmdIf.callbacks.getCmdHistory() : [];
|
|
|
|
window.localStorage.setItem("history", JSON.stringify(history));
|
|
|
|
|
2022-06-22 22:07:46 +02:00
|
|
|
result.push("History saved to storage.");
|
2022-06-21 18:44:20 +02:00
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return printSyntax(save);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const commandList = [about, help, man, project, exitCmd, clear, color, save].sort((a, b) => a.name.localeCompare(b.name));
|