Skip to content

Commit b5d45bd

Browse files
committed
terminal: Updated man and sl commands
1 parent c85f279 commit b5d45bd

7 files changed

Lines changed: 143 additions & 17 deletions

File tree

packages/apps/terminal/src/core/command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class Command {
8888
return this;
8989
}
9090

91-
addOption({ short, long, isInput }: Option): Command {
91+
addOption({ short, long, isInput = false }: Omit<Option, "isInput"> & { isInput?: boolean }): Command {
9292
this.options.push({ short, long, isInput });
9393
return this;
9494
}

packages/apps/terminal/src/core/commands/ls.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { VirtualFolder } from "@prozilla-os/core";
22
import { formatError } from "../_utils/terminal.utils";
33
import { Command } from "../command";
4-
import { ANSI } from "@prozilla-os/shared";
4+
import { Ansi } from "@prozilla-os/shared";
55

66
export const ls = new Command()
77
.setManual({
@@ -19,7 +19,7 @@ export const ls = new Command()
1919
if (!directory)
2020
return formatError(this.name, `Cannot access '${(args)[0]}': No such file or directory`);
2121

22-
const folderNames = directory.subFolders.map((folder) => `${ANSI.fg.blue}${folder.id}${ANSI.reset}`);
22+
const folderNames = directory.subFolders.map((folder) => Ansi.blue(folder.id));
2323
const fileNames = directory.files.map((file) => file.id);
2424

2525
const contents = folderNames.concat(fileNames);

packages/apps/terminal/src/core/commands/man.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ANSI } from "@prozilla-os/shared";
1+
import { Ansi, ANSI } from "@prozilla-os/shared";
22
import { formatError } from "../_utils/terminal.utils";
33
import { Command } from "../command";
44
import { CommandsManager } from "../commands";
@@ -17,6 +17,10 @@ export const man = new Command()
1717
"-k": "Search for manual page using regexp",
1818
},
1919
})
20+
.addOption({
21+
short: "k",
22+
long: "apropos",
23+
})
2024
.setExecute(function(this: Command, args, { options }) {
2125
// Search function
2226
if (options?.includes("k")) {
@@ -49,7 +53,7 @@ export const man = new Command()
4953
const sections = [["NAME"]];
5054

5155
if (manual.purpose) {
52-
sections[0].push(formatText(`${commandName} - ${ANSI.decoration.dim}${ANSI.fg.yellow}${manual.purpose}${ANSI.reset}`));
56+
sections[0].push(formatText(`${commandName} - ${ANSI.decoration.dim}${Ansi.yellow(manual.purpose)}`));
5357
} else {
5458
sections[0].push(formatText(commandName));
5559
}
@@ -72,13 +76,27 @@ export const man = new Command()
7276
sections.push([
7377
"OPTIONS",
7478
formatText(Object.entries(manual.options).map(([key, value]) => {
75-
return `${key} ${ANSI.decoration.dim}${ANSI.fg.yellow}${value}${ANSI.reset}`;
79+
let rawOptionSyntax = key.split(" ");
80+
const shortOption = rawOptionSyntax[0].slice(1);
81+
rawOptionSyntax = rawOptionSyntax.slice(1);
82+
83+
let optionSyntax = "-" + shortOption;
84+
const option = command.options.find((option) => option.short == shortOption);
85+
if (option !== undefined) {
86+
optionSyntax += ", --" + option.long;
87+
}
88+
89+
if (rawOptionSyntax.length) {
90+
optionSyntax += " " + Ansi.dim(rawOptionSyntax.join(" "));
91+
}
92+
93+
return `${optionSyntax} ${ANSI.decoration.dim}${Ansi.yellow(String(value))}`;
7694
}).join("\n")),
7795
]);
7896
}
7997

8098
return sections.map((section) => {
81-
section[0] = ANSI.fg.yellow + section[0] + ANSI.reset;
99+
section[0] = Ansi.yellow(section[0]);
82100
return section.join("\n");
83101
}).join("\n\n");
84102
});

packages/apps/terminal/src/core/commands/sl.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { parseOptionalFloat } from "@prozilla-os/shared";
12
import { formatError } from "../_utils/terminal.utils";
23
import { Command } from "../command";
34
import { Stream } from "../stream";
@@ -136,7 +137,7 @@ const EXTRA_WAGONS = [
136137
],
137138
];
138139

