Added a Vivid theme variant for all themes. (#402)

* Added vivid theme variants

* Update fill theme to handle vivids

* Fixed the network switch color
This commit is contained in:
Jas Singh
2024-10-30 01:48:03 -07:00
committed by GitHub
parent 87f67020e7
commit 9bb582c8cf
44 changed files with 5130 additions and 36 deletions

View File

@@ -5,7 +5,7 @@
*
* A Node.js script to compare theme JSON files against base themes and add missing keys,
* as well as remove any properties that don't exist in the corresponding base theme.
* It assigns values based on matching colors or randomly selects from border colors.
* It assigns values based on matching colors, specific property mappings, or randomly selects from border colors.
*
* Usage:
* node compare_themes.js [--dry-run] [themes_directory]
@@ -123,7 +123,9 @@ const findMissingKeys = (baseJSON, targetJSON) => {
* @returns {boolean} True if the key is excluded, otherwise false.
*/
const isExcludedKey = (key) => {
const excludedPatterns = [];
const excludedPatterns = [
// Add any regex patterns for keys to exclude here
];
return excludedPatterns.some((pattern) => pattern.test(key));
};
@@ -171,9 +173,27 @@ const collectBorderColors = (baseJSON) => {
* @param {string} baseValue - The value of the missing key in the base theme.
* @param {Object} valueToKeysMap - A map from values to keys in the base theme.
* @param {Object} targetJSON - The target JSON object.
* @param {Object} specialKeyMappings - A map of special keys to their source keys.
* @param {string} currentKey - The key currently being processed.
* @returns {*} The best matching value or null if a random selection is needed.
*/
const determineBestMatchValue = (baseValue, valueToKeysMap, targetJSON) => {
const determineBestMatchValue = (baseValue, valueToKeysMap, targetJSON, specialKeyMappings, currentKey) => {
// Check if the current key is in special mappings
if (specialKeyMappings.hasOwnProperty(currentKey)) {
const sourceKey = specialKeyMappings[currentKey];
if (targetJSON.hasOwnProperty(sourceKey)) {
return targetJSON[sourceKey];
} else {
console.warn(
formatMessage(
COLORS.FG_YELLOW,
`⚠️ Source key '${sourceKey}' for special key '${currentKey}' not found. Using random border color.`,
),
);
return null;
}
}
const relatedBaseKeys = valueToKeysMap[baseValue] || [];
const correspondingTargetValues = relatedBaseKeys
@@ -225,8 +245,9 @@ const backupTheme = (themePath) => {
* @param {string} themePath - The path to the theme file.
* @param {Object} baseTheme - The base JSON object.
* @param {boolean} dryRun - If true, no changes will be written to files.
* @param {Object} specialKeyMappings - A map of special keys to their source keys.
*/
const processTheme = (themePath, baseTheme, dryRun) => {
const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => {
const themeJSON = loadJSON(themePath);
const missingKeys = findMissingKeys(baseTheme, themeJSON);
@@ -252,7 +273,7 @@ const processTheme = (themePath, baseTheme, dryRun) => {
}
const baseValue = baseTheme[key];
const bestValue = determineBestMatchValue(baseValue, valueToKeysMap, themeJSON);
const bestValue = determineBestMatchValue(baseValue, valueToKeysMap, themeJSON, specialKeyMappings, key);
if (bestValue !== null) {
themeJSON[key] = bestValue;
@@ -336,8 +357,10 @@ const main = () => {
const baseThemeFile = 'catppuccin_mocha.json';
const baseThemeSplitFile = 'catppuccin_mocha_split.json';
const baseThemeVividFile = 'catppuccin_mocha_vivid.json';
const baseThemePath = path.join(themesDir, baseThemeFile);
const baseThemeSplitPath = path.join(themesDir, baseThemeSplitFile);
const baseThemeVividPath = path.join(themesDir, baseThemeVividFile);
if (!fs.existsSync(baseThemePath)) {
console.error(
@@ -356,13 +379,31 @@ const main = () => {
process.exit(1);
}
if (!fs.existsSync(baseThemeVividPath)) {
console.error(
formatMessage(
COLORS.FG_RED,
`❌ Error: Base vivid theme '${baseThemeVividFile}' does not exist in '${themesDir}'.`,
),
);
process.exit(1);
}
const baseTheme = loadJSON(baseThemePath);
const baseThemeSplit = loadJSON(baseThemeSplitPath);
const baseThemeVivid = loadJSON(baseThemeVividPath);
const themeFiles = fs.readdirSync(themesDir).filter((file) => file.endsWith('.json'));
// Define special key mappings
// Format: "target_key": "source_key"
const specialKeyMappings = {
'theme.bar.menus.menu.network.switch.enabled': 'theme.bar.menus.menu.network.iconbuttons.active',
// Add more special mappings here if needed
};
themeFiles.forEach((file) => {
if (file === baseThemeFile || file === baseThemeSplitFile) {
if (file === baseThemeFile || file === baseThemeSplitFile || file === baseThemeVividFile) {
return;
}
@@ -371,12 +412,14 @@ const main = () => {
if (file.endsWith('_split.json')) {
correspondingBaseTheme = baseThemeSplit;
} else if (file.endsWith('_vivid.json')) {
correspondingBaseTheme = baseThemeVivid;
} else {
correspondingBaseTheme = baseTheme;
}
try {
processTheme(themePath, correspondingBaseTheme, dryRun);
processTheme(themePath, correspondingBaseTheme, dryRun, specialKeyMappings);
} catch (error) {
console.error(formatMessage(COLORS.FG_RED, `❌ Error processing '${file}': ${error.message}`));
}

152
scripts/makevivid.js Normal file
View File

@@ -0,0 +1,152 @@
const fs = require('fs');
const path = require('path');
/**
* WARN: This script is generated by our good friend Chat Jippity
* It may contain errors and weird behaviors...
* Use at your own discretion
*/
const THEMES_DIR = path.join(__dirname, '../themes');
/**
* Swaps the .icon and .text (or .total) properties with the .background property for a given base path.
* @param {Object} themeData - The original theme JSON data.
* @param {String} basePath - The base path of the properties to swap (e.g., 'theme.bar.buttons.volume').
* @param {Array} additionalTextKeys - Additional property keys to treat as 'text' (e.g., ['total']).
*/
function swapProperties(themeData, basePath, additionalTextKeys = []) {
const iconKey = `${basePath}.icon`;
const backgroundKey = `${basePath}.background`;
const textKeys = additionalTextKeys.map((suffix) => `${basePath}.${suffix}`);
if (themeData.hasOwnProperty(iconKey) && themeData.hasOwnProperty(backgroundKey)) {
const originalIcon = themeData[iconKey];
const originalBackground = themeData[backgroundKey];
themeData[iconKey] = originalBackground;
console.log(`✅ Updated '${iconKey}': '${originalIcon}' -> '${themeData[iconKey]}'`);
themeData[backgroundKey] = originalIcon;
console.log(`✅ Updated '${backgroundKey}': '${originalBackground}' -> '${themeData[backgroundKey]}'`);
textKeys.forEach((textKey) => {
if (themeData.hasOwnProperty(textKey)) {
const originalText = themeData[textKey];
themeData[textKey] = originalBackground;
console.log(`✅ Updated '${textKey}': '${originalText}' -> '${themeData[textKey]}'`);
} else {
console.warn(`⚠️ Property '${textKey}' not found in the theme. Skipping.`);
}
});
console.log('');
} else {
console.warn(`⚠️ Missing '.icon' or '.background' for '${basePath}'. Skipping swap.\n`);
}
}
/**
* Identifies all base paths that match "theme.bar.buttons.*" and "theme.bar.buttons.modules.*"
* and have necessary properties to perform swaps.
* @param {Object} themeData - The original theme JSON data.
* @returns {Array} - An array of objects containing basePath and any additional text-like keys.
*/
function identifyBasePaths(themeData) {
const basePathsMap = new Map();
Object.keys(themeData).forEach((key) => {
const regex = /^theme\.bar\.buttons(?:\.modules)?\.\w+\.(icon|background|text|total)$/;
const match = key.match(regex);
if (match) {
const property = match[1];
const basePath = key.substring(0, key.lastIndexOf('.'));
if (!basePathsMap.has(basePath)) {
basePathsMap.set(basePath, []);
}
if (property === 'text' || property === 'total') {
basePathsMap.get(basePath).push(property);
}
}
});
const basePaths = [];
basePathsMap.forEach((additionalTextKeys, basePath) => {
basePaths.push({ basePath, additionalTextKeys });
});
return basePaths;
}
/**
* Generates a vivid variant of the given theme by swapping specified properties.
* @param {String} originalThemePath - Path to the original theme JSON file.
* @param {String} vividThemePath - Path to save the vivid variant JSON file.
*/
function generateVividTheme(originalThemePath, vividThemePath) {
try {
const data = fs.readFileSync(originalThemePath, 'utf8');
const theme = JSON.parse(data);
const basePathObjects = identifyBasePaths(theme);
if (basePathObjects.length === 0) {
console.warn(`⚠️ No swappable property groups found in '${path.basename(originalThemePath)}'. Skipping.\n`);
return;
}
const vividTheme = { ...theme };
basePathObjects.forEach(({ basePath, additionalTextKeys }) => {
swapProperties(vividTheme, basePath, additionalTextKeys);
});
fs.writeFileSync(vividThemePath, JSON.stringify(vividTheme, null, 4), 'utf8');
console.log(`✅ Created vivid variant: '${path.basename(vividThemePath)}'\n`);
} catch (error) {
console.error(`❌ Error processing '${path.basename(originalThemePath)}': ${error.message}\n`);
}
}
/**
* Main function to process all themes and generate their vivid variants.
*/
function main() {
if (!fs.existsSync(THEMES_DIR)) {
console.error(
`❌ Themes directory not found at '${THEMES_DIR}'. Please ensure it exists and contains theme JSON files.\n`,
);
process.exit(1);
}
const files = fs.readdirSync(THEMES_DIR);
const originalThemes = files.filter((file) => {
return file.endsWith('.json') && !file.includes('_vivid') && !file.includes('_split');
});
if (originalThemes.length === 0) {
console.log(' No eligible theme files found to process.\n');
return;
}
originalThemes.forEach((themeFile) => {
const themeName = path.parse(themeFile).name;
const originalThemePath = path.join(THEMES_DIR, themeFile);
const vividThemeFile = `${themeName}_vivid.json`;
const vividThemePath = path.join(THEMES_DIR, vividThemeFile);
if (fs.existsSync(vividThemePath)) {
console.log(` Vivid variant already exists for '${themeFile}'. Skipping.\n`);
return;
}
console.log(`🔄 Processing '${themeFile}'...`);
generateVividTheme(originalThemePath, vividThemePath);
});
console.log('🎉 All eligible vivid themes have been processed.\n');
}
main();