Add btop and pacman terminal commands
This commit is contained in:
1 parent
201a1e0914
commit
dcf27d90b9
3 files changed
+152
-3
No files matched your search
@@ -3,6 +3,7 @@ import { allSitePages } from '#lib/site/pages';
|
||||
import { blankLine, errorLine, line, span } from '../output';
|
||||
import { infoCommands } from './info';
|
||||
import { systemCommands } from './system';
|
||||
import { pacmanCommand } from './pacman';
|
||||
import type { Command, CommandContext } from './types';
|
||||
|
||||
export type { Command, CommandContext } from './types';
|
||||
@@ -38,7 +39,14 @@ const socialShortcuts: Command[] = profile.socials.map((social) => ({
|
||||
run: (_args, context) => context.openUrl(social.url)
|
||||
}));
|
||||
|
||||
export const commands: Command[] = [helpCommand, ...infoCommands, ...systemCommands, ...pageShortcuts, ...socialShortcuts];
|
||||
export const commands: Command[] = [
|
||||
helpCommand,
|
||||
...infoCommands,
|
||||
...systemCommands,
|
||||
pacmanCommand,
|
||||
...pageShortcuts,
|
||||
...socialShortcuts
|
||||
];
|
||||
|
||||
export function findCommand(name: string): Command | undefined {
|
||||
const lowerName = name.toLowerCase();
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { profile } from '#lib/content';
|
||||
import { blankLine, errorLine, line, span, type OutputBlock } from '../output';
|
||||
import type { Command, CommandContext } from './types';
|
||||
|
||||
const stepDelay = 350;
|
||||
const progressBarWidth = 24;
|
||||
|
||||
const jokePackages: Record<string, OutputBlock[]> = {
|
||||
girlfriend: [errorLine('target not found: girlfriend')],
|
||||
money: [errorLine('target not found: money'), line(span('hint: ', 'yellow'), 'try the Ko-fi icon in the top bar')],
|
||||
sleep: [
|
||||
line(span(':: ', 'blue', { bold: true }), 'sleep and energy-drinks are in conflict. Remove energy-drinks? [y/N] n'),
|
||||
errorLine('unresolvable package conflicts detected')
|
||||
],
|
||||
windows: [errorLine('windows is not available for Arch Linux. Nice try.')],
|
||||
job: [errorLine('job requires entry-level-position>=5-years-experience, which cannot be satisfied')],
|
||||
internship: [errorLine('internship requires internship-experience, which requires internship')]
|
||||
};
|
||||
|
||||
function packageVersion(name: string): string {
|
||||
const seed = [...name].reduce((sum, character) => sum + character.charCodeAt(0), 0);
|
||||
return `${(seed % 9) + 1}.${seed % 13}.${seed % 7}-1`;
|
||||
}
|
||||
|
||||
function packageSize(name: string): string {
|
||||
return `${(name.length * 1.37 + 0.42).toFixed(2)} MiB`;
|
||||
}
|
||||
|
||||
function progressLine(index: number, total: number, action: string, name: string): OutputBlock {
|
||||
const counter = `(${index}/${total})`;
|
||||
return line(
|
||||
span(`${counter} ${action} ${name}`.padEnd(38), 'foreground'),
|
||||
span(`[${'#'.repeat(progressBarWidth)}]`, 'green'),
|
||||
span(' 100%', 'muted')
|
||||
);
|
||||
}
|
||||
|
||||
function printOverTime(context: CommandContext, blocks: OutputBlock[]) {
|
||||
blocks.forEach((block, index) => setTimeout(() => context.print(block), index * stepDelay));
|
||||
}
|
||||
|
||||
function installPackages(context: CommandContext, packages: string[]) {
|
||||
const jokeName = packages.find((name) => name in jokePackages);
|
||||
if (jokeName) {
|
||||
printOverTime(context, [line(span('resolving dependencies...', 'muted')), ...jokePackages[jokeName]]);
|
||||
return;
|
||||
}
|
||||
|
||||
const total = packages.length;
|
||||
printOverTime(context, [
|
||||
line(span('resolving dependencies...', 'muted')),
|
||||
line(span('looking for conflicting packages...', 'muted')),
|
||||
blankLine,
|
||||
line(span(`Packages (${total}) `, 'foreground', { bold: true }), packages.map((name) => `${name}-${packageVersion(name)}`).join(' ')),
|
||||
blankLine,
|
||||
line(span('Total Installed Size: ', 'foreground', { bold: true }), packageSize(packages.join(''))),
|
||||
blankLine,
|
||||
line(span(':: ', 'blue', { bold: true }), 'Proceed with installation? [Y/n] y'),
|
||||
...packages.map((name, index) => progressLine(index + 1, total, 'installing', name)),
|
||||
line(span(':: ', 'blue', { bold: true }), 'Running post-transaction hooks...'),
|
||||
progressLine(1, 1, 'Reloading', profile.username),
|
||||
blankLine,
|
||||
line(span('(this is a website, nothing was actually installed, but it felt good right?)', 'muted'))
|
||||
]);
|
||||
}
|
||||
|
||||
function systemUpgrade(context: CommandContext) {
|
||||
printOverTime(context, [
|
||||
line(span(':: ', 'blue', { bold: true }), 'Synchronizing package databases...'),
|
||||
line(' core is up to date'),
|
||||
line(' extra is up to date'),
|
||||
line(span(':: ', 'blue', { bold: true }), 'Starting full system upgrade...'),
|
||||
line(' there is nothing to do'),
|
||||
line(span('btw, I use Arch.', 'accent'))
|
||||
]);
|
||||
}
|
||||
|
||||
function listInstalled(context: CommandContext) {
|
||||
const installed = Object.values(profile.skills)
|
||||
.flat()
|
||||
.map((skill) => skill.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''));
|
||||
context.print(...installed.map((name) => line(span(name, 'foreground', { bold: true }), ' ', span(packageVersion(name), 'green'))));
|
||||
}
|
||||
|
||||
function removePackages(context: CommandContext, packages: string[]) {
|
||||
const name = packages[0];
|
||||
context.print(line(span('checking dependencies...', 'muted')), errorLine(`failed to prepare transaction: ${name} is required by ${profile.username}`));
|
||||
}
|
||||
|
||||
function printHelp(context: CommandContext) {
|
||||
context.print(
|
||||
line('usage: pacman <operation> [...]'),
|
||||
line('operations:'),
|
||||
line(' pacman -S <package> install packages'),
|
||||
line(' pacman -Syu upgrade the system'),
|
||||
line(' pacman -Q list installed packages'),
|
||||
line(' pacman -R <package> remove packages')
|
||||
);
|
||||
}
|
||||
|
||||
function runPacman(args: string[], context: CommandContext) {
|
||||
const [operation, ...packages] = args;
|
||||
if (!operation) {
|
||||
context.print(errorLine('no operation specified (use -h for help)'));
|
||||
return;
|
||||
}
|
||||
if (operation === '-h' || operation === '--help') return printHelp(context);
|
||||
if (operation.startsWith('-Sy') && packages.length === 0) return systemUpgrade(context);
|
||||
if (operation.startsWith('-Q')) return listInstalled(context);
|
||||
|
||||
if (packages.length === 0) {
|
||||
context.print(errorLine('no targets specified (use -h for help)'));
|
||||
return;
|
||||
}
|
||||
if (operation.startsWith('-S')) return installPackages(context, packages.map((name) => name.toLowerCase()));
|
||||
if (operation.startsWith('-R')) return removePackages(context, packages);
|
||||
context.print(errorLine(`invalid option '${operation}'`));
|
||||
}
|
||||
|
||||
export const pacmanCommand: Command = {
|
||||
name: 'pacman',
|
||||
hidden: true,
|
||||
description: 'Arch Linux package manager',
|
||||
run: runPacman
|
||||
};
|
||||
|
||||
export function runAsRoot(args: string[], context: CommandContext): boolean {
|
||||
if (args[0] !== 'pacman') return false;
|
||||
context.print(line(span(`[sudo] password for ${profile.username}: `, 'muted'), '********'));
|
||||
runPacman(args.slice(1), context);
|
||||
return true;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { profile } from '#lib/content';
|
||||
import { themes } from '#lib/theme/themes';
|
||||
import { allSitePages, findSitePage, sitePages } from '#lib/site/pages';
|
||||
import { blankLine, errorLine, line, span } from '../output';
|
||||
import { runAsRoot } from './pacman';
|
||||
import type { Command } from './types';
|
||||
|
||||
function cowsay(message: string): string[] {
|
||||
@@ -122,8 +123,16 @@ export const systemCommands: Command[] = [
|
||||
name: 'sudo',
|
||||
hidden: true,
|
||||
description: 'Elevated privileges',
|
||||
run: (_args, context) =>
|
||||
context.print(errorLine(`${profile.username} is not in the sudoers file. This incident will be reported.`))
|
||||
run: (args, context) => {
|
||||
if (runAsRoot(args, context)) return;
|
||||
context.print(errorLine(`${profile.username} is not in the sudoers file. This incident will be reported.`));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'btop',
|
||||
aliases: ['htop', 'top'],
|
||||
description: 'Open the system monitor',
|
||||
run: (_args, context) => context.openApp('btop')
|
||||
},
|
||||
{
|
||||
name: 'pwd',
|
||||
|
||||
Reference in new issue
Block a user