alacritty-theme-switch
Version:
CLI utility for switching Alacritty color themes
234 lines (233 loc) • 8.46 kB
JavaScript
import * as dntShim from "../_dnt.shims.js";
import interactiveSearchPrompt from "@inquirer/search";
import { parseArgs } from "../deps/jsr.io/@std/cli/1.0.25/parse_args.js";
import { join } from "../deps/jsr.io/@std/path/1.1.4/join.js";
import { fromPromise } from "neverthrow";
import denoJson from "../deno.js";
/**
* Parse CLI arguments.
* @param cliArgs CLI arguments, e.g. returned by Deno.args
*/
export function getArgs(cliArgs, homeDir, os) {
const parsed = parseArgs(cliArgs, {
boolean: ["help", "version"],
string: [
"config",
"themes",
"backup",
"select",
"url",
"ref",
],
alias: {
h: "help",
v: "version",
c: "config",
t: "themes",
b: "backup",
s: "select",
u: "url",
r: "ref",
},
default: {
config: join(getDefaultConfigDir(homeDir, os), "alacritty.toml"),
themes: join(getDefaultConfigDir(homeDir, os), "themes"),
backup: join(getDefaultConfigDir(homeDir, os), "alacritty.bak.toml"),
url: "https://github.com/alacritty/alacritty-theme",
ref: "master",
},
});
// Parse subcommand from positional arguments
const firstArg = parsed._[0];
if (typeof firstArg === "string" && firstArg === "download-themes") {
return {
...parsed,
command: "download-themes",
};
}
if (typeof firstArg === "string" && firstArg === "clear-themes") {
return {
...parsed,
command: "clear-themes",
};
}
return parsed;
}
export function parsePositionalCommand(positionalArgs) {
const firstArg = positionalArgs?.[0];
if (typeof firstArg === "string" && firstArg === "download-themes") {
return "download-themes";
}
if (typeof firstArg === "string" && firstArg === "clear-themes") {
return "clear-themes";
}
return undefined;
}
/**
* Print help message.
*/
export function printHelp() {
console.log(`alacritty-theme-switch ${denoJson.version}\n` +
`Usage:\n` +
` ats [options] Interactive theme selection\n` +
` ats download-themes [options] Download themes from GitHub repository\n` +
` ats clear-themes [options] Delete all themes from themes directory\n` +
`\n` +
`Commands:\n` +
` download-themes Download themes from a GitHub repository\n` +
` clear-themes Delete all theme files from the themes directory\n` +
`\n` +
`Options:\n` +
` -h, --help Show this help message and exit.\n` +
` -v, --version Show the version number and exit.\n` +
` -c, --config Path to the alacritty's configuration file\n` +
` (default: $HOME/.config/alacritty/alacritty.toml)\n` +
` -t, --themes Path to the directory containing custom themes' files\n` +
` (default: $HOME/.config/alacritty/themes)\n` +
` -b, --backup Path to the alacritty's configuration file backup made\n` +
` before every switch\n` +
` (default: $HOME/.config/alacritty/alacritty.bak.toml)\n` +
` -s, --select Path (relative to themes' directory) to a single\n` +
` configuration file that should be used directly instead of\n` +
` prompting a select\n` +
`\n` +
`download-themes options:\n` +
` -u, --url GitHub repository URL to download themes from\n` +
` (default: https://github.com/alacritty/alacritty-theme)\n` +
` -r, --ref Git reference (branch, tag, or commit SHA) to download from\n` +
` (default: master)`);
}
/**
* Print version.
*/
export function printVersion() {
console.log(denoJson.version);
}
class ExitPromptError extends Error {
constructor(options) {
super("Cancelled by user.", options);
Object.defineProperty(this, "_tag", {
enumerable: true,
configurable: true,
writable: true,
value: "ExitPromptError"
});
}
}
class InteractiveSearchError extends Error {
constructor(options) {
super("Interactive search prompt failed.", options);
Object.defineProperty(this, "_tag", {
enumerable: true,
configurable: true,
writable: true,
value: "InteractiveSearchError"
});
}
}
/**
* Displays an interactive prompt to select a theme.
* @param themeManager - Theme manager instance
* @returns A ResultAsync containing the selected theme or an error
*/
export function interactiveThemesSelection(themeManager) {
const themes = themeManager.listThemes();
const activeTheme = themeManager.getFirstActiveTheme();
const filterThemesOnInput = (input) => {
return themes
.filter((theme) => {
// If no input, show all themes
if (!input) {
return true;
}
// Match on brightness
if (/^light|dark$/i.test(input)) {
return theme.brightness === input.trim().toLowerCase();
}
// Dumb fuzzy matching
const inputWords = input.split(/\s+/);
const allMatch = inputWords.every((word) => theme.label.toLowerCase().includes(word.toLowerCase()));
if (allMatch) {
return true;
}
return false;
})
.sort((a, b) => {
// Sort by brightness first (dark before light)
if (a.brightness !== b.brightness) {
return a.brightness === "dark" ? -1 : 1;
}
// Then sort alphabetically by label
return a.label.localeCompare(b.label, undefined, { numeric: true });
})
.map((theme) => {
// Add brightness indicator to theme name
const brightnessIcon = theme.brightness === "light" ? "☀️ " : "🌙";
const themeName = `${brightnessIcon} ${theme.label}`;
return {
name: theme.isCurrentlyActive
? underscore(bold(themeName) + " ✨")
: themeName,
value: theme,
};
});
};
return fromPromise(interactiveSearchPrompt({
message: activeTheme
? `Select Alacritty color theme (current: ${activeTheme.label})`
: `Select Alacritty color theme`,
theme: {
style: {
keysHelpTip: () => "(Use arrow keys to navigate, type to search, space to select and enter to confirm)",
},
},
pageSize: 10,
source: filterThemesOnInput,
}), (error) => {
if (Error.isError(error) && error.name === "ExitPromptError") {
return new ExitPromptError({ cause: error });
}
return new InteractiveSearchError({ cause: error });
});
}
/**
* Get home directory.
*
* Uses $HOME on POSIX systems and $USERPROFILE on Windows.
* If neither is set, uses current directory and logs a warning.
*/
export function getHomeDir(os) {
let homeDir;
if (os === "windows") {
homeDir = dntShim.Deno.env.get("APPDATA") ?? dntShim.Deno.env.get("USERPROFILE");
}
else {
homeDir = dntShim.Deno.env.get("HOME") ?? dntShim.Deno.env.get("XDG_CONFIG_HOME");
}
if (homeDir === undefined) {
console.warn("Could not determine home directory. Using current directory as the default root.");
homeDir = dntShim.Deno.cwd();
}
return homeDir;
}
/**
* Get default alacritty configuration path.
*/
function getDefaultConfigDir(homeDir, os) {
if (os === "windows") {
return join(homeDir, "alacritty");
}
return join(homeDir, ".config", "alacritty");
}
/**
* Make terminal output text bold.
*/
export function bold(s) {
return `\x1b[1m${s}\x1b[0m`;
}
/**
* Make terminal output text underlined.
*/
export function underscore(s) {
return `\x1b[4m${s}\x1b[0m`;
}