From dec781071a6d16aef4a0b3b4e7da7098c1fe118c Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Sun, 22 Dec 2024 02:19:51 -0800 Subject: [PATCH] Added a scrollbar to the network and bluetooth menu. (#593) * Added a scrollbar to the network and bluetooth menu. * Update themes * Adjust height of network menu scroller. --- scripts/fillThemes.js | 186 ++--- .../menus/bluetooth/devices/index.tsx | 8 +- .../menus/network/wifi/WirelessAPs/index.tsx | 28 +- .../settings/pages/theme/menus/bluetooth.tsx | 4 + .../settings/pages/theme/menus/index.tsx | 5 + .../settings/pages/theme/menus/network.tsx | 4 + src/options.ts | 10 + src/scss/style/menus/bluetooth.scss | 12 + src/scss/style/menus/menu.scss | 11 + src/scss/style/menus/network.scss | 12 + themes/catppuccin_frappe.json | 6 +- themes/catppuccin_frappe_split.json | 6 +- themes/catppuccin_frappe_vivid.json | 6 +- themes/catppuccin_latte.json | 6 +- themes/catppuccin_latte_split.json | 6 +- themes/catppuccin_latte_vivid.json | 6 +- themes/catppuccin_macchiato.json | 6 +- themes/catppuccin_macchiato_split.json | 6 +- themes/catppuccin_macchiato_vivid.json | 6 +- themes/catppuccin_mocha.json | 2 + themes/catppuccin_mocha_split.json | 2 + themes/catppuccin_mocha_vivid.json | 714 ++++++++--------- themes/cyberpunk.json | 6 +- themes/cyberpunk_split.json | 6 +- themes/cyberpunk_vivid.json | 6 +- themes/dracula.json | 6 +- themes/dracula_split.json | 6 +- themes/dracula_vivid.json | 6 +- themes/everforest.json | 6 +- themes/everforest_split.json | 6 +- themes/everforest_vivid.json | 6 +- themes/gruvbox.json | 6 +- themes/gruvbox_split.json | 6 +- themes/gruvbox_vivid.json | 6 +- themes/monochrome.json | 6 +- themes/monochrome_split.json | 6 +- themes/monochrome_vivid.json | 6 +- themes/nord.json | 6 +- themes/nord_split.json | 6 +- themes/nord_vivid.json | 6 +- themes/one_dark.json | 6 +- themes/one_dark_split.json | 6 +- themes/one_dark_vivid.json | 6 +- themes/rose_pine.json | 716 +++++++++--------- themes/rose_pine_moon.json | 716 +++++++++--------- themes/rose_pine_moon_split.json | 716 +++++++++--------- themes/rose_pine_moon_vivid.json | 716 +++++++++--------- themes/rose_pine_split.json | 716 +++++++++--------- themes/rose_pine_vivid.json | 716 +++++++++--------- themes/tokyo_night.json | 6 +- themes/tokyo_night_split.json | 6 +- themes/tokyo_night_vivid.json | 6 +- 52 files changed, 2824 insertions(+), 2668 deletions(-) diff --git a/scripts/fillThemes.js b/scripts/fillThemes.js index cc8855a..2d8e3fe 100644 --- a/scripts/fillThemes.js +++ b/scripts/fillThemes.js @@ -13,7 +13,7 @@ * If no themes_directory is provided, it defaults to '~/.config/ags/themes'. */ -const fs = require('fs'); +const fs = require('fs').promises; const path = require('path'); const os = require('os'); @@ -48,14 +48,14 @@ const COLORS = { const formatMessage = (color, message) => `${color}${message}${COLORS.RESET}`; /** - * Loads and parses a JSON file. + * Loads and parses a JSON file asynchronously. * * @param {string} filePath - The path to the JSON file. - * @returns {Object} The parsed JSON object. + * @returns {Promise} The parsed JSON object. */ -const loadJSON = (filePath) => { +const loadJSON = async (filePath) => { try { - const data = fs.readFileSync(filePath, 'utf8'); + const data = await fs.readFile(filePath, 'utf8'); return JSON.parse(data); } catch (error) { console.error(formatMessage(COLORS.FG_RED, `Error reading or parsing '${filePath}': ${error.message}`)); @@ -64,15 +64,15 @@ const loadJSON = (filePath) => { }; /** - * Saves a JSON object to a file with indentation. + * Saves a JSON object to a file with indentation asynchronously. * * @param {string} filePath - The path to the JSON file. * @param {Object} data - The JSON data to save. */ -const saveJSON = (filePath, data) => { +const saveJSON = async (filePath, data) => { try { const jsonString = JSON.stringify(data, null, 2); - fs.writeFileSync(filePath, jsonString, 'utf8'); + await fs.writeFile(filePath, jsonString, 'utf8'); } catch (error) { console.error(formatMessage(COLORS.FG_RED, `Error writing to '${filePath}': ${error.message}`)); process.exit(1); @@ -123,9 +123,7 @@ const findMissingKeys = (baseJSON, targetJSON) => { * @returns {boolean} True if the key is excluded, otherwise false. */ const isExcludedKey = (key) => { - const excludedPatterns = [ - // Add any regex patterns for keys to exclude here - ]; + const excludedPatterns = []; return excludedPatterns.some((pattern) => pattern.test(key)); }; @@ -175,14 +173,18 @@ const collectBorderColors = (baseJSON) => { * @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. + * @param {Object} baseTheme - The base theme JSON object. * @returns {*} The best matching value or null if a random selection is needed. */ -const determineBestMatchValue = (baseValue, valueToKeysMap, targetJSON, specialKeyMappings, currentKey) => { - // Check if the current key is in special mappings +const determineBestMatchValue = (baseValue, valueToKeysMap, targetJSON, specialKeyMappings, currentKey, baseTheme) => { if (specialKeyMappings.hasOwnProperty(currentKey)) { const sourceKey = specialKeyMappings[currentKey]; if (targetJSON.hasOwnProperty(sourceKey)) { + console.log(formatMessage(COLORS.FG_CYAN, `šŸ” Found source key '${sourceKey}' in target JSON.`)); return targetJSON[sourceKey]; + } else if (baseTheme && baseTheme.hasOwnProperty(sourceKey)) { + console.log(formatMessage(COLORS.FG_CYAN, `šŸ” Found source key '${sourceKey}' in base theme.`)); + return baseTheme[sourceKey]; } else { console.warn( formatMessage( @@ -229,14 +231,16 @@ const findExtraKeys = (baseTheme, targetJSON) => { * * @param {string} themePath - The path to the theme file. */ -const backupTheme = (themePath) => { - const backupDir = path.join(path.dirname(themePath), 'backup'); - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir); +const backupTheme = async (themePath) => { + try { + const backupDir = path.join(path.dirname(themePath), 'backup'); + await fs.mkdir(backupDir, { recursive: true }); + const backupPath = path.join(backupDir, path.basename(themePath)); + await fs.copyFile(themePath, backupPath); + console.log(formatMessage(COLORS.FG_CYAN, `Backup created at '${backupPath}'.`)); + } catch (error) { + console.error(formatMessage(COLORS.FG_RED, `āŒ Error creating backup for '${themePath}': ${error.message}`)); } - const backupPath = path.join(backupDir, path.basename(themePath)); - fs.copyFileSync(themePath, backupPath); - console.log(formatMessage(COLORS.FG_CYAN, `Backup created at '${backupPath}'.`)); }; /** @@ -246,9 +250,10 @@ const backupTheme = (themePath) => { * @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. + * @returns {Promise} */ -const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => { - const themeJSON = loadJSON(themePath); +const processTheme = async (themePath, baseTheme, dryRun, specialKeyMappings = {}) => { + const themeJSON = await loadJSON(themePath); const missingKeys = findMissingKeys(baseTheme, themeJSON); let hasChanges = false; @@ -266,14 +271,21 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => const valueToKeysMap = buildValueToKeysMap(baseTheme); const borderColors = collectBorderColors(baseTheme); - missingKeys.forEach((key) => { + for (const key of missingKeys) { if (isExcludedKey(key)) { console.log(formatMessage(COLORS.FG_MAGENTA, `ā— Excluded key from addition: "${key}"`)); - return; + continue; } const baseValue = baseTheme[key]; - const bestValue = determineBestMatchValue(baseValue, valueToKeysMap, themeJSON, specialKeyMappings, key); + const bestValue = determineBestMatchValue( + baseValue, + valueToKeysMap, + themeJSON, + specialKeyMappings, + key, + baseTheme, + ); if (bestValue !== null) { themeJSON[key] = bestValue; @@ -281,7 +293,7 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => } else { if (borderColors.length === 0) { console.error(formatMessage(COLORS.FG_RED, 'āŒ Error: No border colors available to assign.')); - return; + continue; } const randomColor = borderColors[Math.floor(Math.random() * borderColors.length)]; themeJSON[key] = randomColor; @@ -294,7 +306,7 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => } hasChanges = true; - }); + } } const extraKeys = findExtraKeys(baseTheme, themeJSON); @@ -309,11 +321,11 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => ), ); - extraKeys.forEach((key) => { + for (const key of extraKeys) { delete themeJSON[key]; console.log(formatMessage(COLORS.FG_RED, `āž– Removed key: "${key}"`)); hasChanges = true; - }); + } } if (hasChanges) { @@ -325,8 +337,8 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => ), ); } else { - backupTheme(themePath); - saveJSON(themePath, themeJSON); + await backupTheme(themePath); + await saveJSON(themePath, themeJSON); console.log( formatMessage(COLORS.FG_GREEN, `āœ… Updated '${path.basename(themePath)}' with missing and extra keys.`), ); @@ -339,7 +351,7 @@ const processTheme = (themePath, baseTheme, dryRun, specialKeyMappings = {}) => /** * The main function that orchestrates the theme comparison and updating. */ -const main = () => { +const main = async () => { const args = process.argv.slice(2); const dryRunIndex = args.indexOf('--dry-run'); const dryRun = dryRunIndex !== -1; @@ -350,7 +362,9 @@ const main = () => { const themesDir = args[0] || path.join(os.homedir(), '.config', 'ags', 'themes'); - if (!fs.existsSync(themesDir)) { + try { + await fs.access(themesDir); + } catch (error) { console.error(formatMessage(COLORS.FG_RED, `āŒ Error: Themes directory '${themesDir}' does not exist.`)); process.exit(1); } @@ -362,72 +376,68 @@ const main = () => { const baseThemeSplitPath = path.join(themesDir, baseThemeSplitFile); const baseThemeVividPath = path.join(themesDir, baseThemeVividFile); - if (!fs.existsSync(baseThemePath)) { - console.error( - formatMessage(COLORS.FG_RED, `āŒ Error: Base theme '${baseThemeFile}' does not exist in '${themesDir}'.`), - ); - process.exit(1); + const baseThemes = [ + { file: baseThemeFile, path: baseThemePath }, + { file: baseThemeSplitFile, path: baseThemeSplitPath }, + { file: baseThemeVividFile, path: baseThemeVividPath }, + ]; + + for (const theme of baseThemes) { + try { + await fs.access(theme.path); + } catch (error) { + console.error( + formatMessage(COLORS.FG_RED, `āŒ Error: Base theme '${theme.file}' does not exist in '${themesDir}'.`), + ); + process.exit(1); + } } - if (!fs.existsSync(baseThemeSplitPath)) { - console.error( - formatMessage( - COLORS.FG_RED, - `āŒ Error: Base split theme '${baseThemeSplitFile}' does not exist in '${themesDir}'.`, - ), - ); - process.exit(1); - } + const [baseTheme, baseThemeSplit, baseThemeVivid] = await Promise.all([ + loadJSON(baseThemePath), + loadJSON(baseThemeSplitPath), + loadJSON(baseThemeVividPath), + ]); - if (!fs.existsSync(baseThemeVividPath)) { - console.error( - formatMessage( - COLORS.FG_RED, - `āŒ Error: Base vivid theme '${baseThemeVividFile}' does not exist in '${themesDir}'.`, - ), - ); - process.exit(1); - } + const themeFiles = (await fs.readdir(themesDir)).filter((file) => file.endsWith('.json')); - 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.buttons.modules.hypridle.icon': 'theme.bar.buttons.modules.storage.icon', - 'theme.bar.buttons.modules.hypridle.background': 'theme.bar.buttons.modules.storage.background', - 'theme.bar.buttons.modules.hypridle.icon_background': 'theme.bar.buttons.modules.storage.icon_background', - 'theme.bar.buttons.modules.hypridle.text': 'theme.bar.buttons.modules.storage.text', - 'theme.bar.buttons.modules.hypridle.border': 'theme.bar.buttons.modules.storage.border', - // Add more special mappings here if needed + 'theme.bar.menus.menu.bluetooth.scroller.color': 'theme.bar.menus.menu.bluetooth.label.color', + 'theme.bar.menus.menu.network.scroller.color': 'theme.bar.menus.menu.network.label.color', }; - themeFiles.forEach((file) => { - if (file === baseThemeFile || file === baseThemeSplitFile || file === baseThemeVividFile) { - return; - } + const queue = [...themeFiles].filter( + (file) => + !['catppuccin_mocha.json', 'catppuccin_mocha_split.json', 'catppuccin_mocha_vivid.json'].includes(file), + ); - const themePath = path.join(themesDir, file); - let correspondingBaseTheme; + const processQueue = async () => { + while (queue.length > 0) { + const promises = []; + for (let i = 0; i < concurrencyLimit && queue.length > 0; i++) { + const file = queue.shift(); + const themePath = path.join(themesDir, file); + let correspondingBaseTheme; - if (file.endsWith('_split.json')) { - correspondingBaseTheme = baseThemeSplit; - } else if (file.endsWith('_vivid.json')) { - correspondingBaseTheme = baseThemeVivid; - } else { - correspondingBaseTheme = baseTheme; - } + if (file.endsWith('_split.json')) { + correspondingBaseTheme = baseThemeSplit; + } else if (file.endsWith('_vivid.json')) { + correspondingBaseTheme = baseThemeVivid; + } else { + correspondingBaseTheme = baseTheme; + } - try { - processTheme(themePath, correspondingBaseTheme, dryRun, specialKeyMappings); - } catch (error) { - console.error(formatMessage(COLORS.FG_RED, `āŒ Error processing '${file}': ${error.message}`)); + promises.push( + processTheme(themePath, correspondingBaseTheme, dryRun, specialKeyMappings).catch((error) => { + console.error(formatMessage(COLORS.FG_RED, `āŒ Error processing '${file}': ${error.message}`)); + }), + ); + } + await Promise.all(promises); } - }); + }; + + await processQueue(); console.log(formatMessage(COLORS.FG_GREEN, '\nšŸŽ‰ All themes have been processed.')); }; diff --git a/src/components/menus/bluetooth/devices/index.tsx b/src/components/menus/bluetooth/devices/index.tsx index 52b68d8..fbfb1e5 100644 --- a/src/components/menus/bluetooth/devices/index.tsx +++ b/src/components/menus/bluetooth/devices/index.tsx @@ -33,9 +33,11 @@ export const BluetoothDevices = (): JSX.Element => { deviceListBinding.drop(); }} > - - {deviceListBinding()} - + + + {deviceListBinding()} + + ); }; diff --git a/src/components/menus/network/wifi/WirelessAPs/index.tsx b/src/components/menus/network/wifi/WirelessAPs/index.tsx index b36cd3c..8b2751a 100644 --- a/src/components/menus/network/wifi/WirelessAPs/index.tsx +++ b/src/components/menus/network/wifi/WirelessAPs/index.tsx @@ -15,33 +15,35 @@ export const WirelessAPs = (): JSX.Element => { if (filteredWAPs.length <= 0 && staging.get() === undefined) { return (