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:
@@ -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
152
scripts/makevivid.js
Normal 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();
|
||||
Reference in New Issue
Block a user