alacritty-theme-switch
Version:
CLI utility for switching Alacritty color themes
161 lines (160 loc) • 8.49 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ThemeManager_instances, _ThemeManager_config, _ThemeManager_themes, _ThemeManager_themesPaths, _ThemeManager_backupPath, _ThemeManager_configPath, _ThemeManager_setConfig, _ThemeManager_getActiveThemes;
import { errAsync, okAsync, ResultAsync } from "neverthrow";
import { DirectoryIsFileError } from "../utils/fs-errors.js";
import { safeEnsureDir, safeStat, safeWalkAll, } from "../utils/fs-utils.js";
import { isToml, safeParseToml } from "../utils/toml-utils.js";
import { createBackup, parseConfig, writeConfigToFile, } from "./config-operations.js";
import { NoThemesFoundError, ThemeNotFoundError, ThemeNotTOMLError, } from "./errors.js";
import { Theme } from "./theme.js";
/**
* Theme manager encapsulates all operations related to theme management.
* @internal Use `createThemeManager` to create an instance.
*/
class ThemeManager {
constructor(config, themes, backupPath, configPath) {
_ThemeManager_instances.add(this);
/** Alacritty configuration */
_ThemeManager_config.set(this, void 0);
/** All available themes */
_ThemeManager_themes.set(this, void 0);
/** Set of all available themes' paths */
_ThemeManager_themesPaths.set(this, void 0);
/** Path to the backup file */
_ThemeManager_backupPath.set(this, void 0);
/** Path to the Alacritty configuration file */
_ThemeManager_configPath.set(this, void 0);
__classPrivateFieldSet(this, _ThemeManager_config, config, "f");
__classPrivateFieldSet(this, _ThemeManager_themes, themes, "f");
__classPrivateFieldSet(this, _ThemeManager_themesPaths, new Set(themes.map((theme) => theme.path)), "f");
__classPrivateFieldSet(this, _ThemeManager_backupPath, backupPath, "f");
__classPrivateFieldSet(this, _ThemeManager_configPath, configPath, "f");
}
/**
* Configuration getter
* @returns The current parsed Alacritty configuration
*/
getConfig() {
return __classPrivateFieldGet(this, _ThemeManager_config, "f");
}
/**
* Lists themes.
* @returns A list of all available themes
*/
listThemes() {
const activeThemes = __classPrivateFieldGet(this, _ThemeManager_instances, "m", _ThemeManager_getActiveThemes).call(this);
return __classPrivateFieldGet(this, _ThemeManager_themes, "f").map((theme) => new Theme(theme.path, theme.themeContent, activeThemes.has(theme.path)));
}
/**
* Returns the first active theme.
*/
getFirstActiveTheme() {
const activeThemes = __classPrivateFieldGet(this, _ThemeManager_instances, "m", _ThemeManager_getActiveThemes).call(this);
return __classPrivateFieldGet(this, _ThemeManager_themes, "f").find((theme) => activeThemes.has(theme.path));
}
/**
* Applies the selected theme to the Alacritty configuration.
* @param selectedTheme - Theme to apply
* @returns A ResultAsync containing the applied theme or an error
*/
applyTheme(selectedTheme) {
return createBackup(__classPrivateFieldGet(this, _ThemeManager_configPath, "f"), __classPrivateFieldGet(this, _ThemeManager_backupPath, "f"))
.andThen(() => {
const newConfig = structuredClone(this.getConfig());
newConfig.general ??= {};
newConfig.general.import ??= [];
// Remove all themes from import entries first
newConfig.general.import = newConfig.general.import.filter((importEntryPath) => !__classPrivateFieldGet(this, _ThemeManager_themesPaths, "f").has(importEntryPath));
// Then add the selected theme there
newConfig.general.import.push(selectedTheme.path);
return okAsync(newConfig);
})
.andThen((newConfig) => {
return writeConfigToFile(__classPrivateFieldGet(this, _ThemeManager_configPath, "f"), newConfig).map(() => {
__classPrivateFieldGet(this, _ThemeManager_instances, "m", _ThemeManager_setConfig).call(this, newConfig);
return selectedTheme;
});
});
}
/**
* Applies the theme with the given filename.
* @param name - Filename of the theme to apply
* @returns A ResultAsync containing the applied theme or an error
*/
applyThemeByFilename(name) {
const theme = this.listThemes().find((theme) => theme.path.endsWith(name));
if (theme === undefined) {
return errAsync(new ThemeNotFoundError(name));
}
if (!isToml(theme.path)) {
return errAsync(new ThemeNotTOMLError(theme.path));
}
return safeStat(theme.path)
.andThen(() => this.applyTheme(theme))
.mapErr((error) => new ThemeNotFoundError(theme.path, { cause: error }));
}
/**
* Loads all themes from the given directory.
* If the directory doesn't exist, it will be created.
* If the directory exists but is a file, an error will be returned.
* If the directory exists and contains TOML files, they will be parsed and returned.
* If the directory exists and contains no TOML files, an error will be returned.
*
* @param themeDirPath - Path to the directory containing custom themes' files
* @returns A ResultAsync containing an array of all themes or an error
*/
static loadThemes(themeDirPath) {
return safeEnsureDir(themeDirPath)
.andThen(() => safeStat(themeDirPath))
.andThen((stat) => {
return stat.isFile
? errAsync(new DirectoryIsFileError(themeDirPath))
: okAsync(stat);
})
.andThen(() => {
const entriesResult = safeWalkAll(themeDirPath, {
exts: ["toml"],
includeFiles: true,
includeDirs: false,
});
return entriesResult.andThen((entries) => {
if (entries.length === 0) {
return errAsync(new NoThemesFoundError(themeDirPath));
}
const themesResults = entries.map((entry) => {
return safeParseToml(entry.path).map((themeContent) => new Theme(entry.path, themeContent, null));
});
return ResultAsync.combine(themesResults);
});
});
}
}
_ThemeManager_config = new WeakMap(), _ThemeManager_themes = new WeakMap(), _ThemeManager_themesPaths = new WeakMap(), _ThemeManager_backupPath = new WeakMap(), _ThemeManager_configPath = new WeakMap(), _ThemeManager_instances = new WeakSet(), _ThemeManager_setConfig = function _ThemeManager_setConfig(config) {
__classPrivateFieldSet(this, _ThemeManager_config, config, "f");
}, _ThemeManager_getActiveThemes = function _ThemeManager_getActiveThemes() {
const config = this.getConfig();
const imports = config.general?.import ?? [];
const activeThemes = imports.filter((i) => __classPrivateFieldGet(this, _ThemeManager_themesPaths, "f").has(i));
return new Set(activeThemes);
};
/**
* Factory function for creating a theme manager.
*/
export function createThemeManager(params) {
return ThemeManager.loadThemes(params.themesDirPath)
.andThen((themes) => {
return parseConfig(params.configPath).map((config) => {
return new ThemeManager(config, themes, params.backupPath, params.configPath);
});
});
}