From 6efcd60467ae1b8051f837f46288a8ac52ce734b Mon Sep 17 00:00:00 2001 From: Ed Bennett Date: Fri, 3 Jan 2025 06:45:37 +0000 Subject: [PATCH] Cava Module (#662) * First version of the cava module * Update cava stuff * Update themes for cava * Update themes * Handle cava visibility when null * Add bar characters in options --------- Co-authored-by: Ed Bennett Co-authored-by: Jas Singh --- scripts/fillThemes.js | 8 +- src/components/bar/exports.ts | 2 + src/components/bar/index.tsx | 2 + src/components/bar/modules/cava/helpers.ts | 64 ++ src/components/bar/modules/cava/index.tsx | 77 +++ src/components/bar/settings/config.tsx | 37 ++ src/components/bar/settings/theme.tsx | 13 + src/components/bar/shared/Module.tsx | 11 +- src/components/bar/utils/helpers.ts | 22 +- src/lib/constants/services.ts | 3 + src/lib/types/bar.d.ts | 1 + src/lib/types/options.d.ts | 3 +- src/options.ts | 30 + src/scss/style/bar/style.scss | 27 + themes/catppuccin_frappe.json | 9 +- themes/catppuccin_frappe_split.json | 9 +- themes/catppuccin_frappe_vivid.json | 9 +- themes/catppuccin_latte.json | 9 +- themes/catppuccin_latte_split.json | 9 +- themes/catppuccin_latte_vivid.json | 9 +- themes/catppuccin_macchiato.json | 9 +- themes/catppuccin_macchiato_split.json | 9 +- themes/catppuccin_macchiato_vivid.json | 9 +- themes/catppuccin_mocha.json | 7 +- themes/catppuccin_mocha_split.json | 7 +- themes/catppuccin_mocha_vivid.json | 721 ++++++++++---------- themes/cyberpunk.json | 9 +- themes/cyberpunk_split.json | 9 +- themes/cyberpunk_vivid.json | 9 +- themes/dracula.json | 9 +- themes/dracula_split.json | 9 +- themes/dracula_vivid.json | 9 +- themes/everforest.json | 9 +- themes/everforest_split.json | 9 +- themes/everforest_vivid.json | 9 +- themes/gruvbox.json | 9 +- themes/gruvbox_split.json | 9 +- themes/gruvbox_vivid.json | 9 +- themes/monochrome.json | 9 +- themes/monochrome_split.json | 9 +- themes/monochrome_vivid.json | 9 +- themes/nord.json | 9 +- themes/nord_split.json | 9 +- themes/nord_vivid.json | 9 +- themes/one_dark.json | 9 +- themes/one_dark_split.json | 9 +- themes/one_dark_vivid.json | 9 +- themes/rose_pine.json | 723 +++++++++++---------- themes/rose_pine_moon.json | 723 +++++++++++---------- themes/rose_pine_moon_split.json | 723 +++++++++++---------- themes/rose_pine_moon_vivid.json | 723 +++++++++++---------- themes/rose_pine_split.json | 723 +++++++++++---------- themes/rose_pine_vivid.json | 723 +++++++++++---------- themes/tokyo_night.json | 9 +- themes/tokyo_night_split.json | 9 +- themes/tokyo_night_vivid.json | 9 +- 56 files changed, 3065 insertions(+), 2605 deletions(-) create mode 100644 src/components/bar/modules/cava/helpers.ts create mode 100644 src/components/bar/modules/cava/index.tsx diff --git a/scripts/fillThemes.js b/scripts/fillThemes.js index 2d8e3fe..f95876b 100644 --- a/scripts/fillThemes.js +++ b/scripts/fillThemes.js @@ -402,8 +402,11 @@ const main = async () => { const themeFiles = (await fs.readdir(themesDir)).filter((file) => file.endsWith('.json')); const specialKeyMappings = { - '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', + 'theme.bar.buttons.modules.cava.text': 'theme.bar.buttons.modules.submap.text', + 'theme.bar.buttons.modules.cava.background': 'theme.bar.buttons.modules.submap.background', + 'theme.bar.buttons.modules.cava.icon_background': 'theme.bar.buttons.modules.submap.icon_background', + 'theme.bar.buttons.modules.cava.icon': 'theme.bar.buttons.modules.submap.icon', + 'theme.bar.buttons.modules.cava.border': 'theme.bar.buttons.modules.submap.border', }; const queue = [...themeFiles].filter( @@ -412,6 +415,7 @@ const main = async () => { ); const processQueue = async () => { + const concurrencyLimit = 5; while (queue.length > 0) { const promises = []; for (let i = 0; i < concurrencyLimit && queue.length > 0; i++) { diff --git a/src/components/bar/exports.ts b/src/components/bar/exports.ts index 13da913..fc4f9b5 100644 --- a/src/components/bar/exports.ts +++ b/src/components/bar/exports.ts @@ -23,6 +23,7 @@ import { Weather } from '../../components/bar/modules/weather/index'; import { Power } from '../../components/bar/modules/power/index'; import { Hyprsunset } from '../../components/bar/modules/hyprsunset/index'; import { Hypridle } from '../../components/bar/modules/hypridle/index'; +import { Cava } from '../../components/bar/modules/cava/index'; export { Menu, @@ -50,4 +51,5 @@ export { Power, Hyprsunset, Hypridle, + Cava, }; diff --git a/src/components/bar/index.tsx b/src/components/bar/index.tsx index aed8c48..e770c04 100644 --- a/src/components/bar/index.tsx +++ b/src/components/bar/index.tsx @@ -24,6 +24,7 @@ import { Power, Hyprsunset, Hypridle, + Cava, } from './exports'; import { WidgetContainer } from './shared/WidgetContainer'; @@ -62,6 +63,7 @@ const widget = { power: (): JSX.Element => WidgetContainer(Power()), hyprsunset: (): JSX.Element => WidgetContainer(Hyprsunset()), hypridle: (): JSX.Element => WidgetContainer(Hypridle()), + cava: (): JSX.Element => WidgetContainer(Cava()), }; export const Bar = (() => { diff --git a/src/components/bar/modules/cava/helpers.ts b/src/components/bar/modules/cava/helpers.ts new file mode 100644 index 0000000..66c45db --- /dev/null +++ b/src/components/bar/modules/cava/helpers.ts @@ -0,0 +1,64 @@ +import { bind, Variable } from 'astal'; +import { cavaService, mprisService } from 'src/lib/constants/services'; +import options from 'src/options'; + +const { + showActiveOnly, + bars, + autoSensitivity, + lowCutoff, + highCutoff, + noiseReduction, + stereo, + channels, + framerate, + samplerate, +} = options.bar.customModules.cava; + +/** + * Initializes a visibility tracker that updates the visibility status based on the active state and the presence of players. + * + * @param isVis - A variable that holds the visibility status. + */ +export function initVisibilityTracker(isVis: Variable): void { + Variable.derive([bind(showActiveOnly), bind(mprisService, 'players')], (showActive, players) => { + isVis.set(cavaService !== null && (!showActive || players?.length > 0)); + }); +} + +/** + * Initializes a settings tracker that updates the CAVA service settings based on the provided options. + */ +export function initSettingsTracker(): void { + const cava = cavaService; + + if (!cava) { + return; + } + + Variable.derive( + [ + bind(bars), + bind(channels), + bind(framerate), + bind(samplerate), + bind(autoSensitivity), + bind(lowCutoff), + bind(highCutoff), + bind(noiseReduction), + bind(stereo), + ], + (bars, channels, framerate, samplerate, autoSens, lCutoff, hCutoff, noiseRed, isStereo) => { + cava.set_autosens(autoSens); + cava.set_low_cutoff(lCutoff); + cava.set_high_cutoff(hCutoff); + cava.set_noise_reduction(noiseRed); + cava.set_source('auto'); + cava.set_stereo(isStereo); + cava.set_bars(bars); + cava.set_channels(channels); + cava.set_framerate(framerate); + cava.set_samplerate(samplerate); + }, + ); +} diff --git a/src/components/bar/modules/cava/index.tsx b/src/components/bar/modules/cava/index.tsx new file mode 100644 index 0000000..063127a --- /dev/null +++ b/src/components/bar/modules/cava/index.tsx @@ -0,0 +1,77 @@ +import { Variable, bind } from 'astal'; +import { Astal } from 'astal/gtk3'; +import { cavaService } from 'src/lib/constants/services'; +import { BarBoxChild } from 'src/lib/types/bar'; +import { Module } from '../../shared/Module'; +import { inputHandler } from '../../utils/helpers'; +import options from 'src/options'; +import { initSettingsTracker, initVisibilityTracker } from './helpers'; + +const { + icon, + showIcon: label, + showActiveOnly, + barCharacters, + spaceCharacter, + leftClick, + rightClick, + middleClick, + scrollUp, + scrollDown, +} = options.bar.customModules.cava; + +const isVis = Variable(!showActiveOnly.get()); + +initVisibilityTracker(isVis); +initSettingsTracker(); + +export const Cava = (): BarBoxChild => { + let labelBinding: Variable = Variable(''); + + if (cavaService) { + labelBinding = Variable.derive( + [bind(cavaService, 'values'), bind(spaceCharacter), bind(barCharacters)], + (values, spacing, blockCharacters) => { + const valueMap = values + .map((v: number) => { + const index = Math.floor(v * blockCharacters.length); + return blockCharacters[Math.min(index, blockCharacters.length - 1)]; + }) + .join(spacing); + return valueMap; + }, + ); + } + + return Module({ + isVis, + label: labelBinding(), + showIconBinding: bind(label), + textIcon: bind(icon), + boxClass: 'cava', + props: { + setup: (self: Astal.Button) => { + inputHandler(self, { + onPrimaryClick: { + cmd: leftClick, + }, + onSecondaryClick: { + cmd: rightClick, + }, + onMiddleClick: { + cmd: middleClick, + }, + onScrollUp: { + cmd: scrollUp, + }, + onScrollDown: { + cmd: scrollDown, + }, + }); + }, + onDestroy: () => { + labelBinding.drop(); + }, + }, + }); +}; diff --git a/src/components/bar/settings/config.tsx b/src/components/bar/settings/config.tsx index 931e46b..925dfa0 100644 --- a/src/components/bar/settings/config.tsx +++ b/src/components/bar/settings/config.tsx @@ -354,6 +354,43 @@ export const CustomModuleSettings = (): JSX.Element => {