139-
function generateLocomotive(frame: number, wagonCount = 1) {
140+
function generateLocomotive(frame: number, wagonCount = 1, start = 50) {
140141
const smokeHeight = LOCOMOTIVE_SMOKE[0].length;
141142
const locomotiveHeight = LOCOMOTIVE_TOP.length + LOCOMOTIVE_BOTTOM[0].length;
142143
const wagonHeight = COAL_WAGON.length;
@@ -147,7 +148,7 @@ function generateLocomotive(frame: number, wagonCount = 1) {
147148
const top = LOCOMOTIVE_TOP;
148149
const bottom = LOCOMOTIVE_BOTTOM[frame % LOCOMOTIVE_BOTTOM.length];
149150

150-
const distance = 50 - frame;
151+
const distance = start - frame;
151152
const locomotive = smoke.concat(top, bottom).map((line, index) => {
152153
if (index >= wagonStart && wagonCount > 0) {
153154
for (let i = 0; i < wagonCount; i++) {
@@ -175,18 +176,23 @@ function generateLocomotive(frame: number, wagonCount = 1) {
175176
export const sl = new Command()
176177
.setManual({
177178
purpose: "Show animations aimed to correct users who accidentally enter sl instead of ls. SL stands for Steam Locomotive.",
178-
usage: "sl\n"
179-
+ "sl -w number",
179+
usage: "sl [ -w ]",
180180
options: {
181181
"-w number": "Set the amount of wagons (defaults to 1)",
182+
"-s speed": `Set the speed of the locomotive (Defaults to ${ANIMATION_SPEED})`,
182183
},
183184
})
184185
.addOption({
185186
short: "w",
186187
long: "wagons",
187188
isInput: true,
188189
})
189-
.setExecute(function(this: Command, _args, { inputs }) {
190+
.addOption({
191+
short: "s",
192+
long: "speed",
193+
isInput: true,
194+
})
195+
.setExecute(function(this: Command, _args, { inputs, size }) {
190196
let wagonCount = 1;
191197

192198
if (inputs?.w) {
@@ -197,17 +203,19 @@ export const sl = new Command()
197203
}
198204
}
199205

206+
const delay = 100 / parseOptionalFloat(inputs?.s, ANIMATION_SPEED);
207+
200208
const stream = new Stream();
201209

202-
let frame = 0;
210+
let frame = 0;
203211
const interval = setInterval(() => {
204-
const text = generateLocomotive(frame, wagonCount);
212+
const text = generateLocomotive(frame, wagonCount, size.x);
205213
stream.send(text);
206214
frame++;
207215

208216
if (text.trim().length === 0)
209217
stream.stop();
210-
}, 100 / ANIMATION_SPEED);
218+
}, delay);
211219

212220
stream.on(Stream.STOP_EVENT, () => {
213221
clearInterval(interval);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// import { Command } from "../command";
2+
// import { Stream } from "../stream";
3+
4+
// export const watch = new Command()
5+
// .setManual({
6+
// purpose: "Execute a program periodically",
7+
// options: {
8+
// "-n seconds": "Specify update interval (defaults to 2)",
9+
// },
10+
// })
11+
// .addOption({
12+
// short: "n",
13+
// long: "interval",
14+
// isInput: true,
15+
// })
16+
// .setExecute(function(_args, { inputs }) {
17+
// const delay = inputs?.n !== undefined ? parseInt(inputs.n) : 2;
18+
19+
// const stream = new Stream();
20+
21+
// const interval = setInterval(() => {
22+
// const text = "command output here";
23+
// stream.send(text);
24+
// }, delay * 1000);
25+
26+
// stream.on(Stream.STOP_EVENT, () => {
27+
// clearInterval(interval);
28+
// });
29+
30+
// stream.start();
31+
32+
// return stream;
33+
// });
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1-
export function isValidInteger(number: number | string): number | boolean {
2-
return (typeof number === "number" || parseInt(number) || parseInt(number) === 0);
1+
/**
2+
* Checks if `input` is a valid integer.
3+
*/
4+
export function isValidInteger(input: number | string): boolean {
5+
if (typeof input === "number") return true;
6+
if (input.trim() === "") return false;
7+
return Number.isInteger(Number(input));
8+
}
9+
10+
/**
11+
* Checks if `input` is a valid number.
12+
*/
13+
export function isValidNumber(input: number | string): boolean {
14+
if (typeof input === "number") return true;
15+
if (input.trim() === "") return false;
16+
return !isNaN(Number(input));
17+
}
18+
19+
export function parseOptionalInteger(input?: string, defaultValue = 0) {
20+
return (input !== undefined && isValidInteger(input)) ? Number(input) : defaultValue;
21+
}
22+
23+
export function parseOptionalFloat(input?: string, defaultValue = 0) {
24+
return (input !== undefined && isValidNumber(input)) ? parseFloat(input) : defaultValue;
325
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { test as base } from "vitest";
2+
import { extend } from "@prozilla-os/dev-tools";
3+
import { isValidInteger, isValidNumber } from "../src/features";
4+
5+
const test = extend(base);
6+
7+
test.simpleCases(isValidInteger, [
8+
[0, true],
9+
[42, true],
10+
[-7, true],
11+
[3.14, true],
12+
13+
["0", true],
14+
["42", true],
15+
["-7", true],
16+
["1e3", true],
17+
18+
["3.14", false],
19+
["3.0", false],
20+
["1e1.5", false],
21+
["abc", false],
22+
["12abc", false],
23+
["Infinity", false],
24+
["", false],
25+
[" ", false],
26+
]);
27+
28+
test.simpleCases(isValidNumber, [
29+
[0, true],
30+
[42, true],
31+
[-7, true],
32+
[3.14, true],
33+
34+
["0", true],
35+
["42", true],
36+
["-7", true],
37+
["3.14", true],
38+
["1e3", true],
39+
["Infinity", true],
40+
41+
["abc", false],
42+
["12abc", false],
43+
["", false],
44+
[" ", false],
45+
]);

0 commit comments

Comments
 (0)