diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..980d7f4 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +types +node_modules diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..85b3eb0 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint', 'import'], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + root: true, + ignorePatterns: ['.eslintrc.js', 'types/**/*.ts'], + env: { + es6: true, + browser: true, + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/no-explicit-any': 'error', + 'import/extensions': ['off'], + 'import/no-unresolved': 'off', + quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }], + }, +}; diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index ff96a83..0000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,130 +0,0 @@ -env: - es2022: true -extends: - - "eslint:recommended" - - "plugin:@typescript-eslint/recommended" -parser: "@typescript-eslint/parser" -parserOptions: - ecmaVersion: 2022 - sourceType: "module" - project: "./tsconfig.json" - warnOnUnsupportedTypeScriptVersion: false -root: true -ignorePatterns: - - types/ -plugins: - - "@typescript-eslint" -rules: - "@typescript-eslint/ban-ts-comment": - - "off" - "@typescript-eslint/no-non-null-assertion": - - "off" - # "@typescript-eslint/no-explicit-any": - # - "off" - "@typescript-eslint/no-unused-vars": - - error - - varsIgnorePattern: (^unused|_$) - argsIgnorePattern: ^(unused|_) - "@typescript-eslint/no-empty-interface": - - "off" - - arrow-parens: - - error - - as-needed - comma-dangle: - - error - - always-multiline - comma-spacing: - - error - - before: false - after: true - comma-style: - - error - - last - curly: - - error - - multi-or-nest - - consistent - dot-location: - - error - - property - eol-last: - - error - eqeqeq: - - error - - always - indent: - - error - - 4 - - SwitchCase: 1 - keyword-spacing: - - error - - before: true - lines-between-class-members: - - error - - always - - exceptAfterSingleLine: true - padded-blocks: - - error - - never - - allowSingleLineBlocks: false - prefer-const: - - error - quotes: - - error - - double - - avoidEscape: true - semi: - - error - - never - nonblock-statement-body-position: - - error - - below - no-trailing-spaces: - - error - no-useless-escape: - - off - max-len: - - error - - code: 100 - func-call-spacing: - - error - array-bracket-spacing: - - error - space-before-function-paren: - - error - - anonymous: never - named: never - asyncArrow: ignore - space-before-blocks: - - error - key-spacing: - - error - object-curly-spacing: - - error - - always -globals: - Widget: readonly - Utils: readonly - App: readonly - Variable: readonly - Service: readonly - pkg: readonly - ARGV: readonly - Debugger: readonly - GIRepositoryGType: readonly - globalThis: readonly - imports: readonly - Intl: readonly - log: readonly - logError: readonly - print: readonly - printerr: readonly - window: readonly - TextEncoder: readonly - TextDecoder: readonly - console: readonly - setTimeout: readonly - setInterval: readonly - clearTimeout: readonly - clearInterval: readonly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..27ec51a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI + +on: + pull_request: + branches: + - master + +jobs: + code_quality: + runs-on: ubuntu-latest + + steps: + - name: Checkout main repository + uses: actions/checkout@v3 + + - name: Clone ags-types to temp dir + uses: actions/checkout@v3 + with: + repository: Jas-SinghFSU/ags-types + path: temp-ags-types + + - name: Copy types to types/ + run: | + rm -rf types + mkdir -p types + cp -R temp-ags-types/types/* types/ + rm -rf temp-ags-types + + - name: Node Setup + uses: actions/setup-node@v3 + with: + node-version: '21' + + - name: Install Dependencies + run: npm install + + - name: ESLint + run: npm run lint + + - name: Type Check + run: npx tsc --noEmit --pretty --extendedDiagnostics diff --git a/.gitignore b/.gitignore index 2458c1e..9144fba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .weather.json +node_modules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b165aa5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/ags-types"] + path = external/ags-types + url = https://github.com/Jas-SinghFSU/ags-types.git diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..97843e6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +.eslintrc.js +types/**/*.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6e88f12 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "semi": true, + "trailingComma": "all", + "printWidth": 120, + "tabWidth": 4, + "useTabs": false +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..75c0349 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Jas Singh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 243ed20..b855cc5 100644 --- a/README.md +++ b/README.md @@ -11,21 +11,26 @@
# HyprPanel 🚀 + A panel built for Hyprland with [AGS](https://github.com/Aylur/ags) ![HyprPanel](./assets/HyprPanel.png) ## Installation + The [HyprPanel Wiki](https://hyprpanel.com/getting_started/installation.html) contains in depth instructions for installing the panel and all of its dependencies. The instructions below are general instructions for installing the panel. ## Requirements + Bun ```sh curl -fsSL https://bun.sh/install | bash && \ sudo ln -s $HOME/.bun/bin/bun /usr/local/bin/bun ``` + Additional dependencies: + ```sh pipewire libgtop @@ -45,53 +50,110 @@ gnome-bluetooth-3.0 ``` Optional Dependencies: + ```sh ## Used for Tracking GPU Usage in your Dashboard (NVidia only) python python-gpustat + +## Only if a pywal hook from wallpaper changes applied through settings is desired +pywal + +## To check for pacman updates in the default script used in the updates module +pacman-contrib + +## To switch between power profiles in battery module +power-profiles-daemon ``` -Arch (pacman): +### Arch + +pacman: ```bash -sudo pacman -S pipewire libgtop bluez bluez-utils btop networkmanager dart-sass wl-clipboard brightnessctl swww python gnome-bluetooth-3.0 +sudo pacman -S pipewire libgtop bluez bluez-utils btop networkmanager dart-sass wl-clipboard brightnessctl swww python gnome-bluetooth-3.0 pacman-contrib power-profiles-daemon ``` -Arch (AUR): +AUR: + ```bash yay -S grimblast-git gpu-screen-recorder hyprpicker matugen-bin python-gpustat aylurs-gtk-shell-git ``` +### Fedora + +COPR - Add [solopasha/hyprland](https://copr.fedorainfracloud.org/coprs/solopasha/hyprland/) for most hyprland-related dependencies, and [hues-sueh/packages](https://copr.fedorainfracloud.org/coprs/heus-sueh/packages/) for matugen. Both provide the `swww` package, so prioritise the former repo: +```bash +sudo dnf copr enable solopasha/hyprland +sudo dnf copr enable heus-sueh/packages +sudo dnf config-manager --save --setopt=copr:copr.fedorainfracloud.org:heus-sueh:packages.priority=200 +``` + +DNF: +```bash +sudo dnf install pipewire libgtop2 bluez bluez-tools grimblast hyprpicker btop NetworkManager wl-clipboard swww brightnessctl gnome-bluetooth aylurs-gtk-shell power-profiles-daemon gvfs +``` + +bun: +```bash +bun install -g sass +``` + +flatpak: +```bash +flatpak install flathub --system com.dec05eba.gpu_screen_recorder +``` + +#### Optional Dependencies + +pip: +```bash +sudo dnf install python python3-pip; pip install gpustat pywal +``` + +### NixOS + For NixOS/Home-Manager, see [NixOS & Home-Manager instructions](#nixos--home-manager). ## Instructions ### AGS + Once everything is installed you need to put the contents of this repo in `~/.config/ags`. If you already have something in `~/.config/ags`, it's recommended that you back it up with: + ```bash mv $HOME/.config/ags $HOME/.config/ags.bkup ``` + Otherwise you can use this command to install the panel: + ```bash git clone https://github.com/Jas-SinghFSU/HyprPanel.git && \ ln -s $(pwd)/HyprPanel $HOME/.config/ags ``` + ### Nerd Fonts + Additionally, you need to ensure that you have a [Nerd Font](https://www.nerdfonts.com/font-downloads) installed for your icons to render properly. ### Launch the panel + Afterwards you can run the panel with the following command in your terminal: + ```bash ags ``` Or you can add it to your Hyprland config (hyprland.conf) to auto-start with: + ```bash exec-once = ags ``` ### NixOS & Home-Manager + Alternatively, if you're using NixOS and/or Home-Manager, you can setup AGS using the provided Nix Flake. First, add the repository to your Flake's inputs, and enable the overlay. + ```nix # flake.nix @@ -99,7 +161,7 @@ Alternatively, if you're using NixOS and/or Home-Manager, you can setup AGS usin inputs.hyprpanel.url = "github:Jas-SinghFSU/HyprPanel"; # ... - outputs = { self, nixpkgs, ... }@inputs: + outputs = { self, nixpkgs, ... }@inputs: let # ... system = "x86_64-linux"; # change to whatever your system should be. @@ -169,6 +231,7 @@ The panel is automatically scaled based on your font size in `Configuration > Ge ### Specifying bar layouts per monitor To specify layouts for each monitor you can create a JSON object such as: + ```JSON { "0": { @@ -223,19 +286,21 @@ To specify layouts for each monitor you can create a JSON object such as: ``` Where each monitor is defined by its index (0, 1, 2 in this case) and each section (left, middle, right) contains one or more of the following modules: + ```js -"battery" -"dashboard" -"workspaces" -"windowtitle" -"media" -"notifications" -"volume" -"network" -"bluetooth" -"clock" -"systray" +'battery'; +'dashboard'; +'workspaces'; +'windowtitle'; +'media'; +'notifications'; +'volume'; +'network'; +'bluetooth'; +'clock'; +'systray'; ``` + Since the text-box in the options dialog isn't sufficient, it is recommended that you create this JSON configuration in a text editor elsewhere and paste it into the layout text-box under Configuration > Bar > "Bar Layouts for Monitors". ### Additional Configuration diff --git a/config.js b/config.js index 6417940..01d6ae8 100644 --- a/config.js +++ b/config.js @@ -1,46 +1,58 @@ -import GLib from "gi://GLib" +import GLib from 'gi://GLib'; -const main = "/tmp/ags/hyprpanel/main.js" -const entry = `${App.configDir}/main.ts` -const bundler = GLib.getenv("AGS_BUNDLER") || "bun" +const main = '/tmp/ags/hyprpanel/main.js'; +const entry = `${App.configDir}/main.ts`; +const bundler = GLib.getenv('AGS_BUNDLER') || 'bun'; const v = { - ags: pkg.version?.split(".").map(Number) || [], + ags: pkg.version?.split('.').map(Number) || [], expect: [1, 8, 1], -} +}; try { switch (bundler) { - case "bun": await Utils.execAsync([ - "bun", "build", entry, - "--outfile", main, - "--external", "resource://*", - "--external", "gi://*", - "--external", "file://*", - ]); break + case 'bun': + await Utils.execAsync([ + 'bun', + 'build', + entry, + '--outfile', + main, + '--external', + 'resource://*', + '--external', + 'gi://*', + '--external', + 'file://*', + ]); + break; - case "esbuild": await Utils.execAsync([ - "esbuild", "--bundle", entry, - "--format=esm", - `--outfile=${main}`, - "--external:resource://*", - "--external:gi://*", - "--external:file://*", - ]); break + case 'esbuild': + await Utils.execAsync([ + 'esbuild', + '--bundle', + entry, + '--format=esm', + `--outfile=${main}`, + '--external:resource://*', + '--external:gi://*', + '--external:file://*', + ]); + break; default: - throw `"${bundler}" is not a valid bundler` + throw `"${bundler}" is not a valid bundler`; } if (v.ags[1] < v.expect[1] || v.ags[2] < v.expect[2]) { - print(`HyprPanel needs atleast v${v.expect.join(".")} of AGS, yours is v${v.ags.join(".")}`) - App.quit() + print(`HyprPanel needs atleast v${v.expect.join('.')} of AGS, yours is v${v.ags.join('.')}`); + App.quit(); } - await import(`file://${main}`) + await import(`file://${main}`); } catch (error) { - console.error(error) - App.quit() + console.error(error); + App.quit(); } -export { } +export {}; diff --git a/customModules/PollVar.ts b/customModules/PollVar.ts index 620358e..2381e89 100644 --- a/customModules/PollVar.ts +++ b/customModules/PollVar.ts @@ -1,26 +1,25 @@ -import GLib from "gi://GLib?version=2.0"; -import { Binding } from "types/service"; -import { Variable as VariableType } from "types/variable"; - -type GenericFunction = (...args: any[]) => any; +import GLib from 'gi://GLib?version=2.0'; +import { GenericFunction } from 'lib/types/customModules/generic'; +import { Bind } from 'lib/types/variable'; +import { Variable as VariableType } from 'types/variable'; /** * @param {VariableType} targetVariable - The Variable to update with the function's result. - * @param {Array>} trackers - Array of trackers to watch. - * @param {Binding} pollingInterval - The polling interval in milliseconds. - * @param {GenericFunction} someFunc - The function to execute at each interval, which updates the Variable. - * @param {...any} params - Parameters to pass to someFunc. + * @param {Array} trackers - Array of trackers to watch. + * @param {Bind} pollingInterval - The polling interval in milliseconds. + * @param {GenericFunction} someFunc - The function to execute at each interval, which updates the Variable. + * @param {...P} params - Parameters to pass to someFunc. */ -export const pollVariable = ( +export const pollVariable = >( targetVariable: VariableType, - trackers: Array>, - pollingInterval: Binding, - someFunc: GenericFunction, - ...params: any[] + trackers: Array, + pollingInterval: Bind, + someFunc: F, + ...params: P ): void => { let intervalInstance: number | null = null; - const intervalFn = (pollIntrvl: number) => { + const intervalFn = (pollIntrvl: number): void => { if (intervalInstance !== null) { GLib.source_remove(intervalInstance); } @@ -37,39 +36,41 @@ export const pollVariable = ( /** * @param {VariableType} targetVariable - The Variable to update with the result of the command. - * @param {Binding} pollingInterval - The polling interval in milliseconds. + * @param {Array} trackers - Array of trackers to watch. + * @param {Bind} pollingInterval - The polling interval in milliseconds. * @param {string} someCommand - The bash command to execute. - * @param {GenericFunction} someFunc - The function to execute after processing the command result. - * @param {...any} params - Parameters to pass to someFunc. + * @param {GenericFunction} someFunc - The function to execute after processing the command result; + * with the first argument being the result of the command execution. + * @param {...P} params - Additional parameters to pass to someFunc. */ -export const pollVariableBash = ( +export const pollVariableBash = >( targetVariable: VariableType, - trackers: Array>, - pollingInterval: Binding, + trackers: Array, + pollingInterval: Bind, someCommand: string, - someFunc: (res: any, ...params: any[]) => T, - ...params: any[] + someFunc: F, + ...params: P ): void => { let intervalInstance: number | null = null; - const intervalFn = (pollIntrvl: number) => { + const intervalFn = (pollIntrvl: number): void => { if (intervalInstance !== null) { GLib.source_remove(intervalInstance); } intervalInstance = Utils.interval(pollIntrvl, () => { - Utils.execAsync(`bash -c "${someCommand}"`).then((res: any) => { - try { - targetVariable.value = someFunc(res, ...params); - } catch (error) { - console.warn(`An error occurred when running interval bash function: ${error}`); - } - }) + Utils.execAsync(`bash -c "${someCommand}"`) + .then((res: string) => { + try { + targetVariable.value = someFunc(res, ...params); + } catch (error) { + console.warn(`An error occurred when running interval bash function: ${error}`); + } + }) .catch((err) => console.error(`Error running command "${someCommand}": ${err}`)); }); }; - // Set up the interval initially with the provided polling interval Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => { intervalFn(pollIntrvl); }); diff --git a/customModules/config.ts b/customModules/config.ts index 3207500..5e72214 100644 --- a/customModules/config.ts +++ b/customModules/config.ts @@ -2,8 +2,10 @@ import { Option } from 'widget/settings/shared/Option'; import { Header } from 'widget/settings/shared/Header'; import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, GtkWidget } from 'lib/types/widget'; -export const CustomModuleSettings = () => +export const CustomModuleSettings = (): Scrollable => Widget.Scrollable({ vscroll: 'automatic', hscroll: 'automatic', @@ -12,11 +14,11 @@ export const CustomModuleSettings = () => class_name: 'menu-theme-page paged-container', vertical: true, children: [ - /* - ************************************ - * GENERAL * - ************************************ - */ + /* + ************************************ + * GENERAL * + ************************************ + */ Header('General'), Option({ opt: options.bar.customModules.scrollSpeed, @@ -24,11 +26,11 @@ export const CustomModuleSettings = () => type: 'number', }), - /* - ************************************ - * RAM * - ************************************ - */ + /* + ************************************ + * RAM * + ************************************ + */ Header('RAM'), Option({ opt: options.bar.customModules.ram.label, @@ -75,11 +77,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * CPU * - ************************************ - */ + /* + ************************************ + * CPU * + ************************************ + */ Header('CPU'), Option({ opt: options.bar.customModules.cpu.label, @@ -130,11 +132,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * STORAGE * - ************************************ - */ + /* + ************************************ + * STORAGE * + ************************************ + */ Header('Storage'), Option({ opt: options.bar.customModules.storage.icon, @@ -187,11 +189,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * NETSTAT * - ************************************ - */ + /* + ************************************ + * NETSTAT * + ************************************ + */ Header('Netstat'), Option({ opt: options.bar.customModules.netstat.networkInterface, @@ -204,17 +206,7 @@ export const CustomModuleSettings = () => opt: options.bar.customModules.netstat.icon, title: 'Netstat Icon', type: 'enum', - enums: [ - '󰖟', - '󰇚', - '󰕒', - '󰛳', - '', - '󰣺', - '󰖩', - '', - '󰈀', - ], + enums: ['󰖟', '󰇚', '󰕒', '󰛳', '', '󰣺', '󰖩', '', '󰈀'], }), Option({ opt: options.bar.customModules.netstat.label, @@ -267,11 +259,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * KEYBOARD LAYOUT * - ************************************ - */ + /* + ************************************ + * KEYBOARD LAYOUT * + ************************************ + */ Header('Keyboard Layout'), Option({ opt: options.bar.customModules.kbLayout.icon, @@ -321,11 +313,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * UPDATES * - ************************************ - */ + /* + ************************************ + * UPDATES * + ************************************ + */ Header('Updates'), Option({ opt: options.bar.customModules.updates.updateCommand, @@ -336,17 +328,7 @@ export const CustomModuleSettings = () => opt: options.bar.customModules.updates.icon, title: 'Updates Icon', type: 'enum', - enums: [ - '󰚰', - '󰇚', - '', - '󱑢', - '󱑣', - '󰏖', - '', - '󰏔', - '󰏗', - ], + enums: ['󰚰', '󰇚', '', '󱑢', '󱑣', '󰏖', '', '󰏔', '󰏗'], }), Option({ opt: options.bar.customModules.updates.label, @@ -367,7 +349,7 @@ export const CustomModuleSettings = () => opt: options.bar.customModules.updates.pollingInterval, title: 'Polling Interval', type: 'number', - subtitle: "WARNING: Be careful of your package manager\'s rate limit.", + subtitle: "WARNING: Be careful of your package manager's rate limit.", min: 100, max: 60 * 24 * 1000, increment: 1000, @@ -398,11 +380,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * WEATHER * - ************************************ - */ + /* + ************************************ + * WEATHER * + ************************************ + */ Header('Weather'), Option({ opt: options.bar.customModules.weather.label, @@ -446,11 +428,11 @@ export const CustomModuleSettings = () => type: 'string', }), - /* - ************************************ - * POWER * - ************************************ - */ + /* + ************************************ + * POWER * + ************************************ + */ Header('Power'), Option({ opt: options.theme.bar.buttons.modules.power.spacing, @@ -491,4 +473,3 @@ export const CustomModuleSettings = () => ], }), }); - diff --git a/customModules/cpu/computeCPU.ts b/customModules/cpu/computeCPU.ts index ef3542d..460b1db 100644 --- a/customModules/cpu/computeCPU.ts +++ b/customModules/cpu/computeCPU.ts @@ -1,12 +1,10 @@ -// @ts-expect-error +// @ts-expect-error: This import is a special directive that tells the compiler to use the GTop library import GTop from 'gi://GTop'; -const defaultCpuData: number = 0; - let previousCpuData = new GTop.glibtop_cpu(); GTop.glibtop_get_cpu(previousCpuData); -export const computeCPU = () => { +export const computeCPU = (): number => { const currentCpuData = new GTop.glibtop_cpu(); GTop.glibtop_get_cpu(currentCpuData); @@ -19,5 +17,4 @@ export const computeCPU = () => { previousCpuData = currentCpuData; return cpuUsagePercentage; -} - +}; diff --git a/customModules/cpu/index.ts b/customModules/cpu/index.ts index d10b15e..1eb8ea7 100644 --- a/customModules/cpu/index.ts +++ b/customModules/cpu/index.ts @@ -1,31 +1,21 @@ -import options from "options"; - -// @ts-expect-error -import GTop from 'gi://GTop'; +import options from 'options'; // Module initializer -import { module } from "../module" +import { module } from '../module'; // import { CpuData } from "lib/types/customModules/cpu"; -import Button from "types/widgets/button"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; +import Button from 'types/widgets/button'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; // Utility Methods -import { inputHandler } from "customModules/utils"; -import { computeCPU } from "./computeCPU"; -import { pollVariable } from "customModules/PollVar"; +import { inputHandler } from 'customModules/utils'; +import { computeCPU } from './computeCPU'; +import { pollVariable } from 'customModules/PollVar'; +import { Module } from 'lib/types/bar'; // All the user configurable options for the cpu module that are needed -const { - label, - round, - leftClick, - rightClick, - middleClick, - scrollUp, - scrollDown, - pollingInterval -} = options.bar.customModules.cpu; +const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown, pollingInterval } = + options.bar.customModules.cpu; export const cpuUsage = Variable(0); @@ -40,21 +30,19 @@ pollVariable( computeCPU, ); -export const Cpu = () => { - const renderLabel = (cpuUsg: number, rnd: boolean) => { +export const Cpu = (): Module => { + const renderLabel = (cpuUsg: number, rnd: boolean): string => { return rnd ? `${Math.round(cpuUsg)}%` : `${cpuUsg.toFixed(2)}%`; - } + }; const cpuModule = module({ - textIcon: "", - label: Utils.merge( - [cpuUsage.bind("value"), round.bind("value")], - (cpuUsg, rnd) => { - return renderLabel(cpuUsg, rnd); - }), - tooltipText: "CPU", - boxClass: "cpu", - showLabelBinding: label.bind("value"), + textIcon: '', + label: Utils.merge([cpuUsage.bind('value'), round.bind('value')], (cpuUsg, rnd) => { + return renderLabel(cpuUsg, rnd); + }), + tooltipText: 'CPU', + boxClass: 'cpu', + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -75,9 +63,8 @@ export const Cpu = () => { }, }); }, - } + }, }); return cpuModule; -} - +}; diff --git a/customModules/kblayout/getLayout.ts b/customModules/kblayout/getLayout.ts index 97a27ec..9c42071 100644 --- a/customModules/kblayout/getLayout.ts +++ b/customModules/kblayout/getLayout.ts @@ -1,12 +1,18 @@ -import { HyprctlDeviceLayout, HyprctlKeyboard, KbLabelType } from "lib/types/customModules/kbLayout"; -import { layoutMap } from "./layouts"; +import { + HyprctlDeviceLayout, + HyprctlKeyboard, + KbLabelType, + LayoutKeys, + LayoutValues, +} from 'lib/types/customModules/kbLayout'; +import { layoutMap } from './layouts'; -export const getKeyboardLayout = (obj: string, format: KbLabelType) => { - let hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj); - let keyboards = hyprctlDevices['keyboards']; +export const getKeyboardLayout = (obj: string, format: KbLabelType): LayoutKeys | LayoutValues => { + const hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj); + const keyboards = hyprctlDevices['keyboards']; if (keyboards.length === 0) { - return "No KB!" + return format === 'code' ? 'Unknown' : 'Unknown Layout'; } let mainKb = keyboards.find((kb: HyprctlKeyboard) => kb.main); @@ -15,7 +21,8 @@ export const getKeyboardLayout = (obj: string, format: KbLabelType) => { mainKb = keyboards[keyboards.length - 1]; } - let layout = mainKb['active_keymap']; + const layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys; + const foundLayout: LayoutValues = layoutMap[layout]; - return format === "code" ? layoutMap[layout] || layout : layout; -} + return format === 'code' ? foundLayout || layout : layout; +}; diff --git a/customModules/kblayout/index.ts b/customModules/kblayout/index.ts index b984141..479bc7e 100644 --- a/customModules/kblayout/index.ts +++ b/customModules/kblayout/index.ts @@ -1,49 +1,50 @@ -const hyprland = await Service.import("hyprland"); +const hyprland = await Service.import('hyprland'); -import options from "options"; -import { module } from "../module" +import options from 'options'; +import { module } from '../module'; -import { inputHandler } from "customModules/utils"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import Button from "types/widgets/button"; -import Label from "types/widgets/label"; -import { getKeyboardLayout } from "./getLayout"; +import { inputHandler } from 'customModules/utils'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Button from 'types/widgets/button'; +import Label from 'types/widgets/label'; +import { getKeyboardLayout } from './getLayout'; +import { Module } from 'lib/types/bar'; -const { - label, - labelType, - icon, - leftClick, - rightClick, - middleClick, - scrollUp, - scrollDown, -} = options.bar.customModules.kbLayout; +const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } = + options.bar.customModules.kbLayout; -export const KbInput = () => { +export const KbInput = (): Module => { const keyboardModule = module({ - textIcon: icon.bind("value"), - tooltipText: "", + textIcon: icon.bind('value'), + tooltipText: '', labelHook: (self: Label): void => { - self.hook(hyprland, () => { - Utils.execAsync('hyprctl devices -j') - .then((obj) => { - self.label = getKeyboardLayout(obj, labelType.value); - }) - .catch((err) => { console.error(err); }); - }, "keyboard-layout"); + self.hook( + hyprland, + () => { + Utils.execAsync('hyprctl devices -j') + .then((obj) => { + self.label = getKeyboardLayout(obj, labelType.value); + }) + .catch((err) => { + console.error(err); + }); + }, + 'keyboard-layout', + ); self.hook(labelType, () => { Utils.execAsync('hyprctl devices -j') .then((obj) => { self.label = getKeyboardLayout(obj, labelType.value); }) - .catch((err) => { console.error(err); }); + .catch((err) => { + console.error(err); + }); }); }, - boxClass: "kblayout", - showLabelBinding: label.bind("value"), + boxClass: 'kblayout', + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -68,6 +69,4 @@ export const KbInput = () => { }); return keyboardModule; -} - - +}; diff --git a/customModules/kblayout/layouts.ts b/customModules/kblayout/layouts.ts index ff4b0e1..ccd37c6 100644 --- a/customModules/kblayout/layouts.ts +++ b/customModules/kblayout/layouts.ts @@ -1,584 +1,585 @@ export const layoutMap = { - "Abkhazian (Russia)": "RU (Ab)", - "Akan": "GH (Akan)", - "Albanian": "AL", - "Albanian (Plisi)": "AL (Plisi)", - "Albanian (Veqilharxhi)": "AL (Veqilharxhi)", - "Amharic": "ET", - "Arabic": "ARA", - "Arabic (Algeria)": "DZ (Ar)", - "Arabic (AZERTY, Eastern Arabic numerals)": "ARA (Azerty Digits)", - "Arabic (AZERTY)": "ARA (Azerty)", - "Arabic (Buckwalter)": "ARA (Buckwalter)", - "Arabic (Eastern Arabic numerals)": "ARA (Digits)", - "Arabic (Macintosh)": "ARA (Mac)", - "Arabic (Morocco)": "MA", - "Arabic (OLPC)": "ARA (Olpc)", - "Arabic (Pakistan)": "PK (Ara)", - "Arabic (QWERTY, Eastern Arabic numerals)": "ARA (Qwerty Digits)", - "Arabic (QWERTY)": "ARA (Qwerty)", - "Arabic (Syria)": "SY", - "Armenian": "AM", - "Armenian (alt. eastern)": "AM (Eastern-Alt)", - "Armenian (alt. phonetic)": "AM (Phonetic-Alt)", - "Armenian (eastern)": "AM (Eastern)", - "Armenian (phonetic)": "AM (Phonetic)", - "Armenian (western)": "AM (Western)", - "Asturian (Spain, with bottom-dot H and L)": "ES (Ast)", - "Avatime": "GH (Avn)", - "Azerbaijani": "AZ", - "Azerbaijani (Cyrillic)": "AZ (Cyrillic)", - "Azerbaijani (Iran)": "IR (Azb)", - "Bambara": "ML", - "Bangla": "BD", - "Bangla (India, Baishakhi InScript)": "IN (Ben Inscript)", - "Bangla (India, Baishakhi)": "IN (Ben Baishakhi)", - "Bangla (India, Bornona)": "IN (Ben Bornona)", - "Bangla (India, Gitanjali)": "IN (Ben Gitanjali)", - "Bangla (India, Probhat)": "IN (Ben Probhat)", - "Bangla (India)": "IN (Ben)", - "Bangla (Probhat)": "BD (Probhat)", - "Bashkirian": "RU (Bak)", - "Belarusian": "BY", - "Belarusian (intl.)": "BY (Intl)", - "Belarusian (Latin)": "BY (Latin)", - "Belarusian (legacy)": "BY (Legacy)", - "Belarusian (phonetic)": "BY (Phonetic)", - "Belgian": "BE", - "Belgian (alt.)": "BE (Oss)", - "Belgian (ISO, alt.)": "BE (Iso-Alternate)", - "Belgian (Latin-9 only, alt.)": "BE (Oss Latin9)", - "Belgian (no dead keys)": "BE (Nodeadkeys)", - "Belgian (Wang 724 AZERTY)": "BE (Wang)", - "Berber (Algeria, Latin)": "DZ", - "Berber (Algeria, Tifinagh)": "DZ (Ber)", - "Berber (Morocco, Tifinagh alt.)": "MA (Tifinagh-Alt)", - "Berber (Morocco, Tifinagh extended phonetic)": "MA (Tifinagh-Extended-Phonetic)", - "Berber (Morocco, Tifinagh extended)": "MA (Tifinagh-Extended)", - "Berber (Morocco, Tifinagh phonetic, alt.)": "MA (Tifinagh-Alt-Phonetic)", - "Berber (Morocco, Tifinagh phonetic)": "MA (Tifinagh-Phonetic)", - "Berber (Morocco, Tifinagh)": "MA (Tifinagh)", - "Bosnian": "BA", - "Bosnian (US, with Bosnian digraphs)": "BA (Unicodeus)", - "Bosnian (US)": "BA (Us)", - "Bosnian (with Bosnian digraphs)": "BA (Unicode)", - "Bosnian (with guillemets)": "BA (Alternatequotes)", - "Braille": "BRAI", - "Braille (left-handed inverted thumb)": "BRAI (Left Hand Invert)", - "Braille (left-handed)": "BRAI (Left Hand)", - "Braille (right-handed inverted thumb)": "BRAI (Right Hand Invert)", - "Braille (right-handed)": "BRAI (Right Hand)", - "Breton (France)": "FR (Bre)", - "Bulgarian": "BG", - "Bulgarian (enhanced)": "BG (Bekl)", - "Bulgarian (new phonetic)": "BG (Bas Phonetic)", - "Bulgarian (traditional phonetic)": "BG (Phonetic)", - "Burmese": "MM", - "Burmese Zawgyi": "MM (Zawgyi)", - "Cameroon (AZERTY, intl.)": "CM (Azerty)", - "Cameroon (Dvorak, intl.)": "CM (Dvorak)", - "Cameroon Multilingual (QWERTY, intl.)": "CM (Qwerty)", - "Canadian (CSA)": "CA (Multix)", - "Catalan (Spain, with middle-dot L)": "ES (Cat)", - "Cherokee": "US (Chr)", - "Chinese": "CN", - "Chuvash": "RU (Cv)", - "Chuvash (Latin)": "RU (Cv Latin)", - "CloGaelach": "IE (CloGaelach)", - "Crimean Tatar (Turkish Alt-Q)": "UA (Crh Alt)", - "Crimean Tatar (Turkish F)": "UA (Crh F)", - "Crimean Tatar (Turkish Q)": "UA (Crh)", - "Croatian": "HR", - "Croatian (US, with Croatian digraphs)": "HR (Unicodeus)", - "Croatian (US)": "HR (Us)", - "Croatian (with Croatian digraphs)": "HR (Unicode)", - "Croatian (with guillemets)": "HR (Alternatequotes)", - "Czech": "CZ", - "Czech (QWERTY, extended backslash)": "CZ (Qwerty Bksl)", - "Czech (QWERTY, Macintosh)": "CZ (Qwerty-Mac)", - "Czech (QWERTY)": "CZ (Qwerty)", - "Czech (UCW, only accented letters)": "CZ (Ucw)", - "Czech (US, Dvorak, UCW support)": "CZ (Dvorak-Ucw)", - "Czech (with <\\|> key)": "CZ (Bksl)", - "Danish": "DK", - "Danish (Dvorak)": "DK (Dvorak)", - "Danish (Macintosh, no dead keys)": "DK (Mac Nodeadkeys)", - "Danish (Macintosh)": "DK (Mac)", - "Danish (no dead keys)": "DK (Nodeadkeys)", - "Danish (Windows)": "DK (Winkeys)", - "Dari": "AF", - "Dari (Afghanistan, OLPC)": "AF (Fa-Olpc)", - "Dhivehi": "MV", - "Dutch": "NL", - "Dutch (Macintosh)": "NL (Mac)", - "Dutch (standard)": "NL (Std)", - "Dutch (US)": "NL (Us)", - "Dzongkha": "BT", - "English (Australian)": "AU", - "English (Cameroon)": "CM", - "English (Canada)": "CA (Eng)", - "English (classic Dvorak)": "US (Dvorak-Classic)", - "English (Colemak-DH ISO)": "US (Colemak Dh Iso)", - "English (Colemak-DH)": "US (Colemak Dh)", - "English (Colemak)": "US (Colemak)", - "English (Dvorak, alt. intl.)": "US (Dvorak-Alt-Intl)", - "English (Dvorak, intl., with dead keys)": "US (Dvorak-Intl)", - "English (Dvorak, left-handed)": "US (Dvorak-L)", - "English (Dvorak, Macintosh)": "US (Dvorak-Mac)", - "English (Dvorak, right-handed)": "US (Dvorak-R)", - "English (Dvorak)": "US (Dvorak)", - "English (Ghana, GILLBT)": "GH (Gillbt)", - "English (Ghana, multilingual)": "GH (Generic)", - "English (Ghana)": "GH", - "English (India, with rupee)": "IN (Eng)", - "English (intl., with AltGr dead keys)": "US (Altgr-Intl)", - "English (Macintosh)": "US (Mac)", - "English (Mali, US, intl.)": "ML (Us-Intl)", - "English (Mali, US, Macintosh)": "ML (Us-Mac)", - "English (Nigeria)": "NG", - "English (Norman)": "US (Norman)", - "English (programmer Dvorak)": "US (Dvp)", - "English (South Africa)": "ZA", - "English (the divide/multiply toggle the layout)": "US (Olpc2)", - "English (UK, Colemak-DH)": "GB (Colemak Dh)", - "English (UK, Colemak)": "GB (Colemak)", - "English (UK, Dvorak, with UK punctuation)": "GB (Dvorakukp)", - "English (UK, Dvorak)": "GB (Dvorak)", - "English (UK, extended, Windows)": "GB (Extd)", - "English (UK, intl., with dead keys)": "GB (Intl)", - "English (UK, Macintosh, intl.)": "GB (Mac Intl)", - "English (UK, Macintosh)": "GB (Mac)", - "English (UK)": "GB", - "English (US, alt. intl.)": "US (Alt-Intl)", - "English (US, euro on 5)": "US (Euro)", - "English (US, intl., with dead keys)": "US (Intl)", - "English (US, Symbolic)": "US (Symbolic)", - "English (US)": "US", - "English (Workman, intl., with dead keys)": "US (Workman-Intl)", - "English (Workman)": "US (Workman)", - "Esperanto": "EPO", - "Esperanto (Brazil, Nativo)": "BR (Nativo-Epo)", - "Esperanto (legacy)": "EPO (Legacy)", - "Esperanto (Portugal, Nativo)": "PT (Nativo-Epo)", - "Estonian": "EE", - "Estonian (Dvorak)": "EE (Dvorak)", - "Estonian (no dead keys)": "EE (Nodeadkeys)", - "Estonian (US)": "EE (Us)", - "Ewe": "GH (Ewe)", - "Faroese": "FO", - "Faroese (no dead keys)": "FO (Nodeadkeys)", - "Filipino": "PH", - "Filipino (Capewell-Dvorak, Baybayin)": "PH (Capewell-Dvorak-Bay)", - "Filipino (Capewell-Dvorak, Latin)": "PH (Capewell-Dvorak)", - "Filipino (Capewell-QWERF 2006, Baybayin)": "PH (Capewell-Qwerf2k6-Bay)", - "Filipino (Capewell-QWERF 2006, Latin)": "PH (Capewell-Qwerf2k6)", - "Filipino (Colemak, Baybayin)": "PH (Colemak-Bay)", - "Filipino (Colemak, Latin)": "PH (Colemak)", - "Filipino (Dvorak, Baybayin)": "PH (Dvorak-Bay)", - "Filipino (Dvorak, Latin)": "PH (Dvorak)", - "Filipino (QWERTY, Baybayin)": "PH (Qwerty-Bay)", - "Finnish": "FI", - "Finnish (classic, no dead keys)": "FI (Nodeadkeys)", - "Finnish (classic)": "FI (Classic)", - "Finnish (Macintosh)": "FI (Mac)", - "Finnish (Windows)": "FI (Winkeys)", - "French": "FR", - "French (alt., Latin-9 only)": "FR (Oss Latin9)", - "French (alt., no dead keys)": "FR (Oss Nodeadkeys)", - "French (alt.)": "FR (Oss)", - "French (AZERTY, AFNOR)": "FR (Afnor)", - "French (AZERTY)": "FR (Azerty)", - "French (BEPO, AFNOR)": "FR (Bepo Afnor)", - "French (BEPO, Latin-9 only)": "FR (Bepo Latin9)", - "French (BEPO)": "FR (Bepo)", - "French (Cameroon)": "CM (French)", - "French (Canada, Dvorak)": "CA (Fr-Dvorak)", - "French (Canada, legacy)": "CA (Fr-Legacy)", - "French (Canada)": "CA", - "French (Democratic Republic of the Congo)": "CD", - "French (Dvorak)": "FR (Dvorak)", - "French (legacy, alt., no dead keys)": "FR (Latin9 Nodeadkeys)", - "French (legacy, alt.)": "FR (Latin9)", - "French (Macintosh)": "FR (Mac)", - "French (Mali, alt.)": "ML (Fr-Oss)", - "French (Morocco)": "MA (French)", - "French (no dead keys)": "FR (Nodeadkeys)", - "French (Switzerland, Macintosh)": "CH (Fr Mac)", - "French (Switzerland, no dead keys)": "CH (Fr Nodeadkeys)", - "French (Switzerland)": "CH (Fr)", - "French (Togo)": "TG", - "French (US)": "FR (Us)", - "Friulian (Italy)": "IT (Fur)", - "Fula": "GH (Fula)", - "Ga": "GH (Ga)", - "Georgian": "GE", - "Georgian (ergonomic)": "GE (Ergonomic)", - "Georgian (France, AZERTY Tskapo)": "FR (Geo)", - "Georgian (Italy)": "IT (Geo)", - "Georgian (MESS)": "GE (Mess)", - "German": "DE", - "German (Austria, Macintosh)": "AT (Mac)", - "German (Austria, no dead keys)": "AT (Nodeadkeys)", - "German (Austria)": "AT", - "German (dead acute)": "DE (Deadacute)", - "German (dead grave acute)": "DE (Deadgraveacute)", - "German (dead tilde)": "DE (Deadtilde)", - "German (Dvorak)": "DE (Dvorak)", - "German (E1)": "DE (E1)", - "German (E2)": "DE (E2)", - "German (Macintosh, no dead keys)": "DE (Mac Nodeadkeys)", - "German (Macintosh)": "DE (Mac)", - "German (Neo 2)": "DE (Neo)", - "German (no dead keys)": "DE (Nodeadkeys)", - "German (QWERTY)": "DE (Qwerty)", - "German (Switzerland, legacy)": "CH (Legacy)", - "German (Switzerland, Macintosh)": "CH (De Mac)", - "German (Switzerland, no dead keys)": "CH (De Nodeadkeys)", - "German (Switzerland)": "CH", - "German (T3)": "DE (T3)", - "German (US)": "DE (Us)", - "Greek": "GR", - "Greek (extended)": "GR (Extended)", - "Greek (no dead keys)": "GR (Nodeadkeys)", - "Greek (polytonic)": "GR (Polytonic)", - "Greek (simple)": "GR (Simple)", - "Gujarati": "IN (Guj)", - "Hanyu Pinyin Letters (with AltGr dead keys)": "CN (Altgr-Pinyin)", - "Hausa (Ghana)": "GH (Hausa)", - "Hausa (Nigeria)": "NG (Hausa)", - "Hawaiian": "US (Haw)", - "Hebrew": "IL", - "Hebrew (Biblical, Tiro)": "IL (Biblical)", - "Hebrew (lyx)": "IL (Lyx)", - "Hebrew (phonetic)": "IL (Phonetic)", - "Hindi (Bolnagri)": "IN (Bolnagri)", - "Hindi (KaGaPa, phonetic)": "IN (Hin-Kagapa)", - "Hindi (Wx)": "IN (Hin-Wx)", - "Hungarian": "HU", - "Hungarian (no dead keys)": "HU (Nodeadkeys)", - "Hungarian (QWERTY, 101-key, comma, dead keys)": "HU (101 Qwerty Comma Dead)", - "Hungarian (QWERTY, 101-key, comma, no dead keys)": "HU (101 Qwerty Comma Nodead)", - "Hungarian (QWERTY, 101-key, dot, dead keys)": "HU (101 Qwerty Dot Dead)", - "Hungarian (QWERTY, 101-key, dot, no dead keys)": "HU (101 Qwerty Dot Nodead)", - "Hungarian (QWERTY, 102-key, comma, dead keys)": "HU (102 Qwerty Comma Dead)", - "Hungarian (QWERTY, 102-key, comma, no dead keys)": "HU (102 Qwerty Comma Nodead)", - "Hungarian (QWERTY, 102-key, dot, dead keys)": "HU (102 Qwerty Dot Dead)", - "Hungarian (QWERTY, 102-key, dot, no dead keys)": "HU (102 Qwerty Dot Nodead)", - "Hungarian (QWERTY)": "HU (Qwerty)", - "Hungarian (QWERTZ, 101-key, comma, dead keys)": "HU (101 Qwertz Comma Dead)", - "Hungarian (QWERTZ, 101-key, comma, no dead keys)": "HU (101 Qwertz Comma Nodead)", - "Hungarian (QWERTZ, 101-key, dot, dead keys)": "HU (101 Qwertz Dot Dead)", - "Hungarian (QWERTZ, 101-key, dot, no dead keys)": "HU (101 Qwertz Dot Nodead)", - "Hungarian (QWERTZ, 102-key, comma, dead keys)": "HU (102 Qwertz Comma Dead)", - "Hungarian (QWERTZ, 102-key, comma, no dead keys)": "HU (102 Qwertz Comma Nodead)", - "Hungarian (QWERTZ, 102-key, dot, dead keys)": "HU (102 Qwertz Dot Dead)", - "Hungarian (QWERTZ, 102-key, dot, no dead keys)": "HU (102 Qwertz Dot Nodead)", - "Hungarian (standard)": "HU (Standard)", - "Icelandic": "IS", - "Icelandic (Dvorak)": "IS (Dvorak)", - "Icelandic (Macintosh, legacy)": "IS (Mac Legacy)", - "Icelandic (Macintosh)": "IS (Mac)", - "Igbo": "NG (Igbo)", - "Indian": "IN", - "Indic IPA": "IN (Iipa)", - "Indonesian (Arab Melayu, extended phonetic)": "ID (Melayu-Phoneticx)", - "Indonesian (Arab Melayu, phonetic)": "ID (Melayu-Phonetic)", - "Indonesian (Arab Pegon, phonetic)": "ID (Pegon-Phonetic)", - "Indonesian (Latin)": "ID", - "Inuktitut": "CA (Ike)", - "Iraqi": "IQ", - "Irish": "IE", - "Irish (UnicodeExpert)": "IE (UnicodeExpert)", - "Italian": "IT", - "Italian (IBM 142)": "IT (Ibm)", - "Italian (intl., with dead keys)": "IT (Intl)", - "Italian (Macintosh)": "IT (Mac)", - "Italian (no dead keys)": "IT (Nodeadkeys)", - "Italian (US)": "IT (Us)", - "Italian (Windows)": "IT (Winkeys)", - "Japanese": "JP", - "Japanese (Dvorak)": "JP (Dvorak)", - "Japanese (Kana 86)": "JP (Kana86)", - "Japanese (Kana)": "JP (Kana)", - "Japanese (Macintosh)": "JP (Mac)", - "Japanese (OADG 109A)": "JP (OADG109A)", - "Javanese": "ID (Javanese)", - "Kabyle (AZERTY, with dead keys)": "DZ (Azerty-Deadkeys)", - "Kabyle (QWERTY, UK, with dead keys)": "DZ (Qwerty-Gb-Deadkeys)", - "Kabyle (QWERTY, US, with dead keys)": "DZ (Qwerty-Us-Deadkeys)", - "Kalmyk": "RU (Xal)", - "Kannada": "IN (Kan)", - "Kannada (KaGaPa, phonetic)": "IN (Kan-Kagapa)", - "Kashubian": "PL (Csb)", - "Kazakh": "KZ", - "Kazakh (extended)": "KZ (Ext)", - "Kazakh (Latin)": "KZ (Latin)", - "Kazakh (with Russian)": "KZ (Kazrus)", - "Khmer (Cambodia)": "KH", - "Kikuyu": "KE (Kik)", - "Komi": "RU (Kom)", - "Korean": "KR", - "Korean (101/104-key compatible)": "KR (Kr104)", - "Kurdish (Iran, Arabic-Latin)": "IR (Ku Ara)", - "Kurdish (Iran, F)": "IR (Ku F)", - "Kurdish (Iran, Latin Alt-Q)": "IR (Ku Alt)", - "Kurdish (Iran, Latin Q)": "IR (Ku)", - "Kurdish (Iraq, Arabic-Latin)": "IQ (Ku Ara)", - "Kurdish (Iraq, F)": "IQ (Ku F)", - "Kurdish (Iraq, Latin Alt-Q)": "IQ (Ku Alt)", - "Kurdish (Iraq, Latin Q)": "IQ (Ku)", - "Kurdish (Syria, F)": "SY (Ku F)", - "Kurdish (Syria, Latin Alt-Q)": "SY (Ku Alt)", - "Kurdish (Syria, Latin Q)": "SY (Ku)", - "Kurdish (Turkey, F)": "TR (Ku F)", - "Kurdish (Turkey, Latin Alt-Q)": "TR (Ku Alt)", - "Kurdish (Turkey, Latin Q)": "TR (Ku)", - "Kyrgyz": "KG", - "Kyrgyz (phonetic)": "KG (Phonetic)", - "Lao": "LA", - "Lao (STEA)": "LA (Stea)", - "Latvian": "LV", - "Latvian (adapted)": "LV (Adapted)", - "Latvian (apostrophe)": "LV (Apostrophe)", - "Latvian (ergonomic, ŪGJRMV)": "LV (Ergonomic)", - "Latvian (F)": "LV (Fkey)", - "Latvian (modern)": "LV (Modern)", - "Latvian (tilde)": "LV (Tilde)", - "Lithuanian": "LT", - "Lithuanian (IBM LST 1205-92)": "LT (Ibm)", - "Lithuanian (LEKP)": "LT (Lekp)", - "Lithuanian (LEKPa)": "LT (Lekpa)", - "Lithuanian (Ratise)": "LT (Ratise)", - "Lithuanian (standard)": "LT (Std)", - "Lithuanian (US)": "LT (Us)", - "Lower Sorbian": "DE (Dsb)", - "Lower Sorbian (QWERTZ)": "DE (Dsb Qwertz)", - "Macedonian": "MK", - "Macedonian (no dead keys)": "MK (Nodeadkeys)", - "Malay (Jawi, Arabic Keyboard)": "MY", - "Malay (Jawi, phonetic)": "MY (Phonetic)", - "Malayalam": "IN (Mal)", - "Malayalam (enhanced InScript, with rupee)": "IN (Mal Enhanced)", - "Malayalam (Lalitha)": "IN (Mal Lalitha)", - "Maltese": "MT", - "Maltese (UK, with AltGr overrides)": "MT (Alt-Gb)", - "Maltese (US, with AltGr overrides)": "MT (Alt-Us)", - "Maltese (US)": "MT (Us)", - "Manipuri (Eeyek)": "IN (Eeyek)", - "Maori": "MAO", - "Marathi (enhanced InScript)": "IN (Marathi)", - "Marathi (KaGaPa, phonetic)": "IN (Mar-Kagapa)", - "Mari": "RU (Chm)", - "Mmuock": "CM (Mmuock)", - "Moldavian": "MD", - "Moldavian (Gagauz)": "MD (Gag)", - "Mon": "MM (Mnw)", - "Mon (A1)": "MM (Mnw-A1)", - "Mongolian": "MN", - "Mongolian (Bichig)": "CN (Mon Trad)", - "Mongolian (Galik)": "CN (Mon Trad Galik)", - "Mongolian (Manchu Galik)": "CN (Mon Manchu Galik)", - "Mongolian (Manchu)": "CN (Mon Trad Manchu)", - "Mongolian (Todo Galik)": "CN (Mon Todo Galik)", - "Mongolian (Todo)": "CN (Mon Trad Todo)", - "Mongolian (Xibe)": "CN (Mon Trad Xibe)", - "Montenegrin": "ME", - "Montenegrin (Cyrillic, with guillemets)": "ME (Cyrillicalternatequotes)", - "Montenegrin (Cyrillic, ZE and ZHE swapped)": "ME (Cyrillicyz)", - "Montenegrin (Cyrillic)": "ME (Cyrillic)", - "Montenegrin (Latin, QWERTY)": "ME (Latinyz)", - "Montenegrin (Latin, Unicode, QWERTY)": "ME (Latinunicodeyz)", - "Montenegrin (Latin, Unicode)": "ME (Latinunicode)", - "Montenegrin (Latin, with guillemets)": "ME (Latinalternatequotes)", - "N'Ko (AZERTY)": "GN", - "Nepali": "NP", - "Northern Saami (Finland)": "FI (Smi)", - "Northern Saami (Norway, no dead keys)": "NO (Smi Nodeadkeys)", - "Northern Saami (Norway)": "NO (Smi)", - "Northern Saami (Sweden)": "SE (Smi)", - "Norwegian": "NO", - "Norwegian (Colemak)": "NO (Colemak)", - "Norwegian (Dvorak)": "NO (Dvorak)", - "Norwegian (Macintosh, no dead keys)": "NO (Mac Nodeadkeys)", - "Norwegian (Macintosh)": "NO (Mac)", - "Norwegian (no dead keys)": "NO (Nodeadkeys)", - "Norwegian (Windows)": "NO (Winkeys)", - "Occitan": "FR (Oci)", - "Ogham": "IE (Ogam)", - "Ogham (IS434)": "IE (Ogam Is434)", - "Ol Chiki": "IN (Olck)", - "Old Turkic": "TR (Otk)", - "Old Turkic (F)": "TR (Otkf)", - "Oriya": "IN (Ori)", - "Oriya (Bolnagri)": "IN (Ori-Bolnagri)", - "Oriya (Wx)": "IN (Ori-Wx)", - "Ossetian (Georgia)": "GE (Os)", - "Ossetian (legacy)": "RU (Os Legacy)", - "Ossetian (Windows)": "RU (Os Winkeys)", - "Ottoman (F)": "TR (Otf)", - "Ottoman (Q)": "TR (Ot)", - "Pannonian Rusyn": "RS (Rue)", - "Pashto": "AF (Ps)", - "Pashto (Afghanistan, OLPC)": "AF (Ps-Olpc)", - "Persian": "IR", - "Persian (with Persian keypad)": "IR (Pes Keypad)", - "Polish": "PL", - "Polish (British keyboard)": "GB (Pl)", - "Polish (Dvorak, with Polish quotes on key 1)": "PL (Dvorak Altquotes)", - "Polish (Dvorak, with Polish quotes on quotemark key)": "PL (Dvorak Quotes)", - "Polish (Dvorak)": "PL (Dvorak)", - "Polish (legacy)": "PL (Legacy)", - "Polish (programmer Dvorak)": "PL (Dvp)", - "Polish (QWERTZ)": "PL (Qwertz)", - "Portuguese": "PT", - "Portuguese (Brazil, Dvorak)": "BR (Dvorak)", - "Portuguese (Brazil, IBM/Lenovo ThinkPad)": "BR (Thinkpad)", - "Portuguese (Brazil, Nativo for US keyboards)": "BR (Nativo-Us)", - "Portuguese (Brazil, Nativo)": "BR (Nativo)", - "Portuguese (Brazil, no dead keys)": "BR (Nodeadkeys)", - "Portuguese (Brazil)": "BR", - "Portuguese (Macintosh, no dead keys)": "PT (Mac Nodeadkeys)", - "Portuguese (Macintosh)": "PT (Mac)", - "Portuguese (Nativo for US keyboards)": "PT (Nativo-Us)", - "Portuguese (Nativo)": "PT (Nativo)", - "Portuguese (no dead keys)": "PT (Nodeadkeys)", - "Punjabi (Gurmukhi Jhelum)": "IN (Jhelum)", - "Punjabi (Gurmukhi)": "IN (Guru)", - "Romanian": "RO", - "Romanian (Germany, no dead keys)": "DE (Ro Nodeadkeys)", - "Romanian (Germany)": "DE (Ro)", - "Romanian (standard)": "RO (Std)", - "Romanian (Windows)": "RO (Winkeys)", - "Russian": "RU", - "Russian (Belarus)": "BY (Ru)", - "Russian (Czech, phonetic)": "CZ (Rus)", - "Russian (DOS)": "RU (Dos)", - "Russian (engineering, EN)": "RU (Ruchey En)", - "Russian (engineering, RU)": "RU (Ruchey Ru)", - "Russian (Georgia)": "GE (Ru)", - "Russian (Germany, phonetic)": "DE (Ru)", - "Russian (Kazakhstan, with Kazakh)": "KZ (Ruskaz)", - "Russian (legacy)": "RU (Legacy)", - "Russian (Macintosh)": "RU (Mac)", - "Russian (phonetic, AZERTY)": "RU (Phonetic Azerty)", - "Russian (phonetic, Dvorak)": "RU (Phonetic Dvorak)", - "Russian (phonetic, French)": "RU (Phonetic Fr)", - "Russian (phonetic, Windows)": "RU (Phonetic Winkeys)", - "Russian (phonetic, YAZHERTY)": "RU (Phonetic YAZHERTY)", - "Russian (phonetic)": "RU (Phonetic)", - "Russian (Poland, phonetic Dvorak)": "PL (Ru Phonetic Dvorak)", - "Russian (Sweden, phonetic, no dead keys)": "SE (Rus Nodeadkeys)", - "Russian (Sweden, phonetic)": "SE (Rus)", - "Russian (typewriter, legacy)": "RU (Typewriter-Legacy)", - "Russian (typewriter)": "RU (Typewriter)", - "Russian (Ukraine, standard RSTU)": "UA (Rstu Ru)", - "Russian (US, phonetic)": "US (Rus)", - "Saisiyat (Taiwan)": "TW (Saisiyat)", - "Samogitian": "LT (Sgs)", - "Sanskrit (KaGaPa, phonetic)": "IN (San-Kagapa)", - "Scottish Gaelic": "GB (Gla)", - "Serbian": "RS", - "Serbian (Cyrillic, with guillemets)": "RS (Alternatequotes)", - "Serbian (Cyrillic, ZE and ZHE swapped)": "RS (Yz)", - "Serbian (Latin, QWERTY)": "RS (Latinyz)", - "Serbian (Latin, Unicode, QWERTY)": "RS (Latinunicodeyz)", - "Serbian (Latin, Unicode)": "RS (Latinunicode)", - "Serbian (Latin, with guillemets)": "RS (Latinalternatequotes)", - "Serbian (Latin)": "RS (Latin)", - "Serbian (Russia)": "RU (Srp)", - "Serbo-Croatian (US)": "US (Hbs)", - "Shan": "MM (Shn)", - "Shan (Zawgyi Tai)": "MM (Zgt)", - "Sicilian": "IT (Scn)", - "Silesian": "PL (Szl)", - "Sindhi": "PK (Snd)", - "Sinhala (phonetic)": "LK", - "Sinhala (US)": "LK (Us)", - "Slovak": "SK", - "Slovak (extended backslash)": "SK (Bksl)", - "Slovak (QWERTY, extended backslash)": "SK (Qwerty Bksl)", - "Slovak (QWERTY)": "SK (Qwerty)", - "Slovenian": "SI", - "Slovenian (US)": "SI (Us)", - "Slovenian (with guillemets)": "SI (Alternatequotes)", - "Spanish": "ES", - "Spanish (dead tilde)": "ES (Deadtilde)", - "Spanish (Dvorak)": "ES (Dvorak)", - "Spanish (Latin American, Colemak)": "LATAM (Colemak)", - "Spanish (Latin American, dead tilde)": "LATAM (Deadtilde)", - "Spanish (Latin American, Dvorak)": "LATAM (Dvorak)", - "Spanish (Latin American, no dead keys)": "LATAM (Nodeadkeys)", - "Spanish (Latin American)": "LATAM", - "Spanish (Macintosh)": "ES (Mac)", - "Spanish (no dead keys)": "ES (Nodeadkeys)", - "Spanish (Windows)": "ES (Winkeys)", - "Swahili (Kenya)": "KE", - "Swahili (Tanzania)": "TZ", - "Swedish": "SE", - "Swedish (Dvorak, intl.)": "SE (Us Dvorak)", - "Swedish (Dvorak)": "SE (Dvorak)", - "Swedish (Macintosh)": "SE (Mac)", - "Swedish (no dead keys)": "SE (Nodeadkeys)", - "Swedish (Svdvorak)": "SE (Svdvorak)", - "Swedish (US)": "SE (Us)", - "Swedish Sign Language": "SE (Swl)", - "Syriac": "SY (Syc)", - "Syriac (phonetic)": "SY (Syc Phonetic)", - "Taiwanese": "TW", - "Taiwanese (indigenous)": "TW (Indigenous)", - "Tajik": "TJ", - "Tajik (legacy)": "TJ (Legacy)", - "Tamil (InScript, with Arabic numerals)": "IN (Tam)", - "Tamil (InScript, with Tamil numerals)": "IN (Tam Tamilnumbers)", - "Tamil (Sri Lanka, TamilNet '99, TAB encoding)": "LK (Tam TAB)", - "Tamil (Sri Lanka, TamilNet '99)": "LK (Tam Unicode)", - "Tamil (TamilNet '99 with Tamil numerals)": "IN (Tamilnet Tamilnumbers)", - "Tamil (TamilNet '99, TAB encoding)": "IN (Tamilnet TAB)", - "Tamil (TamilNet '99, TSCII encoding)": "IN (Tamilnet TSCII)", - "Tamil (TamilNet '99)": "IN (Tamilnet)", - "Tarifit": "MA (Rif)", - "Tatar": "RU (Tt)", - "Telugu": "IN (Tel)", - "Telugu (KaGaPa, phonetic)": "IN (Tel-Kagapa)", - "Telugu (Sarala)": "IN (Tel-Sarala)", - "Thai": "TH", - "Thai (Pattachote)": "TH (Pat)", - "Thai (TIS-820.2538)": "TH (Tis)", - "Tibetan": "CN (Tib)", - "Tibetan (with ASCII numerals)": "CN (Tib Asciinum)", - "Tswana": "BW", - "Turkish": "TR", - "Turkish (Alt-Q)": "TR (Alt)", - "Turkish (E)": "TR (E)", - "Turkish (F)": "TR (F)", - "Turkish (Germany)": "DE (Tr)", - "Turkish (intl., with dead keys)": "TR (Intl)", - "Turkmen": "TM", - "Turkmen (Alt-Q)": "TM (Alt)", - "Udmurt": "RU (Udm)", - "Ukrainian": "UA", - "Ukrainian (homophonic)": "UA (Homophonic)", - "Ukrainian (legacy)": "UA (Legacy)", - "Ukrainian (macOS)": "UA (MacOS)", - "Ukrainian (phonetic)": "UA (Phonetic)", - "Ukrainian (standard RSTU)": "UA (Rstu)", - "Ukrainian (typewriter)": "UA (Typewriter)", - "Ukrainian (Windows)": "UA (Winkeys)", - "Urdu (alt. phonetic)": "IN (Urd-Phonetic3)", - "Urdu (Pakistan, CRULP)": "PK (Urd-Crulp)", - "Urdu (Pakistan, NLA)": "PK (Urd-Nla)", - "Urdu (Pakistan)": "PK", - "Urdu (phonetic)": "IN (Urd-Phonetic)", - "Urdu (Windows)": "IN (Urd-Winkeys)", - "Uyghur": "CN (Ug)", - "Uzbek": "UZ", - "Uzbek (Afghanistan, OLPC)": "AF (Uz-Olpc)", - "Uzbek (Afghanistan)": "AF (Uz)", - "Uzbek (Latin)": "UZ (Latin)", - "Vietnamese": "VN", - "Vietnamese (France)": "VN (Fr)", - "Vietnamese (US)": "VN (Us)", - "Wolof": "SN", - "Yakut": "RU (Sah)", - "Yoruba": "NG (Yoruba)" -}; + 'Abkhazian (Russia)': 'RU (Ab)', + Akan: 'GH (Akan)', + Albanian: 'AL', + 'Albanian (Plisi)': 'AL (Plisi)', + 'Albanian (Veqilharxhi)': 'AL (Veqilharxhi)', + Amharic: 'ET', + Arabic: 'ARA', + 'Arabic (Algeria)': 'DZ (Ar)', + 'Arabic (AZERTY, Eastern Arabic numerals)': 'ARA (Azerty Digits)', + 'Arabic (AZERTY)': 'ARA (Azerty)', + 'Arabic (Buckwalter)': 'ARA (Buckwalter)', + 'Arabic (Eastern Arabic numerals)': 'ARA (Digits)', + 'Arabic (Macintosh)': 'ARA (Mac)', + 'Arabic (Morocco)': 'MA', + 'Arabic (OLPC)': 'ARA (Olpc)', + 'Arabic (Pakistan)': 'PK (Ara)', + 'Arabic (QWERTY, Eastern Arabic numerals)': 'ARA (Qwerty Digits)', + 'Arabic (QWERTY)': 'ARA (Qwerty)', + 'Arabic (Syria)': 'SY', + Armenian: 'AM', + 'Armenian (alt. eastern)': 'AM (Eastern-Alt)', + 'Armenian (alt. phonetic)': 'AM (Phonetic-Alt)', + 'Armenian (eastern)': 'AM (Eastern)', + 'Armenian (phonetic)': 'AM (Phonetic)', + 'Armenian (western)': 'AM (Western)', + 'Asturian (Spain, with bottom-dot H and L)': 'ES (Ast)', + Avatime: 'GH (Avn)', + Azerbaijani: 'AZ', + 'Azerbaijani (Cyrillic)': 'AZ (Cyrillic)', + 'Azerbaijani (Iran)': 'IR (Azb)', + Bambara: 'ML', + Bangla: 'BD', + 'Bangla (India, Baishakhi InScript)': 'IN (Ben Inscript)', + 'Bangla (India, Baishakhi)': 'IN (Ben Baishakhi)', + 'Bangla (India, Bornona)': 'IN (Ben Bornona)', + 'Bangla (India, Gitanjali)': 'IN (Ben Gitanjali)', + 'Bangla (India, Probhat)': 'IN (Ben Probhat)', + 'Bangla (India)': 'IN (Ben)', + 'Bangla (Probhat)': 'BD (Probhat)', + Bashkirian: 'RU (Bak)', + Belarusian: 'BY', + 'Belarusian (intl.)': 'BY (Intl)', + 'Belarusian (Latin)': 'BY (Latin)', + 'Belarusian (legacy)': 'BY (Legacy)', + 'Belarusian (phonetic)': 'BY (Phonetic)', + Belgian: 'BE', + 'Belgian (alt.)': 'BE (Oss)', + 'Belgian (ISO, alt.)': 'BE (Iso-Alternate)', + 'Belgian (Latin-9 only, alt.)': 'BE (Oss Latin9)', + 'Belgian (no dead keys)': 'BE (Nodeadkeys)', + 'Belgian (Wang 724 AZERTY)': 'BE (Wang)', + 'Berber (Algeria, Latin)': 'DZ', + 'Berber (Algeria, Tifinagh)': 'DZ (Ber)', + 'Berber (Morocco, Tifinagh alt.)': 'MA (Tifinagh-Alt)', + 'Berber (Morocco, Tifinagh extended phonetic)': 'MA (Tifinagh-Extended-Phonetic)', + 'Berber (Morocco, Tifinagh extended)': 'MA (Tifinagh-Extended)', + 'Berber (Morocco, Tifinagh phonetic, alt.)': 'MA (Tifinagh-Alt-Phonetic)', + 'Berber (Morocco, Tifinagh phonetic)': 'MA (Tifinagh-Phonetic)', + 'Berber (Morocco, Tifinagh)': 'MA (Tifinagh)', + Bosnian: 'BA', + 'Bosnian (US, with Bosnian digraphs)': 'BA (Unicodeus)', + 'Bosnian (US)': 'BA (Us)', + 'Bosnian (with Bosnian digraphs)': 'BA (Unicode)', + 'Bosnian (with guillemets)': 'BA (Alternatequotes)', + Braille: 'BRAI', + 'Braille (left-handed inverted thumb)': 'BRAI (Left Hand Invert)', + 'Braille (left-handed)': 'BRAI (Left Hand)', + 'Braille (right-handed inverted thumb)': 'BRAI (Right Hand Invert)', + 'Braille (right-handed)': 'BRAI (Right Hand)', + 'Breton (France)': 'FR (Bre)', + Bulgarian: 'BG', + 'Bulgarian (enhanced)': 'BG (Bekl)', + 'Bulgarian (new phonetic)': 'BG (Bas Phonetic)', + 'Bulgarian (traditional phonetic)': 'BG (Phonetic)', + Burmese: 'MM', + 'Burmese Zawgyi': 'MM (Zawgyi)', + 'Cameroon (AZERTY, intl.)': 'CM (Azerty)', + 'Cameroon (Dvorak, intl.)': 'CM (Dvorak)', + 'Cameroon Multilingual (QWERTY, intl.)': 'CM (Qwerty)', + 'Canadian (CSA)': 'CA (Multix)', + 'Catalan (Spain, with middle-dot L)': 'ES (Cat)', + Cherokee: 'US (Chr)', + Chinese: 'CN', + Chuvash: 'RU (Cv)', + 'Chuvash (Latin)': 'RU (Cv Latin)', + CloGaelach: 'IE (CloGaelach)', + 'Crimean Tatar (Turkish Alt-Q)': 'UA (Crh Alt)', + 'Crimean Tatar (Turkish F)': 'UA (Crh F)', + 'Crimean Tatar (Turkish Q)': 'UA (Crh)', + Croatian: 'HR', + 'Croatian (US, with Croatian digraphs)': 'HR (Unicodeus)', + 'Croatian (US)': 'HR (Us)', + 'Croatian (with Croatian digraphs)': 'HR (Unicode)', + 'Croatian (with guillemets)': 'HR (Alternatequotes)', + Czech: 'CZ', + 'Czech (QWERTY, extended backslash)': 'CZ (Qwerty Bksl)', + 'Czech (QWERTY, Macintosh)': 'CZ (Qwerty-Mac)', + 'Czech (QWERTY)': 'CZ (Qwerty)', + 'Czech (UCW, only accented letters)': 'CZ (Ucw)', + 'Czech (US, Dvorak, UCW support)': 'CZ (Dvorak-Ucw)', + 'Czech (with <\\|> key)': 'CZ (Bksl)', + Danish: 'DK', + 'Danish (Dvorak)': 'DK (Dvorak)', + 'Danish (Macintosh, no dead keys)': 'DK (Mac Nodeadkeys)', + 'Danish (Macintosh)': 'DK (Mac)', + 'Danish (no dead keys)': 'DK (Nodeadkeys)', + 'Danish (Windows)': 'DK (Winkeys)', + Dari: 'AF', + 'Dari (Afghanistan, OLPC)': 'AF (Fa-Olpc)', + Dhivehi: 'MV', + Dutch: 'NL', + 'Dutch (Macintosh)': 'NL (Mac)', + 'Dutch (standard)': 'NL (Std)', + 'Dutch (US)': 'NL (Us)', + Dzongkha: 'BT', + 'English (Australian)': 'AU', + 'English (Cameroon)': 'CM', + 'English (Canada)': 'CA (Eng)', + 'English (classic Dvorak)': 'US (Dvorak-Classic)', + 'English (Colemak-DH ISO)': 'US (Colemak Dh Iso)', + 'English (Colemak-DH)': 'US (Colemak Dh)', + 'English (Colemak)': 'US (Colemak)', + 'English (Dvorak, alt. intl.)': 'US (Dvorak-Alt-Intl)', + 'English (Dvorak, intl., with dead keys)': 'US (Dvorak-Intl)', + 'English (Dvorak, left-handed)': 'US (Dvorak-L)', + 'English (Dvorak, Macintosh)': 'US (Dvorak-Mac)', + 'English (Dvorak, right-handed)': 'US (Dvorak-R)', + 'English (Dvorak)': 'US (Dvorak)', + 'English (Ghana, GILLBT)': 'GH (Gillbt)', + 'English (Ghana, multilingual)': 'GH (Generic)', + 'English (Ghana)': 'GH', + 'English (India, with rupee)': 'IN (Eng)', + 'English (intl., with AltGr dead keys)': 'US (Altgr-Intl)', + 'English (Macintosh)': 'US (Mac)', + 'English (Mali, US, intl.)': 'ML (Us-Intl)', + 'English (Mali, US, Macintosh)': 'ML (Us-Mac)', + 'English (Nigeria)': 'NG', + 'English (Norman)': 'US (Norman)', + 'English (programmer Dvorak)': 'US (Dvp)', + 'English (South Africa)': 'ZA', + 'English (the divide/multiply toggle the layout)': 'US (Olpc2)', + 'English (UK, Colemak-DH)': 'GB (Colemak Dh)', + 'English (UK, Colemak)': 'GB (Colemak)', + 'English (UK, Dvorak, with UK punctuation)': 'GB (Dvorakukp)', + 'English (UK, Dvorak)': 'GB (Dvorak)', + 'English (UK, extended, Windows)': 'GB (Extd)', + 'English (UK, intl., with dead keys)': 'GB (Intl)', + 'English (UK, Macintosh, intl.)': 'GB (Mac Intl)', + 'English (UK, Macintosh)': 'GB (Mac)', + 'English (UK)': 'GB', + 'English (US, alt. intl.)': 'US (Alt-Intl)', + 'English (US, euro on 5)': 'US (Euro)', + 'English (US, intl., with dead keys)': 'US (Intl)', + 'English (US, Symbolic)': 'US (Symbolic)', + 'English (US)': 'US', + 'English (Workman, intl., with dead keys)': 'US (Workman-Intl)', + 'English (Workman)': 'US (Workman)', + Esperanto: 'EPO', + 'Esperanto (Brazil, Nativo)': 'BR (Nativo-Epo)', + 'Esperanto (legacy)': 'EPO (Legacy)', + 'Esperanto (Portugal, Nativo)': 'PT (Nativo-Epo)', + Estonian: 'EE', + 'Estonian (Dvorak)': 'EE (Dvorak)', + 'Estonian (no dead keys)': 'EE (Nodeadkeys)', + 'Estonian (US)': 'EE (Us)', + Ewe: 'GH (Ewe)', + Faroese: 'FO', + 'Faroese (no dead keys)': 'FO (Nodeadkeys)', + Filipino: 'PH', + 'Filipino (Capewell-Dvorak, Baybayin)': 'PH (Capewell-Dvorak-Bay)', + 'Filipino (Capewell-Dvorak, Latin)': 'PH (Capewell-Dvorak)', + 'Filipino (Capewell-QWERF 2006, Baybayin)': 'PH (Capewell-Qwerf2k6-Bay)', + 'Filipino (Capewell-QWERF 2006, Latin)': 'PH (Capewell-Qwerf2k6)', + 'Filipino (Colemak, Baybayin)': 'PH (Colemak-Bay)', + 'Filipino (Colemak, Latin)': 'PH (Colemak)', + 'Filipino (Dvorak, Baybayin)': 'PH (Dvorak-Bay)', + 'Filipino (Dvorak, Latin)': 'PH (Dvorak)', + 'Filipino (QWERTY, Baybayin)': 'PH (Qwerty-Bay)', + Finnish: 'FI', + 'Finnish (classic, no dead keys)': 'FI (Nodeadkeys)', + 'Finnish (classic)': 'FI (Classic)', + 'Finnish (Macintosh)': 'FI (Mac)', + 'Finnish (Windows)': 'FI (Winkeys)', + French: 'FR', + 'French (alt., Latin-9 only)': 'FR (Oss Latin9)', + 'French (alt., no dead keys)': 'FR (Oss Nodeadkeys)', + 'French (alt.)': 'FR (Oss)', + 'French (AZERTY, AFNOR)': 'FR (Afnor)', + 'French (AZERTY)': 'FR (Azerty)', + 'French (BEPO, AFNOR)': 'FR (Bepo Afnor)', + 'French (BEPO, Latin-9 only)': 'FR (Bepo Latin9)', + 'French (BEPO)': 'FR (Bepo)', + 'French (Cameroon)': 'CM (French)', + 'French (Canada, Dvorak)': 'CA (Fr-Dvorak)', + 'French (Canada, legacy)': 'CA (Fr-Legacy)', + 'French (Canada)': 'CA', + 'French (Democratic Republic of the Congo)': 'CD', + 'French (Dvorak)': 'FR (Dvorak)', + 'French (legacy, alt., no dead keys)': 'FR (Latin9 Nodeadkeys)', + 'French (legacy, alt.)': 'FR (Latin9)', + 'French (Macintosh)': 'FR (Mac)', + 'French (Mali, alt.)': 'ML (Fr-Oss)', + 'French (Morocco)': 'MA (French)', + 'French (no dead keys)': 'FR (Nodeadkeys)', + 'French (Switzerland, Macintosh)': 'CH (Fr Mac)', + 'French (Switzerland, no dead keys)': 'CH (Fr Nodeadkeys)', + 'French (Switzerland)': 'CH (Fr)', + 'French (Togo)': 'TG', + 'French (US)': 'FR (Us)', + 'Friulian (Italy)': 'IT (Fur)', + Fula: 'GH (Fula)', + Ga: 'GH (Ga)', + Georgian: 'GE', + 'Georgian (ergonomic)': 'GE (Ergonomic)', + 'Georgian (France, AZERTY Tskapo)': 'FR (Geo)', + 'Georgian (Italy)': 'IT (Geo)', + 'Georgian (MESS)': 'GE (Mess)', + German: 'DE', + 'German (Austria, Macintosh)': 'AT (Mac)', + 'German (Austria, no dead keys)': 'AT (Nodeadkeys)', + 'German (Austria)': 'AT', + 'German (dead acute)': 'DE (Deadacute)', + 'German (dead grave acute)': 'DE (Deadgraveacute)', + 'German (dead tilde)': 'DE (Deadtilde)', + 'German (Dvorak)': 'DE (Dvorak)', + 'German (E1)': 'DE (E1)', + 'German (E2)': 'DE (E2)', + 'German (Macintosh, no dead keys)': 'DE (Mac Nodeadkeys)', + 'German (Macintosh)': 'DE (Mac)', + 'German (Neo 2)': 'DE (Neo)', + 'German (no dead keys)': 'DE (Nodeadkeys)', + 'German (QWERTY)': 'DE (Qwerty)', + 'German (Switzerland, legacy)': 'CH (Legacy)', + 'German (Switzerland, Macintosh)': 'CH (De Mac)', + 'German (Switzerland, no dead keys)': 'CH (De Nodeadkeys)', + 'German (Switzerland)': 'CH', + 'German (T3)': 'DE (T3)', + 'German (US)': 'DE (Us)', + Greek: 'GR', + 'Greek (extended)': 'GR (Extended)', + 'Greek (no dead keys)': 'GR (Nodeadkeys)', + 'Greek (polytonic)': 'GR (Polytonic)', + 'Greek (simple)': 'GR (Simple)', + Gujarati: 'IN (Guj)', + 'Hanyu Pinyin Letters (with AltGr dead keys)': 'CN (Altgr-Pinyin)', + 'Hausa (Ghana)': 'GH (Hausa)', + 'Hausa (Nigeria)': 'NG (Hausa)', + Hawaiian: 'US (Haw)', + Hebrew: 'IL', + 'Hebrew (Biblical, Tiro)': 'IL (Biblical)', + 'Hebrew (lyx)': 'IL (Lyx)', + 'Hebrew (phonetic)': 'IL (Phonetic)', + 'Hindi (Bolnagri)': 'IN (Bolnagri)', + 'Hindi (KaGaPa, phonetic)': 'IN (Hin-Kagapa)', + 'Hindi (Wx)': 'IN (Hin-Wx)', + Hungarian: 'HU', + 'Hungarian (no dead keys)': 'HU (Nodeadkeys)', + 'Hungarian (QWERTY, 101-key, comma, dead keys)': 'HU (101 Qwerty Comma Dead)', + 'Hungarian (QWERTY, 101-key, comma, no dead keys)': 'HU (101 Qwerty Comma Nodead)', + 'Hungarian (QWERTY, 101-key, dot, dead keys)': 'HU (101 Qwerty Dot Dead)', + 'Hungarian (QWERTY, 101-key, dot, no dead keys)': 'HU (101 Qwerty Dot Nodead)', + 'Hungarian (QWERTY, 102-key, comma, dead keys)': 'HU (102 Qwerty Comma Dead)', + 'Hungarian (QWERTY, 102-key, comma, no dead keys)': 'HU (102 Qwerty Comma Nodead)', + 'Hungarian (QWERTY, 102-key, dot, dead keys)': 'HU (102 Qwerty Dot Dead)', + 'Hungarian (QWERTY, 102-key, dot, no dead keys)': 'HU (102 Qwerty Dot Nodead)', + 'Hungarian (QWERTY)': 'HU (Qwerty)', + 'Hungarian (QWERTZ, 101-key, comma, dead keys)': 'HU (101 Qwertz Comma Dead)', + 'Hungarian (QWERTZ, 101-key, comma, no dead keys)': 'HU (101 Qwertz Comma Nodead)', + 'Hungarian (QWERTZ, 101-key, dot, dead keys)': 'HU (101 Qwertz Dot Dead)', + 'Hungarian (QWERTZ, 101-key, dot, no dead keys)': 'HU (101 Qwertz Dot Nodead)', + 'Hungarian (QWERTZ, 102-key, comma, dead keys)': 'HU (102 Qwertz Comma Dead)', + 'Hungarian (QWERTZ, 102-key, comma, no dead keys)': 'HU (102 Qwertz Comma Nodead)', + 'Hungarian (QWERTZ, 102-key, dot, dead keys)': 'HU (102 Qwertz Dot Dead)', + 'Hungarian (QWERTZ, 102-key, dot, no dead keys)': 'HU (102 Qwertz Dot Nodead)', + 'Hungarian (standard)': 'HU (Standard)', + Icelandic: 'IS', + 'Icelandic (Dvorak)': 'IS (Dvorak)', + 'Icelandic (Macintosh, legacy)': 'IS (Mac Legacy)', + 'Icelandic (Macintosh)': 'IS (Mac)', + Igbo: 'NG (Igbo)', + Indian: 'IN', + 'Indic IPA': 'IN (Iipa)', + 'Indonesian (Arab Melayu, extended phonetic)': 'ID (Melayu-Phoneticx)', + 'Indonesian (Arab Melayu, phonetic)': 'ID (Melayu-Phonetic)', + 'Indonesian (Arab Pegon, phonetic)': 'ID (Pegon-Phonetic)', + 'Indonesian (Latin)': 'ID', + Inuktitut: 'CA (Ike)', + Iraqi: 'IQ', + Irish: 'IE', + 'Irish (UnicodeExpert)': 'IE (UnicodeExpert)', + Italian: 'IT', + 'Italian (IBM 142)': 'IT (Ibm)', + 'Italian (intl., with dead keys)': 'IT (Intl)', + 'Italian (Macintosh)': 'IT (Mac)', + 'Italian (no dead keys)': 'IT (Nodeadkeys)', + 'Italian (US)': 'IT (Us)', + 'Italian (Windows)': 'IT (Winkeys)', + Japanese: 'JP', + 'Japanese (Dvorak)': 'JP (Dvorak)', + 'Japanese (Kana 86)': 'JP (Kana86)', + 'Japanese (Kana)': 'JP (Kana)', + 'Japanese (Macintosh)': 'JP (Mac)', + 'Japanese (OADG 109A)': 'JP (OADG109A)', + Javanese: 'ID (Javanese)', + 'Kabyle (AZERTY, with dead keys)': 'DZ (Azerty-Deadkeys)', + 'Kabyle (QWERTY, UK, with dead keys)': 'DZ (Qwerty-Gb-Deadkeys)', + 'Kabyle (QWERTY, US, with dead keys)': 'DZ (Qwerty-Us-Deadkeys)', + Kalmyk: 'RU (Xal)', + Kannada: 'IN (Kan)', + 'Kannada (KaGaPa, phonetic)': 'IN (Kan-Kagapa)', + Kashubian: 'PL (Csb)', + Kazakh: 'KZ', + 'Kazakh (extended)': 'KZ (Ext)', + 'Kazakh (Latin)': 'KZ (Latin)', + 'Kazakh (with Russian)': 'KZ (Kazrus)', + 'Khmer (Cambodia)': 'KH', + Kikuyu: 'KE (Kik)', + Komi: 'RU (Kom)', + Korean: 'KR', + 'Korean (101/104-key compatible)': 'KR (Kr104)', + 'Kurdish (Iran, Arabic-Latin)': 'IR (Ku Ara)', + 'Kurdish (Iran, F)': 'IR (Ku F)', + 'Kurdish (Iran, Latin Alt-Q)': 'IR (Ku Alt)', + 'Kurdish (Iran, Latin Q)': 'IR (Ku)', + 'Kurdish (Iraq, Arabic-Latin)': 'IQ (Ku Ara)', + 'Kurdish (Iraq, F)': 'IQ (Ku F)', + 'Kurdish (Iraq, Latin Alt-Q)': 'IQ (Ku Alt)', + 'Kurdish (Iraq, Latin Q)': 'IQ (Ku)', + 'Kurdish (Syria, F)': 'SY (Ku F)', + 'Kurdish (Syria, Latin Alt-Q)': 'SY (Ku Alt)', + 'Kurdish (Syria, Latin Q)': 'SY (Ku)', + 'Kurdish (Turkey, F)': 'TR (Ku F)', + 'Kurdish (Turkey, Latin Alt-Q)': 'TR (Ku Alt)', + 'Kurdish (Turkey, Latin Q)': 'TR (Ku)', + Kyrgyz: 'KG', + 'Kyrgyz (phonetic)': 'KG (Phonetic)', + Lao: 'LA', + 'Lao (STEA)': 'LA (Stea)', + Latvian: 'LV', + 'Latvian (adapted)': 'LV (Adapted)', + 'Latvian (apostrophe)': 'LV (Apostrophe)', + 'Latvian (ergonomic, ŪGJRMV)': 'LV (Ergonomic)', + 'Latvian (F)': 'LV (Fkey)', + 'Latvian (modern)': 'LV (Modern)', + 'Latvian (tilde)': 'LV (Tilde)', + Lithuanian: 'LT', + 'Lithuanian (IBM LST 1205-92)': 'LT (Ibm)', + 'Lithuanian (LEKP)': 'LT (Lekp)', + 'Lithuanian (LEKPa)': 'LT (Lekpa)', + 'Lithuanian (Ratise)': 'LT (Ratise)', + 'Lithuanian (standard)': 'LT (Std)', + 'Lithuanian (US)': 'LT (Us)', + 'Lower Sorbian': 'DE (Dsb)', + 'Lower Sorbian (QWERTZ)': 'DE (Dsb Qwertz)', + Macedonian: 'MK', + 'Macedonian (no dead keys)': 'MK (Nodeadkeys)', + 'Malay (Jawi, Arabic Keyboard)': 'MY', + 'Malay (Jawi, phonetic)': 'MY (Phonetic)', + Malayalam: 'IN (Mal)', + 'Malayalam (enhanced InScript, with rupee)': 'IN (Mal Enhanced)', + 'Malayalam (Lalitha)': 'IN (Mal Lalitha)', + Maltese: 'MT', + 'Maltese (UK, with AltGr overrides)': 'MT (Alt-Gb)', + 'Maltese (US, with AltGr overrides)': 'MT (Alt-Us)', + 'Maltese (US)': 'MT (Us)', + 'Manipuri (Eeyek)': 'IN (Eeyek)', + Maori: 'MAO', + 'Marathi (enhanced InScript)': 'IN (Marathi)', + 'Marathi (KaGaPa, phonetic)': 'IN (Mar-Kagapa)', + Mari: 'RU (Chm)', + Mmuock: 'CM (Mmuock)', + Moldavian: 'MD', + 'Moldavian (Gagauz)': 'MD (Gag)', + Mon: 'MM (Mnw)', + 'Mon (A1)': 'MM (Mnw-A1)', + Mongolian: 'MN', + 'Mongolian (Bichig)': 'CN (Mon Trad)', + 'Mongolian (Galik)': 'CN (Mon Trad Galik)', + 'Mongolian (Manchu Galik)': 'CN (Mon Manchu Galik)', + 'Mongolian (Manchu)': 'CN (Mon Trad Manchu)', + 'Mongolian (Todo Galik)': 'CN (Mon Todo Galik)', + 'Mongolian (Todo)': 'CN (Mon Trad Todo)', + 'Mongolian (Xibe)': 'CN (Mon Trad Xibe)', + Montenegrin: 'ME', + 'Montenegrin (Cyrillic, with guillemets)': 'ME (Cyrillicalternatequotes)', + 'Montenegrin (Cyrillic, ZE and ZHE swapped)': 'ME (Cyrillicyz)', + 'Montenegrin (Cyrillic)': 'ME (Cyrillic)', + 'Montenegrin (Latin, QWERTY)': 'ME (Latinyz)', + 'Montenegrin (Latin, Unicode, QWERTY)': 'ME (Latinunicodeyz)', + 'Montenegrin (Latin, Unicode)': 'ME (Latinunicode)', + 'Montenegrin (Latin, with guillemets)': 'ME (Latinalternatequotes)', + "N'Ko (AZERTY)": 'GN', + Nepali: 'NP', + 'Northern Saami (Finland)': 'FI (Smi)', + 'Northern Saami (Norway, no dead keys)': 'NO (Smi Nodeadkeys)', + 'Northern Saami (Norway)': 'NO (Smi)', + 'Northern Saami (Sweden)': 'SE (Smi)', + Norwegian: 'NO', + 'Norwegian (Colemak)': 'NO (Colemak)', + 'Norwegian (Dvorak)': 'NO (Dvorak)', + 'Norwegian (Macintosh, no dead keys)': 'NO (Mac Nodeadkeys)', + 'Norwegian (Macintosh)': 'NO (Mac)', + 'Norwegian (no dead keys)': 'NO (Nodeadkeys)', + 'Norwegian (Windows)': 'NO (Winkeys)', + Occitan: 'FR (Oci)', + Ogham: 'IE (Ogam)', + 'Ogham (IS434)': 'IE (Ogam Is434)', + 'Ol Chiki': 'IN (Olck)', + 'Old Turkic': 'TR (Otk)', + 'Old Turkic (F)': 'TR (Otkf)', + Oriya: 'IN (Ori)', + 'Oriya (Bolnagri)': 'IN (Ori-Bolnagri)', + 'Oriya (Wx)': 'IN (Ori-Wx)', + 'Ossetian (Georgia)': 'GE (Os)', + 'Ossetian (legacy)': 'RU (Os Legacy)', + 'Ossetian (Windows)': 'RU (Os Winkeys)', + 'Ottoman (F)': 'TR (Otf)', + 'Ottoman (Q)': 'TR (Ot)', + 'Pannonian Rusyn': 'RS (Rue)', + Pashto: 'AF (Ps)', + 'Pashto (Afghanistan, OLPC)': 'AF (Ps-Olpc)', + Persian: 'IR', + 'Persian (with Persian keypad)': 'IR (Pes Keypad)', + Polish: 'PL', + 'Polish (British keyboard)': 'GB (Pl)', + 'Polish (Dvorak, with Polish quotes on key 1)': 'PL (Dvorak Altquotes)', + 'Polish (Dvorak, with Polish quotes on quotemark key)': 'PL (Dvorak Quotes)', + 'Polish (Dvorak)': 'PL (Dvorak)', + 'Polish (legacy)': 'PL (Legacy)', + 'Polish (programmer Dvorak)': 'PL (Dvp)', + 'Polish (QWERTZ)': 'PL (Qwertz)', + Portuguese: 'PT', + 'Portuguese (Brazil, Dvorak)': 'BR (Dvorak)', + 'Portuguese (Brazil, IBM/Lenovo ThinkPad)': 'BR (Thinkpad)', + 'Portuguese (Brazil, Nativo for US keyboards)': 'BR (Nativo-Us)', + 'Portuguese (Brazil, Nativo)': 'BR (Nativo)', + 'Portuguese (Brazil, no dead keys)': 'BR (Nodeadkeys)', + 'Portuguese (Brazil)': 'BR', + 'Portuguese (Macintosh, no dead keys)': 'PT (Mac Nodeadkeys)', + 'Portuguese (Macintosh)': 'PT (Mac)', + 'Portuguese (Nativo for US keyboards)': 'PT (Nativo-Us)', + 'Portuguese (Nativo)': 'PT (Nativo)', + 'Portuguese (no dead keys)': 'PT (Nodeadkeys)', + 'Punjabi (Gurmukhi Jhelum)': 'IN (Jhelum)', + 'Punjabi (Gurmukhi)': 'IN (Guru)', + Romanian: 'RO', + 'Romanian (Germany, no dead keys)': 'DE (Ro Nodeadkeys)', + 'Romanian (Germany)': 'DE (Ro)', + 'Romanian (standard)': 'RO (Std)', + 'Romanian (Windows)': 'RO (Winkeys)', + Russian: 'RU', + 'Russian (Belarus)': 'BY (Ru)', + 'Russian (Czech, phonetic)': 'CZ (Rus)', + 'Russian (DOS)': 'RU (Dos)', + 'Russian (engineering, EN)': 'RU (Ruchey En)', + 'Russian (engineering, RU)': 'RU (Ruchey Ru)', + 'Russian (Georgia)': 'GE (Ru)', + 'Russian (Germany, phonetic)': 'DE (Ru)', + 'Russian (Kazakhstan, with Kazakh)': 'KZ (Ruskaz)', + 'Russian (legacy)': 'RU (Legacy)', + 'Russian (Macintosh)': 'RU (Mac)', + 'Russian (phonetic, AZERTY)': 'RU (Phonetic Azerty)', + 'Russian (phonetic, Dvorak)': 'RU (Phonetic Dvorak)', + 'Russian (phonetic, French)': 'RU (Phonetic Fr)', + 'Russian (phonetic, Windows)': 'RU (Phonetic Winkeys)', + 'Russian (phonetic, YAZHERTY)': 'RU (Phonetic YAZHERTY)', + 'Russian (phonetic)': 'RU (Phonetic)', + 'Russian (Poland, phonetic Dvorak)': 'PL (Ru Phonetic Dvorak)', + 'Russian (Sweden, phonetic, no dead keys)': 'SE (Rus Nodeadkeys)', + 'Russian (Sweden, phonetic)': 'SE (Rus)', + 'Russian (typewriter, legacy)': 'RU (Typewriter-Legacy)', + 'Russian (typewriter)': 'RU (Typewriter)', + 'Russian (Ukraine, standard RSTU)': 'UA (Rstu Ru)', + 'Russian (US, phonetic)': 'US (Rus)', + 'Saisiyat (Taiwan)': 'TW (Saisiyat)', + Samogitian: 'LT (Sgs)', + 'Sanskrit (KaGaPa, phonetic)': 'IN (San-Kagapa)', + 'Scottish Gaelic': 'GB (Gla)', + Serbian: 'RS', + 'Serbian (Cyrillic, with guillemets)': 'RS (Alternatequotes)', + 'Serbian (Cyrillic, ZE and ZHE swapped)': 'RS (Yz)', + 'Serbian (Latin, QWERTY)': 'RS (Latinyz)', + 'Serbian (Latin, Unicode, QWERTY)': 'RS (Latinunicodeyz)', + 'Serbian (Latin, Unicode)': 'RS (Latinunicode)', + 'Serbian (Latin, with guillemets)': 'RS (Latinalternatequotes)', + 'Serbian (Latin)': 'RS (Latin)', + 'Serbian (Russia)': 'RU (Srp)', + 'Serbo-Croatian (US)': 'US (Hbs)', + Shan: 'MM (Shn)', + 'Shan (Zawgyi Tai)': 'MM (Zgt)', + Sicilian: 'IT (Scn)', + Silesian: 'PL (Szl)', + Sindhi: 'PK (Snd)', + 'Sinhala (phonetic)': 'LK', + 'Sinhala (US)': 'LK (Us)', + Slovak: 'SK', + 'Slovak (extended backslash)': 'SK (Bksl)', + 'Slovak (QWERTY, extended backslash)': 'SK (Qwerty Bksl)', + 'Slovak (QWERTY)': 'SK (Qwerty)', + Slovenian: 'SI', + 'Slovenian (US)': 'SI (Us)', + 'Slovenian (with guillemets)': 'SI (Alternatequotes)', + Spanish: 'ES', + 'Spanish (dead tilde)': 'ES (Deadtilde)', + 'Spanish (Dvorak)': 'ES (Dvorak)', + 'Spanish (Latin American, Colemak)': 'LATAM (Colemak)', + 'Spanish (Latin American, dead tilde)': 'LATAM (Deadtilde)', + 'Spanish (Latin American, Dvorak)': 'LATAM (Dvorak)', + 'Spanish (Latin American, no dead keys)': 'LATAM (Nodeadkeys)', + 'Spanish (Latin American)': 'LATAM', + 'Spanish (Macintosh)': 'ES (Mac)', + 'Spanish (no dead keys)': 'ES (Nodeadkeys)', + 'Spanish (Windows)': 'ES (Winkeys)', + 'Swahili (Kenya)': 'KE', + 'Swahili (Tanzania)': 'TZ', + Swedish: 'SE', + 'Swedish (Dvorak, intl.)': 'SE (Us Dvorak)', + 'Swedish (Dvorak)': 'SE (Dvorak)', + 'Swedish (Macintosh)': 'SE (Mac)', + 'Swedish (no dead keys)': 'SE (Nodeadkeys)', + 'Swedish (Svdvorak)': 'SE (Svdvorak)', + 'Swedish (US)': 'SE (Us)', + 'Swedish Sign Language': 'SE (Swl)', + Syriac: 'SY (Syc)', + 'Syriac (phonetic)': 'SY (Syc Phonetic)', + Taiwanese: 'TW', + 'Taiwanese (indigenous)': 'TW (Indigenous)', + Tajik: 'TJ', + 'Tajik (legacy)': 'TJ (Legacy)', + 'Tamil (InScript, with Arabic numerals)': 'IN (Tam)', + 'Tamil (InScript, with Tamil numerals)': 'IN (Tam Tamilnumbers)', + "Tamil (Sri Lanka, TamilNet '99, TAB encoding)": 'LK (Tam TAB)', + "Tamil (Sri Lanka, TamilNet '99)": 'LK (Tam Unicode)', + "Tamil (TamilNet '99 with Tamil numerals)": 'IN (Tamilnet Tamilnumbers)', + "Tamil (TamilNet '99, TAB encoding)": 'IN (Tamilnet TAB)', + "Tamil (TamilNet '99, TSCII encoding)": 'IN (Tamilnet TSCII)', + "Tamil (TamilNet '99)": 'IN (Tamilnet)', + Tarifit: 'MA (Rif)', + Tatar: 'RU (Tt)', + Telugu: 'IN (Tel)', + 'Telugu (KaGaPa, phonetic)': 'IN (Tel-Kagapa)', + 'Telugu (Sarala)': 'IN (Tel-Sarala)', + Thai: 'TH', + 'Thai (Pattachote)': 'TH (Pat)', + 'Thai (TIS-820.2538)': 'TH (Tis)', + Tibetan: 'CN (Tib)', + 'Tibetan (with ASCII numerals)': 'CN (Tib Asciinum)', + Tswana: 'BW', + Turkish: 'TR', + 'Turkish (Alt-Q)': 'TR (Alt)', + 'Turkish (E)': 'TR (E)', + 'Turkish (F)': 'TR (F)', + 'Turkish (Germany)': 'DE (Tr)', + 'Turkish (intl., with dead keys)': 'TR (Intl)', + Turkmen: 'TM', + 'Turkmen (Alt-Q)': 'TM (Alt)', + Udmurt: 'RU (Udm)', + Ukrainian: 'UA', + 'Ukrainian (homophonic)': 'UA (Homophonic)', + 'Ukrainian (legacy)': 'UA (Legacy)', + 'Ukrainian (macOS)': 'UA (MacOS)', + 'Ukrainian (phonetic)': 'UA (Phonetic)', + 'Ukrainian (standard RSTU)': 'UA (Rstu)', + 'Ukrainian (typewriter)': 'UA (Typewriter)', + 'Ukrainian (Windows)': 'UA (Winkeys)', + 'Urdu (alt. phonetic)': 'IN (Urd-Phonetic3)', + 'Urdu (Pakistan, CRULP)': 'PK (Urd-Crulp)', + 'Urdu (Pakistan, NLA)': 'PK (Urd-Nla)', + 'Urdu (Pakistan)': 'PK', + 'Urdu (phonetic)': 'IN (Urd-Phonetic)', + 'Urdu (Windows)': 'IN (Urd-Winkeys)', + Uyghur: 'CN (Ug)', + Uzbek: 'UZ', + 'Uzbek (Afghanistan, OLPC)': 'AF (Uz-Olpc)', + 'Uzbek (Afghanistan)': 'AF (Uz)', + 'Uzbek (Latin)': 'UZ (Latin)', + Vietnamese: 'VN', + 'Vietnamese (France)': 'VN (Fr)', + 'Vietnamese (US)': 'VN (Us)', + Wolof: 'SN', + Yakut: 'RU (Sah)', + Yoruba: 'NG (Yoruba)', + 'Unknown Layout': 'Unknown', +} as const; diff --git a/customModules/module.ts b/customModules/module.ts index 1578334..5438ade 100644 --- a/customModules/module.ts +++ b/customModules/module.ts @@ -1,8 +1,9 @@ -import { Module } from "lib/types/bar"; -import options from "options"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import { Binding } from "types/service"; -import { Variable as VariableType } from "types/variable"; +import { BarBoxChild, Module } from 'lib/types/bar'; +import { BarButtonStyles } from 'lib/types/options'; +import { Bind } from 'lib/types/variable'; +import { GtkWidget } from 'lib/types/widget'; +import options from 'options'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; const { style } = options.theme.bar.buttons; @@ -15,68 +16,69 @@ export const module = ({ tooltipText, boxClass, props = {}, - showLabelBinding = undefinedVar.bind("value"), + showLabelBinding = undefinedVar.bind('value'), showLabel, labelHook, - hook -}: Module) => { - const getIconWidget = () => { + hook, +}: Module): BarBoxChild => { + const getIconWidget = (): GtkWidget | undefined => { let iconWidget: Gtk.Widget | undefined; if (icon !== undefined) { iconWidget = Widget.Icon({ class_name: `txt-icon bar-button-icon module-icon ${boxClass}`, - icon: icon + icon: icon, }) as unknown as Gtk.Widget; } else if (textIcon !== undefined) { iconWidget = Widget.Label({ class_name: `txt-icon bar-button-icon module-icon ${boxClass}`, - label: textIcon + label: textIcon, }) as unknown as Gtk.Widget; } return iconWidget; - } + }; return { component: Widget.Box({ - className: Utils.merge([style.bind("value"), showLabelBinding], (style: string, shwLabel: boolean) => { - const shouldShowLabel = shwLabel || showLabel; - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `${boxClass} ${styleMap[style]} ${!shouldShowLabel ? "no-label" : ""}`; - }), + className: Utils.merge( + [style.bind('value'), showLabelBinding], + (style: BarButtonStyles, shwLabel: boolean) => { + const shouldShowLabel = shwLabel || showLabel; + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `${boxClass} ${styleMap[style]} ${!shouldShowLabel ? 'no-label' : ''}`; + }, + ), tooltip_text: tooltipText, - children: Utils.merge( - [showLabelBinding], - (showLabelBinding): Gtk.Widget[] => { - const childrenArray: Gtk.Widget[] = []; - const iconWidget = getIconWidget(); + children: Utils.merge([showLabelBinding], (showLabelBinding): Gtk.Widget[] => { + const childrenArray: Gtk.Widget[] = []; + const iconWidget = getIconWidget(); - if (iconWidget !== undefined) { - childrenArray.push(iconWidget); - } - - if (showLabelBinding) { - childrenArray.push( - Widget.Label({ - class_name: `bar-button-label module-label ${boxClass}`, - label: label, - setup: labelHook, - }) as unknown as Gtk.Widget - ); - } - return childrenArray; + if (iconWidget !== undefined) { + childrenArray.push(iconWidget); } - ) as Binding, any, Gtk.Widget[]>, + + if (showLabelBinding) { + childrenArray.push( + Widget.Label({ + class_name: `bar-button-label module-label ${boxClass}`, + label: label, + setup: labelHook, + }) as unknown as Gtk.Widget, + ); + } + return childrenArray; + }) as Bind, setup: hook, }), tooltip_text: tooltipText, isVisible: true, boxClass, - props + props, }; }; diff --git a/customModules/netstat/computeNetwork.ts b/customModules/netstat/computeNetwork.ts index 94e4103..e1b2dcd 100644 --- a/customModules/netstat/computeNetwork.ts +++ b/customModules/netstat/computeNetwork.ts @@ -71,7 +71,11 @@ const getNetworkUsage = (interfaceName: string = ''): NetworkUsage => { return { name: '', rx: 0, tx: 0 }; }; -export const computeNetwork = (round: VariableType, interfaceNameVar: VariableType, dataType: VariableType): NetworkResourceData => { +export const computeNetwork = ( + round: VariableType, + interfaceNameVar: VariableType, + dataType: VariableType, +): NetworkResourceData => { const rateUnit = dataType.value; const interfaceName = interfaceNameVar ? interfaceNameVar.value : ''; diff --git a/customModules/netstat/index.ts b/customModules/netstat/index.ts index ea80354..fce6f1c 100644 --- a/customModules/netstat/index.ts +++ b/customModules/netstat/index.ts @@ -2,7 +2,7 @@ import options from 'options'; import { module } from '../module'; import { inputHandler } from 'customModules/utils'; import { computeNetwork } from './computeNetwork'; -import { NetstatLabelType } from 'lib/types/bar'; +import { Module, NetstatLabelType } from 'lib/types/bar'; import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; import Button from 'types/widgets/button'; import { NetworkResourceData } from 'lib/types/customModules/network'; @@ -23,9 +23,7 @@ const { pollingInterval, } = options.bar.customModules.netstat; -export const networkUsage = Variable( - GET_DEFAULT_NETSTAT_DATA(rateUnit.value), -); +export const networkUsage = Variable(GET_DEFAULT_NETSTAT_DATA(rateUnit.value)); pollVariable( // Variable to poll and update with the result of the function passed in @@ -48,11 +46,8 @@ pollVariable( rateUnit, ); -export const Netstat = () => { - const renderNetworkLabel = ( - lblType: NetstatLabelType, - network: NetworkResourceData, - ): string => { +export const Netstat = (): Module => { + const renderNetworkLabel = (lblType: NetstatLabelType, network: NetworkResourceData): string => { switch (lblType) { case 'in': return `↓ ${network.in}`; @@ -88,19 +83,17 @@ export const Netstat = () => { }, onScrollUp: { fn: () => { - labelType.value = - NETWORK_LABEL_TYPES[ + labelType.value = NETWORK_LABEL_TYPES[ (NETWORK_LABEL_TYPES.indexOf(labelType.value) + 1) % NETWORK_LABEL_TYPES.length - ] as NetstatLabelType; + ] as NetstatLabelType; }, }, onScrollDown: { fn: () => { - labelType.value = - NETWORK_LABEL_TYPES[ + labelType.value = NETWORK_LABEL_TYPES[ (NETWORK_LABEL_TYPES.indexOf(labelType.value) - 1 + NETWORK_LABEL_TYPES.length) % - NETWORK_LABEL_TYPES.length - ] as NetstatLabelType; + NETWORK_LABEL_TYPES.length + ] as NetstatLabelType; }, }, }); @@ -110,4 +103,3 @@ export const Netstat = () => { return netstatModule; }; - diff --git a/customModules/power/index.ts b/customModules/power/index.ts index 5cbb575..2170571 100644 --- a/customModules/power/index.ts +++ b/customModules/power/index.ts @@ -1,24 +1,18 @@ -import options from "options"; -import { module } from "../module" +import options from 'options'; +import { module } from '../module'; -import { inputHandler } from "customModules/utils"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import Button from "types/widgets/button"; +import { inputHandler } from 'customModules/utils'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Button from 'types/widgets/button'; +import { Module } from 'lib/types/bar'; -const { - icon, - leftClick, - rightClick, - middleClick, - scrollUp, - scrollDown, -} = options.bar.customModules.power; +const { icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.power; -export const Power = () => { +export const Power = (): Module => { const powerModule = module({ - tooltipText: "Power Menu", - textIcon: icon.bind("value"), - boxClass: "powermodule", + tooltipText: 'Power Menu', + textIcon: icon.bind('value'), + boxClass: 'powermodule', props: { setup: (self: Button) => { inputHandler(self, { @@ -43,4 +37,4 @@ export const Power = () => { }); return powerModule; -} +}; diff --git a/customModules/ram/computeRam.ts b/customModules/ram/computeRam.ts index 8e8a03e..b8026c2 100644 --- a/customModules/ram/computeRam.ts +++ b/customModules/ram/computeRam.ts @@ -1,9 +1,10 @@ const GLib = imports.gi.GLib; import { divide } from 'customModules/utils'; +import { GenericResourceData } from 'lib/types/customModules/generic'; import { Variable as VariableType } from 'types/variable'; -export const calculateRamUsage = (round: VariableType) => { +export const calculateRamUsage = (round: VariableType): GenericResourceData => { try { const [success, meminfoBytes] = GLib.file_get_contents('/proc/meminfo'); @@ -26,17 +27,14 @@ export const calculateRamUsage = (round: VariableType) => { let usedRam = totalRamInBytes - availableRamInBytes; usedRam = isNaN(usedRam) || usedRam < 0 ? 0 : usedRam; - return { percentage: divide([totalRamInBytes, usedRam], round.value), total: totalRamInBytes, used: usedRam, free: availableRamInBytes, }; - } catch (error) { console.error('Error calculating RAM usage:', error); - return { total: 0, used: 0, percentage: 0 }; + return { total: 0, used: 0, percentage: 0, free: 0 }; } }; - diff --git a/customModules/ram/index.ts b/customModules/ram/index.ts index e479d78..58fb075 100644 --- a/customModules/ram/index.ts +++ b/customModules/ram/index.ts @@ -1,62 +1,48 @@ -import options from "options"; +import options from 'options'; // Module initializer -import { module } from "../module" +import { module } from '../module'; // Types -import { GenericResourceData } from "lib/types/customModules/generic"; -import Button from "types/widgets/button"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; +import { GenericResourceData } from 'lib/types/customModules/generic'; +import Button from 'types/widgets/button'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; // Helper Methods -import { calculateRamUsage } from "./computeRam"; +import { calculateRamUsage } from './computeRam'; // Utility Methods -import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils"; -import { ResourceLabelType } from "lib/types/bar"; +import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils'; +import { Module, ResourceLabelType } from 'lib/types/bar'; // Global Constants -import { LABEL_TYPES } from "lib/types/defaults/bar"; -import { pollVariable } from "customModules/PollVar"; +import { LABEL_TYPES } from 'lib/types/defaults/bar'; +import { pollVariable } from 'customModules/PollVar'; // All the user configurable options for the ram module that are needed -const { - label, - labelType, - round, - leftClick, - rightClick, - middleClick, - pollingInterval -} = options.bar.customModules.ram; +const { label, labelType, round, leftClick, rightClick, middleClick, pollingInterval } = options.bar.customModules.ram; const defaultRamData: GenericResourceData = { total: 0, used: 0, percentage: 0, free: 0 }; -const ramUsage = Variable(defaultRamData); +const ramUsage = Variable(defaultRamData); -pollVariable( - ramUsage, - [round.bind('value')], - pollingInterval.bind('value'), - calculateRamUsage, - round, -); - -export const Ram = () => { +pollVariable(ramUsage, [round.bind('value')], pollingInterval.bind('value'), calculateRamUsage, round); +export const Ram = (): Module => { const ramModule = module({ - textIcon: "", + textIcon: '', label: Utils.merge( - [ramUsage.bind("value"), labelType.bind("value"), round.bind("value")], + [ramUsage.bind('value'), labelType.bind('value'), round.bind('value')], (rmUsg: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => { const returnValue = renderResourceLabel(lblTyp, rmUsg, round); return returnValue; - }), - tooltipText: labelType.bind("value").as(lblTyp => { + }, + ), + tooltipText: labelType.bind('value').as((lblTyp) => { return formatTooltip('RAM', lblTyp); }), - boxClass: "ram", - showLabelBinding: label.bind("value"), + boxClass: 'ram', + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -71,18 +57,22 @@ export const Ram = () => { }, onScrollUp: { fn: () => { - labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType; - } + labelType.value = LABEL_TYPES[ + (LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length + ] as ResourceLabelType; + }, }, onScrollDown: { fn: () => { - labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType; - } + labelType.value = LABEL_TYPES[ + (LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length + ] as ResourceLabelType; + }, }, }); }, - } + }, }); return ramModule; -} +}; diff --git a/customModules/storage/computeStorage.ts b/customModules/storage/computeStorage.ts index 17e11fd..49cbc14 100644 --- a/customModules/storage/computeStorage.ts +++ b/customModules/storage/computeStorage.ts @@ -1,23 +1,20 @@ -// @ts-expect-error +// @ts-expect-error is a special directive that tells the compiler to use the GTop library import GTop from 'gi://GTop'; import { divide } from 'customModules/utils'; import { Variable as VariableType } from 'types/variable'; +import { GenericResourceData } from 'lib/types/customModules/generic'; -let previousFsUsage = new GTop.glibtop_fsusage(); - -export const computeStorage = (round: VariableType) => { +export const computeStorage = (round: VariableType): GenericResourceData => { try { const currentFsUsage = new GTop.glibtop_fsusage(); - GTop.glibtop_get_fsusage(currentFsUsage, "/"); + GTop.glibtop_get_fsusage(currentFsUsage, '/'); const total = currentFsUsage.blocks * currentFsUsage.block_size; const available = currentFsUsage.bavail * currentFsUsage.block_size; const used = total - available; - previousFsUsage = currentFsUsage; - return { total, used, @@ -26,7 +23,6 @@ export const computeStorage = (round: VariableType) => { }; } catch (error) { console.error('Error calculating RAM usage:', error); - return { total: 0, used: 0, percentage: 0 }; + return { total: 0, used: 0, percentage: 0, free: 0 }; } }; - diff --git a/customModules/storage/index.ts b/customModules/storage/index.ts index 2188c45..eed1f01 100644 --- a/customModules/storage/index.ts +++ b/customModules/storage/index.ts @@ -1,52 +1,38 @@ -import options from "options"; -import { module } from "../module" +import options from 'options'; +import { module } from '../module'; -import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils"; -import { computeStorage } from "./computeStorage"; -import { ResourceLabelType } from "lib/types/bar"; -import { GenericResourceData } from "lib/types/customModules/generic"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import Button from "types/widgets/button"; -import { LABEL_TYPES } from "lib/types/defaults/bar"; -import { pollVariable } from "customModules/PollVar"; +import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils'; +import { computeStorage } from './computeStorage'; +import { Module, ResourceLabelType } from 'lib/types/bar'; +import { GenericResourceData } from 'lib/types/customModules/generic'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Button from 'types/widgets/button'; +import { LABEL_TYPES } from 'lib/types/defaults/bar'; +import { pollVariable } from 'customModules/PollVar'; -const { - label, - labelType, - icon, - round, - leftClick, - rightClick, - middleClick, - pollingInterval -} = options.bar.customModules.storage; +const { label, labelType, icon, round, leftClick, rightClick, middleClick, pollingInterval } = + options.bar.customModules.storage; const defaultStorageData = { total: 0, used: 0, percentage: 0, free: 0 }; -const storageUsage = Variable(defaultStorageData); +const storageUsage = Variable(defaultStorageData); -pollVariable( - storageUsage, - [round.bind('value')], - pollingInterval.bind('value'), - computeStorage, - round, -); +pollVariable(storageUsage, [round.bind('value')], pollingInterval.bind('value'), computeStorage, round); -export const Storage = () => { +export const Storage = (): Module => { const storageModule = module({ - textIcon: icon.bind("value"), + textIcon: icon.bind('value'), label: Utils.merge( - [storageUsage.bind("value"), labelType.bind("value"), round.bind("value")], + [storageUsage.bind('value'), labelType.bind('value'), round.bind('value')], (storage: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => { return renderResourceLabel(lblTyp, storage, round); - }), - tooltipText: labelType.bind("value").as(lblTyp => { + }, + ), + tooltipText: labelType.bind('value').as((lblTyp) => { return formatTooltip('Storage', lblTyp); - }), - boxClass: "storage", - showLabelBinding: label.bind("value"), + boxClass: 'storage', + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -61,18 +47,22 @@ export const Storage = () => { }, onScrollUp: { fn: () => { - labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType; - } + labelType.value = LABEL_TYPES[ + (LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length + ] as ResourceLabelType; + }, }, onScrollDown: { fn: () => { - labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType; - } + labelType.value = LABEL_TYPES[ + (LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length + ] as ResourceLabelType; + }, }, }); }, - } + }, }); return storageModule; -} +}; diff --git a/customModules/theme.ts b/customModules/theme.ts index b3b025c..3e3008f 100644 --- a/customModules/theme.ts +++ b/customModules/theme.ts @@ -1,106 +1,146 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, GtkWidget } from 'lib/types/widget'; -export const CustomModuleTheme = () => { +export const CustomModuleTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page customModules paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page customModules paged-container', child: Widget.Box({ - class_name: "bar-theme-page paged-container", + class_name: 'bar-theme-page paged-container', vertical: true, children: [ Header('RAM'), Option({ opt: options.theme.bar.buttons.modules.ram.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.ram.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.ram.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.ram.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.ram.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('CPU'), Option({ opt: options.theme.bar.buttons.modules.cpu.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.cpu.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.cpu.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.cpu.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.cpu.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Storage'), Option({ opt: options.theme.bar.buttons.modules.storage.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.storage.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.storage.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.storage.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.storage.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Netstat'), Option({ opt: options.theme.bar.buttons.modules.netstat.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.netstat.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.netstat.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.netstat.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.netstat.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Keyboard Layout'), Option({ opt: options.theme.bar.buttons.modules.kbLayout.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.kbLayout.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.kbLayout.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.kbLayout.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.kbLayout.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Updates'), Option({ opt: options.theme.bar.buttons.modules.updates.text, title: 'Text', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.updates.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.updates.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.updates.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.updates.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Weather'), Option({ opt: options.theme.bar.buttons.modules.weather.icon, title: 'Icon', type: 'color' }), Option({ opt: options.theme.bar.buttons.modules.weather.text, title: 'Text', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.weather.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.weather.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.weather.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Power'), Option({ opt: options.theme.bar.buttons.modules.power.icon, title: 'Icon', type: 'color' }), - Option({ opt: options.theme.bar.buttons.modules.power.background, title: 'Label Background', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.modules.power.background, + title: 'Label Background', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.modules.power.icon_background, title: 'Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), - ] - }) - }) -} - - + ], + }), + }); +}; diff --git a/customModules/updates/index.ts b/customModules/updates/index.ts index 944a28e..c427fd3 100644 --- a/customModules/updates/index.ts +++ b/customModules/updates/index.ts @@ -1,11 +1,12 @@ -import options from "options"; -import { module } from "../module" +import options from 'options'; +import { module } from '../module'; -import { inputHandler } from "customModules/utils"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import Button from "types/widgets/button"; -import { Variable as VariableType } from "types/variable"; -import { pollVariableBash } from "customModules/PollVar"; +import { inputHandler } from 'customModules/utils'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Button from 'types/widgets/button'; +import { Variable as VariableType } from 'types/variable'; +import { pollVariableBash } from 'customModules/PollVar'; +import { Module } from 'lib/types/bar'; const { updateCommand, @@ -20,12 +21,12 @@ const { scrollDown, } = options.bar.customModules.updates; -const pendingUpdates: VariableType = Variable(" 0"); +const pendingUpdates: VariableType = Variable(' 0'); -const processUpdateCount = (updateCount: string) => { +const processUpdateCount = (updateCount: string): string => { if (!padZero.value) return updateCount; return `${updateCount.padStart(2, '0')}`; -} +}; pollVariableBash( pendingUpdates, @@ -35,13 +36,13 @@ pollVariableBash( processUpdateCount, ); -export const Updates = () => { +export const Updates = (): Module => { const updatesModule = module({ - textIcon: icon.bind("value"), - tooltipText: pendingUpdates.bind("value").as(v => `${v} updates available`), - boxClass: "updates", - label: pendingUpdates.bind("value"), - showLabelBinding: label.bind("value"), + textIcon: icon.bind('value'), + tooltipText: pendingUpdates.bind('value').as((v) => `${v} updates available`), + boxClass: 'updates', + label: pendingUpdates.bind('value'), + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -66,7 +67,4 @@ export const Updates = () => { }); return updatesModule; -} - - - +}; diff --git a/customModules/utils.ts b/customModules/utils.ts index 63bf4e1..20679ab 100644 --- a/customModules/utils.ts +++ b/customModules/utils.ts @@ -1,5 +1,8 @@ import { ResourceLabelType } from 'lib/types/bar'; -import { GenericResourceData } from 'lib/types/customModules/generic'; +import { GenericResourceData, Postfix } from 'lib/types/customModules/generic'; +import { InputHandlerEvents } from 'lib/types/customModules/utils'; +import { ThrottleFn, ThrottleFnCallback } from 'lib/types/utils'; +import { GtkWidget } from 'lib/types/widget'; import { Binding } from 'lib/utils'; import { openMenu } from 'modules/bar/utils'; import options from 'options'; @@ -12,14 +15,11 @@ const { scrollSpeed } = options.bar.customModules; export const runAsyncCommand = ( cmd: string, - fn: Function, - events: { clicked: any; event: Gdk.Event } + fn: (output: string) => void, + events: { clicked: Button; event: Gdk.Event }, ): void => { if (cmd.startsWith('menu:')) { - // if the command starts with 'menu:', then it is a menu command - // and we should App.toggleMenu("menuName") based on the input menu:menuName. Ignoring spaces and case const menuName = cmd.split(':')[1].trim().toLowerCase(); - openMenu(events.clicked, events.event, `${menuName}menu`); return; @@ -31,15 +31,10 @@ export const runAsyncCommand = ( fn(output); } }) - .catch((err) => - console.error(`Error running command "${cmd}": ${err})`) - ); + .catch((err) => console.error(`Error running command "${cmd}": ${err})`)); }; -export function throttle void>( - func: T, - limit: number -): T { +export function throttle(func: T, limit: number): T { let inThrottle: boolean; return function (this: ThisParameterType, ...args: Parameters) { if (!inThrottle) { @@ -52,31 +47,23 @@ export function throttle void>( } as T; } -export const throttledScrollHandler = (interval: number) => - throttle((cmd: string, fn: Function | undefined) => { +export const throttledScrollHandler = (interval: number): ThrottleFn => + throttle((cmd: string, fn: ThrottleFnCallback) => { Utils.execAsync(`bash -c "${cmd}"`) .then((output) => { if (fn !== undefined) { fn(output); } }) - .catch((err) => - console.error(`Error running command "${cmd}": ${err}`) - ); + .catch((err) => console.error(`Error running command "${cmd}": ${err}`)); }, 200 / interval); const dummyVar = Variable(''); export const inputHandler = ( self: Button, - { - onPrimaryClick, - onSecondaryClick, - onMiddleClick, - onScrollUp, - onScrollDown, - } -) => { + { onPrimaryClick, onSecondaryClick, onMiddleClick, onScrollUp, onScrollDown }: InputHandlerEvents, +): void => { const sanitizeInput = (input: VariableType): string => { if (input === undefined) { return ''; @@ -88,46 +75,25 @@ export const inputHandler = ( const interval = scrollSpeed.value; const throttledHandler = throttledScrollHandler(interval); - self.on_primary_click = (clicked: any, event: Gdk.Event) => - runAsyncCommand( - sanitizeInput(onPrimaryClick?.cmd || dummyVar), - onPrimaryClick.fn, - { clicked, event } - ); + self.on_primary_click = (clicked: Button, event: Gdk.Event): void => + runAsyncCommand(sanitizeInput(onPrimaryClick?.cmd || dummyVar), onPrimaryClick.fn, { clicked, event }); - self.on_secondary_click = (clicked: any, event: Gdk.Event) => - runAsyncCommand( - sanitizeInput(onSecondaryClick?.cmd || dummyVar), - onSecondaryClick.fn, - { clicked, event } - ); + self.on_secondary_click = (clicked: Button, event: Gdk.Event): void => + runAsyncCommand(sanitizeInput(onSecondaryClick?.cmd || dummyVar), onSecondaryClick.fn, { clicked, event }); - self.on_middle_click = (clicked: any, event: Gdk.Event) => - runAsyncCommand( - sanitizeInput(onMiddleClick?.cmd || dummyVar), - onMiddleClick.fn, - { clicked, event } - ); + self.on_middle_click = (clicked: Button, event: Gdk.Event): void => + runAsyncCommand(sanitizeInput(onMiddleClick?.cmd || dummyVar), onMiddleClick.fn, { clicked, event }); - self.on_scroll_up = () => - throttledHandler( - sanitizeInput(onScrollUp?.cmd || dummyVar), - onScrollUp.fn - ); + self.on_scroll_up = (): void => throttledHandler(sanitizeInput(onScrollUp?.cmd || dummyVar), onScrollUp.fn); - self.on_scroll_down = () => - throttledHandler( - sanitizeInput(onScrollDown?.cmd || dummyVar), - onScrollDown.fn - ); + self.on_scroll_down = (): void => + throttledHandler(sanitizeInput(onScrollDown?.cmd || dummyVar), onScrollDown.fn); }; // Initial setup of event handlers updateHandlers(); - const sanitizeVariable = ( - someVar: VariableType | undefined - ): Binding => { + const sanitizeVariable = (someVar: VariableType | undefined): Binding => { if (someVar === undefined || typeof someVar.bind !== 'function') { return dummyVar.bind('value'); } @@ -144,37 +110,36 @@ export const inputHandler = ( sanitizeVariable(onScrollUp), sanitizeVariable(onScrollDown), ], - updateHandlers + updateHandlers, ); }; -export const divide = ([total, used]: number[], round: boolean) => { +export const divide = ([total, used]: number[], round: boolean): number => { const percentageTotal = (used / total) * 100; if (round) { return total > 0 ? Math.round(percentageTotal) : 0; } return total > 0 ? parseFloat(percentageTotal.toFixed(2)) : 0; - }; -export const formatSizeInKiB = (sizeInBytes: number, round: boolean) => { - const sizeInGiB = sizeInBytes / (1024 ** 1); +export const formatSizeInKiB = (sizeInBytes: number, round: boolean): number => { + const sizeInGiB = sizeInBytes / 1024 ** 1; return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2)); }; -export const formatSizeInMiB = (sizeInBytes: number, round: boolean) => { - const sizeInGiB = sizeInBytes / (1024 ** 2); +export const formatSizeInMiB = (sizeInBytes: number, round: boolean): number => { + const sizeInGiB = sizeInBytes / 1024 ** 2; return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2)); }; -export const formatSizeInGiB = (sizeInBytes: number, round: boolean) => { - const sizeInGiB = sizeInBytes / (1024 ** 3); +export const formatSizeInGiB = (sizeInBytes: number, round: boolean): number => { + const sizeInGiB = sizeInBytes / 1024 ** 3; return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2)); }; -export const formatSizeInTiB = (sizeInBytes: number, round: boolean) => { - const sizeInGiB = sizeInBytes / (1024 ** 4); +export const formatSizeInTiB = (sizeInBytes: number, round: boolean): number => { + const sizeInGiB = sizeInBytes / 1024 ** 4; return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2)); }; -export const autoFormatSize = (sizeInBytes: number, round: boolean) => { +export const autoFormatSize = (sizeInBytes: number, round: boolean): number => { // auto convert to GiB, MiB, KiB, TiB, or bytes if (sizeInBytes >= 1024 ** 4) return formatSizeInTiB(sizeInBytes, round); if (sizeInBytes >= 1024 ** 3) return formatSizeInGiB(sizeInBytes, round); @@ -182,22 +147,18 @@ export const autoFormatSize = (sizeInBytes: number, round: boolean) => { if (sizeInBytes >= 1024 ** 1) return formatSizeInKiB(sizeInBytes, round); return sizeInBytes; -} +}; -export const getPostfix = (sizeInBytes: number) => { +export const getPostfix = (sizeInBytes: number): Postfix => { if (sizeInBytes >= 1024 ** 4) return 'TiB'; if (sizeInBytes >= 1024 ** 3) return 'GiB'; if (sizeInBytes >= 1024 ** 2) return 'MiB'; if (sizeInBytes >= 1024 ** 1) return 'KiB'; return 'B'; -} +}; -export const renderResourceLabel = ( - lblType: ResourceLabelType, - rmUsg: GenericResourceData, - round: boolean -) => { +export const renderResourceLabel = (lblType: ResourceLabelType, rmUsg: GenericResourceData, round: boolean): string => { const { used, total, percentage, free } = rmUsg; const formatFunctions = { @@ -205,7 +166,7 @@ export const renderResourceLabel = ( GiB: formatSizeInGiB, MiB: formatSizeInMiB, KiB: formatSizeInKiB, - B: (size: number, _: boolean) => size + B: (size: number): number => size, }; // Get them datas in proper GiB, MiB, KiB, TiB, or bytes @@ -217,20 +178,20 @@ export const renderResourceLabel = ( const formatUsed = formatFunctions[postfix] || formatFunctions['B']; const usedSizeFormatted = formatUsed(used, round); - if (lblType === "used/total") { + if (lblType === 'used/total') { return `${usedSizeFormatted}/${totalSizeFormatted} ${postfix}`; } - if (lblType === "used") { + if (lblType === 'used') { return `${autoFormatSize(used, round)} ${getPostfix(used)}`; } - if (lblType === "free") { + if (lblType === 'free') { return `${autoFormatSize(free, round)} ${getPostfix(free)}`; } return `${percentage}%`; }; -export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType) => { +export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType): string => { switch (lblTyp) { case 'used': return `Used ${dataType}`; @@ -243,4 +204,4 @@ export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType) => { default: return ''; } -} +}; diff --git a/customModules/weather/index.ts b/customModules/weather/index.ts index 2fa55e7..9cd4ff1 100644 --- a/customModules/weather/index.ts +++ b/customModules/weather/index.ts @@ -1,41 +1,30 @@ -import options from "options"; -import { module } from "../module" +import options from 'options'; +import { module } from '../module'; -import { inputHandler } from "customModules/utils"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0"; -import Button from "types/widgets/button"; -import { getWeatherStatusIcon, globalWeatherVar } from "globals/weather"; +import { inputHandler } from 'customModules/utils'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Button from 'types/widgets/button'; +import { getWeatherStatusTextIcon, globalWeatherVar } from 'globals/weather'; +import { Module } from 'lib/types/bar'; -const { - label, - unit, - leftClick, - rightClick, - middleClick, - scrollUp, - scrollDown, -} = options.bar.customModules.weather; +const { label, unit, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.weather; -export const Weather = () => { - - const networkModule = module({ - icon: Utils.merge([globalWeatherVar.bind("value")], (wthr) => { - const weatherStatusIcon = getWeatherStatusIcon(wthr); +export const Weather = (): Module => { + const weatherModule = module({ + textIcon: Utils.merge([globalWeatherVar.bind('value')], (wthr) => { + const weatherStatusIcon = getWeatherStatusTextIcon(wthr); return weatherStatusIcon; }), - tooltipText: globalWeatherVar.bind("value").as(v => `Weather Status: ${v.current.condition.text}`), - boxClass: "weather-custom", - label: Utils.merge( - [globalWeatherVar.bind("value"), unit.bind("value")], - (wthr, unt) => { - if (unt === "imperial") { - return `${Math.ceil(wthr.current.temp_f)}° F`; - } else { - return `${Math.ceil(wthr.current.temp_c)}° C`; - } - }, - ), - showLabelBinding: label.bind("value"), + tooltipText: globalWeatherVar.bind('value').as((v) => `Weather Status: ${v.current.condition.text}`), + boxClass: 'weather-custom', + label: Utils.merge([globalWeatherVar.bind('value'), unit.bind('value')], (wthr, unt) => { + if (unt === 'imperial') { + return `${Math.ceil(wthr.current.temp_f)}° F`; + } else { + return `${Math.ceil(wthr.current.temp_c)}° C`; + } + }), + showLabelBinding: label.bind('value'), props: { setup: (self: Button) => { inputHandler(self, { @@ -59,9 +48,5 @@ export const Weather = () => { }, }); - return networkModule; -} - - - - + return weatherModule; +}; diff --git a/directoryMonitorService.ts b/directoryMonitorService.ts index 911dc69..2e1a74a 100644 --- a/directoryMonitorService.ts +++ b/directoryMonitorService.ts @@ -1,8 +1,8 @@ -import Service from "resource:///com/github/Aylur/ags/service.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; -import { monitorFile } from "resource:///com/github/Aylur/ags/utils.js"; -import Gio from "gi://Gio"; -import { FileInfo } from "types/@girs/gio-2.0/gio-2.0.cjs"; +import Service from 'resource:///com/github/Aylur/ags/service.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import { monitorFile } from 'resource:///com/github/Aylur/ags/utils.js'; +import Gio from 'gi://Gio'; +import { FileInfo } from 'types/@girs/gio-2.0/gio-2.0.cjs'; class DirectoryMonitorService extends Service { static { @@ -14,23 +14,19 @@ class DirectoryMonitorService extends Service { this.recursiveDirectoryMonitor(`${App.configDir}/scss`); } - recursiveDirectoryMonitor(directoryPath: string) { + recursiveDirectoryMonitor(directoryPath: string): void { monitorFile(directoryPath, (_, eventType) => { if (eventType === Gio.FileMonitorEvent.CHANGES_DONE_HINT) { - this.emit("changed"); + this.emit('changed'); } }); const directory = Gio.File.new_for_path(directoryPath); - const enumerator = directory.enumerate_children( - "standard::*", - Gio.FileQueryInfoFlags.NONE, - null, - ); + const enumerator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); let fileInfo: FileInfo; while ((fileInfo = enumerator.next_file(null) as FileInfo) !== null) { - const childPath = directoryPath + "/" + fileInfo.get_name(); + const childPath = directoryPath + '/' + fileInfo.get_name(); if (fileInfo.get_file_type() === Gio.FileType.DIRECTORY) { this.recursiveDirectoryMonitor(childPath); } diff --git a/external/ags-types b/external/ags-types new file mode 160000 index 0000000..87b5046 --- /dev/null +++ b/external/ags-types @@ -0,0 +1 @@ +Subproject commit 87b5046791040615cd65b48a04be062662a46e36 diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..bc426d4 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,14 @@ +// globals.d.ts +/* eslint-disable no-var */ + +import { Options, Variable as VariableType } from 'types/variable'; + +declare global { + var globalMousePos: VariableType; + var useTheme: (filePath: string) => void; + var globalWeatherVar: VariableType; + var options: Options; + var removingNotifications: VariableType; +} + +export {}; diff --git a/globals/mousePos.ts b/globals/mousePos.ts index 7e127a3..390ee30 100644 --- a/globals/mousePos.ts +++ b/globals/mousePos.ts @@ -1,3 +1,5 @@ -const globalMousePosVar = Variable([0, 0]); +import { Variable as VariableType } from 'types/variable'; -globalThis["globalMousePos"] = globalMousePosVar; +const globalMousePosVar: VariableType = Variable([0, 0]); + +globalThis['globalMousePos'] = globalMousePosVar; diff --git a/globals/network.ts b/globals/network.ts new file mode 100644 index 0000000..44fbf77 --- /dev/null +++ b/globals/network.ts @@ -0,0 +1,15 @@ +export const WIFI_STATUS_MAP = { + unknown: 'Status Unknown', + unmanaged: 'Unmanaged', + unavailable: 'Unavailable', + disconnected: 'Disconnected', + prepare: 'Preparing Connecting', + config: 'Connecting', + need_auth: 'Needs Authentication', + ip_config: 'Requesting IP', + ip_check: 'Checking Access', + secondaries: 'Waiting on Secondaries', + activated: 'Connected', + deactivating: 'Disconnecting', + failed: 'Connection Failed', +} as const; diff --git a/globals/notification.ts b/globals/notification.ts new file mode 100644 index 0000000..1564029 --- /dev/null +++ b/globals/notification.ts @@ -0,0 +1,37 @@ +import icons from 'modules/icons/index'; +import { Notification } from 'types/service/notifications'; + +export const removingNotifications = Variable(false); + +export const getNotificationIcon = (app_name: string, app_icon: string, app_entry: string): string => { + let icon: string = icons.fallback.notification; + + if (Utils.lookUpIcon(app_name) || Utils.lookUpIcon(app_name.toLowerCase() || '')) { + icon = Utils.lookUpIcon(app_name) + ? app_name + : Utils.lookUpIcon(app_name.toLowerCase()) + ? app_name.toLowerCase() + : ''; + } + + if (Utils.lookUpIcon(app_icon) && icon === '') { + icon = app_icon; + } + + if (Utils.lookUpIcon(app_entry || '') && icon === '') { + icon = app_entry || ''; + } + + return icon; +}; + +export const closeNotifications = async (notifications: Notification[]): Promise => { + removingNotifications.value = true; + for (const notif of notifications) { + notif.close(); + await new Promise((resolve) => setTimeout(resolve, 100)); + } + removingNotifications.value = false; +}; + +globalThis['removingNotifications'] = removingNotifications; diff --git a/globals/useTheme.ts b/globals/useTheme.ts index eecf3ac..15c2322 100644 --- a/globals/useTheme.ts +++ b/globals/useTheme.ts @@ -1,10 +1,12 @@ -import Gio from "gi://Gio" -import { bash, Notify } from "lib/utils"; -import icons from "lib/icons" -import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from "widget/settings/shared/FileChooser"; +import Gio from 'gi://Gio'; +import { bash, Notify } from 'lib/utils'; +import icons from 'lib/icons'; +import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from 'widget/settings/shared/FileChooser'; + +export const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/; globalThis.useTheme = (filePath: string): void => { - let importedConfig = loadJsonFile(filePath); + const importedConfig = loadJsonFile(filePath); if (!importedConfig) { return; @@ -14,22 +16,22 @@ globalThis.useTheme = (filePath: string): void => { summary: `Importing Theme`, body: `Importing: ${filePath}`, iconName: icons.ui.info, - timeout: 7000 + timeout: 7000, }); - let tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`); - let optionsConfigFile = Gio.File.new_for_path(OPTIONS); + const tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`); + const optionsConfigFile = Gio.File.new_for_path(OPTIONS); - let [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null); - let [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null); + const [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null); + const [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null); if (!tmpSuccess || !optionsSuccess) { - console.error("Failed to read existing configuration files."); + console.error('Failed to read existing configuration files.'); return; } - let tmpConfig = JSON.parse(new TextDecoder("utf-8").decode(tmpContent)); - let optionsConfig = JSON.parse(new TextDecoder("utf-8").decode(optionsContent)); + let tmpConfig = JSON.parse(new TextDecoder('utf-8').decode(tmpContent)); + let optionsConfig = JSON.parse(new TextDecoder('utf-8').decode(optionsContent)); const filteredConfig = filterConfigForThemeOnly(importedConfig); tmpConfig = { ...tmpConfig, ...filteredConfig }; @@ -37,6 +39,5 @@ globalThis.useTheme = (filePath: string): void => { saveConfigToFile(tmpConfig, `${TMP}/config.json`); saveConfigToFile(optionsConfig, OPTIONS); - bash("pkill ags && ags"); -} - + bash('pkill ags && ags'); +}; diff --git a/globals/variables.ts b/globals/variables.ts new file mode 100644 index 0000000..721ddab --- /dev/null +++ b/globals/variables.ts @@ -0,0 +1,13 @@ +import { Opt } from 'lib/option'; +import { HexColor, RecursiveOptionsObject } from 'lib/types/options'; + +export const isOpt = (value: unknown): value is Opt => + typeof value === 'object' && value !== null && 'value' in value && value instanceof Opt; + +export const isRecursiveOptionsObject = (value: unknown): value is RecursiveOptionsObject => { + return typeof value === 'object' && value !== null && !Array.isArray(value); +}; + +export const isHexColor = (value: string): value is HexColor => { + return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value); +}; diff --git a/globals/weather.ts b/globals/weather.ts index cd86084..3da7ba1 100644 --- a/globals/weather.ts +++ b/globals/weather.ts @@ -1,9 +1,8 @@ -import options from "options"; -import { UnitType, Weather } from "lib/types/weather.js"; -import { DEFAULT_WEATHER } from "lib/types/defaults/weather.js"; -import GLib from "gi://GLib?version=2.0" - -import icons from "../modules/icons/index.js"; +import options from 'options'; +import { UnitType, Weather, WeatherIconTitle, WeatherIcon } from 'lib/types/weather.js'; +import { DEFAULT_WEATHER } from 'lib/types/defaults/weather.js'; +import GLib from 'gi://GLib?version=2.0'; +import { weatherIcons } from 'modules/icons/weather.js'; const { key, interval, location } = options.menus.clock.weather; @@ -11,12 +10,12 @@ export const globalWeatherVar = Variable(DEFAULT_WEATHER); let weatherIntervalInstance: null | number = null; -const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: string) => { +const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: string): void => { if (weatherIntervalInstance !== null) { GLib.source_remove(weatherIntervalInstance); } - const formattedLocation = loc.replace(" ", "%20"); + const formattedLocation = loc.replace(' ', '%20'); weatherIntervalInstance = Utils.interval(weatherInterval, () => { Utils.execAsync( @@ -24,17 +23,17 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str ) .then((res) => { try { - if (typeof res !== "string") { - return globalWeatherVar.value = DEFAULT_WEATHER; + if (typeof res !== 'string') { + return (globalWeatherVar.value = DEFAULT_WEATHER); } const parsedWeather = JSON.parse(res); - if (Object.keys(parsedWeather).includes("error")) { - return globalWeatherVar.value = DEFAULT_WEATHER; + if (Object.keys(parsedWeather).includes('error')) { + return (globalWeatherVar.value = DEFAULT_WEATHER); } - return globalWeatherVar.value = parsedWeather; + return (globalWeatherVar.value = parsedWeather); } catch (error) { globalWeatherVar.value = DEFAULT_WEATHER; console.warn(`Failed to parse weather data: ${error}`); @@ -44,70 +43,80 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str console.error(`Failed to fetch weather: ${err}`); globalWeatherVar.value = DEFAULT_WEATHER; }); - }) + }); }; -Utils.merge([key.bind("value"), interval.bind("value"), location.bind("value")], (weatherKey, weatherInterval, loc) => { +Utils.merge([key.bind('value'), interval.bind('value'), location.bind('value')], (weatherKey, weatherInterval, loc) => { if (!weatherKey) { - return globalWeatherVar.value = DEFAULT_WEATHER; + return (globalWeatherVar.value = DEFAULT_WEATHER); } weatherIntervalFn(weatherInterval, loc, weatherKey); }); -export const getTemperature = (wthr: Weather, unt: UnitType) => { - if (unt === "imperial") { +export const getTemperature = (wthr: Weather, unt: UnitType): string => { + if (unt === 'imperial') { return `${Math.ceil(wthr.current.temp_f)}° F`; } else { return `${Math.ceil(wthr.current.temp_c)}° C`; } }; -export const getWeatherIcon = (fahren: number) => { +export const getWeatherIcon = (fahren: number): Record => { const icons = { - 100: "", - 75: "", - 50: "", - 25: "", - 0: "", - }; + 100: '', + 75: '', + 50: '', + 25: '', + 0: '', + } as const; const colors = { - 100: "weather-color red", - 75: "weather-color orange", - 50: "weather-color lavender", - 25: "weather-color blue", - 0: "weather-color sky", - }; + 100: 'weather-color red', + 75: 'weather-color orange', + 50: 'weather-color lavender', + 25: 'weather-color blue', + 0: 'weather-color sky', + } as const; - const threshold = - fahren < 0 - ? 0 - : [100, 75, 50, 25, 0].find((threshold) => threshold <= fahren); + type IconKeys = keyof typeof icons; + + const threshold: IconKeys = + fahren < 0 ? 0 : ([100, 75, 50, 25, 0] as IconKeys[]).find((threshold) => threshold <= fahren) || 0; + + const icon = icons[threshold || 50]; + const color = colors[threshold || 50]; return { - icon: icons[threshold || 50], - color: colors[threshold || 50], + icon, + color, }; }; -export const getWindConditions = (wthr: Weather, unt: UnitType) => { - if (unt === "imperial") { +export const getWindConditions = (wthr: Weather, unt: UnitType): string => { + if (unt === 'imperial') { return `${Math.floor(wthr.current.wind_mph)} mph`; } return `${Math.floor(wthr.current.wind_kph)} kph`; -} - -export const getRainChance = (wthr: Weather) => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`; - - -export const getWeatherStatusIcon = (wthr: Weather) => { - let iconQuery = wthr.current.condition.text - .trim() - .toLowerCase() - .replaceAll(" ", "_"); - - if (!wthr.current.is_day && iconQuery === "partly_cloudy") { - iconQuery = "partly_cloudy_night"; - } - return icons.weather[iconQuery]; }; -globalThis["globalWeatherVar"] = globalWeatherVar; + +export const getRainChance = (wthr: Weather): string => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`; + +export const isValidWeatherIconTitle = (title: string): title is WeatherIconTitle => { + return title in weatherIcons; +}; + +export const getWeatherStatusTextIcon = (wthr: Weather): WeatherIcon => { + let iconQuery = wthr.current.condition.text.trim().toLowerCase().replaceAll(' ', '_'); + + if (!wthr.current.is_day && iconQuery === 'partly_cloudy') { + iconQuery = 'partly_cloudy_night'; + } + + if (isValidWeatherIconTitle(iconQuery)) { + return weatherIcons[iconQuery]; + } else { + console.warn(`Unknown weather icon title: ${iconQuery}`); + return weatherIcons['warning']; + } +}; + +globalThis['globalWeatherVar'] = globalWeatherVar; diff --git a/globals/window.ts b/globals/window.ts new file mode 100644 index 0000000..ae823bb --- /dev/null +++ b/globals/window.ts @@ -0,0 +1,10 @@ +export const WINDOW_LAYOUTS: string[] = [ + 'center', + 'top', + 'top-right', + 'top-center', + 'top-left', + 'bottom-left', + 'bottom-center', + 'bottom-right', +]; diff --git a/lib/constants/colors.ts b/lib/constants/colors.ts new file mode 100644 index 0000000..95ac5cf --- /dev/null +++ b/lib/constants/colors.ts @@ -0,0 +1,142 @@ +export const namedColors = new Set([ + 'alice blue', + 'antique white', + 'aqua', + 'aquamarine', + 'azure', + 'beige', + 'bisque', + 'black', + 'blanched almond', + 'blue', + 'blue violet', + 'brown', + 'burlywood', + 'cadet blue', + 'chartreuse', + 'chocolate', + 'coral', + 'cornflower blue', + 'cornsilk', + 'crimson', + 'cyan', + 'dark blue', + 'dark cyan', + 'dark goldenrod', + 'dark gray', + 'dark green', + 'dark khaki', + 'dark magenta', + 'dark olive green', + 'dark orange', + 'dark orchid', + 'dark red', + 'dark salmon', + 'dark sea green', + 'dark slate blue', + 'dark slate gray', + 'dark turquoise', + 'dark violet', + 'deep pink', + 'deep sky blue', + 'dim gray', + 'dodger blue', + 'firebrick', + 'floral white', + 'forest green', + 'fuchsia', + 'gainsboro', + 'ghost white', + 'gold', + 'goldenrod', + 'gray', + 'green', + 'green yellow', + 'honeydew', + 'hot pink', + 'indian red', + 'indigo', + 'ivory', + 'khaki', + 'lavender', + 'lavender blush', + 'lawn green', + 'lemon chiffon', + 'light blue', + 'light coral', + 'light cyan', + 'light goldenrod yellow', + 'light green', + 'light grey', + 'light pink', + 'light salmon', + 'light sea green', + 'light sky blue', + 'light slate gray', + 'light steel blue', + 'light yellow', + 'lime', + 'lime green', + 'linen', + 'magenta', + 'maroon', + 'medium aquamarine', + 'medium blue', + 'medium orchid', + 'medium purple', + 'medium sea green', + 'medium slate blue', + 'medium spring green', + 'medium turquoise', + 'medium violet red', + 'midnight blue', + 'mint cream', + 'misty rose', + 'moccasin', + 'navajo white', + 'navy', + 'old lace', + 'olive', + 'olive drab', + 'orange', + 'orange red', + 'orchid', + 'pale goldenrod', + 'pale green', + 'pale turquoise', + 'pale violet red', + 'papaya whip', + 'peach puff', + 'peru', + 'pink', + 'plum', + 'powder blue', + 'purple', + 'red', + 'rosy brown', + 'royal blue', + 'saddle brown', + 'salmon', + 'sandy brown', + 'sea green', + 'seashell', + 'sienna', + 'silver', + 'sky blue', + 'slate blue', + 'slate gray', + 'snow', + 'spring green', + 'steel blue', + 'tan', + 'teal', + 'thistle', + 'tomato', + 'turquoise', + 'violet', + 'wheat', + 'white', + 'white smoke', + 'yellow', + 'yellow green', +]); diff --git a/lib/icons.ts b/lib/icons.ts index f6da697..8813940 100644 --- a/lib/icons.ts +++ b/lib/icons.ts @@ -1,145 +1,145 @@ export const substitutes = { - "transmission-gtk": "transmission", - "blueberry.py": "blueberry", - "Caprine": "facebook-messenger", - "com.raggesilver.BlackBox-symbolic": "terminal-symbolic", - "org.wezfurlong.wezterm-symbolic": "terminal-symbolic", - "audio-headset-bluetooth": "audio-headphones-symbolic", - "audio-card-analog-usb": "audio-speakers-symbolic", - "audio-card-analog-pci": "audio-card-symbolic", - "preferences-system": "emblem-system-symbolic", - "com.github.Aylur.ags-symbolic": "controls-symbolic", - "com.github.Aylur.ags": "controls-symbolic", -} + 'transmission-gtk': 'transmission', + 'blueberry.py': 'blueberry', + Caprine: 'facebook-messenger', + 'com.raggesilver.BlackBox-symbolic': 'terminal-symbolic', + 'org.wezfurlong.wezterm-symbolic': 'terminal-symbolic', + 'audio-headset-bluetooth': 'audio-headphones-symbolic', + 'audio-card-analog-usb': 'audio-speakers-symbolic', + 'audio-card-analog-pci': 'audio-card-symbolic', + 'preferences-system': 'emblem-system-symbolic', + 'com.github.Aylur.ags-symbolic': 'controls-symbolic', + 'com.github.Aylur.ags': 'controls-symbolic', +} as const; export default { - missing: "image-missing-symbolic", + missing: 'image-missing-symbolic', nix: { - nix: "nix-snowflake-symbolic", + nix: 'nix-snowflake-symbolic', }, app: { - terminal: "terminal-symbolic", + terminal: 'terminal-symbolic', }, fallback: { - executable: "application-x-executable", - notification: "dialog-information-symbolic", - video: "video-x-generic-symbolic", - audio: "audio-x-generic-symbolic", + executable: 'application-x-executable', + notification: 'dialog-information-symbolic', + video: 'video-x-generic-symbolic', + audio: 'audio-x-generic-symbolic', }, ui: { - close: "window-close-symbolic", - colorpicker: "color-select-symbolic", - info: "info-symbolic", - link: "external-link-symbolic", - lock: "system-lock-screen-symbolic", - menu: "open-menu-symbolic", - refresh: "view-refresh-symbolic", - search: "system-search-symbolic", - settings: "emblem-system-symbolic", - themes: "preferences-desktop-theme-symbolic", - tick: "object-select-symbolic", - time: "hourglass-symbolic", - toolbars: "toolbars-symbolic", - warning: "dialog-warning-symbolic", - avatar: "avatar-default-symbolic", + close: 'window-close-symbolic', + colorpicker: 'color-select-symbolic', + info: 'info-symbolic', + link: 'external-link-symbolic', + lock: 'system-lock-screen-symbolic', + menu: 'open-menu-symbolic', + refresh: 'view-refresh-symbolic', + search: 'system-search-symbolic', + settings: 'emblem-system-symbolic', + themes: 'preferences-desktop-theme-symbolic', + tick: 'object-select-symbolic', + time: 'hourglass-symbolic', + toolbars: 'toolbars-symbolic', + warning: 'dialog-warning-symbolic', + avatar: 'avatar-default-symbolic', arrow: { - right: "pan-end-symbolic", - left: "pan-start-symbolic", - down: "pan-down-symbolic", - up: "pan-up-symbolic", + right: 'pan-end-symbolic', + left: 'pan-start-symbolic', + down: 'pan-down-symbolic', + up: 'pan-up-symbolic', }, }, audio: { mic: { - muted: "microphone-disabled-symbolic", - low: "microphone-sensitivity-low-symbolic", - medium: "microphone-sensitivity-medium-symbolic", - high: "microphone-sensitivity-high-symbolic", + muted: 'microphone-disabled-symbolic', + low: 'microphone-sensitivity-low-symbolic', + medium: 'microphone-sensitivity-medium-symbolic', + high: 'microphone-sensitivity-high-symbolic', }, volume: { - muted: "audio-volume-muted-symbolic", - low: "audio-volume-low-symbolic", - medium: "audio-volume-medium-symbolic", - high: "audio-volume-high-symbolic", - overamplified: "audio-volume-overamplified-symbolic", + muted: 'audio-volume-muted-symbolic', + low: 'audio-volume-low-symbolic', + medium: 'audio-volume-medium-symbolic', + high: 'audio-volume-high-symbolic', + overamplified: 'audio-volume-overamplified-symbolic', }, type: { - headset: "audio-headphones-symbolic", - speaker: "audio-speakers-symbolic", - card: "audio-card-symbolic", + headset: 'audio-headphones-symbolic', + speaker: 'audio-speakers-symbolic', + card: 'audio-card-symbolic', }, - mixer: "mixer-symbolic", + mixer: 'mixer-symbolic', }, powerprofile: { - balanced: "power-profile-balanced-symbolic", - "power-saver": "power-profile-power-saver-symbolic", - performance: "power-profile-performance-symbolic", + balanced: 'power-profile-balanced-symbolic', + 'power-saver': 'power-profile-power-saver-symbolic', + performance: 'power-profile-performance-symbolic', }, asusctl: { profile: { - Balanced: "power-profile-balanced-symbolic", - Quiet: "power-profile-power-saver-symbolic", - Performance: "power-profile-performance-symbolic", + Balanced: 'power-profile-balanced-symbolic', + Quiet: 'power-profile-power-saver-symbolic', + Performance: 'power-profile-performance-symbolic', }, mode: { - Integrated: "processor-symbolic", - Hybrid: "controller-symbolic", + Integrated: 'processor-symbolic', + Hybrid: 'controller-symbolic', }, }, battery: { - charging: "battery-flash-symbolic", - warning: "battery-empty-symbolic", + charging: 'battery-flash-symbolic', + warning: 'battery-empty-symbolic', }, bluetooth: { - enabled: "bluetooth-active-symbolic", - disabled: "bluetooth-disabled-symbolic", + enabled: 'bluetooth-active-symbolic', + disabled: 'bluetooth-disabled-symbolic', }, brightness: { - indicator: "display-brightness-symbolic", - keyboard: "keyboard-brightness-symbolic", - screen: "display-brightness-symbolic", + indicator: 'display-brightness-symbolic', + keyboard: 'keyboard-brightness-symbolic', + screen: 'display-brightness-symbolic', }, powermenu: { - sleep: "weather-clear-night-symbolic", - reboot: "system-reboot-symbolic", - logout: "system-log-out-symbolic", - shutdown: "system-shutdown-symbolic", + sleep: 'weather-clear-night-symbolic', + reboot: 'system-reboot-symbolic', + logout: 'system-log-out-symbolic', + shutdown: 'system-shutdown-symbolic', }, recorder: { - recording: "media-record-symbolic", + recording: 'media-record-symbolic', }, notifications: { - noisy: "org.gnome.Settings-notifications-symbolic", - silent: "notifications-disabled-symbolic", - message: "chat-bubbles-symbolic", + noisy: 'org.gnome.Settings-notifications-symbolic', + silent: 'notifications-disabled-symbolic', + message: 'chat-bubbles-symbolic', }, trash: { - full: "user-trash-full-symbolic", - empty: "user-trash-symbolic", + full: 'user-trash-full-symbolic', + empty: 'user-trash-symbolic', }, mpris: { shuffle: { - enabled: "media-playlist-shuffle-symbolic", - disabled: "media-playlist-consecutive-symbolic", + enabled: 'media-playlist-shuffle-symbolic', + disabled: 'media-playlist-consecutive-symbolic', }, loop: { - none: "media-playlist-repeat-symbolic", - track: "media-playlist-repeat-song-symbolic", - playlist: "media-playlist-repeat-symbolic", + none: 'media-playlist-repeat-symbolic', + track: 'media-playlist-repeat-song-symbolic', + playlist: 'media-playlist-repeat-symbolic', }, - playing: "media-playback-pause-symbolic", - paused: "media-playback-start-symbolic", - stopped: "media-playback-start-symbolic", - prev: "media-skip-backward-symbolic", - next: "media-skip-forward-symbolic", + playing: 'media-playback-pause-symbolic', + paused: 'media-playback-start-symbolic', + stopped: 'media-playback-start-symbolic', + prev: 'media-skip-backward-symbolic', + next: 'media-skip-forward-symbolic', }, system: { - cpu: "org.gnome.SystemMonitor-symbolic", - ram: "drive-harddisk-solidstate-symbolic", - temp: "temperature-symbolic", + cpu: 'org.gnome.SystemMonitor-symbolic', + ram: 'drive-harddisk-solidstate-symbolic', + temp: 'temperature-symbolic', }, color: { - dark: "dark-mode-symbolic", - light: "light-mode-symbolic", + dark: 'dark-mode-symbolic', + light: 'light-mode-symbolic', }, -} +}; diff --git a/lib/option.ts b/lib/option.ts index 1428aff..1c6be5a 100644 --- a/lib/option.ts +++ b/lib/option.ts @@ -1,142 +1,147 @@ -import { Variable } from "resource:///com/github/Aylur/ags/variable.js" +import { isHexColor } from 'globals/variables'; +import { Variable } from 'resource:///com/github/Aylur/ags/variable.js'; +import { MkOptionsResult } from './types/options'; type OptProps = { - persistent?: boolean -} + persistent?: boolean; +}; export class Opt extends Variable { - static { Service.register(this) } + static { + Service.register(this); + } constructor(initial: T, { persistent = false }: OptProps = {}) { - super(initial) - this.initial = initial - this.persistent = persistent + super(initial); + this.initial = initial; + this.persistent = persistent; } - initial: T - id = "" - persistent: boolean - toString() { return `${this.value}` } - toJSON() { return `opt:${this.value}` } + initial: T; + id = ''; + persistent: boolean; + toString(): string { + return `${this.value}`; + } + toJSON(): string { + return `opt:${this.value}`; + } getValue = (): T => { - return super.getValue() + return super.getValue(); + }; + init(cacheFile: string): void { + const cacheV = JSON.parse(Utils.readFile(cacheFile) || '{}')[this.id]; + if (cacheV !== undefined) this.value = cacheV; + + this.connect('changed', () => { + const cache = JSON.parse(Utils.readFile(cacheFile) || '{}'); + cache[this.id] = this.value; + Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile); + }); } - init(cacheFile: string) { - const cacheV = JSON.parse(Utils.readFile(cacheFile) || "{}")[this.id] - if (cacheV !== undefined) - this.value = cacheV - - this.connect("changed", () => { - const cache = JSON.parse(Utils.readFile(cacheFile) || "{}") - cache[this.id] = this.value - Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile) - }) - } - - reset() { - if (this.persistent) - return; + reset(): string | undefined { + if (this.persistent) return; if (JSON.stringify(this.value) !== JSON.stringify(this.initial)) { - this.value = this.initial + this.value = this.initial; return this.id; } } - doResetColor() { - if (this.persistent) - return; + doResetColor(): string | undefined { + if (this.persistent) return; - const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(`${this.value}`); - if ((JSON.stringify(this.value) !== JSON.stringify(this.initial)) && isColor) { - this.value = this.initial - return this.id + const isColor = isHexColor(this.value as string); + if (JSON.stringify(this.value) !== JSON.stringify(this.initial) && isColor) { + this.value = this.initial; + return this.id; } return; } } -export const opt = (initial: T, opts?: OptProps) => new Opt(initial, opts) +export const opt = (initial: T, opts?: OptProps): Opt => new Opt(initial, opts); -function getOptions(object: object, path = ""): Opt[] { - return Object.keys(object).flatMap(key => { - const obj: Opt = object[key] - const id = path ? path + "." + key : key +const getOptions = (object: Record, path = ''): Opt[] => { + return Object.keys(object).flatMap((key) => { + const obj = object[key]; + const id = path ? path + '.' + key : key; if (obj instanceof Variable) { - obj.id = id - return obj + const optValue = obj as Opt; + optValue.id = id; + return optValue; } - if (typeof obj === "object") - return getOptions(obj, id) + if (typeof obj === 'object' && obj !== null) { + return getOptions(obj as Record, id); // Recursively process nested objects + } - return [] - }) -} + return []; + }); +}; -export function mkOptions(cacheFile: string, object: T, confFile: string = "config.json") { - for (const opt of getOptions(object)) - opt.init(cacheFile) +export function mkOptions( + cacheFile: string, + object: T, + confFile: string = 'config.json', +): T & MkOptionsResult { + for (const opt of getOptions(object as Record)) opt.init(cacheFile); - Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/")) + Utils.ensureDirectory(cacheFile.split('/').slice(0, -1).join('/')); - const configFile = `${TMP}/${confFile}` - const values = getOptions(object).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {}) - Utils.writeFileSync(JSON.stringify(values, null, 2), configFile) + const configFile = `${TMP}/${confFile}`; + const values = getOptions(object as Record).reduce( + (obj, { id, value }) => ({ [id]: value, ...obj }), + {}, + ); + Utils.writeFileSync(JSON.stringify(values, null, 2), configFile); Utils.monitorFile(configFile, () => { - const cache = JSON.parse(Utils.readFile(configFile) || "{}") - for (const opt of getOptions(object)) { - if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value)) - opt.value = cache[opt.id] + const cache = JSON.parse(Utils.readFile(configFile) || '{}'); + for (const opt of getOptions(object as Record)) { + if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value)) opt.value = cache[opt.id]; } - }) + }); function sleep(ms = 0): Promise { - return new Promise(r => setTimeout(r, ms)) + return new Promise((r) => setTimeout(r, ms)); } - async function reset( - [opt, ...list] = getOptions(object), + const reset = async ( + [opt, ...list] = getOptions(object as Record), id = opt?.reset(), - ): Promise> { - if (!opt) - return sleep().then(() => []) + ): Promise> => { + if (!opt) return sleep().then(() => []); - return id - ? [id, ...(await sleep(50).then(() => reset(list)))] - : await sleep().then(() => reset(list)) - } + return id ? [id, ...(await sleep(50).then(() => reset(list)))] : await sleep().then(() => reset(list)); + }; - async function resetTheme( - [opt, ...list] = getOptions(object), + const resetTheme = async ( + [opt, ...list] = getOptions(object as Record), id = opt?.doResetColor(), - ): Promise> { - if (!opt) - return sleep().then(() => []) + ): Promise> => { + if (!opt) return sleep().then(() => []); return id ? [id, ...(await sleep(50).then(() => resetTheme(list)))] - : await sleep().then(() => resetTheme(list)) - } + : await sleep().then(() => resetTheme(list)); + }; return Object.assign(object, { configFile, - array: () => getOptions(object), + array: () => getOptions(object as Record), async reset() { - return (await reset()).join("\n") + return (await reset()).join('\n'); }, async resetTheme() { - return (await resetTheme()).join("\n") + return (await resetTheme()).join('\n'); }, handler(deps: string[], callback: () => void) { - for (const opt of getOptions(object)) { - if (deps.some(i => opt.id.startsWith(i))) - opt.connect("changed", callback) + for (const opt of getOptions(object as Record)) { + if (deps.some((i) => opt.id.startsWith(i))) opt.connect('changed', callback); } }, - }) + }); } - diff --git a/lib/session.ts b/lib/session.ts index 3c828c3..595b366 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -1,16 +1,16 @@ -import GLib from "gi://GLib?version=2.0" +import GLib from 'gi://GLib?version=2.0'; declare global { - const OPTIONS: string - const TMP: string - const USER: string + const OPTIONS: string; + const TMP: string; + const USER: string; } Object.assign(globalThis, { OPTIONS: `${GLib.get_user_cache_dir()}/ags/hyprpanel/options.json`, TMP: `${GLib.get_tmp_dir()}/ags/hyprpanel`, USER: GLib.get_user_name(), -}) +}); -Utils.ensureDirectory(TMP) -App.addIcons(`${App.configDir}/assets`) +Utils.ensureDirectory(TMP); +App.addIcons(`${App.configDir}/assets`); diff --git a/lib/shared/media.ts b/lib/shared/media.ts index 733fdcf..23e0c6f 100644 --- a/lib/shared/media.ts +++ b/lib/shared/media.ts @@ -1,5 +1,5 @@ -import { MprisPlayer } from "types/service/mpris"; -const mpris = await Service.import("mpris"); +import { MprisPlayer } from 'types/service/mpris'; +const mpris = await Service.import('mpris'); export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]): MprisPlayer => { const statusOrder = { @@ -12,18 +12,12 @@ export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]): return mpris.players[0]; } - const isPlaying = mpris.players.some( - (p) => p["play-back-status"] === "Playing", - ); + const isPlaying = mpris.players.some((p: MprisPlayer) => p.play_back_status === 'Playing'); - const playerStillExists = mpris.players.some( - (p) => activePlayer["bus-name"] === p["bus-name"], - ); + const playerStillExists = mpris.players.some((p) => activePlayer.bus_name === p.bus_name); const nextPlayerUp = mpris.players.sort( - (a, b) => - statusOrder[a["play-back-status"]] - - statusOrder[b["play-back-status"]], + (a: MprisPlayer, b: MprisPlayer) => statusOrder[a.play_back_status] - statusOrder[b.play_back_status], )[0]; if (isPlaying || !playerStillExists) { @@ -31,4 +25,4 @@ export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]): } return activePlayer; -} +}; diff --git a/lib/shared/notifications.ts b/lib/shared/notifications.ts new file mode 100644 index 0000000..f772c69 --- /dev/null +++ b/lib/shared/notifications.ts @@ -0,0 +1,12 @@ +import { Notification } from 'types/service/notifications'; + +export const filterNotifications = (notifications: Notification[], filter: string[]): Notification[] => { + const notifFilter = new Set(filter.map((name: string) => name.toLowerCase().replace(/\s+/g, '_'))); + + const filteredNotifications = notifications.filter((notif: Notification) => { + const normalizedAppName = notif.app_name.toLowerCase().replace(/\s+/g, '_'); + return !notifFilter.has(normalizedAppName); + }); + + return filteredNotifications; +}; diff --git a/lib/types/audio.d.ts b/lib/types/audio.d.ts new file mode 100644 index 0000000..9332431 --- /dev/null +++ b/lib/types/audio.d.ts @@ -0,0 +1,5 @@ +export type InputDevices = Button, Attribute>, Attribute>, Attribute>[]; + +type DummyDevices = Button, Attribute>, Attribute>, Attribute>[]; +type RealPlaybackDevices = Button, Attribute>, Attribute>, Attribute>[]; +export type PlaybackDevices = DummyDevices | RealPlaybackDevices; diff --git a/lib/types/bar.d.ts b/lib/types/bar.d.ts index 907a728..01176ae 100644 --- a/lib/types/bar.d.ts +++ b/lib/types/bar.d.ts @@ -1,43 +1,45 @@ -import { Binding, Connectable } from "types/service" -import { Variable } from "types/variable" -import Box from "types/widgets/box"; -import Label from "types/widgets/label"; -import { Widget as WidgetType } from "types/widgets/widget" +import { Binding, Connectable } from 'types/service'; +import { Variable } from 'types/variable'; +import Box from 'types/widgets/box'; +import Button from 'types/widgets/button'; +import Label from 'types/widgets/label'; +import { Attribute, Child } from './widget'; -export type Child = { +export type BarBoxChild = { component: Box; isVisible?: boolean; isVis?: Variable; boxClass: string; - props: ButtonProps; -}; +} & ButtonProps; + +export type SelfButton = Button; export type BoxHook = (self: Box) => void; export type LabelHook = (self: Label) => void; export type Module = { - icon?: string | Binding, - textIcon?: string | Binding, - label?: string | Binding, - labelHook?: LabelHook, - boundLabel?: string, - tooltipText?: string | Binding, - boxClass: string, - props?: ButtonProps, - showLabel?: boolean, - showLabelBinding?: Binding, - hook?: BoxHook, - connection?: Binding -} + icon?: string | Binding; + textIcon?: string | Binding; + label?: string | Binding; + labelHook?: LabelHook; + boundLabel?: string; + tooltipText?: string | Binding; + boxClass: string; + props?: ButtonProps; + showLabel?: boolean; + showLabelBinding?: Binding; + hook?: BoxHook; + connection?: Binding; +}; -export type ResourceLabelType = "used/total" | "used" | "percentage" | "free"; +export type ResourceLabelType = 'used/total' | 'used' | 'percentage' | 'free'; -export type StorageIcon = "󰋊" | "" | "󱛟" | "" | "" | ""; +export type StorageIcon = '󰋊' | '' | '󱛟' | '' | '' | ''; -export type NetstatIcon = "󰖟" | "󰇚" | "󰕒" | "󰛳" | "" | "󰣺" | "󰖩" | "" | "󰈀"; -export type NetstatLabelType = "full" | "in" | "out"; -export type RateUnit = "GiB" | "MiB" | "KiB" | "auto"; +export type NetstatIcon = '󰖟' | '󰇚' | '󰕒' | '󰛳' | '' | '󰣺' | '󰖩' | '' | '󰈀'; +export type NetstatLabelType = 'full' | 'in' | 'out'; +export type RateUnit = 'GiB' | 'MiB' | 'KiB' | 'auto'; -export type UpdatesIcon = "󰚰" | "󰇚" | "" | "󱑢" | "󱑣" | "󰏖" | "" | "󰏔" | "󰏗"; +export type UpdatesIcon = '󰚰' | '󰇚' | '' | '󱑢' | '󱑣' | '󰏖' | '' | '󰏔' | '󰏗'; -export type PowerIcon = "" | "" | "󰍃" | "󰿅" | "󰒲" | "󰤄"; +export type PowerIcon = '' | '' | '󰍃' | '󰿅' | '󰒲' | '󰤄'; diff --git a/lib/types/customModules/generic.d.ts b/lib/types/customModules/generic.d.ts index 8741fd5..839e04a 100644 --- a/lib/types/customModules/generic.d.ts +++ b/lib/types/customModules/generic.d.ts @@ -1,6 +1,13 @@ -export type GenericResourceData = { +export type GenericFunction = (...args: P) => T; + +export type GenericResourceMetrics = { total: number; used: number; - free: number; percentage: number; -} +}; + +type GenericResourceData = ResourceUsage & { + free: number; +}; + +export type Postfix = 'TiB' | 'GiB' | 'MiB' | 'KiB' | 'B'; diff --git a/lib/types/customModules/kbLayout.d.ts b/lib/types/customModules/kbLayout.d.ts index aee1464..93ecea4 100644 --- a/lib/types/customModules/kbLayout.d.ts +++ b/lib/types/customModules/kbLayout.d.ts @@ -1,5 +1,7 @@ -export type KbLabelType = "layout" | "code"; -export type KbIcon = "" | "󰌌" | "" | "󰬴" | "󰗊"; +import { layoutMap } from 'customModules/kblayout/layouts'; + +export type KbLabelType = 'layout' | 'code'; +export type KbIcon = '' | '󰌌' | '' | '󰬴' | '󰗊'; export type HyprctlKeyboard = { address: string; @@ -22,7 +24,10 @@ export type HyprctlMouse = { export type HyprctlDeviceLayout = { mice: HyprctlMouse[]; keyboards: HyprctlKeyboard[]; - tablets: any[]; - touch: any[]; - switches: any[]; + tablets: unknown[]; + touch: unknown[]; + switches: unknown[]; }; + +export type LayoutKeys = keyof typeof layoutMap; +export type LayoutValues = (typeof layoutMap)[LayoutKeys]; diff --git a/lib/types/customModules/network.d.ts b/lib/types/customModules/network.d.ts index 77a02c6..dbef13f 100644 --- a/lib/types/customModules/network.d.ts +++ b/lib/types/customModules/network.d.ts @@ -1,5 +1,4 @@ export type NetworkResourceData = { in: string; out: string; -} - +}; diff --git a/lib/types/customModules/utils.d.ts b/lib/types/customModules/utils.d.ts index e69de29..9764bc8 100644 --- a/lib/types/customModules/utils.d.ts +++ b/lib/types/customModules/utils.d.ts @@ -0,0 +1,9 @@ +import { Binding } from 'lib/utils'; + +export type InputHandlerEvents = { + onPrimaryClick?: Binding; + onSecondaryClick?: Binding; + onMiddleClick?: Binding; + onScrollUp?: Binding; + onScrollDown?: Binding; +}; diff --git a/lib/types/defaults/bar.ts b/lib/types/defaults/bar.ts index 37a3dc6..78966c4 100644 --- a/lib/types/defaults/bar.ts +++ b/lib/types/defaults/bar.ts @@ -1,5 +1,5 @@ -import { NetstatLabelType, ResourceLabelType } from "../bar"; +import { NetstatLabelType, ResourceLabelType } from '../bar'; -export const LABEL_TYPES: ResourceLabelType[] = ["used/total", "used", "free", "percentage"]; +export const LABEL_TYPES: ResourceLabelType[] = ['used/total', 'used', 'free', 'percentage']; -export const NETWORK_LABEL_TYPES: NetstatLabelType[] = ["full", "in", "out"]; +export const NETWORK_LABEL_TYPES: NetstatLabelType[] = ['full', 'in', 'out']; diff --git a/lib/types/defaults/netstat.ts b/lib/types/defaults/netstat.ts index 2184e0b..083344b 100644 --- a/lib/types/defaults/netstat.ts +++ b/lib/types/defaults/netstat.ts @@ -1,10 +1,10 @@ -import { RateUnit } from "../bar"; -import { NetworkResourceData } from "../customModules/network"; +import { RateUnit } from '../bar'; +import { NetworkResourceData } from '../customModules/network'; export const GET_DEFAULT_NETSTAT_DATA = (dataType: RateUnit): NetworkResourceData => { if (dataType === 'auto') { - return { in: `0 Kib/s`, out: `0 Kib/s` } + return { in: `0 Kib/s`, out: `0 Kib/s` }; } - return { in: `0 ${dataType}/s`, out: `0 ${dataType}/s` } + return { in: `0 ${dataType}/s`, out: `0 ${dataType}/s` }; }; diff --git a/lib/types/defaults/options.ts b/lib/types/defaults/options.ts index 1e390c9..11b7347 100644 --- a/lib/types/defaults/options.ts +++ b/lib/types/defaults/options.ts @@ -1,60 +1,60 @@ export const defaultColorMap = { - "rosewater": "#f5e0dc", - "flamingo": "#f2cdcd", - "pink": "#f5c2e7", - "mauve": "#cba6f7", - "red": "#f38ba8", - "maroon": "#eba0ac", - "peach": "#fab387", - "yellow": "#f9e2af", - "green": "#a6e3a1", - "teal": "#94e2d5", - "sky": "#89dceb", - "sapphire": "#74c7ec", - "blue": "#89b4fa", - "lavender": "#b4befe", - "text": "#cdd6f4", - "subtext1": "#bac2de", - "subtext2": "#a6adc8", - "overlay2": "#9399b2", - "overlay1": "#7f849c", - "overlay0": "#6c7086", - "surface2": "#585b70", - "surface1": "#45475a", - "surface0": "#313244", - "base2": "#242438", - "base": "#1e1e2e", - "mantle": "#181825", - "crust": "#11111b", - "surface1_2": "#454759", - "text2": "#cdd6f3", - "pink2": "#f5c2e6", - "red2": "#f38ba7", - "peach2": "#fab386", - "mantle2": "#181824", - "surface0_2": "#313243", - "surface2_2": "#585b69", - "overlay1_2": "#7f849b", - "lavender2": "#b4befd", - "mauve2": "#cba6f6", - "green2": "#a6e3a0", - "sky2": "#89dcea", - "teal2": "#94e2d4", - "yellow2": "#f9e2ad", - "maroon2": "#eba0ab", - "crust2": "#11111a", - "pink3": "#f5c2e8", - "red3": "#f38ba9", - "mantle3": "#181826", - "surface0_3": "#313245", - "surface2_3": "#585b71", - "overlay1_3": "#7f849d", - "lavender3": "#b4beff", - "mauve3": "#cba6f8", - "green3": "#a6e3a2", - "sky3": "#89dcec", - "teal3": "#94e2d6", - "yellow3": "#f9e2ae", - "maroon3": "#eba0ad", - "crust3": "#11111c", -}; + rosewater: '#f5e0dc', + flamingo: '#f2cdcd', + pink: '#f5c2e7', + mauve: '#cba6f7', + red: '#f38ba8', + maroon: '#eba0ac', + peach: '#fab387', + yellow: '#f9e2af', + green: '#a6e3a1', + teal: '#94e2d5', + sky: '#89dceb', + sapphire: '#74c7ec', + blue: '#89b4fa', + lavender: '#b4befe', + text: '#cdd6f4', + subtext1: '#bac2de', + subtext2: '#a6adc8', + overlay2: '#9399b2', + overlay1: '#7f849c', + overlay0: '#6c7086', + surface2: '#585b70', + surface1: '#45475a', + surface0: '#313244', + base2: '#242438', + base: '#1e1e2e', + mantle: '#181825', + crust: '#11111b', + surface1_2: '#454759', + text2: '#cdd6f3', + pink2: '#f5c2e6', + red2: '#f38ba7', + peach2: '#fab386', + mantle2: '#181824', + surface0_2: '#313243', + surface2_2: '#585b69', + overlay1_2: '#7f849b', + lavender2: '#b4befd', + mauve2: '#cba6f6', + green2: '#a6e3a0', + sky2: '#89dcea', + teal2: '#94e2d4', + yellow2: '#f9e2ad', + maroon2: '#eba0ab', + crust2: '#11111a', + pink3: '#f5c2e8', + red3: '#f38ba9', + mantle3: '#181826', + surface0_3: '#313245', + surface2_3: '#585b71', + overlay1_3: '#7f849d', + lavender3: '#b4beff', + mauve3: '#cba6f8', + green3: '#a6e3a2', + sky3: '#89dcec', + teal3: '#94e2d6', + yellow3: '#f9e2ae', + maroon3: '#eba0ad', + crust3: '#11111c', +} as const; diff --git a/lib/types/defaults/weather.ts b/lib/types/defaults/weather.ts index ed4443e..cde95e3 100644 --- a/lib/types/defaults/weather.ts +++ b/lib/types/defaults/weather.ts @@ -1,1053 +1,1053 @@ export const DEFAULT_WEATHER = { - "location": { - "name": "Tahiti", - "region": "Somewhere", - "country": "United States of America", - "lat": 0, - "lon": 0, - "tz_id": "Tahiti", - "localtime_epoch": 1721981457, - "localtime": "2024-07-26 1:10" + location: { + name: 'Tahiti', + region: 'Somewhere', + country: 'United States of America', + lat: 0, + lon: 0, + tz_id: 'Tahiti', + localtime_epoch: 1721981457, + localtime: '2024-07-26 1:10', }, - "current": { - "last_updated_epoch": 1721980800, - "last_updated": "2024-07-26 01:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + current: { + last_updated_epoch: 1721980800, + last_updated: '2024-07-26 01:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "NW", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0.0, - "precip_in": 0.0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "vis_km": 0, - "vis_miles": 0, - "uv": 0, - "gust_mph": 0, - "gust_kph": 0, + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'NW', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0.0, + precip_in: 0.0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + vis_km: 0, + vis_miles: 0, + uv: 0, + gust_mph: 0, + gust_kph: 0, }, - "forecast": { - "forecastday": [ + forecast: { + forecastday: [ { - "date": "2024-07-26", - "date_epoch": 1721952000, - "day": { - "maxtemp_c": 0, - "maxtemp_f": 0, - "mintemp_c": 0, - "mintemp_f": 0, - "avgtemp_c": 0, - "avgtemp_f": 0, - "maxwind_mph": 0, - "maxwind_kph": 0, - "totalprecip_mm": 0, - "totalprecip_in": 0, - "totalsnow_cm": 0, - "avgvis_km": 0, - "avgvis_miles": 0, - "avghumidity": 0, - "daily_will_it_rain": 0, - "daily_chance_of_rain": 0, - "daily_will_it_snow": 0, - "daily_chance_of_snow": 0, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + date: '2024-07-26', + date_epoch: 1721952000, + day: { + maxtemp_c: 0, + maxtemp_f: 0, + mintemp_c: 0, + mintemp_f: 0, + avgtemp_c: 0, + avgtemp_f: 0, + maxwind_mph: 0, + maxwind_kph: 0, + totalprecip_mm: 0, + totalprecip_in: 0, + totalsnow_cm: 0, + avgvis_km: 0, + avgvis_miles: 0, + avghumidity: 0, + daily_will_it_rain: 0, + daily_chance_of_rain: 0, + daily_will_it_snow: 0, + daily_chance_of_snow: 0, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "uv": 0, + uv: 0, }, - "astro": { - "sunrise": "06:01 AM", - "sunset": "08:10 PM", - "moonrise": "11:32 PM", - "moonset": "12:01 PM", - "moon_phase": "Waning Gibbous", - "moon_illumination": 0, - "is_moon_up": 0, - "is_sun_up": 0, + astro: { + sunrise: '06:01 AM', + sunset: '08:10 PM', + moonrise: '11:32 PM', + moonset: '12:01 PM', + moon_phase: 'Waning Gibbous', + moon_illumination: 0, + is_moon_up: 0, + is_sun_up: 0, }, - "hour": [ + hour: [ { - "time_epoch": 1721977200, - "time": "2024-07-26 00:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721977200, + time: '2024-07-26 00:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721980800, - "time": "2024-07-26 01:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721980800, + time: '2024-07-26 01:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721984400, - "time": "2024-07-26 02:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721984400, + time: '2024-07-26 02:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721988000, - "time": "2024-07-26 03:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721988000, + time: '2024-07-26 03:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721991600, - "time": "2024-07-26 04:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721991600, + time: '2024-07-26 04:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721995200, - "time": "2024-07-26 05:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721995200, + time: '2024-07-26 05:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1721998800, - "time": "2024-07-26 06:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1721998800, + time: '2024-07-26 06:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1722002400, - "time": "2024-07-26 07:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722002400, + time: '2024-07-26 07:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 7.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 7.0, }, { - "time_epoch": 1722006000, - "time": "2024-07-26 08:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722006000, + time: '2024-07-26 08:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 7.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 7.0, }, { - "time_epoch": 1722009600, - "time": "2024-07-26 09:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722009600, + time: '2024-07-26 09:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 8.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 8.0, }, { - "time_epoch": 1722013200, - "time": "2024-07-26 10:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722013200, + time: '2024-07-26 10:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 8.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 8.0, }, { - "time_epoch": 1722016800, - "time": "2024-07-26 11:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722016800, + time: '2024-07-26 11:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 9.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 9.0, }, { - "time_epoch": 1722020400, - "time": "2024-07-26 12:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722020400, + time: '2024-07-26 12:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 9.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 9.0, }, { - "time_epoch": 1722024000, - "time": "2024-07-26 13:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722024000, + time: '2024-07-26 13:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 9.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 9.0, }, { - "time_epoch": 1722027600, - "time": "2024-07-26 14:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722027600, + time: '2024-07-26 14:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 10.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 10.0, }, { - "time_epoch": 1722031200, - "time": "2024-07-26 15:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722031200, + time: '2024-07-26 15:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 10.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 10.0, }, { - "time_epoch": 1722034800, - "time": "2024-07-26 16:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722034800, + time: '2024-07-26 16:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 10.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 10.0, }, { - "time_epoch": 1722038400, - "time": "2024-07-26 17:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722038400, + time: '2024-07-26 17:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 10.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 10.0, }, { - "time_epoch": 1722042000, - "time": "2024-07-26 18:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722042000, + time: '2024-07-26 18:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 9.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 9.0, }, { - "time_epoch": 1722045600, - "time": "2024-07-26 19:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722045600, + time: '2024-07-26 19:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 9.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 9.0, }, { - "time_epoch": 1722049200, - "time": "2024-07-26 20:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 1, - "condition": { - "text": "Sunny", - "icon": "//cdn.weatherapi.com/weather/64x64/day/113.png", - "code": 1000 + time_epoch: 1722049200, + time: '2024-07-26 20:00', + temp_c: 0, + temp_f: 0, + is_day: 1, + condition: { + text: 'Sunny', + icon: '//cdn.weatherapi.com/weather/64x64/day/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 8.0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 8.0, }, { - "time_epoch": 1722052800, - "time": "2024-07-26 21:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1722052800, + time: '2024-07-26 21:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1722056400, - "time": "2024-07-26 22:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1722056400, + time: '2024-07-26 22:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, }, { - "time_epoch": 1722060000, - "time": "2024-07-26 23:00", - "temp_c": 0, - "temp_f": 0, - "is_day": 0, - "condition": { - "text": "Clear ", - "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png", - "code": 1000 + time_epoch: 1722060000, + time: '2024-07-26 23:00', + temp_c: 0, + temp_f: 0, + is_day: 0, + condition: { + text: 'Clear ', + icon: '//cdn.weatherapi.com/weather/64x64/night/113.png', + code: 1000, }, - "wind_mph": 0, - "wind_kph": 0, - "wind_degree": 0, - "wind_dir": "N", - "pressure_mb": 0, - "pressure_in": 0, - "precip_mm": 0, - "precip_in": 0, - "snow_cm": 0, - "humidity": 0, - "cloud": 0, - "feelslike_c": 0, - "feelslike_f": 0, - "windchill_c": 0, - "windchill_f": 0, - "heatindex_c": 0, - "heatindex_f": 0, - "dewpoint_c": 0, - "dewpoint_f": 0, - "will_it_rain": 0, - "chance_of_rain": 0, - "will_it_snow": 0, - "chance_of_snow": 0, - "vis_km": 0, - "vis_miles": 0, - "gust_mph": 0, - "gust_kph": 0, - "uv": 0 - } - ] - } - ] - } -} + wind_mph: 0, + wind_kph: 0, + wind_degree: 0, + wind_dir: 'N', + pressure_mb: 0, + pressure_in: 0, + precip_mm: 0, + precip_in: 0, + snow_cm: 0, + humidity: 0, + cloud: 0, + feelslike_c: 0, + feelslike_f: 0, + windchill_c: 0, + windchill_f: 0, + heatindex_c: 0, + heatindex_f: 0, + dewpoint_c: 0, + dewpoint_f: 0, + will_it_rain: 0, + chance_of_rain: 0, + will_it_snow: 0, + chance_of_snow: 0, + vis_km: 0, + vis_miles: 0, + gust_mph: 0, + gust_kph: 0, + uv: 0, + }, + ], + }, + ], + }, +}; diff --git a/lib/types/dropdownmenu.d.ts b/lib/types/dropdownmenu.d.ts new file mode 100644 index 0000000..1349b3d --- /dev/null +++ b/lib/types/dropdownmenu.d.ts @@ -0,0 +1,11 @@ +import { WindowProps } from 'types/widgets/window'; +import { GtkWidget, Transition } from './widget'; + +export type DropdownMenuProps = { + name: string; + child: GtkWidget; + layout?: string; + transition?: Transition; + exclusivity?: Exclusivity; + fixed?: boolean; +} & WindowProps; diff --git a/lib/types/filechooser.d.ts b/lib/types/filechooser.d.ts new file mode 100644 index 0000000..8bb9d8f --- /dev/null +++ b/lib/types/filechooser.d.ts @@ -0,0 +1,3 @@ +export type Config = { + [key: string]: string | number | boolean | object; +}; diff --git a/lib/types/gpustat.d.ts b/lib/types/gpustat.d.ts index ede3378..49dc75d 100644 --- a/lib/types/gpustat.d.ts +++ b/lib/types/gpustat.d.ts @@ -12,14 +12,14 @@ export type GPU_Stat = { index: number; uuid: string; name: string; - "temperature.gpu": number; - "fan.speed": number; - "utilization.gpu": number; - "utilization.enc": number; - "utilization.dec": number; - "power.draw": number; - "enforced.power.limit": number; - "memory.used": number; - "memory.total": number; + 'temperature.gpu': number; + 'fan.speed': number; + 'utilization.gpu': number; + 'utilization.enc': number; + 'utilization.dec': number; + 'power.draw': number; + 'enforced.power.limit': number; + 'memory.used': number; + 'memory.total': number; processes: Process[]; }; diff --git a/lib/types/mpris.d.ts b/lib/types/mpris.d.ts new file mode 100644 index 0000000..5450e80 --- /dev/null +++ b/lib/types/mpris.d.ts @@ -0,0 +1,2 @@ +export type LoopStatus = 'none' | 'track' | 'playlist'; +export type PlaybackStatus = 'playing' | 'paused' | 'stopped'; diff --git a/lib/types/network.d.ts b/lib/types/network.d.ts index 63c8804..58dcdec 100644 --- a/lib/types/network.d.ts +++ b/lib/types/network.d.ts @@ -1,3 +1,5 @@ +import { WIFI_STATUS_MAP } from 'globals/network'; + export type AccessPoint = { bssid: string | null; address: string | null; @@ -7,4 +9,8 @@ export type AccessPoint = { strength: number; frequency: number; iconName: string | undefined; -} +}; + +export type WifiStatus = keyof typeof WIFI_STATUS_MAP; + +export type WifiIcon = '󰤩' | '󰤨' | '󰤪' | '󰤨' | '󰤩' | '󰤮' | '󰤨' | '󰤥' | '󰤢' | '󰤟' | '󰤯'; diff --git a/lib/types/notification.d.ts b/lib/types/notification.d.ts index 60f8dea..027f195 100644 --- a/lib/types/notification.d.ts +++ b/lib/types/notification.d.ts @@ -1,3 +1,5 @@ +import icons from 'modules/icons/index'; + export interface NotificationArgs { appName?: string; body?: string; @@ -9,3 +11,5 @@ export interface NotificationArgs { timeout?: number; transient?: boolean; } + +export type NotificationIcon = keyof typeof icons.notifications; diff --git a/lib/types/options.d.ts b/lib/types/options.d.ts index 9c6330e..6c913f1 100644 --- a/lib/types/options.d.ts +++ b/lib/types/options.d.ts @@ -1,119 +1,215 @@ -import { Opt } from "lib/option"; -import { Variable } from "types/variable"; +import { Opt } from 'lib/option'; +import { Variable } from 'types/variable'; +import { defaultColorMap } from './defaults/options'; -export type Unit = "imperial" | "metric"; -export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown"; -export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left" | "left" | "right"; -export type OSDAnchor = "top left" | "top" | "top right" | "right" | "bottom right" | "bottom" | "bottom left" | "left"; -export type BarButtonStyles = "default" | "split" | "wave" | "wave2"; +export type MkOptionsResult = { + configFile: string; + array: () => Opt[]; + reset: () => Promise; + resetTheme: () => Promise; + handler: (deps: string[], callback: () => void) => void; +}; + +export type RecursiveOptionsObject = { + [key: string]: RecursiveOptionsObject | Opt | Opt; +}; + +export type BarLocation = 'top' | 'bottom'; + +export type Unit = 'imperial' | 'metric'; +export type PowerOptions = 'sleep' | 'reboot' | 'logout' | 'shutdown'; +export type NotificationAnchor = + | 'top' + | 'top right' + | 'top left' + | 'bottom' + | 'bottom right' + | 'bottom left' + | 'left' + | 'right'; +export type OSDAnchor = 'top left' | 'top' | 'top right' | 'right' | 'bottom right' | 'bottom' | 'bottom left' | 'left'; +export type BarButtonStyles = 'default' | 'split' | 'wave' | 'wave2'; export type ThemeExportData = { - filePath: string, - themeOnly: boolean -} + filePath: string; + themeOnly: boolean; +}; export type RowProps = { - opt: Opt - title: string - note?: string + opt: Opt; + title: string; + note?: string; type?: - | "number" - | "color" - | "float" - | "object" - | "string" - | "enum" - | "boolean" - | "img" - | "wallpaper" - | "export" - | "import" - | "config_import" - | "font" - enums?: string[] - max?: number - min?: number - disabledBinding?: Variable - exportData?: ThemeExportData - subtitle?: string | VarType | Opt, - subtitleLink?: string, - dependencies?: string[], - increment?: number -} + | 'number' + | 'color' + | 'float' + | 'object' + | 'string' + | 'enum' + | 'boolean' + | 'img' + | 'wallpaper' + | 'export' + | 'import' + | 'config_import' + | 'font'; + enums?: T[]; + max?: number; + min?: number; + disabledBinding?: Variable; + exportData?: ThemeExportData; + subtitle?: string | VarType | Opt; + subtitleLink?: string; + dependencies?: string[]; + increment?: number; +}; -export type OSDOrientation = "horizontal" | "vertical"; +export type OSDOrientation = 'horizontal' | 'vertical'; export type HexColor = `#${string}`; +export type WindowLayer = 'top' | 'bottom' | 'overlay' | 'background'; + +export type ActiveWsIndicator = 'underline' | 'highlight' | 'color'; + export type MatugenColors = { - "background": HexColor, - "error": HexColor, - "error_container": HexColor, - "inverse_on_surface": HexColor, - "inverse_primary": HexColor, - "inverse_surface": HexColor, - "on_background": HexColor, - "on_error": HexColor, - "on_error_container": HexColor, - "on_primary": HexColor, - "on_primary_container": HexColor, - "on_primary_fixed": HexColor, - "on_primary_fixed_variant": HexColor, - "on_secondary": HexColor, - "on_secondary_container": HexColor, - "on_secondary_fixed": HexColor, - "on_secondary_fixed_variant": HexColor, - "on_surface": HexColor, - "on_surface_variant": HexColor, - "on_tertiary": HexColor, - "on_tertiary_container": HexColor, - "on_tertiary_fixed": HexColor, - "on_tertiary_fixed_variant": HexColor, - "outline": HexColor, - "outline_variant": HexColor, - "primary": HexColor, - "primary_container": HexColor, - "primary_fixed": HexColor, - "primary_fixed_dim": HexColor, - "scrim": HexColor, - "secondary": HexColor, - "secondary_container": HexColor, - "secondary_fixed": HexColor, - "secondary_fixed_dim": HexColor, - "shadow": HexColor, - "surface": HexColor, - "surface_bright": HexColor, - "surface_container": HexColor, - "surface_container_high": HexColor, - "surface_container_highest": HexColor, - "surface_container_low": HexColor, - "surface_container_lowest": HexColor, - "surface_dim": HexColor, - "surface_variant": HexColor, - "tertiary": HexColor, - "tertiary_container": HexColor, - "tertiary_fixed": HexColor, - "tertiary_fixed_dim": HexColor -} + background: HexColor; + error: HexColor; + error_container: HexColor; + inverse_on_surface: HexColor; + inverse_primary: HexColor; + inverse_surface: HexColor; + on_background: HexColor; + on_error: HexColor; + on_error_container: HexColor; + on_primary: HexColor; + on_primary_container: HexColor; + on_primary_fixed: HexColor; + on_primary_fixed_variant: HexColor; + on_secondary: HexColor; + on_secondary_container: HexColor; + on_secondary_fixed: HexColor; + on_secondary_fixed_variant: HexColor; + on_surface: HexColor; + on_surface_variant: HexColor; + on_tertiary: HexColor; + on_tertiary_container: HexColor; + on_tertiary_fixed: HexColor; + on_tertiary_fixed_variant: HexColor; + outline: HexColor; + outline_variant: HexColor; + primary: HexColor; + primary_container: HexColor; + primary_fixed: HexColor; + primary_fixed_dim: HexColor; + scrim: HexColor; + secondary: HexColor; + secondary_container: HexColor; + secondary_fixed: HexColor; + secondary_fixed_dim: HexColor; + shadow: HexColor; + surface: HexColor; + surface_bright: HexColor; + surface_container: HexColor; + surface_container_high: HexColor; + surface_container_highest: HexColor; + surface_container_low: HexColor; + surface_container_lowest: HexColor; + surface_dim: HexColor; + surface_variant: HexColor; + tertiary: HexColor; + tertiary_container: HexColor; + tertiary_fixed: HexColor; + tertiary_fixed_dim: HexColor; +}; -type MatugenScheme = - | "content" - | "expressive" - | "fidelity" - | "fruit-salad" - | "monochrome" - | "neutral" - | "rainbow" - | "tonal-spot"; +export type MatugenVariation = { + rosewater: HexColor; + flamingo: HexColor; + pink: HexColor; + mauve: HexColor; + red: HexColor; + maroon: HexColor; + peach: HexColor; + yellow: HexColor; + green: HexColor; + teal: HexColor; + sky: HexColor; + sapphire: HexColor; + blue: HexColor; + lavender: HexColor; + text: HexColor; + subtext1: HexColor; + subtext2: HexColor; + overlay2: HexColor; + overlay1: HexColor; + overlay0: HexColor; + surface2: HexColor; + surface1: HexColor; + surface0: HexColor; + base2: HexColor; + base: HexColor; + mantle: HexColor; + crust: HexColor; + notifications_closer: HexColor; + notifications_background: HexColor; + dashboard_btn_text: HexColor; + red2: HexColor; + peach2: HexColor; + pink2: HexColor; + mantle2: HexColor; + surface1_2: HexColor; + surface0_2: HexColor; + overlay1_2: HexColor; + text2: HexColor; + lavender2: HexColor; + crust2: HexColor; + maroon2: HexColor; + mauve2: HexColor; + green2: HexColor; + surface2_2: HexColor; + sky2: HexColor; + teal2: HexColor; + yellow2: HexColor; + pink3: HexColor; + red3: HexColor; + mantle3: HexColor; + surface0_3: HexColor; + surface2_3: HexColor; + overlay1_3: HexColor; + lavender3: HexColor; + mauve3: HexColor; + green3: HexColor; + sky3: HexColor; + teal3: HexColor; + yellow3: HexColor; + maroon3: HexColor; + crust3: HexColor; + notifications_closer?: HexColor; + notifications_background?: HexColor; + dashboard_btn_text?: HexColor; +}; +export type MatugenScheme = + | 'content' + | 'expressive' + | 'fidelity' + | 'fruit-salad' + | 'monochrome' + | 'neutral' + | 'rainbow' + | 'tonal-spot'; -type MatugenVariation = - | "standard_1" - | "standard_2" - | "standard_3" - | "monochrome_1" - | "monochrome_2" - | "monochrome_3" - | "vivid_1" - | "vivid_2" - | "vivid_3" +export type MatugenVariations = + | 'standard_1' + | 'standard_2' + | 'standard_3' + | 'monochrome_1' + | 'monochrome_2' + | 'monochrome_3' + | 'vivid_1' + | 'vivid_2' + | 'vivid_3'; -type MatugenTheme = "light" | "dark"; +type MatugenTheme = 'light' | 'dark'; + +export type ColorMapKey = keyof typeof defaultColorMap; +export type ColorMapValue = (typeof defaultColorMap)[ColorMapKey]; diff --git a/lib/types/popupwindow.d.ts b/lib/types/popupwindow.d.ts new file mode 100644 index 0000000..0c29706 --- /dev/null +++ b/lib/types/popupwindow.d.ts @@ -0,0 +1,35 @@ +import { Widget } from 'types/widgets/widget'; +import { WindowProps } from 'types/widgets/window'; +import { Transition } from './widget'; + +export type PopupWindowProps = { + name: string; + child: any; + layout?: Layouts; + transition?: any; + exclusivity?: Exclusivity; +} & WindowProps; + +export type LayoutFunction = ( + name: string, + child: Widget, + transition: Transition, +) => { + center: () => Widget; + top: () => Widget; + 'top-right': () => Widget; + 'top-center': () => Widget; + 'top-left': () => Widget; + 'bottom-left': () => Widget; + 'bottom-center': () => Widget; + 'bottom-right': () => Widget; +}; +export type Layouts = + | 'center' + | 'top' + | 'top-right' + | 'top-center' + | 'top-left' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right'; diff --git a/lib/types/power.d.ts b/lib/types/power.d.ts index 5a44633..ef3c536 100644 --- a/lib/types/power.d.ts +++ b/lib/types/power.d.ts @@ -1 +1 @@ -export type Action = "sleep" | "reboot" | "logout" | "shutdown"; +export type Action = 'sleep' | 'reboot' | 'logout' | 'shutdown'; diff --git a/lib/types/powerprofiles.d.ts b/lib/types/powerprofiles.d.ts new file mode 100644 index 0000000..a3592fa --- /dev/null +++ b/lib/types/powerprofiles.d.ts @@ -0,0 +1,8 @@ +import icons from 'modules/icons/index'; +import PowerProfiles from 'types/service/powerprofiles.js'; + +export type PowerProfiles = InstanceType; +export type PowerProfile = 'power-saver' | 'balanced' | 'performance'; +export type PowerProfileObject = { + [key: string]: string; +}; diff --git a/lib/types/systray.d.ts b/lib/types/systray.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/lib/types/utils.d.ts b/lib/types/utils.d.ts new file mode 100644 index 0000000..884d339 --- /dev/null +++ b/lib/types/utils.d.ts @@ -0,0 +1,6 @@ +import { substitutes } from 'lib/icons'; + +type SubstituteKeys = keyof typeof substitutes; + +export type ThrottleFn = (cmd: string, fn: ((output: string) => void) | undefined) => void; +export type ThrottleFnCallback = ((output: string) => void) | undefined; diff --git a/lib/types/variable.d.ts b/lib/types/variable.d.ts new file mode 100644 index 0000000..af540dd --- /dev/null +++ b/lib/types/variable.d.ts @@ -0,0 +1 @@ +export type Bind = OriginalBinding, unknown>; diff --git a/lib/types/volume.d.ts b/lib/types/volume.d.ts new file mode 100644 index 0000000..5e2cf32 --- /dev/null +++ b/lib/types/volume.d.ts @@ -0,0 +1,3 @@ +export type VolumeIcons = { + [index: number]: string; +}; diff --git a/lib/types/weather.d.ts b/lib/types/weather.d.ts index 54753ab..daf0e1a 100644 --- a/lib/types/weather.d.ts +++ b/lib/types/weather.d.ts @@ -1,10 +1,12 @@ -export type UnitType = "imperial" | "metric"; +import { weatherIcons } from 'modules/icons/weather'; + +export type UnitType = 'imperial' | 'metric'; export type Weather = { location: Location; current: Current; forecast: Forecast; -} +}; export type Current = { last_updated_epoch?: number; @@ -43,17 +45,17 @@ export type Current = { chance_of_rain?: number; will_it_snow?: number; chance_of_snow?: number; -} +}; export type Condition = { text: string; icon: string; code: number; -} +}; export type Forecast = { forecastday: Forecastday[]; -} +}; export type Forecastday = { date: string; @@ -61,7 +63,7 @@ export type Forecastday = { day: Day; astro: Astro; hour: Current[]; -} +}; export type Astro = { sunrise: string; @@ -72,7 +74,7 @@ export type Astro = { moon_illumination: number; is_moon_up: number; is_sun_up: number; -} +}; export type Day = { maxtemp_c: number; @@ -95,7 +97,7 @@ export type Day = { daily_chance_of_snow: number; condition: Condition; uv: number; -} +}; export type Location = { name: string; @@ -106,4 +108,11 @@ export type Location = { tz_id: string; localtime_epoch: number; localtime: string; -} +}; + +export type TemperatureIconColorMap = { + [key: number]: string; +}; + +export type WeatherIconTitle = keyof typeof weatherIcons; +export type WeatherIcon = (typeof weatherIcons)[WeatherIconTitle]; diff --git a/lib/types/widget.d.ts b/lib/types/widget.d.ts index 7fcda61..57dfd0b 100644 --- a/lib/types/widget.d.ts +++ b/lib/types/widget.d.ts @@ -1,3 +1,28 @@ +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; +import Box from 'types/widgets/box'; + export type Exclusivity = 'normal' | 'ignore' | 'exclusive'; -export type Anchor = "left" | "right" | "top" | "down"; -export type Transition = "none" | "crossfade" | "slide_right" | "slide_left" | "slide_up" | "slide_down"; +export type Anchor = 'left' | 'right' | 'top' | 'down'; +export type Transition = 'none' | 'crossfade' | 'slide_right' | 'slide_left' | 'slide_up' | 'slide_down'; + +export type Layouts = + | 'center' + | 'top' + | 'top-right' + | 'top-center' + | 'top-left' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right'; + +export type Attribute = unknown; +export type Child = Gtk.Widget; +export type GtkWidget = Gtk.Widget; +export type BoxWidget = Box; + +export type GButton = Gtk.Button; +export type GBox = Gtk.Box; +export type GLabel = Gtk.Label; +export type GCenterBox = Gtk.Box; + +export type EventHandler = (self: Self, event: Gdk.Event) => boolean | unknown; diff --git a/lib/types/workspace.d.ts b/lib/types/workspace.d.ts index 1117291..9c46c15 100644 --- a/lib/types/workspace.d.ts +++ b/lib/types/workspace.d.ts @@ -1,8 +1,25 @@ export type WorkspaceRule = { - workspaceString: string, - monitor: string, -} + workspaceString: string; + monitor: string; +}; export type WorkspaceMap = { - [key: string]: number[], -} + [key: string]: number[]; +}; + +export type MonitorMap = { + [key: number]: string; +}; + +export type WorkspaceIcons = { + [key: string]: string; +}; + +export type WorkspaceIconsColored = { + [key: string]: { + color: string; + icon: string; + }; +}; + +export type WorkspaceIconMap = WorkspaceIcons | WorkspaceIconsColored; diff --git a/lib/utils.ts b/lib/utils.ts index 10446b9..8c50008 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,59 +1,65 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { type Application } from "types/service/applications" -import { NotificationAnchor } from "./types/options" -import { OSDAnchor } from "lib/types/options"; -import icons, { substitutes } from "./icons" -import Gtk from "gi://Gtk?version=3.0" -import Gdk from "gi://Gdk" -import GLib from "gi://GLib?version=2.0" -import GdkPixbuf from "gi://GdkPixbuf"; -import { NotificationArgs } from "types/utils/notify" +import { type Application } from 'types/service/applications'; +import { NotificationAnchor } from './types/options'; +import { OSDAnchor } from 'lib/types/options'; +import icons, { substitutes } from './icons'; +import Gtk from 'gi://Gtk?version=3.0'; +import Gdk from 'gi://Gdk'; +import GLib from 'gi://GLib?version=2.0'; +import GdkPixbuf from 'gi://GdkPixbuf'; +import { NotificationArgs } from 'types/utils/notify'; +import { SubstituteKeys } from './types/utils'; +import { Window } from 'types/@girs/gtk-3.0/gtk-3.0.cjs'; +import { namedColors } from './constants/colors'; -export type Binding = import("types/service").Binding +export type Binding = import('types/service').Binding; /** - * @returns substitute icon || name || fallback icon - */ -export function icon(name: string | null, fallback = icons.missing) { - if (!name) - return fallback || "" + * @returns substitute icon || name || fallback icon + */ +export function icon(name: string | null, fallback = icons.missing): string { + const validateSubstitute = (name: string): name is SubstituteKeys => name in substitutes; - if (GLib.file_test(name, GLib.FileTest.EXISTS)) - return name + if (!name) return fallback || ''; - const icon = (substitutes[name] || name) - if (Utils.lookUpIcon(icon)) - return icon + if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name; - print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`) - return fallback + let icon: string = name; + + if (validateSubstitute(name)) { + icon = substitutes[name]; + } + + if (Utils.lookUpIcon(icon)) return icon; + + print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`); + return fallback; } /** * @returns execAsync(["bash", "-c", cmd]) */ -export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]) { - const cmd = typeof strings === "string" ? strings : strings - .flatMap((str, i) => str + `${values[i] ?? ""}`) - .join("") +export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]): Promise { + const cmd = + typeof strings === 'string' ? strings : strings.flatMap((str, i) => str + `${values[i] ?? ''}`).join(''); - return Utils.execAsync(["bash", "-c", cmd]).catch(err => { - console.error(cmd, err) - return "" - }) + return Utils.execAsync(['bash', '-c', cmd]).catch((err) => { + console.error(cmd, err); + return ''; + }); } /** * @returns execAsync(cmd) */ -export async function sh(cmd: string | string[]) { - return Utils.execAsync(cmd).catch(err => { - console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err) - return "" - }) +export async function sh(cmd: string | string[]): Promise { + return Utils.execAsync(cmd).catch((err) => { + console.error(typeof cmd === 'string' ? cmd : cmd.join(' '), err); + return ''; + }); } -export function forMonitors(widget: (monitor: number) => Gtk.Window) { +export function forMonitors(widget: (monitor: number) => Gtk.Window): Window[] { const n = Gdk.Display.get_default()?.get_n_monitors() || 1; return range(n, 0).flatMap(widget); } @@ -61,64 +67,62 @@ export function forMonitors(widget: (monitor: number) => Gtk.Window) { /** * @returns [start...length] */ -export function range(length: number, start = 1) { - return Array.from({ length }, (_, i) => i + start) +export function range(length: number, start = 1): number[] { + return Array.from({ length }, (_, i) => i + start); } /** * @returns true if all of the `bins` are found */ -export function dependencies(...bins: string[]) { - const missing = bins.filter(bin => Utils.exec({ - cmd: `which ${bin}`, - out: () => false, - err: () => true, - })) +export function dependencies(...bins: string[]): boolean { + const missing = bins.filter((bin) => + Utils.exec({ + cmd: `which ${bin}`, + out: () => false, + err: () => true, + }), + ); if (missing.length > 0) { - console.warn(Error(`missing dependencies: ${missing.join(", ")}`)) + console.warn(Error(`missing dependencies: ${missing.join(', ')}`)); Notify({ - summary: "Dependencies not found!", - body: `The following dependencies are missing: ${missing.join(", ")}`, + summary: 'Dependencies not found!', + body: `The following dependencies are missing: ${missing.join(', ')}`, iconName: icons.ui.warning, - timeout: 7000 + timeout: 7000, }); } - return missing.length === 0 + return missing.length === 0; } /** * run app detached */ -export function launchApp(app: Application) { +export function launchApp(app: Application): void { const exe = app.executable .split(/\s+/) - .filter(str => !str.startsWith("%") && !str.startsWith("@")) - .join(" ") + .filter((str) => !str.startsWith('%') && !str.startsWith('@')) + .join(' '); - bash(`${exe} &`) - app.frequency += 1 + bash(`${exe} &`); + app.frequency += 1; } /** * to use with drag and drop */ -export function createSurfaceFromWidget(widget: Gtk.Widget) { +export function createSurfaceFromWidget(widget: Gtk.Widget): GdkPixbuf.Pixbuf { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const cairo = imports.gi.cairo as any - const alloc = widget.get_allocation() - const surface = new cairo.ImageSurface( - cairo.Format.ARGB32, - alloc.width, - alloc.height, - ) - const cr = new cairo.Context(surface) - cr.setSourceRGBA(255, 255, 255, 0) - cr.rectangle(0, 0, alloc.width, alloc.height) - cr.fill() - widget.draw(cr) - return surface + const cairo = imports.gi.cairo as any; + const alloc = widget.get_allocation(); + const surface = new cairo.ImageSurface(cairo.Format.ARGB32, alloc.width, alloc.height); + const cr = new cairo.Context(surface); + cr.setSourceRGBA(255, 255, 255, 0); + cr.rectangle(0, 0, alloc.width, alloc.height); + cr.fill(); + widget.draw(cr); + return surface; } /** @@ -129,9 +133,10 @@ export const isAnImage = (imgFilePath: string): boolean => { GdkPixbuf.Pixbuf.new_from_file(imgFilePath); return true; } catch (error) { + console.error(error); return false; } -} +}; export const Notify = (notifPayload: NotificationArgs): void => { let command = 'notify-send'; @@ -145,20 +150,42 @@ export const Notify = (notifPayload: NotificationArgs): void => { if (notifPayload.transient) command += ` -e`; if (notifPayload.id !== undefined) command += ` -r ${notifPayload.id}`; - Utils.execAsync(command) -} + Utils.execAsync(command); +}; -export function getPosition(pos: NotificationAnchor | OSDAnchor): ("top" | "bottom" | "left" | "right")[] { - const positionMap: { [key: string]: ("top" | "bottom" | "left" | "right")[] } = { - "top": ["top"], - "top right": ["top", "right"], - "top left": ["top", "left"], - "bottom": ["bottom"], - "bottom right": ["bottom", "right"], - "bottom left": ["bottom", "left"], - "right": ["right"], - "left": ["left"], +export function getPosition(pos: NotificationAnchor | OSDAnchor): ('top' | 'bottom' | 'left' | 'right')[] { + const positionMap: { [key: string]: ('top' | 'bottom' | 'left' | 'right')[] } = { + top: ['top'], + 'top right': ['top', 'right'], + 'top left': ['top', 'left'], + bottom: ['bottom'], + 'bottom right': ['bottom', 'right'], + 'bottom left': ['bottom', 'left'], + right: ['right'], + left: ['left'], }; - return positionMap[pos] || ["top"]; + return positionMap[pos] || ['top']; } +export const isValidGjsColor = (color: string): boolean => { + const colorLower = color.toLowerCase().trim(); + + if (namedColors.has(colorLower)) { + return true; + } + + const hexColorRegex = /^#(?:[a-fA-F0-9]{3,4}|[a-fA-F0-9]{6,8})$/; + + const rgbRegex = /^rgb\(\s*(\d{1,3}%?\s*,\s*){2}\d{1,3}%?\s*\)$/; + const rgbaRegex = /^rgba\(\s*(\d{1,3}%?\s*,\s*){3}(0|1|0?\.\d+)\s*\)$/; + + if (hexColorRegex.test(color)) { + return true; + } + + if (rgbRegex.test(colorLower) || rgbaRegex.test(colorLower)) { + return true; + } + + return false; +}; diff --git a/lib/variables.ts b/lib/variables.ts index 71c47b4..303c121 100644 --- a/lib/variables.ts +++ b/lib/variables.ts @@ -1,16 +1,15 @@ -import GLib from "gi://GLib" +import GLib from 'gi://GLib'; +import { DateTime } from 'types/@girs/glib-2.0/glib-2.0.cjs'; export const clock = Variable(GLib.DateTime.new_now_local(), { - poll: [1000, () => GLib.DateTime.new_now_local()], -}) + poll: [1000, (): DateTime => GLib.DateTime.new_now_local()], +}); export const uptime = Variable(0, { - poll: [60_000, "cat /proc/uptime", line => - Number.parseInt(line.split(".")[0]) / 60, - ], -}) + poll: [60_000, 'cat /proc/uptime', (line): number => Number.parseInt(line.split('.')[0]) / 60], +}); export const distro = { - id: GLib.get_os_info("ID"), - logo: GLib.get_os_info("LOGO"), -} + id: GLib.get_os_info('ID'), + logo: GLib.get_os_info('LOGO'), +}; diff --git a/main.ts b/main.ts index 8e55a66..6f995a6 100644 --- a/main.ts +++ b/main.ts @@ -1,27 +1,21 @@ -import "lib/session"; -import "scss/style"; -import "globals/useTheme"; -import "globals/mousePos"; +import 'lib/session'; +import 'scss/style'; +import 'globals/useTheme'; +import 'globals/mousePos'; -import { Bar } from "modules/bar/Bar"; -import MenuWindows from "./modules/menus/main.js"; -import SettingsDialog from "widget/settings/SettingsDialog"; -import Notifications from "./modules/notifications/index.js"; -import { forMonitors } from "lib/utils"; -import OSD from "modules/osd/index"; +import { Bar } from 'modules/bar/Bar'; +import MenuWindows from './modules/menus/main.js'; +import SettingsDialog from 'widget/settings/SettingsDialog'; +import Notifications from './modules/notifications/index.js'; +import { forMonitors } from 'lib/utils'; +import OSD from 'modules/osd/index'; App.config({ onConfigParsed: () => Utils.execAsync(`python3 ${App.configDir}/services/bluetooth.py`), - windows: [ - ...MenuWindows, - Notifications(), - SettingsDialog(), - ...forMonitors(Bar), - OSD(), - ], + windows: [...MenuWindows, Notifications(), SettingsDialog(), ...forMonitors(Bar), OSD()], closeWindowDelay: { sideright: 350, launcher: 350, bar0: 350, }, -}) +}); diff --git a/modules/bar/Bar.ts b/modules/bar/Bar.ts index b3b4f43..7898985 100644 --- a/modules/bar/Bar.ts +++ b/modules/bar/Bar.ts @@ -1,8 +1,10 @@ -const hyprland = await Service.import("hyprland"); +const hyprland = await Service.import('hyprland'); import { Menu, - Workspaces, ClientTitle, Media, + Workspaces, + ClientTitle, + Media, Notifications, Volume, Network, @@ -20,121 +22,114 @@ import { Updates, Weather, Power, -} from "./Exports" +} from './Exports'; -import { BarItemBox as WidgetContainer } from "../shared/barItemBox.js"; -import options from "options"; -import Gdk from "gi://Gdk?version=3.0"; -import Button from "types/widgets/button.js"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0.js"; +import { BarItemBox as WidgetContainer } from '../shared/barItemBox.js'; +import options from 'options'; +import Gdk from 'gi://Gdk?version=3.0'; +import Button from 'types/widgets/button.js'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js'; import './SideEffects'; +import { WindowLayer } from 'lib/types/options.js'; +import { Attribute, Child } from 'lib/types/widget.js'; +import Window from 'types/widgets/window.js'; const { layouts } = options.bar; +const { location } = options.theme.bar; export type BarWidget = keyof typeof widget; -type Section = "battery" - | "dashboard" - | "workspaces" - | "windowtitle" - | "media" - | "notifications" - | "volume" - | "network" - | "bluetooth" - | "clock" - | "Ram" - | "Cpu" - | "Storage" - | "Netstat" - | "KbInput" - | "Updates" - | "Weather" - | "Power" - | "systray"; +type Section = + | 'battery' + | 'dashboard' + | 'workspaces' + | 'windowtitle' + | 'media' + | 'notifications' + | 'volume' + | 'network' + | 'bluetooth' + | 'clock' + | 'ram' + | 'cpu' + | 'storage' + | 'netstat' + | 'kbinput' + | 'updates' + | 'weather' + | 'power' + | 'systray'; type Layout = { - left: Section[], - middle: Section[], - right: Section[], -} + left: Section[]; + middle: Section[]; + right: Section[]; +}; type BarLayout = { - [key: string]: Layout -} + [key: string]: Layout; +}; -const getModulesForMonitor = (monitor: number, curLayouts: BarLayout) => { - const foundMonitor = Object.keys(curLayouts).find(mon => mon === monitor.toString()); +const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => { + const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString()); + const wildcard = Object.keys(layouts).find((key) => key === '*'); - const defaultSetup: Layout = { - left: [ - "dashboard", - "workspaces", - "windowtitle" - ], - middle: [ - "media" - ], - right: [ - "volume", - "network", - "bluetooth", - "battery", - "systray", - "clock", - "notifications" - ] + if (matchingKey) { + return layouts[matchingKey]; } - if (foundMonitor === undefined) { - return defaultSetup; + if (wildcard) { + return layouts[wildcard]; } - return curLayouts[foundMonitor]; - -} + return { + left: ['dashboard', 'workspaces', 'windowtitle'], + middle: ['media'], + right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'], + }; +}; const widget = { - battery: () => WidgetContainer(BatteryLabel()), - dashboard: () => WidgetContainer(Menu()), - workspaces: (monitor: number) => WidgetContainer(Workspaces(monitor)), - windowtitle: () => WidgetContainer(ClientTitle()), - media: () => WidgetContainer(Media()), - notifications: () => WidgetContainer(Notifications()), - volume: () => WidgetContainer(Volume()), - network: () => WidgetContainer(Network()), - bluetooth: () => WidgetContainer(Bluetooth()), - clock: () => WidgetContainer(Clock()), - systray: () => WidgetContainer(SysTray()), - ram: () => WidgetContainer(Ram()), - cpu: () => WidgetContainer(Cpu()), - storage: () => WidgetContainer(Storage()), - netstat: () => WidgetContainer(Netstat()), - kbinput: () => WidgetContainer(KbInput()), - updates: () => WidgetContainer(Updates()), - weather: () => WidgetContainer(Weather()), - power: () => WidgetContainer(Power()), + battery: (): Button => WidgetContainer(BatteryLabel()), + dashboard: (): Button => WidgetContainer(Menu()), + workspaces: (monitor: number): Button => WidgetContainer(Workspaces(monitor)), + windowtitle: (): Button => WidgetContainer(ClientTitle()), + media: (): Button => WidgetContainer(Media()), + notifications: (): Button => WidgetContainer(Notifications()), + volume: (): Button => WidgetContainer(Volume()), + network: (): Button => WidgetContainer(Network()), + bluetooth: (): Button => WidgetContainer(Bluetooth()), + clock: (): Button => WidgetContainer(Clock()), + systray: (): Button => WidgetContainer(SysTray()), + ram: (): Button => WidgetContainer(Ram()), + cpu: (): Button => WidgetContainer(Cpu()), + storage: (): Button => WidgetContainer(Storage()), + netstat: (): Button => WidgetContainer(Netstat()), + kbinput: (): Button => WidgetContainer(KbInput()), + updates: (): Button => WidgetContainer(Updates()), + weather: (): Button => WidgetContainer(Weather()), + power: (): Button => WidgetContainer(Power()), }; type GdkMonitors = { [key: string]: { - key: string, - model: string, - used: boolean - } + key: string; + model: string; + used: boolean; + }; }; function getGdkMonitors(): GdkMonitors { const display = Gdk.Display.get_default(); if (display === null) { - console.error("Failed to get Gdk display."); + console.error('Failed to get Gdk display.'); return {}; } const numGdkMonitors = display.get_n_monitors(); - const gdkMonitors = {}; + const gdkMonitors: GdkMonitors = {}; for (let i = 0; i < numGdkMonitors; i++) { const curMonitor = display.get_monitor(i); @@ -144,7 +139,7 @@ function getGdkMonitors(): GdkMonitors { continue; } - const model = curMonitor.get_model(); + const model = curMonitor.get_model() || ''; const geometry = curMonitor.get_geometry(); const scaleFactor = curMonitor.get_scale_factor(); @@ -159,35 +154,35 @@ function getGdkMonitors(): GdkMonitors { * NOTE: Some more funky stuff being done by GDK. * We render windows/bar based on the monitor ID. So if you have 3 monitors, then your * monitor IDs will be [0, 1, 2]. Hyprland will NEVER change what ID belongs to what monitor. - * + * * So if hyprland determines id 0 = DP-1, even after you unplug, shut off or restart your monitor, * the id 0 will ALWAYS be DP-1. - * + * * However, GDK (the righteous genius that it is) will change the order of ID anytime your monitor * setup is changed. So if you unplug your monitor and plug it back it, it now becomes the last id. * So if DP-1 was id 0 and you unplugged it, it will reconfigure to id 2. This sucks because now * there's a mismtach between what GDK determines the monitor is at id 2 and what Hyprland determines * is at id 2. - * + * * So for that reason, we need to redirect the input `monitor` that the Bar module takes in, to the * proper Hyprland monitor. So when monitor id 0 comes in, we need to find what the id of that monitor * is being determined as by Hyprland so the bars show up on the right monitors. - * + * * Since GTK3 doesn't contain connection names and only monitor models, we have to make the best guess * in the case that there are multiple models in the same resolution with the same scale. We find the * 'right' monitor by checking if the model matches along with the resolution and scale. If monitor at * ID 0 for GDK is being reported as 'MSI MAG271CQR' we find the same model in the Hyprland monitor list * and check if the resolution and scaling is the same... if it is then we determine it's a match. - * + * * The edge-case that we just can't handle is if you have the same monitors in the same resolution at the same * scale. So if you've got 2 'MSI MAG271CQR' monitors at 2560x1440 at scale 1, then we just match the first - * monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration. - * You may have the bar showing up on the wrong one in this case because we don't know what the connector id + * monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration. + * You may have the bar showing up on the wrong one in this case because we don't know what the connector id * is of either of these monitors (DP-1, DP-2) which are unique values - as these are only in GTK4. - * + * * Keep in mind though, this is ONLY an issue if you change your monitor setup by plugging in a new one, restarting * an existing one or shutting it off. - * + * * If your monitors aren't changed in the current session you're in then none of this safeguarding is relevant. * * Fun stuff really... :facepalm: @@ -197,7 +192,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set { + const directMatch = hyprland.monitors.find((hypMon) => { const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`; return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id) && hypMon.id === monitor; }); @@ -216,7 +211,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set { + const hyprlandMonitor = hyprland.monitors.find((hypMon) => { const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`; return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id); }); @@ -227,7 +222,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set !usedHyprlandMonitors.has(hypMon.id)); + const fallbackMonitor = hyprland.monitors.find((hypMon) => !usedHyprlandMonitors.has(hypMon.id)); if (fallbackMonitor) { usedHyprlandMonitors.add(fallbackMonitor.id); @@ -250,49 +245,63 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set { const usedHyprlandMonitors = new Set(); - return (monitor: number) => { + return (monitor: number): Window => { const hyprlandMonitor = gdkMonitorIdToHyprlandId(monitor, usedHyprlandMonitors); return Widget.Window({ name: `bar-${hyprlandMonitor}`, - class_name: "bar", + class_name: 'bar', monitor, visible: true, - anchor: ["top", "left", "right"], - exclusivity: "exclusive", - layer: options.theme.bar.layer.bind("value"), + anchor: location.bind('value').as((ln) => [ln, 'left', 'right']), + exclusivity: 'exclusive', + layer: Utils.merge( + [options.theme.bar.layer.bind('value'), options.tear.bind('value')], + (barLayer: WindowLayer, tear: boolean) => { + if (tear && barLayer === 'overlay') { + return 'top'; + } + return barLayer; + }, + ), child: Widget.Box({ class_name: 'bar-panel-container', child: Widget.CenterBox({ class_name: 'bar-panel', css: 'padding: 1px', startWidget: Widget.Box({ - class_name: "box-left", + class_name: 'box-left', hexpand: true, - setup: self => { + setup: (self) => { self.hook(layouts, (self) => { - const foundLayout = getModulesForMonitor(hyprlandMonitor, layouts.value as BarLayout); - self.children = foundLayout.left.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button); + const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout); + self.children = foundLayout.left + .filter((mod) => Object.keys(widget).includes(mod)) + .map((w) => widget[w](hyprlandMonitor) as Button); }); }, }), centerWidget: Widget.Box({ - class_name: "box-center", - hpack: "center", - setup: self => { + class_name: 'box-center', + hpack: 'center', + setup: (self) => { self.hook(layouts, (self) => { - const foundLayout = getModulesForMonitor(hyprlandMonitor, layouts.value as BarLayout); - self.children = foundLayout.middle.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button); + const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout); + self.children = foundLayout.middle + .filter((mod) => Object.keys(widget).includes(mod)) + .map((w) => widget[w](hyprlandMonitor) as Button); }); }, }), endWidget: Widget.Box({ - class_name: "box-right", - hpack: "end", - setup: self => { + class_name: 'box-right', + hpack: 'end', + setup: (self) => { self.hook(layouts, (self) => { - const foundLayout = getModulesForMonitor(hyprlandMonitor, layouts.value as BarLayout); - self.children = foundLayout.right.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button); + const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout); + self.children = foundLayout.right + .filter((mod) => Object.keys(widget).includes(mod)) + .map((w) => widget[w](hyprlandMonitor) as Button); }); }, }), diff --git a/modules/bar/Exports.ts b/modules/bar/Exports.ts index 9990bb4..c6a540a 100644 --- a/modules/bar/Exports.ts +++ b/modules/bar/Exports.ts @@ -1,24 +1,24 @@ -import { Menu } from "./menu/index"; -import { Workspaces } from "./workspaces/index"; -import { ClientTitle } from "./window_title/index"; -import { Media } from "./media/index"; -import { Notifications } from "./notifications/index"; -import { Volume } from "./volume/index"; -import { Network } from "./network/index"; -import { Bluetooth } from "./bluetooth/index"; -import { BatteryLabel } from "./battery/index"; -import { Clock } from "./clock/index"; -import { SysTray } from "./systray/index"; +import { Menu } from './menu/index'; +import { Workspaces } from './workspaces/index'; +import { ClientTitle } from './window_title/index'; +import { Media } from './media/index'; +import { Notifications } from './notifications/index'; +import { Volume } from './volume/index'; +import { Network } from './network/index'; +import { Bluetooth } from './bluetooth/index'; +import { BatteryLabel } from './battery/index'; +import { Clock } from './clock/index'; +import { SysTray } from './systray/index'; // Custom Modules -import { Ram } from "../../customModules/ram/index"; -import { Cpu } from "../../customModules/cpu/index"; -import { Storage } from "customModules/storage/index"; -import { Netstat } from "customModules/netstat/index"; -import { KbInput } from "customModules/kblayout/index"; -import { Updates } from "customModules/updates/index"; -import { Weather } from "customModules/weather/index"; -import { Power } from "customModules/power/index"; +import { Ram } from '../../customModules/ram/index'; +import { Cpu } from '../../customModules/cpu/index'; +import { Storage } from 'customModules/storage/index'; +import { Netstat } from 'customModules/netstat/index'; +import { KbInput } from 'customModules/kblayout/index'; +import { Updates } from 'customModules/updates/index'; +import { Weather } from 'customModules/weather/index'; +import { Power } from 'customModules/power/index'; export { Menu, diff --git a/modules/bar/SideEffects.ts b/modules/bar/SideEffects.ts index 2a30311..8b055bf 100644 --- a/modules/bar/SideEffects.ts +++ b/modules/bar/SideEffects.ts @@ -1,14 +1,14 @@ -import options from "options"; +import options from 'options'; const { showIcon, showTime } = options.bar.clock; -showIcon.connect("changed", () => { +showIcon.connect('changed', () => { if (!showTime.value && !showIcon.value) { showTime.value = true; } }); -showTime.connect("changed", () => { +showTime.connect('changed', () => { if (!showTime.value && !showIcon.value) { showIcon.value = true; } diff --git a/modules/bar/battery/index.ts b/modules/bar/battery/index.ts index e35c511..98ed650 100644 --- a/modules/bar/battery/index.ts +++ b/modules/bar/battery/index.ts @@ -1,34 +1,37 @@ -const battery = await Service.import("battery"); +const battery = await Service.import('battery'); import Gdk from 'gi://Gdk?version=3.0'; -import { openMenu } from "../utils.js"; -import options from "options"; +import { openMenu } from '../utils.js'; +import options from 'options'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { label: show_label } = options.bar.battery; -const BatteryLabel = () => { +const BatteryLabel = (): BarBoxChild => { const isVis = Variable(battery.available); - const batIcon = Utils.merge([battery.bind("percent"), battery.bind("charging"), battery.bind("charged")], + const batIcon = Utils.merge( + [battery.bind('percent'), battery.bind('charging'), battery.bind('charged')], (batPercent: number, batCharging, batCharged) => { - if (batCharged) - return `battery-level-100-charged-symbolic`; - else - return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`; - }); + if (batCharged) return `battery-level-100-charged-symbolic`; + else return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`; + }, + ); - battery.connect("changed", ({ available }) => { + battery.connect('changed', ({ available }) => { isVis.value = available; }); - const formatTime = (seconds: number) => { + const formatTime = (seconds: number): Record => { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); return { hours, minutes }; }; - const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean) => { + const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean): string => { if (isCharged) { - return "Fully Charged!!!"; + return 'Fully Charged!!!'; } const { hours, minutes } = formatTime(timeSeconds); @@ -41,59 +44,56 @@ const BatteryLabel = () => { return { component: Widget.Box({ - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `battery ${styleMap[style]} ${!showLabel ? "no-label" : ""}`; - }), - visible: battery.bind("available"), - tooltip_text: battery.bind("time_remaining").as((t) => t.toString()), - children: Utils.merge( - [battery.bind("available"), show_label.bind("value")], - (batAvail, showLabel) => { - if (batAvail && showLabel) { - return [ - Widget.Icon({ - class_name: "bar-button-icon battery", - icon: batIcon - }), - Widget.Label({ - class_name: "bar-button-label battery", - label: battery.bind("percent").as((p) => `${Math.floor(p)}%`), - }), - ]; - } else if (batAvail && !showLabel) { - return [ - Widget.Icon({ - class_name: "bar-button-icon battery", - icon: batIcon - }) - ]; - } else { - return []; - } + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), show_label.bind('value')], + (style, showLabel) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `battery ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`; }, ), + visible: battery.bind('available'), + tooltip_text: battery.bind('time_remaining').as((t) => t.toString()), + children: Utils.merge([battery.bind('available'), show_label.bind('value')], (batAvail, showLabel) => { + if (batAvail && showLabel) { + return [ + Widget.Icon({ + class_name: 'bar-button-icon battery', + icon: batIcon, + }), + Widget.Label({ + class_name: 'bar-button-label battery', + label: battery.bind('percent').as((p) => `${Math.floor(p)}%`), + }), + ]; + } else if (batAvail && !showLabel) { + return [ + Widget.Icon({ + class_name: 'bar-button-icon battery', + icon: batIcon, + }), + ]; + } else { + return []; + } + }), setup: (self) => { self.hook(battery, () => { if (battery.available) { - self.tooltip_text = generateTooltip( - battery.time_remaining, - battery.charging, - battery.charged, - ); + self.tooltip_text = generateTooltip(battery.time_remaining, battery.charging, battery.charged); } }); }, }), isVis, - boxClass: "battery", + boxClass: 'battery', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "energymenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'energymenu'); }, }, }; diff --git a/modules/bar/bluetooth/index.ts b/modules/bar/bluetooth/index.ts index 8d1662b..c9cbb45 100644 --- a/modules/bar/bluetooth/index.ts +++ b/modules/bar/bluetooth/index.ts @@ -1,41 +1,41 @@ -const bluetooth = await Service.import('bluetooth') +const bluetooth = await Service.import('bluetooth'); import Gdk from 'gi://Gdk?version=3.0'; -import options from "options"; -import { openMenu } from "../utils.js"; +import options from 'options'; +import { openMenu } from '../utils.js'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { label } = options.bar.bluetooth; -const Bluetooth = () => { +const Bluetooth = (): BarBoxChild => { const btIcon = Widget.Label({ - label: bluetooth.bind("enabled").as((v) => v ? "󰂯" : "󰂲"), - class_name: "bar-button-icon bluetooth txt-icon bar", + label: bluetooth.bind('enabled').as((v) => (v ? '󰂯' : '󰂲')), + class_name: 'bar-button-icon bluetooth txt-icon bar', }); const btText = Widget.Label({ - label: Utils.merge([ - bluetooth.bind("enabled"), - bluetooth.bind("connected_devices"), - ], - (btEnabled, btDevices) => { - return btEnabled && btDevices.length ? ` Connected (${btDevices.length})` - : btEnabled ? "On" - : "Off" - - }), - class_name: "bar-button-label bluetooth", + label: Utils.merge([bluetooth.bind('enabled'), bluetooth.bind('connected_devices')], (btEnabled, btDevices) => { + return btEnabled && btDevices.length ? ` Connected (${btDevices.length})` : btEnabled ? 'On' : 'Off'; + }), + class_name: 'bar-button-label bluetooth', }); return { component: Widget.Box({ - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), label.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `bluetooth ${styleMap[style]} ${!showLabel ? "no-label" : ""}`; - }), - children: options.bar.bluetooth.label.bind("value").as((showLabel) => { + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), label.bind('value')], + (style, showLabel) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `bluetooth ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`; + }, + ), + children: options.bar.bluetooth.label.bind('value').as((showLabel) => { if (showLabel) { return [btIcon, btText]; } @@ -43,14 +43,13 @@ const Bluetooth = () => { }), }), isVisible: true, - boxClass: "bluetooth", + boxClass: 'bluetooth', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "bluetoothmenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'bluetoothmenu'); }, }, }; +}; -} - -export { Bluetooth } +export { Bluetooth }; diff --git a/modules/bar/clock/index.ts b/modules/bar/clock/index.ts index fb99e3f..c55a505 100644 --- a/modules/bar/clock/index.ts +++ b/modules/bar/clock/index.ts @@ -1,44 +1,46 @@ import Gdk from 'gi://Gdk?version=3.0'; -import GLib from "gi://GLib"; -import { openMenu } from "../utils.js"; -import options from "options"; +import GLib from 'gi://GLib'; +import { openMenu } from '../utils.js'; +import options from 'options'; +import { DateTime } from 'types/@girs/glib-2.0/glib-2.0.cjs'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { format, icon, showIcon, showTime } = options.bar.clock; const { style } = options.theme.bar.buttons; - const date = Variable(GLib.DateTime.new_now_local(), { - poll: [1000, () => GLib.DateTime.new_now_local()], + poll: [1000, (): DateTime => GLib.DateTime.new_now_local()], }); -const time = Utils.derive([date, format], (c, f) => c.format(f) || ""); - -const Clock = () => { +const time = Utils.derive([date, format], (c, f) => c.format(f) || ''); +const Clock = (): BarBoxChild => { const clockTime = Widget.Label({ - class_name: "bar-button-label clock bar", + class_name: 'bar-button-label clock bar', label: time.bind(), }); const clockIcon = Widget.Label({ - label: icon.bind("value"), - class_name: "bar-button-icon clock txt-icon bar", + label: icon.bind('value'), + class_name: 'bar-button-icon clock txt-icon bar', }); return { component: Widget.Box({ - className: Utils.merge([ - style.bind("value"), - showIcon.bind("value"), showTime.bind("value") - ], (btnStyle, shwIcn, shwLbl) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - - return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? "no-label" : ""} ${!shwIcn ? "no-icon" : ""}`; - }), - children: Utils.merge([showIcon.bind("value"), showTime.bind("value")], (shIcn, shTm) => { + className: Utils.merge( + [style.bind('value'), showIcon.bind('value'), showTime.bind('value')], + (btnStyle, shwIcn, shwLbl) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? 'no-label' : ''} ${!shwIcn ? 'no-icon' : ''}`; + }, + ), + children: Utils.merge([showIcon.bind('value'), showTime.bind('value')], (shIcn, shTm) => { if (shIcn && !shTm) { return [clockIcon]; } else if (shTm && !shIcn) { @@ -46,13 +48,13 @@ const Clock = () => { } return [clockIcon, clockTime]; - }) + }), }), isVisible: true, - boxClass: "clock", + boxClass: 'clock', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "calendarmenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'calendarmenu'); }, }, }; diff --git a/modules/bar/media/index.ts b/modules/bar/media/index.ts index e7f5237..7266a5c 100644 --- a/modules/bar/media/index.ts +++ b/modules/bar/media/index.ts @@ -1,20 +1,23 @@ import Gdk from 'gi://Gdk?version=3.0'; -const mpris = await Service.import("mpris"); -import { openMenu } from "../utils.js"; -import options from "options"; +const mpris = await Service.import('mpris'); +import { openMenu } from '../utils.js'; +import options from 'options'; import { getCurrentPlayer } from 'lib/shared/media.js'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { show_artist, truncation, truncation_size, show_label, show_active_only } = options.bar.media; -const Media = () => { +const Media = (): BarBoxChild => { const activePlayer = Variable(mpris.players[0]); const isVis = Variable(!show_active_only.value); - show_active_only.connect("changed", () => { + show_active_only.connect('changed', () => { isVis.value = !show_active_only.value || mpris.players.length > 0; }); - mpris.connect("changed", () => { + mpris.connect('changed', () => { const curPlayer = getCurrentPlayer(activePlayer.value); activePlayer.value = curPlayer; isVis.value = !show_active_only.value || mpris.players.length > 0; @@ -22,41 +25,37 @@ const Media = () => { const getIconForPlayer = (playerName: string): string => { const windowTitleMap = [ - ["Firefox", "󰈹"], - ["Microsoft Edge", "󰇩"], - ["Discord", ""], - ["Plex", "󰚺"], - ["Spotify", "󰓇"], - ["(.*)", "󰝚"], + ['Firefox', '󰈹'], + ['Microsoft Edge', '󰇩'], + ['Discord', ''], + ['Plex', '󰚺'], + ['Spotify', '󰓇'], + ['(.*)', '󰝚'], ]; - const foundMatch = windowTitleMap.find((wt) => - RegExp(wt[0], "i").test(playerName), - ); + const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0], 'i').test(playerName)); - return foundMatch ? foundMatch[1] : "󰝚"; + return foundMatch ? foundMatch[1] : '󰝚'; }; - const songIcon = Variable(""); + const songIcon = Variable(''); - const mediaLabel = Utils.watch("Media", [mpris, show_artist, truncation, truncation_size, show_label], () => { + const mediaLabel = Utils.watch('Media', [mpris, show_artist, truncation, truncation_size, show_label], () => { if (activePlayer.value && show_label.value) { const { track_title, identity, track_artists } = activePlayer.value; songIcon.value = getIconForPlayer(identity); - const trackArtist = show_artist.value - ? ` - ${track_artists.join(', ')}` - : ``; + const trackArtist = show_artist.value ? ` - ${track_artists.join(', ')}` : ``; const truncatedLabel = truncation.value ? `${track_title + trackArtist}`.substring(0, truncation_size.value) : `${track_title + trackArtist}`; return track_title.length === 0 ? `No media playing...` - : ((truncatedLabel.length < truncation_size.value) || !truncation.value) - ? `${truncatedLabel}` - : `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`; + : truncatedLabel.length < truncation_size.value || !truncation.value + ? `${truncatedLabel}` + : `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`; } else { - songIcon.value = getIconForPlayer(activePlayer.value?.identity || ""); + songIcon.value = getIconForPlayer(activePlayer.value?.identity || ''); return `Media`; } }); @@ -65,22 +64,26 @@ const Media = () => { component: Widget.Box({ visible: false, child: Widget.Box({ - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `media ${styleMap[style]}`; - }), + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), show_label.bind('value')], + (style) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `media ${styleMap[style]}`; + }, + ), child: Widget.Box({ children: [ Widget.Label({ - class_name: "bar-button-icon media txt-icon bar", - label: songIcon.bind("value").as(v => v || "󰝚"), + class_name: 'bar-button-icon media txt-icon bar', + label: songIcon.bind('value').as((v) => v || '󰝚'), }), Widget.Label({ - class_name: "bar-button-label media", + class_name: 'bar-button-label media', label: mediaLabel, }), ], @@ -88,13 +91,13 @@ const Media = () => { }), }), isVis, - boxClass: "media", - name: "media", + boxClass: 'media', + name: 'media', props: { on_scroll_up: () => activePlayer.value?.next(), on_scroll_down: () => activePlayer.value?.previous(), - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "mediamenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'mediamenu'); }, }, }; diff --git a/modules/bar/menu/index.ts b/modules/bar/menu/index.ts index a258dc5..5612dc4 100644 --- a/modules/bar/menu/index.ts +++ b/modules/bar/menu/index.ts @@ -1,28 +1,32 @@ import Gdk from 'gi://Gdk?version=3.0'; -import { openMenu } from "../utils.js"; -import options from "options"; +import { openMenu } from '../utils.js'; +import options from 'options'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; -const Menu = () => { +const Menu = (): BarBoxChild => { return { component: Widget.Box({ - className: Utils.merge([options.theme.bar.buttons.style.bind("value")], (style) => { + className: Utils.merge([options.theme.bar.buttons.style.bind('value')], (style) => { const styleMap = { - default: "style1", - split: "style2", - wave: "style3", + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', }; return `dashboard ${styleMap[style]}`; }), child: Widget.Label({ - class_name: "bar-menu_label bar-button_icon txt-icon bar", - label: options.bar.launcher.icon.bind("value"), + class_name: 'bar-menu_label bar-button_icon txt-icon bar', + label: options.bar.launcher.icon.bind('value'), }), }), isVisible: true, - boxClass: "dashboard", + boxClass: 'dashboard', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "dashboardmenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'dashboardmenu'); }, }, }; diff --git a/modules/bar/network/index.ts b/modules/bar/network/index.ts index da944fa..24c11b8 100644 --- a/modules/bar/network/index.ts +++ b/modules/bar/network/index.ts @@ -1,69 +1,77 @@ import Gdk from 'gi://Gdk?version=3.0'; -const network = await Service.import("network"); -import options from "options"; -import { openMenu } from "../utils.js"; +const network = await Service.import('network'); +import options from 'options'; +import { openMenu } from '../utils.js'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { label: networkLabel, truncation, truncation_size } = options.bar.network; -const Network = () => { +const Network = (): BarBoxChild => { return { component: Widget.Box({ - vpack: "fill", + vpack: 'fill', vexpand: true, - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), networkLabel.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `network ${styleMap[style]}${!showLabel ? " no-label" : ""}`; - }), + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), networkLabel.bind('value')], + (style, showLabel) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `network ${styleMap[style]}${!showLabel ? ' no-label' : ''}`; + }, + ), children: [ Widget.Icon({ - class_name: "bar-button-icon network", - icon: Utils.merge([ - network.bind("primary"), - network.bind("wifi"), - network.bind("wired") - ], (pmry, wfi, wrd) => { - if (pmry === "wired") { - return wrd.icon_name; - } - return wfi.icon_name; - }) + class_name: 'bar-button-icon network', + icon: Utils.merge( + [network.bind('primary'), network.bind('wifi'), network.bind('wired')], + (pmry, wfi, wrd) => { + if (pmry === 'wired') { + return wrd.icon_name; + } + return wfi.icon_name; + }, + ), }), Widget.Box({ - vpack: "center", - child: Utils.merge([ - network.bind("primary"), - network.bind("wifi"), - networkLabel.bind("value"), - truncation.bind("value"), - truncation_size.bind("value") - ], (pmry, wfi, showLbl, trunc, tSize) => { - if (!showLbl) { - return Widget.Box(); - } - if (pmry === "wired") { + vpack: 'center', + child: Utils.merge( + [ + network.bind('primary'), + network.bind('wifi'), + networkLabel.bind('value'), + truncation.bind('value'), + truncation_size.bind('value'), + ], + (pmry, wfi, showLbl, trunc, tSize) => { + if (!showLbl) { + return Widget.Box(); + } + if (pmry === 'wired') { + return Widget.Label({ + class_name: 'bar-button-label network', + label: 'Wired'.substring(0, tSize), + }); + } return Widget.Label({ - class_name: "bar-button-label network", - label: "Wired".substring(0, tSize), - }) - } - return Widget.Label({ - class_name: "bar-button-label network", - label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : "--", - }) - - }) + class_name: 'bar-button-label network', + label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : '--', + }); + }, + ), }), - ] + ], }), isVisible: true, - boxClass: "network", + boxClass: 'network', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "networkmenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'networkmenu'); }, }, }; diff --git a/modules/bar/notifications/index.ts b/modules/bar/notifications/index.ts index f168a4e..4a29de4 100644 --- a/modules/bar/notifications/index.ts +++ b/modules/bar/notifications/index.ts @@ -1,39 +1,50 @@ import Gdk from 'gi://Gdk?version=3.0'; -import { openMenu } from "../utils.js"; -import options from "options"; +import { openMenu } from '../utils.js'; +import options from 'options'; +import { filterNotifications } from 'lib/shared/notifications.js'; +import { BarBoxChild } from 'lib/types/bar.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; const { show_total } = options.bar.notifications; +const { ignore } = options.notifications; -const notifs = await Service.import("notifications"); +const notifs = await Service.import('notifications'); -export const Notifications = () => { +export const Notifications = (): BarBoxChild => { return { component: Widget.Box({ - hpack: "start", - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_total.bind("value")], (style, showTotal) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `notifications ${styleMap[style]} ${!showTotal ? "no-label" : ""}`; - }), + hpack: 'start', + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), show_total.bind('value')], + (style, showTotal) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `notifications ${styleMap[style]} ${!showTotal ? 'no-label' : ''}`; + }, + ), child: Widget.Box({ - hpack: "start", - class_name: "bar-notifications", + hpack: 'start', + class_name: 'bar-notifications', children: Utils.merge( - [notifs.bind("notifications"), notifs.bind("dnd"), show_total.bind("value")], - (notif, dnd, showTotal) => { + [notifs.bind('notifications'), notifs.bind('dnd'), show_total.bind('value'), ignore.bind('value')], + (notif, dnd, showTotal, ignoredNotifs) => { + const filteredNotifications = filterNotifications(notif, ignoredNotifs); + const notifIcon = Widget.Label({ - hpack: "center", - class_name: "bar-button-icon notifications txt-icon bar", - label: dnd ? "󰂛" : notif.length > 0 ? "󱅫" : "󰂚", + hpack: 'center', + class_name: 'bar-button-icon notifications txt-icon bar', + label: dnd ? '󰂛' : filteredNotifications.length > 0 ? '󱅫' : '󰂚', }); const notifLabel = Widget.Label({ - hpack: "center", - class_name: "bar-button-label notifications", - label: notif.length.toString(), + hpack: 'center', + class_name: 'bar-button-label notifications', + label: filteredNotifications.length.toString(), }); if (showTotal) { @@ -45,10 +56,10 @@ export const Notifications = () => { }), }), isVisible: true, - boxClass: "notifications", + boxClass: 'notifications', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "notificationsmenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'notificationsmenu'); }, }, }; diff --git a/modules/bar/systray/index.ts b/modules/bar/systray/index.ts index 1d4cc0e..0f204c1 100644 --- a/modules/bar/systray/index.ts +++ b/modules/bar/systray/index.ts @@ -1,47 +1,43 @@ import Gdk from 'gi://Gdk?version=3.0'; -const systemtray = await Service.import("systemtray"); -import options from "options"; +import { BarBoxChild, SelfButton } from 'lib/types/bar'; +import { Notify } from 'lib/utils'; +const systemtray = await Service.import('systemtray'); +import options from 'options'; const { ignore } = options.bar.systray; -const SysTray = () => { +const SysTray = (): BarBoxChild => { const isVis = Variable(false); - const items = Utils.merge( - [systemtray.bind("items"), ignore.bind("value")], - (items, ignored) => { - const filteredTray = items.filter(({ id }) => !ignored.includes(id)); + const items = Utils.merge([systemtray.bind('items'), ignore.bind('value')], (items, ignored) => { + const filteredTray = items.filter(({ id }) => !ignored.includes(id)); - isVis.value = filteredTray.length > 0; + isVis.value = filteredTray.length > 0; - return filteredTray.map((item) => { - if (item.menu !== undefined) { - item.menu["class_name"] = "systray-menu"; - } - - return Widget.Button({ - cursor: "pointer", - child: Widget.Icon({ - class_name: "systray-icon", - icon: item.bind("icon"), - }), - on_primary_click: (_: any, event: Gdk.Event) => item.activate(event), - on_secondary_click: (_, event) => item.openMenu(event), - tooltip_markup: item.bind("tooltip_markup"), - }); + return filteredTray.map((item) => { + return Widget.Button({ + cursor: 'pointer', + child: Widget.Icon({ + class_name: 'systray-icon', + icon: item.bind('icon'), + }), + on_primary_click: (_: SelfButton, event: Gdk.Event) => item.activate(event), + on_secondary_click: (_, event) => item.openMenu(event), + onMiddleClick: () => Notify({ summary: 'App Name', body: item.id }), + tooltip_markup: item.bind('tooltip_markup'), }); - }, - ); + }); + }); return { component: Widget.Box({ - class_name: "systray", + class_name: 'systray', children: items, }), isVisible: true, - boxClass: "systray", + boxClass: 'systray', isVis, - props: {} + props: {}, }; }; diff --git a/modules/bar/utils.ts b/modules/bar/utils.ts index c8ee55e..660af79 100644 --- a/modules/bar/utils.ts +++ b/modules/bar/utils.ts @@ -1,6 +1,8 @@ import Gdk from 'gi://Gdk?version=3.0'; +import { Child } from 'lib/types/widget'; +import Button from 'types/widgets/button'; -export const closeAllMenus = () => { +export const closeAllMenus = (): void => { const menuWindows = App.windows .filter((w) => { if (w.name) { @@ -18,7 +20,7 @@ export const closeAllMenus = () => { }); }; -export const openMenu = (clicked: any, event: Gdk.Event, window: string) => { +export const openMenu = (clicked: Button, event: Gdk.Event, window: string): void => { /* * NOTE: We have to make some adjustments so the menu pops up relatively * to the center of the button clicked. We don't want the menu to spawn diff --git a/modules/bar/volume/index.ts b/modules/bar/volume/index.ts index 4c62062..64e76b8 100644 --- a/modules/bar/volume/index.ts +++ b/modules/bar/volume/index.ts @@ -1,56 +1,71 @@ import Gdk from 'gi://Gdk?version=3.0'; -const audio = await Service.import("audio"); -import { openMenu } from "../utils.js"; -import options from "options"; +const audio = await Service.import('audio'); +import { openMenu } from '../utils.js'; +import options from 'options'; +import { Binding } from 'lib/utils.js'; +import { VolumeIcons } from 'lib/types/volume.js'; +import { BarBoxChild } from 'lib/types/bar.js'; +import { Bind } from 'lib/types/variable.js'; +import Button from 'types/widgets/button.js'; +import { Child } from 'lib/types/widget.js'; -const Volume = () => { - const icons = { - 101: "󰕾", - 66: "󰕾", - 34: "󰖀", - 1: "󰕿", - 0: "󰝟", +const Volume = (): BarBoxChild => { + const icons: VolumeIcons = { + 101: '󰕾', + 66: '󰕾', + 34: '󰖀', + 1: '󰕿', + 0: '󰝟', }; - const getIcon = () => { - const icon = Utils.merge( - [audio.speaker.bind("is_muted"), audio.speaker.bind("volume")], + const getIcon = (): Bind => { + const icon: Binding = Utils.merge( + [audio.speaker.bind('is_muted'), audio.speaker.bind('volume')], (isMuted, vol) => { - return isMuted - ? 0 - : [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100); + if (isMuted) return 0; + + const foundVol = [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100); + + if (foundVol !== undefined) { + return foundVol; + } + + return 101; }, ); - return icon.as((i) => i !== undefined ? icons[i] : 101); + return icon.as((i: number) => (i !== undefined ? icons[i] : icons[101])); }; const volIcn = Widget.Label({ hexpand: true, label: getIcon(), - class_name: "bar-button-icon volume txt-icon bar", + class_name: 'bar-button-icon volume txt-icon bar', }); const volPct = Widget.Label({ hexpand: true, - label: audio.speaker.bind("volume").as((v) => `${Math.round(v * 100)}%`), - class_name: "bar-button-label volume", + label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`), + class_name: 'bar-button-label volume', }); return { component: Widget.Box({ hexpand: true, vexpand: true, - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), options.bar.volume.label.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - - return `volume ${styleMap[style]} ${!showLabel ? "no-label" : ""}`; - }), - children: options.bar.volume.label.bind("value").as((showLabel) => { + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), options.bar.volume.label.bind('value')], + (style, showLabel) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `volume ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`; + }, + ), + children: options.bar.volume.label.bind('value').as((showLabel) => { if (showLabel) { return [volIcn, volPct]; } @@ -58,10 +73,10 @@ const Volume = () => { }), }), isVisible: true, - boxClass: "volume", + boxClass: 'volume', props: { - on_primary_click: (clicked: any, event: Gdk.Event) => { - openMenu(clicked, event, "audiomenu"); + on_primary_click: (clicked: Button, event: Gdk.Event): void => { + openMenu(clicked, event, 'audiomenu'); }, }, }; diff --git a/modules/bar/window_title/index.ts b/modules/bar/window_title/index.ts index 58a887c..07856d6 100644 --- a/modules/bar/window_title/index.ts +++ b/modules/bar/window_title/index.ts @@ -1,121 +1,119 @@ -const hyprland = await Service.import("hyprland"); +const hyprland = await Service.import('hyprland'); +import { BarBoxChild } from 'lib/types/bar'; import options from 'options'; -import { ActiveClient } from 'types/service/hyprland' +import { ActiveClient } from 'types/service/hyprland'; -const filterTitle = (windowtitle: ActiveClient) => { +const filterTitle = (windowtitle: ActiveClient): Record => { const windowTitleMap = [ // user provided values ...options.bar.windowtitle.title_map.value, // Original Entries - ["kitty", "󰄛", "Kitty Terminal"], - ["firefox", "󰈹", "Firefox"], - ["microsoft-edge", "󰇩", "Edge"], - ["discord", "", "Discord"], - ["vesktop", "", "Vesktop"], - ["org.kde.dolphin", "", "Dolphin"], - ["plex", "󰚺", "Plex"], - ["steam", "", "Steam"], - ["spotify", "󰓇", "Spotify"], - ["ristretto", "󰋩", "Ristretto"], - ["obsidian", "󱓧", "Obsidian"], + ['kitty', '󰄛', 'Kitty Terminal'], + ['firefox', '󰈹', 'Firefox'], + ['microsoft-edge', '󰇩', 'Edge'], + ['discord', '', 'Discord'], + ['vesktop', '', 'Vesktop'], + ['org.kde.dolphin', '', 'Dolphin'], + ['plex', '󰚺', 'Plex'], + ['steam', '', 'Steam'], + ['spotify', '󰓇', 'Spotify'], + ['ristretto', '󰋩', 'Ristretto'], + ['obsidian', '󱓧', 'Obsidian'], // Browsers - ["google-chrome", "", "Google Chrome"], - ["brave-browser", "󰖟", "Brave Browser"], - ["chromium", "", "Chromium"], - ["opera", "", "Opera"], - ["vivaldi", "󰖟", "Vivaldi"], - ["waterfox", "󰖟", "Waterfox"], - ["thorium", "󰖟", "Waterfox"], - ["tor-browser", "", "Tor Browser"], - ["floorp", "󰈹", "Floorp"], + ['google-chrome', '', 'Google Chrome'], + ['brave-browser', '󰖟', 'Brave Browser'], + ['chromium', '', 'Chromium'], + ['opera', '', 'Opera'], + ['vivaldi', '󰖟', 'Vivaldi'], + ['waterfox', '󰖟', 'Waterfox'], + ['thorium', '󰖟', 'Waterfox'], + ['tor-browser', '', 'Tor Browser'], + ['floorp', '󰈹', 'Floorp'], // Terminals - ["gnome-terminal", "", "GNOME Terminal"], - ["konsole", "", "Konsole"], - ["alacritty", "", "Alacritty"], - ["wezterm", "", "Wezterm"], - ["foot", "󰽒", "Foot Terminal"], - ["tilix", "", "Tilix"], - ["xterm", "", "XTerm"], - ["urxvt", "", "URxvt"], - ["st", "", "st Terminal"], + ['gnome-terminal', '', 'GNOME Terminal'], + ['konsole', '', 'Konsole'], + ['alacritty', '', 'Alacritty'], + ['wezterm', '', 'Wezterm'], + ['foot', '󰽒', 'Foot Terminal'], + ['tilix', '', 'Tilix'], + ['xterm', '', 'XTerm'], + ['urxvt', '', 'URxvt'], + ['st', '', 'st Terminal'], // Development Tools - ["code", "󰨞", "Visual Studio Code"], - ["vscode", "󰨞", "VS Code"], - ["sublime-text", "", "Sublime Text"], - ["atom", "", "Atom"], - ["android-studio", "󰀴", "Android Studio"], - ["intellij-idea", "", "IntelliJ IDEA"], - ["pycharm", "󱃖", "PyCharm"], - ["webstorm", "󱃖", "WebStorm"], - ["phpstorm", "󱃖", "PhpStorm"], - ["eclipse", "", "Eclipse"], - ["netbeans", "", "NetBeans"], - ["docker", "", "Docker"], - ["vim", "", "Vim"], - ["neovim", "", "Neovim"], - ["neovide", "", "Neovide"], - ["emacs", "", "Emacs"], + ['code', '󰨞', 'Visual Studio Code'], + ['vscode', '󰨞', 'VS Code'], + ['sublime-text', '', 'Sublime Text'], + ['atom', '', 'Atom'], + ['android-studio', '󰀴', 'Android Studio'], + ['intellij-idea', '', 'IntelliJ IDEA'], + ['pycharm', '󱃖', 'PyCharm'], + ['webstorm', '󱃖', 'WebStorm'], + ['phpstorm', '󱃖', 'PhpStorm'], + ['eclipse', '', 'Eclipse'], + ['netbeans', '', 'NetBeans'], + ['docker', '', 'Docker'], + ['vim', '', 'Vim'], + ['neovim', '', 'Neovim'], + ['neovide', '', 'Neovide'], + ['emacs', '', 'Emacs'], // Communication Tools - ["slack", "󰒱", "Slack"], - ["telegram-desktop", "", "Telegram"], - ["org.telegram.desktop", "", "Telegram"], - ["whatsapp", "󰖣", "WhatsApp"], - ["teams", "󰊻", "Microsoft Teams"], - ["skype", "󰒯", "Skype"], - ["thunderbird", "", "Thunderbird"], + ['slack', '󰒱', 'Slack'], + ['telegram-desktop', '', 'Telegram'], + ['org.telegram.desktop', '', 'Telegram'], + ['whatsapp', '󰖣', 'WhatsApp'], + ['teams', '󰊻', 'Microsoft Teams'], + ['skype', '󰒯', 'Skype'], + ['thunderbird', '', 'Thunderbird'], // File Managers - ["nautilus", "󰝰", "Files (Nautilus)"], - ["thunar", "󰝰", "Thunar"], - ["pcmanfm", "󰝰", "PCManFM"], - ["nemo", "󰝰", "Nemo"], - ["ranger", "󰝰", "Ranger"], - ["doublecmd", "󰝰", "Double Commander"], - ["krusader", "󰝰", "Krusader"], + ['nautilus', '󰝰', 'Files (Nautilus)'], + ['thunar', '󰝰', 'Thunar'], + ['pcmanfm', '󰝰', 'PCManFM'], + ['nemo', '󰝰', 'Nemo'], + ['ranger', '󰝰', 'Ranger'], + ['doublecmd', '󰝰', 'Double Commander'], + ['krusader', '󰝰', 'Krusader'], // Media Players - ["vlc", "󰕼", "VLC Media Player"], - ["mpv", "", "MPV"], - ["rhythmbox", "󰓃", "Rhythmbox"], + ['vlc', '󰕼', 'VLC Media Player'], + ['mpv', '', 'MPV'], + ['rhythmbox', '󰓃', 'Rhythmbox'], // Graphics Tools - ["gimp", "", "GIMP"], - ["inkscape", "", "Inkscape"], - ["krita", "", "Krita"], - ["blender", "󰂫", "Blender"], + ['gimp', '', 'GIMP'], + ['inkscape', '', 'Inkscape'], + ['krita', '', 'Krita'], + ['blender', '󰂫', 'Blender'], // Video Editing - ["kdenlive", "", "Kdenlive"], + ['kdenlive', '', 'Kdenlive'], // Games and Gaming Platforms - ["lutris", "󰺵", "Lutris"], - ["heroic", "󰺵", "Heroic Games Launcher"], - ["minecraft", "󰍳", "Minecraft"], - ["csgo", "󰺵", "CS:GO"], - ["dota2", "󰺵", "Dota 2"], + ['lutris', '󰺵', 'Lutris'], + ['heroic', '󰺵', 'Heroic Games Launcher'], + ['minecraft', '󰍳', 'Minecraft'], + ['csgo', '󰺵', 'CS:GO'], + ['dota2', '󰺵', 'Dota 2'], // Office and Productivity - ["evernote", "", "Evernote"], - ["sioyek", "", "Sioyek"], - + ['evernote', '', 'Evernote'], + ['sioyek', '', 'Sioyek'], // Cloud Services and Sync - ["dropbox", "󰇣", "Dropbox"], + ['dropbox', '󰇣', 'Dropbox'], // Desktop - ["^$", "󰇄", "Desktop"], + ['^$', '󰇄', 'Desktop'], // Fallback icon - ["(.+)", "󰣆", `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`], + ['(.+)', '󰣆', `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`], ]; - const foundMatch = windowTitleMap.find((wt) => - RegExp(wt[0]).test(windowtitle.class.toLowerCase()), - ); + const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0]).test(windowtitle.class.toLowerCase())); // return the default icon if no match is found or // if the array element matched is not of size 3 @@ -127,37 +125,93 @@ const filterTitle = (windowtitle: ActiveClient) => { } return { - icon: foundMatch ? foundMatch[1] : windowTitleMap[windowTitleMap.length - 1][1], - label: foundMatch ? foundMatch[2] : windowTitleMap[windowTitleMap.length - 1][2] + icon: foundMatch[1], + label: foundMatch[2], }; }; -const ClientTitle = () => { +const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: boolean): string => { + if (useCustomTitle) return filterTitle(client).label; + if (useClassName) return client.class; + + const title = client.title; + // If the title is empty or only filled with spaces, fallback to the class name + if (title.length === 0 || title.match(/^ *$/)) { + return client.class; + } + return title; +}; + +const truncateTitle = (title: string, max_size: number): string => { + if (max_size > 0 && title.length > max_size) { + return title.substring(0, max_size).trim() + '...'; + } + return title; +}; + +const ClientTitle = (): BarBoxChild => { + const { custom_title, class_name, label, icon, truncation, truncation_size } = options.bar.windowtitle; + return { component: Widget.Box({ - className: Utils.merge([options.theme.bar.buttons.style.bind("value"), options.bar.windowtitle.label.bind("value")], (style, showLabel) => { - const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - }; - return `windowtitle ${styleMap[style]} ${!showLabel ? "no-label" : ""}`; - }), - children: options.bar.windowtitle.label.bind("value").as((showLabel) => { - const titleIcon = Widget.Label({ - class_name: "bar-button-icon windowtitle txt-icon bar", - label: hyprland.active.bind("client").as((v) => filterTitle(v).icon), - }); - const titleLabel = Widget.Label({ - class_name: "bar-button-label windowtitle", - label: hyprland.active.bind("client").as((v) => filterTitle(v).label), - }); - return showLabel ? [titleIcon, titleLabel] : [titleIcon]; - }), + className: Utils.merge( + [options.theme.bar.buttons.style.bind('value'), label.bind('value')], + (style, showLabel) => { + const styleMap = { + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style3', + }; + return `windowtitle ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`; + }, + ), + children: Utils.merge( + [ + hyprland.active.bind('client'), + custom_title.bind('value'), + class_name.bind('value'), + label.bind('value'), + icon.bind('value'), + truncation.bind('value'), + truncation_size.bind('value'), + ], + (client, useCustomTitle, useClassName, showLabel, showIcon, truncate, truncationSize) => { + if (showIcon) { + return [ + Widget.Label({ + class_name: 'bar-button-icon windowtitle txt-icon bar', + label: filterTitle(client).icon, + }), + Widget.Label({ + class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`, + label: truncateTitle( + getTitle(client, useCustomTitle, useClassName), + truncate ? truncationSize : -1, + ), + }), + ]; + } + + if (showLabel) { + return [ + Widget.Label({ + class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`, + label: truncateTitle( + getTitle(client, useCustomTitle, useClassName), + truncate ? truncationSize : -1, + ), + }), + ]; + } + + return []; + }, + ), }), isVisible: true, - boxClass: "windowtitle", - props: {} + boxClass: 'windowtitle', + props: {}, }; }; diff --git a/modules/bar/workspaces/helpers.ts b/modules/bar/workspaces/helpers.ts index 1fbd45f..26d6d9c 100644 --- a/modules/bar/workspaces/helpers.ts +++ b/modules/bar/workspaces/helpers.ts @@ -1,23 +1,21 @@ -const hyprland = await Service.import("hyprland"); +const hyprland = await Service.import('hyprland'); -import { WorkspaceMap, WorkspaceRule } from "lib/types/workspace"; -import options from "options"; -import { Variable } from "types/variable"; - -const { - workspaces, - reverse_scroll, -} = options.bar.workspaces; +import { MonitorMap, WorkspaceMap, WorkspaceRule } from 'lib/types/workspace'; +import options from 'options'; +import { Variable } from 'types/variable'; +const { workspaces, reverse_scroll } = options.bar.workspaces; export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, monitor: number): boolean => { if (!wsRules || !Object.keys(wsRules).length) { return true; } - const monitorMap = {}; - const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor })); - const monitors = [...new Map([...workspaceMonitorList, ...hyprland.monitors].map(item => [item.id, item])).values()]; + const monitorMap: MonitorMap = {}; + const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ id: m.monitorID, name: m.monitor })); + const monitors = [ + ...new Map([...workspaceMonitorList, ...hyprland.monitors].map((item) => [item.id, item])).values(), + ]; monitors.forEach((m) => (monitorMap[m.id] = m.name)); @@ -32,9 +30,9 @@ export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, mo export const getWorkspaceRules = (): WorkspaceMap => { try { - const rules = Utils.exec("hyprctl workspacerules -j"); + const rules = Utils.exec('hyprctl workspacerules -j'); - const workspaceRules = {}; + const workspaceRules: WorkspaceMap = {}; JSON.parse(rules).forEach((rule: WorkspaceRule, index: number) => { if (Object.hasOwnProperty.call(workspaceRules, rule.monitor)) { @@ -60,13 +58,13 @@ export const getCurrentMonitorWorkspaces = (monitor: number): number[] => { } const monitorWorkspaces = getWorkspaceRules(); - const monitorMap = {}; + const monitorMap: MonitorMap = {}; hyprland.monitors.forEach((m) => (monitorMap[m.id] = m.name)); const currentMonitorName = monitorMap[monitor]; return monitorWorkspaces[currentMonitorName]; -} +}; export const goToNextWS = (currentMonitorWorkspaces: Variable, activeWorkspaces: boolean): void => { if (activeWorkspaces === true) { @@ -74,18 +72,17 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable, activeW let nextIndex = hyprland.active.workspace.id + 1; if (nextIndex > activeWses[activeWses.length - 1].id) { - nextIndex = activeWses[0].id; } - hyprland.messageAsync(`dispatch workspace ${nextIndex}`) + hyprland.messageAsync(`dispatch workspace ${nextIndex}`); } else if (currentMonitorWorkspaces.value === undefined) { let nextIndex = hyprland.active.workspace.id + 1; if (nextIndex > workspaces.value) { nextIndex = 0; } - hyprland.messageAsync(`dispatch workspace ${nextIndex}`) + hyprland.messageAsync(`dispatch workspace ${nextIndex}`); } else { const curWorkspace = hyprland.active.workspace.id; const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); @@ -94,9 +91,9 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable, activeW nextIndex = 0; } - hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`) + hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`); } -} +}; export const goToPrevWS = (currentMonitorWorkspaces: Variable, activeWorkspaces: boolean): void => { if (activeWorkspaces === true) { @@ -104,11 +101,10 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable, activeW let prevIndex = hyprland.active.workspace.id - 1; if (prevIndex < activeWses[0].id) { - prevIndex = activeWses[activeWses.length - 1].id; } - hyprland.messageAsync(`dispatch workspace ${prevIndex}`) + hyprland.messageAsync(`dispatch workspace ${prevIndex}`); } else if (currentMonitorWorkspaces.value === undefined) { let prevIndex = hyprland.active.workspace.id - 1; @@ -116,7 +112,7 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable, activeW prevIndex = workspaces.value; } - hyprland.messageAsync(`dispatch workspace ${prevIndex}`) + hyprland.messageAsync(`dispatch workspace ${prevIndex}`); } else { const curWorkspace = hyprland.active.workspace.id; const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); @@ -125,11 +121,11 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable, activeW prevIndex = currentMonitorWorkspaces.value.length - 1; } - hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`) + hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`); } -} +}; -export function throttle void>(func: T, limit: number): T { +export function throttle void>(func: T, limit: number): T { let inThrottle: boolean; return function (this: ThisParameterType, ...args: Parameters) { if (!inThrottle) { @@ -147,7 +143,11 @@ type ThrottledScrollHandlers = { throttledScrollDown: () => void; }; -export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonitorWorkspaces: Variable, activeWorkspaces: boolean = false): ThrottledScrollHandlers => { +export const createThrottledScrollHandlers = ( + scrollSpeed: number, + currentMonitorWorkspaces: Variable, + activeWorkspaces: boolean = false, +): ThrottledScrollHandlers => { const throttledScrollUp = throttle(() => { if (reverse_scroll.value === true) { goToPrevWS(currentMonitorWorkspaces, activeWorkspaces); @@ -165,4 +165,4 @@ export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonito }, 200 / scrollSpeed); return { throttledScrollUp, throttledScrollDown }; -} +}; diff --git a/modules/bar/workspaces/index.ts b/modules/bar/workspaces/index.ts index c73e157..161d292 100644 --- a/modules/bar/workspaces/index.ts +++ b/modules/bar/workspaces/index.ts @@ -1,271 +1,43 @@ -const hyprland = await Service.import("hyprland"); -import options from "options"; -import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces, getWorkspaceRules, getWorkspacesForMonitor } from "./helpers"; -import { Workspace } from "types/service/hyprland"; +import options from 'options'; +import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces } from './helpers'; +import { BarBoxChild, SelfButton } from 'lib/types/bar'; +import { occupiedWses } from './variants/occupied'; +import { defaultWses } from './variants/default'; -const { - workspaces, - monitorSpecific, - workspaceMask, - scroll_speed, - spacing -} = options.bar.workspaces; +const { workspaces, scroll_speed } = options.bar.workspaces; -function range(length: number, start = 1) { - return Array.from({ length }, (_, i) => i + start); -} - -const Workspaces = (monitor = -1) => { +const Workspaces = (monitor = -1): BarBoxChild => { const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces(monitor)); - workspaces.connect("changed", () => { - currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor) - }) - - const renderClassnames = (showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string, i: number) => { - if (showIcons) { - return `workspace-icon txt-icon bar`; - } - if (showNumbered) { - const numActiveInd = hyprland.active.workspace.id === i - ? numberedActiveIndicator - : ""; - - return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`; - } - return "default"; - } - - const renderLabel = (showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, i: number, index: number) => { - if (showIcons) { - if (hyprland.active.workspace.id === i) { - return active; - } - if ((hyprland.getWorkspace(i)?.windows || 0) > 0) { - return occupied; - } - if ( - monitor !== -1 - ) { - return available; - } - } - return workspaceMask - ? `${index + 1}` - : `${i}`; - } - const defaultWses = () => { - return Widget.Box({ - children: Utils.merge( - [workspaces.bind("value"), monitorSpecific.bind()], - (workspaces: number, monitorSpecific: boolean) => { - return range(workspaces || 8) - .filter((i) => { - if (!monitorSpecific) { - return true; - } - const workspaceRules = getWorkspaceRules(); - return getWorkspacesForMonitor(i, workspaceRules, monitor); - }) - .sort((a, b) => { - return a - b; - }) - .map((i, index) => { - return Widget.Button({ - class_name: "workspace-button", - on_primary_click: () => { - hyprland.messageAsync(`dispatch workspace ${i}`) - - }, - child: Widget.Label({ - attribute: i, - vpack: "center", - css: spacing.bind("value").as(sp => `margin: 0rem ${0.375 * sp}rem;`), - class_name: Utils.merge( - [ - options.bar.workspaces.show_icons.bind("value"), - options.bar.workspaces.show_numbered.bind("value"), - options.bar.workspaces.numbered_active_indicator.bind("value"), - options.bar.workspaces.icons.available.bind("value"), - options.bar.workspaces.icons.active.bind("value"), - options.bar.workspaces.icons.occupied.bind("value"), - hyprland.active.workspace.bind("id") - ], - (showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string) => { - if (showIcons) { - return `workspace-icon txt-icon bar`; - } - if (showNumbered) { - const numActiveInd = hyprland.active.workspace.id === i - ? numberedActiveIndicator - : ""; - - return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`; - } - return "default"; - }, - ), - label: Utils.merge( - [ - options.bar.workspaces.show_icons.bind("value"), - options.bar.workspaces.icons.available.bind("value"), - options.bar.workspaces.icons.active.bind("value"), - options.bar.workspaces.icons.occupied.bind("value"), - workspaceMask.bind("value"), - hyprland.active.workspace.bind("id") - ], - (showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, _: number) => { - if (showIcons) { - if (hyprland.active.workspace.id === i) { - return active; - } - if ((hyprland.getWorkspace(i)?.windows || 0) > 0) { - return occupied; - } - if ( - monitor !== -1 - ) { - return available; - } - } - return workspaceMask - ? `${index + 1}` - : `${i}`; - }, - ), - setup: (self) => { - self.hook(hyprland, () => { - self.toggleClassName( - "active", - hyprland.active.workspace.id === i, - ); - self.toggleClassName( - "occupied", - (hyprland.getWorkspace(i)?.windows || 0) > 0, - ); - }); - }, - }) - }); - }); - }, - ) - }) - } - const occupiedWses = () => { - return Widget.Box({ - children: Utils.merge( - [ - monitorSpecific.bind("value"), - hyprland.bind("workspaces"), - workspaceMask.bind("value"), - workspaces.bind("value"), - options.bar.workspaces.show_icons.bind("value"), - options.bar.workspaces.icons.available.bind("value"), - options.bar.workspaces.icons.active.bind("value"), - options.bar.workspaces.icons.occupied.bind("value"), - options.bar.workspaces.show_numbered.bind("value"), - options.bar.workspaces.numbered_active_indicator.bind("value"), - spacing.bind("value"), - hyprland.active.workspace.bind("id"), - ], - ( - monitorSpecific: boolean, - wkSpaces: Workspace[], - workspaceMask: boolean, - totalWkspcs: number, - showIcons: boolean, - available: string, - active: string, - occupied: string, - showNumbered: boolean, - numberedActiveIndicator: string, - spacing: number, - activeId: number, - ) => { - let allWkspcs = range(totalWkspcs || 8); - - const activeWorkspaces = wkSpaces.map(w => w.id); - const workspaceRules = getWorkspaceRules(); - - // Sometimes hyprland doesn't have all the monitors in the list - // so we complement it with monitors from the workspace list - const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor })); - const curMonitor = hyprland.monitors.find(m => m.id === monitor) - || workspaceMonitorList.find(m => m.id === monitor); - - // go through each key in workspaceRules and flatten the array - const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => { - return [...acc, ...workspaceRules[k]]; - }, [] as number[]); - - const activesForMonitor = activeWorkspaces.filter(w => { - if (curMonitor && Object.hasOwnProperty.call(workspaceRules, curMonitor.name) && workspacesWithRules.includes(w)) { - return workspaceRules[curMonitor.name].includes(w); - } - return true; - }); - - if (monitorSpecific) { - const wrkspcsInRange = range(totalWkspcs).filter(w => { - return getWorkspacesForMonitor(w, workspaceRules, monitor); - }); - allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])]; - } else { - allWkspcs = [...new Set([...allWkspcs, ...activeWorkspaces])]; - } - - return allWkspcs - .sort((a, b) => { - return a - b; - }) - .map((i, index) => { - return Widget.Button({ - class_name: "workspace-button", - on_primary_click: () => { - hyprland.messageAsync(`dispatch workspace ${i}`) - - }, - child: Widget.Label({ - attribute: i, - vpack: "center", - css: `margin: 0rem ${0.375 * spacing}rem;`, - class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i), - label: renderLabel(showIcons, available, active, occupied, workspaceMask, i, index), - setup: (self) => { - self.toggleClassName( - "active", - activeId === i, - ); - self.toggleClassName( - "occupied", - (hyprland.getWorkspace(i)?.windows || 0) > 0, - ); - }, - }) - }); - }); - }, - ) - }) - } + workspaces.connect('changed', () => { + currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor); + }); return { component: Widget.Box({ - class_name: "workspaces", - child: options.bar.workspaces.hideUnoccupied.bind("value").as(hideUnoccupied => hideUnoccupied ? occupiedWses() : defaultWses()), + class_name: 'workspaces', + child: options.bar.workspaces.hideUnoccupied + .bind('value') + .as((hideUnoccupied) => (hideUnoccupied ? occupiedWses(monitor) : defaultWses(monitor))), }), isVisible: true, - boxClass: "workspaces", + boxClass: 'workspaces', props: { - setup: (self: any) => { - Utils.merge([scroll_speed.bind("value"), options.bar.workspaces.hideUnoccupied.bind("value")], (scroll_speed, hideUnoccupied) => { - const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(scroll_speed, currentMonitorWorkspaces, hideUnoccupied) - self.on_scroll_up = throttledScrollUp; - self.on_scroll_down = throttledScrollDown; - }); - } - } + setup: (self: SelfButton): void => { + Utils.merge( + [scroll_speed.bind('value'), options.bar.workspaces.hideUnoccupied.bind('value')], + (scroll_speed, hideUnoccupied) => { + const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers( + scroll_speed, + currentMonitorWorkspaces, + hideUnoccupied, + ); + self.on_scroll_up = throttledScrollUp; + self.on_scroll_down = throttledScrollDown; + }, + ); + }, + }, }; }; diff --git a/modules/bar/workspaces/utils.ts b/modules/bar/workspaces/utils.ts new file mode 100644 index 0000000..ed29582 --- /dev/null +++ b/modules/bar/workspaces/utils.ts @@ -0,0 +1,89 @@ +import { WorkspaceIconMap } from 'lib/types/workspace'; +import { isValidGjsColor } from 'lib/utils'; + +const hyprland = await Service.import('hyprland'); + +const getWsIcon = (wsIconMap: WorkspaceIconMap, i: number): string => { + const iconEntry = wsIconMap[i]; + + if (!iconEntry) { + return `${i}`; + } + + const hasIcon = typeof iconEntry === 'object' && 'icon' in iconEntry && iconEntry.icon !== ''; + + if (typeof iconEntry === 'string' && iconEntry !== '') { + return iconEntry; + } + + if (hasIcon) { + return iconEntry.icon; + } + + return `${i}`; +}; + +export const getWsColor = (wsIconMap: WorkspaceIconMap, i: number): string => { + const iconEntry = wsIconMap[i]; + if (!iconEntry) { + return ''; + } + + const hasColor = typeof iconEntry === 'object' && 'color' in iconEntry && iconEntry.color !== ''; + if (hasColor && isValidGjsColor(iconEntry.color)) { + return `color: ${iconEntry.color}; border-bottom-color: ${iconEntry.color};`; + } + return ''; +}; + +export const renderClassnames = ( + showIcons: boolean, + showNumbered: boolean, + numberedActiveIndicator: string, + showWsIcons: boolean, + i: number, +): string => { + if (showIcons) { + return `workspace-icon txt-icon bar`; + } + if (showNumbered || showWsIcons) { + const numActiveInd = hyprland.active.workspace.id === i ? `${numberedActiveIndicator}` : ''; + + const className = + `workspace-number can_${numberedActiveIndicator} ` + + `${numActiveInd} ` + + `${showWsIcons ? 'txt-icon' : ''}`; + + return className; + } + return 'default'; +}; + +export const renderLabel = ( + showIcons: boolean, + available: string, + active: string, + occupied: string, + workspaceMask: boolean, + showWsIcons: boolean, + wsIconMap: WorkspaceIconMap, + i: number, + index: number, + monitor: number, +): string => { + if (showIcons) { + if (hyprland.active.workspace.id === i) { + return active; + } + if ((hyprland.getWorkspace(i)?.windows || 0) > 0) { + return occupied; + } + if (monitor !== -1) { + return available; + } + } + if (showWsIcons) { + return getWsIcon(wsIconMap, i); + } + return workspaceMask ? `${index + 1}` : `${i}`; +}; diff --git a/modules/bar/workspaces/variants/default.ts b/modules/bar/workspaces/variants/default.ts new file mode 100644 index 0000000..ccd985b --- /dev/null +++ b/modules/bar/workspaces/variants/default.ts @@ -0,0 +1,125 @@ +const hyprland = await Service.import('hyprland'); +import options from 'options'; +import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers'; +import { range } from 'lib/utils'; +import { BoxWidget } from 'lib/types/widget'; +import { getWsColor, renderClassnames, renderLabel } from '../utils'; +import { WorkspaceIconMap } from 'lib/types/workspace'; + +const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces; +export const defaultWses = (monitor: number): BoxWidget => { + return Widget.Box({ + children: Utils.merge( + [workspaces.bind('value'), monitorSpecific.bind()], + (workspaces: number, monitorSpecific: boolean) => { + return range(workspaces || 8) + .filter((i) => { + if (!monitorSpecific) { + return true; + } + const workspaceRules = getWorkspaceRules(); + return getWorkspacesForMonitor(i, workspaceRules, monitor); + }) + .sort((a, b) => { + return a - b; + }) + .map((i, index) => { + return Widget.Button({ + class_name: 'workspace-button', + on_primary_click: () => { + hyprland.messageAsync(`dispatch workspace ${i}`); + }, + child: Widget.Label({ + attribute: i, + vpack: 'center', + css: Utils.merge( + [ + spacing.bind('value'), + options.bar.workspaces.showWsIcons.bind('value'), + options.bar.workspaces.workspaceIconMap.bind('value'), + options.theme.matugen.bind('value'), + ], + ( + sp: number, + showWsIcons: boolean, + workspaceIconMap: WorkspaceIconMap, + matugen: boolean, + ) => { + return ( + `margin: 0rem ${0.375 * sp}rem;` + + `${showWsIcons && !matugen ? getWsColor(workspaceIconMap, i) : ''}` + ); + }, + ), + class_name: Utils.merge( + [ + options.bar.workspaces.show_icons.bind('value'), + options.bar.workspaces.show_numbered.bind('value'), + options.bar.workspaces.numbered_active_indicator.bind('value'), + options.bar.workspaces.showWsIcons.bind('value'), + options.bar.workspaces.icons.available.bind('value'), + options.bar.workspaces.icons.active.bind('value'), + hyprland.active.workspace.bind('id'), + ], + ( + showIcons: boolean, + showNumbered: boolean, + numberedActiveIndicator: string, + showWsIcons: boolean, + ) => { + return renderClassnames( + showIcons, + showNumbered, + numberedActiveIndicator, + showWsIcons, + i, + ); + }, + ), + label: Utils.merge( + [ + options.bar.workspaces.show_icons.bind('value'), + options.bar.workspaces.icons.available.bind('value'), + options.bar.workspaces.icons.active.bind('value'), + options.bar.workspaces.icons.occupied.bind('value'), + options.bar.workspaces.workspaceIconMap.bind('value'), + options.bar.workspaces.showWsIcons.bind('value'), + workspaceMask.bind('value'), + hyprland.active.workspace.bind('id'), + ], + ( + showIcons: boolean, + available: string, + active: string, + occupied: string, + wsIconMap: WorkspaceIconMap, + showWsIcons: boolean, + workspaceMask: boolean, + ) => { + return renderLabel( + showIcons, + available, + active, + occupied, + workspaceMask, + showWsIcons, + wsIconMap, + i, + index, + monitor, + ); + }, + ), + setup: (self) => { + self.hook(hyprland, () => { + self.toggleClassName('active', hyprland.active.workspace.id === i); + self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0); + }); + }, + }), + }); + }); + }, + ), + }); +}; diff --git a/modules/bar/workspaces/variants/occupied.ts b/modules/bar/workspaces/variants/occupied.ts new file mode 100644 index 0000000..028ec4a --- /dev/null +++ b/modules/bar/workspaces/variants/occupied.ts @@ -0,0 +1,134 @@ +const hyprland = await Service.import('hyprland'); +import options from 'options'; +import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers'; +import { Workspace } from 'types/service/hyprland'; +import { getWsColor, renderClassnames, renderLabel } from '../utils'; +import { range } from 'lib/utils'; +import { BoxWidget } from 'lib/types/widget'; +import { WorkspaceIconMap } from 'lib/types/workspace'; + +const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces; + +export const occupiedWses = (monitor: number): BoxWidget => { + return Widget.Box({ + children: Utils.merge( + [ + monitorSpecific.bind('value'), + hyprland.bind('workspaces'), + workspaceMask.bind('value'), + workspaces.bind('value'), + options.bar.workspaces.show_icons.bind('value'), + options.bar.workspaces.icons.available.bind('value'), + options.bar.workspaces.icons.active.bind('value'), + options.bar.workspaces.icons.occupied.bind('value'), + options.bar.workspaces.show_numbered.bind('value'), + options.bar.workspaces.numbered_active_indicator.bind('value'), + spacing.bind('value'), + hyprland.active.workspace.bind('id'), + options.bar.workspaces.workspaceIconMap.bind('value'), + options.bar.workspaces.showWsIcons.bind('value'), + options.theme.matugen.bind('value'), + ], + ( + monitorSpecific: boolean, + wkSpaces: Workspace[], + workspaceMask: boolean, + totalWkspcs: number, + showIcons: boolean, + available: string, + active: string, + occupied: string, + showNumbered: boolean, + numberedActiveIndicator: string, + spacing: number, + activeId: number, + wsIconMap: WorkspaceIconMap, + showWsIcons: boolean, + matugen: boolean, + ) => { + let allWkspcs = range(totalWkspcs || 8); + + const activeWorkspaces = wkSpaces.map((w) => w.id); + const workspaceRules = getWorkspaceRules(); + + // Sometimes hyprland doesn't have all the monitors in the list + // so we complement it with monitors from the workspace list + const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ + id: m.monitorID, + name: m.monitor, + })); + const curMonitor = + hyprland.monitors.find((m) => m.id === monitor) || + workspaceMonitorList.find((m) => m.id === monitor); + + // go through each key in workspaceRules and flatten the array + const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => { + return [...acc, ...workspaceRules[k]]; + }, [] as number[]); + + const activesForMonitor = activeWorkspaces.filter((w) => { + if ( + curMonitor && + Object.hasOwnProperty.call(workspaceRules, curMonitor.name) && + workspacesWithRules.includes(w) + ) { + return workspaceRules[curMonitor.name].includes(w); + } + return true; + }); + + if (monitorSpecific) { + const wrkspcsInRange = range(totalWkspcs).filter((w) => { + return getWorkspacesForMonitor(w, workspaceRules, monitor); + }); + allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])]; + } else { + allWkspcs = [...new Set([...allWkspcs, ...activeWorkspaces])]; + } + + return allWkspcs + .sort((a, b) => { + return a - b; + }) + .map((i, index) => { + return Widget.Button({ + class_name: 'workspace-button', + on_primary_click: () => { + hyprland.messageAsync(`dispatch workspace ${i}`); + }, + child: Widget.Label({ + attribute: i, + vpack: 'center', + css: + `margin: 0rem ${0.375 * spacing}rem;` + + `${showWsIcons && !matugen ? getWsColor(wsIconMap, i) : ''}`, + class_name: renderClassnames( + showIcons, + showNumbered, + numberedActiveIndicator, + showWsIcons, + i, + ), + label: renderLabel( + showIcons, + available, + active, + occupied, + workspaceMask, + showWsIcons, + wsIconMap, + i, + index, + monitor, + ), + setup: (self) => { + self.toggleClassName('active', activeId === i); + self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0); + }, + }), + }); + }); + }, + ), + }); +}; diff --git a/modules/icons/index.ts b/modules/icons/index.ts index a573875..14d0a06 100644 --- a/modules/icons/index.ts +++ b/modules/icons/index.ts @@ -1,199 +1,199 @@ export const substitutes = { - "transmission-gtk": "transmission", - "blueberry.py": "blueberry", - Caprine: "facebook-messenger", - "com.raggesilver.BlackBox-symbolic": "terminal-symbolic", - "org.wezfurlong.wezterm-symbolic": "terminal-symbolic", - "audio-headset-bluetooth": "audio-headphones-symbolic", - "audio-card-analog-usb": "audio-speakers-symbolic", - "audio-card-analog-pci": "audio-card-symbolic", - "preferences-system": "emblem-system-symbolic", - "com.github.Aylur.ags-symbolic": "controls-symbolic", - "com.github.Aylur.ags": "controls-symbolic", + 'transmission-gtk': 'transmission', + 'blueberry.py': 'blueberry', + Caprine: 'facebook-messenger', + 'com.raggesilver.BlackBox-symbolic': 'terminal-symbolic', + 'org.wezfurlong.wezterm-symbolic': 'terminal-symbolic', + 'audio-headset-bluetooth': 'audio-headphones-symbolic', + 'audio-card-analog-usb': 'audio-speakers-symbolic', + 'audio-card-analog-pci': 'audio-card-symbolic', + 'preferences-system': 'emblem-system-symbolic', + 'com.github.Aylur.ags-symbolic': 'controls-symbolic', + 'com.github.Aylur.ags': 'controls-symbolic', }; export default { - missing: "image-missing-symbolic", + missing: 'image-missing-symbolic', nix: { - nix: "nix-snowflake-symbolic", + nix: 'nix-snowflake-symbolic', }, app: { - terminal: "terminal-symbolic", + terminal: 'terminal-symbolic', }, fallback: { - executable: "application-x-executable", - notification: "dialog-information-symbolic", - video: "video-x-generic-symbolic", - audio: "audio-x-generic-symbolic", + executable: 'application-x-executable', + notification: 'dialog-information-symbolic', + video: 'video-x-generic-symbolic', + audio: 'audio-x-generic-symbolic', }, ui: { - close: "window-close-symbolic", - colorpicker: "color-select-symbolic", - info: "info-symbolic", - link: "external-link-symbolic", - lock: "system-lock-screen-symbolic", - menu: "open-menu-symbolic", - refresh: "view-refresh-symbolic", - search: "system-search-symbolic", - settings: "emblem-system-symbolic", - themes: "preferences-desktop-theme-symbolic", - tick: "object-select-symbolic", - time: "hourglass-symbolic", - toolbars: "toolbars-symbolic", - warning: "dialog-warning-symbolic", - avatar: "avatar-default-symbolic", + close: 'window-close-symbolic', + colorpicker: 'color-select-symbolic', + info: 'info-symbolic', + link: 'external-link-symbolic', + lock: 'system-lock-screen-symbolic', + menu: 'open-menu-symbolic', + refresh: 'view-refresh-symbolic', + search: 'system-search-symbolic', + settings: 'emblem-system-symbolic', + themes: 'preferences-desktop-theme-symbolic', + tick: 'object-select-symbolic', + time: 'hourglass-symbolic', + toolbars: 'toolbars-symbolic', + warning: 'dialog-warning-symbolic', + avatar: 'avatar-default-symbolic', arrow: { - right: "pan-end-symbolic", - left: "pan-start-symbolic", - down: "pan-down-symbolic", - up: "pan-up-symbolic", + right: 'pan-end-symbolic', + left: 'pan-start-symbolic', + down: 'pan-down-symbolic', + up: 'pan-up-symbolic', }, }, audio: { mic: { - muted: "microphone-disabled-symbolic", - low: "microphone-sensitivity-low-symbolic", - medium: "microphone-sensitivity-medium-symbolic", - high: "microphone-sensitivity-high-symbolic", + muted: 'microphone-disabled-symbolic', + low: 'microphone-sensitivity-low-symbolic', + medium: 'microphone-sensitivity-medium-symbolic', + high: 'microphone-sensitivity-high-symbolic', }, volume: { - muted: "audio-volume-muted-symbolic", - low: "audio-volume-low-symbolic", - medium: "audio-volume-medium-symbolic", - high: "audio-volume-high-symbolic", - overamplified: "audio-volume-overamplified-symbolic", + muted: 'audio-volume-muted-symbolic', + low: 'audio-volume-low-symbolic', + medium: 'audio-volume-medium-symbolic', + high: 'audio-volume-high-symbolic', + overamplified: 'audio-volume-overamplified-symbolic', }, type: { - headset: "audio-headphones-symbolic", - speaker: "audio-speakers-symbolic", - card: "audio-card-symbolic", + headset: 'audio-headphones-symbolic', + speaker: 'audio-speakers-symbolic', + card: 'audio-card-symbolic', }, - mixer: "mixer-symbolic", + mixer: 'mixer-symbolic', }, powerprofile: { - balanced: "power-profile-balanced-symbolic", - "power-saver": "power-profile-power-saver-symbolic", - performance: "power-profile-performance-symbolic", + balanced: 'power-profile-balanced-symbolic', + 'power-saver': 'power-profile-power-saver-symbolic', + performance: 'power-profile-performance-symbolic', }, asusctl: { profile: { - Balanced: "power-profile-balanced-symbolic", - Quiet: "power-profile-power-saver-symbolic", - Performance: "power-profile-performance-symbolic", + Balanced: 'power-profile-balanced-symbolic', + Quiet: 'power-profile-power-saver-symbolic', + Performance: 'power-profile-performance-symbolic', }, mode: { - Integrated: "processor-symbolic", - Hybrid: "controller-symbolic", + Integrated: 'processor-symbolic', + Hybrid: 'controller-symbolic', }, }, battery: { - charging: "battery-flash-symbolic", - warning: "battery-empty-symbolic", + charging: 'battery-flash-symbolic', + warning: 'battery-empty-symbolic', }, bluetooth: { - enabled: "bluetooth-active-symbolic", - disabled: "bluetooth-disabled-symbolic", + enabled: 'bluetooth-active-symbolic', + disabled: 'bluetooth-disabled-symbolic', }, brightness: { - indicator: "display-brightness-symbolic", - keyboard: "keyboard-brightness-symbolic", - screen: "display-brightness-symbolic", + indicator: 'display-brightness-symbolic', + keyboard: 'keyboard-brightness-symbolic', + screen: 'display-brightness-symbolic', }, powermenu: { - sleep: "weather-clear-night-symbolic", - reboot: "system-reboot-symbolic", - logout: "system-log-out-symbolic", - shutdown: "system-shutdown-symbolic", + sleep: 'weather-clear-night-symbolic', + reboot: 'system-reboot-symbolic', + logout: 'system-log-out-symbolic', + shutdown: 'system-shutdown-symbolic', }, recorder: { - recording: "media-record-symbolic", + recording: 'media-record-symbolic', }, notifications: { - noisy: "org.gnome.Settings-notifications-symbolic", - silent: "notifications-disabled-symbolic", - message: "chat-bubbles-symbolic", + noisy: 'org.gnome.Settings-notifications-symbolic', + silent: 'notifications-disabled-symbolic', + message: 'chat-bubbles-symbolic', }, trash: { - full: "user-trash-full-symbolic", - empty: "user-trash-symbolic", + full: 'user-trash-full-symbolic', + empty: 'user-trash-symbolic', }, mpris: { shuffle: { - enabled: "media-playlist-shuffle-symbolic", - disabled: "media-playlist-consecutive-symbolic", + enabled: 'media-playlist-shuffle-symbolic', + disabled: 'media-playlist-consecutive-symbolic', }, loop: { - none: "media-playlist-repeat-symbolic", - track: "media-playlist-repeat-song-symbolic", - playlist: "media-playlist-repeat-symbolic", + none: 'media-playlist-repeat-symbolic', + track: 'media-playlist-repeat-song-symbolic', + playlist: 'media-playlist-repeat-symbolic', }, - playing: "media-playback-pause-symbolic", - paused: "media-playback-start-symbolic", - stopped: "media-playback-start-symbolic", - prev: "media-skip-backward-symbolic", - next: "media-skip-forward-symbolic", + playing: 'media-playback-pause-symbolic', + paused: 'media-playback-start-symbolic', + stopped: 'media-playback-start-symbolic', + prev: 'media-skip-backward-symbolic', + next: 'media-skip-forward-symbolic', }, system: { - cpu: "org.gnome.SystemMonitor-symbolic", - ram: "drive-harddisk-solidstate-symbolic", - temp: "temperature-symbolic", + cpu: 'org.gnome.SystemMonitor-symbolic', + ram: 'drive-harddisk-solidstate-symbolic', + temp: 'temperature-symbolic', }, color: { - dark: "dark-mode-symbolic", - light: "light-mode-symbolic", + dark: 'dark-mode-symbolic', + light: 'light-mode-symbolic', }, weather: { - warning: "dialog-warning-symbolic", - sunny: "weather-clear-symbolic", - clear: "weather-clear-night-symbolic", - partly_cloudy: "weather-few-clouds-symbolic", - partly_cloudy_night: "weather-few-clouds-night-symbolic", - cloudy: "weather-overcast-symbolic", - overcast: "weather-overcast-symbolic", - mist: "weather-overcast-symbolic", - patchy_rain_nearby: "weather-showers-scattered-symbolic", - patchy_rain_possible: "weather-showers-scattered-symbolic", - patchy_snow_possible: "weather-snow-symbolic", - patchy_sleet_possible: "weather-snow-symbolic", - patchy_freezing_drizzle_possible: "weather-showers-scattered-symbolic", - thundery_outbreaks_possible: "weather-overcast-symbolic", - blowing_snow: "weather-snow-symbolic", - blizzard: "weather-snow-symbolic", - fog: "weather-fog-symbolic", - freezing_fog: "weather-fog-symbolic", - patchy_light_drizzle: "weather-showers-scattered-symbolic", - light_drizzle: "weather-showers-symbolic", - freezing_drizzle: "weather-showers-symbolic", - heavy_freezing_drizzle: "weather-showers-symbolic", - patchy_light_rain: "weather-showers-scattered-symbolic", - light_rain: "weather-showers-symbolic", - moderate_rain_at_times: "weather-showers-symbolic", - moderate_rain: "weather-showers-symbolic", - heavy_rain_at_times: "weather-showers-symbolic", - heavy_rain: "weather-showers-symbolic", - light_freezing_rain: "weather-showers-symbolic", - moderate_or_heavy_freezing_rain: "weather-showers-symbolic", - light_sleet: "weather-snow-symbolic", - moderate_or_heavy_sleet: "weather-snow-symbolic", - patchy_light_snow: "weather-snow-symbolic", - light_snow: "weather-snow-symbolic", - patchy_moderate_snow: "weather-snow-symbolic", - moderate_snow: "weather-snow-symbolic", - patchy_heavy_snow: "weather-snow-symbolic", - heavy_snow: "weather-snow-symbolic", - ice_pellets: "weather-showers-symbolic", - light_rain_shower: "weather-showers-symbolic", - moderate_or_heavy_rain_shower: "weather-showers-symbolic", - torrential_rain_shower: "weather-showers-symbolic", - light_sleet_showers: "weather-showers-symbolic", - moderate_or_heavy_sleet_showers: "weather-showers-symbolic", - light_snow_showers: "weather-snow-symbolic", - moderate_or_heavy_snow_showers: "weather-snow-symbolic", - light_showers_of_ice_pellets: "weather-showers-symbolic", - moderate_or_heavy_showers_of_ice_pellets: "weather-showers-symbolic", - patchy_light_rain_with_thunder: "weather-showers-scattered-symbolic", - moderate_or_heavy_rain_with_thunder: "weather-showers-symbolic", - patchy_light_snow_with_thunder: "weather-snow-symbolic", - moderate_or_heavy_snow_with_thunder: "weather-snow-symbolic", + warning: 'dialog-warning-symbolic', + sunny: 'weather-clear-symbolic', + clear: 'weather-clear-night-symbolic', + partly_cloudy: 'weather-few-clouds-symbolic', + partly_cloudy_night: 'weather-few-clouds-night-symbolic', + cloudy: 'weather-overcast-symbolic', + overcast: 'weather-overcast-symbolic', + mist: 'weather-overcast-symbolic', + patchy_rain_nearby: 'weather-showers-scattered-symbolic', + patchy_rain_possible: 'weather-showers-scattered-symbolic', + patchy_snow_possible: 'weather-snow-symbolic', + patchy_sleet_possible: 'weather-snow-symbolic', + patchy_freezing_drizzle_possible: 'weather-showers-scattered-symbolic', + thundery_outbreaks_possible: 'weather-overcast-symbolic', + blowing_snow: 'weather-snow-symbolic', + blizzard: 'weather-snow-symbolic', + fog: 'weather-fog-symbolic', + freezing_fog: 'weather-fog-symbolic', + patchy_light_drizzle: 'weather-showers-scattered-symbolic', + light_drizzle: 'weather-showers-symbolic', + freezing_drizzle: 'weather-showers-symbolic', + heavy_freezing_drizzle: 'weather-showers-symbolic', + patchy_light_rain: 'weather-showers-scattered-symbolic', + light_rain: 'weather-showers-symbolic', + moderate_rain_at_times: 'weather-showers-symbolic', + moderate_rain: 'weather-showers-symbolic', + heavy_rain_at_times: 'weather-showers-symbolic', + heavy_rain: 'weather-showers-symbolic', + light_freezing_rain: 'weather-showers-symbolic', + moderate_or_heavy_freezing_rain: 'weather-showers-symbolic', + light_sleet: 'weather-snow-symbolic', + moderate_or_heavy_sleet: 'weather-snow-symbolic', + patchy_light_snow: 'weather-snow-symbolic', + light_snow: 'weather-snow-symbolic', + patchy_moderate_snow: 'weather-snow-symbolic', + moderate_snow: 'weather-snow-symbolic', + patchy_heavy_snow: 'weather-snow-symbolic', + heavy_snow: 'weather-snow-symbolic', + ice_pellets: 'weather-showers-symbolic', + light_rain_shower: 'weather-showers-symbolic', + moderate_or_heavy_rain_shower: 'weather-showers-symbolic', + torrential_rain_shower: 'weather-showers-symbolic', + light_sleet_showers: 'weather-showers-symbolic', + moderate_or_heavy_sleet_showers: 'weather-showers-symbolic', + light_snow_showers: 'weather-snow-symbolic', + moderate_or_heavy_snow_showers: 'weather-snow-symbolic', + light_showers_of_ice_pellets: 'weather-showers-symbolic', + moderate_or_heavy_showers_of_ice_pellets: 'weather-showers-symbolic', + patchy_light_rain_with_thunder: 'weather-showers-scattered-symbolic', + moderate_or_heavy_rain_with_thunder: 'weather-showers-symbolic', + patchy_light_snow_with_thunder: 'weather-snow-symbolic', + moderate_or_heavy_snow_with_thunder: 'weather-snow-symbolic', }, -}; +} as const; diff --git a/modules/icons/weather.ts b/modules/icons/weather.ts new file mode 100644 index 0000000..90980a0 --- /dev/null +++ b/modules/icons/weather.ts @@ -0,0 +1,54 @@ +export const weatherIcons = { + warning: '󰼯', + sunny: '󰖙', + clear: '󰖔', + partly_cloudy: '󰖕', + partly_cloudy_night: '󰼱', + cloudy: '󰖐', + overcast: '󰖕', + mist: '󰖑', + patchy_rain_nearby: '󰼳', + patchy_rain_possible: '󰼳', + patchy_snow_possible: '󰼴', + patchy_sleet_possible: '󰙿', + patchy_freezing_drizzle_possible: '󰙿', + thundery_outbreaks_possible: '󰙾', + blowing_snow: '󰼶', + blizzard: '󰼶', + fog: '󰖑', + freezing_fog: '󰖑', + patchy_light_drizzle: '󰼳', + light_drizzle: '󰼳', + freezing_drizzle: '󰙿', + heavy_freezing_drizzle: '󰙿', + patchy_light_rain: '󰼳', + light_rain: '󰼳', + moderate_rain_at_times: '󰖗', + moderate_rain: '󰼳', + heavy_rain_at_times: '󰖖', + heavy_rain: '󰖖', + light_freezing_rain: '󰙿', + moderate_or_heavy_freezing_rain: '󰙿', + light_sleet: '󰙿', + moderate_or_heavy_sleet: '󰙿', + patchy_light_snow: '󰼴', + light_snow: '󰼴', + patchy_moderate_snow: '󰼴', + moderate_snow: '󰼶', + patchy_heavy_snow: '󰼶', + heavy_snow: '󰼶', + ice_pellets: '󰖒', + light_rain_shower: '󰖖', + moderate_or_heavy_rain_shower: '󰖖', + torrential_rain_shower: '󰖖', + light_sleet_showers: '󰼵', + moderate_or_heavy_sleet_showers: '󰼵', + light_snow_showers: '󰼵', + moderate_or_heavy_snow_showers: '󰼵', + light_showers_of_ice_pellets: '󰖒', + moderate_or_heavy_showers_of_ice_pellets: '󰖒', + patchy_light_rain_with_thunder: '󰙾', + moderate_or_heavy_rain_with_thunder: '󰙾', + patchy_light_snow_with_thunder: '󰼶', + moderate_or_heavy_snow_with_thunder: '󰼶', +} as const; diff --git a/modules/menus/DropdownMenu.ts b/modules/menus/DropdownMenu.ts deleted file mode 100644 index 2c9cc18..0000000 --- a/modules/menus/DropdownMenu.ts +++ /dev/null @@ -1,184 +0,0 @@ -const hyprland = await Service.import("hyprland"); -import { Exclusivity } from "lib/types/widget"; -import { bash } from "lib/utils"; -import { Monitor } from "types/service/hyprland"; - -export const Padding = (name: string) => - Widget.EventBox({ - hexpand: true, - vexpand: true, - can_focus: true, - child: Widget.Box(), - setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)), - }); - -const moveBoxToCursor = (self: any, fixed: boolean) => { - if (fixed) { - return; - } - - globalMousePos.connect("changed", async ({ value }) => { - const curHyprlandMonitor = hyprland.monitors.find(m => m.id === hyprland.active.monitor.id); - const dropdownWidth = self.child.get_allocation().width; - - let hyprScaling = 1; - try { - const monitorInfo = await bash('hyprctl monitors -j'); - const parsedMonitorInfo = JSON.parse(monitorInfo); - - const foundMonitor = parsedMonitorInfo.find((monitor: Monitor) => - monitor.id === hyprland.active.monitor.id - ); - hyprScaling = foundMonitor?.scale || 1; - } catch (error) { - console.error(`Error parsing hyprland monitors: ${error}`); - } - - let monWidth = curHyprlandMonitor?.width; - let monHeight = curHyprlandMonitor?.height; - - if (monWidth === undefined || monHeight === undefined || hyprScaling === undefined) { - return; - } - - // If GDK Scaling is applied, then get divide width by scaling - // to get the proper coordinates. - // Ex: On a 2860px wide monitor... if scaling is set to 2, then the right - // end of the monitor is the 1430th pixel. - const gdkScale = Utils.exec('bash -c "echo $GDK_SCALE"'); - - if (/^\d+(.\d+)?$/.test(gdkScale)) { - const scale = parseFloat(gdkScale); - monWidth = monWidth / scale; - monHeight = monHeight / scale; - } else { - monWidth = monWidth / hyprScaling; - monHeight = monHeight / hyprScaling; - } - - // If monitor is vertical (transform = 1 || 3) swap height and width - const isVertical = curHyprlandMonitor?.transform !== undefined - ? curHyprlandMonitor.transform % 2 !== 0 - : false; - - if (isVertical) { - [monWidth, monHeight] = [monHeight, monWidth]; - } - - let marginRight = monWidth - dropdownWidth / 2; - marginRight = fixed ? marginRight - monWidth / 2 : marginRight - value[0]; - let marginLeft = monWidth - dropdownWidth - marginRight; - - const minimumMargin = 0; - - if (marginRight < minimumMargin) { - marginRight = minimumMargin; - marginLeft = monWidth - dropdownWidth - minimumMargin; - } - - if (marginLeft < minimumMargin) { - marginLeft = minimumMargin; - marginRight = monWidth - dropdownWidth - minimumMargin; - } - - const marginTop = 45; - const marginBottom = monHeight - marginTop; - self.set_margin_left(marginLeft); - self.set_margin_right(marginRight); - self.set_margin_bottom(marginBottom); - }); -}; - -// NOTE: We make the window visible for 2 seconds (on startup) so the child -// elements can allocat their proper dimensions. -// Otherwise the width that we rely on for menu positioning is set improperly -// for the first time we open a menu of each type. -const initRender = Variable(true); - -setTimeout(() => { - initRender.value = false; -}, 2000); - -export default ({ - name, - child, - layout = "center", - transition, - exclusivity = "ignore" as Exclusivity, - fixed = false, - ...props -}) => - Widget.Window({ - name, - class_names: [name, "dropdown-menu"], - setup: (w) => w.keybind("Escape", () => App.closeWindow(name)), - visible: initRender.bind("value"), - keymode: "on-demand", - exclusivity, - layer: "top", - anchor: ["top", "left"], - child: Widget.EventBox({ - class_name: "parent-event", - on_primary_click: () => App.closeWindow(name), - on_secondary_click: () => App.closeWindow(name), - child: Widget.Box({ - class_name: "top-eb", - vertical: true, - children: [ - Widget.EventBox({ - class_name: "mid-eb event-top-padding-static", - hexpand: true, - vexpand: false, - can_focus: false, - child: Widget.Box(), - setup: (w) => { - w.on("button-press-event", () => App.toggleWindow(name)); - w.set_margin_top(1); - }, - }), - Widget.EventBox({ - class_name: "mid-eb event-top-padding", - hexpand: true, - vexpand: false, - can_focus: false, - child: Widget.Box(), - setup: (w) => { - w.on("button-press-event", () => App.toggleWindow(name)); - w.set_margin_top(1); - }, - }), - Widget.EventBox({ - class_name: "in-eb menu-event-box", - on_primary_click: () => { - return true; - }, - on_secondary_click: () => { - return true; - }, - setup: (self) => { - moveBoxToCursor(self, fixed); - }, - child: Widget.Box({ - class_name: "dropdown-menu-container", - css: "padding: 1px; margin: -1px;", - child: Widget.Revealer({ - revealChild: false, - setup: (self) => - self.hook(App, (_, wname, visible) => { - if (wname === name) self.reveal_child = visible; - }), - transition: "crossfade", - transitionDuration: 350, - child: Widget.Box({ - class_name: "dropdown-menu-container", - can_focus: true, - children: [child], - }), - }), - }), - }), - ], - }), - }), - ...props, - }); diff --git a/modules/menus/audio/active/SelectedInput.ts b/modules/menus/audio/active/SelectedInput.ts index 6bae8ae..2099ae7 100644 --- a/modules/menus/audio/active/SelectedInput.ts +++ b/modules/menus/audio/active/SelectedInput.ts @@ -1,31 +1,36 @@ -const audio = await Service.import("audio"); +const audio = await Service.import('audio'); +import { BarBoxChild } from 'lib/types/bar.js'; import { getIcon } from '../utils.js'; -const renderActiveInput = () => { +const renderActiveInput = (): BarBoxChild => { return [ Widget.Box({ - class_name: "menu-slider-container input", + class_name: 'menu-slider-container input', children: [ Widget.Button({ vexpand: false, - vpack: "end", + vpack: 'end', setup: (self) => { self.hook(audio, () => { const mic = audio.microphone; - const className = `menu-active-button input ${mic.is_muted ? "muted" : ""}`; + const className = `menu-active-button input ${mic.is_muted ? 'muted' : ''}`; return (self.class_name = className); }); }, - on_primary_click: () => - (audio.microphone.is_muted = !audio.microphone.is_muted), + on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted), child: Widget.Icon({ - class_name: "menu-active-icon input", + class_name: 'menu-active-icon input', setup: (self) => { self.hook(audio, () => { - self.icon = getIcon( - audio.microphone.volume, - audio.microphone.is_muted, - )["mic"]; + const isMicMuted = + audio.microphone.is_muted !== null ? audio.microphone.is_muted : true; + + if (audio.microphone.volume > 0) { + self.icon = getIcon(audio.microphone.volume, isMicMuted)['mic']; + return; + } + + self.icon = getIcon(100, false)['mic']; }); }, }), @@ -34,15 +39,17 @@ const renderActiveInput = () => { vertical: true, children: [ Widget.Label({ - class_name: "menu-active input", - hpack: "start", - truncate: "end", + class_name: 'menu-active input', + hpack: 'start', + truncate: 'end', wrap: true, - label: audio.bind("microphone").as((v) => v.description === null ? "No input device found..." : v.description), + label: audio + .bind('microphone') + .as((v) => (v.description === null ? 'No input device found...' : v.description)), }), Widget.Slider({ - value: audio.microphone.bind("volume").as((v) => v), - class_name: "menu-active-slider menu-slider inputs", + value: audio.microphone.bind('volume').as((v) => v), + class_name: 'menu-active-slider menu-slider inputs', draw_value: false, hexpand: true, min: 0, @@ -52,11 +59,9 @@ const renderActiveInput = () => { ], }), Widget.Label({ - class_name: "menu-active-percentage input", - vpack: "end", - label: audio.microphone - .bind("volume") - .as((v) => `${Math.round(v * 100)}%`), + class_name: 'menu-active-percentage input', + vpack: 'end', + label: audio.microphone.bind('volume').as((v) => `${Math.round(v * 100)}%`), }), ], }), diff --git a/modules/menus/audio/active/SelectedPlayback.ts b/modules/menus/audio/active/SelectedPlayback.ts index 02e5a10..2ab5887 100644 --- a/modules/menus/audio/active/SelectedPlayback.ts +++ b/modules/menus/audio/active/SelectedPlayback.ts @@ -1,31 +1,29 @@ -const audio = await Service.import("audio"); -import { getIcon } from "../utils.js"; +const audio = await Service.import('audio'); +import { BarBoxChild } from 'lib/types/bar.js'; +import { getIcon } from '../utils.js'; -const renderActivePlayback = () => { +const renderActivePlayback = (): BarBoxChild => { return [ Widget.Box({ - class_name: "menu-slider-container playback", + class_name: 'menu-slider-container playback', children: [ Widget.Button({ vexpand: false, - vpack: "end", + vpack: 'end', setup: (self) => { self.hook(audio, () => { const spkr = audio.speaker; - const className = `menu-active-button playback ${spkr.is_muted ? "muted" : ""}`; + const className = `menu-active-button playback ${spkr.is_muted ? 'muted' : ''}`; return (self.class_name = className); }); }, - on_primary_click: () => - (audio.speaker.is_muted = !audio.speaker.is_muted), + on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted), child: Widget.Icon({ - class_name: "menu-active-icon playback", + class_name: 'menu-active-icon playback', setup: (self) => { self.hook(audio, () => { - self.icon = getIcon( - audio.speaker.volume, - audio.speaker.is_muted, - )["spkr"]; + const isSpeakerMuted = audio.speaker.is_muted !== null ? audio.speaker.is_muted : true; + self.icon = getIcon(audio.speaker.volume, isSpeakerMuted)['spkr']; }); }, }), @@ -34,16 +32,16 @@ const renderActivePlayback = () => { vertical: true, children: [ Widget.Label({ - class_name: "menu-active playback", - hpack: "start", - truncate: "end", + class_name: 'menu-active playback', + hpack: 'start', + truncate: 'end', expand: true, wrap: true, - label: audio.bind("speaker").as((v) => v.description || ""), + label: audio.bind('speaker').as((v) => v.description || ''), }), Widget.Slider({ - value: audio["speaker"].bind("volume"), - class_name: "menu-active-slider menu-slider playback", + value: audio['speaker'].bind('volume'), + class_name: 'menu-active-slider menu-slider playback', draw_value: false, hexpand: true, min: 0, @@ -53,11 +51,9 @@ const renderActivePlayback = () => { ], }), Widget.Label({ - vpack: "end", - class_name: "menu-active-percentage playback", - label: audio.speaker - .bind("volume") - .as((v) => `${Math.round(v * 100)}%`), + vpack: 'end', + class_name: 'menu-active-percentage playback', + label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`), }), ], }), diff --git a/modules/menus/audio/active/index.ts b/modules/menus/audio/active/index.ts index 84d14bf..fc1c65a 100644 --- a/modules/menus/audio/active/index.ts +++ b/modules/menus/audio/active/index.ts @@ -1,39 +1,40 @@ -import { renderActiveInput } from "./SelectedInput.js"; -import { renderActivePlayback } from "./SelectedPlayback.js"; +import { BarBoxChild } from 'lib/types/bar.js'; +import { renderActiveInput } from './SelectedInput.js'; +import { renderActivePlayback } from './SelectedPlayback.js'; -const activeDevices = () => { - return Widget.Box({ - class_name: "menu-section-container volume", - vertical: true, - children: [ - Widget.Box({ - class_name: "menu-label-container volume selected", - hpack: "fill", - child: Widget.Label({ - class_name: "menu-label audio volume", - hexpand: true, - hpack: "start", - label: "Volume", - }), - }), - Widget.Box({ - class_name: "menu-items-section selected", +const activeDevices = (): BarBoxChild => { + return Widget.Box({ + class_name: 'menu-section-container volume', vertical: true, children: [ - Widget.Box({ - class_name: "menu-active-container playback", - vertical: true, - children: renderActivePlayback(), - }), - Widget.Box({ - class_name: "menu-active-container input", - vertical: true, - children: renderActiveInput(), - }), + Widget.Box({ + class_name: 'menu-label-container volume selected', + hpack: 'fill', + child: Widget.Label({ + class_name: 'menu-label audio volume', + hexpand: true, + hpack: 'start', + label: 'Volume', + }), + }), + Widget.Box({ + class_name: 'menu-items-section selected', + vertical: true, + children: [ + Widget.Box({ + class_name: 'menu-active-container playback', + vertical: true, + children: renderActivePlayback(), + }), + Widget.Box({ + class_name: 'menu-active-container input', + vertical: true, + children: renderActiveInput(), + }), + ], + }), ], - }), - ], - }); + }); }; export { activeDevices }; diff --git a/modules/menus/audio/available/InputDevices.ts b/modules/menus/audio/available/InputDevices.ts index 42b2a76..8c75a5a 100644 --- a/modules/menus/audio/available/InputDevices.ts +++ b/modules/menus/audio/available/InputDevices.ts @@ -1,7 +1,8 @@ -const audio = await Service.import("audio"); -import { Stream } from "types/service/audio"; +const audio = await Service.import('audio'); +import { InputDevices } from 'lib/types/audio'; +import { Stream } from 'types/service/audio'; -const renderInputDevices = (inputDevices: Stream[]) => { +const renderInputDevices = (inputDevices: Stream[]): InputDevices => { if (inputDevices.length === 0) { return [ Widget.Button({ @@ -9,11 +10,11 @@ const renderInputDevices = (inputDevices: Stream[]) => { child: Widget.Box({ children: [ Widget.Box({ - hpack: "start", + hpack: 'start', children: [ Widget.Label({ - class_name: "menu-button-name input", - label: "No input devices found...", + class_name: 'menu-button-name input', + label: 'No input devices found...', }), ], }), @@ -29,28 +30,28 @@ const renderInputDevices = (inputDevices: Stream[]) => { child: Widget.Box({ children: [ Widget.Box({ - hpack: "start", + hpack: 'start', children: [ Widget.Label({ wrap: true, class_name: audio.microphone - .bind("description") + .bind('description') .as((v) => device.description === v - ? "menu-button-icon active input txt-icon" - : "menu-button-icon input txt-icon", + ? 'menu-button-icon active input txt-icon' + : 'menu-button-icon input txt-icon', ), - label: "", + label: '', }), Widget.Label({ - truncate: "end", + truncate: 'end', wrap: true, class_name: audio.microphone - .bind("description") + .bind('description') .as((v) => device.description === v - ? "menu-button-name active input" - : "menu-button-name input", + ? 'menu-button-name active input' + : 'menu-button-name input', ), label: device.description, }), diff --git a/modules/menus/audio/available/PlaybackDevices.ts b/modules/menus/audio/available/PlaybackDevices.ts index 67bdd54..02bc618 100644 --- a/modules/menus/audio/available/PlaybackDevices.ts +++ b/modules/menus/audio/available/PlaybackDevices.ts @@ -1,16 +1,17 @@ -const audio = await Service.import("audio"); -import { Stream } from "types/service/audio"; +const audio = await Service.import('audio'); +import { PlaybackDevices } from 'lib/types/audio'; +import { Stream } from 'types/service/audio'; -const renderPlaybacks = (playbackDevices: Stream[]) => { +const renderPlaybacks = (playbackDevices: Stream[]): PlaybackDevices => { return playbackDevices.map((device) => { - if (device.description === "Dummy Output") { + if (device.description === 'Dummy Output') { return Widget.Box({ - class_name: "menu-unfound-button playback", + class_name: 'menu-unfound-button playback', child: Widget.Box({ children: [ Widget.Label({ - class_name: "menu-button-name playback", - label: "No playback devices found...", + class_name: 'menu-button-name playback', + label: 'No playback devices found...', }), ], }), @@ -22,29 +23,29 @@ const renderPlaybacks = (playbackDevices: Stream[]) => { child: Widget.Box({ children: [ Widget.Box({ - hpack: "start", + hpack: 'start', children: [ Widget.Label({ - truncate: "end", + truncate: 'end', wrap: true, class_name: audio.speaker - .bind("description") + .bind('description') .as((v) => device.description === v - ? "menu-button-icon active playback txt-icon" - : "menu-button-icon playback txt-icon", + ? 'menu-button-icon active playback txt-icon' + : 'menu-button-icon playback txt-icon', ), - label: "", + label: '', }), Widget.Label({ - truncate: "end", + truncate: 'end', wrap: true, class_name: audio.speaker - .bind("description") + .bind('description') .as((v) => device.description === v - ? "menu-button-name active playback" - : "menu-button-name playback", + ? 'menu-button-name active playback' + : 'menu-button-name playback', ), label: device.description, }), diff --git a/modules/menus/audio/available/index.ts b/modules/menus/audio/available/index.ts index 1dca28d..e9b0d48 100644 --- a/modules/menus/audio/available/index.ts +++ b/modules/menus/audio/available/index.ts @@ -1,66 +1,63 @@ -const audio = await Service.import("audio"); -import { renderInputDevices } from "./InputDevices.js"; -import { renderPlaybacks } from "./PlaybackDevices.js"; +const audio = await Service.import('audio'); +import { BoxWidget } from 'lib/types/widget.js'; +import { renderInputDevices } from './InputDevices.js'; +import { renderPlaybacks } from './PlaybackDevices.js'; -const availableDevices = () => { +const availableDevices = (): BoxWidget => { return Widget.Box({ vertical: true, children: [ Widget.Box({ - class_name: "menu-section-container playback", + class_name: 'menu-section-container playback', vertical: true, children: [ Widget.Box({ - class_name: "menu-label-container playback", - hpack: "fill", + class_name: 'menu-label-container playback', + hpack: 'fill', child: Widget.Label({ - class_name: "menu-label audio playback", + class_name: 'menu-label audio playback', hexpand: true, - hpack: "start", - label: "Playback Devices", + hpack: 'start', + label: 'Playback Devices', }), }), Widget.Box({ - class_name: "menu-items-section playback", + class_name: 'menu-items-section playback', vertical: true, children: [ Widget.Box({ - class_name: "menu-container playback", + class_name: 'menu-container playback', vertical: true, children: [ Widget.Box({ vertical: true, - children: audio - .bind("speakers") - .as((v) => renderPlaybacks(v)), + children: audio.bind('speakers').as((v) => renderPlaybacks(v)), }), ], }), ], }), Widget.Box({ - class_name: "menu-label-container input", - hpack: "fill", + class_name: 'menu-label-container input', + hpack: 'fill', child: Widget.Label({ - class_name: "menu-label audio input", + class_name: 'menu-label audio input', hexpand: true, - hpack: "start", - label: "Input Devices", + hpack: 'start', + label: 'Input Devices', }), }), Widget.Box({ - class_name: "menu-items-section input", + class_name: 'menu-items-section input', vertical: true, children: [ Widget.Box({ - class_name: "menu-container input", + class_name: 'menu-container input', vertical: true, children: [ Widget.Box({ vertical: true, - children: audio - .bind("microphones") - .as((v) => renderInputDevices(v)), + children: audio.bind('microphones').as((v) => renderInputDevices(v)), }), ], }), diff --git a/modules/menus/audio/index.ts b/modules/menus/audio/index.ts index f39231b..5bf317d 100644 --- a/modules/menus/audio/index.ts +++ b/modules/menus/audio/index.ts @@ -1,24 +1,23 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { activeDevices } from "./active/index.js"; -import { availableDevices } from "./available/index.js"; +import Window from 'types/widgets/window.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +import { activeDevices } from './active/index.js'; +import { availableDevices } from './available/index.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { +export default (): Window => { return DropdownMenu({ - name: "audiomenu", - transition: "crossfade", + name: 'audiomenu', + transition: 'crossfade', child: Widget.Box({ - class_name: "menu-items audio", - hpack: "fill", + class_name: 'menu-items audio', + hpack: 'fill', hexpand: true, child: Widget.Box({ vertical: true, - hpack: "fill", + hpack: 'fill', hexpand: true, - class_name: "menu-items-container audio", - children: [ - activeDevices(), - availableDevices(), - ], + class_name: 'menu-items-container audio', + children: [activeDevices(), availableDevices()], }), }), }); diff --git a/modules/menus/audio/utils.ts b/modules/menus/audio/utils.ts index 2aa71a7..aa1de5c 100644 --- a/modules/menus/audio/utils.ts +++ b/modules/menus/audio/utils.ts @@ -1,27 +1,29 @@ -const getIcon = (audioVol, isMuted) => { - const speakerIcons = { - 101: "audio-volume-overamplified-symbolic", - 66: "audio-volume-high-symbolic", - 34: "audio-volume-medium-symbolic", - 1: "audio-volume-low-symbolic", - 0: "audio-volume-muted-symbolic", - }; +const speakerIcons = { + 101: 'audio-volume-overamplified-symbolic', + 66: 'audio-volume-high-symbolic', + 34: 'audio-volume-medium-symbolic', + 1: 'audio-volume-low-symbolic', + 0: 'audio-volume-muted-symbolic', +} as const; - const inputIcons = { - 66: "microphone-sensitivity-high-symbolic", - 34: "microphone-sensitivity-medium-symbolic", - 1: "microphone-sensitivity-low-symbolic", - 0: "microphone-disabled-symbolic", - }; +const inputIcons = { + 101: 'microphone-sensitivity-high-symbolic', + 66: 'microphone-sensitivity-high-symbolic', + 34: 'microphone-sensitivity-medium-symbolic', + 1: 'microphone-sensitivity-low-symbolic', + 0: 'microphone-disabled-symbolic', +}; - const icon = isMuted - ? 0 - : [101, 66, 34, 1, 0].find((threshold) => threshold <= audioVol * 100); +type IconVolumes = keyof typeof speakerIcons; - return { - spkr: speakerIcons[icon], - mic: inputIcons[icon], - }; +const getIcon = (audioVol: number, isMuted: boolean): Record => { + const thresholds: IconVolumes[] = [101, 66, 34, 1, 0]; + const icon = isMuted ? 0 : thresholds.find((threshold) => threshold <= audioVol * 100) || 0; + + return { + spkr: speakerIcons[icon], + mic: inputIcons[icon], + }; }; export { getIcon }; diff --git a/modules/menus/bluetooth/devices/connectedControls.ts b/modules/menus/bluetooth/devices/connectedControls.ts index 0c7e6c2..62e8345 100644 --- a/modules/menus/bluetooth/devices/connectedControls.ts +++ b/modules/menus/bluetooth/devices/connectedControls.ts @@ -1,74 +1,67 @@ -import { BluetoothDevice } from "types/service/bluetooth"; +import { BoxWidget } from 'lib/types/widget'; +import { BluetoothDevice } from 'types/service/bluetooth'; -const connectedControls = (dev: BluetoothDevice, connectedDevices: BluetoothDevice[]) => { +const connectedControls = (dev: BluetoothDevice, connectedDevices: BluetoothDevice[]): BoxWidget => { if (!connectedDevices.includes(dev.address)) { return Widget.Box({}); } return Widget.Box({ - vpack: "start", - class_name: "bluetooth-controls", + vpack: 'start', + class_name: 'bluetooth-controls', children: [ Widget.Button({ - class_name: "menu-icon-button unpair bluetooth", + class_name: 'menu-icon-button unpair bluetooth', child: Widget.Label({ - tooltip_text: dev.paired ? "Unpair" : "Pair", - class_name: "menu-icon-button-label unpair bluetooth txt-icon", - label: dev.paired ? "" : "", + tooltip_text: dev.paired ? 'Unpair' : 'Pair', + class_name: 'menu-icon-button-label unpair bluetooth txt-icon', + label: dev.paired ? '' : '', }), on_primary_click: () => Utils.execAsync([ - "bash", - "-c", - `bluetoothctl ${dev.paired ? "unpair" : "pair"} ${dev.address}`, + 'bash', + '-c', + `bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`, ]).catch((err) => - console.error( - `bluetoothctl ${dev.paired ? "unpair" : "pair"} ${dev.address}`, - err, - ), + console.error(`bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`, err), ), }), Widget.Button({ - class_name: "menu-icon-button disconnect bluetooth", + class_name: 'menu-icon-button disconnect bluetooth', child: Widget.Label({ - tooltip_text: dev.connected ? "Disconnect" : "Connect", - class_name: "menu-icon-button-label disconnect bluetooth txt-icon", - label: dev.connected ? "󱘖" : "", + tooltip_text: dev.connected ? 'Disconnect' : 'Connect', + class_name: 'menu-icon-button-label disconnect bluetooth txt-icon', + label: dev.connected ? '󱘖' : '', }), on_primary_click: () => dev.setConnection(!dev.connected), }), Widget.Button({ - class_name: "menu-icon-button untrust bluetooth", + class_name: 'menu-icon-button untrust bluetooth', child: Widget.Label({ - tooltip_text: dev.trusted ? "Untrust" : "Trust", - class_name: "menu-icon-button-label untrust bluetooth txt-icon", - label: dev.trusted ? "" : "󱖡", + tooltip_text: dev.trusted ? 'Untrust' : 'Trust', + class_name: 'menu-icon-button-label untrust bluetooth txt-icon', + label: dev.trusted ? '' : '󱖡', }), on_primary_click: () => Utils.execAsync([ - "bash", - "-c", - `bluetoothctl ${dev.trusted ? "untrust" : "trust"} ${dev.address}`, + 'bash', + '-c', + `bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`, ]).catch((err) => - console.error( - `bluetoothctl ${dev.trusted ? "untrust" : "trust"} ${dev.address}`, - err, - ), + console.error(`bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`, err), ), }), Widget.Button({ - class_name: "menu-icon-button delete bluetooth", + class_name: 'menu-icon-button delete bluetooth', child: Widget.Label({ - tooltip_text: "Forget", - class_name: "menu-icon-button-label delete bluetooth txt-icon", - label: "󰆴", + tooltip_text: 'Forget', + class_name: 'menu-icon-button-label delete bluetooth txt-icon', + label: '󰆴', }), on_primary_click: () => { - Utils.execAsync([ - "bash", - "-c", - `bluetoothctl remove ${dev.address}`, - ]).catch((err) => console.error("Bluetooth Remove", err)); + Utils.execAsync(['bash', '-c', `bluetoothctl remove ${dev.address}`]).catch((err) => + console.error('Bluetooth Remove', err), + ); }, }), ], diff --git a/modules/menus/bluetooth/devices/devicelist.ts b/modules/menus/bluetooth/devices/devicelist.ts index 6827145..e1fdcc9 100644 --- a/modules/menus/bluetooth/devices/devicelist.ts +++ b/modules/menus/bluetooth/devices/devicelist.ts @@ -1,32 +1,31 @@ -import { Bluetooth } from "types/service/bluetooth.js"; -import Box from "types/widgets/box.js"; -import { connectedControls } from "./connectedControls.js"; -import { getBluetoothIcon } from "../utils.js"; -import Gtk from "types/@girs/gtk-3.0/gtk-3.0.js"; +import { Bluetooth } from 'types/service/bluetooth.js'; +import Box from 'types/widgets/box.js'; +import { connectedControls } from './connectedControls.js'; +import { getBluetoothIcon } from '../utils.js'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -const devices = (bluetooth: Bluetooth, self: Box) => { +const devices = (bluetooth: Bluetooth, self: Box): Box => { return self.hook(bluetooth, () => { if (!bluetooth.enabled) { return (self.child = Widget.Box({ - class_name: "bluetooth-items", + class_name: 'bluetooth-items', vertical: true, expand: true, - vpack: "center", - hpack: "center", + vpack: 'center', + hpack: 'center', children: [ Widget.Label({ - class_name: "bluetooth-disabled dim", + class_name: 'bluetooth-disabled dim', hexpand: true, - label: "Bluetooth is disabled", + label: 'Bluetooth is disabled', }), ], })); } const availableDevices = bluetooth.devices - .filter( - (btDev) => btDev.name !== null, - ) + .filter((btDev) => btDev.name !== null) .sort((a, b) => { if (a.connected || a.paired) { return -1; @@ -39,25 +38,23 @@ const devices = (bluetooth: Bluetooth, self: Box) => { return b.name - a.name; }); - const conDevNames = availableDevices - .filter((d) => d.connected || d.paired) - .map((d) => d.address); + const conDevNames = availableDevices.filter((d) => d.connected || d.paired).map((d) => d.address); if (!availableDevices.length) { return (self.child = Widget.Box({ - class_name: "bluetooth-items", + class_name: 'bluetooth-items', vertical: true, expand: true, - vpack: "center", - hpack: "center", + vpack: 'center', + hpack: 'center', children: [ Widget.Label({ - class_name: "no-bluetooth-devices dim", + class_name: 'no-bluetooth-devices dim', hexpand: true, - label: "No devices currently found", + label: 'No devices currently found', }), Widget.Label({ - class_name: "search-bluetooth-label dim", + class_name: 'search-bluetooth-label dim', hexpand: true, label: "Press '󰑐' to search", }), @@ -74,41 +71,40 @@ const devices = (bluetooth: Bluetooth, self: Box) => { hexpand: true, class_name: `bluetooth-element-item ${device}`, on_primary_click: () => { - if (!conDevNames.includes(device.address)) - device.setConnection(true); + if (!conDevNames.includes(device.address)) device.setConnection(true); }, child: Widget.Box({ hexpand: true, children: [ Widget.Box({ hexpand: true, - hpack: "start", - class_name: "menu-button-container", + hpack: 'start', + class_name: 'menu-button-container', children: [ Widget.Label({ - vpack: "start", - class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? "active" : ""} txt-icon`, - label: getBluetoothIcon(`${device["icon-name"]}-symbolic`), + vpack: 'start', + class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? 'active' : ''} txt-icon`, + label: getBluetoothIcon(`${device['icon_name']}-symbolic`), }), Widget.Box({ vertical: true, - vpack: "center", + vpack: 'center', children: [ Widget.Label({ - vpack: "center", - hpack: "start", - class_name: "menu-button-name bluetooth", - truncate: "end", + vpack: 'center', + hpack: 'start', + class_name: 'menu-button-name bluetooth', + truncate: 'end', wrap: true, label: device.alias, }), Widget.Revealer({ - hpack: "start", + hpack: 'start', reveal_child: device.connected || device.paired, child: Widget.Label({ - hpack: "start", - class_name: "connection-status dim", - label: device.connected ? "Connected" : "Paired", + hpack: 'start', + class_name: 'connection-status dim', + label: device.connected ? 'Connected' : 'Paired', }), }), ], @@ -116,14 +112,14 @@ const devices = (bluetooth: Bluetooth, self: Box) => { ], }), Widget.Box({ - hpack: "end", + hpack: 'end', children: device.connecting ? [ - Widget.Spinner({ - vpack: "start", - class_name: "spinner bluetooth", - }), - ] + Widget.Spinner({ + vpack: 'start', + class_name: 'spinner bluetooth', + }), + ] : [], }), ], diff --git a/modules/menus/bluetooth/devices/index.ts b/modules/menus/bluetooth/devices/index.ts index 9b7d3a1..31315d5 100644 --- a/modules/menus/bluetooth/devices/index.ts +++ b/modules/menus/bluetooth/devices/index.ts @@ -1,17 +1,18 @@ -const bluetooth = await Service.import("bluetooth"); -import { label } from "./label.js"; -import { devices } from "./devicelist.js"; +const bluetooth = await Service.import('bluetooth'); +import { label } from './label.js'; +import { devices } from './devicelist.js'; +import { BoxWidget } from 'lib/types/widget.js'; -const Devices = () => { +const Devices = (): BoxWidget => { return Widget.Box({ - class_name: "menu-section-container", + class_name: 'menu-section-container', vertical: true, children: [ label(bluetooth), Widget.Box({ - class_name: "menu-items-section", + class_name: 'menu-items-section', child: Widget.Box({ - class_name: "menu-content", + class_name: 'menu-content', vertical: true, setup: (self) => { devices(bluetooth, self); diff --git a/modules/menus/bluetooth/devices/label.ts b/modules/menus/bluetooth/devices/label.ts index d5b8555..45fabf6 100644 --- a/modules/menus/bluetooth/devices/label.ts +++ b/modules/menus/bluetooth/devices/label.ts @@ -1,7 +1,10 @@ -import { Bluetooth } from "types/service/bluetooth"; -const label = (bluetooth: Bluetooth) => { +import { BoxWidget } from 'lib/types/widget'; +import { Bluetooth } from 'types/service/bluetooth'; + +const label = (bluetooth: Bluetooth): BoxWidget => { const searchInProgress = Variable(false); - const startRotation = () => { + + const startRotation = (): void => { searchInProgress.value = true; setTimeout(() => { searchInProgress.value = false; @@ -9,61 +12,48 @@ const label = (bluetooth: Bluetooth) => { }; return Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", - vpack: "start", + class_name: 'menu-label-container', + hpack: 'fill', + vpack: 'start', children: [ Widget.Label({ - class_name: "menu-label", - vpack: "center", - hpack: "start", - label: "Bluetooth", + class_name: 'menu-label', + vpack: 'center', + hpack: 'start', + label: 'Bluetooth', }), Widget.Box({ - class_name: "controls-container", - vpack: "start", + class_name: 'controls-container', + vpack: 'start', children: [ Widget.Switch({ - class_name: "menu-switch bluetooth", + class_name: 'menu-switch bluetooth', hexpand: true, - hpack: "end", - active: bluetooth.bind("enabled"), + hpack: 'end', + active: bluetooth.bind('enabled'), on_activate: ({ active }) => { searchInProgress.value = false; - Utils.execAsync([ - "bash", - "-c", - `bluetoothctl power ${active ? "on" : "off"}`, - ]).catch((err) => - console.error( - `bluetoothctl power ${active ? "on" : "off"}`, - err, - ), + Utils.execAsync(['bash', '-c', `bluetoothctl power ${active ? 'on' : 'off'}`]).catch( + (err) => console.error(`bluetoothctl power ${active ? 'on' : 'off'}`, err), ); }, }), Widget.Separator({ - class_name: "menu-separator bluetooth", + class_name: 'menu-separator bluetooth', }), Widget.Button({ - vpack: "center", - class_name: "menu-icon-button search", + vpack: 'center', + class_name: 'menu-icon-button search', on_primary_click: () => { startRotation(); - Utils.execAsync([ - "bash", - "-c", - "bluetoothctl --timeout 120 scan on", - ]).catch((err) => { + Utils.execAsync(['bash', '-c', 'bluetoothctl --timeout 120 scan on']).catch((err) => { searchInProgress.value = false; - console.error("bluetoothctl --timeout 120 scan on", err); + console.error('bluetoothctl --timeout 120 scan on', err); }); }, child: Widget.Icon({ - class_name: searchInProgress - .bind("value") - .as((v) => (v ? "spinning" : "")), - icon: "view-refresh-symbolic", + class_name: searchInProgress.bind('value').as((v) => (v ? 'spinning' : '')), + icon: 'view-refresh-symbolic', }), }), ], diff --git a/modules/menus/bluetooth/index.ts b/modules/menus/bluetooth/index.ts index 9557faf..3eb722e 100644 --- a/modules/menus/bluetooth/index.ts +++ b/modules/menus/bluetooth/index.ts @@ -1,21 +1,23 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { Devices } from "./devices/index.js"; +import Window from 'types/widgets/window.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +import { Devices } from './devices/index.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { - return DropdownMenu({ - name: "bluetoothmenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "menu-items bluetooth", - hpack: "fill", - hexpand: true, - child: Widget.Box({ - vertical: true, - hpack: "fill", - hexpand: true, - class_name: "menu-items-container bluetooth", - child: Devices(), - }), - }), - }); +export default (): Window => { + return DropdownMenu({ + name: 'bluetoothmenu', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'menu-items bluetooth', + hpack: 'fill', + hexpand: true, + child: Widget.Box({ + vertical: true, + hpack: 'fill', + hexpand: true, + class_name: 'menu-items-container bluetooth', + child: Devices(), + }), + }), + }); }; diff --git a/modules/menus/bluetooth/utils.ts b/modules/menus/bluetooth/utils.ts index 2024f8b..c305538 100644 --- a/modules/menus/bluetooth/utils.ts +++ b/modules/menus/bluetooth/utils.ts @@ -1,31 +1,29 @@ -const getBluetoothIcon = (iconName: string) => { +const getBluetoothIcon = (iconName: string): string => { const deviceIconMap = [ - ["^audio-card*", "󰎄"], - ["^audio-headphones*", "󰋋"], - ["^audio-headset*", "󰋎"], - ["^audio-input*", "󰍬"], - ["^audio-speakers*", "󰓃"], - ["^bluetooth*", "󰂯"], - ["^camera*", "󰄀"], - ["^computer*", "󰟀"], - ["^input-gaming*", "󰍬"], - ["^input-keyboard*", "󰌌"], - ["^input-mouse*", "󰍽"], - ["^input-tablet*", "󰓶"], - ["^media*", "󱛟"], - ["^modem*", "󱂇"], - ["^network*", "󱂇"], - ["^phone*", "󰄞"], - ["^printer*", "󰐪"], - ["^scanner*", "󰚫"], - ["^video-camera*", "󰕧"], + ['^audio-card*', '󰎄'], + ['^audio-headphones*', '󰋋'], + ['^audio-headset*', '󰋎'], + ['^audio-input*', '󰍬'], + ['^audio-speakers*', '󰓃'], + ['^bluetooth*', '󰂯'], + ['^camera*', '󰄀'], + ['^computer*', '󰟀'], + ['^input-gaming*', '󰍬'], + ['^input-keyboard*', '󰌌'], + ['^input-mouse*', '󰍽'], + ['^input-tablet*', '󰓶'], + ['^media*', '󱛟'], + ['^modem*', '󱂇'], + ['^network*', '󱂇'], + ['^phone*', '󰄞'], + ['^printer*', '󰐪'], + ['^scanner*', '󰚫'], + ['^video-camera*', '󰕧'], ]; - const foundMatch = deviceIconMap.find((icon) => - RegExp(icon[0]).test(iconName.toLowerCase()), - ); + const foundMatch = deviceIconMap.find((icon) => RegExp(icon[0]).test(iconName.toLowerCase())); - return foundMatch ? foundMatch[1] : "󰂯"; + return foundMatch ? foundMatch[1] : '󰂯'; }; export { getBluetoothIcon }; diff --git a/modules/menus/calendar/calendar.ts b/modules/menus/calendar/calendar.ts index 4d50c0c..d2af59e 100644 --- a/modules/menus/calendar/calendar.ts +++ b/modules/menus/calendar/calendar.ts @@ -1,22 +1,24 @@ -const CalendarWidget = () => { - return Widget.Box({ - class_name: "calendar-menu-item-container calendar", - hpack: "fill", - vpack: "fill", - expand: true, - child: Widget.Box({ - class_name: "calendar-container-box", - child: Widget.Calendar({ +import { BoxWidget } from 'lib/types/widget'; + +const CalendarWidget = (): BoxWidget => { + return Widget.Box({ + class_name: 'calendar-menu-item-container calendar', + hpack: 'fill', + vpack: 'fill', expand: true, - hpack: "fill", - vpack: "fill", - class_name: "calendar-menu-widget", - showDayNames: true, - showDetails: false, - showHeading: true, - }), - }), - }); + child: Widget.Box({ + class_name: 'calendar-container-box', + child: Widget.Calendar({ + expand: true, + hpack: 'fill', + vpack: 'fill', + class_name: 'calendar-menu-widget', + showDayNames: true, + showDetails: false, + showHeading: true, + }), + }), + }); }; export { CalendarWidget }; diff --git a/modules/menus/calendar/index.ts b/modules/menus/calendar/index.ts index d985554..4ff2525 100644 --- a/modules/menus/calendar/index.ts +++ b/modules/menus/calendar/index.ts @@ -1,29 +1,31 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { TimeWidget } from "./time/index.js"; -import { CalendarWidget } from "./calendar.js"; -import { WeatherWidget } from "./weather/index.js"; +import DropdownMenu from '../shared/dropdown/index.js'; +import { TimeWidget } from './time/index.js'; +import { CalendarWidget } from './calendar.js'; +import { WeatherWidget } from './weather/index.js'; +import Window from 'types/widgets/window.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { - return DropdownMenu({ - name: "calendarmenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "calendar-menu-content", - css: "padding: 1px; margin: -1px;", - vexpand: false, - children: [ - Widget.Box({ - class_name: "calendar-content-container", - vertical: true, - children: [ - Widget.Box({ - class_name: "calendar-content-items", - vertical: true, - children: [TimeWidget(), CalendarWidget(), WeatherWidget()], - }), - ], +export default (): Window => { + return DropdownMenu({ + name: 'calendarmenu', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'calendar-menu-content', + css: 'padding: 1px; margin: -1px;', + vexpand: false, + children: [ + Widget.Box({ + class_name: 'calendar-content-container', + vertical: true, + children: [ + Widget.Box({ + class_name: 'calendar-content-items', + vertical: true, + children: [TimeWidget(), CalendarWidget(), WeatherWidget()], + }), + ], + }), + ], }), - ], - }), - }); + }); }; diff --git a/modules/menus/calendar/time/index.ts b/modules/menus/calendar/time/index.ts index 11a4639..0812a61 100644 --- a/modules/menus/calendar/time/index.ts +++ b/modules/menus/calendar/time/index.ts @@ -1,69 +1,70 @@ -import options from "options"; +import { BoxWidget } from 'lib/types/widget'; +import options from 'options'; const { military } = options.menus.clock.time; -const time = Variable("", { - poll: [1000, 'date "+%I:%M:%S"'], +const time = Variable('', { + poll: [1000, 'date "+%I:%M:%S"'], }); -const period = Variable("", { - poll: [1000, 'date "+%p"'], +const period = Variable('', { + poll: [1000, 'date "+%p"'], }); -const militaryTime = Variable("", { - poll: [1000, 'date "+%H:%M:%S"'], +const militaryTime = Variable('', { + poll: [1000, 'date "+%H:%M:%S"'], }); -const TimeWidget = () => { - return Widget.Box({ - class_name: "calendar-menu-item-container clock", - hexpand: true, - vpack: "center", - hpack: "fill", - child: Widget.Box({ - hexpand: true, - vpack: "center", - hpack: "center", - class_name: "clock-content-items", - children: military.bind("value").as((is24hr) => { - if (!is24hr) { - return [ - Widget.Box({ - hpack: "center", - children: [ - Widget.Label({ - class_name: "clock-content-time", - label: time.bind(), - }), - ], +const TimeWidget = (): BoxWidget => { + return Widget.Box({ + class_name: 'calendar-menu-item-container clock', + hexpand: true, + vpack: 'center', + hpack: 'fill', + child: Widget.Box({ + hexpand: true, + vpack: 'center', + hpack: 'center', + class_name: 'clock-content-items', + children: military.bind('value').as((is24hr) => { + if (!is24hr) { + return [ + Widget.Box({ + hpack: 'center', + children: [ + Widget.Label({ + class_name: 'clock-content-time', + label: time.bind(), + }), + ], + }), + Widget.Box({ + hpack: 'center', + children: [ + Widget.Label({ + vpack: 'end', + class_name: 'clock-content-period', + label: period.bind(), + }), + ], + }), + ]; + } + + return [ + Widget.Box({ + hpack: 'center', + children: [ + Widget.Label({ + class_name: 'clock-content-time', + label: militaryTime.bind(), + }), + ], + }), + ]; }), - Widget.Box({ - hpack: "center", - children: [ - Widget.Label({ - vpack: "end", - class_name: "clock-content-period", - label: period.bind(), - }), - ], - }), - ]; - } - - return [ - Widget.Box({ - hpack: "center", - children: [ - Widget.Label({ - class_name: "clock-content-time", - label: militaryTime.bind(), - }), - ], - }), - ]; - }), - }), - }); + }), + }); }; export { TimeWidget }; diff --git a/modules/menus/calendar/weather/hourly/icon/index.ts b/modules/menus/calendar/weather/hourly/icon/index.ts index 0f9517b..2c636c1 100644 --- a/modules/menus/calendar/weather/hourly/icon/index.ts +++ b/modules/menus/calendar/weather/hourly/icon/index.ts @@ -1,32 +1,43 @@ -import { Weather } from "lib/types/weather.js"; -import { Variable } from "types/variable.js"; -import icons from "../../../../../icons/index.js"; +import { Weather, WeatherIconTitle } from 'lib/types/weather.js'; +import { Variable } from 'types/variable.js'; +import { weatherIcons } from 'modules/icons/weather.js'; +import { isValidWeatherIconTitle } from 'globals/weather'; +import { BoxWidget } from 'lib/types/widget'; +import { getNextEpoch } from '../utils'; -export const HourlyIcon = (theWeather: Variable, getNextEpoch: any) => { - return Widget.Icon({ - class_name: "hourly-weather-icon", - icon: theWeather.bind("value").as((w) => { - if (!Object.keys(w).length) { - return "-"; - } +export const HourlyIcon = (theWeather: Variable, hoursFromNow: number): BoxWidget => { + const getIconQuery = (wthr: Weather): WeatherIconTitle => { + const nextEpoch = getNextEpoch(wthr, hoursFromNow); + const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find((h) => h.time_epoch === nextEpoch); - const nextEpoch = getNextEpoch(w); - const weatherAtEpoch = w.forecast.forecastday[0].hour.find( - (h) => h.time_epoch === nextEpoch, - ); + if (weatherAtEpoch === undefined) { + return 'warning'; + } - let iconQuery = weatherAtEpoch?.condition.text - .trim() - .toLowerCase() - .replaceAll(" ", "_") - || "warning" - ; + let iconQuery = weatherAtEpoch.condition.text.trim().toLowerCase().replaceAll(' ', '_'); - if (!weatherAtEpoch?.is_day && iconQuery === "partly_cloudy") { - iconQuery = "partly_cloudy_night"; - } + if (!weatherAtEpoch?.is_day && iconQuery === 'partly_cloudy') { + iconQuery = 'partly_cloudy_night'; + } - return icons.weather[iconQuery]; + if (isValidWeatherIconTitle(iconQuery)) { + return iconQuery; + } else { + return 'warning'; + } + }; + + return Widget.Box({ + hpack: 'center', + child: theWeather.bind('value').as((w) => { + const iconQuery = getIconQuery(w); + const weatherIcn = weatherIcons[iconQuery] || weatherIcons['warning']; + + return Widget.Label({ + hpack: 'center', + class_name: 'hourly-weather-icon txt-icon', + label: weatherIcn, + }); }), }); }; diff --git a/modules/menus/calendar/weather/hourly/index.ts b/modules/menus/calendar/weather/hourly/index.ts index 046bf92..0eb1982 100644 --- a/modules/menus/calendar/weather/hourly/index.ts +++ b/modules/menus/calendar/weather/hourly/index.ts @@ -1,44 +1,25 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; -import { HourlyIcon } from "./icon/index.js"; -import { HourlyTemp } from "./temperature/index.js"; -import { HourlyTime } from "./time/index.js"; +import { Weather } from 'lib/types/weather'; +import { Variable } from 'types/variable'; +import { HourlyIcon } from './icon/index.js'; +import { HourlyTemp } from './temperature/index.js'; +import { HourlyTime } from './time/index.js'; +import { BoxWidget } from 'lib/types/widget.js'; -export const Hourly = (theWeather: Variable) => { +export const Hourly = (theWeather: Variable): BoxWidget => { return Widget.Box({ vertical: false, hexpand: true, - hpack: "fill", - class_name: "hourly-weather-container", + hpack: 'fill', + class_name: 'hourly-weather-container', children: [1, 2, 3, 4].map((hoursFromNow) => { - const getNextEpoch = (wthr: Weather) => { - const currentEpoch = wthr.location.localtime_epoch; - const epochAtHourStart = currentEpoch - (currentEpoch % 3600); - let nextEpoch = 3600 * hoursFromNow + epochAtHourStart; - - const curHour = new Date(currentEpoch * 1000).getHours(); - - /* - * NOTE: Since the API is only capable of showing the current day; if - * the hours left in the day are less than 4 (aka spilling into the next day), - * then rewind to contain the prediction within the current day. - */ - if (curHour > 19) { - const hoursToRewind = curHour - 19; - nextEpoch = - 3600 * hoursFromNow + epochAtHourStart - hoursToRewind * 3600; - } - return nextEpoch; - }; - return Widget.Box({ - class_name: "hourly-weather-item", + class_name: 'hourly-weather-item', hexpand: true, vertical: true, children: [ - HourlyTime(theWeather, getNextEpoch), - HourlyIcon(theWeather, getNextEpoch), - HourlyTemp(theWeather, getNextEpoch), + HourlyTime(theWeather, hoursFromNow), + HourlyIcon(theWeather, hoursFromNow), + HourlyTemp(theWeather, hoursFromNow), ], }); }), diff --git a/modules/menus/calendar/weather/hourly/temperature/index.ts b/modules/menus/calendar/weather/hourly/temperature/index.ts index d649358..440c994 100644 --- a/modules/menus/calendar/weather/hourly/temperature/index.ts +++ b/modules/menus/calendar/weather/hourly/temperature/index.ts @@ -1,29 +1,27 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; -import options from "options"; +import { Weather } from 'lib/types/weather'; +import { Variable } from 'types/variable'; +import options from 'options'; +import Label from 'types/widgets/label'; +import { Child } from 'lib/types/widget'; +import { getNextEpoch } from '../utils'; const { unit } = options.menus.clock.weather; -export const HourlyTemp = (theWeather: Variable, getNextEpoch: any) => { +export const HourlyTemp = (theWeather: Variable, hoursFromNow: number): Label => { return Widget.Label({ - class_name: "hourly-weather-temp", - label: Utils.merge( - [theWeather.bind("value"), unit.bind("value")], - (wthr, unt) => { - if (!Object.keys(wthr).length) { - return "-"; - } + class_name: 'hourly-weather-temp', + label: Utils.merge([theWeather.bind('value'), unit.bind('value')], (wthr, unt) => { + if (!Object.keys(wthr).length) { + return '-'; + } - const nextEpoch = getNextEpoch(wthr); - const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find( - (h) => h.time_epoch === nextEpoch, - ); + const nextEpoch = getNextEpoch(wthr, hoursFromNow); + const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find((h) => h.time_epoch === nextEpoch); - if (unt === "imperial") { - return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : "-"}° F`; - } - return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_c) : "-"}° C`; - }, - ), + if (unt === 'imperial') { + return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : '-'}° F`; + } + return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_c) : '-'}° C`; + }), }); }; diff --git a/modules/menus/calendar/weather/hourly/time/index.ts b/modules/menus/calendar/weather/hourly/time/index.ts index 48e2ebe..717eac4 100644 --- a/modules/menus/calendar/weather/hourly/time/index.ts +++ b/modules/menus/calendar/weather/hourly/time/index.ts @@ -1,18 +1,21 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; +import { Weather } from 'lib/types/weather'; +import { Child } from 'lib/types/widget'; +import { Variable } from 'types/variable'; +import Label from 'types/widgets/label'; +import { getNextEpoch } from '../utils'; -export const HourlyTime = (theWeather: Variable, getNextEpoch: any) => { +export const HourlyTime = (theWeather: Variable, hoursFromNow: number): Label => { return Widget.Label({ - class_name: "hourly-weather-time", - label: theWeather.bind("value").as((w) => { + class_name: 'hourly-weather-time', + label: theWeather.bind('value').as((w) => { if (!Object.keys(w).length) { - return "-"; + return '-'; } - const nextEpoch = getNextEpoch(w); + const nextEpoch = getNextEpoch(w, hoursFromNow); const dateAtEpoch = new Date(nextEpoch * 1000); let hours = dateAtEpoch.getHours(); - const ampm = hours >= 12 ? "PM" : "AM"; + const ampm = hours >= 12 ? 'PM' : 'AM'; hours = hours % 12 || 12; return `${hours}${ampm}`; diff --git a/modules/menus/calendar/weather/hourly/utils.ts b/modules/menus/calendar/weather/hourly/utils.ts new file mode 100644 index 0000000..13f1d53 --- /dev/null +++ b/modules/menus/calendar/weather/hourly/utils.ts @@ -0,0 +1,20 @@ +import { Weather } from 'lib/types/weather'; + +export const getNextEpoch = (wthr: Weather, hoursFromNow: number): number => { + const currentEpoch = wthr.location.localtime_epoch; + const epochAtHourStart = currentEpoch - (currentEpoch % 3600); + let nextEpoch = 3600 * hoursFromNow + epochAtHourStart; + + const curHour = new Date(currentEpoch * 1000).getHours(); + + /* + * NOTE: Since the API is only capable of showing the current day; if + * the hours left in the day are less than 4 (aka spilling into the next day), + * then rewind to contain the prediction within the current day. + */ + if (curHour > 19) { + const hoursToRewind = curHour - 19; + nextEpoch = 3600 * hoursFromNow + epochAtHourStart - hoursToRewind * 3600; + } + return nextEpoch; +}; diff --git a/modules/menus/calendar/weather/icon/index.ts b/modules/menus/calendar/weather/icon/index.ts index ee74236..dff981a 100644 --- a/modules/menus/calendar/weather/icon/index.ts +++ b/modules/menus/calendar/weather/icon/index.ts @@ -1,19 +1,18 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; -import { getWeatherStatusIcon } from "globals/weather.js"; +import { Weather } from 'lib/types/weather'; +import { Variable } from 'types/variable'; +import { getWeatherStatusTextIcon } from 'globals/weather.js'; +import { BoxWidget } from 'lib/types/widget'; -export const TodayIcon = (theWeather: Variable) => { +export const TodayIcon = (theWeather: Variable): BoxWidget => { return Widget.Box({ - vpack: "center", - hpack: "start", - class_name: "calendar-menu-weather today icon container", - children: [ - Widget.Icon({ - class_name: "calendar-menu-weather today icon", - icon: theWeather.bind("value").as((v) => { - return getWeatherStatusIcon(v); - }), + vpack: 'center', + hpack: 'start', + class_name: 'calendar-menu-weather today icon container', + child: Widget.Label({ + class_name: 'calendar-menu-weather today icon txt-icon', + label: theWeather.bind('value').as((w) => { + return getWeatherStatusTextIcon(w); }), - ], + }), }); }; diff --git a/modules/menus/calendar/weather/index.ts b/modules/menus/calendar/weather/index.ts index 58fc2ea..bf4a6bf 100644 --- a/modules/menus/calendar/weather/index.ts +++ b/modules/menus/calendar/weather/index.ts @@ -1,21 +1,22 @@ -import { TodayIcon } from "./icon/index.js"; -import { TodayStats } from "./stats/index.js"; -import { TodayTemperature } from "./temperature/index.js"; -import { Hourly } from "./hourly/index.js"; -import { globalWeatherVar } from "globals/weather.js"; +import { TodayIcon } from './icon/index.js'; +import { TodayStats } from './stats/index.js'; +import { TodayTemperature } from './temperature/index.js'; +import { Hourly } from './hourly/index.js'; +import { globalWeatherVar } from 'globals/weather.js'; +import { BoxWidget } from 'lib/types/widget.js'; -const WeatherWidget = () => { +const WeatherWidget = (): BoxWidget => { return Widget.Box({ - class_name: "calendar-menu-item-container weather", + class_name: 'calendar-menu-item-container weather', child: Widget.Box({ - class_name: "weather-container-box", + class_name: 'weather-container-box', setup: (self) => { return (self.child = Widget.Box({ vertical: true, hexpand: true, children: [ Widget.Box({ - class_name: "calendar-menu-weather today", + class_name: 'calendar-menu-weather today', hexpand: true, children: [ TodayIcon(globalWeatherVar), @@ -24,7 +25,7 @@ const WeatherWidget = () => { ], }), Widget.Separator({ - class_name: "menu-separator weather", + class_name: 'menu-separator weather', }), Hourly(globalWeatherVar), ], diff --git a/modules/menus/calendar/weather/stats/index.ts b/modules/menus/calendar/weather/stats/index.ts index d0e8d40..197032f 100644 --- a/modules/menus/calendar/weather/stats/index.ts +++ b/modules/menus/calendar/weather/stats/index.ts @@ -1,29 +1,30 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; -import options from "options"; -import { Unit } from "lib/types/options"; -import { getRainChance, getWindConditions } from "globals/weather"; +import { Weather } from 'lib/types/weather'; +import { Variable } from 'types/variable'; +import options from 'options'; +import { Unit } from 'lib/types/options'; +import { getRainChance, getWindConditions } from 'globals/weather'; +import { BoxWidget } from 'lib/types/widget'; const { unit } = options.menus.clock.weather; -export const TodayStats = (theWeather: Variable) => { +export const TodayStats = (theWeather: Variable): BoxWidget => { return Widget.Box({ - class_name: "calendar-menu-weather today stats container", - hpack: "end", - vpack: "center", + class_name: 'calendar-menu-weather today stats container', + hpack: 'end', + vpack: 'center', vertical: true, children: [ Widget.Box({ - class_name: "weather wind", + class_name: 'weather wind', children: [ Widget.Label({ - class_name: "weather wind icon txt-icon", - label: "", + class_name: 'weather wind icon txt-icon', + label: '', }), Widget.Label({ - class_name: "weather wind label", + class_name: 'weather wind label', label: Utils.merge( - [theWeather.bind("value"), unit.bind("value")], + [theWeather.bind('value'), unit.bind('value')], (wthr: Weather, unt: Unit) => { return getWindConditions(wthr, unt); }, @@ -32,19 +33,15 @@ export const TodayStats = (theWeather: Variable) => { ], }), Widget.Box({ - class_name: "weather precip", + class_name: 'weather precip', children: [ Widget.Label({ - class_name: "weather precip icon txt-icon", - label: "", + class_name: 'weather precip icon txt-icon', + label: '', }), Widget.Label({ - class_name: "weather precip label", - label: theWeather - .bind("value") - .as( - (v) => getRainChance(v), - ), + class_name: 'weather precip label', + label: theWeather.bind('value').as((v) => getRainChance(v)), }), ], }), diff --git a/modules/menus/calendar/weather/temperature/index.ts b/modules/menus/calendar/weather/temperature/index.ts index 366b983..597a75c 100644 --- a/modules/menus/calendar/weather/temperature/index.ts +++ b/modules/menus/calendar/weather/temperature/index.ts @@ -1,43 +1,41 @@ -import { Weather } from "lib/types/weather"; -import { Variable } from "types/variable"; -import options from "options"; -import { getTemperature, getWeatherIcon } from "globals/weather"; +import { Weather } from 'lib/types/weather'; +import { Variable } from 'types/variable'; +import options from 'options'; +import { getTemperature, getWeatherIcon } from 'globals/weather'; +import { BoxWidget } from 'lib/types/widget'; const { unit } = options.menus.clock.weather; -export const TodayTemperature = (theWeather: Variable) => { +export const TodayTemperature = (theWeather: Variable): BoxWidget => { return Widget.Box({ - hpack: "center", - vpack: "center", + hpack: 'center', + vpack: 'center', vertical: true, children: [ Widget.Box({ hexpand: true, - vpack: "center", - class_name: "calendar-menu-weather today temp container", + vpack: 'center', + class_name: 'calendar-menu-weather today temp container', vertical: false, children: [ Widget.Box({ hexpand: true, - hpack: "center", + hpack: 'center', children: [ Widget.Label({ - class_name: "calendar-menu-weather today temp label", - label: Utils.merge( - [theWeather.bind("value"), unit.bind("value")], - (wthr, unt) => { - return getTemperature(wthr, unt); - }, - ), + class_name: 'calendar-menu-weather today temp label', + label: Utils.merge([theWeather.bind('value'), unit.bind('value')], (wthr, unt) => { + return getTemperature(wthr, unt); + }), }), Widget.Label({ class_name: theWeather - .bind("value") + .bind('value') .as( (v) => `calendar-menu-weather today temp label icon txt-icon ${getWeatherIcon(Math.ceil(v.current.temp_f)).color}`, ), label: theWeather - .bind("value") + .bind('value') .as((v) => getWeatherIcon(Math.ceil(v.current.temp_f)).icon), }), ], @@ -45,18 +43,18 @@ export const TodayTemperature = (theWeather: Variable) => { ], }), Widget.Box({ - hpack: "center", + hpack: 'center', child: Widget.Label({ max_width_chars: 17, - truncate: "end", + truncate: 'end', lines: 2, class_name: theWeather - .bind("value") + .bind('value') .as( (v) => `calendar-menu-weather today condition label ${getWeatherIcon(Math.ceil(v.current.temp_f)).color}`, ), - label: theWeather.bind("value").as((v) => v.current.condition.text), + label: theWeather.bind('value').as((v) => v.current.condition.text), }), }), ], diff --git a/modules/menus/dashboard/controls/index.ts b/modules/menus/dashboard/controls/index.ts index 62684f7..44d4fa7 100644 --- a/modules/menus/dashboard/controls/index.ts +++ b/modules/menus/dashboard/controls/index.ts @@ -1,95 +1,91 @@ -const network = await Service.import("network"); -const bluetooth = await Service.import("bluetooth"); -const notifications = await Service.import("notifications"); -const audio = await Service.import("audio"); +import { BoxWidget } from 'lib/types/widget'; -const Controls = () => { +const network = await Service.import('network'); +const bluetooth = await Service.import('bluetooth'); +const notifications = await Service.import('notifications'); +const audio = await Service.import('audio'); + +const Controls = (): BoxWidget => { return Widget.Box({ - class_name: "dashboard-card controls-container", - hpack: "fill", - vpack: "fill", + class_name: 'dashboard-card controls-container', + hpack: 'fill', + vpack: 'fill', expand: true, children: [ Widget.Button({ - tooltip_text: "Toggle Wifi", + tooltip_text: 'Toggle Wifi', expand: true, setup: (self) => { self.hook(network, () => { - return (self.class_name = `dashboard-button wifi ${!network.wifi.enabled ? "disabled" : ""}`); + return (self.class_name = `dashboard-button wifi ${!network.wifi.enabled ? 'disabled' : ''}`); }); }, on_primary_click: () => network.toggleWifi(), child: Widget.Label({ - class_name: "txt-icon", + class_name: 'txt-icon', setup: (self) => { self.hook(network, () => { - return (self.label = network.wifi.enabled ? "󰤨" : "󰤭"); + return (self.label = network.wifi.enabled ? '󰤨' : '󰤭'); }); }, }), }), Widget.Button({ - tooltip_text: "Toggle Bluetooth", + tooltip_text: 'Toggle Bluetooth', expand: true, class_name: bluetooth - .bind("enabled") - .as( - (btOn) => `dashboard-button bluetooth ${!btOn ? "disabled" : ""}`, - ), + .bind('enabled') + .as((btOn) => `dashboard-button bluetooth ${!btOn ? 'disabled' : ''}`), on_primary_click: () => bluetooth.toggle(), child: Widget.Label({ - class_name: "txt-icon", - label: bluetooth.bind("enabled").as((btOn) => (btOn ? "󰂯" : "󰂲")), + class_name: 'txt-icon', + label: bluetooth.bind('enabled').as((btOn) => (btOn ? '󰂯' : '󰂲')), }), }), Widget.Button({ - tooltip_text: "Toggle Notifications", + tooltip_text: 'Toggle Notifications', expand: true, class_name: notifications - .bind("dnd") - .as( - (dnd) => `dashboard-button notifications ${dnd ? "disabled" : ""}`, - ), + .bind('dnd') + .as((dnd) => `dashboard-button notifications ${dnd ? 'disabled' : ''}`), on_primary_click: () => (notifications.dnd = !notifications.dnd), child: Widget.Label({ - class_name: "txt-icon", - label: notifications.bind("dnd").as((dnd) => (dnd ? "󰂛" : "󰂚")), + class_name: 'txt-icon', + label: notifications.bind('dnd').as((dnd) => (dnd ? '󰂛' : '󰂚')), }), }), Widget.Button({ - tooltip_text: "Toggle Mute (Playback)", + tooltip_text: 'Toggle Mute (Playback)', expand: true, - on_primary_click: () => - (audio.speaker.is_muted = !audio.speaker.is_muted), + on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted), setup: (self) => { self.hook(audio, () => { - return (self.class_name = `dashboard-button playback ${audio.speaker.is_muted ? "disabled" : ""}`); + return (self.class_name = `dashboard-button playback ${audio.speaker.is_muted ? 'disabled' : ''}`); }); }, child: Widget.Label({ - class_name: "txt-icon", + class_name: 'txt-icon', setup: (self) => { self.hook(audio, () => { - return (self.label = audio.speaker.is_muted ? "󰖁" : "󰕾"); + return (self.label = audio.speaker.is_muted ? '󰖁' : '󰕾'); }); }, }), }), Widget.Button({ - tooltip_text: "Toggle Mute (Microphone)", + tooltip_text: 'Toggle Mute (Microphone)', expand: true, - on_primary_click: () => - (audio.microphone.is_muted = !audio.microphone.is_muted), + on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted), setup: (self) => { self.hook(audio, () => { - return (self.class_name = `dashboard-button input ${audio.microphone.is_muted ? "disabled" : ""}`); + return (self.class_name = `dashboard-button input ${audio.microphone.is_muted ? 'disabled' : ''}`); }); }, child: Widget.Label({ - class_name: "txt-icon", + class_name: 'txt-icon', setup: (self) => { self.hook(audio, () => { - return (self.label = audio.microphone.is_muted ? "󰍭" : "󰍬"); + return (self.label = audio.microphone.is_muted ? '󰍭' : '󰍬'); }); }, }), diff --git a/modules/menus/dashboard/directories/index.ts b/modules/menus/dashboard/directories/index.ts index 6841858..5b6aa11 100644 --- a/modules/menus/dashboard/directories/index.ts +++ b/modules/menus/dashboard/directories/index.ts @@ -1,68 +1,63 @@ -import options from "options"; +import { BoxWidget } from 'lib/types/widget'; +import options from 'options'; const { left, right } = options.menus.dashboard.directories; -const Directories = () => { +const Directories = (): BoxWidget => { return Widget.Box({ - class_name: "dashboard-card directories-container", - vpack: "fill", - hpack: "fill", + class_name: 'dashboard-card directories-container', + vpack: 'fill', + hpack: 'fill', expand: true, children: [ Widget.Box({ vertical: true, expand: true, - class_name: "section right", + class_name: 'section right', children: [ Widget.Button({ - hpack: "start", + hpack: 'start', expand: true, - class_name: "directory-link left top", - on_primary_click: left.directory1.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + class_name: 'directory-link left top', + on_primary_click: left.directory1.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: left.directory1.label.bind("value"), + hpack: 'start', + label: left.directory1.label.bind('value'), }), }), Widget.Button({ expand: true, - hpack: "start", - class_name: "directory-link left middle", - on_primary_click: left.directory2.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + hpack: 'start', + class_name: 'directory-link left middle', + on_primary_click: left.directory2.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: left.directory2.label.bind("value"), + hpack: 'start', + label: left.directory2.label.bind('value'), }), }), Widget.Button({ expand: true, - hpack: "start", - class_name: "directory-link left bottom", - on_primary_click: left.directory3.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + hpack: 'start', + class_name: 'directory-link left bottom', + on_primary_click: left.directory3.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: left.directory3.label.bind("value"), + hpack: 'start', + label: left.directory3.label.bind('value'), }), }), ], @@ -70,57 +65,51 @@ const Directories = () => { Widget.Box({ vertical: true, expand: true, - class_name: "section left", + class_name: 'section left', children: [ Widget.Button({ - hpack: "start", + hpack: 'start', expand: true, - class_name: "directory-link right top", - on_primary_click: right.directory1.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + class_name: 'directory-link right top', + on_primary_click: right.directory1.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: right.directory1.label.bind("value"), + hpack: 'start', + label: right.directory1.label.bind('value'), }), }), Widget.Button({ expand: true, - hpack: "start", - class_name: "directory-link right middle", - on_primary_click: right.directory2.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + hpack: 'start', + class_name: 'directory-link right middle', + on_primary_click: right.directory2.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: right.directory2.label.bind("value"), + hpack: 'start', + label: right.directory2.label.bind('value'), }), }), Widget.Button({ expand: true, - hpack: "start", - class_name: "directory-link right bottom", - on_primary_click: right.directory3.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), + hpack: 'start', + class_name: 'directory-link right bottom', + on_primary_click: right.directory3.command.bind('value').as((cmd) => { + return () => { + App.closeWindow('dashboardmenu'); + Utils.execAsync(cmd); + }; + }), child: Widget.Label({ - hpack: "start", - label: right.directory3.label.bind("value"), + hpack: 'start', + label: right.directory3.label.bind('value'), }), }), ], diff --git a/modules/menus/dashboard/index.ts b/modules/menus/dashboard/index.ts index 538b7a7..ea37628 100644 --- a/modules/menus/dashboard/index.ts +++ b/modules/menus/dashboard/index.ts @@ -1,37 +1,33 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { Profile } from "./profile/index.js"; -import { Shortcuts } from "./shortcuts/index.js"; -import { Controls } from "./controls/index.js"; -import { Stats } from "./stats/index.js"; -import { Directories } from "./directories/index.js"; +import DropdownMenu from '../shared/dropdown/index.js'; +import { Profile } from './profile/index.js'; +import { Shortcuts } from './shortcuts/index.js'; +import { Controls } from './controls/index.js'; +import { Stats } from './stats/index.js'; +import { Directories } from './directories/index.js'; +import Window from 'types/widgets/window.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { - return DropdownMenu({ - name: "dashboardmenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "dashboard-menu-content", - css: "padding: 1px; margin: -1px;", - vexpand: false, - children: [ - Widget.Box({ - class_name: "dashboard-content-container", - vertical: true, - children: [ - Widget.Box({ - class_name: "dashboard-content-items", - vertical: true, - children: [ - Profile(), - Shortcuts(), - Controls(), - Directories(), - Stats(), - ], - }), - ], +export default (): Window => { + return DropdownMenu({ + name: 'dashboardmenu', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'dashboard-menu-content', + css: 'padding: 1px; margin: -1px;', + vexpand: false, + children: [ + Widget.Box({ + class_name: 'dashboard-content-container', + vertical: true, + children: [ + Widget.Box({ + class_name: 'dashboard-content-items', + vertical: true, + children: [Profile(), Shortcuts(), Controls(), Directories(), Stats()], + }), + ], + }), + ], }), - ], - }), - }); + }); }; diff --git a/modules/menus/dashboard/profile/index.ts b/modules/menus/dashboard/profile/index.ts index d2d1673..9d2531a 100644 --- a/modules/menus/dashboard/profile/index.ts +++ b/modules/menus/dashboard/profile/index.ts @@ -1,58 +1,67 @@ -import icons from "../../../icons/index.js"; -import powermenu from "../../power/helpers/actions.js"; -import { PowerOptions } from "lib/types/options.js"; -import GdkPixbuf from "gi://GdkPixbuf"; +import powermenu from '../../power/helpers/actions.js'; +import { PowerOptions } from 'lib/types/options.js'; +import GdkPixbuf from 'gi://GdkPixbuf'; -import options from "options"; +import options from 'options'; +import { BoxWidget, Child } from 'lib/types/widget.js'; +import Label from 'types/widgets/label.js'; const { image, name } = options.menus.dashboard.powermenu.avatar; const { confirmation, shutdown, logout, sleep, reboot } = options.menus.dashboard.powermenu; -const Profile = () => { - const handleClick = (action: PowerOptions) => { +const Profile = (): BoxWidget => { + const handleClick = (action: PowerOptions): void => { const actions = { shutdown: shutdown.value, reboot: reboot.value, logout: logout.value, sleep: sleep.value, }; - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); if (!confirmation.value) { - Utils.execAsync(actions[action]) - .catch((err) => console.error(`Failed to execute ${action} command. Error: ${err}`)); + Utils.execAsync(actions[action]).catch((err) => + console.error(`Failed to execute ${action} command. Error: ${err}`), + ); } else { powermenu.action(action); } }; + const getIconForButton = (txtIcon: string): Label => { + return Widget.Label({ + className: 'txt-icon', + label: txtIcon, + }); + }; + return Widget.Box({ - class_name: "profiles-container", - hpack: "fill", + class_name: 'profiles-container', + hpack: 'fill', hexpand: true, children: [ Widget.Box({ - class_name: "profile-picture-container dashboard-card", + class_name: 'profile-picture-container dashboard-card', hexpand: true, vertical: true, children: [ Widget.Box({ - hpack: "center", - class_name: "profile-picture", - css: image.bind("value").as(i => { + hpack: 'center', + class_name: 'profile-picture', + css: image.bind('value').as((i) => { try { GdkPixbuf.Pixbuf.new_from_file(i); - return `background-image: url("${i}")` + return `background-image: url("${i}")`; } catch { - return `background-image: url("${App.configDir}/assets/hyprpanel.png")` + return `background-image: url("${App.configDir}/assets/hyprpanel.png")`; } }), }), Widget.Label({ - hpack: "center", - class_name: "profile-name", - label: name.bind("value").as((v) => { - if (v === "system") { - return Utils.exec("bash -c whoami"); + hpack: 'center', + class_name: 'profile-name', + label: name.bind('value').as((v) => { + if (v === 'system') { + return Utils.exec('bash -c whoami'); } return v; }), @@ -60,37 +69,37 @@ const Profile = () => { ], }), Widget.Box({ - class_name: "power-menu-container dashboard-card", + class_name: 'power-menu-container dashboard-card', vertical: true, vexpand: true, children: [ Widget.Button({ - class_name: "dashboard-button shutdown", - on_clicked: () => handleClick("shutdown"), - tooltip_text: "Shut Down", + class_name: 'dashboard-button shutdown', + on_clicked: () => handleClick('shutdown'), + tooltip_text: 'Shut Down', vexpand: true, - child: Widget.Icon(icons.powermenu.shutdown), + child: getIconForButton('󰐥'), }), Widget.Button({ - class_name: "dashboard-button restart", - on_clicked: () => handleClick("reboot"), - tooltip_text: "Restart", + class_name: 'dashboard-button restart', + on_clicked: () => handleClick('reboot'), + tooltip_text: 'Restart', vexpand: true, - child: Widget.Icon(icons.powermenu.reboot), + child: getIconForButton('󰜉'), }), Widget.Button({ - class_name: "dashboard-button lock", - on_clicked: () => handleClick("logout"), - tooltip_text: "Log Out", + class_name: 'dashboard-button lock', + on_clicked: () => handleClick('logout'), + tooltip_text: 'Log Out', vexpand: true, - child: Widget.Icon(icons.powermenu.logout), + child: getIconForButton('󰿅'), }), Widget.Button({ - class_name: "dashboard-button sleep", - on_clicked: () => handleClick("sleep"), - tooltip_text: "Sleep", + class_name: 'dashboard-button sleep', + on_clicked: () => handleClick('sleep'), + tooltip_text: 'Sleep', vexpand: true, - child: Widget.Icon(icons.powermenu.sleep), + child: getIconForButton('󰤄'), }), ], }), diff --git a/modules/menus/dashboard/shortcuts/index.ts b/modules/menus/dashboard/shortcuts/index.ts index 3db359a..ce00aae 100644 --- a/modules/menus/dashboard/shortcuts/index.ts +++ b/modules/menus/dashboard/shortcuts/index.ts @@ -1,24 +1,28 @@ -const hyprland = await Service.import("hyprland"); -import options from "options"; -import { Variable as VarType } from "types/variable"; +const hyprland = await Service.import('hyprland'); +import { Attribute, BoxWidget, Child } from 'lib/types/widget'; +import options from 'options'; +import { Variable as VarType } from 'types/variable'; +import Box from 'types/widgets/box'; +import Button from 'types/widgets/button'; +import Label from 'types/widgets/label'; const { left, right } = options.menus.dashboard.shortcuts; -const Shortcuts = () => { +const Shortcuts = (): BoxWidget => { const isRecording = Variable(false, { poll: [ 1000, `${App.configDir}/services/screen_record.sh status`, - (out) => { - if (out === "recording") { + (out): boolean => { + if (out === 'recording') { return true; } return false; }, ], }); - const handleClick = (action: any, tOut: number = 250) => { - App.closeWindow("dashboardmenu"); + const handleClick = (action: string, tOut: number = 250): void => { + App.closeWindow('dashboardmenu'); setTimeout(() => { Utils.execAsync(action) @@ -30,8 +34,8 @@ const Shortcuts = () => { }; const recordingDropdown = Widget.Menu({ - class_name: "dropdown recording", - hpack: "fill", + class_name: 'dropdown recording', + hpack: 'fill', hexpand: true, setup: (self) => { self.hook(hyprland, () => { @@ -39,26 +43,26 @@ const Shortcuts = () => { return Widget.MenuItem({ label: `Display ${mon.name}`, on_activate: () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync( - `${App.configDir}/services/screen_record.sh start ${mon.name}`, - ).catch((err) => console.error(err)); + App.closeWindow('dashboardmenu'); + Utils.execAsync(`${App.configDir}/services/screen_record.sh start ${mon.name}`).catch( + (err) => console.error(err), + ); }, }); }); // NOTE: This is disabled since window recording isn't available on wayland - const apps = hyprland.clients.map((clt) => { - return Widget.MenuItem({ - label: `${clt.class.charAt(0).toUpperCase() + clt.class.slice(1)} (Workspace ${clt.workspace.name})`, - on_activate: () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync( - `${App.configDir}/services/screen_record.sh start ${clt.focusHistoryID}`, - ).catch((err) => console.error(err)); - }, - }); - }); + // const apps = hyprland.clients.map((clt) => { + // return Widget.MenuItem({ + // label: `${clt.class.charAt(0).toUpperCase() + clt.class.slice(1)} (Workspace ${clt.workspace.name})`, + // on_activate: () => { + // App.closeWindow('dashboardmenu'); + // Utils.execAsync( + // `${App.configDir}/services/screen_record.sh start ${clt.focusHistoryID}`, + // ).catch((err) => console.error(err)); + // }, + // }); + // }); return (self.children = [ ...displays, @@ -85,15 +89,15 @@ const Shortcuts = () => { type Shortcut = ShortcutFixed | ShortcutVariable; - const cmdLn = (sCut: ShortcutVariable) => { - return sCut.command.value.length > 0 + const cmdLn = (sCut: ShortcutVariable): boolean => { + return sCut.command.value.length > 0; }; const leftCardHidden = Variable( - !(cmdLn(left.shortcut1) || cmdLn(left.shortcut2) || cmdLn(left.shortcut3) || cmdLn(left.shortcut4)) + !(cmdLn(left.shortcut1) || cmdLn(left.shortcut2) || cmdLn(left.shortcut3) || cmdLn(left.shortcut4)), ); - function createButton(shortcut: Shortcut, className: string) { + const createButton = (shortcut: Shortcut, className: string): Button, Attribute> => { if (shortcut.configurable !== false) { return Widget.Button({ vexpand: true, @@ -101,7 +105,7 @@ const Shortcuts = () => { class_name: className, on_primary_click: () => handleClick(shortcut.command.value), child: Widget.Label({ - class_name: "button-label txt-icon", + class_name: 'button-label txt-icon', label: shortcut.icon.value, }), }); @@ -112,166 +116,197 @@ const Shortcuts = () => { tooltip_text: shortcut.tooltip, class_name: className, on_primary_click: (_, event) => { - if (shortcut.command === "settings-dialog") { - App.closeWindow("dashboardmenu"); - App.toggleWindow("settings-dialog"); - } else if (shortcut.command === "record") { + if (shortcut.command === 'settings-dialog') { + App.closeWindow('dashboardmenu'); + App.toggleWindow('settings-dialog'); + } else if (shortcut.command === 'record') { if (isRecording.value === true) { - App.closeWindow("dashboardmenu"); - return Utils.execAsync( - `${App.configDir}/services/screen_record.sh stop`, - ).catch((err) => console.error(err)); + App.closeWindow('dashboardmenu'); + return Utils.execAsync(`${App.configDir}/services/screen_record.sh stop`).catch((err) => + console.error(err), + ); } else { recordingDropdown.popup_at_pointer(event); } } }, child: Widget.Label({ - class_name: "button-label txt-icon", + class_name: 'button-label txt-icon', label: shortcut.icon, }), }); } - } + }; - function createButtonIfCommandExists(shortcut: Shortcut, className: string, command: string) { + const createButtonIfCommandExists = ( + shortcut: Shortcut, + className: string, + command: string, + ): Button, Attribute> | Box => { if (command.length > 0) { return createButton(shortcut, className); } return Widget.Box(); - } + }; return Widget.Box({ - class_name: "shortcuts-container", - hpack: "fill", + class_name: 'shortcuts-container', + hpack: 'fill', hexpand: true, children: [ Widget.Box({ - child: Utils.merge([ - left.shortcut1.command.bind("value"), - left.shortcut2.command.bind("value"), - left.shortcut1.tooltip.bind("value"), - left.shortcut2.tooltip.bind("value"), - left.shortcut1.icon.bind("value"), - left.shortcut2.icon.bind("value"), - left.shortcut3.command.bind("value"), - left.shortcut4.command.bind("value"), - left.shortcut3.tooltip.bind("value"), - left.shortcut4.tooltip.bind("value"), - left.shortcut3.icon.bind("value"), - left.shortcut4.icon.bind("value") - ], () => { - const isVisibleLeft = cmdLn(left.shortcut1) || cmdLn(left.shortcut2); - const isVisibleRight = cmdLn(left.shortcut3) || cmdLn(left.shortcut4); + child: Utils.merge( + [ + left.shortcut1.command.bind('value'), + left.shortcut2.command.bind('value'), + left.shortcut1.tooltip.bind('value'), + left.shortcut2.tooltip.bind('value'), + left.shortcut1.icon.bind('value'), + left.shortcut2.icon.bind('value'), + left.shortcut3.command.bind('value'), + left.shortcut4.command.bind('value'), + left.shortcut3.tooltip.bind('value'), + left.shortcut4.tooltip.bind('value'), + left.shortcut3.icon.bind('value'), + left.shortcut4.icon.bind('value'), + ], + () => { + const isVisibleLeft = cmdLn(left.shortcut1) || cmdLn(left.shortcut2); + const isVisibleRight = cmdLn(left.shortcut3) || cmdLn(left.shortcut4); - if (!isVisibleLeft && !isVisibleRight) { - leftCardHidden.value = true; - return Widget.Box(); - } + if (!isVisibleLeft && !isVisibleRight) { + leftCardHidden.value = true; + return Widget.Box(); + } - leftCardHidden.value = false; + leftCardHidden.value = false; - return Widget.Box({ - class_name: "container most-used dashboard-card", - children: [ - Widget.Box({ - className: `card-button-section-container ${isVisibleRight && isVisibleLeft ? "visible" : ""}`, - child: isVisibleLeft ? Widget.Box({ - vertical: true, - hexpand: true, - vexpand: true, - children: [ - createButtonIfCommandExists( - left.shortcut1, - `dashboard-button top-button ${cmdLn(left.shortcut2) ? "paired" : ""}`, - left.shortcut1.command.value), - createButtonIfCommandExists( - left.shortcut2, - "dashboard-button", - left.shortcut2.command.value - ), - ], - }) : Widget.Box({ - children: [], - }) - }), - Widget.Box({ - className: "card-button-section-container", - child: isVisibleRight ? Widget.Box({ - vertical: true, - hexpand: true, - vexpand: true, - children: [ - createButtonIfCommandExists( - left.shortcut3, - `dashboard-button top-button ${cmdLn(left.shortcut4) ? "paired" : ""}`, - left.shortcut3.command.value), - createButtonIfCommandExists( - left.shortcut4, - "dashboard-button", - left.shortcut4.command.value - ), - ], - }) : Widget.Box({ - children: [], + return Widget.Box({ + class_name: 'container most-used dashboard-card', + children: [ + Widget.Box({ + className: `card-button-section-container ${isVisibleRight && isVisibleLeft ? 'visible' : ''}`, + child: isVisibleLeft + ? Widget.Box({ + vertical: true, + hexpand: true, + vexpand: true, + children: [ + createButtonIfCommandExists( + left.shortcut1, + `dashboard-button top-button ${cmdLn(left.shortcut2) ? 'paired' : ''}`, + left.shortcut1.command.value, + ), + createButtonIfCommandExists( + left.shortcut2, + 'dashboard-button', + left.shortcut2.command.value, + ), + ], + }) + : Widget.Box({ + children: [], + }), }), - }), - ] - }); - }) + Widget.Box({ + className: 'card-button-section-container', + child: isVisibleRight + ? Widget.Box({ + vertical: true, + hexpand: true, + vexpand: true, + children: [ + createButtonIfCommandExists( + left.shortcut3, + `dashboard-button top-button ${cmdLn(left.shortcut4) ? 'paired' : ''}`, + left.shortcut3.command.value, + ), + createButtonIfCommandExists( + left.shortcut4, + 'dashboard-button', + left.shortcut4.command.value, + ), + ], + }) + : Widget.Box({ + children: [], + }), + }), + ], + }); + }, + ), }), Widget.Box({ - child: Utils.merge([ - right.shortcut1.command.bind("value"), - right.shortcut1.tooltip.bind("value"), - right.shortcut1.icon.bind("value"), - right.shortcut3.command.bind("value"), - right.shortcut3.tooltip.bind("value"), - right.shortcut3.icon.bind("value"), - leftCardHidden.bind("value"), - isRecording.bind("value") - ], () => { - return Widget.Box({ - class_name: `container utilities dashboard-card ${!leftCardHidden.value ? "paired" : ""}`, - children: [ - Widget.Box({ - className: `card-button-section-container visible`, - child: Widget.Box({ - vertical: true, - hexpand: true, - vexpand: true, - children: [ - createButtonIfCommandExists(right.shortcut1, "dashboard-button top-button paired", right.shortcut1.command.value), - createButtonIfCommandExists( - { - tooltip: "HyprPanel Configuration", - command: "settings-dialog", - icon: "󰒓", - configurable: false - }, "dashboard-button", "settings-dialog"), - ], - }) - }), - Widget.Box({ - className: "card-button-section-container", - child: Widget.Box({ - vertical: true, - hexpand: true, - vexpand: true, - children: [ - createButtonIfCommandExists(right.shortcut3, "dashboard-button top-button paired", right.shortcut3.command.value), - createButtonIfCommandExists({ - tooltip: "Record Screen", - command: "record", - icon: "󰑊", - configurable: false - }, `dashboard-button record ${isRecording.value ? "active" : ""}`, "record"), - ], + child: Utils.merge( + [ + right.shortcut1.command.bind('value'), + right.shortcut1.tooltip.bind('value'), + right.shortcut1.icon.bind('value'), + right.shortcut3.command.bind('value'), + right.shortcut3.tooltip.bind('value'), + right.shortcut3.icon.bind('value'), + leftCardHidden.bind('value'), + isRecording.bind('value'), + ], + () => { + return Widget.Box({ + class_name: `container utilities dashboard-card ${!leftCardHidden.value ? 'paired' : ''}`, + children: [ + Widget.Box({ + className: `card-button-section-container visible`, + child: Widget.Box({ + vertical: true, + hexpand: true, + vexpand: true, + children: [ + createButtonIfCommandExists( + right.shortcut1, + 'dashboard-button top-button paired', + right.shortcut1.command.value, + ), + createButtonIfCommandExists( + { + tooltip: 'HyprPanel Configuration', + command: 'settings-dialog', + icon: '󰒓', + configurable: false, + }, + 'dashboard-button', + 'settings-dialog', + ), + ], + }), }), - }), - ] - }); - }) + Widget.Box({ + className: 'card-button-section-container', + child: Widget.Box({ + vertical: true, + hexpand: true, + vexpand: true, + children: [ + createButtonIfCommandExists( + right.shortcut3, + 'dashboard-button top-button paired', + right.shortcut3.command.value, + ), + createButtonIfCommandExists( + { + tooltip: 'Record Screen', + command: 'record', + icon: '󰑊', + configurable: false, + }, + `dashboard-button record ${isRecording.value ? 'active' : ''}`, + 'record', + ), + ], + }), + }), + ], + }); + }, + ), }), ], }); diff --git a/modules/menus/dashboard/stats/index.ts b/modules/menus/dashboard/stats/index.ts index 05fa1f9..43e8457 100644 --- a/modules/menus/dashboard/stats/index.ts +++ b/modules/menus/dashboard/stats/index.ts @@ -1,32 +1,34 @@ -import options from "options"; -import { GPU_Stat } from "lib/types/gpustat"; -import { dependencies } from "lib/utils"; +import options from 'options'; +import { GPU_Stat } from 'lib/types/gpustat'; +import { dependencies } from 'lib/utils'; +import { BoxWidget } from 'lib/types/widget'; +import { GenericResourceMetrics } from 'lib/types/customModules/generic'; const { terminal } = options; const { enable_gpu } = options.menus.dashboard.stats; -const Stats = () => { - const divide = ([total, free]) => free / total; +const Stats = (): BoxWidget => { + const divide = ([total, free]: number[]): number => free / total; - const formatSizeInGB = (sizeInKB: number) => - Number((sizeInKB / 1024 ** 2).toFixed(2)); + const formatSizeInGB = (sizeInKB: number): number => Number((sizeInKB / 1024 ** 2).toFixed(2)); const cpu = Variable(0, { poll: [ 2000, - "top -b -n 1", - (out) => { - if (typeof out !== "string") { + 'top -b -n 1', + (out): number => { + if (typeof out !== 'string') { return 0; } - const cpuOut = out.split("\n").find((line) => line.includes("Cpu(s)")); + const cpuOut = out.split('\n').find((line) => line.includes('Cpu(s)')); if (cpuOut === undefined) { return 0; } - return divide([100, cpuOut.split(/\s+/)[1].replace(",", ".")]); + const freeCpu = parseFloat(cpuOut.split(/\s+/)[1].replace(',', '.')); + return divide([100, freeCpu]); }, ], }); @@ -36,22 +38,19 @@ const Stats = () => { { poll: [ 2000, - "free", - (out) => { - if (typeof out !== "string") { + 'free', + (out): GenericResourceMetrics => { + if (typeof out !== 'string') { return { total: 0, used: 0, percentage: 0 }; } - const ramOut = out.split("\n").find((line) => line.includes("Mem:")); + const ramOut = out.split('\n').find((line) => line.includes('Mem:')); if (ramOut === undefined) { return { total: 0, used: 0, percentage: 0 }; } - const [totalRam, usedRam] = ramOut - .split(/\s+/) - .splice(1, 2) - .map(Number); + const [totalRam, usedRam] = ramOut.split(/\s+/).splice(1, 2).map(Number); return { percentage: divide([totalRam, usedRam]), @@ -66,8 +65,8 @@ const Stats = () => { const gpu = Variable(0); const GPUStat = Widget.Box({ - child: enable_gpu.bind("value").as((gpStat) => { - if (!gpStat || !dependencies("gpustat")) { + child: enable_gpu.bind('value').as((gpStat) => { + if (!gpStat || !dependencies('gpustat')) { return Widget.Box(); } @@ -75,19 +74,19 @@ const Stats = () => { vertical: true, children: [ Widget.Box({ - class_name: "stat gpu", + class_name: 'stat gpu', hexpand: true, - vpack: "center", - setup: self => { - const getGpuUsage = () => { + vpack: 'center', + setup: (self) => { + const getGpuUsage = (): void => { if (!enable_gpu.value) { gpu.value = 0; return; } - Utils.execAsync("gpustat --json") + Utils.execAsync('gpustat --json') .then((out) => { - if (typeof out !== "string") { + if (typeof out !== 'string') { return 0; } try { @@ -96,80 +95,79 @@ const Stats = () => { const totalGpu = 100; const usedGpu = data.gpus.reduce((acc: number, gpu: GPU_Stat) => { - - return acc + gpu["utilization.gpu"] + return acc + gpu['utilization.gpu']; }, 0) / data.gpus.length; gpu.value = divide([totalGpu, usedGpu]); } catch (e) { - console.error("Error getting GPU stats:", e); + console.error('Error getting GPU stats:', e); gpu.value = 0; } }) .catch((err) => { - console.error(`An error occurred while fetching GPU stats: ${err}`) - }) - } + console.error(`An error occurred while fetching GPU stats: ${err}`); + }); + }; - self.poll(2000, getGpuUsage) + self.poll(2000, getGpuUsage); - Utils.merge([gpu.bind("value"), enable_gpu.bind("value")], (gpu, enableGpu) => { + Utils.merge([gpu.bind('value'), enable_gpu.bind('value')], (gpu, enableGpu) => { if (!enableGpu) { - return self.children = []; + return (self.children = []); } - return self.children = [ + return (self.children = [ Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { - return () => { - App.closeWindow("dashboardmenu"); + on_primary_click: terminal.bind('value').as((term) => { + return (): void => { + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.Label({ - class_name: "txt-icon", - label: "󰢮", - }) + class_name: 'txt-icon', + label: '󰢮', + }), }), Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { - return () => { - App.closeWindow("dashboardmenu"); + on_primary_click: terminal.bind('value').as((term) => { + return (): void => { + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.LevelBar({ - class_name: "stats-bar", + class_name: 'stats-bar', hexpand: true, - vpack: "center", + vpack: 'center', value: gpu, }), }), - ] - }) + ]); + }); }, }), Widget.Box({ - hpack: "end", - children: Utils.merge([gpu.bind("value"), enable_gpu.bind("value")], (gpuUsed, enableGpu) => { + hpack: 'end', + children: Utils.merge([gpu.bind('value'), enable_gpu.bind('value')], (gpuUsed, enableGpu) => { if (!enableGpu) { return []; } return [ Widget.Label({ - class_name: "stat-value gpu", + class_name: 'stat-value gpu', label: `${Math.floor(gpuUsed * 100)}%`, - }) + }), ]; - }) - }) - ] - }) - }) + }), + }), + ], + }); + }), }); const storage = Variable( @@ -177,13 +175,13 @@ const Stats = () => { { poll: [ 2000, - "df -B1 /", - (out) => { - if (typeof out !== "string") { + 'df -B1 /', + (out): GenericResourceMetrics => { + if (typeof out !== 'string') { return { total: 0, used: 0, percentage: 0 }; } - const dfOut = out.split("\n").find((line) => line.startsWith("/")); + const dfOut = out.split('\n').find((line) => line.startsWith('/')); if (dfOut === undefined) { return { total: 0, used: 0, percentage: 0 }; @@ -207,58 +205,58 @@ const Stats = () => { ); return Widget.Box({ - class_name: "dashboard-card stats-container", + class_name: 'dashboard-card stats-container', vertical: true, - vpack: "fill", - hpack: "fill", + vpack: 'fill', + hpack: 'fill', expand: true, children: [ Widget.Box({ vertical: true, children: [ Widget.Box({ - class_name: "stat cpu", + class_name: 'stat cpu', hexpand: true, - vpack: "center", + vpack: 'center', children: [ Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.Label({ - class_name: "txt-icon", - label: "", - }) + class_name: 'txt-icon', + label: '', + }), }), Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.LevelBar({ - class_name: "stats-bar", + class_name: 'stats-bar', hexpand: true, - vpack: "center", - bar_mode: "continuous", + vpack: 'center', + bar_mode: 'continuous', max_value: 1, - value: cpu.bind("value"), + value: cpu.bind('value'), }), }), ], }), Widget.Label({ - hpack: "end", - class_name: "stat-value cpu", - label: cpu.bind("value").as((v) => `${Math.floor(v * 100)}%`), + hpack: 'end', + class_name: 'stat-value cpu', + label: cpu.bind('value').as((v) => `${Math.floor(v * 100)}%`), }), ], }), @@ -266,46 +264,46 @@ const Stats = () => { vertical: true, children: [ Widget.Box({ - class_name: "stat ram", - vpack: "center", + class_name: 'stat ram', + vpack: 'center', hexpand: true, children: [ Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.Label({ - class_name: "txt-icon", - label: "", - }) + class_name: 'txt-icon', + label: '', + }), }), Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.LevelBar({ - class_name: "stats-bar", + class_name: 'stats-bar', hexpand: true, - vpack: "center", - value: ram.bind("value").as((v) => v.percentage), + vpack: 'center', + value: ram.bind('value').as((v) => v.percentage), }), }), ], }), Widget.Label({ - hpack: "end", - class_name: "stat-value ram", - label: ram.bind("value").as((v) => `${v.used}/${v.total} GB`), + hpack: 'end', + class_name: 'stat-value ram', + label: ram.bind('value').as((v) => `${v.used}/${v.total} GB`), }), ], }), @@ -314,46 +312,46 @@ const Stats = () => { vertical: true, children: [ Widget.Box({ - class_name: "stat storage", + class_name: 'stat storage', hexpand: true, - vpack: "center", + vpack: 'center', children: [ Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.Label({ - class_name: "txt-icon", - label: "󰋊", - }) + class_name: 'txt-icon', + label: '󰋊', + }), }), Widget.Button({ - on_primary_click: terminal.bind("value").as(term => { + on_primary_click: terminal.bind('value').as((term) => { return () => { - App.closeWindow("dashboardmenu"); + App.closeWindow('dashboardmenu'); Utils.execAsync(`bash -c "${term} -e btop"`).catch( (err) => `Failed to open btop: ${err}`, ); - } + }; }), child: Widget.LevelBar({ - class_name: "stats-bar", + class_name: 'stats-bar', hexpand: true, - vpack: "center", - value: storage.bind("value").as((v) => v.percentage), + vpack: 'center', + value: storage.bind('value').as((v) => v.percentage), }), - }) + }), ], }), Widget.Label({ - hpack: "end", - class_name: "stat-value storage", - label: storage.bind("value").as((v) => `${v.used}/${v.total} GB`), + hpack: 'end', + class_name: 'stat-value storage', + label: storage.bind('value').as((v) => `${v.used}/${v.total} GB`), }), ], }), diff --git a/modules/menus/energy/brightness/index.ts b/modules/menus/energy/brightness/index.ts index cfb4ec5..cdefd1a 100644 --- a/modules/menus/energy/brightness/index.ts +++ b/modules/menus/energy/brightness/index.ts @@ -1,40 +1,41 @@ -import brightness from "../../../../services/Brightness.js"; -import icons from "../../../icons/index.js"; +import { BoxWidget } from 'lib/types/widget.js'; +import brightness from '../../../../services/Brightness.js'; +import icons from '../../../icons/index.js'; -const Brightness = () => { +const Brightness = (): BoxWidget => { return Widget.Box({ - class_name: "menu-section-container brightness", + class_name: 'menu-section-container brightness', vertical: true, children: [ Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", + class_name: 'menu-label-container', + hpack: 'fill', child: Widget.Label({ - class_name: "menu-label", + class_name: 'menu-label', hexpand: true, - hpack: "start", - label: "Brightness", + hpack: 'start', + label: 'Brightness', }), }), Widget.Box({ - class_name: "menu-items-section", - vpack: "fill", + class_name: 'menu-items-section', + vpack: 'fill', vexpand: true, vertical: true, child: Widget.Box({ - class_name: "brightness-container", + class_name: 'brightness-container', children: [ Widget.Icon({ vexpand: true, - vpack: "center", - class_name: "brightness-slider-icon", + vpack: 'center', + class_name: 'brightness-slider-icon', icon: icons.brightness.screen, }), Widget.Slider({ - vpack: "center", + vpack: 'center', vexpand: true, - value: brightness.bind("screen"), - class_name: "menu-active-slider menu-slider brightness", + value: brightness.bind('screen'), + class_name: 'menu-active-slider menu-slider brightness', draw_value: false, hexpand: true, min: 0, @@ -42,12 +43,10 @@ const Brightness = () => { onChange: ({ value }) => (brightness.screen = value), }), Widget.Label({ - vpack: "center", + vpack: 'center', vexpand: true, - class_name: "brightness-slider-label", - label: brightness - .bind("screen") - .as((b) => `${Math.round(b * 100)}%`), + class_name: 'brightness-slider-label', + label: brightness.bind('screen').as((b) => `${Math.round(b * 100)}%`), }), ], }), diff --git a/modules/menus/energy/index.ts b/modules/menus/energy/index.ts index 514f8a1..86c898b 100644 --- a/modules/menus/energy/index.ts +++ b/modules/menus/energy/index.ts @@ -1,24 +1,23 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { EnergyProfiles } from "./profiles/index.js"; -import { Brightness } from "./brightness/index.js"; +import DropdownMenu from '../shared/dropdown/index.js'; +import { EnergyProfiles } from './profiles/index.js'; +import { Brightness } from './brightness/index.js'; +import { Attribute, Child } from 'lib/types/widget.js'; +import Window from 'types/widgets/window.js'; -export default () => { +export default (): Window => { return DropdownMenu({ - name: "energymenu", - transition: "crossfade", + name: 'energymenu', + transition: 'crossfade', child: Widget.Box({ - class_name: "menu-items energy", - hpack: "fill", + class_name: 'menu-items energy', + hpack: 'fill', hexpand: true, child: Widget.Box({ vertical: true, - hpack: "fill", + hpack: 'fill', hexpand: true, - class_name: "menu-items-container energy", - children: [ - Brightness(), - EnergyProfiles(), - ], + class_name: 'menu-items-container energy', + children: [Brightness(), EnergyProfiles()], }), }), }); diff --git a/modules/menus/energy/profiles/index.ts b/modules/menus/energy/profiles/index.ts index 80ba93f..f06dd73 100644 --- a/modules/menus/energy/profiles/index.ts +++ b/modules/menus/energy/profiles/index.ts @@ -1,49 +1,61 @@ -const powerProfiles = await Service.import("powerprofiles"); -import icons from "../../../icons/index.js"; +const powerProfiles = await Service.import('powerprofiles'); +import { PowerProfile, PowerProfileObject, PowerProfiles } from 'lib/types/powerprofiles.js'; +import icons from '../../../icons/index.js'; +import { BoxWidget } from 'lib/types/widget.js'; + +const EnergyProfiles = (): BoxWidget => { + const isValidProfile = (profile: string): profile is PowerProfile => + profile === 'power-saver' || profile === 'balanced' || profile === 'performance'; -const EnergyProfiles = () => { return Widget.Box({ - class_name: "menu-section-container energy", + class_name: 'menu-section-container energy', vertical: true, children: [ Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", + class_name: 'menu-label-container', + hpack: 'fill', child: Widget.Label({ - class_name: "menu-label", + class_name: 'menu-label', hexpand: true, - hpack: "start", - label: "Power Profile", + hpack: 'start', + label: 'Power Profile', }), }), Widget.Box({ - class_name: "menu-items-section", - vpack: "fill", + class_name: 'menu-items-section', + vpack: 'fill', vexpand: true, vertical: true, - children: powerProfiles.bind("profiles").as((profiles) => { - return profiles.map((prof) => { - const ProfileLabels = { - "power-saver": "Power Saver", - balanced: "Balanced", - performance: "Performance", + children: powerProfiles.bind('profiles').as((profiles: PowerProfiles) => { + return profiles.map((prof: PowerProfileObject) => { + const profileLabels = { + 'power-saver': 'Power Saver', + balanced: 'Balanced', + performance: 'Performance', }; + + const profileType = prof.Profile; + + if (!isValidProfile(profileType)) { + return profileLabels.balanced; + } + return Widget.Button({ on_primary_click: () => { powerProfiles.active_profile = prof.Profile; }, - class_name: powerProfiles.bind("active_profile").as((active) => { - return `power-profile-item ${active === prof.Profile ? "active" : ""}`; + class_name: powerProfiles.bind('active_profile').as((active) => { + return `power-profile-item ${active === prof.Profile ? 'active' : ''}`; }), child: Widget.Box({ children: [ Widget.Icon({ - class_name: "power-profile-icon", - icon: icons.powerprofile[prof.Profile], + class_name: 'power-profile-icon', + icon: icons.powerprofile[profileType], }), Widget.Label({ - class_name: "power-profile-label", - label: ProfileLabels[prof.Profile], + class_name: 'power-profile-label', + label: profileLabels[profileType], }), ], }), diff --git a/modules/menus/main.ts b/modules/menus/main.ts index 0f83fe1..274579f 100644 --- a/modules/menus/main.ts +++ b/modules/menus/main.ts @@ -1,14 +1,14 @@ -import PowerMenu from "./power/index.js"; -import Verification from "./power/verification.js"; -import AudioMenu from "./audio/index.js"; -import NetworkMenu from "./network/index.js"; -import BluetoothMenu from "./bluetooth/index.js"; -import MediaMenu from "./media/index.js"; -import NotificationsMenu from "./notifications/index.js"; -import CalendarMenu from "./calendar/index.js"; -import EnergyMenu from "./energy/index.js"; -import DashboardMenu from "./dashboard/index.js"; -import PowerDropdown from "./powerDropdown/index.js"; +import PowerMenu from './power/index.js'; +import Verification from './power/verification.js'; +import AudioMenu from './audio/index.js'; +import NetworkMenu from './network/index.js'; +import BluetoothMenu from './bluetooth/index.js'; +import MediaMenu from './media/index.js'; +import NotificationsMenu from './notifications/index.js'; +import CalendarMenu from './calendar/index.js'; +import EnergyMenu from './energy/index.js'; +import DashboardMenu from './dashboard/index.js'; +import PowerDropdown from './powerDropdown/index.js'; export default [ PowerMenu(), diff --git a/modules/menus/media/components/bar.ts b/modules/menus/media/components/bar.ts index c6dcd37..50d03e8 100644 --- a/modules/menus/media/components/bar.ts +++ b/modules/menus/media/components/bar.ts @@ -1,16 +1,19 @@ -const media = await Service.import("mpris"); +import { BoxWidget } from 'lib/types/widget'; +import { Mpris, MprisPlayer } from 'types/service/mpris'; -const Bar = (getPlayerInfo: Function) => { +const media = await Service.import('mpris'); + +const Bar = (getPlayerInfo: (media: Mpris) => MprisPlayer): BoxWidget => { return Widget.Box({ - class_name: "media-indicator-current-progress-bar", + class_name: 'media-indicator-current-progress-bar', hexpand: true, children: [ Widget.Box({ hexpand: true, child: Widget.Slider({ hexpand: true, - tooltip_text: "--", - class_name: "menu-slider media progress", + tooltip_text: '--', + class_name: 'menu-slider media progress', draw_value: false, on_change: ({ value }) => { const foundPlayer = getPlayerInfo(media); @@ -20,7 +23,7 @@ const Bar = (getPlayerInfo: Function) => { return (foundPlayer.position = value * foundPlayer.length); }, setup: (self) => { - const update = () => { + const update = (): void => { const foundPlayer = getPlayerInfo(media); if (foundPlayer !== undefined) { const value = foundPlayer.length ? foundPlayer.position / foundPlayer.length : 0; @@ -32,28 +35,25 @@ const Bar = (getPlayerInfo: Function) => { self.hook(media, update); self.poll(1000, update); - function updateTooltip() { + const updateTooltip = (): void => { const foundPlayer = getPlayerInfo(media); if (foundPlayer === undefined) { - return self.tooltip_text = '00:00' + self.tooltip_text = '00:00'; + return; } const curHour = Math.floor(foundPlayer.position / 3600); const curMin = Math.floor((foundPlayer.position % 3600) / 60); const curSec = Math.floor(foundPlayer.position % 60); - if ( - typeof foundPlayer.position === "number" && - foundPlayer.position >= 0 - ) { + if (typeof foundPlayer.position === 'number' && foundPlayer.position >= 0) { // WARN: These nested ternaries are absolutely disgusting lol - self.tooltip_text = `${curHour > 0 - ? (curHour < 10 ? "0" + curHour : curHour) + ":" - : "" - }${curMin < 10 ? "0" + curMin : curMin}:${curSec < 10 ? "0" + curSec : curSec}`; + self.tooltip_text = `${ + curHour > 0 ? (curHour < 10 ? '0' + curHour : curHour) + ':' : '' + }${curMin < 10 ? '0' + curMin : curMin}:${curSec < 10 ? '0' + curSec : curSec}`; } else { self.tooltip_text = `00:00`; } - } + }; self.poll(1000, updateTooltip); self.hook(media, updateTooltip); }, diff --git a/modules/menus/media/components/controls.ts b/modules/menus/media/components/controls.ts index 54471f5..5cbce21 100644 --- a/modules/menus/media/components/controls.ts +++ b/modules/menus/media/components/controls.ts @@ -1,75 +1,81 @@ -import { MprisPlayer } from "types/service/mpris.js"; -import icons from "../../../icons/index.js"; -const media = await Service.import("mpris"); +import { MprisPlayer } from 'types/service/mpris.js'; +import icons from '../../../icons/index.js'; +import { LoopStatus, PlaybackStatus } from 'lib/types/mpris.js'; +import { BoxWidget } from 'lib/types/widget.js'; +const media = await Service.import('mpris'); -const Controls = (getPlayerInfo: Function) => { - const isLoopActive = (player: MprisPlayer) => { - return player["loop-status"] !== null && - ["track", "playlist"].includes(player["loop-status"].toLowerCase()) - ? "active" - : ""; +const Controls = (getPlayerInfo: () => MprisPlayer): BoxWidget => { + const isValidLoopStatus = (status: string): status is LoopStatus => ['none', 'track', 'playlist'].includes(status); + + const isValidPlaybackStatus = (status: string): status is PlaybackStatus => + ['playing', 'paused', 'stopped'].includes(status); + + const isLoopActive = (player: MprisPlayer): string => { + return player['loop_status'] !== null && ['track', 'playlist'].includes(player['loop_status'].toLowerCase()) + ? 'active' + : ''; }; - const isShuffleActive = (player: MprisPlayer) => { - return player["shuffle-status"] !== null && player["shuffle-status"] - ? "active" - : ""; + const isShuffleActive = (player: MprisPlayer): string => { + return player['shuffle_status'] !== null && player['shuffle_status'] ? 'active' : ''; }; return Widget.Box({ - class_name: "media-indicator-current-player-controls", + class_name: 'media-indicator-current-player-controls', vertical: true, children: [ Widget.Box({ - class_name: "media-indicator-current-controls", - hpack: "center", + class_name: 'media-indicator-current-controls', + hpack: 'center', children: [ Widget.Box({ - class_name: "media-indicator-control shuffle", + class_name: 'media-indicator-control shuffle', children: [ Widget.Button({ - hpack: "center", + hpack: 'center', hasTooltip: true, setup: (self) => { self.hook(media, () => { const foundPlayer = getPlayerInfo(); if (foundPlayer === undefined) { - self.tooltip_text = "Unavailable"; - self.class_name = - "media-indicator-control-button shuffle disabled"; + self.tooltip_text = 'Unavailable'; + self.class_name = 'media-indicator-control-button shuffle disabled'; return; } self.tooltip_text = foundPlayer.shuffle_status !== null ? foundPlayer.shuffle_status - ? "Shuffling" - : "Not Shuffling" + ? 'Shuffling' + : 'Not Shuffling' : null; - self.on_primary_click = () => foundPlayer.shuffle(); - self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? "enabled" : "disabled"}`; + self.on_primary_click = (): void => { + foundPlayer.shuffle(); + }; + self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? 'enabled' : 'disabled'}`; }); }, - child: Widget.Icon(icons.mpris.shuffle["enabled"]), + child: Widget.Icon(icons.mpris.shuffle['enabled']), }), ], }), Widget.Box({ children: [ Widget.Button({ - hpack: "center", + hpack: 'center', child: Widget.Icon(icons.mpris.prev), setup: (self) => { self.hook(media, () => { const foundPlayer = getPlayerInfo(); if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button prev disabled"; + self.class_name = 'media-indicator-control-button prev disabled'; return; } - self.on_primary_click = () => foundPlayer.previous(); - self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? "enabled" : "disabled"}`; + self.on_primary_click = (): void => { + foundPlayer.previous(); + }; + self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? 'enabled' : 'disabled'}`; }); }, }), @@ -78,35 +84,35 @@ const Controls = (getPlayerInfo: Function) => { Widget.Box({ children: [ Widget.Button({ - hpack: "center", + hpack: 'center', setup: (self) => { self.hook(media, () => { const foundPlayer = getPlayerInfo(); if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button play disabled"; + self.class_name = 'media-indicator-control-button play disabled'; return; } - self.on_primary_click = () => foundPlayer.playPause(); - self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? "enabled" : "disabled"}`; + self.on_primary_click = (): void => { + foundPlayer.playPause(); + }; + self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? 'enabled' : 'disabled'}`; }); }, child: Widget.Icon({ - icon: Utils.watch( - icons.mpris.paused, - media, - "changed", - () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - return icons.mpris["paused"]; - } - return icons.mpris[ - foundPlayer.play_back_status.toLowerCase() - ]; - }, - ), + icon: Utils.watch(icons.mpris.paused, media, 'changed', () => { + const foundPlayer: MprisPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + return icons.mpris['paused']; + } + const playbackStatus = foundPlayer.play_back_status?.toLowerCase(); + + if (playbackStatus && isValidPlaybackStatus(playbackStatus)) { + return icons.mpris[playbackStatus]; + } else { + return icons.mpris['paused']; + } + }), }), }), ], @@ -115,64 +121,68 @@ const Controls = (getPlayerInfo: Function) => { class_name: `media-indicator-control next`, children: [ Widget.Button({ - hpack: "center", + hpack: 'center', child: Widget.Icon(icons.mpris.next), setup: (self) => { self.hook(media, () => { const foundPlayer = getPlayerInfo(); if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button next disabled"; + self.class_name = 'media-indicator-control-button next disabled'; return; } - self.on_primary_click = () => foundPlayer.next(); - self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? "enabled" : "disabled"}`; + self.on_primary_click = (): void => { + foundPlayer.next(); + }; + self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? 'enabled' : 'disabled'}`; }); }, }), ], }), Widget.Box({ - class_name: "media-indicator-control loop", + class_name: 'media-indicator-control loop', children: [ Widget.Button({ - hpack: "center", + hpack: 'center', setup: (self) => { self.hook(media, () => { const foundPlayer = getPlayerInfo(); if (foundPlayer === undefined) { - self.tooltip_text = "Unavailable"; - self.class_name = - "media-indicator-control-button shuffle disabled"; + self.tooltip_text = 'Unavailable'; + self.class_name = 'media-indicator-control-button shuffle disabled'; return; } self.tooltip_text = foundPlayer.loop_status !== null ? foundPlayer.loop_status - ? "Shuffling" - : "Not Shuffling" + ? 'Shuffling' + : 'Not Shuffling' : null; - self.on_primary_click = () => foundPlayer.loop(); - self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? "enabled" : "disabled"}`; + self.on_primary_click = (): void => { + foundPlayer.loop(); + }; + self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? 'enabled' : 'disabled'}`; }); }, child: Widget.Icon({ setup: (self) => { self.hook(media, () => { - const foundPlayer = getPlayerInfo(); + const foundPlayer: MprisPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { - self.icon = icons.mpris.loop["none"]; + self.icon = icons.mpris.loop['none']; return; } - self.icon = - foundPlayer.loop_status === null - ? icons.mpris.loop["none"] - : icons.mpris.loop[ - foundPlayer.loop_status?.toLowerCase() - ]; + const loopStatus = foundPlayer.loop_status?.toLowerCase(); + + if (loopStatus && isValidLoopStatus(loopStatus)) { + self.icon = icons.mpris.loop[loopStatus]; + } else { + self.icon = icons.mpris.loop['none']; + } }); }, }), diff --git a/modules/menus/media/components/mediainfo.ts b/modules/menus/media/components/mediainfo.ts index b8a64aa..ea6cb76 100644 --- a/modules/menus/media/components/mediainfo.ts +++ b/modules/menus/media/components/mediainfo.ts @@ -1,78 +1,82 @@ -const media = await Service.import("mpris"); +import { BoxWidget } from 'lib/types/widget'; +import { MprisPlayer } from 'types/service/mpris'; -const MediaInfo = (getPlayerInfo: Function) => { +const media = await Service.import('mpris'); + +const MediaInfo = (getPlayerInfo: () => MprisPlayer): BoxWidget => { return Widget.Box({ - class_name: "media-indicator-current-media-info", - hpack: "center", + class_name: 'media-indicator-current-media-info', + hpack: 'center', hexpand: true, vertical: true, children: [ Widget.Box({ - class_name: "media-indicator-current-song-name", - hpack: "center", + class_name: 'media-indicator-current-song-name', + hpack: 'center', children: [ Widget.Label({ - truncate: "end", + truncate: 'end', max_width_chars: 31, wrap: true, - class_name: "media-indicator-current-song-name-label", + class_name: 'media-indicator-current-song-name-label', setup: (self) => { self.hook(media, () => { const curPlayer = getPlayerInfo(); return (self.label = - curPlayer !== undefined && curPlayer["track-title"].length - ? curPlayer["track-title"] - : "No Media Currently Playing"); + curPlayer !== undefined && curPlayer['track_title'].length + ? curPlayer['track_title'] + : 'No Media Currently Playing'); }); }, }), ], }), Widget.Box({ - class_name: "media-indicator-current-song-author", - hpack: "center", + class_name: 'media-indicator-current-song-author', + hpack: 'center', children: [ Widget.Label({ - truncate: "end", + truncate: 'end', wrap: true, max_width_chars: 35, - class_name: "media-indicator-current-song-author-label", + class_name: 'media-indicator-current-song-author-label', setup: (self) => { self.hook(media, () => { const curPlayer = getPlayerInfo(); - const makeArtistList = (trackArtists: string[]) => { + const makeArtistList = (trackArtists: string[]): string => { if (trackArtists.length === 1 && !trackArtists[0].length) { - return "-----"; + return '-----'; } - return trackArtists.join(", "); + return trackArtists.join(', '); }; + return (self.label = - curPlayer !== undefined && curPlayer["track-artists"].length - ? makeArtistList(curPlayer["track-artists"]) - : "-----"); + curPlayer !== undefined && curPlayer['track_artists'].length + ? makeArtistList(curPlayer['track_artists']) + : '-----'); }); }, }), ], }), Widget.Box({ - class_name: "media-indicator-current-song-album", - hpack: "center", + class_name: 'media-indicator-current-song-album', + hpack: 'center', children: [ Widget.Label({ - truncate: "end", + truncate: 'end', wrap: true, max_width_chars: 40, - class_name: "media-indicator-current-song-album-label", + class_name: 'media-indicator-current-song-album-label', setup: (self) => { self.hook(media, () => { const curPlayer = getPlayerInfo(); return (self.label = - curPlayer !== undefined && curPlayer["track-album"].length - ? curPlayer["track-album"] - : "---"); + curPlayer !== undefined && curPlayer['track_album'].length + ? curPlayer['track_album'] + : '---'); }); }, }), diff --git a/modules/menus/media/index.ts b/modules/menus/media/index.ts index c86272b..ebc45ae 100644 --- a/modules/menus/media/index.ts +++ b/modules/menus/media/index.ts @@ -1,20 +1,22 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { Media } from "./media.js"; +import Window from 'types/widgets/window.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +import { Media } from './media.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { - return DropdownMenu({ - name: "mediamenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "menu-items media", - hpack: "fill", - hexpand: true, - child: Widget.Box({ - class_name: "menu-items-container media", - hpack: "fill", - hexpand: true, - child: Media(), - }), - }), - }); +export default (): Window => { + return DropdownMenu({ + name: 'mediamenu', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'menu-items media', + hpack: 'fill', + hexpand: true, + child: Widget.Box({ + class_name: 'menu-items-container media', + hpack: 'fill', + hexpand: true, + child: Media(), + }), + }), + }); }; diff --git a/modules/menus/media/media.ts b/modules/menus/media/media.ts index a0ea66d..2035516 100644 --- a/modules/menus/media/media.ts +++ b/modules/menus/media/media.ts @@ -1,16 +1,16 @@ -const media = await Service.import("mpris"); -import { MediaInfo } from "./components/mediainfo.js"; -import { Controls } from "./components/controls.js"; -import { Bar } from "./components/bar.js"; -import { MprisPlayer } from "types/service/mpris.js"; -import options from "options.js"; +const media = await Service.import('mpris'); +import { MediaInfo } from './components/mediainfo.js'; +import { Controls } from './components/controls.js'; +import { Bar } from './components/bar.js'; +import { MprisPlayer } from 'types/service/mpris.js'; +import options from 'options.js'; +import { BoxWidget } from 'lib/types/widget.js'; const { tint, color } = options.theme.bar.menus.menu.media.card; const generateAlbumArt = (imageUrl: string): string => { const userTint = tint.value; const userHexColor = color.value; - let css: string; const r = parseInt(userHexColor.slice(1, 3), 16); const g = parseInt(userHexColor.slice(3, 5), 16); @@ -18,36 +18,30 @@ const generateAlbumArt = (imageUrl: string): string => { const alpha = userTint / 100; - css = `background-image: linear-gradient( + const css = `background-image: linear-gradient( rgba(${r}, ${g}, ${b}, ${alpha}), rgba(${r}, ${g}, ${b}, ${alpha}), ${userHexColor} 65em ), url("${imageUrl}");`; return css; -} -const Media = () => { - const curPlayer = Variable(""); +}; +const Media = (): BoxWidget => { + const curPlayer = Variable(''); - media.connect("changed", () => { + media.connect('changed', () => { const statusOrder = { Playing: 1, Paused: 2, Stopped: 3, }; - const isPlaying = media.players.find( - (p) => p["play-back-status"] === "Playing", - ); + const isPlaying = media.players.find((p) => p['play_back_status'] === 'Playing'); - const playerStillExists = media.players.some( - (p) => curPlayer.value === p["bus-name"], - ); + const playerStillExists = media.players.some((p) => curPlayer.value === p['bus_name']); const nextPlayerUp = media.players.sort( - (a, b) => - statusOrder[a["play-back-status"]] - - statusOrder[b["play-back-status"]], + (a, b) => statusOrder[a['play_back_status']] - statusOrder[b['play_back_status']], )[0].bus_name; if (isPlaying || !playerStillExists) { @@ -60,26 +54,22 @@ const Media = () => { }; return Widget.Box({ - class_name: "menu-section-container", + class_name: 'menu-section-container', children: [ Widget.Box({ - class_name: "menu-items-section", + class_name: 'menu-items-section', vertical: false, child: Widget.Box({ - class_name: "menu-content", + class_name: 'menu-content', children: [ Widget.Box({ - class_name: "media-content", + class_name: 'media-content', child: Widget.Box({ - class_name: "media-indicator-right-section", - hpack: "fill", + class_name: 'media-indicator-right-section', + hpack: 'fill', hexpand: true, vertical: true, - children: [ - MediaInfo(getPlayerInfo), - Controls(getPlayerInfo), - Bar(getPlayerInfo), - ], + children: [MediaInfo(getPlayerInfo), Controls(getPlayerInfo), Bar(getPlayerInfo)], }), }), ], @@ -91,7 +81,7 @@ const Media = () => { } }); - Utils.merge([color.bind("value"), tint.bind("value")], () => { + Utils.merge([color.bind('value'), tint.bind('value')], () => { const curPlayer = getPlayerInfo(); if (curPlayer !== undefined) { self.css = generateAlbumArt(curPlayer.track_cover_url); diff --git a/modules/menus/network/ethernet/index.ts b/modules/menus/network/ethernet/index.ts index b03fc04..e1bf0d0 100644 --- a/modules/menus/network/ethernet/index.ts +++ b/modules/menus/network/ethernet/index.ts @@ -1,52 +1,54 @@ -const network = await Service.import("network"); +import { BoxWidget } from 'lib/types/widget'; -const Ethernet = () => { +const network = await Service.import('network'); + +const Ethernet = (): BoxWidget => { return Widget.Box({ - class_name: "menu-section-container ethernet", + class_name: 'menu-section-container ethernet', vertical: true, children: [ Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", + class_name: 'menu-label-container', + hpack: 'fill', child: Widget.Label({ - class_name: "menu-label", + class_name: 'menu-label', hexpand: true, - hpack: "start", - label: "Ethernet", + hpack: 'start', + label: 'Ethernet', }), }), Widget.Box({ - class_name: "menu-items-section", + class_name: 'menu-items-section', vertical: true, child: Widget.Box({ - class_name: "menu-content", + class_name: 'menu-content', vertical: true, setup: (self) => { self.hook(network, () => { return (self.child = Widget.Box({ - class_name: "network-element-item", + class_name: 'network-element-item', child: Widget.Box({ - hpack: "start", + hpack: 'start', children: [ Widget.Icon({ - class_name: `network-icon ethernet ${network.wired.state === "activated" ? "active" : ""}`, + class_name: `network-icon ethernet ${network.wired.state === 'activated' ? 'active' : ''}`, tooltip_text: network.wired.internet, - icon: `${network.wired["icon_name"]}`, + icon: `${network.wired['icon_name']}`, }), Widget.Box({ - class_name: "connection-container", + class_name: 'connection-container', vertical: true, children: [ Widget.Label({ - class_name: "active-connection", - hpack: "start", - truncate: "end", + class_name: 'active-connection', + hpack: 'start', + truncate: 'end', wrap: true, - label: `Ethernet Connection ${network.wired.state !== "unknown" && typeof network.wired?.speed === "number" ? `(${network.wired?.speed / 1000} Gbps)` : ""}`, + label: `Ethernet Connection ${network.wired.state !== 'unknown' && typeof network.wired?.speed === 'number' ? `(${network.wired?.speed / 1000} Gbps)` : ''}`, }), Widget.Label({ - hpack: "start", - class_name: "connection-status dim", + hpack: 'start', + class_name: 'connection-status dim', label: network.wired.internet.charAt(0).toUpperCase() + network.wired.internet.slice(1), diff --git a/modules/menus/network/index.ts b/modules/menus/network/index.ts index 9bef10b..d0fb9ff 100644 --- a/modules/menus/network/index.ts +++ b/modules/menus/network/index.ts @@ -1,19 +1,21 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { Ethernet } from "./ethernet/index.js"; -import { Wifi } from "./wifi/index.js"; +import Window from 'types/widgets/window.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +import { Ethernet } from './ethernet/index.js'; +import { Wifi } from './wifi/index.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => { - return DropdownMenu({ - name: "networkmenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "menu-items network", - child: Widget.Box({ - vertical: true, - hexpand: true, - class_name: "menu-items-container network", - children: [Ethernet(), Wifi()], - }), - }), - }); +export default (): Window => { + return DropdownMenu({ + name: 'networkmenu', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'menu-items network', + child: Widget.Box({ + vertical: true, + hexpand: true, + class_name: 'menu-items-container network', + children: [Ethernet(), Wifi()], + }), + }), + }); }; diff --git a/modules/menus/network/utils.ts b/modules/menus/network/utils.ts index a46e85b..229189a 100644 --- a/modules/menus/network/utils.ts +++ b/modules/menus/network/utils.ts @@ -1,23 +1,23 @@ -const getWifiIcon = (iconName: string) => { - const deviceIconMap = [ - ["network-wireless-acquiring", "󰤩"], - ["network-wireless-connected", "󰤨"], - ["network-wireless-encrypted", "󰤪"], - ["network-wireless-hotspot", "󰤨"], - ["network-wireless-no-route", "󰤩"], - ["network-wireless-offline", "󰤮"], - ["network-wireless-signal-excellent", "󰤨"], - ["network-wireless-signal-good", "󰤥"], - ["network-wireless-signal-ok", "󰤢"], - ["network-wireless-signal-weak", "󰤟"], - ["network-wireless-signal-none", "󰤯"], +import { WifiIcon } from 'lib/types/network'; + +const getWifiIcon = (iconName: string): WifiIcon => { + const deviceIconMap: [string, WifiIcon][] = [ + ['network-wireless-acquiring', '󰤩'], + ['network-wireless-connected', '󰤨'], + ['network-wireless-encrypted', '󰤪'], + ['network-wireless-hotspot', '󰤨'], + ['network-wireless-no-route', '󰤩'], + ['network-wireless-offline', '󰤮'], + ['network-wireless-signal-excellent', '󰤨'], + ['network-wireless-signal-good', '󰤥'], + ['network-wireless-signal-ok', '󰤢'], + ['network-wireless-signal-weak', '󰤟'], + ['network-wireless-signal-none', '󰤯'], ]; - const foundMatch = deviceIconMap.find((icon) => - RegExp(icon[0]).test(iconName.toLowerCase()), - ); + const foundMatch = deviceIconMap.find((icon) => RegExp(icon[0]).test(iconName.toLowerCase())); - return foundMatch ? foundMatch[1] : "󰤨"; + return foundMatch ? foundMatch[1] : '󰤨'; }; export { getWifiIcon }; diff --git a/modules/menus/network/wifi/APStaging.ts b/modules/menus/network/wifi/APStaging.ts index 40bc5d0..29b2798 100644 --- a/modules/menus/network/wifi/APStaging.ts +++ b/modules/menus/network/wifi/APStaging.ts @@ -1,18 +1,26 @@ -import { Network } from "types/service/network"; -import { Variable } from "types/variable"; -import { AccessPoint } from "lib/types/network"; -const renderWapStaging = (self: any, network: Network, staging: Variable, connecting: Variable) => { - Utils.merge([network.bind("wifi"), staging.bind("value")], () => { +import { Network } from 'types/service/network'; +import { Variable } from 'types/variable'; +import { AccessPoint } from 'lib/types/network'; +import Box from 'types/widgets/box'; +import { Attribute, Child } from 'lib/types/widget'; + +const renderWapStaging = ( + self: Box, + network: Network, + staging: Variable, + connecting: Variable, +): void => { + Utils.merge([network.bind('wifi'), staging.bind('value')], () => { if (!Object.keys(staging.value).length) { return (self.child = Widget.Box()); } return (self.child = Widget.Box({ - class_name: "network-element-item staging", + class_name: 'network-element-item staging', vertical: true, children: [ Widget.Box({ - hpack: "fill", + hpack: 'fill', hexpand: true, children: [ Widget.Icon({ @@ -20,74 +28,70 @@ const renderWapStaging = (self: any, network: Network, staging: Variable staging.value.bssid === c), + hpack: 'end', + reveal_child: connecting.bind('value').as((c) => staging.value.bssid === c), child: Widget.Spinner({ - class_name: "spinner wap", + class_name: 'spinner wap', }), }), ], }), Widget.Box({ - class_name: "network-password-input-container", - hpack: "fill", + class_name: 'network-password-input-container', + hpack: 'fill', hexpand: true, children: [ Widget.Entry({ - hpack: "start", + hpack: 'start', hexpand: true, visibility: false, - class_name: "network-password-input", - placeholder_text: "enter password", + class_name: 'network-password-input', + placeholder_text: 'enter password', onAccept: (selfInp) => { - connecting.value = staging.value.bssid || ""; + connecting.value = staging.value.bssid || ''; Utils.execAsync( `nmcli dev wifi connect ${staging.value.bssid} password ${selfInp.text}`, ) .catch((err) => { - connecting.value = ""; - console.error( - `Failed to connect to wifi: ${staging.value.ssid}... ${err}`, - ); + connecting.value = ''; + console.error(`Failed to connect to wifi: ${staging.value.ssid}... ${err}`); Utils.notify({ - summary: "Network", + summary: 'Network', body: err, timeout: 5000, }); }) .then(() => { - connecting.value = ""; + connecting.value = ''; staging.value = {} as AccessPoint; }); - selfInp.text = ""; + selfInp.text = ''; }, }), Widget.Button({ - hpack: "end", - class_name: "close-network-password-input-button", + hpack: 'end', + class_name: 'close-network-password-input-button', on_primary_click: () => { - connecting.value = ""; + connecting.value = ''; staging.value = {} as AccessPoint; }, child: Widget.Icon({ - class_name: "close-network-password-input-icon", - icon: "window-close-symbolic", + class_name: 'close-network-password-input-icon', + icon: 'window-close-symbolic', }), }), ], diff --git a/modules/menus/network/wifi/WirelessAPs.ts b/modules/menus/network/wifi/WirelessAPs.ts index 5bd65c9..fac891d 100644 --- a/modules/menus/network/wifi/WirelessAPs.ts +++ b/modules/menus/network/wifi/WirelessAPs.ts @@ -1,46 +1,53 @@ -import { Network } from "types/service/network.js"; -import { AccessPoint } from "lib/types/network.js"; -import { Variable } from "types/variable.js"; -import { getWifiIcon } from "../utils.js"; -const renderWAPs = (self: any, network: Network, staging: Variable, connecting: Variable) => { - const getIdBySsid = (ssid: string, nmcliOutput: string) => { - const lines = nmcliOutput.trim().split("\n"); +import { Network } from 'types/service/network.js'; +import { AccessPoint, WifiStatus } from 'lib/types/network.js'; +import { Variable } from 'types/variable.js'; +import { getWifiIcon } from '../utils.js'; +import { WIFI_STATUS_MAP } from 'globals/network.js'; +import { Attribute, Child } from 'lib/types/widget.js'; +import Box from 'types/widgets/box.js'; +const renderWAPs = ( + self: Box, + network: Network, + staging: Variable, + connecting: Variable, +): void => { + const getIdBySsid = (ssid: string, nmcliOutput: string): string | undefined => { + const lines = nmcliOutput.trim().split('\n'); for (const line of lines) { const columns = line.trim().split(/\s{2,}/); if (columns[0].includes(ssid)) { return columns[1]; } } - return null; }; - const WifiStatusMap = { - unknown: "Status Unknown", - unmanaged: "Unmanaged", - unavailable: "Unavailable", - disconnected: "Disconnected", - prepare: "Preparing Connecting", - config: "Connecting", - need_auth: "Needs Authentication", - ip_config: "Requesting IP", - ip_check: "Checking Access", - secondaries: "Waiting on Secondaries", - activated: "Connected", - deactivating: "Disconnecting", - failed: "Connection Failed", + const isValidWifiStatus = (status: string): status is WifiStatus => { + return status in WIFI_STATUS_MAP; }; + + const getWifiStatus = (): string => { + const wifiState = network.wifi.state?.toLowerCase(); + + if (wifiState && isValidWifiStatus(wifiState)) { + return WIFI_STATUS_MAP[wifiState]; + } + return WIFI_STATUS_MAP['unknown']; + }; + self.hook(network, () => { - Utils.merge([staging.bind("value"), connecting.bind("value")], () => { - // Sometimes the network service will yield a "this._device is undefined" when + Utils.merge([staging.bind('value'), connecting.bind('value')], () => { + // NOTE: Sometimes the network service will yield a "this._device is undefined" when // trying to access the "access_points" property. So we must validate that // it's not 'undefined' - // + // -- // Also this is an AGS bug that needs to be fixed - let WAPs = - network.wifi._device !== undefined ? network.wifi["access-points"] : []; - const dedupeWAPs = () => { - const dedupMap = {}; + // TODO: Remove @ts-ignore once AGS bug is fixed + // @ts-expect-error to fix AGS bug + let WAPs = network.wifi._device !== undefined ? network.wifi['access_points'] : []; + + const dedupeWAPs = (): AccessPoint[] => { + const dedupMap: Record = {}; WAPs.forEach((item: AccessPoint) => { if (item.ssid !== null && !Object.hasOwnProperty.call(dedupMap, item.ssid)) { dedupMap[item.ssid] = item; @@ -52,7 +59,7 @@ const renderWAPs = (self: any, network: Network, staging: Variable, WAPs = dedupeWAPs(); - const isInStaging = (wap: AccessPoint) => { + const isInStaging = (wap: AccessPoint): boolean => { if (Object.keys(staging.value).length === 0) { return false; } @@ -60,15 +67,15 @@ const renderWAPs = (self: any, network: Network, staging: Variable, return wap.bssid === staging.value.bssid; }; - const isDisconnecting = (wap: AccessPoint) => { + const isDisconnecting = (wap: AccessPoint): boolean => { if (wap.ssid === network.wifi.ssid) { - return network.wifi.state.toLowerCase() === "deactivating"; + return network.wifi.state.toLowerCase() === 'deactivating'; } return false; }; const filteredWAPs = WAPs.filter((ap: AccessPoint) => { - return ap.ssid !== "Unknown" && !isInStaging(ap); + return ap.ssid !== 'Unknown' && !isInStaging(ap); }).sort((a: AccessPoint, b: AccessPoint) => { if (network.wifi.ssid === a.ssid) { return -1; @@ -83,11 +90,11 @@ const renderWAPs = (self: any, network: Network, staging: Variable, if (filteredWAPs.length <= 0 && Object.keys(staging.value).length === 0) { return (self.child = Widget.Label({ - class_name: "waps-not-found dim", + class_name: 'waps-not-found dim', expand: true, - hpack: "center", - vpack: "center", - label: "No Wi-Fi Networks Found", + hpack: 'center', + vpack: 'center', + label: 'No Wi-Fi Networks Found', })); } return (self.children = filteredWAPs.map((ap: AccessPoint) => { @@ -99,62 +106,57 @@ const renderWAPs = (self: any, network: Network, staging: Variable, return; } - connecting.value = ap.bssid || ""; + connecting.value = ap.bssid || ''; Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`) .then(() => { - connecting.value = ""; + connecting.value = ''; staging.value = {} as AccessPoint; }) .catch((err) => { - if ( - err - .toLowerCase() - .includes("secrets were required, but not provided") - ) { + if (err.toLowerCase().includes('secrets were required, but not provided')) { staging.value = ap; } else { Utils.notify({ - summary: "Network", + summary: 'Network', body: err, timeout: 5000, }); } - connecting.value = ""; + connecting.value = ''; }); }, - class_name: "network-element-item", + class_name: 'network-element-item', child: Widget.Box({ hexpand: true, children: [ Widget.Box({ - hpack: "start", + hpack: 'start', hexpand: true, children: [ Widget.Label({ - vpack: "start", - class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? "active" : ""} txt-icon`, - label: getWifiIcon(`${ap["iconName"]}`), + vpack: 'start', + class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? 'active' : ''} txt-icon`, + label: getWifiIcon(`${ap['iconName']}`), }), Widget.Box({ - class_name: "connection-container", - vpack: "center", + class_name: 'connection-container', + vpack: 'center', vertical: true, children: [ Widget.Label({ - vpack: "center", - class_name: "active-connection", - hpack: "start", - truncate: "end", + vpack: 'center', + class_name: 'active-connection', + hpack: 'start', + truncate: 'end', wrap: true, label: ap.ssid, }), Widget.Revealer({ revealChild: ap.ssid === network.wifi.ssid, child: Widget.Label({ - hpack: "start", - class_name: "connection-status dim", - label: - WifiStatusMap[network.wifi.state.toLowerCase()], + hpack: 'start', + class_name: 'connection-status dim', + label: getWifiStatus(), }), }), ], @@ -162,48 +164,48 @@ const renderWAPs = (self: any, network: Network, staging: Variable, ], }), Widget.Revealer({ - hpack: "end", - vpack: "start", - reveal_child: - ap.bssid === connecting.value || isDisconnecting(ap), + hpack: 'end', + vpack: 'start', + reveal_child: ap.bssid === connecting.value || isDisconnecting(ap), child: Widget.Spinner({ - vpack: "start", - class_name: "spinner wap", + vpack: 'start', + class_name: 'spinner wap', }), }), ], }), }), Widget.Revealer({ - vpack: "start", + vpack: 'start', reveal_child: ap.bssid !== connecting.value && ap.active, child: Widget.Button({ - tooltip_text: "Delete/Forget Network", - class_name: "menu-icon-button network disconnect", + tooltip_text: 'Delete/Forget Network', + class_name: 'menu-icon-button network disconnect', on_primary_click: () => { - connecting.value = ap.bssid || ""; - Utils.execAsync("nmcli connection show --active").then(() => { - Utils.execAsync("nmcli connection show --active").then( - (res) => { - const connectionId = getIdBySsid(ap.ssid || "", res); + connecting.value = ap.bssid || ''; + Utils.execAsync('nmcli connection show --active').then(() => { + Utils.execAsync('nmcli connection show --active').then((res) => { + const connectionId = getIdBySsid(ap.ssid || '', res); - Utils.execAsync( - `nmcli connection delete ${connectionId} "${ap.ssid}"`, - ) - .then(() => (connecting.value = "")) - .catch((err) => { - connecting.value = ""; - console.error( - `Error while forgetting "${ap.ssid}": ${err}`, - ); - }); - }, - ); + if (connectionId === undefined) { + console.error( + `Error while forgetting "${ap.ssid}": Connection ID not found`, + ); + return; + } + + Utils.execAsync(`nmcli connection delete ${connectionId} "${ap.ssid}"`) + .then(() => (connecting.value = '')) + .catch((err) => { + connecting.value = ''; + console.error(`Error while forgetting "${ap.ssid}": ${err}`); + }); + }); }); }, child: Widget.Label({ - class_name: "txt-icon delete-network", - label: "󰚃", + class_name: 'txt-icon delete-network', + label: '󰚃', }), }), }), diff --git a/modules/menus/network/wifi/index.ts b/modules/menus/network/wifi/index.ts index 8fedae3..dc583fc 100644 --- a/modules/menus/network/wifi/index.ts +++ b/modules/menus/network/wifi/index.ts @@ -1,64 +1,63 @@ -const network = await Service.import("network"); -import { renderWAPs } from "./WirelessAPs.js"; -import { renderWapStaging } from "./APStaging.js"; -import { AccessPoint } from "lib/types/network.js"; +const network = await Service.import('network'); +import { renderWAPs } from './WirelessAPs.js'; +import { renderWapStaging } from './APStaging.js'; +import { AccessPoint } from 'lib/types/network.js'; +import { BoxWidget } from 'lib/types/widget.js'; const Staging = Variable({} as AccessPoint); -const Connecting = Variable(""); +const Connecting = Variable(''); const searchInProgress = Variable(false); -const startRotation = () => { +const startRotation = (): void => { searchInProgress.value = true; setTimeout(() => { searchInProgress.value = false; }, 5 * 1000); }; -const Wifi = () => { +const Wifi = (): BoxWidget => { return Widget.Box({ - class_name: "menu-section-container wifi", + class_name: 'menu-section-container wifi', vertical: true, children: [ Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", + class_name: 'menu-label-container', + hpack: 'fill', children: [ Widget.Label({ - class_name: "menu-label", + class_name: 'menu-label', hexpand: true, - hpack: "start", - label: "Wi-Fi", + hpack: 'start', + label: 'Wi-Fi', }), Widget.Button({ - vpack: "center", - hpack: "end", - class_name: "menu-icon-button search network", + vpack: 'center', + hpack: 'end', + class_name: 'menu-icon-button search network', on_primary_click: () => { startRotation(); network.wifi.scan(); }, child: Widget.Icon({ - class_name: searchInProgress - .bind("value") - .as((v) => (v ? "spinning" : "")), - icon: "view-refresh-symbolic", + class_name: searchInProgress.bind('value').as((v) => (v ? 'spinning' : '')), + icon: 'view-refresh-symbolic', }), }), ], }), Widget.Box({ - class_name: "menu-items-section", + class_name: 'menu-items-section', vertical: true, children: [ Widget.Box({ - class_name: "wap-staging", + class_name: 'wap-staging', setup: (self) => { renderWapStaging(self, network, Staging, Connecting); }, }), Widget.Box({ - class_name: "available-waps", + class_name: 'available-waps', vertical: true, setup: (self) => { renderWAPs(self, network, Staging, Connecting); diff --git a/modules/menus/notifications/controls/index.ts b/modules/menus/notifications/controls/index.ts index 5226f4d..467bf00 100644 --- a/modules/menus/notifications/controls/index.ts +++ b/modules/menus/notifications/controls/index.ts @@ -1,32 +1,34 @@ -import { Notifications } from "types/service/notifications"; +import { closeNotifications } from 'globals/notification'; +import { BoxWidget } from 'lib/types/widget'; +import { Notifications } from 'types/service/notifications'; -const Controls = (notifs: Notifications) => { +const Controls = (notifs: Notifications): BoxWidget => { return Widget.Box({ - class_name: "notification-menu-controls", + class_name: 'notification-menu-controls', expand: false, vertical: false, children: [ Widget.Box({ - class_name: "menu-label-container notifications", - hpack: "start", - vpack: "center", + class_name: 'menu-label-container notifications', + hpack: 'start', + vpack: 'center', expand: true, children: [ Widget.Label({ - class_name: "menu-label notifications", - label: "Notifications", + class_name: 'menu-label notifications', + label: 'Notifications', }), ], }), Widget.Box({ - hpack: "end", - vpack: "center", + hpack: 'end', + vpack: 'center', expand: false, children: [ Widget.Switch({ - class_name: "menu-switch notifications", - vpack: "center", - active: notifs.bind("dnd").as((dnd: boolean) => !dnd), + class_name: 'menu-switch notifications', + vpack: 'center', + active: notifs.bind('dnd').as((dnd: boolean) => !dnd), on_activate: ({ active }) => { notifs.dnd = !active; }, @@ -34,18 +36,28 @@ const Controls = (notifs: Notifications) => { Widget.Box({ children: [ Widget.Separator({ - hpack: "center", + hpack: 'center', vexpand: true, vertical: true, - class_name: "menu-separator notification-controls", + class_name: 'menu-separator notification-controls', }), Widget.Button({ - class_name: "clear-notifications-button", - tooltip_text: "Clear Notifications", - on_primary_click: () => notifs.clear(), + className: 'clear-notifications-button', + tooltip_text: 'Clear Notifications', + on_primary_click: () => { + if (removingNotifications.value) { + return; + } + + closeNotifications(notifs.notifications); + }, child: Widget.Label({ - class_name: "clear-notifications-label txt-icon", - label: "", + class_name: removingNotifications.bind('value').as((removing: boolean) => { + return removing + ? 'clear-notifications-label txt-icon removing' + : 'clear-notifications-label txt-icon'; + }), + label: '', }), }), ], diff --git a/modules/menus/notifications/index.ts b/modules/menus/notifications/index.ts index de4a4d3..ae2b151 100644 --- a/modules/menus/notifications/index.ts +++ b/modules/menus/notifications/index.ts @@ -1,46 +1,45 @@ -import DropdownMenu from "../DropdownMenu.js"; -const notifs = await Service.import("notifications"); -import { Controls } from "./controls/index.js"; -import { NotificationCard } from "./notification/index.js"; -import { NotificationPager } from "./pager/index.js"; +import { Notification } from 'types/service/notifications.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +const notifs = await Service.import('notifications'); +import { Controls } from './controls/index.js'; +import { NotificationCard } from './notification/index.js'; +import { NotificationPager } from './pager/index.js'; -import options from "options"; +import options from 'options'; +import Window from 'types/widgets/window.js'; +import { Attribute, Child } from 'lib/types/widget.js'; const { displayedTotal } = options.notifications; -const { show: showPager } = options.theme.bar.menus.menu.notifications.pager; -export default () => { +export default (): Window => { const curPage = Variable(1); - Utils.merge([curPage.bind("value"), displayedTotal.bind("value"), notifs.bind("notifications")], (currentPage, dispTotal, notifications) => { - // If the page doesn't have enough notifications to display, go back - // to the previous page. - if (notifications.length <= (currentPage - 1) * dispTotal) { - curPage.value = currentPage <= 1 ? 1 : currentPage - 1; - } - }); + Utils.merge( + [curPage.bind('value'), displayedTotal.bind('value'), notifs.bind('notifications')], + (currentPage: number, dispTotal: number, notifications: Notification[]) => { + // If the page doesn't have enough notifications to display, go back + // to the previous page. + if (notifications.length <= (currentPage - 1) * dispTotal) { + curPage.value = currentPage <= 1 ? 1 : currentPage - 1; + } + }, + ); return DropdownMenu({ - name: "notificationsmenu", - transition: "crossfade", + name: 'notificationsmenu', + transition: 'crossfade', child: Widget.Box({ - class_name: "notification-menu-content", - css: "padding: 1px; margin: -1px;", + class_name: 'notification-menu-content', + css: 'padding: 1px; margin: -1px;', hexpand: true, vexpand: false, children: [ Widget.Box({ - class_name: "notification-card-container menu", + class_name: 'notification-card-container menu', vertical: true, hexpand: false, vexpand: false, - children: showPager.bind("value").as(shPgr => { - if (shPgr) { - return [Controls(notifs), NotificationCard(notifs, curPage), NotificationPager(curPage)]; - } - return [Controls(notifs), NotificationCard(notifs, curPage)] - - }) + children: [Controls(notifs), NotificationCard(notifs, curPage), NotificationPager(curPage)], }), ], }), diff --git a/modules/menus/notifications/notification/actions/index.ts b/modules/menus/notifications/notification/actions/index.ts index 3d9f2c9..bf68dad 100644 --- a/modules/menus/notifications/notification/actions/index.ts +++ b/modules/menus/notifications/notification/actions/index.ts @@ -1,35 +1,36 @@ -import { Notification, Notifications } from "types/service/notifications"; -const Actions = (notif: Notification, notifs: Notifications) => { +import { BoxWidget } from 'lib/types/widget'; +import { Notification, Notifications } from 'types/service/notifications'; +const Actions = (notif: Notification, notifs: Notifications): BoxWidget => { if (notif.actions !== undefined && notif.actions.length > 0) { return Widget.Box({ - class_name: "notification-card-actions menu", + class_name: 'notification-card-actions menu', hexpand: true, - vpack: "end", + vpack: 'end', children: notif.actions.map((action) => { return Widget.Button({ hexpand: true, - class_name: "notification-action-buttons menu", + class_name: 'notification-action-buttons menu', on_primary_click: () => { - if (action.id.includes("scriptAction:-")) { - App.closeWindow("notificationsmenu"); - Utils.execAsync( - `${action.id.replace("scriptAction:-", "")}`, - ).catch((err) => console.error(err)); + if (action.id.includes('scriptAction:-')) { + App.closeWindow('notificationsmenu'); + Utils.execAsync(`${action.id.replace('scriptAction:-', '')}`).catch((err) => + console.error(err), + ); notifs.CloseNotification(notif.id); } else { - App.closeWindow("notificationsmenu"); + App.closeWindow('notificationsmenu'); notif.invoke(action.id); } }, child: Widget.Box({ - hpack: "center", + hpack: 'center', hexpand: true, children: [ Widget.Label({ - class_name: "notification-action-buttons-label menu", + class_name: 'notification-action-buttons-label menu', hexpand: true, max_width_chars: 15, - truncate: "end", + truncate: 'end', wrap: true, label: action.label, }), @@ -41,7 +42,7 @@ const Actions = (notif: Notification, notifs: Notifications) => { } return Widget.Box({ - class_name: "spacer", + class_name: 'spacer', }); }; diff --git a/modules/menus/notifications/notification/body/index.ts b/modules/menus/notifications/notification/body/index.ts index 2372de2..62aac7a 100644 --- a/modules/menus/notifications/notification/body/index.ts +++ b/modules/menus/notifications/notification/body/index.ts @@ -1,23 +1,24 @@ -import { notifHasImg } from "../../utils.js"; -import { Notification } from "types/service/notifications"; +import { BoxWidget } from 'lib/types/widget.js'; +import { notifHasImg } from '../../utils.js'; +import { Notification } from 'types/service/notifications'; -export const Body = (notif: Notification) => { +export const Body = (notif: Notification): BoxWidget => { return Widget.Box({ - vpack: "start", + vpack: 'start', hexpand: true, - class_name: "notification-card-body menu", + class_name: 'notification-card-body menu', children: [ Widget.Label({ hexpand: true, use_markup: true, xalign: 0, - justification: "left", - truncate: "end", + justification: 'left', + truncate: 'end', lines: 2, max_width_chars: !notifHasImg(notif) ? 35 : 28, wrap: true, - class_name: "notification-card-body-label menu", - label: notif["body"], + class_name: 'notification-card-body-label menu', + label: notif['body'], }), ], }); diff --git a/modules/menus/notifications/notification/close/index.ts b/modules/menus/notifications/notification/close/index.ts index c5fa2c4..261f949 100644 --- a/modules/menus/notifications/notification/close/index.ts +++ b/modules/menus/notifications/notification/close/index.ts @@ -1,14 +1,17 @@ -import { Notification, Notifications } from "types/service/notifications"; -export const CloseButton = (notif: Notification, notifs: Notifications) => { +import { Attribute } from 'lib/types/widget'; +import { Notification, Notifications } from 'types/service/notifications'; +import Button from 'types/widgets/button'; +import Label from 'types/widgets/label'; +export const CloseButton = (notif: Notification, notifs: Notifications): Button, Attribute> => { return Widget.Button({ - class_name: "close-notification-button menu", + class_name: 'close-notification-button menu', on_primary_click: () => { notifs.CloseNotification(notif.id); }, child: Widget.Label({ - class_name: "txt-icon notif-close", - label: "󰅜", - hpack: "center", + class_name: 'txt-icon notif-close', + label: '󰅜', + hpack: 'center', }), }); }; diff --git a/modules/menus/notifications/notification/header/icon.ts b/modules/menus/notifications/notification/header/icon.ts index af6162b..c51bc01 100644 --- a/modules/menus/notifications/notification/header/icon.ts +++ b/modules/menus/notifications/notification/header/icon.ts @@ -1,32 +1,19 @@ -import icons from "../../../../icons/index.js"; +import { Notification } from 'types/service/notifications.js'; +import { NotificationIcon } from 'lib/types/notification.js'; +import { getNotificationIcon } from 'globals/notification'; +import { BoxWidget } from 'lib/types/widget'; -const NotificationIcon = ({ app_entry, app_icon, app_name }) => { - let icon = icons.fallback.notification; - - if ( - Utils.lookUpIcon(app_name) || - Utils.lookUpIcon(app_name.toLowerCase() || "") - ) - icon = Utils.lookUpIcon(app_name) - ? app_name - : Utils.lookUpIcon(app_name.toLowerCase()) - ? app_name.toLowerCase() - : ""; - - if (Utils.lookUpIcon(app_icon) && icon === "") icon = app_icon; - - if (Utils.lookUpIcon(app_entry || "") && icon === "") icon = app_entry || ""; - - return Widget.Box({ - css: ` +const NotificationIcon = ({ app_entry = '', app_icon = '', app_name = '' }: Partial): BoxWidget => { + return Widget.Box({ + css: ` min-width: 2rem; min-height: 2rem; `, - child: Widget.Icon({ - class_name: "notification-icon menu", - icon, - }), - }); + child: Widget.Icon({ + class_name: 'notification-icon menu', + icon: getNotificationIcon(app_name, app_icon, app_entry), + }), + }); }; export { NotificationIcon }; diff --git a/modules/menus/notifications/notification/header/index.ts b/modules/menus/notifications/notification/header/index.ts index df95f7b..1373ae3 100644 --- a/modules/menus/notifications/notification/header/index.ts +++ b/modules/menus/notifications/notification/header/index.ts @@ -1,50 +1,51 @@ -import GLib from "gi://GLib"; -import { Notification } from "types/service/notifications"; -import { NotificationIcon } from "./icon.js"; -import { notifHasImg } from "../../utils.js"; -import options from "options.js"; +import GLib from 'gi://GLib'; +import { Notification } from 'types/service/notifications'; +import { NotificationIcon } from './icon.js'; +import { notifHasImg } from '../../utils.js'; +import options from 'options.js'; +import { BoxWidget } from 'lib/types/widget.js'; const { military } = options.menus.clock.time; -export const Header = (notif: Notification) => { - const time = (time: number, format = "%I:%M %p") => { - return GLib.DateTime.new_from_unix_local(time).format(military.value ? "%H:%M" : format); - } +export const Header = (notif: Notification): BoxWidget => { + const time = (time: number, format = '%I:%M %p'): string => { + return GLib.DateTime.new_from_unix_local(time).format(military.value ? '%H:%M' : format) || '--:--'; + }; return Widget.Box({ vertical: false, hexpand: true, children: [ Widget.Box({ - class_name: "notification-card-header menu", - hpack: "start", + class_name: 'notification-card-header menu', + hpack: 'start', children: [NotificationIcon(notif)], }), Widget.Box({ - class_name: "notification-card-header menu", + class_name: 'notification-card-header menu', hexpand: true, - vpack: "start", + vpack: 'start', children: [ Widget.Label({ - class_name: "notification-card-header-label menu", - hpack: "start", + class_name: 'notification-card-header-label menu', + hpack: 'start', hexpand: true, vexpand: true, max_width_chars: !notifHasImg(notif) ? 34 : 22, - truncate: "end", + truncate: 'end', wrap: true, - label: notif["summary"], + label: notif['summary'], }), ], }), Widget.Box({ - class_name: "notification-card-header menu", - hpack: "end", - vpack: "start", + class_name: 'notification-card-header menu', + hpack: 'end', + vpack: 'start', hexpand: true, child: Widget.Label({ vexpand: true, - class_name: "notification-time", + class_name: 'notification-time', label: time(notif.time), }), }), diff --git a/modules/menus/notifications/notification/image/index.ts b/modules/menus/notifications/notification/image/index.ts index c132385..6b01768 100644 --- a/modules/menus/notifications/notification/image/index.ts +++ b/modules/menus/notifications/notification/image/index.ts @@ -1,17 +1,18 @@ -import { Notification } from "types/service/notifications"; -import { notifHasImg } from "../../utils.js"; +import { Notification } from 'types/service/notifications'; +import { notifHasImg } from '../../utils.js'; +import { BoxWidget } from 'lib/types/widget.js'; -const Image = (notif: Notification) => { +const Image = (notif: Notification): BoxWidget => { if (notifHasImg(notif)) { return Widget.Box({ - class_name: "notification-card-image-container menu", - hpack: "center", - vpack: "center", + class_name: 'notification-card-image-container menu', + hpack: 'center', + vpack: 'center', vexpand: false, child: Widget.Box({ - hpack: "center", + hpack: 'center', vexpand: false, - class_name: "notification-card-image menu", + class_name: 'notification-card-image menu', css: `background-image: url("${notif.image}")`, }), }); diff --git a/modules/menus/notifications/notification/index.ts b/modules/menus/notifications/notification/index.ts index 74cdfa1..9254327 100644 --- a/modules/menus/notifications/notification/index.ts +++ b/modules/menus/notifications/notification/index.ts @@ -1,68 +1,77 @@ -import { Notifications, Notification } from "types/service/notifications"; -import { notifHasImg } from "../utils.js"; -import { Header } from "./header/index.js"; -import { Actions } from "./actions/index.js"; -import { Image } from "./image/index.js"; -import { Placeholder } from "./placeholder/index.js"; -import { Body } from "./body/index.js"; -import { CloseButton } from "./close/index.js"; -import options from "options.js"; -import { Variable } from "types/variable.js"; +import { Notifications, Notification } from 'types/service/notifications'; +import { notifHasImg } from '../utils.js'; +import { Header } from './header/index.js'; +import { Actions } from './actions/index.js'; +import { Image } from './image/index.js'; +import { Placeholder } from './placeholder/index.js'; +import { Body } from './body/index.js'; +import { CloseButton } from './close/index.js'; +import options from 'options.js'; +import { Variable } from 'types/variable.js'; +import { filterNotifications } from 'lib/shared/notifications.js'; +import Scrollable from 'types/widgets/scrollable.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -const { displayedTotal } = options.notifications; +const { displayedTotal, ignore } = options.notifications; -const NotificationCard = (notifs: Notifications, curPage: Variable) => { +const NotificationCard = (notifs: Notifications, curPage: Variable): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", + vscroll: 'automatic', child: Widget.Box({ - class_name: "menu-content-container notifications", - hpack: "center", + class_name: 'menu-content-container notifications', + hpack: 'center', vexpand: true, spacing: 0, vertical: true, setup: (self) => { - Utils.merge([notifs.bind("notifications"), curPage.bind("value"), displayedTotal.bind("value")], (notifications, currentPage, dispTotal) => { - const sortedNotifications = notifications.sort( - (a, b) => b.time - a.time, - ); + Utils.merge( + [ + notifs.bind('notifications'), + curPage.bind('value'), + displayedTotal.bind('value'), + ignore.bind('value'), + ], + (notifications, currentPage, dispTotal, ignoredNotifs) => { + const filteredNotifications = filterNotifications(notifications, ignoredNotifs); - if (notifications.length <= 0) { - return (self.children = [Placeholder(notifs)]); - } + const sortedNotifications = filteredNotifications.sort((a, b) => b.time - a.time); - const pageStart = (currentPage - 1) * dispTotal; - const pageEnd = currentPage * dispTotal; - return (self.children = sortedNotifications.slice(pageStart, pageEnd).map((notif: Notification) => { - return Widget.Box({ - class_name: "notification-card-content-container", - children: [ - Widget.Box({ - class_name: "notification-card menu", - vpack: "start", - hexpand: true, - vexpand: false, + if (filteredNotifications.length <= 0) { + return (self.children = [Placeholder(notifs)]); + } + + const pageStart = (currentPage - 1) * dispTotal; + const pageEnd = currentPage * dispTotal; + return (self.children = sortedNotifications + .slice(pageStart, pageEnd) + .map((notif: Notification) => { + return Widget.Box({ + class_name: 'notification-card-content-container', children: [ - Image(notif), Widget.Box({ - vpack: "center", - vertical: true, + class_name: 'notification-card menu', + vpack: 'start', hexpand: true, - class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : " menu"}`, + vexpand: false, children: [ - Header(notif), - Body(notif), - Actions(notif, notifs), + Image(notif), + Widget.Box({ + vpack: 'center', + vertical: true, + hexpand: true, + class_name: `notification-card-content ${!notifHasImg(notif) ? 'noimg' : ' menu'}`, + children: [Header(notif), Body(notif), Actions(notif, notifs)], + }), ], }), + CloseButton(notif, notifs), ], - }), - CloseButton(notif, notifs), - ], - }); - })); - }); + }); + })); + }, + ); }, - }) + }), }); }; diff --git a/modules/menus/notifications/notification/placeholder/index.ts b/modules/menus/notifications/notification/placeholder/index.ts index 32b4473..8e53af9 100644 --- a/modules/menus/notifications/notification/placeholder/index.ts +++ b/modules/menus/notifications/notification/placeholder/index.ts @@ -1,24 +1,25 @@ -import { Notifications } from "types/service/notifications"; +import { BoxWidget } from 'lib/types/widget'; +import { Notifications } from 'types/service/notifications'; -const Placeholder = (notifs: Notifications) => { +const Placeholder = (notifs: Notifications): BoxWidget => { return Widget.Box({ - class_name: "notification-label-container", - vpack: "fill", - hpack: "center", + class_name: 'notification-label-container', + vpack: 'fill', + hpack: 'center', expand: true, child: Widget.Box({ - vpack: "center", + vpack: 'center', vertical: true, expand: true, children: [ Widget.Label({ - vpack: "center", - class_name: "placeholder-label dim bell", - label: notifs.bind("dnd").as((dnd) => (dnd ? "󰂛" : "󰂚")), + vpack: 'center', + class_name: 'placeholder-label dim bell', + label: notifs.bind('dnd').as((dnd) => (dnd ? '󰂛' : '󰂚')), }), Widget.Label({ - vpack: "start", - class_name: "placehold-label dim message", + vpack: 'start', + class_name: 'placehold-label dim message', label: "You're all caught up :)", }), ], diff --git a/modules/menus/notifications/pager/index.ts b/modules/menus/notifications/pager/index.ts index 92dceb7..62dd1bf 100644 --- a/modules/menus/notifications/pager/index.ts +++ b/modules/menus/notifications/pager/index.ts @@ -1,74 +1,88 @@ -const notifs = await Service.import("notifications"); +const notifs = await Service.import('notifications'); -import options from "options"; -import { Variable } from "types/variable"; +import { BoxWidget } from 'lib/types/widget'; +import options from 'options'; +import { Notification } from 'types/service/notifications'; +import { Variable } from 'types/variable'; const { displayedTotal } = options.notifications; +const { show: showPager } = options.theme.bar.menus.menu.notifications.pager; -export const NotificationPager = (curPage: Variable) => { +export const NotificationPager = (curPage: Variable): BoxWidget => { return Widget.Box({ - class_name: "notification-menu-pager", + class_name: 'notification-menu-pager', hexpand: true, vexpand: false, - children: Utils.merge([curPage.bind("value"), displayedTotal.bind("value"), notifs.bind("notifications")], (currentPage, dispTotal, notifications) => { - return [ - Widget.Button({ - hexpand: true, - hpack: "start", - class_name: `pager-button left ${currentPage <= 1 ? "disabled" : ""}`, - onPrimaryClick: () => { - curPage.value = 1; - }, - child: Widget.Label({ - className: "pager-button-label", - label: "" + children: Utils.merge( + [ + curPage.bind('value'), + displayedTotal.bind('value'), + notifs.bind('notifications'), + showPager.bind('value'), + ], + (currentPage: number, dispTotal: number, _: Notification[], showPgr: boolean) => { + if (showPgr === false) { + return []; + } + return [ + Widget.Button({ + hexpand: true, + hpack: 'start', + class_name: `pager-button left ${currentPage <= 1 ? 'disabled' : ''}`, + onPrimaryClick: () => { + curPage.value = 1; + }, + child: Widget.Label({ + className: 'pager-button-label', + label: '', + }), }), - }), - Widget.Button({ - hexpand: true, - hpack: "start", - class_name: `pager-button left ${currentPage <= 1 ? "disabled" : ""}`, - onPrimaryClick: () => { - curPage.value = currentPage <= 1 ? 1 : currentPage - 1; - }, - child: Widget.Label({ - className: "pager-button-label", - label: "" + Widget.Button({ + hexpand: true, + hpack: 'start', + class_name: `pager-button left ${currentPage <= 1 ? 'disabled' : ''}`, + onPrimaryClick: () => { + curPage.value = currentPage <= 1 ? 1 : currentPage - 1; + }, + child: Widget.Label({ + className: 'pager-button-label', + label: '', + }), }), - }), - Widget.Label({ - hexpand: true, - hpack: "center", - class_name: "pager-label", - label: `${currentPage} / ${Math.ceil(notifs.notifications.length / dispTotal) || 1}` - }), - Widget.Button({ - hexpand: true, - hpack: "end", - class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? "disabled" : ""}`, - onPrimaryClick: () => { - const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value); - curPage.value = currentPage >= maxPage ? currentPage : currentPage + 1; - }, - child: Widget.Label({ - className: "pager-button-label", - label: "" + Widget.Label({ + hexpand: true, + hpack: 'center', + class_name: 'pager-label', + label: `${currentPage} / ${Math.ceil(notifs.notifications.length / dispTotal) || 1}`, }), - }), - Widget.Button({ - hexpand: true, - hpack: "end", - class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? "disabled" : ""}`, - onPrimaryClick: () => { - const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value); - curPage.value = maxPage; - }, - child: Widget.Label({ - className: "pager-button-label", - label: "󰄾" + Widget.Button({ + hexpand: true, + hpack: 'end', + class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? 'disabled' : ''}`, + onPrimaryClick: () => { + const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value); + curPage.value = currentPage >= maxPage ? currentPage : currentPage + 1; + }, + child: Widget.Label({ + className: 'pager-button-label', + label: '', + }), }), - }), - ] - }) - }) -} + Widget.Button({ + hexpand: true, + hpack: 'end', + class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? 'disabled' : ''}`, + onPrimaryClick: () => { + const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value); + curPage.value = maxPage; + }, + child: Widget.Label({ + className: 'pager-button-label', + label: '󰄾', + }), + }), + ]; + }, + ), + }); +}; diff --git a/modules/menus/notifications/utils.ts b/modules/menus/notifications/utils.ts index f050165..dcf2c7d 100644 --- a/modules/menus/notifications/utils.ts +++ b/modules/menus/notifications/utils.ts @@ -1,7 +1,7 @@ -import { Notification } from "types/service/notifications"; +import { Notification } from 'types/service/notifications'; -const notifHasImg = (notif: Notification) => { - return notif.image !== undefined && notif.image.length; +const notifHasImg = (notif: Notification): boolean => { + return notif.image !== undefined && notif.image.length ? true : false; }; export { notifHasImg }; diff --git a/modules/menus/power/helpers/actions.ts b/modules/menus/power/helpers/actions.ts index 74216fc..fee7dfa 100644 --- a/modules/menus/power/helpers/actions.ts +++ b/modules/menus/power/helpers/actions.ts @@ -1,5 +1,5 @@ -import { Action } from "lib/types/power"; -import options from "options"; +import { Action } from 'lib/types/power'; +import options from 'options'; const { sleep, reboot, logout, shutdown } = options.menus.dashboard.powermenu; class PowerMenu extends Service { @@ -8,50 +8,50 @@ class PowerMenu extends Service { this, {}, { - title: ["string"], - cmd: ["string"], + title: ['string'], + cmd: ['string'], }, ); } - #title = ""; - #cmd = ""; + #title = ''; + #cmd = ''; - get title() { + get title(): string { return this.#title; } - action(action: Action) { + action(action: Action): void { [this.#cmd, this.#title] = { - sleep: [sleep.value, "Sleep"], - reboot: [reboot.value, "Reboot"], - logout: [logout.value, "Log Out"], - shutdown: [shutdown.value, "Shutdown"], + sleep: [sleep.value, 'Sleep'], + reboot: [reboot.value, 'Reboot'], + logout: [logout.value, 'Log Out'], + shutdown: [shutdown.value, 'Shutdown'], }[action]; - this.notify("cmd"); - this.notify("title"); - this.emit("changed"); - App.closeWindow("powermenu"); - App.openWindow("verification"); + this.notify('cmd'); + this.notify('title'); + this.emit('changed'); + App.closeWindow('powermenu'); + App.openWindow('verification'); } - customAction(action: Action, cmnd: string) { + customAction(action: Action, cmnd: string): void { [this.#cmd, this.#title] = [cmnd, action]; - this.notify("cmd"); - this.notify("title"); - this.emit("changed"); - App.closeWindow("powermenu"); - App.openWindow("verification"); + this.notify('cmd'); + this.notify('title'); + this.emit('changed'); + App.closeWindow('powermenu'); + App.openWindow('verification'); } - shutdown = () => { - this.action("shutdown"); + shutdown = (): void => { + this.action('shutdown'); }; - exec = () => { - App.closeWindow("verification"); + exec = (): void => { + App.closeWindow('verification'); Utils.execAsync(this.#cmd); }; } diff --git a/modules/menus/power/index.ts b/modules/menus/power/index.ts index aa9065e..394cd77 100644 --- a/modules/menus/power/index.ts +++ b/modules/menus/power/index.ts @@ -1,15 +1,17 @@ -import { Action } from "lib/types/power.js"; -import PopupWindow from "../PopupWindow.js"; -import powermenu from "./helpers/actions.js"; -import icons from "../../icons/index.js"; +import { Action } from 'lib/types/power.js'; +import PopupWindow from '../shared/popup/index.js'; +import powermenu from './helpers/actions.js'; +import icons from '../../icons/index.js'; +import Window from 'types/widgets/window.js'; +import { Attribute, Child, GButton } from 'lib/types/widget.js'; -const SysButton = (action: Action, label: string) => +const SysButton = (action: Action, label: string): GButton => Widget.Button({ class_name: `widget-button powermenu-button-${action}`, on_clicked: () => powermenu.action(action), child: Widget.Box({ vertical: true, - class_name: "system-button widget-box", + class_name: 'system-button widget-box', children: [ Widget.Icon({ class_name: `system-button_icon ${action}`, @@ -22,17 +24,17 @@ const SysButton = (action: Action, label: string) => ], }), }); -export default () => +export default (): Window => PopupWindow({ - name: "powermenu", - transition: "crossfade", + name: 'powermenu', + transition: 'crossfade', child: Widget.Box({ - class_name: "powermenu horizontal", + class_name: 'powermenu horizontal', children: [ - SysButton("shutdown", "SHUTDOWN"), - SysButton("logout", "LOG OUT"), - SysButton("reboot", "REBOOT"), - SysButton("sleep", "SLEEP"), + SysButton('shutdown', 'SHUTDOWN'), + SysButton('logout', 'LOG OUT'), + SysButton('reboot', 'REBOOT'), + SysButton('sleep', 'SLEEP'), ], }), }); diff --git a/modules/menus/power/verification.ts b/modules/menus/power/verification.ts index 6cdfc8e..a71f46b 100644 --- a/modules/menus/power/verification.ts +++ b/modules/menus/power/verification.ts @@ -1,52 +1,54 @@ -import PopupWindow from "../PopupWindow.js"; -import powermenu from "./helpers/actions.js"; +import Window from 'types/widgets/window.js'; +import PopupWindow from '../shared/popup/index.js'; +import powermenu from './helpers/actions.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export default () => - PopupWindow({ - name: "verification", - transition: "crossfade", - child: Widget.Box({ - class_name: "verification", - child: Widget.Box({ - class_name: "verification-content", - expand: true, - vertical: true, - children: [ - Widget.Box({ - class_name: "text-box", - vertical: true, - children: [ - Widget.Label({ - class_name: "title", - label: powermenu.bind("title").as((t) => t.toUpperCase()), - }), - Widget.Label({ - class_name: "desc", - label: powermenu - .bind("title") - .as((p) => `Are you sure you want to ${p.toLowerCase()}?`), - }), - ], - }), - Widget.Box({ - class_name: "buttons horizontal", - vexpand: true, - vpack: "end", - homogeneous: true, - children: [ - Widget.Button({ - class_name: "verification-button bar-verification_yes", - child: Widget.Label("Yes"), - on_clicked: powermenu.exec, - }), - Widget.Button({ - class_name: "verification-button bar-verification_no", - child: Widget.Label("No"), - on_clicked: () => App.toggleWindow("verification"), - }), - ], - }), - ], - }), - }), - }); +export default (): Window => + PopupWindow({ + name: 'verification', + transition: 'crossfade', + child: Widget.Box({ + class_name: 'verification', + child: Widget.Box({ + class_name: 'verification-content', + expand: true, + vertical: true, + children: [ + Widget.Box({ + class_name: 'text-box', + vertical: true, + children: [ + Widget.Label({ + class_name: 'title', + label: powermenu.bind('title').as((t) => t.toUpperCase()), + }), + Widget.Label({ + class_name: 'desc', + label: powermenu + .bind('title') + .as((p) => `Are you sure you want to ${p.toLowerCase()}?`), + }), + ], + }), + Widget.Box({ + class_name: 'buttons horizontal', + vexpand: true, + vpack: 'end', + homogeneous: true, + children: [ + Widget.Button({ + class_name: 'verification-button bar-verification_yes', + child: Widget.Label('Yes'), + on_clicked: powermenu.exec, + }), + Widget.Button({ + class_name: 'verification-button bar-verification_no', + child: Widget.Label('No'), + on_clicked: () => App.toggleWindow('verification'), + }), + ], + }), + ], + }), + }), + }); diff --git a/modules/menus/powerDropdown/button.ts b/modules/menus/powerDropdown/button.ts index ecdb85b..f2ae20f 100644 --- a/modules/menus/powerDropdown/button.ts +++ b/modules/menus/powerDropdown/button.ts @@ -1,44 +1,52 @@ -import icons from "lib/icons"; -import { PowerOptions } from "lib/types/options"; -import options from "options"; -import powermenu from "../power/helpers/actions"; +import { PowerOptions } from 'lib/types/options'; +import options from 'options'; +import powermenu from '../power/helpers/actions'; +import { GButton } from 'lib/types/widget'; const { confirmation, shutdown, logout, sleep, reboot, showLabel } = options.menus.power; -export const PowerButton = (action: PowerOptions) => { - const handleClick = (action: PowerOptions) => { +export const PowerButton = (action: PowerOptions): GButton => { + const handleClick = (action: PowerOptions): void => { const actions = { shutdown: shutdown.value, reboot: reboot.value, logout: logout.value, sleep: sleep.value, }; - App.closeWindow("powerdropdownmenu"); + App.closeWindow('powerdropdownmenu'); if (!confirmation.value) { - Utils.execAsync(actions[action]) - .catch((err) => console.error(`Failed to execute ${action} command. Error: ${err}`)); + Utils.execAsync(actions[action]).catch((err) => + console.error(`Failed to execute ${action} command. Error: ${err}`), + ); } else { powermenu.customAction(action, actions[action]); } }; + const powerIconMap = { + shutdown: '󰐥', + reboot: '󰜉', + logout: '󰿅', + sleep: '󰤄', + }; + return Widget.Button({ - className: showLabel.bind("value").as(shwLbl => { - return `power-menu-button ${action} ${!shwLbl ? "no-label" : ""}`; + className: showLabel.bind('value').as((shwLbl) => { + return `power-menu-button ${action} ${!shwLbl ? 'no-label' : ''}`; }), on_clicked: () => handleClick(action), child: Widget.Box({ vertical: false, - children: showLabel.bind("value").as(shwLbl => { + children: showLabel.bind('value').as((shwLbl) => { if (shwLbl) { return [ - Widget.Icon({ - icon: icons.powermenu[action], - className: `power-button-icon ${action}-icon`, + Widget.Label({ + label: powerIconMap[action], + className: `power-button-icon ${action}-icon txt-icon`, }), Widget.Label({ - hpack: "center", + hpack: 'center', hexpand: true, label: action.charAt(0).toUpperCase() + action.slice(1), className: `power-button-label ${action}-label show-label`, @@ -46,12 +54,12 @@ export const PowerButton = (action: PowerOptions) => { ]; } return [ - Widget.Icon({ - icon: icons.powermenu[action], - className: `power-button-icon ${action}-icon no-label`, + Widget.Label({ + label: powerIconMap[action], + className: `power-button-icon ${action}-icon no-label txt-icon`, }), ]; }), - }) + }), }); }; diff --git a/modules/menus/powerDropdown/index.ts b/modules/menus/powerDropdown/index.ts index ec56353..8ebfee4 100644 --- a/modules/menus/powerDropdown/index.ts +++ b/modules/menus/powerDropdown/index.ts @@ -1,27 +1,20 @@ -import options from "options.js"; -import DropdownMenu from "../DropdownMenu.js"; -import { PowerButton } from "./button.js"; +import Window from 'types/widgets/window.js'; +import DropdownMenu from '../shared/dropdown/index.js'; +import { PowerButton } from './button.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -const { showLabel } = options.menus.power; - -export default () => { +export default (): Window => { return DropdownMenu({ - name: "powerdropdownmenu", - transition: "crossfade", + name: 'powerdropdownmenu', + transition: 'crossfade', child: Widget.Box({ - class_name: "menu-items power-dropdown", + class_name: 'menu-items power-dropdown', child: Widget.Box({ vertical: true, hexpand: true, - class_name: "menu-items-container power-dropdown", - children: [ - PowerButton('shutdown'), - PowerButton('reboot'), - PowerButton('logout'), - PowerButton('sleep'), - ], + class_name: 'menu-items-container power-dropdown', + children: [PowerButton('shutdown'), PowerButton('reboot'), PowerButton('logout'), PowerButton('sleep')], }), }), }); }; - diff --git a/modules/menus/shared/dropdown/eventBoxes/index.ts b/modules/menus/shared/dropdown/eventBoxes/index.ts new file mode 100644 index 0000000..727610d --- /dev/null +++ b/modules/menus/shared/dropdown/eventBoxes/index.ts @@ -0,0 +1,33 @@ +import { Attribute, BoxWidget } from 'lib/types/widget'; +import EventBox from 'types/widgets/eventbox'; +import { BarLocation } from 'lib/types/options'; + +const createEventBox = (className: string, windowName: string): EventBox => { + return Widget.EventBox({ + class_name: className, + hexpand: true, + vexpand: false, + can_focus: false, + child: Widget.Box(), + setup: (w) => { + w.on('button-press-event', () => App.toggleWindow(windowName)); + }, + }); +}; + +export const barEventMargins = ( + windowName: string, + location: BarLocation = 'top', +): [EventBox, EventBox] => { + if (location === 'top') { + return [ + createEventBox('mid-eb event-top-padding-static', windowName), + createEventBox('mid-eb event-top-padding', windowName), + ]; + } else { + return [ + createEventBox('mid-eb event-bottom-padding', windowName), + createEventBox('mid-eb event-bottom-padding-static', windowName), + ]; + } +}; diff --git a/modules/menus/shared/dropdown/index.ts b/modules/menus/shared/dropdown/index.ts new file mode 100644 index 0000000..56bd97b --- /dev/null +++ b/modules/menus/shared/dropdown/index.ts @@ -0,0 +1,99 @@ +import options from 'options'; +import { DropdownMenuProps } from 'lib/types/dropdownmenu'; +import { Attribute, Child, Exclusivity } from 'lib/types/widget'; +import Window from 'types/widgets/window'; +import { moveBoxToCursor } from './locationHandler/index'; +import { barEventMargins } from './eventBoxes/index'; + +const { location } = options.theme.bar; + +// NOTE: We make the window visible for 2 seconds (on startup) so the child +// elements can allocat their proper dimensions. +// Otherwise the width that we rely on for menu positioning is set improperly +// for the first time we open a menu of each type. +const initRender = Variable(true); + +setTimeout(() => { + initRender.value = false; +}, 2000); + +export default ({ + name, + child, + transition, + exclusivity = 'ignore' as Exclusivity, + fixed = false, + ...props +}: DropdownMenuProps): Window => + Widget.Window({ + name, + class_names: [name, 'dropdown-menu'], + setup: (w) => w.keybind('Escape', () => App.closeWindow(name)), + visible: initRender.bind('value'), + keymode: 'on-demand', + exclusivity, + layer: 'top', + anchor: location.bind('value').as((ln) => [ln, 'left']), + child: Widget.EventBox({ + class_name: 'parent-event', + on_primary_click: () => App.closeWindow(name), + on_secondary_click: () => App.closeWindow(name), + child: Widget.Box({ + class_name: 'top-eb', + vertical: true, + children: [ + Widget.Box({ + className: 'event-box-container', + children: location.bind('value').as((lcn) => { + if (lcn === 'top') { + return barEventMargins(name); + } else { + return []; + } + }), + }), + Widget.EventBox({ + class_name: 'in-eb menu-event-box', + on_primary_click: () => { + return true; + }, + on_secondary_click: () => { + return true; + }, + setup: (self) => { + moveBoxToCursor(self, fixed); + }, + child: Widget.Box({ + class_name: 'dropdown-menu-container', + css: 'padding: 1px; margin: -1px;', + child: Widget.Revealer({ + revealChild: false, + setup: (self) => + self.hook(App, (_, wname, visible) => { + if (wname === name) self.reveal_child = visible; + }), + transition, + transitionDuration: 350, + child: Widget.Box({ + class_name: 'dropdown-menu-container', + can_focus: true, + children: [child], + }), + }), + }), + }), + Widget.Box({ + className: 'event-box-container', + children: location.bind('value').as((lcn) => { + if (lcn === 'bottom') { + return barEventMargins(name); + } else { + return []; + } + }), + }), + ], + }), + }), + ...props, + }); diff --git a/modules/menus/shared/dropdown/locationHandler/index.ts b/modules/menus/shared/dropdown/locationHandler/index.ts new file mode 100644 index 0000000..71752a7 --- /dev/null +++ b/modules/menus/shared/dropdown/locationHandler/index.ts @@ -0,0 +1,95 @@ +const hyprland = await Service.import('hyprland'); + +import { bash } from 'lib/utils'; +import { Widget as TWidget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs'; +import { Monitor } from 'types/service/hyprland'; +import Box from 'types/widgets/box'; +import EventBox from 'types/widgets/eventbox'; +import Revealer from 'types/widgets/revealer'; + +type NestedRevealer = Revealer, unknown>; +type NestedBox = Box; +type NestedEventBox = EventBox; + +const { location } = options.theme.bar; + +export const moveBoxToCursor = (self: T, fixed: boolean): void => { + if (fixed) { + return; + } + + globalMousePos.connect('changed', async ({ value }) => { + const curHyprlandMonitor = hyprland.monitors.find((m) => m.id === hyprland.active.monitor.id); + const dropdownWidth = self.child.get_allocation().width; + const dropdownHeight = self.child.get_allocation().height; + + let hyprScaling = 1; + try { + const monitorInfo = await bash('hyprctl monitors -j'); + const parsedMonitorInfo = JSON.parse(monitorInfo); + + const foundMonitor = parsedMonitorInfo.find( + (monitor: Monitor) => monitor.id === hyprland.active.monitor.id, + ); + hyprScaling = foundMonitor?.scale || 1; + } catch (error) { + console.error(`Error parsing hyprland monitors: ${error}`); + } + + let monWidth = curHyprlandMonitor?.width; + let monHeight = curHyprlandMonitor?.height; + + if (monWidth === undefined || monHeight === undefined || hyprScaling === undefined) { + return; + } + + // If GDK Scaling is applied, then get divide width by scaling + // to get the proper coordinates. + // Ex: On a 2860px wide monitor... if scaling is set to 2, then the right + // end of the monitor is the 1430th pixel. + const gdkScale = Utils.exec('bash -c "echo $GDK_SCALE"'); + + if (/^\d+(.\d+)?$/.test(gdkScale)) { + const scale = parseFloat(gdkScale); + monWidth = monWidth / scale; + monHeight = monHeight / scale; + } else { + monWidth = monWidth / hyprScaling; + monHeight = monHeight / hyprScaling; + } + + // If monitor is vertical (transform = 1 || 3) swap height and width + const isVertical = curHyprlandMonitor?.transform !== undefined ? curHyprlandMonitor.transform % 2 !== 0 : false; + + if (isVertical) { + [monWidth, monHeight] = [monHeight, monWidth]; + } + + let marginRight = monWidth - dropdownWidth / 2; + marginRight = fixed ? marginRight - monWidth / 2 : marginRight - value[0]; + let marginLeft = monWidth - dropdownWidth - marginRight; + + const minimumMargin = 0; + + if (marginRight < minimumMargin) { + marginRight = minimumMargin; + marginLeft = monWidth - dropdownWidth - minimumMargin; + } + + if (marginLeft < minimumMargin) { + marginLeft = minimumMargin; + marginRight = monWidth - dropdownWidth - minimumMargin; + } + + self.set_margin_left(marginLeft); + self.set_margin_right(marginRight); + + if (location.value === 'top') { + self.set_margin_top(0); + self.set_margin_bottom(monHeight); + } else { + self.set_margin_bottom(0); + self.set_margin_top(monHeight - dropdownHeight); + } + }); +}; diff --git a/modules/menus/PopupWindow.ts b/modules/menus/shared/popup/index.ts similarity index 65% rename from modules/menus/PopupWindow.ts rename to modules/menus/shared/popup/index.ts index af26df4..659c12a 100644 --- a/modules/menus/PopupWindow.ts +++ b/modules/menus/shared/popup/index.ts @@ -1,23 +1,32 @@ -import { Exclusivity, Transition } from "lib/types/widget"; +import { WINDOW_LAYOUTS } from 'globals/window'; +import { LayoutFunction, Layouts, PopupWindowProps } from 'lib/types/popupwindow'; +import { Attribute, Child, Exclusivity, GtkWidget, Transition } from 'lib/types/widget'; +import Box from 'types/widgets/box'; +import EventBox from 'types/widgets/eventbox'; +import Window from 'types/widgets/window'; type Opts = { - className: string - vexpand: boolean -} + className: string; + vexpand: boolean; +}; -export const Padding = (name: string, opts: Opts) => +export const Padding = (name: string, opts: Opts): EventBox, unknown> => Widget.EventBox({ - class_name: opts?.className || "", + class_name: opts?.className || '', hexpand: true, - vexpand: typeof opts?.vexpand === "boolean" ? opts.vexpand : true, + vexpand: typeof opts?.vexpand === 'boolean' ? opts.vexpand : true, can_focus: false, child: Widget.Box(), - setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)), + setup: (w) => w.on('button-press-event', () => App.toggleWindow(name)), }); -const PopupRevealer = (name: string, child: any, transition = "slide_down" as Transition) => +const PopupRevealer = ( + name: string, + child: GtkWidget, + transition = 'slide_down' as Transition, +): Box => Widget.Box( - { css: "padding: 1px;" }, + { css: 'padding: 1px;' }, Widget.Revealer({ transition, child: Widget.Box({ @@ -32,7 +41,7 @@ const PopupRevealer = (name: string, child: any, transition = "slide_down" as Tr }), ); -const Layout = (name: string, child: any, transition: Transition) => ({ +const Layout: LayoutFunction = (name: string, child: GtkWidget, transition: Transition) => ({ center: () => Widget.CenterBox( {}, @@ -49,14 +58,10 @@ const Layout = (name: string, child: any, transition: Transition) => ({ Widget.CenterBox( {}, Padding(name, {} as Opts), - Widget.Box( - { vertical: true }, - PopupRevealer(name, child, transition), - Padding(name, {} as Opts), - ), + Widget.Box({ vertical: true }, PopupRevealer(name, child, transition), Padding(name, {} as Opts)), Padding(name, {} as Opts), ), - "top-right": () => + 'top-right': () => Widget.Box( {}, Padding(name, {} as Opts), @@ -67,13 +72,13 @@ const Layout = (name: string, child: any, transition: Transition) => ({ }, Padding(name, { vexpand: false, - className: "event-top-padding", + className: 'event-top-padding', }), PopupRevealer(name, child, transition), Padding(name, {} as Opts), ), ), - "top-center": () => + 'top-center': () => Widget.Box( {}, Padding(name, {} as Opts), @@ -84,14 +89,14 @@ const Layout = (name: string, child: any, transition: Transition) => ({ }, Padding(name, { vexpand: false, - className: "event-top-padding", + className: 'event-top-padding', }), PopupRevealer(name, child, transition), Padding(name, {} as Opts), ), Padding(name, {} as Opts), ), - "top-left": () => + 'top-left': () => Widget.Box( {}, Widget.Box( @@ -101,14 +106,14 @@ const Layout = (name: string, child: any, transition: Transition) => ({ }, Padding(name, { vexpand: false, - className: "event-top-padding", + className: 'event-top-padding', }), PopupRevealer(name, child, transition), Padding(name, {} as Opts), ), Padding(name, {} as Opts), ), - "bottom-left": () => + 'bottom-left': () => Widget.Box( {}, Widget.Box( @@ -121,7 +126,7 @@ const Layout = (name: string, child: any, transition: Transition) => ({ ), Padding(name, {} as Opts), ), - "bottom-center": () => + 'bottom-center': () => Widget.Box( {}, Padding(name, {} as Opts), @@ -135,7 +140,7 @@ const Layout = (name: string, child: any, transition: Transition) => ({ ), Padding(name, {} as Opts), ), - "bottom-right": () => + 'bottom-right': () => Widget.Box( {}, Padding(name, {} as Opts), @@ -150,23 +155,32 @@ const Layout = (name: string, child: any, transition: Transition) => ({ ), }); +const isValidLayout = (layout: string): layout is Layouts => { + return WINDOW_LAYOUTS.includes(layout); +}; + export default ({ name, child, - layout = "center", + layout = 'center', transition, - exclusivity = "ignore" as Exclusivity, + exclusivity = 'ignore' as Exclusivity, ...props -}) => - Widget.Window({ +}: PopupWindowProps): Window => { + const layoutFn = isValidLayout(layout) ? layout : 'center'; + + const layoutWidget = Layout(name, child, transition)[layoutFn](); + + return Widget.Window({ name, - class_names: [name, "popup-window"], - setup: (w) => w.keybind("Escape", () => App.closeWindow(name)), + class_names: [name, 'popup-window'], + setup: (w) => w.keybind('Escape', () => App.closeWindow(name)), visible: false, - keymode: "on-demand", + keymode: 'on-demand', exclusivity, - layer: "top", - anchor: ["top", "bottom", "right", "left"], - child: Layout(name, child, transition)[layout](), + layer: 'top', + anchor: ['top', 'bottom', 'right', 'left'], + child: layoutWidget, ...props, }); +}; diff --git a/modules/notifications/actions/index.ts b/modules/notifications/actions/index.ts index 85e3eb9..402f1d9 100644 --- a/modules/notifications/actions/index.ts +++ b/modules/notifications/actions/index.ts @@ -1,35 +1,37 @@ -import { Notification, Notifications } from "types/service/notifications"; +import { Attribute, Child } from 'lib/types/widget'; +import { Notification, Notifications } from 'types/service/notifications'; +import Box from 'types/widgets/box'; -const Action = (notif: Notification, notifs: Notifications) => { +const Action = (notif: Notification, notifs: Notifications): Box => { if (notif.actions !== undefined && notif.actions.length > 0) { return Widget.Box({ - class_name: "notification-card-actions", + class_name: 'notification-card-actions', hexpand: true, - vpack: "end", + vpack: 'end', children: notif.actions.map((action) => { return Widget.Button({ hexpand: true, - class_name: "notification-action-buttons", + class_name: 'notification-action-buttons', on_primary_click: () => { - if (action.id.includes("scriptAction:-")) { - Utils.execAsync( - `${action.id.replace("scriptAction:-", "")}`, - ).catch((err) => console.error(err)); + if (action.id.includes('scriptAction:-')) { + Utils.execAsync(`${action.id.replace('scriptAction:-', '')}`).catch((err) => + console.error(err), + ); notifs.CloseNotification(notif.id); } else { notif.invoke(action.id); } }, child: Widget.Box({ - hpack: "center", + hpack: 'center', hexpand: true, children: [ Widget.Label({ - class_name: "notification-action-buttons-label", + class_name: 'notification-action-buttons-label', hexpand: true, label: action.label, max_width_chars: 15, - truncate: "end", + truncate: 'end', wrap: true, }), ], diff --git a/modules/notifications/body/index.ts b/modules/notifications/body/index.ts index 0369615..92fa7a7 100644 --- a/modules/notifications/body/index.ts +++ b/modules/notifications/body/index.ts @@ -1,23 +1,25 @@ -import { Notification } from "types/service/notifications"; -import { notifHasImg } from "../../menus/notifications/utils.js"; +import { Notification } from 'types/service/notifications'; +import { notifHasImg } from '../../menus/notifications/utils.js'; +import Box from 'types/widgets/box.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -export const Body = (notif: Notification) => { +export const Body = (notif: Notification): Box => { return Widget.Box({ - vpack: "start", + vpack: 'start', hexpand: true, - class_name: "notification-card-body", + class_name: 'notification-card-body', children: [ Widget.Label({ hexpand: true, use_markup: true, xalign: 0, - justification: "left", - truncate: "end", + justification: 'left', + truncate: 'end', lines: 2, max_width_chars: !notifHasImg(notif) ? 35 : 28, wrap: true, - class_name: "notification-card-body-label", - label: notif["body"], + class_name: 'notification-card-body-label', + label: notif['body'], }), ], }); diff --git a/modules/notifications/close/index.ts b/modules/notifications/close/index.ts index 7e067f9..d7a0d6e 100644 --- a/modules/notifications/close/index.ts +++ b/modules/notifications/close/index.ts @@ -1,15 +1,18 @@ -import { Notification, Notifications } from "types/service/notifications"; +import { Attribute, Child } from 'lib/types/widget'; +import { Notification, Notifications } from 'types/service/notifications'; +import Button from 'types/widgets/button'; +import Label from 'types/widgets/label'; -export const CloseButton = (notif: Notification, notifs: Notifications) => { +export const CloseButton = (notif: Notification, notifs: Notifications): Button, Attribute> => { return Widget.Button({ - class_name: "close-notification-button", + class_name: 'close-notification-button', on_primary_click: () => { notifs.CloseNotification(notif.id); }, child: Widget.Label({ - class_name: "txt-icon notif-close", - label: "󰅜", - hpack: "center", + class_name: 'txt-icon notif-close', + label: '󰅜', + hpack: 'center', }), }); }; diff --git a/modules/notifications/header/icon.ts b/modules/notifications/header/icon.ts index 7f53111..5a02ca2 100644 --- a/modules/notifications/header/icon.ts +++ b/modules/notifications/header/icon.ts @@ -1,32 +1,23 @@ -import icons from "../../icons/index.js"; +import { Notification } from 'types/service/notifications.js'; +import { getNotificationIcon } from 'globals/notification.js'; +import Box from 'types/widgets/box'; +import { Attribute, Child } from 'lib/types/widget'; -const NotificationIcon = ({ app_entry, app_icon, app_name }) => { - let icon = icons.fallback.notification; - - if ( - Utils.lookUpIcon(app_name) || - Utils.lookUpIcon(app_name.toLowerCase() || "") - ) - icon = Utils.lookUpIcon(app_name) - ? app_name - : Utils.lookUpIcon(app_name.toLowerCase()) - ? app_name.toLowerCase() - : ""; - - if (Utils.lookUpIcon(app_icon) && icon === "") icon = app_icon; - - if (Utils.lookUpIcon(app_entry || "") && icon === "") icon = app_entry || ""; - - return Widget.Box({ - css: ` +const NotificationIcon = ({ + app_entry = '', + app_icon = '', + app_name = '', +}: Partial): Box => { + return Widget.Box({ + css: ` min-width: 2rem; min-height: 2rem; `, - child: Widget.Icon({ - class_name: "notification-icon", - icon, - }), - }); + child: Widget.Icon({ + class_name: 'notification-icon', + icon: getNotificationIcon(app_name, app_icon, app_entry), + }), + }); }; export { NotificationIcon }; diff --git a/modules/notifications/header/index.ts b/modules/notifications/header/index.ts index 07b94c1..71d038d 100644 --- a/modules/notifications/header/index.ts +++ b/modules/notifications/header/index.ts @@ -1,51 +1,53 @@ -import GLib from "gi://GLib"; -import { notifHasImg } from "../../menus/notifications/utils.js"; -import { NotificationIcon } from "./icon.js"; -import { Notification } from "types/service/notifications"; -import options from "options.js"; +import GLib from 'gi://GLib'; +import { notifHasImg } from '../../menus/notifications/utils.js'; +import { NotificationIcon } from './icon.js'; +import { Notification } from 'types/service/notifications'; +import options from 'options.js'; +import Box from 'types/widgets/box.js'; +import { Attribute, Child } from 'lib/types/widget.js'; const { military } = options.menus.clock.time; -export const Header = (notif: Notification) => { - const time = (time: number, format = "%I:%M %p") => { - return GLib.DateTime.new_from_unix_local(time).format(military.value ? "%H:%M" : format); - } +export const Header = (notif: Notification): Box => { + const time = (time: number, format = '%I:%M %p'): string => { + return GLib.DateTime.new_from_unix_local(time).format(military.value ? '%H:%M' : format) || '--'; + }; return Widget.Box({ vertical: false, hexpand: true, children: [ Widget.Box({ - class_name: "notification-card-header", - hpack: "start", + class_name: 'notification-card-header', + hpack: 'start', children: [NotificationIcon(notif)], }), Widget.Box({ - class_name: "notification-card-header", + class_name: 'notification-card-header', hexpand: true, - hpack: "start", - vpack: "start", + hpack: 'start', + vpack: 'start', children: [ Widget.Label({ - class_name: "notification-card-header-label", - hpack: "start", + class_name: 'notification-card-header-label', + hpack: 'start', hexpand: true, vexpand: true, max_width_chars: !notifHasImg(notif) ? 30 : 19, - truncate: "end", + truncate: 'end', wrap: true, - label: notif["summary"], + label: notif['summary'], }), ], }), Widget.Box({ - class_name: "notification-card-header menu", - hpack: "end", - vpack: "start", + class_name: 'notification-card-header menu', + hpack: 'end', + vpack: 'start', hexpand: true, child: Widget.Label({ vexpand: true, - class_name: "notification-time", + class_name: 'notification-time', label: time(notif.time), }), }), diff --git a/modules/notifications/image/index.ts b/modules/notifications/image/index.ts index eb068a9..df1fded 100644 --- a/modules/notifications/image/index.ts +++ b/modules/notifications/image/index.ts @@ -1,17 +1,19 @@ -import { Notification } from "types/service/notifications"; -import { notifHasImg } from "../../menus/notifications/utils.js"; +import { Notification } from 'types/service/notifications'; +import { notifHasImg } from '../../menus/notifications/utils.js'; +import Box from 'types/widgets/box.js'; +import { Attribute, Child } from 'lib/types/widget.js'; -const Image = (notif: Notification) => { +const Image = (notif: Notification): Box => { if (notifHasImg(notif)) { return Widget.Box({ - class_name: "notification-card-image-container", - hpack: "center", - vpack: "center", + class_name: 'notification-card-image-container', + hpack: 'center', + vpack: 'center', vexpand: false, child: Widget.Box({ - hpack: "center", + hpack: 'center', vexpand: false, - class_name: "notification-card-image", + class_name: 'notification-card-image', css: `background-image: url("${notif.image}")`, }), }); diff --git a/modules/notifications/index.ts b/modules/notifications/index.ts index 4e742d9..7e86589 100644 --- a/modules/notifications/index.ts +++ b/modules/notifications/index.ts @@ -1,71 +1,79 @@ -const notifs = await Service.import("notifications"); -import options from "options"; -import { notifHasImg } from "../menus/notifications/utils.js"; -import { Image } from "./image/index.js"; -import { Action } from "./actions/index.js"; -import { Header } from "./header/index.js"; -import { Body } from "./body/index.js"; -import { CloseButton } from "./close/index.js"; -import { getPosition } from "lib/utils.js"; -const hyprland = await Service.import("hyprland"); - -const { position, timeout, cache_actions, monitor, active_monitor, displayedTotal } = options.notifications; +const notifs = await Service.import('notifications'); +import options from 'options'; +import { notifHasImg } from '../menus/notifications/utils.js'; +import { Image } from './image/index.js'; +import { Action } from './actions/index.js'; +import { Header } from './header/index.js'; +import { Body } from './body/index.js'; +import { CloseButton } from './close/index.js'; +import { getPosition } from 'lib/utils.js'; +import { filterNotifications } from 'lib/shared/notifications.js'; +import { Notification } from 'types/service/notifications.js'; +import Window from 'types/widgets/window.js'; +import Box from 'types/widgets/box.js'; +import { Attribute, Child } from 'lib/types/widget.js'; +const hyprland = await Service.import('hyprland'); +const { position, timeout, cache_actions, monitor, active_monitor, displayedTotal, ignore } = options.notifications; const curMonitor = Variable(monitor.value); -hyprland.active.connect("changed", () => { +hyprland.active.connect('changed', () => { curMonitor.value = hyprland.active.monitor.id; -}) +}); -export default () => { - Utils.merge([timeout.bind("value"), cache_actions.bind("value")], (timeout, doCaching) => { +export default (): Window, unknown> => { + Utils.merge([timeout.bind('value'), cache_actions.bind('value')], (timeout, doCaching) => { notifs.popupTimeout = timeout; notifs.cacheActions = doCaching; }); return Widget.Window({ - name: "notifications-window", - class_name: "notifications-window", - monitor: Utils.merge([ - curMonitor.bind("value"), - monitor.bind("value"), - active_monitor.bind("value")], (curMon, mon, activeMonitor) => { + name: 'notifications-window', + class_name: 'notifications-window', + monitor: Utils.merge( + [curMonitor.bind('value'), monitor.bind('value'), active_monitor.bind('value')], + (curMon, mon, activeMonitor) => { if (activeMonitor === true) { return curMon; } return mon; - } + }, ), - layer: "overlay", - anchor: position.bind("value").as(v => getPosition(v)), - exclusivity: "normal", + layer: options.tear.bind('value').as((tear) => (tear ? 'top' : 'overlay')), + anchor: position.bind('value').as((v) => getPosition(v)), + exclusivity: 'normal', child: Widget.Box({ - class_name: "notification-card-container", + class_name: 'notification-card-container', vertical: true, hexpand: true, setup: (self) => { - self.hook(notifs, () => { - return (self.children = notifs.popups.slice(0, displayedTotal.value).map((notif) => { - return Widget.Box({ - class_name: "notification-card", - vpack: "start", - hexpand: true, - children: [ - Image(notif), - Widget.Box({ - vpack: "start", - vertical: true, - hexpand: true, - class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : ""}`, - children: [Header(notif), Body(notif), Action(notif, notifs)], - }), - CloseButton(notif, notifs), - ], - }); - })); - }); + Utils.merge( + [notifs.bind('popups'), ignore.bind('value')], + (notifications: Notification[], ignoredNotifs: string[]) => { + const filteredNotifications = filterNotifications(notifications, ignoredNotifs); + + return (self.children = filteredNotifications.slice(0, displayedTotal.value).map((notif) => { + return Widget.Box({ + class_name: 'notification-card', + vpack: 'start', + hexpand: true, + children: [ + Image(notif), + Widget.Box({ + vpack: 'start', + vertical: true, + hexpand: true, + class_name: `notification-card-content ${!notifHasImg(notif) ? 'noimg' : ''}`, + children: [Header(notif), Body(notif), Action(notif, notifs)], + }), + CloseButton(notif, notifs), + ], + }); + })); + }, + ); }, }), }); diff --git a/modules/osd/bar/index.ts b/modules/osd/bar/index.ts index 208c73c..b73cda1 100644 --- a/modules/osd/bar/index.ts +++ b/modules/osd/bar/index.ts @@ -1,44 +1,89 @@ -import { OSDOrientation } from "lib/types/options"; -import brightness from "services/Brightness" -import options from "options" -const audio = await Service.import("audio") +import { OSDOrientation } from 'lib/types/options'; +import brightness from 'services/Brightness'; +import options from 'options'; +import Box from 'types/widgets/box'; +import { Attribute, Child } from 'lib/types/widget'; +const audio = await Service.import('audio'); -export const OSDBar = (ort: OSDOrientation) => { +export const OSDBar = (ort: OSDOrientation): Box => { return Widget.Box({ - class_name: "osd-bar-container", + class_name: 'osd-bar-container', children: [ Widget.LevelBar({ - class_name: "osd-bar", - vertical: ort === "vertical", - inverted: ort === "vertical", - bar_mode: "continuous", - setup: self => { - self.hook(brightness, () => { - self.class_names = self.class_names.filter(c => c !== "overflow"); - self.value = brightness.screen; - }, "notify::screen") - self.hook(brightness, () => { - self.class_names = self.class_names.filter(c => c !== "overflow"); - self.value = brightness.kbd; - }, "notify::kbd") - self.hook(audio.microphone, () => { - self.toggleClassName("overflow", audio.microphone.volume > 1) - self.value = audio.microphone.volume <= 1 ? audio.microphone.volume : audio.microphone.volume - 1; - }, "notify::volume") - self.hook(audio.microphone, () => { - self.toggleClassName("overflow", audio.microphone.volume > 1 && (!options.theme.osd.muted_zero.value || audio.microphone.is_muted === false)); - self.value = (options.theme.osd.muted_zero.value && audio.microphone.is_muted !== false) ? 0 : audio.microphone.volume <= 1 ? audio.microphone.volume : audio.microphone.volume - 1; - }, "notify::is-muted") - self.hook(audio.speaker, () => { - self.toggleClassName("overflow", audio.speaker.volume > 1) - self.value = audio.speaker.volume <= 1 ? audio.speaker.volume : audio.speaker.volume - 1; - }, "notify::volume") - self.hook(audio.speaker, () => { - self.toggleClassName("overflow", audio.speaker.volume > 1 && (!options.theme.osd.muted_zero.value || audio.speaker.is_muted === false)); - self.value = options.theme.osd.muted_zero.value && audio.speaker.is_muted !== false ? 0 : audio.speaker.volume <= 1 ? audio.speaker.volume : audio.speaker.volume - 1; - }, "notify::is-muted") - } - }) - ] + class_name: 'osd-bar', + vertical: ort === 'vertical', + inverted: ort === 'vertical', + bar_mode: 'continuous', + setup: (self) => { + self.hook( + brightness, + () => { + self.class_names = self.class_names.filter((c) => c !== 'overflow'); + self.value = brightness.screen; + }, + 'notify::screen', + ); + self.hook( + brightness, + () => { + self.class_names = self.class_names.filter((c) => c !== 'overflow'); + self.value = brightness.kbd; + }, + 'notify::kbd', + ); + self.hook( + audio.microphone, + () => { + self.toggleClassName('overflow', audio.microphone.volume > 1); + self.value = + audio.microphone.volume <= 1 ? audio.microphone.volume : audio.microphone.volume - 1; + }, + 'notify::volume', + ); + self.hook( + audio.microphone, + () => { + self.toggleClassName( + 'overflow', + audio.microphone.volume > 1 && + (!options.theme.osd.muted_zero.value || audio.microphone.is_muted === false), + ); + self.value = + options.theme.osd.muted_zero.value && audio.microphone.is_muted !== false + ? 0 + : audio.microphone.volume <= 1 + ? audio.microphone.volume + : audio.microphone.volume - 1; + }, + 'notify::is-muted', + ); + self.hook( + audio.speaker, + () => { + self.toggleClassName('overflow', audio.speaker.volume > 1); + self.value = audio.speaker.volume <= 1 ? audio.speaker.volume : audio.speaker.volume - 1; + }, + 'notify::volume', + ); + self.hook( + audio.speaker, + () => { + self.toggleClassName( + 'overflow', + audio.speaker.volume > 1 && + (!options.theme.osd.muted_zero.value || audio.speaker.is_muted === false), + ); + self.value = + options.theme.osd.muted_zero.value && audio.speaker.is_muted !== false + ? 0 + : audio.speaker.volume <= 1 + ? audio.speaker.volume + : audio.speaker.volume - 1; + }, + 'notify::is-muted', + ); + }, + }), + ], }); -} +}; diff --git a/modules/osd/icon/index.ts b/modules/osd/icon/index.ts index e991fe6..1682c91 100644 --- a/modules/osd/icon/index.ts +++ b/modules/osd/icon/index.ts @@ -1,37 +1,62 @@ -import { OSDOrientation } from "lib/types/options"; -import brightness from "services/Brightness" -const audio = await Service.import("audio") +import { Attribute, Child } from 'lib/types/widget'; +import brightness from 'services/Brightness'; +import Box from 'types/widgets/box'; +const audio = await Service.import('audio'); -export const OSDIcon = (ort: OSDOrientation) => { +export const OSDIcon = (): Box => { return Widget.Box({ - class_name: "osd-icon-container", + class_name: 'osd-icon-container', hexpand: true, child: Widget.Label({ - class_name: "osd-icon txt-icon", + class_name: 'osd-icon txt-icon', hexpand: true, vexpand: true, - hpack: "center", - vpack: "center", - setup: self => { - self.hook(brightness, () => { - self.label = "󱍖"; - }, "notify::screen") - self.hook(brightness, () => { - self.label = "󰥻"; - }, "notify::kbd") - self.hook(audio.microphone, () => { - self.label = audio.microphone.is_muted ? "󰍭" : "󰍬"; - }, "notify::volume") - self.hook(audio.microphone, () => { - self.label = audio.microphone.is_muted ? "󰍭" : "󰍬"; - }, "notify::is-muted") - self.hook(audio.speaker, () => { - self.label = audio.speaker.is_muted ? "󰝟" : "󰕾"; - }, "notify::volume") - self.hook(audio.speaker, () => { - self.label = audio.speaker.is_muted ? "󰝟" : "󰕾"; - }, "notify::is-muted") - } - }) + hpack: 'center', + vpack: 'center', + setup: (self) => { + self.hook( + brightness, + () => { + self.label = '󱍖'; + }, + 'notify::screen', + ); + self.hook( + brightness, + () => { + self.label = '󰥻'; + }, + 'notify::kbd', + ); + self.hook( + audio.microphone, + () => { + self.label = audio.microphone.is_muted ? '󰍭' : '󰍬'; + }, + 'notify::volume', + ); + self.hook( + audio.microphone, + () => { + self.label = audio.microphone.is_muted ? '󰍭' : '󰍬'; + }, + 'notify::is-muted', + ); + self.hook( + audio.speaker, + () => { + self.label = audio.speaker.is_muted ? '󰝟' : '󰕾'; + }, + 'notify::volume', + ); + self.hook( + audio.speaker, + () => { + self.label = audio.speaker.is_muted ? '󰝟' : '󰕾'; + }, + 'notify::is-muted', + ); + }, + }), }); -} +}; diff --git a/modules/osd/index.ts b/modules/osd/index.ts index cc9a6b9..8e3dbd6 100644 --- a/modules/osd/index.ts +++ b/modules/osd/index.ts @@ -1,135 +1,199 @@ -import options from "options"; -import brightness from "services/Brightness" -import { OSDLabel } from "./label/index"; -import { OSDBar } from "./bar/index"; -import { OSDIcon } from "./icon/index"; -import { getPosition } from "lib/utils"; -const hyprland = await Service.import("hyprland"); -const audio = await Service.import("audio") +import options from 'options'; +import brightness from 'services/Brightness'; +import { OSDLabel } from './label/index'; +import { OSDBar } from './bar/index'; +import { OSDIcon } from './icon/index'; +import { getPosition } from 'lib/utils'; +import { Attribute, Child } from 'lib/types/widget'; +import { Revealer } from 'resource:///com/github/Aylur/ags/widgets/revealer.js'; +import { Window } from 'resource:///com/github/Aylur/ags/widgets/window.js'; +const hyprland = await Service.import('hyprland'); +const audio = await Service.import('audio'); -const { - enable, - orientation, - location, - active_monitor, - monitor -} = options.theme.osd; +const { enable, duration, orientation, location, active_monitor, monitor } = options.theme.osd; const curMonitor = Variable(monitor.value); -hyprland.active.connect("changed", () => { +hyprland.active.connect('changed', () => { curMonitor.value = hyprland.active.monitor.id; -}) +}); -const DELAY = 2500; +let count = 0; -let count = 0 -const handleReveal = (self: any, property: string) => { - if (!enable.value) { +const handleRevealRevealer = (self: Revealer, property: 'reveal_child' | 'visible'): void => { + if (!enable.value || property !== 'reveal_child') { return; } - self[property] = true - count++ - Utils.timeout(DELAY, () => { - count-- - if (count === 0) - self[property] = false - }) -} + self.reveal_child = true; -const renderOSD = () => { + count++; + Utils.timeout(duration.value, () => { + count--; + if (count === 0) { + self.reveal_child = false; + } + }); +}; +const handleRevealWindow = (self: Window, property: 'reveal_child' | 'visible'): void => { + if (!enable.value || property !== 'visible') { + return; + } + + self.visible = true; + + count++; + Utils.timeout(duration.value, () => { + count--; + + if (count === 0) { + self.visible = false; + } + }); +}; + +const handleReveal = ( + self: Revealer | Window, + property: 'reveal_child' | 'visible', +): void => { + if (self instanceof Revealer) { + handleRevealRevealer(self, property); + } else if (self instanceof Window) { + handleRevealWindow(self, property); + } +}; + +const renderOSD = (): Revealer => { return Widget.Revealer({ - transition: "crossfade", + transition: 'crossfade', reveal_child: false, - setup: self => { - self.hook(brightness, () => { - handleReveal(self, "reveal_child"); - }, "notify::screen") - self.hook(brightness, () => { - handleReveal(self, "reveal_child"); - }, "notify::kbd") - self.hook(audio.microphone, () => { - handleReveal(self, "reveal_child"); - }, "notify::volume") - self.hook(audio.microphone, () => { - handleReveal(self, "reveal_child"); - }, "notify::is-muted") - self.hook(audio.speaker, () => { - handleReveal(self, "reveal_child"); - }, "notify::volume") - self.hook(audio.speaker, () => { - handleReveal(self, "reveal_child"); - }, "notify::is-muted") - - + setup: (self) => { + self.hook( + brightness, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::screen', + ); + self.hook( + brightness, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::kbd', + ); + self.hook( + audio.microphone, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::volume', + ); + self.hook( + audio.microphone, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::is-muted', + ); + self.hook( + audio.speaker, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::volume', + ); + self.hook( + audio.speaker, + () => { + handleReveal(self, 'reveal_child'); + }, + 'notify::is-muted', + ); }, child: Widget.Box({ - class_name: "osd-container", - vertical: orientation.bind("value").as(ort => ort === "vertical"), - children: orientation.bind("value").as(ort => { - if (ort === "vertical") { - return [ - OSDLabel(ort), - OSDBar(ort), - OSDIcon(ort) - ] + class_name: 'osd-container', + vertical: orientation.bind('value').as((ort) => ort === 'vertical'), + children: orientation.bind('value').as((ort) => { + if (ort === 'vertical') { + return [OSDLabel(), OSDBar(ort), OSDIcon()]; } - return [ - OSDIcon(ort), - OSDBar(ort), - OSDLabel(ort), - ] - }) - }) - }) -} - -export default () => Widget.Window({ - monitor: Utils.merge([ - curMonitor.bind("value"), - monitor.bind("value"), - active_monitor.bind("value")], (curMon, mon, activeMonitor) => { - if (activeMonitor === true) { - return curMon; - } - - return mon; + return [OSDIcon(), OSDBar(ort), OSDLabel()]; + }), }), - name: `indicator`, - class_name: "indicator", - layer: "overlay", - anchor: location.bind("value").as(v => getPosition(v)), - click_through: true, - child: Widget.Box({ - css: "padding: 1px;", - expand: true, - child: renderOSD(), - }), - setup: self => { - self.hook(enable, () => { - handleReveal(self, "visible"); - }) - self.hook(brightness, () => { - handleReveal(self, "visible"); - }, "notify::screen") - self.hook(brightness, () => { - handleReveal(self, "visible"); - }, "notify::kbd") - self.hook(audio.microphone, () => { - handleReveal(self, "visible"); - }, "notify::volume") - self.hook(audio.microphone, () => { - handleReveal(self, "visible"); - }, "notify::is-muted") - self.hook(audio.speaker, () => { - handleReveal(self, "visible"); - }, "notify::volume") - self.hook(audio.speaker, () => { - handleReveal(self, "visible"); - }, "notify::is-muted") - }, -}) + }); +}; + +export default (): Window => + Widget.Window({ + monitor: Utils.merge( + [curMonitor.bind('value'), monitor.bind('value'), active_monitor.bind('value')], + (curMon, mon, activeMonitor) => { + if (activeMonitor === true) { + return curMon; + } + + return mon; + }, + ), + name: `indicator`, + class_name: 'indicator', + layer: options.tear.bind('value').as((tear) => (tear ? 'top' : 'overlay')), + anchor: location.bind('value').as((v) => getPosition(v)), + click_through: true, + child: Widget.Box({ + css: 'padding: 1px;', + expand: true, + child: renderOSD(), + }), + setup: (self) => { + self.hook(enable, () => { + handleReveal(self, 'visible'); + }); + self.hook( + brightness, + () => { + handleReveal(self, 'visible'); + }, + 'notify::screen', + ); + self.hook( + brightness, + () => { + handleReveal(self, 'visible'); + }, + 'notify::kbd', + ); + self.hook( + audio.microphone, + () => { + handleReveal(self, 'visible'); + }, + 'notify::volume', + ); + self.hook( + audio.microphone, + () => { + handleReveal(self, 'visible'); + }, + 'notify::is-muted', + ); + self.hook( + audio.speaker, + () => { + handleReveal(self, 'visible'); + }, + 'notify::volume', + ); + self.hook( + audio.speaker, + () => { + handleReveal(self, 'visible'); + }, + 'notify::is-muted', + ); + }, + }); diff --git a/modules/osd/label/index.ts b/modules/osd/label/index.ts index 33fc923..9e61636 100644 --- a/modules/osd/label/index.ts +++ b/modules/osd/label/index.ts @@ -1,45 +1,86 @@ -import { OSDOrientation } from "lib/types/options"; -import brightness from "services/Brightness" -import options from "options" -const audio = await Service.import("audio") +import brightness from 'services/Brightness'; +import options from 'options'; +import Box from 'types/widgets/box'; +import { Attribute, Child } from 'lib/types/widget'; +const audio = await Service.import('audio'); -export const OSDLabel = (ort: OSDOrientation) => { +export const OSDLabel = (): Box => { return Widget.Box({ - class_name: "osd-label-container", + class_name: 'osd-label-container', hexpand: true, vexpand: true, child: Widget.Label({ - class_name: "osd-label", + class_name: 'osd-label', hexpand: true, vexpand: true, - hpack: "center", - vpack: "center", - setup: self => { - self.hook(brightness, () => { - self.class_names = self.class_names.filter(c => c !== "overflow"); - self.label = `${Math.round(brightness.screen * 100)}`; - }, "notify::screen") - self.hook(brightness, () => { - self.class_names = self.class_names.filter(c => c !== "overflow"); - self.label = `${Math.round(brightness.kbd * 100)}`; - }, "notify::kbd") - self.hook(audio.microphone, () => { - self.toggleClassName("overflow", audio.microphone.volume > 1) - self.label = `${Math.round(audio.microphone.volume * 100)}`; - }, "notify::volume") - self.hook(audio.microphone, () => { - self.toggleClassName("overflow", audio.microphone.volume > 1 && (!options.theme.osd.muted_zero.value || audio.microphone.is_muted === false)); - self.label = `${options.theme.osd.muted_zero.value && audio.microphone.is_muted !== false ? 0 : Math.round(audio.microphone.volume * 100)}`; - }, "notify::is-muted") - self.hook(audio.speaker, () => { - self.toggleClassName("overflow", audio.speaker.volume > 1) - self.label = `${Math.round(audio.speaker.volume * 100)}`; - }, "notify::volume") - self.hook(audio.speaker, () => { - self.toggleClassName("overflow", audio.speaker.volume > 1 && (!options.theme.osd.muted_zero.value || audio.speaker.is_muted === false)); - self.label = `${options.theme.osd.muted_zero.value && audio.speaker.is_muted !== false ? 0 : Math.round(audio.speaker.volume * 100)}`; - }, "notify::is-muted") - } - }) + hpack: 'center', + vpack: 'center', + setup: (self) => { + self.hook( + brightness, + () => { + self.class_names = self.class_names.filter((c) => c !== 'overflow'); + self.label = `${Math.round(brightness.screen * 100)}`; + }, + 'notify::screen', + ); + self.hook( + brightness, + () => { + self.class_names = self.class_names.filter((c) => c !== 'overflow'); + self.label = `${Math.round(brightness.kbd * 100)}`; + }, + 'notify::kbd', + ); + self.hook( + audio.microphone, + () => { + self.toggleClassName('overflow', audio.microphone.volume > 1); + self.label = `${Math.round(audio.microphone.volume * 100)}`; + }, + 'notify::volume', + ); + self.hook( + audio.microphone, + () => { + self.toggleClassName( + 'overflow', + audio.microphone.volume > 1 && + (!options.theme.osd.muted_zero.value || audio.microphone.is_muted === false), + ); + const inputVolume = + options.theme.osd.muted_zero.value && audio.microphone.is_muted !== false + ? 0 + : Math.round(audio.microphone.volume * 100); + self.label = `${inputVolume}`; + }, + 'notify::is-muted', + ); + self.hook( + audio.speaker, + () => { + self.toggleClassName('overflow', audio.speaker.volume > 1); + self.label = `${Math.round(audio.speaker.volume * 100)}`; + }, + 'notify::volume', + ); + self.hook( + audio.speaker, + () => { + self.toggleClassName( + 'overflow', + audio.speaker.volume > 1 && + (!options.theme.osd.muted_zero.value || audio.speaker.is_muted === false), + ); + const speakerVolume = + options.theme.osd.muted_zero.value && audio.speaker.is_muted !== false + ? 0 + : Math.round(audio.speaker.volume * 100); + self.label = `${speakerVolume}`; + }, + 'notify::is-muted', + ); + }, + }), }); -} +}; diff --git a/modules/shared/barItemBox.ts b/modules/shared/barItemBox.ts index 676a455..0f84c41 100644 --- a/modules/shared/barItemBox.ts +++ b/modules/shared/barItemBox.ts @@ -1,26 +1,29 @@ -import Gtk from "gi://Gtk?version=3.0"; -import { Child } from "lib/types/bar"; -import options from "options"; -import { ButtonProps } from "types/widgets/button"; +import { BarBoxChild } from 'lib/types/bar'; +import { Bind } from 'lib/types/variable'; +import { Attribute, GtkWidget } from 'lib/types/widget'; +import options from 'options'; +import Button from 'types/widgets/button'; -export const BarItemBox = (child: Child): ButtonProps => { - const computeVisible = () => { +export const BarItemBox = (child: BarBoxChild): Button => { + const computeVisible = (): Bind | boolean => { if (child.isVis !== undefined) { - return child.isVis.bind("value"); + return child.isVis.bind('value'); } return child.isVisible; }; return Widget.Button({ - class_name: options.theme.bar.buttons.style.bind("value").as((style) => { + class_name: options.theme.bar.buttons.style.bind('value').as((style) => { const styleMap = { - default: "style1", - split: "style2", - wave: "style3", - wave2: "style4", + default: 'style1', + split: 'style2', + wave: 'style3', + wave2: 'style4', }; - return `bar_item_box_visible ${styleMap[style]} ${Object.hasOwnProperty.call(child, "boxClass") ? child.boxClass : ""}`; + const boxClassName = Object.hasOwnProperty.call(child, 'boxClass') ? child.boxClass : ''; + + return `bar_item_box_visible ${styleMap[style]} ${boxClassName}`; }), child: child.component, visible: computeVisible(), diff --git a/options.ts b/options.ts index 1681ab7..104e7c7 100644 --- a/options.ts +++ b/options.ts @@ -1,91 +1,108 @@ -import { opt, mkOptions } from "lib/option" -import { NetstatIcon, NetstatLabelType, PowerIcon, RateUnit, ResourceLabelType, StorageIcon, UpdatesIcon } from "lib/types/bar"; -import { KbIcon, KbLabelType } from "lib/types/customModules/kbLayout"; -import { BarButtonStyles, NotificationAnchor, OSDAnchor, OSDOrientation } from "lib/types/options"; -import { MatugenScheme, MatugenTheme, MatugenVariation } from "lib/types/options"; -import { UnitType } from "lib/types/weather"; +import { opt, mkOptions } from 'lib/option'; +import { + NetstatIcon, + NetstatLabelType, + PowerIcon, + RateUnit, + ResourceLabelType, + StorageIcon, + UpdatesIcon, +} from 'lib/types/bar'; +import { KbIcon, KbLabelType } from 'lib/types/customModules/kbLayout'; +import { + ActiveWsIndicator, + BarButtonStyles, + BarLocation, + NotificationAnchor, + OSDAnchor, + OSDOrientation, + WindowLayer, +} from 'lib/types/options'; +import { MatugenScheme, MatugenTheme, MatugenVariations } from 'lib/types/options'; +import { UnitType } from 'lib/types/weather'; +import { WorkspaceIcons, WorkspaceIconsColored } from 'lib/types/workspace'; // WARN: CHANGING THESE VALUES WILL PREVENT MATUGEN COLOR GENERATION FOR THE CHANGED VALUE export const colors = { - rosewater: "#f5e0dc", - flamingo: "#f2cdcd", - pink: "#f5c2e7", - mauve: "#cba6f7", - red: "#f38ba8", - maroon: "#eba0ac", - peach: "#fab387", - yellow: "#f9e2af", - green: "#a6e3a1", - teal: "#94e2d5", - sky: "#89dceb", - sapphire: "#74c7ec", - blue: "#89b4fa", - lavender: "#b4befe", - text: "#cdd6f4", - subtext1: "#bac2de", - subtext2: "#a6adc8", - overlay2: "#9399b2", - overlay1: "#7f849c", - overlay0: "#6c7086", - surface2: "#585b70", - surface1: "#45475a", - surface0: "#313244", - base2: "#242438", - base: "#1e1e2e", - mantle: "#181825", - crust: "#11111b" + rosewater: '#f5e0dc', + flamingo: '#f2cdcd', + pink: '#f5c2e7', + mauve: '#cba6f7', + red: '#f38ba8', + maroon: '#eba0ac', + peach: '#fab387', + yellow: '#f9e2af', + green: '#a6e3a1', + teal: '#94e2d5', + sky: '#89dceb', + sapphire: '#74c7ec', + blue: '#89b4fa', + lavender: '#b4befe', + text: '#cdd6f4', + subtext1: '#bac2de', + subtext2: '#a6adc8', + overlay2: '#9399b2', + overlay1: '#7f849c', + overlay0: '#6c7086', + surface2: '#585b70', + surface1: '#45475a', + surface0: '#313244', + base2: '#242438', + base: '#1e1e2e', + mantle: '#181825', + crust: '#11111b', }; // WARN: CHANGING THESE VALUES WILL PREVENT MATUGEN COLOR GENERATION FOR THE CHANGED VALUE const secondary_colors = { - text: "#cdd6f3", - pink: "#f5c2e6", - red: "#f38ba7", - peach: "#fab386", - mantle: "#181824", - surface1: "#454759", - surface0: "#313243", - overlay1: "#7f849b", - lavender: "#b4befd", - mauve: "#cba6f6", - green: "#a6e3a0", - sky: "#89dcea", - teal: "#94e2d4", - yellow: "#f9e2ad", - maroon: "#eba0ab", - crust: "#11111a", - surface2: "#585b69", -} + text: '#cdd6f3', + pink: '#f5c2e6', + red: '#f38ba7', + peach: '#fab386', + mantle: '#181824', + surface1: '#454759', + surface0: '#313243', + overlay1: '#7f849b', + lavender: '#b4befd', + mauve: '#cba6f6', + green: '#a6e3a0', + sky: '#89dcea', + teal: '#94e2d4', + yellow: '#f9e2ad', + maroon: '#eba0ab', + crust: '#11111a', + surface2: '#585b69', +}; const tertiary_colors = { - pink: "#f5c2e8", - red: "#f38ba9", - mantle: "#181826", - surface0: "#313245", - overlay1: "#7f849d", - lavender: "#b4beff", - mauve: "#cba6f8", - green: "#a6e3a2", - sky: "#89dcec", - teal: "#94e2d6", - yellow: "#f9e2ae", - maroon: "#eba0ad", - crust: "#11111c", - surface2: "#585b71", -} + pink: '#f5c2e8', + red: '#f38ba9', + mantle: '#181826', + surface0: '#313245', + overlay1: '#7f849d', + lavender: '#b4beff', + mauve: '#cba6f8', + green: '#a6e3a2', + sky: '#89dcec', + teal: '#94e2d6', + yellow: '#f9e2ae', + maroon: '#eba0ad', + crust: '#11111c', + surface2: '#585b71', +}; const options = mkOptions(OPTIONS, { theme: { matugen: opt(false), matugen_settings: { - mode: opt("dark"), - scheme_type: opt("tonal-spot"), - variation: opt("standard_1"), + mode: opt('dark'), + scheme_type: opt('tonal-spot'), + variation: opt('standard_1'), contrast: opt(0.0), }, font: { - size: opt("1.2rem"), - name: opt("Ubuntu Nerd Font"), + size: opt('1.2rem'), + name: opt('Ubuntu Nerd Font'), weight: opt(600), }, notification: { @@ -98,19 +115,20 @@ const options = mkOptions(OPTIONS, { }, label: opt(colors.lavender), border: opt(secondary_colors.surface0), - border_radius: opt("0.6em"), + border_radius: opt('0.6em'), time: opt(secondary_colors.overlay1), text: opt(colors.text), labelicon: opt(colors.lavender), close_button: { background: opt(secondary_colors.red), - label: opt(colors.crust) - } + label: opt(colors.crust), + }, }, osd: { scaling: opt(100), + duration: opt(2500), enable: opt(true), - orientation: opt("vertical"), + orientation: opt('vertical'), opacity: opt(100), bar_container: opt(colors.crust), icon_container: opt(tertiary_colors.lavender), @@ -121,32 +139,34 @@ const options = mkOptions(OPTIONS, { label: opt(tertiary_colors.lavender), monitor: opt(0), active_monitor: opt(true), - radius: opt("0.4em"), - margins: opt("0px 5px 0px 0px"), - location: opt("right"), + radius: opt('0.4em'), + margins: opt('0px 5px 0px 0px'), + location: opt('right'), muted_zero: opt(false), }, bar: { scaling: opt(100), floating: opt(false), - layer: opt<"top" | "bottom" | "overlay" | "background">("top"), - margin_top: opt("0.5em"), + location: opt('top'), + layer: opt('top'), + margin_top: opt('0.5em'), opacity: opt(100), - margin_bottom: opt("0em"), - margin_sides: opt("0.5em"), - border_radius: opt("0.4em"), - outer_spacing: opt("1.6em"), - label_spacing: opt("0.5em"), + margin_bottom: opt('0em'), + margin_sides: opt('0.5em'), + border_radius: opt('0.4em'), + outer_spacing: opt('1.6em'), + label_spacing: opt('0.5em'), transparent: opt(false), + dropdownGap: opt('2.9em'), background: opt(colors.crust), buttons: { - style: opt("default"), + style: opt('default'), monochrome: opt(false), - spacing: opt("0.25em"), - padding_x: opt("0.7rem"), - padding_y: opt("0.2rem"), - y_margins: opt("0.4em"), - radius: opt("0.3em"), + spacing: opt('0.25em'), + padding_x: opt('0.7rem'), + padding_y: opt('0.2rem'), + y_margins: opt('0.4em'), + radius: opt('0.3em'), opacity: opt(100), background_opacity: opt(100), background_hover_opacity: opt(100), @@ -159,7 +179,7 @@ const options = mkOptions(OPTIONS, { background: opt(colors.base2), hover: opt(colors.surface1), icon: opt(colors.yellow), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, workspaces: { background: opt(colors.base2), @@ -167,11 +187,12 @@ const options = mkOptions(OPTIONS, { available: opt(colors.sky), occupied: opt(colors.flamingo), active: opt(colors.pink), - numbered_active_highlight_border: opt("0.2em"), - numbered_active_highlight_padding: opt("0.2em"), + numbered_active_highlight_border: opt('0.2em'), + numbered_active_highlight_padding: opt('0.2em'), numbered_active_highlighted_text_color: opt(colors.mantle), numbered_active_underline_color: opt(colors.pink), - spacing: opt("0.5em"), + spacing: opt('0.5em'), + fontSize: opt('1.2em'), }, windowtitle: { background: opt(colors.base2), @@ -179,7 +200,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.pink), icon: opt(colors.pink), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, media: { background: opt(colors.base2), @@ -187,7 +208,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.lavender), icon: opt(colors.lavender), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, volume: { background: opt(colors.base2), @@ -195,7 +216,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.maroon), icon: opt(colors.maroon), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, network: { background: opt(colors.base2), @@ -203,7 +224,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.mauve), icon: opt(colors.mauve), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, bluetooth: { background: opt(colors.base2), @@ -211,12 +232,12 @@ const options = mkOptions(OPTIONS, { text: opt(colors.sky), icon: opt(colors.sky), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, systray: { background: opt(colors.base2), hover: opt(colors.surface1), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, battery: { background: opt(colors.base2), @@ -224,7 +245,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.yellow), icon: opt(colors.yellow), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, clock: { background: opt(colors.base2), @@ -232,7 +253,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.pink), icon: opt(colors.pink), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, notifications: { background: opt(colors.base2), @@ -240,7 +261,7 @@ const options = mkOptions(OPTIONS, { icon: opt(colors.lavender), icon_background: opt(colors.base2), total: opt(colors.lavender), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, modules: { ram: { @@ -248,56 +269,56 @@ const options = mkOptions(OPTIONS, { text: opt(colors.yellow), icon: opt(colors.yellow), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, cpu: { background: opt(colors.base2), text: opt(colors.red), icon: opt(colors.red), icon_background: opt(colors.base2), - spacing: opt("0.5em"), + spacing: opt('0.5em'), }, storage: { background: opt(colors.base2), text: opt(colors.pink), icon: opt(colors.pink), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, netstat: { background: opt(colors.base2), text: opt(colors.green), icon: opt(colors.green), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, kbLayout: { background: opt(colors.base2), text: opt(colors.sky), icon: opt(colors.sky), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, updates: { background: opt(colors.base2), text: opt(colors.mauve), icon: opt(colors.mauve), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, weather: { background: opt(colors.base2), text: opt(colors.lavender), icon: opt(colors.lavender), icon_background: opt(colors.base2), - spacing: opt("0.45em"), + spacing: opt('0.45em'), }, power: { background: opt(colors.base2), icon: opt(colors.red), icon_background: opt(colors.base2), - spacing: opt("0.45em"), - } + spacing: opt('0.45em'), + }, }, }, menus: { @@ -305,11 +326,11 @@ const options = mkOptions(OPTIONS, { background: opt(colors.crust), opacity: opt(100), cards: opt(colors.base), - card_radius: opt("0.4em"), + card_radius: opt('0.4em'), border: { - size: opt("0.13em"), - radius: opt("0.7em"), - color: opt(colors.surface0) + size: opt('0.13em'), + radius: opt('0.7em'), + color: opt(colors.surface0), }, text: opt(colors.text), dimtext: opt(colors.surface2), @@ -318,11 +339,11 @@ const options = mkOptions(OPTIONS, { popover: { text: opt(colors.lavender), background: opt(secondary_colors.mantle), - border: opt(secondary_colors.mantle) + border: opt(secondary_colors.mantle), }, listitems: { passive: opt(colors.text), - active: opt(secondary_colors.lavender) + active: opt(secondary_colors.lavender), }, icons: { passive: opt(colors.surface2), @@ -331,21 +352,21 @@ const options = mkOptions(OPTIONS, { switch: { enabled: opt(colors.lavender), disabled: opt(tertiary_colors.surface0), - puck: opt(secondary_colors.surface1) + puck: opt(secondary_colors.surface1), }, check_radio_button: { background: opt(colors.surface1), - active: opt(tertiary_colors.lavender) + active: opt(tertiary_colors.lavender), }, buttons: { default: opt(colors.lavender), active: opt(secondary_colors.pink), disabled: opt(tertiary_colors.surface2), - text: opt(secondary_colors.mantle) + text: opt(secondary_colors.mantle), }, iconbuttons: { passive: opt(secondary_colors.text), - active: opt(tertiary_colors.lavender) + active: opt(tertiary_colors.lavender), }, progressbar: { foreground: opt(colors.lavender), @@ -355,16 +376,16 @@ const options = mkOptions(OPTIONS, { primary: opt(colors.lavender), background: opt(tertiary_colors.surface2), backgroundhover: opt(colors.surface1), - puck: opt(colors.overlay0) + puck: opt(colors.overlay0), }, dropdownmenu: { background: opt(colors.crust), text: opt(colors.text), - divider: opt(colors.base) + divider: opt(colors.base), }, tooltip: { background: opt(colors.crust), - text: opt(tertiary_colors.lavender) + text: opt(tertiary_colors.lavender), }, menu: { media: { @@ -392,8 +413,8 @@ const options = mkOptions(OPTIONS, { primary: opt(colors.pink), background: opt(tertiary_colors.surface2), backgroundhover: opt(colors.surface1), - puck: opt(colors.overlay0) - } + puck: opt(colors.overlay0), + }, }, volume: { scaling: opt(100), @@ -412,7 +433,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.text), listitems: { passive: opt(colors.text), - active: opt(secondary_colors.maroon) + active: opt(secondary_colors.maroon), }, iconbutton: { passive: opt(colors.text), @@ -426,14 +447,14 @@ const options = mkOptions(OPTIONS, { primary: opt(colors.maroon), background: opt(tertiary_colors.surface2), backgroundhover: opt(colors.surface1), - puck: opt(colors.surface2) + puck: opt(colors.surface2), }, input_slider: { primary: opt(colors.maroon), background: opt(tertiary_colors.surface2), backgroundhover: opt(colors.surface1), - puck: opt(colors.surface2) - } + puck: opt(colors.surface2), + }, }, network: { scaling: opt(100), @@ -455,7 +476,7 @@ const options = mkOptions(OPTIONS, { }, listitems: { passive: opt(colors.text), - active: opt(secondary_colors.mauve) + active: opt(secondary_colors.mauve), }, icons: { passive: opt(colors.overlay2), @@ -463,7 +484,7 @@ const options = mkOptions(OPTIONS, { }, iconbuttons: { passive: opt(colors.text), - active: opt(colors.mauve) + active: opt(colors.mauve), }, }, bluetooth: { @@ -486,11 +507,11 @@ const options = mkOptions(OPTIONS, { switch: { enabled: opt(colors.sky), disabled: opt(tertiary_colors.surface0), - puck: opt(secondary_colors.surface1) + puck: opt(secondary_colors.surface1), }, listitems: { passive: opt(colors.text), - active: opt(secondary_colors.sky) + active: opt(secondary_colors.sky), }, icons: { passive: opt(colors.overlay2), @@ -498,14 +519,14 @@ const options = mkOptions(OPTIONS, { }, iconbutton: { passive: opt(colors.text), - active: opt(colors.sky) + active: opt(colors.sky), }, }, systray: { dropdownmenu: { background: opt(colors.crust), text: opt(colors.text), - divider: opt(colors.base) + divider: opt(colors.base), }, }, battery: { @@ -525,7 +546,7 @@ const options = mkOptions(OPTIONS, { text: opt(colors.text), listitems: { passive: opt(secondary_colors.text), - active: opt(colors.yellow) + active: opt(colors.yellow), }, icons: { passive: opt(colors.overlay2), @@ -535,7 +556,7 @@ const options = mkOptions(OPTIONS, { primary: opt(colors.yellow), background: opt(tertiary_colors.surface2), backgroundhover: opt(colors.surface1), - puck: opt(colors.overlay0) + puck: opt(colors.overlay0), }, }, clock: { @@ -577,8 +598,8 @@ const options = mkOptions(OPTIONS, { hourly: { time: opt(colors.pink), icon: opt(colors.pink), - temperature: opt(colors.pink) - } + temperature: opt(colors.pink), + }, }, }, dashboard: { @@ -595,8 +616,8 @@ const options = mkOptions(OPTIONS, { }, profile: { name: opt(colors.pink), - size: opt("8.5em"), - radius: opt("0.4em"), + size: opt('8.5em'), + radius: opt('0.4em'), }, powermenu: { shutdown: opt(colors.red), @@ -612,12 +633,12 @@ const options = mkOptions(OPTIONS, { confirm: opt(colors.green), deny: opt(colors.red), button_text: opt(secondary_colors.crust), - } + }, }, shortcuts: { background: opt(colors.lavender), text: opt(secondary_colors.mantle), - recording: opt(colors.green) + recording: opt(colors.green), }, controls: { disabled: opt(colors.surface2), @@ -664,7 +685,7 @@ const options = mkOptions(OPTIONS, { bottom: { color: opt(colors.lavender), }, - } + }, }, monitors: { bar_background: opt(colors.surface1), @@ -692,7 +713,7 @@ const options = mkOptions(OPTIONS, { }, power: { scaling: opt(90), - radius: opt("0.4em"), + radius: opt('0.4em'), background: { color: opt(colors.crust), }, @@ -724,11 +745,11 @@ const options = mkOptions(OPTIONS, { text: opt(colors.sky), icon: opt(secondary_colors.mantle), }, - } + }, }, notifications: { scaling: opt(100), - height: opt("58em"), + height: opt('58em'), label: opt(colors.lavender), no_notifications_label: opt(colors.surface0), background: opt(colors.crust), @@ -739,7 +760,7 @@ const options = mkOptions(OPTIONS, { switch: { enabled: opt(colors.lavender), disabled: opt(tertiary_colors.surface0), - puck: opt(secondary_colors.surface1) + puck: opt(secondary_colors.surface1), }, pager: { show: opt(true), @@ -749,83 +770,56 @@ const options = mkOptions(OPTIONS, { }, scrollbar: { color: opt(colors.lavender), - width: opt("0.35em"), - radius: opt("0.2em") - } - } - } - } - } + width: opt('0.35em'), + radius: opt('0.2em'), + }, + }, + }, + }, + }, }, bar: { layouts: opt({ - "1": { - left: [ - "dashboard", - "workspaces", - "windowtitle" - ], - middle: [ - "media" - ], - right: [ - "volume", - "clock", - "notifications" - ] + '1': { + left: ['dashboard', 'workspaces', 'windowtitle'], + middle: ['media'], + right: ['volume', 'clock', 'notifications'], }, - "2": { - left: [ - "dashboard", - "workspaces", - "windowtitle" - ], - middle: [ - "media" - ], - right: [ - "volume", - "clock", - "notifications" - ] + '2': { + left: ['dashboard', 'workspaces', 'windowtitle'], + middle: ['media'], + right: ['volume', 'clock', 'notifications'], + }, + '0': { + left: ['dashboard', 'workspaces', 'windowtitle'], + middle: ['media'], + right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'], }, - "0": { - left: [ - "dashboard", - "workspaces", - "windowtitle" - ], - middle: [ - "media" - ], - right: [ - "volume", - "network", - "bluetooth", - "battery", - "systray", - "clock", - "notifications" - ] - } }), launcher: { - icon: opt("󰣇"), + icon: opt('󰣇'), }, windowtitle: { + custom_title: opt(true), title_map: opt([]), + class_name: opt(true), label: opt(true), + icon: opt(true), + truncation: opt(true), + truncation_size: opt(50), }, workspaces: { show_icons: opt(false), show_numbered: opt(false), - numbered_active_indicator: opt<"underline" | "highlight">("underline"), + showWsIcons: opt(false), + numbered_active_indicator: opt('underline'), icons: { - available: opt(""), - active: opt(""), - occupied: opt(""), + available: opt(''), + active: opt(''), + occupied: opt(''), }, + workspaceIconMap: opt({}), workspaces: opt(10), spacing: opt(1), monitorSpecific: opt(true), @@ -849,20 +843,20 @@ const options = mkOptions(OPTIONS, { label: opt(true), }, systray: { - ignore: opt([]), + ignore: opt([]), }, clock: { - icon: opt("󰸗"), + icon: opt('󰸗'), showIcon: opt(true), showTime: opt(true), - format: opt("%a %b %d %I:%M:%S %p"), + format: opt('%a %b %d %I:%M:%S %p'), }, media: { show_artist: opt(false), truncation: opt(true), show_label: opt(true), truncation_size: opt(30), - show_active_only: opt(false) + show_active_only: opt(false), }, notifications: { show_total: opt(false), @@ -871,107 +865,107 @@ const options = mkOptions(OPTIONS, { scrollSpeed: opt(5), ram: { label: opt(true), - labelType: opt("percentage"), + labelType: opt('percentage'), round: opt(true), pollingInterval: opt(2000), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), }, cpu: { label: opt(true), round: opt(true), pollingInterval: opt(2000), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), - scrollUp: opt(""), - scrollDown: opt(""), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), + scrollUp: opt(''), + scrollDown: opt(''), }, storage: { label: opt(true), - icon: opt("󰋊"), + icon: opt('󰋊'), round: opt(false), - labelType: opt("percentage"), + labelType: opt('percentage'), pollingInterval: opt(2000), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), }, netstat: { label: opt(true), - networkInterface: opt(""), - icon: opt("󰖟"), + networkInterface: opt(''), + icon: opt('󰖟'), round: opt(true), - labelType: opt("full"), - rateUnit: opt("auto"), + labelType: opt('full'), + rateUnit: opt('auto'), pollingInterval: opt(2000), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), }, kbLayout: { label: opt(true), - labelType: opt("code"), - icon: opt("󰌌"), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), - scrollUp: opt(""), - scrollDown: opt(""), + labelType: opt('code'), + icon: opt('󰌌'), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), + scrollUp: opt(''), + scrollDown: opt(''), }, updates: { - updateCommand: opt("$HOME/.config/ags/scripts/checkUpdates.sh -arch"), + updateCommand: opt('$HOME/.config/ags/scripts/checkUpdates.sh -arch'), label: opt(true), padZero: opt(true), - icon: opt("󰏖"), + icon: opt('󰏖'), pollingInterval: opt(1000 * 60 * 60 * 6), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), - scrollUp: opt(""), - scrollDown: opt(""), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), + scrollUp: opt(''), + scrollDown: opt(''), }, weather: { label: opt(true), - unit: opt("imperial"), - leftClick: opt(""), - rightClick: opt(""), - middleClick: opt(""), - scrollUp: opt(""), - scrollDown: opt(""), + unit: opt('imperial'), + leftClick: opt(''), + rightClick: opt(''), + middleClick: opt(''), + scrollUp: opt(''), + scrollDown: opt(''), }, power: { - icon: opt(""), + icon: opt(''), showLabel: opt(true), - leftClick: opt("menu:powerdropdown"), - rightClick: opt(""), - middleClick: opt(""), - scrollUp: opt(""), - scrollDown: opt(""), + leftClick: opt('menu:powerdropdown'), + rightClick: opt(''), + middleClick: opt(''), + scrollUp: opt(''), + scrollDown: opt(''), }, - } + }, }, menus: { power: { showLabel: opt(true), confirmation: opt(true), - sleep: opt("systemctl suspend"), - reboot: opt("systemctl reboot"), - logout: opt("pkill Hyprland"), - shutdown: opt("shutdown now"), + sleep: opt('systemctl suspend'), + reboot: opt('systemctl reboot'), + logout: opt('pkill Hyprland'), + shutdown: opt('shutdown now'), }, dashboard: { powermenu: { confirmation: opt(true), - sleep: opt("systemctl suspend"), - reboot: opt("systemctl reboot"), - logout: opt("pkill Hyprland"), - shutdown: opt("shutdown now"), + sleep: opt('systemctl suspend'), + reboot: opt('systemctl reboot'), + logout: opt('pkill Hyprland'), + shutdown: opt('shutdown now'), avatar: { - image: opt("avatar-default-symbolic"), - name: opt<"system" | string>("system"), + image: opt('avatar-default-symbolic'), + name: opt<'system' | string>('system'), }, }, stats: { @@ -980,68 +974,68 @@ const options = mkOptions(OPTIONS, { shortcuts: { left: { shortcut1: { - icon: opt("󰇩"), - tooltip: opt("Microsoft Edge"), - command: opt("microsoft-edge-stable") + icon: opt('󰇩'), + tooltip: opt('Microsoft Edge'), + command: opt('microsoft-edge-stable'), }, shortcut2: { - icon: opt(""), - tooltip: opt("Spotify"), - command: opt("spotify-launcher") + icon: opt(''), + tooltip: opt('Spotify'), + command: opt('spotify-launcher'), }, shortcut3: { - icon: opt(""), - tooltip: opt("Discord"), - command: opt("discord") + icon: opt(''), + tooltip: opt('Discord'), + command: opt('discord'), }, shortcut4: { - icon: opt(""), - tooltip: opt("Search Apps"), - command: opt("rofi -show drun") + icon: opt(''), + tooltip: opt('Search Apps'), + command: opt('rofi -show drun'), }, }, right: { shortcut1: { - icon: opt(""), - tooltip: opt("Color Picker"), - command: opt("hyprpicker -a") + icon: opt(''), + tooltip: opt('Color Picker'), + command: opt('hyprpicker -a'), }, shortcut3: { - icon: opt("󰄀"), - tooltip: opt("Screenshot"), - command: opt("bash -c \"$HOME/.config/ags/services/snapshot.sh\"") + icon: opt('󰄀'), + tooltip: opt('Screenshot'), + command: opt('bash -c "$HOME/.config/ags/services/snapshot.sh"'), }, - } + }, }, directories: { left: { directory1: { - label: opt("󰉍 Downloads"), - command: opt("bash -c \"dolphin $HOME/Downloads/\"") + label: opt('󰉍 Downloads'), + command: opt('bash -c "dolphin $HOME/Downloads/"'), }, directory2: { - label: opt("󰉏 Videos"), - command: opt("bash -c \"dolphin $HOME/Videos/\"") + label: opt('󰉏 Videos'), + command: opt('bash -c "dolphin $HOME/Videos/"'), }, directory3: { - label: opt("󰚝 Projects"), - command: opt("bash -c \"dolphin $HOME/Projects/\"") + label: opt('󰚝 Projects'), + command: opt('bash -c "dolphin $HOME/Projects/"'), }, }, right: { directory1: { - label: opt("󱧶 Documents"), - command: opt("bash -c \"dolphin $HOME/Documents/\"") + label: opt('󱧶 Documents'), + command: opt('bash -c "dolphin $HOME/Documents/"'), }, directory2: { - label: opt("󰉏 Pictures"), - command: opt("bash -c \"dolphin $HOME/Pictures/\"") + label: opt('󰉏 Pictures'), + command: opt('bash -c "dolphin $HOME/Pictures/"'), }, directory3: { - label: opt("󱂵 Home"), - command: opt("bash -c \"dolphin $HOME/\"") + label: opt('󱂵 Home'), + command: opt('bash -c "dolphin $HOME/"'), }, - } + }, }, }, clock: { @@ -1050,24 +1044,28 @@ const options = mkOptions(OPTIONS, { }, weather: { interval: opt(60000), - unit: opt("imperial"), - location: opt("Los Angeles"), + unit: opt('imperial'), + location: opt('Los Angeles'), key: opt( - JSON.parse(Utils.readFile(`${App.configDir}/.weather.json`) || "{}")?.weather_api_key || "", + JSON.parse(Utils.readFile(`${App.configDir}/.weather.json`) || '{}')?.weather_api_key || '', ), - } - } + }, + }, }, - terminal: opt("kitty"), + terminal: opt('kitty'), + + tear: opt(false), wallpaper: { enable: opt(true), - image: opt("") + image: opt(''), + pywal: opt(false), }, notifications: { - position: opt("top right"), + position: opt('top right'), + ignore: opt([]), displayedTotal: opt(10), monitor: opt(0), active_monitor: opt(true), @@ -1076,7 +1074,7 @@ const options = mkOptions(OPTIONS, { }, dummy: opt(true), -}) +}); -globalThis["options"] = options -export default options +globalThis['options'] = options; +export default options; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d15b8fb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3089 @@ +{ + "name": "hyprpanel", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hyprpanel", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.5.4", + "@typescript-eslint/eslint-plugin": "^8.5.0", + "@typescript-eslint/parser": "^8.5.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-prettier": "^5.2.1", + "prettier": "^3.3.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.6.2" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz", + "integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/type-utils": "8.5.0", + "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz", + "integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/typescript-estree": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", + "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz", + "integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.5.0", + "@typescript-eslint/utils": "8.5.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", + "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", + "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/typescript-estree": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", + "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", + "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a617f97 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "hyprpanel", + "version": "1.0.0", + "description": "A customizble panel built for Hyprland.", + "main": "config.js", + "directories": { + "lib": "lib" + }, + "scripts": { + "lint": "eslint --config .eslintrc.js .", + "lint:fix": "eslint --config .eslintrc.js .", + "format": "prettier --write 'modules/**/*.ts'" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.5.4", + "@typescript-eslint/eslint-plugin": "^8.5.0", + "@typescript-eslint/parser": "^8.5.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-prettier": "^5.2.1", + "prettier": "^3.3.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.6.2" + } +} diff --git a/scripts/checkUpdates.sh b/scripts/checkUpdates.sh index 69ca9f9..39bf6ec 100755 --- a/scripts/checkUpdates.sh +++ b/scripts/checkUpdates.sh @@ -1,13 +1,42 @@ #!/bin/bash check_arch_updates() { - result=$(yay -Qum --noconfirm 2>/dev/null | wc -l || echo 0) - echo "$result" + official_updates=0 + aur_updates=0 + + if [ "$1" = "-y" ]; then + aur_updates=$(yay -Qum 2>/dev/null | wc -l) + elif [ "$1" = "-p" ]; then + official_updates=$(checkupdates 2>/dev/null | wc -l) + else + official_updates=$(checkupdates 2>/dev/null | wc -l) + aur_updates=$(yay -Qum 2>/dev/null | wc -l) + fi + + total_updates=$((official_updates + aur_updates)) + + echo $total_updates +} + +check_ubuntu_updates() { + result=$(apt-get -s -o Debug::NoLocking=true upgrade | grep -c ^Inst) + echo "$result" +} + +check_fedora_updates() { + result=$(dnf check-update -q | grep -v '^Loaded plugins' | grep -v '^No match for' | wc -l) + echo "$result" } case "$1" in -arch) - check_arch_updates + check_arch_updates "$2" + ;; +-ubuntu) + check_ubuntu_updates + ;; +-fedora) + check_fedora_updates ;; *) echo "0" diff --git a/scss/options_trackers.ts b/scss/options_trackers.ts index b83545c..ef360de 100644 --- a/scss/options_trackers.ts +++ b/scss/options_trackers.ts @@ -1,7 +1,7 @@ -import icons from "lib/icons"; -import { Notify, isAnImage } from "lib/utils"; -import options from "options"; -import Wallpaper from "services/Wallpaper"; +import icons from 'lib/icons'; +import { bash, dependencies, Notify, isAnImage } from 'lib/utils'; +import options from 'options'; +import Wallpaper from 'services/Wallpaper'; const { matugen } = options.theme; const { mode, scheme_type, contrast } = options.theme.matugen_settings; @@ -11,45 +11,48 @@ const ensureMatugenWallpaper = (): void => { if (matugen.value && (!options.wallpaper.image.value.length || !isAnImage(wallpaperPath))) { Notify({ - summary: "Matugen Failed", + summary: 'Matugen Failed', body: "Please select a wallpaper in 'Theming > General' first.", iconName: icons.ui.warning, - timeout: 7000 - }) + timeout: 7000, + }); matugen.value = false; } -} +}; -export const initializeTrackers = (resetCssFunc: Function) => { - matugen.connect("changed", () => { +export const initializeTrackers = (resetCssFunc: () => void): void => { + matugen.connect('changed', () => { ensureMatugenWallpaper(); options.resetTheme(); - }) + }); - mode.connect("changed", () => { + mode.connect('changed', () => { options.resetTheme(); - }) - scheme_type.connect("changed", () => { + }); + scheme_type.connect('changed', () => { options.resetTheme(); - }) - contrast.connect("changed", () => { + }); + contrast.connect('changed', () => { options.resetTheme(); - }) + }); - Wallpaper.connect("changed", () => { - console.info("Wallpaper changed, regenerating Matugen colors...") + Wallpaper.connect('changed', () => { + console.info('Wallpaper changed, regenerating Matugen colors...'); if (options.theme.matugen.value) { options.resetTheme(); resetCssFunc(); } - }) + }); - options.wallpaper.image.connect("changed", () => { + options.wallpaper.image.connect('changed', () => { if ((!Wallpaper.isRunning() && options.theme.matugen.value) || !options.wallpaper.enable.value) { - console.info("Wallpaper path changed, regenerating Matugen colors...") + console.info('Wallpaper path changed, regenerating Matugen colors...'); options.resetTheme(); resetCssFunc(); } - }) - -} + if (options.wallpaper.pywal.value && dependencies('wal')) { + const wallpaperPath = options.wallpaper.image.value; + bash(`wal -i ${wallpaperPath}`); + } + }); +}; diff --git a/scss/style.ts b/scss/style.ts index 46885e0..ebfb935 100644 --- a/scss/style.ts +++ b/scss/style.ts @@ -1,63 +1,61 @@ -import options from "options"; -import { bash, dependencies } from "lib/utils"; -import { MatugenColors } from "lib/types/options"; -import { initializeTrackers } from "./options_trackers"; -import { generateMatugenColors, replaceHexValues } from "../services/matugen/index"; +import options from 'options'; +import { bash, dependencies } from 'lib/utils'; +import { MatugenColors, RecursiveOptionsObject } from 'lib/types/options'; +import { initializeTrackers } from './options_trackers'; +import { generateMatugenColors, replaceHexValues } from '../services/matugen/index'; -const deps = [ - "font", - "theme", - "bar.flatButtons", - "bar.position", - "bar.battery.charging", - "bar.battery.blocks", -]; +const deps = ['font', 'theme', 'bar.flatButtons', 'bar.position', 'bar.battery.charging', 'bar.battery.blocks']; -function extractVariables(theme: typeof options.theme, prefix = "", matugenColors: MatugenColors | undefined) { +function extractVariables(theme: RecursiveOptionsObject, prefix = '', matugenColors?: MatugenColors): string[] { let result = [] as string[]; - for (let key in theme) { - if (theme.hasOwnProperty(key)) { - const value = theme[key]; + for (const key in theme) { + if (!theme.hasOwnProperty(key)) { + continue; + } - const newPrefix = prefix ? `${prefix}-${key}` : key; + const value = theme[key]; - const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value.value); - const replacedValue = isColor && matugenColors !== undefined ? replaceHexValues(value.value, matugenColors) : value.value; - if (typeof value === 'object' && value !== null && !Array.isArray(value)) { - if (typeof value.value !== 'undefined') { - result.push(`$${newPrefix}: ${replacedValue};`); - } else { - result = result.concat(extractVariables(value, newPrefix, matugenColors)); - } - } else if (typeof value === 'function' && value.name === 'opt') { - result.push(`$${newPrefix}: ${replacedValue};`); - } + const newPrefix = prefix ? `${prefix}-${key}` : key; + + const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value.value); + const replacedValue = + isColor && matugenColors !== undefined ? replaceHexValues(value.value, matugenColors) : value.value; + + if (typeof value === 'function') { + result.push(`$${newPrefix}: ${replacedValue};`); + continue; + } + if (typeof value !== 'object' || value === null || Array.isArray(value)) continue; + + if (typeof value.value !== 'undefined') { + result.push(`$${newPrefix}: ${replacedValue};`); + } else { + result = result.concat(extractVariables(value as RecursiveOptionsObject, newPrefix, matugenColors)); } } + return result; } -async function resetCss() { - if (!dependencies("sass")) return; +const resetCss = async (): Promise => { + if (!dependencies('sass')) return; try { const matugenColors = await generateMatugenColors(); - const variables = [ - ...extractVariables(options.theme, '', matugenColors), - ]; + const variables = [...extractVariables(options.theme, '', matugenColors)]; - const vars = `${TMP}/variables.scss` - const css = `${TMP}/main.css` - const scss = `${TMP}/entry.scss` + const vars = `${TMP}/variables.scss`; + const css = `${TMP}/main.css`; + const scss = `${TMP}/entry.scss`; const localScss = `${App.configDir}/scss/main.scss`; const themeVariables = variables; const integratedVariables = themeVariables; - const imports = [vars].map(f => `@import '${f}';`); + const imports = [vars].map((f) => `@import '${f}';`); - await Utils.writeFile(integratedVariables.join("\n"), vars); + await Utils.writeFile(integratedVariables.join('\n'), vars); let mainScss = Utils.readFile(localScss); mainScss = `${imports}\n${mainScss}`; @@ -68,11 +66,9 @@ async function resetCss() { App.applyCss(css, true); } catch (error) { - error instanceof Error - ? logError(error) - : console.error(error); + console.error(error); } -} +}; initializeTrackers(resetCss); diff --git a/scss/style/bar/systray.scss b/scss/style/bar/systray.scss index dcd3716..838207c 100644 --- a/scss/style/bar/systray.scss +++ b/scss/style/bar/systray.scss @@ -1,11 +1,11 @@ .systray button:not(:first-child) { - margin-left: $bar-buttons-systray-spacing; + margin-left: $bar-buttons-systray-spacing; } .systray-icon { - font-size: 1.3em; + font-size: 1.3em; } .style2.systray { - padding: $bar-buttons-padding_y $bar-buttons-padding_x; + padding: $bar-buttons-padding_y $bar-buttons-padding_x; } diff --git a/scss/style/bar/window_title.scss b/scss/style/bar/window_title.scss index 53e4836..92bee80 100644 --- a/scss/style/bar/window_title.scss +++ b/scss/style/bar/window_title.scss @@ -5,6 +5,10 @@ .bar-button-label.windowtitle { color: if($bar-buttons-monochrome, $bar-buttons-text, $bar-buttons-windowtitle-text); margin-left: $bar-buttons-windowtitle-spacing; + + &.no-icon { + margin-left: 0; + } } .style2 { diff --git a/scss/style/bar/workspace.scss b/scss/style/bar/workspace.scss index 776c7f5..3480e87 100644 --- a/scss/style/bar/workspace.scss +++ b/scss/style/bar/workspace.scss @@ -1,81 +1,82 @@ .workspaces { - label { - font-size: 0.2em; - min-width: 4em; - min-height: 4em; - border-radius: 1.9rem * .6; - transition: 300ms * .5; - background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); - color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); + label { + font-size: 0.2em; + min-width: 4em; + min-height: 4em; + border-radius: 1.9rem * 0.6; + transition: 300ms * 0.5; + background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); + color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); - &.occupied { - background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); - color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); - min-width: 4em; - min-height: 4em; - } + &.occupied { + background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); + color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); + min-width: 4em; + min-height: 4em; + } - &.active { - color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active); - background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active); - min-width: 12em; - min-height: 4em; - } + &.active { + color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active); + background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active); + min-width: 12em; + min-height: 4em; + } - &.workspace-icon { - background-color: transparent; - min-width: 0em; - min-height: 0em; - border-radius: 0em; - transition: 300ms * .5; - font-size: 1em; - } + &.workspace-icon { + background-color: transparent; + min-width: 0em; + min-height: 0em; + border-radius: 0em; + transition: 300ms * 0.5; + font-size: 1em; + } - &.workspace-number { - background-color: transparent; - min-width: 0em; - min-height: 0em; - border-radius: 0em; - transition: 0ms; - padding: 0em 0.2em; - font-size: 1.2em; - } + &.workspace-number { + background-color: transparent; + min-width: 0em; + min-height: 0em; + border-radius: 0em; + transition: 0ms; + padding: 0em 0.2em; + font-size: $bar-buttons-workspaces-fontSize; + } - &.underline { - border-top: 0.1em solid transparent; - border-bottom: 0.1em solid $bar-buttons-workspaces-numbered_active_underline_color; - } + &.underline { + border-top: 0.1em solid transparent; + border-bottom: 0.1em solid $bar-buttons-workspaces-numbered_active_underline_color; + } - &.highlight { - color: $bar-buttons-workspaces-numbered_active_highlighted_text_color; - border-radius: $bar-buttons-workspaces-numbered_active_highlight_border; - background-color: $bar-buttons-workspaces-active; - padding: 0em $bar-buttons-workspaces-numbered_active_highlight_padding; + &.highlight { + color: $bar-buttons-workspaces-numbered_active_highlighted_text_color; + border-radius: $bar-buttons-workspaces-numbered_active_highlight_border; + background-color: $bar-buttons-workspaces-active; + padding: 0em $bar-buttons-workspaces-numbered_active_highlight_padding; + } } - } } .workspace-button { - &:hover label { - color: $bar-buttons-workspaces-hover; + &:hover label { + color: $bar-buttons-workspaces-hover; - &.default { - background-color: $bar-buttons-workspaces-hover; + &.default { + background-color: $bar-buttons-workspaces-hover; + } } - } - &:hover .can_underline { - border-top: 0.1em solid transparent; - border-bottom: 0.1em solid if($bar-buttons-monochrome, $bar-buttons-workspaces-hover, $bar-buttons-workspaces-hover); - } + &:hover .can_underline { + border-top: 0.1em solid transparent; + border-bottom: 0.1em solid + if($bar-buttons-monochrome, $bar-buttons-workspaces-hover, $bar-buttons-workspaces-hover); + } - &:hover .can_highlight { - background-color: $bar-buttons-workspaces-hover; - color: $bar-buttons-workspaces-numbered_active_highlighted_text_color; - border-radius: $bar-buttons-workspaces-numbered_active_highlight_border; - } + &:hover .can_highlight { + background-color: $bar-buttons-workspaces-hover; + color: $bar-buttons-workspaces-numbered_active_highlighted_text_color; + border-radius: $bar-buttons-workspaces-numbered_active_highlight_border; + } } .style2.workspaces { - padding: $bar-buttons-padding_y $bar-buttons-padding_x; + padding: $bar-buttons-padding_y $bar-buttons-padding_x; } diff --git a/scss/style/menus/calendar.scss b/scss/style/menus/calendar.scss index 67757b1..ec370e1 100644 --- a/scss/style/menus/calendar.scss +++ b/scss/style/menus/calendar.scss @@ -101,9 +101,9 @@ min-width: 3em; .calendar-menu-weather.today.icon { - image { + label { color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-clock-weather-icon); - font-size: 6em; + font-size: 5em; } } @@ -164,7 +164,8 @@ .hourly-weather-icon { color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-clock-weather-hourly-icon); - margin-bottom: 0.5em; + margin-bottom: 0.25em; + font-size: 1.8em; } .hourly-weather-temp { diff --git a/scss/style/menus/dashboard.scss b/scss/style/menus/dashboard.scss index 6121bdc..d292038 100644 --- a/scss/style/menus/dashboard.scss +++ b/scss/style/menus/dashboard.scss @@ -53,17 +53,25 @@ margin-bottom: 0.75em; } - image { + label { color: if($bar-menus-monochrome, $bar-menus-buttons-text, $bar-menus-menu-dashboard-shortcuts-text); - font-size: 1.5em; + font-size: 1.7em; } &.shutdown { background: if($bar-menus-monochrome, $bar-menus-buttons-default, $bar-menus-menu-dashboard-powermenu-shutdown); + + label { + font-size: 1.9em; + } } &.restart { background: if($bar-menus-monochrome, $bar-menus-buttons-default, $bar-menus-menu-dashboard-powermenu-restart); + + label { + font-size: 1.9em; + } } &.lock { @@ -92,6 +100,7 @@ } } + } } @@ -294,7 +303,7 @@ .stat { label { margin-right: 0.75em; - font-size: 1.5em; + font-size: 1.3em; min-width: 1.65em; } diff --git a/scss/style/menus/menu.scss b/scss/style/menus/menu.scss index 89ebf53..349169a 100644 --- a/scss/style/menus/menu.scss +++ b/scss/style/menus/menu.scss @@ -1,257 +1,256 @@ .menu-slider { - trough { - border-radius: 0.3rem; - background: $surface0; - - highlight, - progress { - background: $peach; - border-radius: 0.3rem; - } - } - - slider { - box-shadow: none; - background-color: transparent; - min-height: 0.6rem; - min-width: 0.6rem; - border: 0rem solid transparent; - border-radius: 0.3rem; - } - - &:hover { trough { - background: $surface0; + border-radius: 0.3rem; + background: $surface0; + + highlight, + progress { + background: $peach; + border-radius: 0.3rem; + } } slider { - background: $overlay0; - box-shadow: none; + box-shadow: none; + background-color: transparent; + min-height: 0.6rem; + min-width: 0.6rem; + border: 0rem solid transparent; + border-radius: 0.3rem; + } + + &:hover { + trough { + background: $surface0; + } + + slider { + background: $overlay0; + box-shadow: none; + } } - } } .menu-switch { - font-size: 1.3em; - background-color: $surface0; - border-radius: 0.2em; - - &:checked { - background: $sky; - } - - trough { - - highlight, - progress { - background-color: $peach; - border-radius: 0.3em; - } - } - - slider { - box-shadow: none; - background-color: $overlay0; - min-height: 1em; - min-width: 1em; - border: 0em solid transparent; + font-size: 1.3em; + background-color: $surface0; border-radius: 0.2em; - margin: 0.1em 0.15em; - } - &:hover { + &:checked { + background: $sky; + } + trough { - background: $surface0; + highlight, + progress { + background-color: $peach; + border-radius: 0.3em; + } } slider { - background: $overlay0; - box-shadow: none; + box-shadow: none; + background-color: $overlay0; + min-height: 1em; + min-width: 1em; + border: 0em solid transparent; + border-radius: 0.2em; + margin: 0.1em 0.15em; } - } - &:active { - background-color: $sky; - } + &:hover { + trough { + background: $surface0; + } + + slider { + background: $overlay0; + box-shadow: none; + } + } + + &:active { + background-color: $sky; + } } .menu-separator { - min-height: .1rem; - margin: .6rem 0rem; - background: $surface1; + min-height: 0.1rem; + margin: 0.6rem 0rem; + background: $surface1; } .menu-items { - background: $crust; - border: $bar-menus-border-size solid $bar-menus-border-color; - border-radius: $bar-menus-border-radius; - color: $text; + background: $crust; + border: $bar-menus-border-size solid $bar-menus-border-color; + border-radius: $bar-menus-border-radius; + color: $text; } .menu-items-container { - border-radius: 0.4em; - font-size: 1.3em; + border-radius: 0.4em; + font-size: 1.3em; } .menu-section-container { - margin: 1em 0em; + margin: 1em 0em; - .menu-label { - color: $text; - font-size: 1.1em; - font-weight: bold; - } + .menu-label { + color: $text; + font-size: 1.1em; + font-weight: bold; + } - .menu-label-container { - background: $base; - border-radius: $bar-menus-card_radius; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; - margin: 0em 1em; - min-height: 2em; - } + .menu-label-container { + background: $base; + border-radius: $bar-menus-card_radius; + border-bottom-left-radius: 0em; + border-bottom-right-radius: 0em; + margin: 0em 1em; + min-height: 2em; + } - &:first-child { - margin-bottom: 0em; - } + &:first-child { + margin-bottom: 0em; + } - &:last-child { - margin-top: 0em; - } + &:last-child { + margin-top: 0em; + } - &:nth-child(2) { - margin-top: 1em; - } + &:nth-child(2) { + margin-top: 1em; + } - .menu-items-section { - background: $base; - border-radius: $bar-menus-card_radius; - border-top-left-radius: 0em; - border-top-right-radius: 0em; - padding: 0.9em; - margin: 0em 1em; - } + .menu-items-section { + background: $base; + border-radius: $bar-menus-card_radius; + border-top-left-radius: 0em; + border-top-right-radius: 0em; + padding: 0.9em; + margin: 0em 1em; + } } .menu-active { - font-size: 0.9em; - font-weight: bold; - margin: 0rem 1em; - margin-bottom: 0.9em; + font-size: 0.9em; + font-weight: bold; + margin: 0rem 1em; + margin-bottom: 0.9em; } .menu-active-container { - &:first-child { - margin-bottom: 0.5em; - } + &:first-child { + margin-bottom: 0.5em; + } } .menu-active-button { - padding: 0.1em; - margin-bottom: -0.2em; + padding: 0.1em; + margin-bottom: -0.2em; - .menu-active-icon { - font-size: 1.4em; - font-weight: bold; + .menu-active-icon { + font-size: 1.4em; + font-weight: bold; + } - } + &.muted image { + color: $maroon; + } - &.muted image { - color: $maroon; - } - - &:hover image { - color: $maroon; - } + &:hover image { + color: $maroon; + } } .menu-active-percentage { - font-size: 0.9em; - min-width: 2.5em; - font-weight: bold; + font-size: 0.9em; + min-width: 2.5em; + font-weight: bold; } .menu-active-slider { - margin-left: 1rem; - margin-right: 1.5rem; + margin-left: 1rem; + margin-right: 1.5rem; } .menu-active-slider * { - min-height: 0.85em; - border-radius: .2em; + min-height: 0.85em; + border-radius: 0.2em; } .menu-slider-container { - margin-bottom: .7rem; + margin-bottom: 0.7rem; } .menu-label-dim { - color: $overlay0; - margin-right: 1rem; - font-size: 1em; - font-weight: bold; + color: $overlay0; + margin-right: 1rem; + font-size: 1em; + font-weight: bold; } .menu-icon-button { - &:hover { - color: $surface2; - } + &:hover { + color: $surface2; + } } .menu-dropdown-label-container { - background: $base; - border-radius: 0.4em; + background: $base; + border-radius: 0.4em; } .menu-button { - margin-bottom: .4em; + margin-bottom: 0.4em; } .menu-button-name { - font-size: 0.95em; - font-weight: bold; - margin-left: 0.5em; - margin-right: 1.2rem; + font-size: 0.95em; + font-weight: bold; + margin-left: 0.5em; + margin-right: 1.2rem; } .menu-button-icon { - font-size: 1.3em; - font-weight: bold; - margin-right: .5rem; + font-size: 1.3em; + font-weight: bold; + margin-right: 0.5rem; } .menu-item-box { - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } .dropdown-menu-container { - min-height: 10em; + min-height: 10em; } .menu-label { - margin: 0.5em 1em; - color: $sky; + margin: 0.5em 1em; + color: $sky; } -.event-top-padding-static * { - min-height: 0em; - margin-top: 2.8em; +.event-box-container { + min-height: 0em; + margin-top: $bar-dropdownGap; } .event-top-padding * { - min-height: 0em; - margin-top: if($bar-floating, $bar-margin_top, 0); + min-height: 0em; + margin-top: if($bar-floating and $bar-location == 'top', $bar-margin_top, 0); + margin-bottom: if($bar-floating and $bar-location == 'bottom', $bar-margin_bottom, 0); } @keyframes spin { - to { - -gtk-icon-transform: rotate(1turn); - } + to { + -gtk-icon-transform: rotate(1turn); + } } image.spinning { - animation-name: spin; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; + animation-name: spin; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; } diff --git a/scss/style/menus/notifications.scss b/scss/style/menus/notifications.scss index b71492e..5f2d735 100644 --- a/scss/style/menus/notifications.scss +++ b/scss/style/menus/notifications.scss @@ -94,7 +94,7 @@ .clear-notifications-button { margin-right: 0.3em; - &:hover label { + &:hover label:not(.removing) { color: transparentize(if($bar-menus-monochrome, $bar-menus-buttons-default, $bar-menus-menu-notifications-clear), 0.5); } } @@ -102,6 +102,10 @@ .clear-notifications-label { color: if($bar-menus-monochrome, $bar-menus-buttons-default, $bar-menus-menu-notifications-clear); font-size: 1.5em; + + &.removing { + color: $bar-menus-buttons-disabled; + } } scrollbar { diff --git a/scss/style/menus/powerdropdown.scss b/scss/style/menus/powerdropdown.scss index 0fe97d2..853835c 100644 --- a/scss/style/menus/powerdropdown.scss +++ b/scss/style/menus/powerdropdown.scss @@ -101,11 +101,11 @@ } } - label { + label:not(.txt-icon) { margin-left: 1em; } - image { + label.txt-icon { min-width: 3.75rem * $bar-menus-menu-power-scaling * 0.01; min-height: 3.75rem * $bar-menus-menu-power-scaling * 0.01; background: $red; diff --git a/scss/style/osd/index.scss b/scss/style/osd/index.scss index 63a1100..8b60d26 100644 --- a/scss/style/osd/index.scss +++ b/scss/style/osd/index.scss @@ -31,8 +31,8 @@ border-radius: if($osd-orientation =="vertical", 0em 0em $osd-radius $osd-radius, $osd-radius 0em 0em $osd-radius ); .osd-icon { - font-size: 2em; - padding: if($osd-orientation =="vertical", 0.2em 0em, 0em 0.2em); + font-size: 2.1em; + padding: if($osd-orientation =="vertical", 0.2em 0em, 0em 0.4em); color: $osd-icon; } } diff --git a/services/Brightness.ts b/services/Brightness.ts index 32d9bdb..cbfec92 100644 --- a/services/Brightness.ts +++ b/services/Brightness.ts @@ -1,70 +1,74 @@ // <3 Aylur for this brightness service -import { bash, dependencies, sh } from "lib/utils" +import { bash, dependencies, sh } from 'lib/utils'; -if (!dependencies("brightnessctl")) - App.quit() +if (!dependencies('brightnessctl')) App.quit(); -const get = (args: string) => Number(Utils.exec(`brightnessctl ${args}`)) -const screen = await bash`ls -w1 /sys/class/backlight | head -1` -const kbd = await bash`ls -w1 /sys/class/leds | head -1` +const get = (args: string): number => Number(Utils.exec(`brightnessctl ${args}`)); +const screen = await bash`ls -w1 /sys/class/backlight | head -1`; +const kbd = await bash`ls -w1 /sys/class/leds | head -1`; class Brightness extends Service { static { - Service.register(this, {}, { - "screen": ["float", "rw"], - "kbd": ["int", "rw"], - }) + Service.register( + this, + {}, + { + screen: ['float', 'rw'], + kbd: ['int', 'rw'], + }, + ); } - #kbdMax = get(`--device ${kbd} max`) - #kbd = get(`--device ${kbd} get`) - #screenMax = get(`--device ${screen} max`) - #screen = get(`--device ${screen} get`) / (get(`--device ${screen} max`) || 1) + #kbdMax = get(`--device ${kbd} max`); + #kbd = get(`--device ${kbd} get`); + #screenMax = get(`--device ${screen} max`); + #screen = get(`--device ${screen} get`) / (get(`--device ${screen} max`) || 1); - get kbd() { return this.#kbd } - get screen() { return this.#screen } + get kbd(): number { + return this.#kbd; + } + get screen(): number { + return this.#screen; + } set kbd(value) { - if (value < 0 || value > this.#kbdMax) - return + if (value < 0 || value > this.#kbdMax) return; sh(`brightnessctl -d ${kbd} s ${value} -q`).then(() => { - this.#kbd = value - this.changed("kbd") - }) + this.#kbd = value; + this.changed('kbd'); + }); } set screen(percent) { - if (percent < 0) - percent = 0 + if (percent < 0) percent = 0; - if (percent > 1) - percent = 1 + if (percent > 1) percent = 1; sh(`brightnessctl set ${Math.round(percent * 100)}% -d ${screen} -q`).then(() => { - this.#screen = percent - this.changed("screen") - }) + this.#screen = percent; + this.changed('screen'); + }); } constructor() { - super() + super(); - const screenPath = `/sys/class/backlight/${screen}/brightness` - const kbdPath = `/sys/class/leds/${kbd}/brightness` + const screenPath = `/sys/class/backlight/${screen}/brightness`; + const kbdPath = `/sys/class/leds/${kbd}/brightness`; - Utils.monitorFile(screenPath, async f => { - const v = await Utils.readFileAsync(f) - this.#screen = Number(v) / this.#screenMax - this.changed("screen") - }) + Utils.monitorFile(screenPath, async (f) => { + const v = await Utils.readFileAsync(f); + this.#screen = Number(v) / this.#screenMax; + this.changed('screen'); + }); - Utils.monitorFile(kbdPath, async f => { - const v = await Utils.readFileAsync(f) - this.#kbd = Number(v) / this.#kbdMax - this.changed("kbd") - }) + Utils.monitorFile(kbdPath, async (f) => { + const v = await Utils.readFileAsync(f); + this.#kbd = Number(v) / this.#kbdMax; + this.changed('kbd'); + }); } } -export default new Brightness +export default new Brightness(); diff --git a/services/Wallpaper.ts b/services/Wallpaper.ts index be87f8a..a27947c 100644 --- a/services/Wallpaper.ts +++ b/services/Wallpaper.ts @@ -1,87 +1,97 @@ -import { dependencies, sh } from "lib/utils" -import options from "options"; -const hyprland = await Service.import("hyprland"); +import { dependencies, sh } from 'lib/utils'; +import options from 'options'; +const hyprland = await Service.import('hyprland'); -const WP = `${Utils.HOME}/.config/background` +const WP = `${Utils.HOME}/.config/background`; class Wallpaper extends Service { static { - Service.register(this, {}, { - "wallpaper": ["string"], - }) + Service.register( + this, + {}, + { + wallpaper: ['string'], + }, + ); } - #blockMonitor = false - #isRunning = false + #blockMonitor = false; + #isRunning = false; - #wallpaper() { - if (!dependencies("swww")) - return + #wallpaper(): void { + if (!dependencies('swww')) return; - hyprland.monitors.map(m => m.name); - sh("hyprctl cursorpos").then(pos => { + hyprland.monitors.map((m) => m.name); + sh('hyprctl cursorpos').then((pos) => { sh([ - "swww", "img", - "--invert-y", - "--transition-type", "grow", - "--transition-duration", "1.5", - "--transition-fps", "30", - "--transition-pos", pos.replace(" ", ""), + 'swww', + 'img', + '--invert-y', + '--transition-type', + 'grow', + '--transition-duration', + '1.5', + '--transition-fps', + '30', + '--transition-pos', + pos.replace(' ', ''), WP, ]).then(() => { - this.changed("wallpaper") - }) - }) + this.changed('wallpaper'); + }); + }); } - async #setWallpaper(path: string) { - this.#blockMonitor = true + async #setWallpaper(path: string): Promise { + this.#blockMonitor = true; - await sh(`cp ${path} ${WP}`) - this.#wallpaper() + await sh(`cp ${path} ${WP}`); + this.#wallpaper(); - this.#blockMonitor = false + this.#blockMonitor = false; } - readonly set = (path: string) => { this.#setWallpaper(path) } - readonly isRunning = () => { return this.#isRunning } + readonly set = (path: string): void => { + this.#setWallpaper(path); + }; + readonly isRunning = (): boolean => { + return this.#isRunning; + }; - get wallpaper() { return WP } + get wallpaper(): string { + return WP; + } constructor() { - super() + super(); - options.wallpaper.enable.connect("changed", () => { + options.wallpaper.enable.connect('changed', () => { if (options.wallpaper.enable.value) { - this.#isRunning = true - Utils.execAsync("swww-daemon") + this.#isRunning = true; + Utils.execAsync('swww-daemon') .then(() => { - this.#wallpaper + this.#wallpaper(); }) - .catch(() => null) + .catch(() => null); } else { - this.#isRunning = false - Utils.execAsync("pkill swww-daemon") - .catch(() => null) + this.#isRunning = false; + Utils.execAsync('pkill swww-daemon').catch(() => null); } + }); - }) + if (!dependencies('swww') || !options.wallpaper.enable.value) return this; - if (!dependencies("swww") || !options.wallpaper.enable.value) - return this - - this.#isRunning = true + this.#isRunning = true; Utils.monitorFile(WP, () => { - if (!this.#blockMonitor) - this.#wallpaper() - }) + if (!this.#blockMonitor) this.#wallpaper(); + }); - Utils.execAsync("swww-daemon") + Utils.execAsync('swww-daemon') .then(() => { - this.#wallpaper + this.#wallpaper(); }) - .catch(() => null) + .catch(() => null); } } -export default new Wallpaper +export default new Wallpaper(); diff --git a/services/matugen/index.ts b/services/matugen/index.ts index 10b0988..e39c3cb 100644 --- a/services/matugen/index.ts +++ b/services/matugen/index.ts @@ -1,16 +1,16 @@ -import { defaultColorMap } from "lib/types/defaults/options"; -import { HexColor, MatugenColors } from "lib/types/options"; -import { getMatugenVariations } from "./variations"; -import { bash, dependencies, Notify, isAnImage } from "lib/utils"; -import options from "options"; -import icons from "lib/icons"; -import { Variable } from "types/variable"; +import { defaultColorMap } from 'lib/types/defaults/options'; +import { ColorMapValue, ColorMapKey, HexColor, MatugenColors } from 'lib/types/options'; +import { getMatugenVariations } from './variations'; +import { bash, dependencies, Notify, isAnImage } from 'lib/utils'; +import options from 'options'; +import icons from 'lib/icons'; +import { Variable } from 'types/variable'; const { scheme_type, contrast } = options.theme.matugen_settings; const { matugen } = options.theme; -const updateOptColor = (color: HexColor, opt: Variable) => { +const updateOptColor = (color: HexColor, opt: Variable): void => { opt.value = color; -} +}; export async function generateMatugenColors(): Promise { if (!matugen.value || !dependencies('matugen')) { @@ -21,18 +21,18 @@ export async function generateMatugenColors(): Promise General' first.", iconName: icons.ui.warning, - timeout: 7000 - }) + timeout: 7000, + }); return; } - const normalizedContrast = contrast.value > 1 ? 1 - : contrast.value < -1 ? -1 - : contrast.value - const contents = await bash(`matugen image ${wallpaperPath} -t scheme-${scheme_type.value} --contrast ${normalizedContrast} --json hex`); + const normalizedContrast = contrast.value > 1 ? 1 : contrast.value < -1 ? -1 : contrast.value; + const contents = await bash( + `matugen image ${wallpaperPath} -t scheme-${scheme_type.value} --contrast ${normalizedContrast} --json hex`, + ); return JSON.parse(contents).colors[options.theme.matugen_settings.mode.value]; } catch (error) { @@ -42,6 +42,10 @@ export async function generateMatugenColors(): Promise { + return defaultColorMap.hasOwnProperty(color); +}; + export const replaceHexValues = (incomingHex: HexColor, matugenColors: MatugenColors): HexColor => { if (!options.theme.matugen.value) { return incomingHex; @@ -49,11 +53,18 @@ export const replaceHexValues = (incomingHex: HexColor, matugenColors: MatugenCo const matugenVariation = getMatugenVariations(matugenColors, options.theme.matugen_settings.variation.value); updateOptColor(matugenVariation.base, options.theme.bar.menus.menu.media.card.color as Variable); - for (let curColor of Object.keys(defaultColorMap)) { - if (defaultColorMap[curColor] === incomingHex) { - return matugenVariation[curColor]; + + for (const curColor of Object.keys(defaultColorMap)) { + const currentColor: string = curColor; + if (!isColorValid(currentColor)) { + continue; + } + + const curColorValue: ColorMapValue = defaultColorMap[currentColor]; + if (curColorValue === incomingHex) { + return matugenVariation[currentColor]; } } return incomingHex; -} +}; diff --git a/services/matugen/variations.ts b/services/matugen/variations.ts index 37c1456..7daba85 100644 --- a/services/matugen/variations.ts +++ b/services/matugen/variations.ts @@ -1,571 +1,570 @@ -import { MatugenColors, MatugenVariation } from "lib/types/options"; +import { MatugenColors, MatugenVariation, MatugenVariations } from 'lib/types/options'; /* * NOTE: This maps the values of the default colors to the values generated by Matugen. * Each of the variations are carefully tested and curated to make sure that colors don't * have weird luminocity overlaps (light on light, dark on dark). */ -export const getMatugenVariations = (matugenColors: MatugenColors, variation: MatugenVariation) => { +export const getMatugenVariations = (matugenColors: MatugenColors, variation: MatugenVariations): MatugenVariation => { const matVtns = { - "standard_1": { - "rosewater": matugenColors.secondary, - "flamingo": matugenColors.secondary, - "pink": matugenColors.tertiary, - "mauve": matugenColors.primary, - "red": matugenColors.tertiary, - "maroon": matugenColors.primary, - "peach": matugenColors.tertiary, - "yellow": matugenColors.secondary, - "green": matugenColors.primary, - "teal": matugenColors.secondary, - "sky": matugenColors.secondary, - "sapphire": matugenColors.primary, - "blue": matugenColors.primary, - "lavender": matugenColors.primary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.primary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.tertiary, - "peach2": matugenColors.tertiary, - "pink2": matugenColors.tertiary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "overlay1_2": matugenColors.outline, - "text2": matugenColors.on_background, - "lavender2": matugenColors.primary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.primary, - "mauve2": matugenColors.primary, - "green2": matugenColors.primary, - "surface2_2": matugenColors.surface, - "sky2": matugenColors.secondary, - "teal2": matugenColors.secondary, - "yellow2": matugenColors.secondary, - "pink3": matugenColors.tertiary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.primary, - "mauve3": matugenColors.primary, - "green3": matugenColors.primary, - "sky3": matugenColors.secondary, - "teal3": matugenColors.secondary, - "yellow3": matugenColors.secondary, - "maroon3": matugenColors.primary, - "crust3": matugenColors.surface_dim, + standard_1: { + rosewater: matugenColors.secondary, + flamingo: matugenColors.secondary, + pink: matugenColors.tertiary, + mauve: matugenColors.primary, + red: matugenColors.tertiary, + maroon: matugenColors.primary, + peach: matugenColors.tertiary, + yellow: matugenColors.secondary, + green: matugenColors.primary, + teal: matugenColors.secondary, + sky: matugenColors.secondary, + sapphire: matugenColors.primary, + blue: matugenColors.primary, + lavender: matugenColors.primary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.primary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.tertiary, + peach2: matugenColors.tertiary, + pink2: matugenColors.tertiary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + overlay1_2: matugenColors.outline, + text2: matugenColors.on_background, + lavender2: matugenColors.primary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.primary, + mauve2: matugenColors.primary, + green2: matugenColors.primary, + surface2_2: matugenColors.surface, + sky2: matugenColors.secondary, + teal2: matugenColors.secondary, + yellow2: matugenColors.secondary, + pink3: matugenColors.tertiary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.primary, + mauve3: matugenColors.primary, + green3: matugenColors.primary, + sky3: matugenColors.secondary, + teal3: matugenColors.secondary, + yellow3: matugenColors.secondary, + maroon3: matugenColors.primary, + crust3: matugenColors.surface_dim, }, - "standard_2": { - "rosewater": matugenColors.primary, - "flamingo": matugenColors.primary, - "pink": matugenColors.tertiary, - "mauve": matugenColors.secondary, - "red": matugenColors.tertiary, - "maroon": matugenColors.secondary, - "peach": matugenColors.tertiary, - "yellow": matugenColors.primary, - "green": matugenColors.secondary, - "teal": matugenColors.primary, - "sky": matugenColors.primary, - "sapphire": matugenColors.secondary, - "blue": matugenColors.secondary, - "lavender": matugenColors.secondary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.tertiary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.tertiary, - "peach2": matugenColors.tertiary, - "pink2": matugenColors.tertiary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "overlay1_2": matugenColors.outline, - "text2": matugenColors.on_background, - "lavender2": matugenColors.secondary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.secondary, - "surface2_2": matugenColors.surface, - "mauve2": matugenColors.secondary, - "green2": matugenColors.secondary, - "sky2": matugenColors.primary, - "teal2": matugenColors.primary, - "yellow2": matugenColors.primary, - "pink3": matugenColors.tertiary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.secondary, - "mauve3": matugenColors.secondary, - "green3": matugenColors.secondary, - "sky3": matugenColors.primary, - "teal3": matugenColors.primary, - "yellow3": matugenColors.primary, - "maroon3": matugenColors.secondary, - "crust3": matugenColors.surface_dim, + standard_2: { + rosewater: matugenColors.primary, + flamingo: matugenColors.primary, + pink: matugenColors.tertiary, + mauve: matugenColors.secondary, + red: matugenColors.tertiary, + maroon: matugenColors.secondary, + peach: matugenColors.tertiary, + yellow: matugenColors.primary, + green: matugenColors.secondary, + teal: matugenColors.primary, + sky: matugenColors.primary, + sapphire: matugenColors.secondary, + blue: matugenColors.secondary, + lavender: matugenColors.secondary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.tertiary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.tertiary, + peach2: matugenColors.tertiary, + pink2: matugenColors.tertiary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + overlay1_2: matugenColors.outline, + text2: matugenColors.on_background, + lavender2: matugenColors.secondary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.secondary, + surface2_2: matugenColors.surface, + mauve2: matugenColors.secondary, + green2: matugenColors.secondary, + sky2: matugenColors.primary, + teal2: matugenColors.primary, + yellow2: matugenColors.primary, + pink3: matugenColors.tertiary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.secondary, + mauve3: matugenColors.secondary, + green3: matugenColors.secondary, + sky3: matugenColors.primary, + teal3: matugenColors.primary, + yellow3: matugenColors.primary, + maroon3: matugenColors.secondary, + crust3: matugenColors.surface_dim, }, - "standard_3": { - "rosewater": matugenColors.secondary, - "flamingo": matugenColors.secondary, - "pink": matugenColors.secondary, - "mauve": matugenColors.primary, - "red": matugenColors.secondary, - "maroon": matugenColors.primary, - "peach": matugenColors.secondary, - "yellow": matugenColors.secondary, - "green": matugenColors.primary, - "teal": matugenColors.secondary, - "sky": matugenColors.secondary, - "sapphire": matugenColors.primary, - "blue": matugenColors.primary, - "lavender": matugenColors.primary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.secondary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.secondary, - "peach2": matugenColors.secondary, - "pink2": matugenColors.secondary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "surface2_2": matugenColors.surface, - "overlay1_2": matugenColors.outline, - "text2": matugenColors.on_background, - "lavender2": matugenColors.primary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.primary, - "mauve2": matugenColors.primary, - "green2": matugenColors.primary, - "sky2": matugenColors.secondary, - "teal2": matugenColors.secondary, - "yellow2": matugenColors.secondary, - "pink3": matugenColors.secondary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.primary, - "mauve3": matugenColors.primary, - "green3": matugenColors.primary, - "sky3": matugenColors.secondary, - "teal3": matugenColors.secondary, - "yellow3": matugenColors.secondary, - "maroon3": matugenColors.primary, - "crust3": matugenColors.surface_dim, + standard_3: { + rosewater: matugenColors.secondary, + flamingo: matugenColors.secondary, + pink: matugenColors.secondary, + mauve: matugenColors.primary, + red: matugenColors.secondary, + maroon: matugenColors.primary, + peach: matugenColors.secondary, + yellow: matugenColors.secondary, + green: matugenColors.primary, + teal: matugenColors.secondary, + sky: matugenColors.secondary, + sapphire: matugenColors.primary, + blue: matugenColors.primary, + lavender: matugenColors.primary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.secondary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.secondary, + peach2: matugenColors.secondary, + pink2: matugenColors.secondary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + surface2_2: matugenColors.surface, + overlay1_2: matugenColors.outline, + text2: matugenColors.on_background, + lavender2: matugenColors.primary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.primary, + mauve2: matugenColors.primary, + green2: matugenColors.primary, + sky2: matugenColors.secondary, + teal2: matugenColors.secondary, + yellow2: matugenColors.secondary, + pink3: matugenColors.secondary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.primary, + mauve3: matugenColors.primary, + green3: matugenColors.primary, + sky3: matugenColors.secondary, + teal3: matugenColors.secondary, + yellow3: matugenColors.secondary, + maroon3: matugenColors.primary, + crust3: matugenColors.surface_dim, }, - "vivid_1": { - "rosewater": matugenColors.surface, - "flamingo": matugenColors.surface, - "pink": matugenColors.surface, - "mauve": matugenColors.surface, - "red": matugenColors.surface, - "maroon": matugenColors.surface, - "peach": matugenColors.surface, - "yellow": matugenColors.surface, - "green": matugenColors.surface, - "teal": matugenColors.surface, - "sky": matugenColors.surface, - "sapphire": matugenColors.surface, - "blue": matugenColors.surface, - "lavender": matugenColors.surface, - "text": matugenColors.surface, - "subtext1": matugenColors.primary_container, - "subtext2": matugenColors.primary_container, - "overlay2": matugenColors.primary_container, - "overlay1": matugenColors.primary_container, - "overlay0": matugenColors.primary_container, - "surface2": matugenColors.surface_container_high, - "surface1": matugenColors.surface_container_high, - "surface0": matugenColors.surface_container_high, - "base2": matugenColors.primary, - "base": matugenColors.primary, - "mantle": matugenColors.surface_container_low, - "crust": matugenColors.surface_container_lowest, - "red2": matugenColors.primary_container, - "peach2": matugenColors.primary_container, - "pink2": matugenColors.primary_container, - "mantle2": matugenColors.primary, - "surface1_2": matugenColors.primary, - "surface0_2": matugenColors.primary, - "overlay1_2": matugenColors.surface_container_high, - "text2": matugenColors.outline, - "lavender2": matugenColors.primary_container, - "crust2": matugenColors.primary, - "maroon2": matugenColors.primary_container, - "mauve2": matugenColors.primary_container, - "surface2_2": matugenColors.primary_container, - "green2": matugenColors.primary_container, - "sky2": matugenColors.primary_container, - "teal2": matugenColors.primary_container, - "yellow2": matugenColors.primary_container, - "pink3": matugenColors.primary_fixed, - "red3": matugenColors.secondary, - "mantle3": matugenColors.primary, - "surface0_3": matugenColors.primary, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.primary, - "lavender3": matugenColors.primary, - "mauve3": matugenColors.primary, - "green3": matugenColors.primary_fixed, - "sky3": matugenColors.primary, - "teal3": matugenColors.primary, - "yellow3": matugenColors.primary_fixed, - "maroon3": matugenColors.primary_fixed, - "crust3": matugenColors.primary, + vivid_1: { + rosewater: matugenColors.surface, + flamingo: matugenColors.surface, + pink: matugenColors.surface, + mauve: matugenColors.surface, + red: matugenColors.surface, + maroon: matugenColors.surface, + peach: matugenColors.surface, + yellow: matugenColors.surface, + green: matugenColors.surface, + teal: matugenColors.surface, + sky: matugenColors.surface, + sapphire: matugenColors.surface, + blue: matugenColors.surface, + lavender: matugenColors.surface, + text: matugenColors.surface, + subtext1: matugenColors.primary_container, + subtext2: matugenColors.primary_container, + overlay2: matugenColors.primary_container, + overlay1: matugenColors.primary_container, + overlay0: matugenColors.primary_container, + surface2: matugenColors.surface_container_high, + surface1: matugenColors.surface_container_high, + surface0: matugenColors.surface_container_high, + base2: matugenColors.primary, + base: matugenColors.primary, + mantle: matugenColors.surface_container_low, + crust: matugenColors.surface_container_lowest, + red2: matugenColors.primary_container, + peach2: matugenColors.primary_container, + pink2: matugenColors.primary_container, + mantle2: matugenColors.primary, + surface1_2: matugenColors.primary, + surface0_2: matugenColors.primary, + overlay1_2: matugenColors.surface_container_high, + text2: matugenColors.outline, + lavender2: matugenColors.primary_container, + crust2: matugenColors.primary, + maroon2: matugenColors.primary_container, + mauve2: matugenColors.primary_container, + surface2_2: matugenColors.primary_container, + green2: matugenColors.primary_container, + sky2: matugenColors.primary_container, + teal2: matugenColors.primary_container, + yellow2: matugenColors.primary_container, + pink3: matugenColors.primary_fixed, + red3: matugenColors.secondary, + mantle3: matugenColors.primary, + surface0_3: matugenColors.primary, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.primary, + lavender3: matugenColors.primary, + mauve3: matugenColors.primary, + green3: matugenColors.primary_fixed, + sky3: matugenColors.primary, + teal3: matugenColors.primary, + yellow3: matugenColors.primary_fixed, + maroon3: matugenColors.primary_fixed, + crust3: matugenColors.primary, }, - "vivid_2": { - "rosewater": matugenColors.surface, - "flamingo": matugenColors.surface, - "pink": matugenColors.surface, - "mauve": matugenColors.surface, - "red": matugenColors.surface, - "maroon": matugenColors.surface, - "peach": matugenColors.surface, - "yellow": matugenColors.surface, - "green": matugenColors.surface, - "teal": matugenColors.surface, - "sky": matugenColors.surface, - "sapphire": matugenColors.surface, - "blue": matugenColors.surface, - "lavender": matugenColors.surface, - "text": matugenColors.surface, - "subtext1": matugenColors.secondary_container, - "subtext2": matugenColors.secondary_container, - "overlay2": matugenColors.secondary_container, - "overlay1": matugenColors.secondary_container, - "overlay0": matugenColors.secondary_container, - "surface2": matugenColors.surface_container_high, - "surface1": matugenColors.surface_container_high, - "surface0": matugenColors.surface_container_high, - "base2": matugenColors.secondary, - "base": matugenColors.secondary, - "mantle": matugenColors.surface_container_low, - "crust": matugenColors.surface_container_lowest, - "red2": matugenColors.secondary_container, - "peach2": matugenColors.secondary_container, - "pink2": matugenColors.secondary_container, - "surface2_2": matugenColors.primary_container, - "mantle2": matugenColors.secondary, - "surface1_2": matugenColors.secondary, - "surface0_2": matugenColors.secondary, - "overlay1_2": matugenColors.surface_container_high, - "text2": matugenColors.outline, - "lavender2": matugenColors.secondary_container, - "crust2": matugenColors.secondary, - "maroon2": matugenColors.secondary_container, - "mauve2": matugenColors.secondary_container, - "green2": matugenColors.secondary_container, - "sky2": matugenColors.secondary_container, - "teal2": matugenColors.secondary_container, - "yellow2": matugenColors.secondary_container, - "pink3": matugenColors.secondary_fixed, - "red3": matugenColors.secondary, - "mantle3": matugenColors.secondary, - "surface0_3": matugenColors.secondary, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.secondary, - "lavender3": matugenColors.secondary, - "mauve3": matugenColors.secondary, - "green3": matugenColors.secondary_fixed, - "sky3": matugenColors.secondary, - "teal3": matugenColors.secondary, - "yellow3": matugenColors.secondary_fixed, - "maroon3": matugenColors.secondary_fixed, - "crust3": matugenColors.secondary, + vivid_2: { + rosewater: matugenColors.surface, + flamingo: matugenColors.surface, + pink: matugenColors.surface, + mauve: matugenColors.surface, + red: matugenColors.surface, + maroon: matugenColors.surface, + peach: matugenColors.surface, + yellow: matugenColors.surface, + green: matugenColors.surface, + teal: matugenColors.surface, + sky: matugenColors.surface, + sapphire: matugenColors.surface, + blue: matugenColors.surface, + lavender: matugenColors.surface, + text: matugenColors.surface, + subtext1: matugenColors.secondary_container, + subtext2: matugenColors.secondary_container, + overlay2: matugenColors.secondary_container, + overlay1: matugenColors.secondary_container, + overlay0: matugenColors.secondary_container, + surface2: matugenColors.surface_container_high, + surface1: matugenColors.surface_container_high, + surface0: matugenColors.surface_container_high, + base2: matugenColors.secondary, + base: matugenColors.secondary, + mantle: matugenColors.surface_container_low, + crust: matugenColors.surface_container_lowest, + red2: matugenColors.secondary_container, + peach2: matugenColors.secondary_container, + pink2: matugenColors.secondary_container, + surface2_2: matugenColors.primary_container, + mantle2: matugenColors.secondary, + surface1_2: matugenColors.secondary, + surface0_2: matugenColors.secondary, + overlay1_2: matugenColors.surface_container_high, + text2: matugenColors.outline, + lavender2: matugenColors.secondary_container, + crust2: matugenColors.secondary, + maroon2: matugenColors.secondary_container, + mauve2: matugenColors.secondary_container, + green2: matugenColors.secondary_container, + sky2: matugenColors.secondary_container, + teal2: matugenColors.secondary_container, + yellow2: matugenColors.secondary_container, + pink3: matugenColors.secondary_fixed, + red3: matugenColors.secondary, + mantle3: matugenColors.secondary, + surface0_3: matugenColors.secondary, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.secondary, + lavender3: matugenColors.secondary, + mauve3: matugenColors.secondary, + green3: matugenColors.secondary_fixed, + sky3: matugenColors.secondary, + teal3: matugenColors.secondary, + yellow3: matugenColors.secondary_fixed, + maroon3: matugenColors.secondary_fixed, + crust3: matugenColors.secondary, }, - "vivid_3": { - "rosewater": matugenColors.surface, - "flamingo": matugenColors.surface, - "pink": matugenColors.surface, - "mauve": matugenColors.surface, - "red": matugenColors.surface, - "maroon": matugenColors.surface, - "peach": matugenColors.surface, - "yellow": matugenColors.surface, - "green": matugenColors.surface, - "teal": matugenColors.surface, - "sky": matugenColors.surface, - "sapphire": matugenColors.surface, - "blue": matugenColors.surface, - "lavender": matugenColors.surface, - "text": matugenColors.surface, - "subtext1": matugenColors.tertiary_container, - "subtext2": matugenColors.tertiary_container, - "overlay2": matugenColors.tertiary_container, - "overlay1": matugenColors.tertiary_container, - "overlay0": matugenColors.tertiary_container, - "surface2": matugenColors.surface_container_high, - "surface1": matugenColors.surface_container_high, - "surface0": matugenColors.surface_container_high, - "base2": matugenColors.tertiary, - "base": matugenColors.tertiary, - "mantle": matugenColors.surface_container_low, - "crust": matugenColors.surface_container_lowest, - "red2": matugenColors.tertiary_container, - "peach2": matugenColors.tertiary_container, - "pink2": matugenColors.tertiary_container, - "mantle2": matugenColors.tertiary, - "surface1_2": matugenColors.tertiary, - "surface0_2": matugenColors.tertiary, - "overlay1_2": matugenColors.surface_container_high, - "text2": matugenColors.outline, - "lavender2": matugenColors.tertiary_container, - "surface2_2": matugenColors.primary_container, - "crust2": matugenColors.tertiary, - "maroon2": matugenColors.tertiary_container, - "mauve2": matugenColors.tertiary_container, - "green2": matugenColors.tertiary_container, - "sky2": matugenColors.tertiary_container, - "teal2": matugenColors.tertiary_container, - "yellow2": matugenColors.tertiary_container, - "pink3": matugenColors.tertiary_fixed, - "red3": matugenColors.secondary, - "mantle3": matugenColors.tertiary, - "surface0_3": matugenColors.tertiary, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.tertiary, - "lavender3": matugenColors.tertiary, - "mauve3": matugenColors.tertiary, - "green3": matugenColors.tertiary_fixed, - "sky3": matugenColors.tertiary, - "teal3": matugenColors.tertiary, - "yellow3": matugenColors.tertiary_fixed, - "maroon3": matugenColors.tertiary_fixed, - "crust3": matugenColors.tertiary, + vivid_3: { + rosewater: matugenColors.surface, + flamingo: matugenColors.surface, + pink: matugenColors.surface, + mauve: matugenColors.surface, + red: matugenColors.surface, + maroon: matugenColors.surface, + peach: matugenColors.surface, + yellow: matugenColors.surface, + green: matugenColors.surface, + teal: matugenColors.surface, + sky: matugenColors.surface, + sapphire: matugenColors.surface, + blue: matugenColors.surface, + lavender: matugenColors.surface, + text: matugenColors.surface, + subtext1: matugenColors.tertiary_container, + subtext2: matugenColors.tertiary_container, + overlay2: matugenColors.tertiary_container, + overlay1: matugenColors.tertiary_container, + overlay0: matugenColors.tertiary_container, + surface2: matugenColors.surface_container_high, + surface1: matugenColors.surface_container_high, + surface0: matugenColors.surface_container_high, + base2: matugenColors.tertiary, + base: matugenColors.tertiary, + mantle: matugenColors.surface_container_low, + crust: matugenColors.surface_container_lowest, + red2: matugenColors.tertiary_container, + peach2: matugenColors.tertiary_container, + pink2: matugenColors.tertiary_container, + mantle2: matugenColors.tertiary, + surface1_2: matugenColors.tertiary, + surface0_2: matugenColors.tertiary, + overlay1_2: matugenColors.surface_container_high, + text2: matugenColors.outline, + lavender2: matugenColors.tertiary_container, + surface2_2: matugenColors.primary_container, + crust2: matugenColors.tertiary, + maroon2: matugenColors.tertiary_container, + mauve2: matugenColors.tertiary_container, + green2: matugenColors.tertiary_container, + sky2: matugenColors.tertiary_container, + teal2: matugenColors.tertiary_container, + yellow2: matugenColors.tertiary_container, + pink3: matugenColors.tertiary_fixed, + red3: matugenColors.secondary, + mantle3: matugenColors.tertiary, + surface0_3: matugenColors.tertiary, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.tertiary, + lavender3: matugenColors.tertiary, + mauve3: matugenColors.tertiary, + green3: matugenColors.tertiary_fixed, + sky3: matugenColors.tertiary, + teal3: matugenColors.tertiary, + yellow3: matugenColors.tertiary_fixed, + maroon3: matugenColors.tertiary_fixed, + crust3: matugenColors.tertiary, }, - "monochrome_1": { - "rosewater": matugenColors.primary, - "flamingo": matugenColors.primary, - "pink": matugenColors.primary, - "mauve": matugenColors.primary, - "red": matugenColors.primary, - "maroon": matugenColors.primary, - "peach": matugenColors.primary, - "yellow": matugenColors.primary, - "green": matugenColors.primary, - "teal": matugenColors.primary, - "sky": matugenColors.primary, - "sapphire": matugenColors.primary, - "blue": matugenColors.primary, - "lavender": matugenColors.primary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.primary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.primary, - "peach2": matugenColors.primary, - "pink2": matugenColors.primary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "surface2_2": matugenColors.surface, - "overlay1_2": matugenColors.outline, - "text2": matugenColors.on_background, - "lavender2": matugenColors.primary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.primary, - "mauve2": matugenColors.primary, - "green2": matugenColors.primary, - "sky2": matugenColors.primary, - "teal2": matugenColors.primary, - "yellow2": matugenColors.primary, - "pink3": matugenColors.primary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.primary, - "mauve3": matugenColors.primary, - "green3": matugenColors.primary, - "sky3": matugenColors.primary, - "teal3": matugenColors.primary, - "yellow3": matugenColors.primary, - "maroon3": matugenColors.primary, - "crust3": matugenColors.surface_dim, + monochrome_1: { + rosewater: matugenColors.primary, + flamingo: matugenColors.primary, + pink: matugenColors.primary, + mauve: matugenColors.primary, + red: matugenColors.primary, + maroon: matugenColors.primary, + peach: matugenColors.primary, + yellow: matugenColors.primary, + green: matugenColors.primary, + teal: matugenColors.primary, + sky: matugenColors.primary, + sapphire: matugenColors.primary, + blue: matugenColors.primary, + lavender: matugenColors.primary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.primary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.primary, + peach2: matugenColors.primary, + pink2: matugenColors.primary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + surface2_2: matugenColors.surface, + overlay1_2: matugenColors.outline, + text2: matugenColors.on_background, + lavender2: matugenColors.primary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.primary, + mauve2: matugenColors.primary, + green2: matugenColors.primary, + sky2: matugenColors.primary, + teal2: matugenColors.primary, + yellow2: matugenColors.primary, + pink3: matugenColors.primary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.primary, + mauve3: matugenColors.primary, + green3: matugenColors.primary, + sky3: matugenColors.primary, + teal3: matugenColors.primary, + yellow3: matugenColors.primary, + maroon3: matugenColors.primary, + crust3: matugenColors.surface_dim, }, - "monochrome_2": { - "rosewater": matugenColors.secondary, - "flamingo": matugenColors.secondary, - "pink": matugenColors.secondary, - "mauve": matugenColors.secondary, - "red": matugenColors.secondary, - "maroon": matugenColors.secondary, - "peach": matugenColors.secondary, - "yellow": matugenColors.secondary, - "green": matugenColors.secondary, - "teal": matugenColors.secondary, - "sky": matugenColors.secondary, - "sapphire": matugenColors.secondary, - "blue": matugenColors.secondary, - "lavender": matugenColors.secondary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.secondary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.secondary, - "peach2": matugenColors.secondary, - "pink2": matugenColors.secondary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "overlay1_2": matugenColors.outline, - "surface2_2": matugenColors.surface, - "text2": matugenColors.on_background, - "lavender2": matugenColors.secondary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.secondary, - "mauve2": matugenColors.secondary, - "green2": matugenColors.secondary, - "sky2": matugenColors.secondary, - "teal2": matugenColors.secondary, - "yellow2": matugenColors.secondary, - "pink3": matugenColors.secondary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.secondary, - "mauve3": matugenColors.secondary, - "green3": matugenColors.secondary, - "sky3": matugenColors.secondary, - "teal3": matugenColors.secondary, - "yellow3": matugenColors.secondary, - "maroon3": matugenColors.secondary, - "crust3": matugenColors.surface_dim, + monochrome_2: { + rosewater: matugenColors.secondary, + flamingo: matugenColors.secondary, + pink: matugenColors.secondary, + mauve: matugenColors.secondary, + red: matugenColors.secondary, + maroon: matugenColors.secondary, + peach: matugenColors.secondary, + yellow: matugenColors.secondary, + green: matugenColors.secondary, + teal: matugenColors.secondary, + sky: matugenColors.secondary, + sapphire: matugenColors.secondary, + blue: matugenColors.secondary, + lavender: matugenColors.secondary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.secondary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.secondary, + peach2: matugenColors.secondary, + pink2: matugenColors.secondary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + overlay1_2: matugenColors.outline, + surface2_2: matugenColors.surface, + text2: matugenColors.on_background, + lavender2: matugenColors.secondary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.secondary, + mauve2: matugenColors.secondary, + green2: matugenColors.secondary, + sky2: matugenColors.secondary, + teal2: matugenColors.secondary, + yellow2: matugenColors.secondary, + pink3: matugenColors.secondary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.secondary, + mauve3: matugenColors.secondary, + green3: matugenColors.secondary, + sky3: matugenColors.secondary, + teal3: matugenColors.secondary, + yellow3: matugenColors.secondary, + maroon3: matugenColors.secondary, + crust3: matugenColors.surface_dim, }, - "monochrome_3": { - "rosewater": matugenColors.tertiary, - "flamingo": matugenColors.tertiary, - "pink": matugenColors.tertiary, - "mauve": matugenColors.tertiary, - "red": matugenColors.tertiary, - "maroon": matugenColors.tertiary, - "peach": matugenColors.tertiary, - "yellow": matugenColors.tertiary, - "green": matugenColors.tertiary, - "teal": matugenColors.tertiary, - "sky": matugenColors.tertiary, - "sapphire": matugenColors.tertiary, - "blue": matugenColors.tertiary, - "lavender": matugenColors.tertiary, - "text": matugenColors.on_background, - "subtext1": matugenColors.outline, - "subtext2": matugenColors.outline, - "overlay2": matugenColors.outline, - "overlay1": matugenColors.outline, - "overlay0": matugenColors.outline, - "surface2": matugenColors.outline, - "surface1": matugenColors.surface_bright, - "surface0": matugenColors.surface_bright, - "base2": matugenColors.inverse_on_surface, - "base": matugenColors.inverse_on_surface, - "mantle": matugenColors.surface_dim, - "crust": matugenColors.surface_dim, - "notifications_closer": matugenColors.tertiary, - "notifications_background": matugenColors.surface_dim, - "dashboard_btn_text": matugenColors.surface_dim, - "red2": matugenColors.tertiary, - "peach2": matugenColors.tertiary, - "pink2": matugenColors.tertiary, - "mantle2": matugenColors.surface_dim, - "surface1_2": matugenColors.inverse_on_surface, - "surface0_2": matugenColors.surface_bright, - "overlay1_2": matugenColors.outline, - "text2": matugenColors.on_background, - "lavender2": matugenColors.tertiary, - "crust2": matugenColors.surface_dim, - "maroon2": matugenColors.tertiary, - "surface2_2": matugenColors.surface, - "mauve2": matugenColors.tertiary, - "green2": matugenColors.tertiary, - "sky2": matugenColors.tertiary, - "teal2": matugenColors.tertiary, - "yellow2": matugenColors.tertiary, - "pink3": matugenColors.tertiary, - "red3": matugenColors.secondary, - "mantle3": matugenColors.inverse_on_surface, - "surface0_3": matugenColors.outline, - "surface2_3": matugenColors.outline, - "overlay1_3": matugenColors.outline, - "lavender3": matugenColors.tertiary, - "mauve3": matugenColors.tertiary, - "green3": matugenColors.tertiary, - "sky3": matugenColors.tertiary, - "teal3": matugenColors.tertiary, - "yellow3": matugenColors.tertiary, - "maroon3": matugenColors.tertiary, - "crust3": matugenColors.surface_dim, + monochrome_3: { + rosewater: matugenColors.tertiary, + flamingo: matugenColors.tertiary, + pink: matugenColors.tertiary, + mauve: matugenColors.tertiary, + red: matugenColors.tertiary, + maroon: matugenColors.tertiary, + peach: matugenColors.tertiary, + yellow: matugenColors.tertiary, + green: matugenColors.tertiary, + teal: matugenColors.tertiary, + sky: matugenColors.tertiary, + sapphire: matugenColors.tertiary, + blue: matugenColors.tertiary, + lavender: matugenColors.tertiary, + text: matugenColors.on_background, + subtext1: matugenColors.outline, + subtext2: matugenColors.outline, + overlay2: matugenColors.outline, + overlay1: matugenColors.outline, + overlay0: matugenColors.outline, + surface2: matugenColors.outline, + surface1: matugenColors.surface_bright, + surface0: matugenColors.surface_bright, + base2: matugenColors.inverse_on_surface, + base: matugenColors.inverse_on_surface, + mantle: matugenColors.surface_dim, + crust: matugenColors.surface_dim, + notifications_closer: matugenColors.tertiary, + notifications_background: matugenColors.surface_dim, + dashboard_btn_text: matugenColors.surface_dim, + red2: matugenColors.tertiary, + peach2: matugenColors.tertiary, + pink2: matugenColors.tertiary, + mantle2: matugenColors.surface_dim, + surface1_2: matugenColors.inverse_on_surface, + surface0_2: matugenColors.surface_bright, + overlay1_2: matugenColors.outline, + text2: matugenColors.on_background, + lavender2: matugenColors.tertiary, + crust2: matugenColors.surface_dim, + maroon2: matugenColors.tertiary, + surface2_2: matugenColors.surface, + mauve2: matugenColors.tertiary, + green2: matugenColors.tertiary, + sky2: matugenColors.tertiary, + teal2: matugenColors.tertiary, + yellow2: matugenColors.tertiary, + pink3: matugenColors.tertiary, + red3: matugenColors.secondary, + mantle3: matugenColors.inverse_on_surface, + surface0_3: matugenColors.outline, + surface2_3: matugenColors.outline, + overlay1_3: matugenColors.outline, + lavender3: matugenColors.tertiary, + mauve3: matugenColors.tertiary, + green3: matugenColors.tertiary, + sky3: matugenColors.tertiary, + teal3: matugenColors.tertiary, + yellow3: matugenColors.tertiary, + maroon3: matugenColors.tertiary, + crust3: matugenColors.surface_dim, }, - }; return matVtns[variation]; -} +}; diff --git a/services/screen_record.sh b/services/screen_record.sh index 87544e1..9289523 100755 --- a/services/screen_record.sh +++ b/services/screen_record.sh @@ -26,8 +26,10 @@ startRecording() { echo "Usage: $0 start screen [screen_name]" exit 1 fi + + GPU_TYPE=$(lspci | grep -E 'VGA|3D' | grep -Ev '00:02.0|Integrated' > /dev/null && echo "" || echo "-encoder cpu") - gpu-screen-recorder -w "$target" -f 60 -a "$(pactl get-default-sink).monitor" -o "$outputPath" & + gpu-screen-recorder -w "$target" -f 60 -a "$(pactl get-default-sink).monitor" -o "$outputPath" $GPU_TYPE & echo "Recording started. Output will be saved to $outputPath" } diff --git a/tsconfig.json b/tsconfig.json index ff8d4be..0b2bd6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,21 @@ { "compilerOptions": { + "noEmit": true, "allowImportingTsExtensions": true, "target": "ES2022", "module": "ES2022", - "lib": [ - "ES2022" - ], + "lib": ["ES2022"], "allowJs": true, "checkJs": true, "strict": true, - "noImplicitAny": false, + "noImplicitAny": true, + "strictNullChecks": true, + "alwaysStrict": true, + "noImplicitThis": true, "baseUrl": ".", - "typeRoots": [ - "types", - "lib/types/globals", - ], - "skipLibCheck": true - } + "typeRoots": ["types", "lib/types/globals"], + "skipLibCheck": true, + "types": [] + }, + "exclude": ["types/@girs/**/*.js"] } diff --git a/widget/RegularWindow.ts b/widget/RegularWindow.ts index 1e4225d..5c85baa 100644 --- a/widget/RegularWindow.ts +++ b/widget/RegularWindow.ts @@ -1,3 +1,3 @@ -import Gtk from "gi://Gtk?version=3.0" +import Gtk from 'gi://Gtk?version=3.0'; -export default Widget.subclass(Gtk.Window) +export default Widget.subclass(Gtk.Window); diff --git a/widget/settings/SettingsDialog.ts b/widget/settings/SettingsDialog.ts index 15b3395..8d8cc23 100644 --- a/widget/settings/SettingsDialog.ts +++ b/widget/settings/SettingsDialog.ts @@ -1,93 +1,89 @@ -import RegularWindow from "widget/RegularWindow" -import icons from "lib/icons" -import options from "options" -import { ThemesMenu } from "./pages/theme/index" -import { SettingsMenu } from "./pages/config/index" -import "./side_effects"; +import RegularWindow from 'widget/RegularWindow'; +import icons from 'lib/icons'; +import options from 'options'; +import { ThemesMenu } from './pages/theme/index'; +import { SettingsMenu } from './pages/config/index'; +import './side_effects'; +import { GBox, GCenterBox } from 'lib/types/widget'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; -type Page = "Configuration" | "Theming" +type Page = 'Configuration' | 'Theming'; -const CurrentPage = Variable("Configuration"); +const CurrentPage = Variable('Configuration'); -const pagerMap: Page[] = [ - "Configuration", - "Theming", -] +const pagerMap: Page[] = ['Configuration', 'Theming']; -const Header = () => Widget.CenterBox({ - class_name: "header", - start_widget: Widget.Button({ - class_name: "reset", - on_clicked: options.reset, - hpack: "start", - vpack: "start", - child: Widget.Icon(icons.ui.refresh), - tooltip_text: "Reset", - }), - center_widget: Widget.Box({ +const Header = (): GCenterBox => + Widget.CenterBox({ + class_name: 'header', + start_widget: Widget.Button({ + class_name: 'reset', + on_clicked: options.reset, + hpack: 'start', + vpack: 'start', + child: Widget.Icon(icons.ui.refresh), + tooltip_text: 'Reset', + }), + center_widget: Widget.Box({}), + end_widget: Widget.Button({ + class_name: 'close', + hpack: 'end', + vpack: 'start', + child: Widget.Icon(icons.ui.close), + on_clicked: () => App.closeWindow('settings-dialog'), + }), + }); - }), - end_widget: Widget.Button({ - class_name: "close", - hpack: "end", - vpack: "start", - child: Widget.Icon(icons.ui.close), - on_clicked: () => App.closeWindow("settings-dialog"), - }), -}) - -const PageContainer = () => { +const PageContainer = (): GBox => { return Widget.Box({ - hpack: "fill", + hpack: 'fill', hexpand: true, vertical: true, - children: CurrentPage.bind("value").as(v => { + children: CurrentPage.bind('value').as((v) => { return [ Widget.Box({ - class_name: "option-pages-container", - hpack: "center", + class_name: 'option-pages-container', + hpack: 'center', hexpand: true, children: pagerMap.map((page) => { return Widget.Button({ xalign: 0, - hpack: "center", + hpack: 'center', class_name: `pager-button ${v === page ? 'active' : ''} category`, label: page, - on_primary_click: () => CurrentPage.value = page - }) - }) + on_primary_click: () => (CurrentPage.value = page), + }); + }), }), Widget.Stack({ vexpand: false, - class_name: "themes-menu-stack", + class_name: 'themes-menu-stack', children: { - "Configuration": SettingsMenu(), - "Theming": ThemesMenu(), + Configuration: SettingsMenu(), + Theming: ThemesMenu(), }, - shown: CurrentPage.bind("value") - }) - ] - }) - }) -} + shown: CurrentPage.bind('value'), + }), + ]; + }), + }); +}; -export default () => RegularWindow({ - name: "settings-dialog", - class_name: "settings-dialog", - title: "Settings", - setup(win) { - win.on("delete-event", () => { - win.hide() - return true - }) - win.set_default_size(200, 300) - }, - child: Widget.Box({ - class_name: "settings-dialog-box", - vertical: true, - children: [ - Header(), - PageContainer() - ], - }), -}) +export default (): Gtk.Window => + RegularWindow({ + name: 'settings-dialog', + class_name: 'settings-dialog', + title: 'Settings', + setup(win) { + win.on('delete-event', () => { + win.hide(); + return true; + }); + win.set_default_size(200, 300); + }, + child: Widget.Box({ + class_name: 'settings-dialog-box', + vertical: true, + children: [Header(), PageContainer()], + }), + }); diff --git a/widget/settings/pages/config/bar/index.ts b/widget/settings/pages/config/bar/index.ts index 8a0cc0c..a213b88 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -1,119 +1,496 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import Gtk from 'types/@girs/gtk-3.0/gtk-3.0'; -export const BarSettings = () => { +export const BarSettings = (): Scrollable => { return Widget.Scrollable({ - vscroll: "always", - hscroll: "automatic", - class_name: "menu-theme-page paged-container", + vscroll: 'always', + hscroll: 'automatic', + class_name: 'menu-theme-page paged-container', child: Widget.Box({ vertical: true, children: [ + /* + ****************************** + * LAYOUTS * + ****************************** + */ Header('Layouts'), + Option( + { + opt: options.bar.layouts, + title: 'Bar Layouts for Monitors', + subtitle: 'Wiki Link: https://hyprpanel.com/configuration/panel.html#layouts', + type: 'object', + subtitleLink: 'https://hyprpanel.com/configuration/panel.html#layouts', + }, + 'bar-layout-input', + ), Option({ - opt: options.bar.layouts, - title: 'Bar Layouts for Monitors', - subtitle: 'Wiki Link: https://hyprpanel.com/configuration/panel.html#layouts', - type: 'object', - subtitleLink: 'https://hyprpanel.com/configuration/panel.html#layouts' - }, - 'bar-layout-input'), + opt: options.theme.bar.floating, + title: 'Floating Bar', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.location, + title: 'Location', + type: 'enum', + enums: ['top', 'bottom'], + }), + /* + ****************************** + * SPACING * + ****************************** + */ Header('Spacing'), - Option({ opt: options.theme.bar.outer_spacing, title: 'Outer Spacing', subtitle: 'Spacing on the outer left and right edges of the bar.', type: 'string' }), - Option({ opt: options.theme.bar.buttons.y_margins, title: 'Vertical Margins', subtitle: 'Spacing above/below the buttons in the bar.', type: 'string' }), - Option({ opt: options.theme.bar.buttons.spacing, title: 'Button Spacing', subtitle: 'Spacing between the buttons in the bar.', type: 'string' }), - Option({ opt: options.theme.bar.buttons.padding_x, title: 'Button Horizontal Padding', type: 'string' }), - Option({ opt: options.theme.bar.buttons.padding_y, title: 'Button Vertical Padding', type: 'string' }), - Option({ opt: options.theme.bar.buttons.radius, title: 'Button Radius', type: 'string' }), - Option({ opt: options.theme.bar.floating, title: 'Floating Bar', type: 'boolean' }), - Option({ opt: options.theme.bar.layer, title: 'Layer', type: 'enum', subtitle: 'Layer determines the Z index of your bar.', enums: ["top", "bottom", "overlay", "background"] }), - Option({ opt: options.theme.bar.margin_top, title: 'Margin Top', subtitle: 'Only applies if floating is enabled', type: 'string' }), - Option({ opt: options.theme.bar.margin_bottom, title: 'Margin Bottom', subtitle: 'Only applies if floating is enabled', type: 'string' }), - Option({ opt: options.theme.bar.margin_sides, title: 'Margin Sides', subtitle: 'Only applies if floating is enabled', type: 'string' }), - Option({ opt: options.theme.bar.border_radius, title: 'Border Radius', subtitle: 'Only applies if floating is enabled', type: 'string' }), + Option({ + opt: options.theme.bar.outer_spacing, + title: 'Outer Spacing', + subtitle: 'Spacing on the outer left and right edges of the bar.', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.y_margins, + title: 'Vertical Margins', + subtitle: 'Spacing above/below the buttons in the bar.', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.spacing, + title: 'Button Spacing', + subtitle: 'Spacing between the buttons in the bar.', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.padding_x, + title: 'Button Horizontal Padding', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.padding_y, + title: 'Button Vertical Padding', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.radius, + title: 'Button Radius', + type: 'string', + }), + Option({ + opt: options.theme.bar.layer, + title: 'Layer', + type: 'enum', + subtitle: 'Layer determines the Z index of your bar.', + enums: ['top', 'bottom', 'overlay', 'background'], + }), + Option({ + opt: options.theme.bar.dropdownGap, + title: 'Dropdown Gap', + subtitle: 'The gap between the dropdown and the bar', + type: 'string', + }), + Option({ + opt: options.theme.bar.margin_top, + title: 'Margin Top', + subtitle: 'Only applies if floating is enabled', + type: 'string', + }), + Option({ + opt: options.theme.bar.margin_bottom, + title: 'Margin Bottom', + subtitle: 'Only applies if floating is enabled', + type: 'string', + }), + Option({ + opt: options.theme.bar.margin_sides, + title: 'Margin Sides', + subtitle: 'Only applies if floating is enabled', + type: 'string', + }), + Option({ + opt: options.theme.bar.border_radius, + title: 'Border Radius', + subtitle: 'Only applies if floating is enabled', + type: 'string', + }), + /* + ****************************** + * DASHBOARD * + ****************************** + */ Header('Dashboard'), - Option({ opt: options.bar.launcher.icon, title: 'Dashboard Menu Icon', type: 'string' }), + Option({ + opt: options.bar.launcher.icon, + title: 'Dashboard Menu Icon', + type: 'string', + }), + /* + ****************************** + * WORKSPACES * + ****************************** + */ Header('Workspaces'), - Option({ opt: options.bar.workspaces.show_icons, title: 'Show Workspace Icons', type: 'boolean' }), - Option({ opt: options.bar.workspaces.icons.available, title: 'Workspace Available', type: 'string' }), - Option({ opt: options.bar.workspaces.icons.active, title: 'Workspace Active', type: 'string' }), - Option({ opt: options.bar.workspaces.icons.occupied, title: 'Workspace Occupied', type: 'string' }), - Option({ opt: options.bar.workspaces.show_numbered, title: 'Show Workspace Numbers', type: 'boolean' }), - Option({ opt: options.bar.workspaces.numbered_active_indicator, title: 'Numbered Workspace Identifier', subtitle: 'Only applicable if Workspace Numbers are enabled', type: 'enum', enums: ["underline", "highlight", "color"] }), - Option({ opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_border, title: 'Highlight Radius', subtitle: 'Only applicable if Workspace Numbers are enabled', type: 'string' }), - Option({ opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_padding, title: 'Highlight Padding', subtitle: 'Only applicable if Workspace Numbers are enabled', type: 'string' }), - Option({ opt: options.bar.workspaces.spacing, title: 'Spacing', subtitle: 'Spacing between workspace icons', type: 'float' }), - Option({ opt: options.bar.workspaces.workspaces, title: 'Total Workspaces', subtitle: 'The least amount of workspaces to always show.', type: 'number' }), - Option({ opt: options.bar.workspaces.monitorSpecific, title: 'Monitor Specific', subtitle: 'Only workspaces applicable to the monitor will be displayed.\nWorks in conjuction with \'Total Workspaces\'.', type: 'boolean' }), - Option({ opt: options.bar.workspaces.hideUnoccupied, title: 'Hide Unoccupied', subtitle: 'Only show workspaces that are occupied or active', type: 'boolean' }), + Option({ + opt: options.theme.bar.buttons.workspaces.fontSize, + title: 'Indicator Size', + subtitle: + 'Only applicable to numbered workspaces and mapped icons\n' + + 'Adjust with caution as it may cause the bar to expand', + type: 'string', + }), + Option({ + opt: options.bar.workspaces.show_icons, + title: 'Show Workspace Icons', + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.icons.available, + title: 'Workspace Available', + type: 'string', + }), + Option({ + opt: options.bar.workspaces.icons.active, + title: 'Workspace Active', + type: 'string', + }), + Option({ + opt: options.bar.workspaces.icons.occupied, + title: 'Workspace Occupied', + type: 'string', + }), + Option({ + opt: options.bar.workspaces.show_numbered, + title: 'Show Workspace Numbers', + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.numbered_active_indicator, + title: 'Numbered Workspace Identifier', + subtitle: 'Only applicable if Workspace Numbers are enabled', + type: 'enum', + enums: ['underline', 'highlight', 'color'], + }), + Option({ + opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_border, + title: 'Highlight Radius', + subtitle: 'Only applicable if Workspace Numbers are enabled', + type: 'string', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_padding, + title: 'Highlight Padding', + subtitle: 'Only applicable if Workspace Numbers are enabled', + type: 'string', + }), + Option({ + opt: options.bar.workspaces.showWsIcons, + title: 'Map Workspaces to Icons', + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.workspaceIconMap, + title: 'Workspace Icon Mappings', + type: 'object', + }), + Option({ + opt: options.bar.workspaces.spacing, + title: 'Spacing', + subtitle: 'Spacing between workspace icons', + type: 'float', + }), + Option({ + opt: options.bar.workspaces.workspaces, + title: 'Total Workspaces', + subtitle: 'The least amount of workspaces to always show.', + type: 'number', + }), + Option({ + opt: options.bar.workspaces.monitorSpecific, + title: 'Monitor Specific', + subtitle: + 'Only workspaces applicable to the monitor will be displayed.\n' + + "Works in conjuction with 'Total Workspaces'.", + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.hideUnoccupied, + title: 'Hide Unoccupied', + subtitle: 'Only show workspaces that are occupied or active', + type: 'boolean', + }), Option({ opt: options.bar.workspaces.workspaceMask, title: 'Mask Workspace Numbers On Monitors', - - subtitle: `Only applicable if Workspace Numbers and Monitor Specific are enabled. -Forces each Monitor's Workspace labels to start from 1.`, - type: 'boolean' + subtitle: + 'Only applicable if Workspace Numbers and Monitor Specific are enabled.\n' + + "Forces each Monitor's Workspace labels to start from 1.", + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.reverse_scroll, + title: 'Invert Scroll', + subtitle: 'Scrolling up will go to the previous workspace rather than the next.', + type: 'boolean', + }), + Option({ + opt: options.bar.workspaces.scroll_speed, + title: 'Scrolling Speed', + type: 'number', }), - Option({ opt: options.bar.workspaces.reverse_scroll, title: 'Invert Scroll', subtitle: 'Scrolling up will go to the previous workspace rather than the next.', type: 'boolean' }), - Option({ opt: options.bar.workspaces.scroll_speed, title: 'Scrolling Speed', type: 'number' }), + /* + ****************************** + * WINDOW TITLES * + ****************************** + */ Header('Window Titles'), - Option({ opt: options.bar.windowtitle.label, title: 'Show Window Title Label', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.windowtitle.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.windowtitle.custom_title, + title: 'Use Custom Title', + type: 'boolean', + }), Option({ opt: options.bar.windowtitle.title_map, title: 'Window Title Mappings', - subtitle: 'Wiki Link: https://hyprpanel.com/configuration/panel.html#window-title-mappings', + subtitle: + 'Only applicable if Show Custom Title is enabled\nWiki Link: https://hyprpanel.com/configuration/panel.html#window-title-mappings', type: 'object', - subtitleLink: 'https://hyprpanel.com/configuration/panel.html#window-title-mappings' + subtitleLink: 'https://hyprpanel.com/configuration/panel.html#window-title-mappings', + }), + Option({ + opt: options.bar.windowtitle.class_name, + title: 'Use Class Name', + subtitle: + "Only applicable if Show Custom Title is disabled\nDisplays the window's class name instead of its title.", + type: 'boolean', + }), + Option({ + opt: options.bar.windowtitle.label, + title: 'Show Window Title Label', + type: 'boolean', + }), + Option({ + opt: options.bar.windowtitle.icon, + title: 'Show Icon', + type: 'boolean', + }), + Option({ + opt: options.bar.windowtitle.truncation, + title: 'Truncate Window Title', + subtitle: 'Will truncate the window title to the specified size below.', + type: 'boolean', + }), + Option({ + opt: options.bar.windowtitle.truncation_size, + title: 'Truncation Size', + type: 'number', + min: 10, + }), + Option({ + opt: options.theme.bar.buttons.windowtitle.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', }), + /* + ****************************** + * VOLUME * + ****************************** + */ Header('Volume'), - Option({ opt: options.bar.volume.label, title: 'Show Volume Percentage', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.volume.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.volume.label, + title: 'Show Volume Percentage', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.buttons.volume.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + /* + ****************************** + * NETWORK * + ****************************** + */ Header('Network'), - Option({ opt: options.bar.network.label, title: 'Show Network Name', type: 'boolean' }), - Option({ opt: options.bar.network.truncation, title: 'Truncate Network Name', subtitle: 'Will truncate the network name to the specified size below.', type: 'boolean' }), - Option({ opt: options.bar.network.truncation_size, title: 'Truncation Size', type: 'number' }), - Option({ opt: options.theme.bar.buttons.network.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.network.label, + title: 'Show Network Name', + type: 'boolean', + }), + Option({ + opt: options.bar.network.truncation, + title: 'Truncate Network Name', + subtitle: 'Will truncate the network name to the specified size below.', + type: 'boolean', + }), + Option({ + opt: options.bar.network.truncation_size, + title: 'Truncation Size', + type: 'number', + }), + Option({ + opt: options.theme.bar.buttons.network.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + /* + ****************************** + * BLUETOOTH * + ****************************** + */ Header('Bluetooth'), - Option({ opt: options.bar.bluetooth.label, title: 'Show Bluetooth Label', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.bluetooth.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.bluetooth.label, + title: 'Show Bluetooth Label', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.buttons.bluetooth.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + /* + ****************************** + * BATTERY * + ****************************** + */ Header('Battery'), - Option({ opt: options.bar.battery.label, title: 'Show Battery Percentage', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.battery.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.battery.label, + title: 'Show Battery Percentage', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.buttons.battery.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), - // Header('System Tray'), - // TODO: Figure out how to handle arrays - // Option({ opt: options.bar.systray.ignore, title: 'Ignore', subtitle: 'Comma separated string of apps to ignore in the tray', type: 'string' }), + /* + ****************************** + * SYSTEM TRAY * + ****************************** + */ + Header('System Tray'), + Option({ + opt: options.bar.systray.ignore, + title: 'Ignore List', + subtitle: + 'An array of applications to prevent from showing in the system tray.\n' + + 'Wiki: https://hyprpanel.com/configuration/panel.html#system-tray', + subtitleLink: 'https://hyprpanel.com/configuration/panel.html#system-tray', + type: 'object', + }), + /* + ****************************** + * CLOCK * + ****************************** + */ Header('Clock'), - Option({ opt: options.bar.clock.format, title: 'Clock Format', type: 'string' }), - Option({ opt: options.bar.clock.icon, title: 'Icon', type: 'string' }), - Option({ opt: options.bar.clock.showIcon, title: 'Show Icon', type: 'boolean' }), - Option({ opt: options.bar.clock.showTime, title: 'Show Time', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.clock.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), + Option({ + opt: options.bar.clock.format, + title: 'Clock Format', + type: 'string', + }), + Option({ + opt: options.bar.clock.icon, + title: 'Icon', + type: 'string', + }), + Option({ + opt: options.bar.clock.showIcon, + title: 'Show Icon', + type: 'boolean', + }), + Option({ + opt: options.bar.clock.showTime, + title: 'Show Time', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.buttons.clock.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + /* + ****************************** + * MEDIA * + ****************************** + */ Header('Media'), - Option({ opt: options.theme.bar.buttons.media.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), - Option({ opt: options.bar.media.show_artist, title: 'Show Track Artist', type: 'boolean' }), - Option({ opt: options.bar.media.show_label, title: 'Toggle Media Label', type: 'boolean' }), - Option({ opt: options.bar.media.truncation, title: 'Truncate Media Label', subtitle: 'Only applicable if Toggle Media Label is enabled', type: 'boolean' }), - Option({ opt: options.bar.media.truncation_size, title: 'Truncation Size', subtitle: 'Only applicable if Toggle Media Label is enabled', type: 'number', min: 10 }), - Option({ opt: options.bar.media.show_active_only, title: 'Auto Hide', subtitle: 'Button will automatically hide if no media is detected.', type: 'boolean' }), + Option({ + opt: options.theme.bar.buttons.media.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + Option({ + opt: options.bar.media.show_artist, + title: 'Show Track Artist', + type: 'boolean', + }), + Option({ + opt: options.bar.media.show_label, + title: 'Toggle Media Label', + type: 'boolean', + }), + Option({ + opt: options.bar.media.truncation, + title: 'Truncate Media Label', + subtitle: 'Only applicable if Toggle Media Label is enabled', + type: 'boolean', + }), + Option({ + opt: options.bar.media.truncation_size, + title: 'Truncation Size', + subtitle: 'Only applicable if Toggle Media Label is enabled', + type: 'number', + min: 10, + }), + Option({ + opt: options.bar.media.show_active_only, + title: 'Auto Hide', + subtitle: 'Button will automatically hide if no media is detected.', + type: 'boolean', + }), + /* + ****************************** + * NOTIFICATIONS * + ****************************** + */ Header('Notifications'), - Option({ opt: options.bar.notifications.show_total, title: 'Show Total # of notifications', type: 'boolean' }), - Option({ opt: options.theme.bar.buttons.notifications.spacing, title: 'Inner Spacing', subtitle: 'Spacing between the icon and the label inside the buttons.', type: 'string' }), - ] - }) - }) -} + Option({ + opt: options.bar.notifications.show_total, + title: 'Show Total # of notifications', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.buttons.notifications.spacing, + title: 'Inner Spacing', + subtitle: 'Spacing between the icon and the label inside the buttons.', + type: 'string', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/config/general/index.ts b/widget/settings/pages/config/general/index.ts index a58a94c..4bf629b 100644 --- a/widget/settings/pages/config/general/index.ts +++ b/widget/settings/pages/config/general/index.ts @@ -1,19 +1,29 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const BarGeneral = () => { +export const BarGeneral = (): Scrollable => { return Widget.Scrollable({ - class_name: "bar-theme-page paged-container", - vscroll: "automatic", + class_name: 'bar-theme-page paged-container', + vscroll: 'automatic', child: Widget.Box({ vertical: true, children: [ Header('General Settings'), Option({ opt: options.theme.font.name, title: 'Font', type: 'font' }), Option({ opt: options.theme.font.size, title: 'Font Size', type: 'string' }), - Option({ opt: options.theme.font.weight, title: 'Font Weight', subtitle: "100, 200, 300, etc.", type: 'number', increment: 100, min: 100, max: 900 }), + Option({ + opt: options.theme.font.weight, + title: 'Font Weight', + subtitle: '100, 200, 300, etc.', + type: 'number', + increment: 100, + min: 100, + max: 900, + }), Option({ opt: options.dummy, title: 'Config', @@ -21,26 +31,130 @@ export const BarGeneral = () => { type: 'config_import', exportData: { filePath: OPTIONS, - themeOnly: false - } + themeOnly: false, + }, + }), + Option({ + opt: options.terminal, + title: 'Terminal', + subtitle: "Tools such as 'btop' will open in this terminal", + type: 'string', + }), + Option({ + opt: options.tear, + title: 'Tearing Compatible', + subtitle: + 'Makes HyprPanel compatible with Hyprland tearing.\n' + + "Enabling this will change all overlays (Notifications, OSDs, Bar) to the 'top' layer instead the 'overlay' layer.", + type: 'boolean', }), - Option({ opt: options.terminal, title: 'Terminal', subtitle: "Tools such as 'btop' will open in this terminal", type: 'string' }), Header('Scaling'), - Option({ opt: options.theme.bar.scaling, title: 'Bar', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.notification.scaling, title: 'Notifications', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.osd.scaling, title: 'OSD', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.dashboard.scaling, title: 'Dashboard Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.dashboard.confirmation_scaling, title: 'Confirmation Dialog', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.media.scaling, title: 'Media Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.volume.scaling, title: 'Volume Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.network.scaling, title: 'Network Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.bluetooth.scaling, title: 'Bluetooth Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.battery.scaling, title: 'Battery Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.clock.scaling, title: 'Clock Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.notifications.scaling, title: 'Notifications Menu', type: 'number', min: 1, max: 100, increment: 5 }), - Option({ opt: options.theme.bar.menus.menu.power.scaling, title: 'Power Menu', type: 'number', min: 1, max: 100, increment: 5 }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.scaling, + title: 'Bar', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.notification.scaling, + title: 'Notifications', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.osd.scaling, + title: 'OSD', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.scaling, + title: 'Dashboard Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.confirmation_scaling, + title: 'Confirmation Dialog', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.media.scaling, + title: 'Media Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.volume.scaling, + title: 'Volume Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.network.scaling, + title: 'Network Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.scaling, + title: 'Bluetooth Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.battery.scaling, + title: 'Battery Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.clock.scaling, + title: 'Clock Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.scaling, + title: 'Notifications Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + Option({ + opt: options.theme.bar.menus.menu.power.scaling, + title: 'Power Menu', + type: 'number', + min: 1, + max: 100, + increment: 5, + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/config/index.ts b/widget/settings/pages/config/index.ts index 44bf23b..31f4fa3 100644 --- a/widget/settings/pages/config/index.ts +++ b/widget/settings/pages/config/index.ts @@ -1,68 +1,70 @@ -import { BarGeneral } from "./general/index"; -import { BarSettings } from "./bar/index"; -import { ClockMenuSettings } from "./menus/clock"; -import { DashboardMenuSettings } from "./menus/dashboard"; -import { NotificationSettings } from "./notifications/index"; -import { OSDSettings } from "./osd/index"; -import { CustomModuleSettings } from "customModules/config"; -import { PowerMenuSettings } from "./menus/power"; +import { BarGeneral } from './general/index'; +import { BarSettings } from './bar/index'; +import { ClockMenuSettings } from './menus/clock'; +import { DashboardMenuSettings } from './menus/dashboard'; +import { NotificationSettings } from './notifications/index'; +import { OSDSettings } from './osd/index'; +import { CustomModuleSettings } from 'customModules/config'; +import { PowerMenuSettings } from './menus/power'; +import { GBox } from 'lib/types/widget'; -type Page = "General" - | "Bar" - | "Clock Menu" - | "Dashboard Menu" - | "Power Menu" - | "Notifications" - | "OSD" - | "Custom Modules"; +type Page = + | 'General' + | 'Bar' + | 'Clock Menu' + | 'Dashboard Menu' + | 'Power Menu' + | 'Notifications' + | 'OSD' + | 'Custom Modules'; -const CurrentPage = Variable("General"); +const CurrentPage = Variable('General'); const pagerMap: Page[] = [ - "General", - "Bar", - "Notifications", - "OSD", - "Power Menu", - "Clock Menu", - "Dashboard Menu", - "Custom Modules", -] + 'General', + 'Bar', + 'Notifications', + 'OSD', + 'Power Menu', + 'Clock Menu', + 'Dashboard Menu', + 'Custom Modules', +]; -export const SettingsMenu = () => { +export const SettingsMenu = (): GBox => { return Widget.Box({ vertical: true, - children: CurrentPage.bind("value").as(v => { + children: CurrentPage.bind('value').as((v) => { return [ Widget.Box({ - class_name: "option-pages-container", - hpack: "center", + class_name: 'option-pages-container', + hpack: 'center', hexpand: true, children: pagerMap.map((page) => { return Widget.Button({ - hpack: "center", + hpack: 'center', class_name: `pager-button ${v === page ? 'active' : ''}`, label: page, - on_primary_click: () => CurrentPage.value = page - }) - }) + on_primary_click: () => (CurrentPage.value = page), + }); + }), }), Widget.Stack({ vexpand: true, - class_name: "themes-menu-stack", + class_name: 'themes-menu-stack', children: { - "General": BarGeneral(), - "Bar": BarSettings(), - "Notifications": NotificationSettings(), - "OSD": OSDSettings(), - "Clock Menu": ClockMenuSettings(), - "Dashboard Menu": DashboardMenuSettings(), - "Custom Modules": CustomModuleSettings(), - "Power Menu": PowerMenuSettings(), + General: BarGeneral(), + Bar: BarSettings(), + Notifications: NotificationSettings(), + OSD: OSDSettings(), + 'Clock Menu': ClockMenuSettings(), + 'Dashboard Menu': DashboardMenuSettings(), + 'Custom Modules': CustomModuleSettings(), + 'Power Menu': PowerMenuSettings(), }, - shown: CurrentPage.bind("value") - }) - ] - }) - }) -} + shown: CurrentPage.bind('value'), + }), + ]; + }), + }); +}; diff --git a/widget/settings/pages/config/menus/clock.ts b/widget/settings/pages/config/menus/clock.ts index 8ef2436..972cb78 100644 --- a/widget/settings/pages/config/menus/clock.ts +++ b/widget/settings/pages/config/menus/clock.ts @@ -1,24 +1,46 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const ClockMenuSettings = () => { +export const ClockMenuSettings = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", + vscroll: 'automatic', child: Widget.Box({ - class_name: "bar-theme-page paged-container", + class_name: 'bar-theme-page paged-container', vertical: true, children: [ Header('Time'), Option({ opt: options.menus.clock.time.military, title: 'Use 24hr time', type: 'boolean' }), Header('Weather'), - Option({ opt: options.menus.clock.weather.location, title: 'Location', subtitle: 'Zip Code, Postal Code, City, etc.', type: 'string' }), - Option({ opt: options.menus.clock.weather.key, title: 'Weather API Key', subtitle: 'May require AGS restart. https://weatherapi.com/', type: 'string' }), - Option({ opt: options.menus.clock.weather.unit, title: 'Units', type: 'enum', enums: ['imperial', 'metric'] }), - Option({ opt: options.menus.clock.weather.interval, title: 'Weather Fetching Interval (ms)', subtitle: 'May require AGS restart.', type: 'number' }), - ] - }) - }) -} + Option({ + opt: options.menus.clock.weather.location, + title: 'Location', + subtitle: 'Zip Code, Postal Code, City, etc.', + type: 'string', + }), + Option({ + opt: options.menus.clock.weather.key, + title: 'Weather API Key', + subtitle: 'May require AGS restart. https://weatherapi.com/', + type: 'string', + }), + Option({ + opt: options.menus.clock.weather.unit, + title: 'Units', + type: 'enum', + enums: ['imperial', 'metric'], + }), + Option({ + opt: options.menus.clock.weather.interval, + title: 'Weather Fetching Interval (ms)', + subtitle: 'May require AGS restart.', + type: 'number', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/config/menus/dashboard.ts b/widget/settings/pages/config/menus/dashboard.ts index ed40e62..8a56e22 100644 --- a/widget/settings/pages/config/menus/dashboard.ts +++ b/widget/settings/pages/config/menus/dashboard.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const DashboardMenuSettings = () => { +export const DashboardMenuSettings = (): Scrollable => { return Widget.Scrollable({ - class_name: "bar-theme-page paged-container", - vscroll: "always", - hscroll: "automatic", + class_name: 'bar-theme-page paged-container', + vscroll: 'always', + hscroll: 'automatic', vexpand: true, overlayScrolling: true, child: Widget.Box({ @@ -15,53 +17,195 @@ export const DashboardMenuSettings = () => { children: [ Header('Power Menu'), Option({ opt: options.menus.dashboard.powermenu.avatar.image, title: 'Profile Image', type: 'img' }), - Option({ opt: options.menus.dashboard.powermenu.avatar.name, title: 'Profile Name', subtitle: 'Use \'system\' to automatically set system name', type: 'string' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.profile.size, title: 'Profile Image Size', type: 'string' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.profile.radius, title: 'Profile Image Radius', type: 'string' }), + Option({ + opt: options.menus.dashboard.powermenu.avatar.name, + title: 'Profile Name', + subtitle: "Use 'system' to automatically set system name", + type: 'string', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.profile.size, + title: 'Profile Image Size', + type: 'string', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.profile.radius, + title: 'Profile Image Radius', + type: 'string', + }), - Option({ opt: options.menus.dashboard.powermenu.confirmation, title: 'Show Confirmation Dialogue', type: 'boolean' }), + Option({ + opt: options.menus.dashboard.powermenu.confirmation, + title: 'Show Confirmation Dialogue', + type: 'boolean', + }), Option({ opt: options.menus.dashboard.powermenu.shutdown, title: 'Shutdown Command', type: 'string' }), Option({ opt: options.menus.dashboard.powermenu.reboot, title: 'Reboot Command', type: 'string' }), Option({ opt: options.menus.dashboard.powermenu.logout, title: 'Logout Command', type: 'string' }), Option({ opt: options.menus.dashboard.powermenu.sleep, title: 'Sleep Command', type: 'string' }), Header('Resource Usage Metrics'), - Option({ opt: options.menus.dashboard.stats.enable_gpu, title: 'Track GPU', subtitle: 'NOTE: This is currently only available for NVidia GPUs and requires \'python-gpustat\'.', type: 'boolean' }), + Option({ + opt: options.menus.dashboard.stats.enable_gpu, + title: 'Track GPU', + subtitle: "NOTE: This is currently only available for NVidia GPUs and requires 'python-gpustat'.", + type: 'boolean', + }), Header('Shortcuts'), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut1.icon, title: 'Left - Shortcut 1 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut1.command, title: 'Left - Shortcut 1 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut1.tooltip, title: 'Left - Shortcut 1 (Tooltip)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut2.icon, title: 'Left - Shortcut 2 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut2.command, title: 'Left - Shortcut 2 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut2.tooltip, title: 'Left - Shortcut 2 (Tooltip)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut3.icon, title: 'Left - Shortcut 3 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut3.command, title: 'Left - Shortcut 3 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut3.tooltip, title: 'Left - Shortcut 3 (Tooltip)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut4.icon, title: 'Left - Shortcut 4 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut4.command, title: 'Left - Shortcut 4 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.left.shortcut4.tooltip, title: 'Left - Shortcut 4 (Tooltip)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut1.icon, title: 'Right - Shortcut 1 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut1.command, title: 'Right - Shortcut 1 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut1.tooltip, title: 'Right - Shortcut 1 (Tooltip)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut3.icon, title: 'Right - Shortcut 3 (Icon)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut3.command, title: 'Right - Shortcut 3 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.shortcuts.right.shortcut3.tooltip, title: 'Right - Shortcut 3 (Tooltip)', type: 'string' }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut1.icon, + title: 'Left - Shortcut 1 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut1.command, + title: 'Left - Shortcut 1 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut1.tooltip, + title: 'Left - Shortcut 1 (Tooltip)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut2.icon, + title: 'Left - Shortcut 2 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut2.command, + title: 'Left - Shortcut 2 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut2.tooltip, + title: 'Left - Shortcut 2 (Tooltip)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut3.icon, + title: 'Left - Shortcut 3 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut3.command, + title: 'Left - Shortcut 3 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut3.tooltip, + title: 'Left - Shortcut 3 (Tooltip)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut4.icon, + title: 'Left - Shortcut 4 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut4.command, + title: 'Left - Shortcut 4 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.left.shortcut4.tooltip, + title: 'Left - Shortcut 4 (Tooltip)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut1.icon, + title: 'Right - Shortcut 1 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut1.command, + title: 'Right - Shortcut 1 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut1.tooltip, + title: 'Right - Shortcut 1 (Tooltip)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut3.icon, + title: 'Right - Shortcut 3 (Icon)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut3.command, + title: 'Right - Shortcut 3 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.shortcuts.right.shortcut3.tooltip, + title: 'Right - Shortcut 3 (Tooltip)', + type: 'string', + }), Header('Directories'), - Option({ opt: options.menus.dashboard.directories.left.directory1.label, title: 'Left - Directory 1 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.left.directory1.command, title: 'Left - Directory 1 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.left.directory2.label, title: 'Left - Directory 2 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.left.directory2.command, title: 'Left - Directory 2 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.left.directory3.label, title: 'Left - Directory 3 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.left.directory3.command, title: 'Left - Directory 3 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory1.label, title: 'Right - Directory 1 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory1.command, title: 'Right - Directory 1 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory2.label, title: 'Right - Directory 2 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory2.command, title: 'Right - Directory 2 (Command)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory3.label, title: 'Right - Directory 3 (Label)', type: 'string' }), - Option({ opt: options.menus.dashboard.directories.right.directory3.command, title: 'Right - Directory 3 (Command)', type: 'string' }), - ] - }) - }) -} + Option({ + opt: options.menus.dashboard.directories.left.directory1.label, + title: 'Left - Directory 1 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.left.directory1.command, + title: 'Left - Directory 1 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.left.directory2.label, + title: 'Left - Directory 2 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.left.directory2.command, + title: 'Left - Directory 2 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.left.directory3.label, + title: 'Left - Directory 3 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.left.directory3.command, + title: 'Left - Directory 3 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory1.label, + title: 'Right - Directory 1 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory1.command, + title: 'Right - Directory 1 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory2.label, + title: 'Right - Directory 2 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory2.command, + title: 'Right - Directory 2 (Command)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory3.label, + title: 'Right - Directory 3 (Label)', + type: 'string', + }), + Option({ + opt: options.menus.dashboard.directories.right.directory3.command, + title: 'Right - Directory 3 (Command)', + type: 'string', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/config/menus/power.ts b/widget/settings/pages/config/menus/power.ts index 988a5d1..e1ce949 100644 --- a/widget/settings/pages/config/menus/power.ts +++ b/widget/settings/pages/config/menus/power.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import { Attribute, Child } from 'lib/types/widget'; +import Scrollable from 'types/widgets/scrollable'; -export const PowerMenuSettings = () => { +export const PowerMenuSettings = (): Scrollable => { return Widget.Scrollable({ - class_name: "bar-theme-page paged-container", - vscroll: "always", - hscroll: "automatic", + class_name: 'bar-theme-page paged-container', + vscroll: 'always', + hscroll: 'automatic', vexpand: true, overlayScrolling: true, child: Widget.Box({ @@ -20,7 +22,7 @@ export const PowerMenuSettings = () => { Option({ opt: options.menus.power.reboot, title: 'Reboot Command', type: 'string' }), Option({ opt: options.menus.power.logout, title: 'Logout Command', type: 'string' }), Option({ opt: options.menus.power.sleep, title: 'Sleep Command', type: 'string' }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/config/notifications/index.ts b/widget/settings/pages/config/notifications/index.ts index 7660e72..3d7925e 100644 --- a/widget/settings/pages/config/notifications/index.ts +++ b/widget/settings/pages/config/notifications/index.ts @@ -1,30 +1,95 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const NotificationSettings = () => { +export const NotificationSettings = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", + vscroll: 'automatic', child: Widget.Box({ - class_name: "bar-theme-page paged-container", + class_name: 'bar-theme-page paged-container', vertical: true, children: [ Header('Notification Settings'), - Option({ opt: options.notifications.position, title: 'Notification Location', type: 'enum', enums: ['top left', 'top', 'top right', 'right', 'bottom right', 'bottom', 'bottom left', 'left'] }), - Option({ opt: options.theme.notification.border_radius, title: 'Border Radius', type: 'string' }), - Option({ opt: options.notifications.monitor, title: 'Monitor', subtitle: 'The ID of the monitor on which to display the notification', type: 'number' }), - Option({ opt: options.notifications.active_monitor, title: 'Follow Cursor', subtitle: 'The notification will follow the monitor of your cursor', type: 'boolean' }), - Option({ opt: options.notifications.timeout, title: 'Notification Timeout', subtitle: 'How long notification popups will last (in milliseconds).', type: 'number' }), - Option({ opt: options.notifications.cache_actions, title: 'Preserve Actions', subtitle: 'This will persist the action buttons of a notification after rebooting.', type: 'boolean' }), + Option({ + opt: options.notifications.ignore, + title: 'Ignored Applications', + subtitle: + 'Applications to ignore.\n' + + 'Wiki: https://hyprpanel.com/configuration/notifications.html#ignored-applications', + subtitleLink: 'https://hyprpanel.com/configuration/notifications.html#ignored-applications', + type: 'object', + }), + Option({ + opt: options.notifications.position, + title: 'Notification Location', + type: 'enum', + enums: ['top left', 'top', 'top right', 'right', 'bottom right', 'bottom', 'bottom left', 'left'], + }), + Option({ + opt: options.theme.notification.border_radius, + title: 'Border Radius', + type: 'string', + }), + Option({ + opt: options.notifications.monitor, + title: 'Monitor', + subtitle: 'The ID of the monitor on which to display the notification', + type: 'number', + }), + Option({ + opt: options.notifications.active_monitor, + title: 'Follow Cursor', + subtitle: 'The notification will follow the monitor of your cursor', + type: 'boolean', + }), + Option({ + opt: options.notifications.timeout, + title: 'Notification Timeout', + subtitle: 'How long notification popups will last (in milliseconds).', + type: 'number', + }), + Option({ + opt: options.notifications.cache_actions, + title: 'Preserve Actions', + subtitle: 'This will persist the action buttons of a notification after rebooting.', + type: 'boolean', + }), Header('Notification Menu Settings'), - Option({ opt: options.theme.bar.menus.menu.notifications.height, title: 'Notification Menu Height', type: 'string' }), - Option({ opt: options.notifications.displayedTotal, title: 'Displayed Total', subtitle: 'How many notifications to show in the menu at once.\nNewer notifications will display towards the top.', type: 'number', min: 1 }), - Option({ opt: options.theme.bar.menus.menu.notifications.pager.show, title: 'Show Pager', subtitle: "Shows the pagination footer at the bottom of the menu.", type: 'boolean' }), - Option({ opt: options.theme.bar.menus.menu.notifications.scrollbar.width, title: 'Scrollbar Width', type: 'string' }), - Option({ opt: options.theme.bar.menus.menu.notifications.scrollbar.radius, title: 'Scrollbar Radius', type: 'string' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.notifications.height, + title: 'Notification Menu Height', + type: 'string', + }), + Option({ + opt: options.notifications.displayedTotal, + title: 'Displayed Total', + subtitle: + 'How many notifications to show in the menu at once.\n' + + 'Newer notifications will display towards the top.', + type: 'number', + min: 1, + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.pager.show, + title: 'Show Pager', + subtitle: 'Shows the pagination footer at the bottom of the menu.', + type: 'boolean', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.scrollbar.width, + title: 'Scrollbar Width', + type: 'string', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.scrollbar.radius, + title: 'Scrollbar Radius', + type: 'string', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/config/osd/index.ts b/widget/settings/pages/config/osd/index.ts index e9a7f9e..f9fca0f 100644 --- a/widget/settings/pages/config/osd/index.ts +++ b/widget/settings/pages/config/osd/index.ts @@ -1,25 +1,71 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const OSDSettings = () => { +export const OSDSettings = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", + vscroll: 'automatic', child: Widget.Box({ - class_name: "bar-theme-page paged-container", + class_name: 'bar-theme-page paged-container', vertical: true, children: [ Header('On Screen Display'), Option({ opt: options.theme.osd.enable, title: 'Enabled', type: 'boolean' }), - Option({ opt: options.theme.osd.orientation, title: 'Orientation', type: 'enum', enums: ["horizontal", "vertical"] }), - Option({ opt: options.theme.osd.location, title: 'Position', subtitle: 'Position of the OSD on the screen', type: 'enum', enums: ["top left", "top", "top right", "right", "bottom right", "bottom", "bottom left", "left"] }), - Option({ opt: options.theme.osd.monitor, title: 'Monitor', subtitle: 'The ID of the monitor on which to display the OSD', type: 'number' }), - Option({ opt: options.theme.osd.active_monitor, title: 'Follow Cursor', subtitle: 'The OSD will follow the monitor of your cursor', type: 'boolean' }), - Option({ opt: options.theme.osd.radius, title: 'Radius', subtitle: 'Radius of the on-screen-display that indicates volume/brightness change', type: 'string' }), - Option({ opt: options.theme.osd.margins, title: 'Margins', subtitle: 'Margins in the following format: top right bottom left', type: 'string' }), - Option({ opt: options.theme.osd.muted_zero, title: 'Mute Volume as Zero', subtitle: 'Display volume as 0 when muting, instead of previous device volume', type: 'boolean' }), - ] - }) - }) -} + Option({ + opt: options.theme.osd.duration, + title: 'Duration', + type: 'number', + min: 100, + max: 10000, + increment: 500, + }), + Option({ + opt: options.theme.osd.orientation, + title: 'Orientation', + type: 'enum', + enums: ['horizontal', 'vertical'], + }), + Option({ + opt: options.theme.osd.location, + title: 'Position', + subtitle: 'Position of the OSD on the screen', + type: 'enum', + enums: ['top left', 'top', 'top right', 'right', 'bottom right', 'bottom', 'bottom left', 'left'], + }), + Option({ + opt: options.theme.osd.monitor, + title: 'Monitor', + subtitle: 'The ID of the monitor on which to display the OSD', + type: 'number', + }), + Option({ + opt: options.theme.osd.active_monitor, + title: 'Follow Cursor', + subtitle: 'The OSD will follow the monitor of your cursor', + type: 'boolean', + }), + Option({ + opt: options.theme.osd.radius, + title: 'Radius', + subtitle: 'Radius of the on-screen-display that indicates volume/brightness change', + type: 'string', + }), + Option({ + opt: options.theme.osd.margins, + title: 'Margins', + subtitle: 'Margins in the following format: top right bottom left', + type: 'string', + }), + Option({ + opt: options.theme.osd.muted_zero, + title: 'Mute Volume as Zero', + subtitle: 'Display volume as 0 when muting, instead of previous device volume', + type: 'boolean', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/bar/index.ts b/widget/settings/pages/theme/bar/index.ts index 59acac3..b67836b 100644 --- a/widget/settings/pages/theme/bar/index.ts +++ b/widget/settings/pages/theme/bar/index.ts @@ -1,25 +1,65 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const BarTheme = () => { +export const BarTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "always", - hscroll: "automatic", - class_name: "bar-theme-page paged-container", + vscroll: 'always', + hscroll: 'automatic', + class_name: 'bar-theme-page paged-container', child: Widget.Box({ vertical: true, children: [ Header('General'), Option({ opt: options.theme.bar.transparent, title: 'Transparent', type: 'boolean' }), Option({ opt: options.theme.bar.background, title: 'Background Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.style, title: 'Button Style', type: 'enum', enums: ['default', 'split', 'wave', 'wave2'] }), - Option({ opt: options.theme.bar.opacity, title: 'Background Opacity', type: 'number', increment: 5, min: 0, max: 100 }), - Option({ opt: options.theme.bar.buttons.opacity, title: 'Button Opacity', type: 'number', increment: 5, min: 0, max: 100 }), - Option({ opt: options.theme.bar.buttons.background_opacity, title: 'Button Background Opacity', type: 'number', increment: 5, min: 0, max: 100 }), - Option({ opt: options.theme.bar.buttons.background_hover_opacity, title: 'Button Background Hover Opacity', type: 'number', increment: 5, min: 0, max: 100 }), - Option({ opt: options.theme.bar.buttons.monochrome, title: 'Use Global Colors', type: 'boolean', disabledBinding: options.theme.matugen }), + Option({ + opt: options.theme.bar.buttons.style, + title: 'Button Style', + type: 'enum', + enums: ['default', 'split', 'wave', 'wave2'], + }), + Option({ + opt: options.theme.bar.opacity, + title: 'Background Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), + Option({ + opt: options.theme.bar.buttons.opacity, + title: 'Button Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), + Option({ + opt: options.theme.bar.buttons.background_opacity, + title: 'Button Background Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), + Option({ + opt: options.theme.bar.buttons.background_hover_opacity, + title: 'Button Background Hover Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), + Option({ + opt: options.theme.bar.buttons.monochrome, + title: 'Use Global Colors', + type: 'boolean', + disabledBinding: options.theme.matugen, + }), Option({ opt: options.theme.bar.buttons.background, title: 'Button Background', type: 'color' }), Option({ opt: options.theme.bar.buttons.hover, title: 'Button Hover', type: 'color' }), Option({ opt: options.theme.bar.buttons.text, title: 'Button Text', type: 'color' }), @@ -27,8 +67,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Dashboard Button'), @@ -38,12 +79,36 @@ export const BarTheme = () => { Header('Workspaces'), Option({ opt: options.theme.bar.buttons.workspaces.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.hover, title: 'Workspace Hover Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.available, title: 'Workspace Available Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.occupied, title: 'Workspace Occupied Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.active, title: 'Workspace Active Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.numbered_active_highlighted_text_color, title: 'Highlighted Workspace Text Color', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.numbered_active_underline_color, title: 'Workspace Underline Color', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.workspaces.hover, + title: 'Workspace Hover Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.available, + title: 'Workspace Available Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.occupied, + title: 'Workspace Occupied Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.active, + title: 'Workspace Active Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.numbered_active_highlighted_text_color, + title: 'Highlighted Workspace Text Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.buttons.workspaces.numbered_active_underline_color, + title: 'Workspace Underline Color', + type: 'color', + }), Header('Window Title'), Option({ opt: options.theme.bar.buttons.windowtitle.background, title: 'Background', type: 'color' }), @@ -53,8 +118,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.windowtitle.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Media'), @@ -65,8 +131,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.media.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Volume'), @@ -77,8 +144,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.volume.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Network'), @@ -89,8 +157,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.network.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Bluetooth'), @@ -101,8 +170,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.bluetooth.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('System Tray'), @@ -117,8 +187,9 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.battery.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Clock'), @@ -129,22 +200,28 @@ export const BarTheme = () => { Option({ opt: options.theme.bar.buttons.clock.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), Header('Notifications'), Option({ opt: options.theme.bar.buttons.notifications.background, title: 'Background', type: 'color' }), Option({ opt: options.theme.bar.buttons.notifications.hover, title: 'Hover', type: 'color' }), - Option({ opt: options.theme.bar.buttons.notifications.total, title: 'Notification Count', type: 'color' }), + Option({ + opt: options.theme.bar.buttons.notifications.total, + title: 'Notification Count', + type: 'color', + }), Option({ opt: options.theme.bar.buttons.notifications.icon, title: 'Icon', type: 'color' }), Option({ opt: options.theme.bar.buttons.notifications.icon_background, title: 'Button Icon Background', - subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.', - type: 'color' + subtitle: + "Applies a background color to the icon section of the button.\nRequires 'split' button styling.", + type: 'color', }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/index.ts b/widget/settings/pages/theme/index.ts index 20cceda..0f09cc5 100644 --- a/widget/settings/pages/theme/index.ts +++ b/widget/settings/pages/theme/index.ts @@ -1,108 +1,111 @@ -import { BarTheme } from "./bar/index"; -import { NotificationsTheme } from "./notifications/index"; -import { BatteryMenuTheme } from "./menus/battery"; -import { BluetoothMenuTheme } from "./menus/bluetooth"; -import { ClockMenuTheme } from "./menus/clock"; -import { DashboardMenuTheme } from "./menus/dashboard"; -import { MenuTheme } from "./menus/index"; -import { MediaMenuTheme } from "./menus/media"; -import { NetworkMenuTheme } from "./menus/network"; -import { NotificationsMenuTheme } from "./menus/notifications"; -import { SystrayMenuTheme } from "./menus/systray"; -import { VolumeMenuTheme } from "./menus/volume"; -import { OsdTheme } from "./osd/index"; -import { Matugen } from "./menus/matugen"; -import { CustomModuleTheme } from "customModules/theme"; -import { PowerMenuTheme } from "./menus/power"; +import { BarTheme } from './bar/index'; +import { NotificationsTheme } from './notifications/index'; +import { BatteryMenuTheme } from './menus/battery'; +import { BluetoothMenuTheme } from './menus/bluetooth'; +import { ClockMenuTheme } from './menus/clock'; +import { DashboardMenuTheme } from './menus/dashboard'; +import { MenuTheme } from './menus/index'; +import { MediaMenuTheme } from './menus/media'; +import { NetworkMenuTheme } from './menus/network'; +import { NotificationsMenuTheme } from './menus/notifications'; +import { SystrayMenuTheme } from './menus/systray'; +import { VolumeMenuTheme } from './menus/volume'; +import { OsdTheme } from './osd/index'; +import { Matugen } from './menus/matugen'; +import { CustomModuleTheme } from 'customModules/theme'; +import { PowerMenuTheme } from './menus/power'; +import { GBox } from 'lib/types/widget'; -type Page = "General Settings" - | "Matugen Settings" - | "Bar" - | "Notifications" - | "OSD" - | "Battery Menu" - | "Bluetooth Menu" - | "Clock Menu" - | "Dashboard Menu" - | "Media Menu" - | "Network Menu" - | "Notifications Menu" - | "System Tray" - | "Volume Menu" - | "Power Menu" - | "Custom Modules"; +type Page = + | 'General Settings' + | 'Matugen Settings' + | 'Bar' + | 'Notifications' + | 'OSD' + | 'Battery Menu' + | 'Bluetooth Menu' + | 'Clock Menu' + | 'Dashboard Menu' + | 'Media Menu' + | 'Network Menu' + | 'Notifications Menu' + | 'System Tray' + | 'Volume Menu' + | 'Power Menu' + | 'Custom Modules'; -const CurrentPage = Variable("General Settings"); +const CurrentPage = Variable('General Settings'); const pagerMap: Page[] = [ - "General Settings", - "Matugen Settings", - "Bar", - "Notifications", - "OSD", - "Battery Menu", - "Bluetooth Menu", - "Clock Menu", - "Dashboard Menu", - "Media Menu", "Network Menu", - "Notifications Menu", - "System Tray", - "Volume Menu", - "Power Menu", - "Custom Modules", -] + 'General Settings', + 'Matugen Settings', + 'Bar', + 'Notifications', + 'OSD', + 'Battery Menu', + 'Bluetooth Menu', + 'Clock Menu', + 'Dashboard Menu', + 'Media Menu', + 'Network Menu', + 'Notifications Menu', + 'System Tray', + 'Volume Menu', + 'Power Menu', + 'Custom Modules', +]; -export const ThemesMenu = () => { +export const ThemesMenu = (): GBox => { return Widget.Box({ vertical: true, - children: CurrentPage.bind("value").as(v => { + children: CurrentPage.bind('value').as((v) => { return [ Widget.Box({ - class_name: "option-pages-container", - hpack: "center", + class_name: 'option-pages-container', + hpack: 'center', hexpand: true, vertical: true, - children: [0, 1, 2].map(section => { + children: [0, 1, 2].map((section) => { return Widget.Box({ children: pagerMap.map((page, index) => { if (index >= section * 6 && index < section * 6 + 6) { return Widget.Button({ - hpack: "center", + hpack: 'center', xalign: 0, class_name: `pager-button ${v === page ? 'active' : ''}`, label: page, - on_primary_click: () => CurrentPage.value = page - }) + on_primary_click: () => (CurrentPage.value = page), + }); } return Widget.Box(); - }) - }) - }) + }), + }); + }), }), Widget.Stack({ vexpand: true, - class_name: "themes-menu-stack", + class_name: 'themes-menu-stack', children: { - "General Settings": MenuTheme(), - "Matugen Settings": Matugen(), - "Bar": BarTheme(), - "Notifications": NotificationsTheme(), - "OSD": OsdTheme(), - "Battery Menu": BatteryMenuTheme(), - "Bluetooth Menu": BluetoothMenuTheme(), - "Clock Menu": ClockMenuTheme(), - "Dashboard Menu": DashboardMenuTheme(), - "Media Menu": MediaMenuTheme(), - "Network Menu": NetworkMenuTheme(), - "Notifications Menu": NotificationsMenuTheme(), - "System Tray": SystrayMenuTheme(), - "Volume Menu": VolumeMenuTheme(), - "Power Menu": PowerMenuTheme(), - "Custom Modules": CustomModuleTheme(), + 'General Settings': MenuTheme(), + 'Matugen Settings': Matugen(), + Bar: BarTheme(), + Notifications: NotificationsTheme(), + OSD: OsdTheme(), + 'Battery Menu': BatteryMenuTheme(), + 'Bluetooth Menu': BluetoothMenuTheme(), + 'Clock Menu': ClockMenuTheme(), + 'Dashboard Menu': DashboardMenuTheme(), + 'Media Menu': MediaMenuTheme(), + 'Network Menu': NetworkMenuTheme(), + 'Notifications Menu': NotificationsMenuTheme(), + 'System Tray': SystrayMenuTheme(), + 'Volume Menu': VolumeMenuTheme(), + 'Power Menu': PowerMenuTheme(), + 'Custom Modules': CustomModuleTheme(), }, - shown: CurrentPage.bind("value"), - }) - ] - }) - }) -} + shown: CurrentPage.bind('value'), + }), + ]; + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/battery.ts b/widget/settings/pages/theme/menus/battery.ts index 4be41c0..6955f80 100644 --- a/widget/settings/pages/theme/menus/battery.ts +++ b/widget/settings/pages/theme/menus/battery.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const BatteryMenuTheme = () => { +export const BatteryMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page battery paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page battery paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -19,7 +21,11 @@ export const BatteryMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.battery.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.battery.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.battery.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.battery.border.color, title: 'Border', type: 'color' }), @@ -28,8 +34,16 @@ export const BatteryMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.battery.label.color, title: 'Label', type: 'color' }), Header('List Items'), - Option({ opt: options.theme.bar.menus.menu.battery.listitems.active, title: 'Active/Hover', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.battery.listitems.passive, title: 'Passive', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.battery.listitems.active, + title: 'Active/Hover', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.battery.listitems.passive, + title: 'Passive', + type: 'color', + }), Header('Icons'), Option({ opt: options.theme.bar.menus.menu.battery.icons.active, title: 'Active', type: 'color' }), @@ -37,10 +51,18 @@ export const BatteryMenuTheme = () => { Header('Slider'), Option({ opt: options.theme.bar.menus.menu.battery.slider.primary, title: 'Primary', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.battery.slider.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.battery.slider.backgroundhover, title: 'Background (Hover)', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.battery.slider.background, + title: 'Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.battery.slider.backgroundhover, + title: 'Background (Hover)', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.battery.slider.puck, title: 'Puck', type: 'color' }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/bluetooth.ts b/widget/settings/pages/theme/menus/bluetooth.ts index a5580b3..d3adb36 100644 --- a/widget/settings/pages/theme/menus/bluetooth.ts +++ b/widget/settings/pages/theme/menus/bluetooth.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const BluetoothMenuTheme = () => { +export const BluetoothMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page bluetooth paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page bluetooth paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -19,7 +21,11 @@ export const BluetoothMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.bluetooth.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.bluetooth.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.bluetooth.border.color, title: 'Border', type: 'color' }), @@ -28,28 +34,56 @@ export const BluetoothMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.bluetooth.label.color, title: 'Label', type: 'color' }), Header('Status'), - Option({ opt: options.theme.bar.menus.menu.bluetooth.status, title: 'Connection Status', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.status, + title: 'Connection Status', + type: 'color', + }), Header('List Items'), - Option({ opt: options.theme.bar.menus.menu.bluetooth.listitems.active, title: 'Active/Hover', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.bluetooth.listitems.passive, title: 'Passive', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.listitems.active, + title: 'Active/Hover', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.listitems.passive, + title: 'Passive', + type: 'color', + }), Header('Icons'), Option({ opt: options.theme.bar.menus.menu.bluetooth.icons.active, title: 'Active', type: 'color' }), Option({ opt: options.theme.bar.menus.menu.bluetooth.icons.passive, title: 'Passive', type: 'color' }), Header('Icon Buttons'), - Option({ opt: options.theme.bar.menus.menu.bluetooth.iconbutton.active, title: 'Active', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.bluetooth.iconbutton.passive, title: 'Passive', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.iconbutton.active, + title: 'Active', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.iconbutton.passive, + title: 'Passive', + type: 'color', + }), Header('Switch'), Option({ opt: options.theme.bar.menus.menu.bluetooth.switch.enabled, title: 'Enabled', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.bluetooth.switch.disabled, title: 'Disabled', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.bluetooth.switch.disabled, + title: 'Disabled', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.bluetooth.switch.puck, title: 'Puck', type: 'color' }), Header('Switch Divider'), - Option({ opt: options.theme.bar.menus.menu.bluetooth.switch_divider, title: 'Switch Divider', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.bluetooth.switch_divider, + title: 'Switch Divider', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/clock.ts b/widget/settings/pages/theme/menus/clock.ts index f245e8c..2727939 100644 --- a/widget/settings/pages/theme/menus/clock.ts +++ b/widget/settings/pages/theme/menus/clock.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const ClockMenuTheme = () => { +export const ClockMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page clock paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page clock paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -19,37 +21,110 @@ export const ClockMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.clock.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.clock.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.clock.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.clock.border.color, title: 'Border', type: 'color' }), Header('Time'), Option({ opt: options.theme.bar.menus.menu.clock.time.time, title: 'Time', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.time.timeperiod, title: 'Period', subtitle: 'AM/PM', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.clock.time.timeperiod, + title: 'Period', + subtitle: 'AM/PM', + type: 'color', + }), Header('Calendar'), - Option({ opt: options.theme.bar.menus.menu.clock.calendar.yearmonth, title: 'Year/Month', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.clock.calendar.yearmonth, + title: 'Year/Month', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.clock.calendar.weekdays, title: 'Weekdays', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.calendar.paginator, title: 'Navigation Arrows (Hover)', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.calendar.currentday, title: 'Current Day', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.clock.calendar.paginator, + title: 'Navigation Arrows (Hover)', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.calendar.currentday, + title: 'Current Day', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.clock.calendar.days, title: 'Days', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.calendar.contextdays, title: 'Trailing/Leading Days', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.clock.calendar.contextdays, + title: 'Trailing/Leading Days', + type: 'color', + }), Header('Weather'), - Option({ opt: options.theme.bar.menus.menu.clock.weather.icon, title: 'Current Weather Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.temperature, title: 'Current Temperature', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.status, title: 'Current Status', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.stats, title: 'Current Stats', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelyhot, title: 'Thermometer - Extremely Hot', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.thermometer.hot, title: 'Thermometer - Hot', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.thermometer.moderate, title: 'Thermometer - Moderate', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.thermometer.cold, title: 'Thermometer - Cold', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelycold, title: 'Thermometer - Extremely Cold', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.hourly.time, title: 'Hourly Weather Time', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.hourly.icon, title: 'Hourly Weather Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.clock.weather.hourly.temperature, title: 'Hourly Weather Temperature', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.clock.weather.icon, + title: 'Current Weather Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.temperature, + title: 'Current Temperature', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.status, + title: 'Current Status', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.stats, + title: 'Current Stats', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelyhot, + title: 'Thermometer - Extremely Hot', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.thermometer.hot, + title: 'Thermometer - Hot', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.thermometer.moderate, + title: 'Thermometer - Moderate', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.thermometer.cold, + title: 'Thermometer - Cold', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelycold, + title: 'Thermometer - Extremely Cold', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.hourly.time, + title: 'Hourly Weather Time', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.hourly.icon, + title: 'Hourly Weather Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.clock.weather.hourly.temperature, + title: 'Hourly Weather Temperature', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/dashboard.ts b/widget/settings/pages/theme/menus/dashboard.ts index b69a461..76bb88f 100644 --- a/widget/settings/pages/theme/menus/dashboard.ts +++ b/widget/settings/pages/theme/menus/dashboard.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const DashboardMenuTheme = () => { +export const DashboardMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "always", - hscroll: "automatic", - class_name: "menu-theme-page dashboard paged-container", + vscroll: 'always', + hscroll: 'automatic', + class_name: 'menu-theme-page dashboard paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -16,69 +18,251 @@ export const DashboardMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.dashboard.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.dashboard.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.dashboard.border.color, title: 'Border', type: 'color' }), Header('Profile'), - Option({ opt: options.theme.bar.menus.menu.dashboard.profile.name, title: 'Profile Name', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.profile.name, + title: 'Profile Name', + type: 'color', + }), Header('Power Menu'), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.shutdown, title: 'Shutdown', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.restart, title: 'Restart', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.logout, title: 'Log Out', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.shutdown, + title: 'Shutdown', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.restart, + title: 'Restart', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.logout, + title: 'Log Out', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.sleep, title: 'Sleep', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.card, title: 'Confirmation Dialog Card', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.background, title: 'Confirmation Dialog Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.border, title: 'Confirmation Dialog Border', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.label, title: 'Confirmation Dialog Label', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.body, title: 'Confirmation Dialog Description', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.confirm, title: 'Confirmation Dialog Confirm Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.deny, title: 'Confirmation Dialog Cancel Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.button_text, title: 'Confirmation Dialog Button Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.card, + title: 'Confirmation Dialog Card', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.background, + title: 'Confirmation Dialog Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.border, + title: 'Confirmation Dialog Border', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.label, + title: 'Confirmation Dialog Label', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.body, + title: 'Confirmation Dialog Description', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.confirm, + title: 'Confirmation Dialog Confirm Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.deny, + title: 'Confirmation Dialog Cancel Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.button_text, + title: 'Confirmation Dialog Button Text', + type: 'color', + }), Header('Shortcuts'), - Option({ opt: options.theme.bar.menus.menu.dashboard.shortcuts.background, title: 'Primary', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.shortcuts.background, + title: 'Primary', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.dashboard.shortcuts.text, title: 'Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.shortcuts.recording, title: 'Recording', subtitle: 'Color of the Record button when recording is in progress', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.shortcuts.recording, + title: 'Recording', + subtitle: 'Color of the Record button when recording is in progress', + type: 'color', + }), Header('Controls'), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.disabled, title: 'Module Off', subtitle: 'Button color when element is disabled', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.wifi.background, title: 'Wifi Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.wifi.text, title: 'Wifi Button Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.background, title: 'Bluetooth Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.text, title: 'Bluetooth Button Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.notifications.background, title: 'Notifications Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.notifications.text, title: 'Notifications Button Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.volume.background, title: 'Volume Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.volume.text, title: 'Volume Button Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.input.background, title: 'Input Button', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.controls.input.text, title: 'Input Button Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.disabled, + title: 'Module Off', + subtitle: 'Button color when element is disabled', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.wifi.background, + title: 'Wifi Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.wifi.text, + title: 'Wifi Button Text', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.background, + title: 'Bluetooth Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.text, + title: 'Bluetooth Button Text', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.notifications.background, + title: 'Notifications Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.notifications.text, + title: 'Notifications Button Text', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.volume.background, + title: 'Volume Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.volume.text, + title: 'Volume Button Text', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.input.background, + title: 'Input Button', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.controls.input.text, + title: 'Input Button Text', + type: 'color', + }), Header('Directories'), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.left.top.color, title: 'Directory: Left - Top', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.left.middle.color, title: 'Directory: Left - Middle', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.left.bottom.color, title: 'Directory: Left - Bottom', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.right.top.color, title: 'Directory: Right - Top', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.right.middle.color, title: 'Directory: Right - Middle', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.directories.right.bottom.color, title: 'Directory: Right - Bottom', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.left.top.color, + title: 'Directory: Left - Top', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.left.middle.color, + title: 'Directory: Left - Middle', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.left.bottom.color, + title: 'Directory: Left - Bottom', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.right.top.color, + title: 'Directory: Right - Top', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.right.middle.color, + title: 'Directory: Right - Middle', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.directories.right.bottom.color, + title: 'Directory: Right - Bottom', + type: 'color', + }), Header('System Stats'), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.bar_background, title: 'Bar Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.icon, title: 'CPU Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.bar, title: 'CPU Bar', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.label, title: 'CPU Label', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.ram.icon, title: 'RAM Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.ram.bar, title: 'RAM Bar', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.ram.label, title: 'RAM Label', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.icon, title: 'GPU Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.bar, title: 'GPU Bar', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.label, title: 'GPU Label', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.disk.icon, title: 'Disk Icon', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.disk.bar, title: 'Disk Bar', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.dashboard.monitors.disk.label, title: 'Disk Label', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.bar_background, + title: 'Bar Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.icon, + title: 'CPU Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.bar, + title: 'CPU Bar', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.label, + title: 'CPU Label', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.ram.icon, + title: 'RAM Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.ram.bar, + title: 'RAM Bar', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.ram.label, + title: 'RAM Label', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.icon, + title: 'GPU Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.bar, + title: 'GPU Bar', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.label, + title: 'GPU Label', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.disk.icon, + title: 'Disk Icon', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.disk.bar, + title: 'Disk Bar', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.dashboard.monitors.disk.label, + title: 'Disk Label', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/index.ts b/widget/settings/pages/theme/menus/index.ts index 97ae58c..333fe96 100644 --- a/widget/settings/pages/theme/menus/index.ts +++ b/widget/settings/pages/theme/menus/index.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const MenuTheme = () => { +export const MenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -20,14 +22,42 @@ export const MenuTheme = () => { type: 'config_import', exportData: { filePath: OPTIONS, - themeOnly: true - } + themeOnly: true, + }, + }), + Option({ + opt: options.theme.bar.menus.monochrome, + title: 'Use Global Colors', + type: 'boolean', + disabledBinding: options.theme.matugen, + }), + Option({ + opt: options.wallpaper.pywal, + title: 'Generate Pywal Colors', + subtitle: 'Whether to also generate pywal colors with chosen wallpaper', + type: 'boolean', + }), + Option({ + opt: options.wallpaper.enable, + title: 'Apply Wallpapers', + subtitle: 'Whether to apply the wallpaper or to only use it for Matugen color generation.', + type: 'boolean', + }), + Option({ + opt: options.wallpaper.image, + title: 'Wallpaper', + subtitle: options.wallpaper.image.bind('value'), + type: 'wallpaper', }), - Option({ opt: options.theme.bar.menus.monochrome, title: 'Use Global Colors', type: 'boolean', disabledBinding: options.theme.matugen }), - Option({ opt: options.wallpaper.enable, title: 'Apply Wallpapers', subtitle: 'Whether to apply the wallpaper or to only use it for Matugen color generation.', type: 'boolean' }), - Option({ opt: options.wallpaper.image, title: 'Wallpaper', subtitle: options.wallpaper.image.bind("value"), type: 'wallpaper' }), Option({ opt: options.theme.bar.menus.background, title: 'Background Color', type: 'color' }), - Option({ opt: options.theme.bar.menus.opacity, title: 'Menu Opacity', type: 'number', increment: 5, min: 0, max: 100 }), + Option({ + opt: options.theme.bar.menus.opacity, + title: 'Menu Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), Option({ opt: options.theme.bar.menus.cards, title: 'Cards', type: 'color' }), Option({ opt: options.theme.bar.menus.card_radius, title: 'Card Radius', type: 'string' }), Option({ opt: options.theme.bar.menus.text, title: 'Primary Text', type: 'color' }), @@ -45,7 +75,13 @@ export const MenuTheme = () => { Option({ opt: options.theme.bar.menus.popover.background, title: 'Background', type: 'color' }), Header('List Items'), - Option({ opt: options.theme.bar.menus.listitems.active, title: 'Active', subtitle: 'Items of a list (network name, bluetooth device name, playback device, etc.) when active or hovered.', type: 'color' }), + Option({ + opt: options.theme.bar.menus.listitems.active, + title: 'Active', + subtitle: + 'Items of a list (network name, bluetooth device name, playback device, etc.) when active or hovered.', + type: 'color', + }), Option({ opt: options.theme.bar.menus.listitems.passive, title: 'Passive', type: 'color' }), Header('Icons'), @@ -58,7 +94,11 @@ export const MenuTheme = () => { Option({ opt: options.theme.bar.menus.switch.puck, title: 'Puck', type: 'color' }), Header('Check/Radio Buttons'), - Option({ opt: options.theme.bar.menus.check_radio_button.background, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.check_radio_button.background, + title: 'Background', + type: 'color', + }), Option({ opt: options.theme.bar.menus.check_radio_button.active, title: 'Active', type: 'color' }), Header('Buttons'), @@ -78,7 +118,11 @@ export const MenuTheme = () => { Header('Slider'), Option({ opt: options.theme.bar.menus.slider.primary, title: 'Primary', type: 'color' }), Option({ opt: options.theme.bar.menus.slider.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.slider.backgroundhover, title: 'Background (Hover)', type: 'color' }), + Option({ + opt: options.theme.bar.menus.slider.backgroundhover, + title: 'Background (Hover)', + type: 'color', + }), Option({ opt: options.theme.bar.menus.slider.puck, title: 'Puck', type: 'color' }), Header('Dropdown Menu'), @@ -89,8 +133,7 @@ export const MenuTheme = () => { Header('Tooltips'), Option({ opt: options.theme.bar.menus.tooltip.background, title: 'Background', type: 'color' }), Option({ opt: options.theme.bar.menus.tooltip.text, title: 'Text', type: 'color' }), - - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/matugen.ts b/widget/settings/pages/theme/menus/matugen.ts index d007d33..6f7a208 100644 --- a/widget/settings/pages/theme/menus/matugen.ts +++ b/widget/settings/pages/theme/menus/matugen.ts @@ -1,53 +1,71 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const Matugen = () => { +export const Matugen = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page paged-container', vexpand: true, child: Widget.Box({ vertical: true, children: [ Header('Matugen Settings'), - Option({ opt: options.theme.matugen, title: 'Enable Matugen', subtitle: 'WARNING: THIS WILL REPLACE YOUR CURRENT COLOR SCHEME!!!', type: 'boolean', dependencies: ["matugen", "swww"] }), - Option({ opt: options.theme.matugen_settings.mode, title: 'Matugen Theme', type: 'enum', enums: ["light", "dark"] }), + Option({ + opt: options.theme.matugen, + title: 'Enable Matugen', + subtitle: 'WARNING: THIS WILL REPLACE YOUR CURRENT COLOR SCHEME!!!', + type: 'boolean', + dependencies: ['matugen', 'swww'], + }), + Option({ + opt: options.theme.matugen_settings.mode, + title: 'Matugen Theme', + type: 'enum', + enums: ['light', 'dark'], + }), Option({ opt: options.theme.matugen_settings.scheme_type, title: 'Matugen Scheme', type: 'enum', enums: [ - "content", - "expressive", - "fidelity", - "fruit-salad", - "monochrome", - "neutral", - "rainbow", - "tonal-spot" - ] + 'content', + 'expressive', + 'fidelity', + 'fruit-salad', + 'monochrome', + 'neutral', + 'rainbow', + 'tonal-spot', + ], }), Option({ opt: options.theme.matugen_settings.variation, title: 'Matugen Variation', type: 'enum', enums: [ - "standard_1", - "standard_2", - "standard_3", - "monochrome_1", - "monochrome_2", - "monochrome_3", - "vivid_1", - "vivid_2", - "vivid_3", - ] + 'standard_1', + 'standard_2', + 'standard_3', + 'monochrome_1', + 'monochrome_2', + 'monochrome_3', + 'vivid_1', + 'vivid_2', + 'vivid_3', + ], }), - Option({ opt: options.theme.matugen_settings.contrast, title: 'Contrast', subtitle: 'Range: -1 to 1 (Default: 0)', type: 'float' }), - ] - }) - }) -} + Option({ + opt: options.theme.matugen_settings.contrast, + title: 'Contrast', + subtitle: 'Range: -1 to 1 (Default: 0)', + type: 'float', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/media.ts b/widget/settings/pages/theme/menus/media.ts index 0c6f9ac..6058cd9 100644 --- a/widget/settings/pages/theme/menus/media.ts +++ b/widget/settings/pages/theme/menus/media.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const MediaMenuTheme = () => { +export const MediaMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page media paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page media paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -18,27 +20,64 @@ export const MediaMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.media.album, title: 'Album', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.media.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.media.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.media.border.color, title: 'Border', type: 'color' }), Header('Card/Album Art'), Option({ opt: options.theme.bar.menus.menu.media.card.color, title: 'Color', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.media.card.tint, title: 'Tint', type: 'number', increment: 5, min: 0, max: 100 }), + Option({ + opt: options.theme.bar.menus.menu.media.card.tint, + title: 'Tint', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), Header('Buttons'), - Option({ opt: options.theme.bar.menus.menu.media.buttons.inactive, title: 'Unavailable', subtitle: 'Disabled button when media control isn\'t available.', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.media.buttons.enabled, title: 'Enabled', subtitle: 'Ex: Button color when shuffle/loop is enabled.', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.media.buttons.background, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.media.buttons.inactive, + title: 'Unavailable', + subtitle: "Disabled button when media control isn't available.", + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.media.buttons.enabled, + title: 'Enabled', + subtitle: 'Ex: Button color when shuffle/loop is enabled.', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.media.buttons.background, + title: 'Background', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.media.buttons.text, title: 'Text', type: 'color' }), Header('Slider'), - Option({ opt: options.theme.bar.menus.menu.media.slider.primary, title: 'Primary Color', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.media.slider.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.media.slider.backgroundhover, title: 'Backround (Hover)', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.media.slider.primary, + title: 'Primary Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.media.slider.background, + title: 'Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.media.slider.backgroundhover, + title: 'Backround (Hover)', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.media.slider.puck, title: 'Puck', type: 'color' }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/network.ts b/widget/settings/pages/theme/menus/network.ts index 58bdc8d..da52e8c 100644 --- a/widget/settings/pages/theme/menus/network.ts +++ b/widget/settings/pages/theme/menus/network.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const NetworkMenuTheme = () => { +export const NetworkMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page network paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page network paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -19,7 +21,11 @@ export const NetworkMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.network.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.network.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.network.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.network.border.color, title: 'Border', type: 'color' }), @@ -28,20 +34,40 @@ export const NetworkMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.network.label.color, title: 'Label', type: 'color' }), Header('Status'), - Option({ opt: options.theme.bar.menus.menu.network.status.color, title: 'Connection Status', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.network.status.color, + title: 'Connection Status', + type: 'color', + }), Header('List Items'), - Option({ opt: options.theme.bar.menus.menu.network.listitems.active, title: 'Active/Hover', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.network.listitems.passive, title: 'Passive', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.network.listitems.active, + title: 'Active/Hover', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.network.listitems.passive, + title: 'Passive', + type: 'color', + }), Header('Icons'), Option({ opt: options.theme.bar.menus.menu.network.icons.active, title: 'Active', type: 'color' }), Option({ opt: options.theme.bar.menus.menu.network.icons.passive, title: 'Passive', type: 'color' }), Header('Icon Buttons'), - Option({ opt: options.theme.bar.menus.menu.network.iconbuttons.active, title: 'Active', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.network.iconbuttons.passive, title: 'Passive', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.network.iconbuttons.active, + title: 'Active', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.network.iconbuttons.passive, + title: 'Passive', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/notifications.ts b/widget/settings/pages/theme/menus/notifications.ts index dc9fb89..c313ac6 100644 --- a/widget/settings/pages/theme/menus/notifications.ts +++ b/widget/settings/pages/theme/menus/notifications.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const NotificationsMenuTheme = () => { +export const NotificationsMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page notifications paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page notifications paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -15,25 +17,65 @@ export const NotificationsMenuTheme = () => { Header('Notifications Menu Theme Settings'), Option({ opt: options.theme.bar.menus.menu.notifications.label, title: 'Menu Label', type: 'color' }), Option({ opt: options.theme.bar.menus.menu.notifications.card, title: 'Card', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.no_notifications_label, title: 'Empty Notifications Backdrop', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.notifications.background, + title: 'Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.no_notifications_label, + title: 'Empty Notifications Backdrop', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.notifications.border, title: 'Border', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.switch_divider, title: 'Switch Divider', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.clear, title: 'Clear Notifications Button', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.notifications.switch_divider, + title: 'Switch Divider', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.clear, + title: 'Clear Notifications Button', + type: 'color', + }), Header('Switch'), - Option({ opt: options.theme.bar.menus.menu.notifications.switch.enabled, title: 'Enabled', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.switch.disabled, title: 'Disabled', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.notifications.switch.enabled, + title: 'Enabled', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.switch.disabled, + title: 'Disabled', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.notifications.switch.puck, title: 'Puck', type: 'color' }), Header('Scrollbar'), - Option({ opt: options.theme.bar.menus.menu.notifications.scrollbar.color, title: 'Scrollbar Color', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.notifications.scrollbar.color, + title: 'Scrollbar Color', + type: 'color', + }), Header('Pagination'), - Option({ opt: options.theme.bar.menus.menu.notifications.pager.background, title: 'Pager Footer Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.pager.button, title: 'Pager Button Color', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.notifications.pager.label, title: 'Pager Label Color', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.notifications.pager.background, + title: 'Pager Footer Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.pager.button, + title: 'Pager Button Color', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.notifications.pager.label, + title: 'Pager Label Color', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/power.ts b/widget/settings/pages/theme/menus/power.ts index 037bd6c..43e7398 100644 --- a/widget/settings/pages/theme/menus/power.ts +++ b/widget/settings/pages/theme/menus/power.ts @@ -1,47 +1,101 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const PowerMenuTheme = () => { +export const PowerMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page power paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page power paged-container', vexpand: true, child: Widget.Box({ vertical: true, children: [ Header('Background'), - Option({ opt: options.theme.bar.menus.menu.power.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.power.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.power.border.color, title: 'Border', type: 'color' }), Header('Shutdown Button'), - Option({ opt: options.theme.bar.menus.menu.power.buttons.shutdown.background, title: 'Label Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.shutdown.icon_background, title: 'Icon Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.shutdown.text, title: 'Label Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.shutdown.background, + title: 'Label Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.shutdown.icon_background, + title: 'Icon Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.shutdown.text, + title: 'Label Text', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.power.buttons.shutdown.icon, title: 'Icon', type: 'color' }), Header('Reboot Button'), - Option({ opt: options.theme.bar.menus.menu.power.buttons.restart.background, title: 'Label Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.restart.icon_background, title: 'Icon Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.restart.text, title: 'Label Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.restart.background, + title: 'Label Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.restart.icon_background, + title: 'Icon Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.restart.text, + title: 'Label Text', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.power.buttons.restart.icon, title: 'Icon', type: 'color' }), Header('Logout Button'), - Option({ opt: options.theme.bar.menus.menu.power.buttons.logout.background, title: 'Label Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.logout.icon_background, title: 'Icon Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.logout.text, title: 'Label Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.logout.background, + title: 'Label Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.logout.icon_background, + title: 'Icon Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.logout.text, + title: 'Label Text', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.power.buttons.logout.icon, title: 'Icon', type: 'color' }), Header('Sleep Button'), - Option({ opt: options.theme.bar.menus.menu.power.buttons.sleep.background, title: 'Label Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.sleep.icon_background, title: 'Icon Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.power.buttons.sleep.text, title: 'Label Text', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.sleep.background, + title: 'Label Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.sleep.icon_background, + title: 'Icon Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.power.buttons.sleep.text, + title: 'Label Text', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.power.buttons.sleep.icon, title: 'Icon', type: 'color' }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/systray.ts b/widget/settings/pages/theme/menus/systray.ts index ad0cde6..5276a03 100644 --- a/widget/settings/pages/theme/menus/systray.ts +++ b/widget/settings/pages/theme/menus/systray.ts @@ -1,22 +1,32 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const SystrayMenuTheme = () => { +export const SystrayMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page systray paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page systray paged-container', vexpand: true, child: Widget.Box({ vertical: true, children: [ Header('Dropdown Menu'), - Option({ opt: options.theme.bar.menus.menu.systray.dropdownmenu.background, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.systray.dropdownmenu.background, + title: 'Background', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.systray.dropdownmenu.text, title: 'Text', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.systray.dropdownmenu.divider, title: 'Section Divider', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.bar.menus.menu.systray.dropdownmenu.divider, + title: 'Section Divider', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/menus/volume.ts b/widget/settings/pages/theme/menus/volume.ts index 5505245..efad554 100644 --- a/widget/settings/pages/theme/menus/volume.ts +++ b/widget/settings/pages/theme/menus/volume.ts @@ -1,13 +1,15 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const VolumeMenuTheme = () => { +export const VolumeMenuTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "menu-theme-page volume paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'menu-theme-page volume paged-container', vexpand: true, child: Widget.Box({ vertical: true, @@ -19,7 +21,11 @@ export const VolumeMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.volume.card.color, title: 'Card', type: 'color' }), Header('Background'), - Option({ opt: options.theme.bar.menus.menu.volume.background.color, title: 'Background', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.volume.background.color, + title: 'Background', + type: 'color', + }), Header('Border'), Option({ opt: options.theme.bar.menus.menu.volume.border.color, title: 'Border', type: 'color' }), @@ -28,30 +34,65 @@ export const VolumeMenuTheme = () => { Option({ opt: options.theme.bar.menus.menu.volume.label.color, title: 'Label', type: 'color' }), Header('List Items'), - Option({ opt: options.theme.bar.menus.menu.volume.listitems.active, title: 'Active/Hover', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.volume.listitems.active, + title: 'Active/Hover', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.volume.listitems.passive, title: 'Passive', type: 'color' }), Header('Icon Button'), - Option({ opt: options.theme.bar.menus.menu.volume.iconbutton.active, title: 'Active/Hover', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.volume.iconbutton.passive, title: 'Passive', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.volume.iconbutton.active, + title: 'Active/Hover', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.volume.iconbutton.passive, + title: 'Passive', + type: 'color', + }), Header('Icons'), Option({ opt: options.theme.bar.menus.menu.volume.icons.active, title: 'Active', type: 'color' }), Option({ opt: options.theme.bar.menus.menu.volume.icons.passive, title: 'Passive', type: 'color' }), Header('Audio Slider'), - Option({ opt: options.theme.bar.menus.menu.volume.audio_slider.primary, title: 'Primary', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.volume.audio_slider.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.volume.audio_slider.backgroundhover, title: 'Background (Hover)', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.volume.audio_slider.primary, + title: 'Primary', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.volume.audio_slider.background, + title: 'Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.volume.audio_slider.backgroundhover, + title: 'Background (Hover)', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.volume.audio_slider.puck, title: 'Puck', type: 'color' }), Header('Input Slider'), - Option({ opt: options.theme.bar.menus.menu.volume.input_slider.primary, title: 'Primary', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.volume.input_slider.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.menus.menu.volume.input_slider.backgroundhover, title: 'Background (Hover)', type: 'color' }), + Option({ + opt: options.theme.bar.menus.menu.volume.input_slider.primary, + title: 'Primary', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.volume.input_slider.background, + title: 'Background', + type: 'color', + }), + Option({ + opt: options.theme.bar.menus.menu.volume.input_slider.backgroundhover, + title: 'Background (Hover)', + type: 'color', + }), Option({ opt: options.theme.bar.menus.menu.volume.input_slider.puck, title: 'Puck', type: 'color' }), - - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/notifications/index.ts b/widget/settings/pages/theme/notifications/index.ts index fbf71a9..25adf1a 100644 --- a/widget/settings/pages/theme/notifications/index.ts +++ b/widget/settings/pages/theme/notifications/index.ts @@ -1,30 +1,61 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const NotificationsTheme = () => { +export const NotificationsTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "notifications-theme-page paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'notifications-theme-page paged-container', vexpand: true, child: Widget.Box({ vertical: true, children: [ Header('Notifications Theme Settings'), Option({ opt: options.theme.notification.background, title: 'Notification Background', type: 'color' }), - Option({ opt: options.theme.notification.opacity, title: 'Notification Opacity', type: 'number', increment: 5, min: 0, max: 100 }), - Option({ opt: options.theme.notification.actions.background, title: 'Action Button Background', subtitle: 'Buttons that perform actions within a notification', type: 'color' }), - Option({ opt: options.theme.notification.actions.text, title: 'Action Button Text Color', type: 'color' }), + Option({ + opt: options.theme.notification.opacity, + title: 'Notification Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), + Option({ + opt: options.theme.notification.actions.background, + title: 'Action Button Background', + subtitle: 'Buttons that perform actions within a notification', + type: 'color', + }), + Option({ + opt: options.theme.notification.actions.text, + title: 'Action Button Text Color', + type: 'color', + }), Option({ opt: options.theme.notification.label, title: 'Label', type: 'color' }), Option({ opt: options.theme.notification.border, title: 'Border', type: 'color' }), Option({ opt: options.theme.notification.time, title: 'Time Stamp', type: 'color' }), Option({ opt: options.theme.notification.text, title: 'Body Text', type: 'color' }), - Option({ opt: options.theme.notification.labelicon, title: 'Label Icon', subtitle: 'Icon that accompanies the label. Doesn\'t apply if icon is an app icon.', type: 'color' }), - Option({ opt: options.theme.notification.close_button.background, title: 'Dismiss Button', type: 'color' }), - Option({ opt: options.theme.notification.close_button.label, title: 'Dismiss Button Text', type: 'color' }), - ] - }) - }) -} + Option({ + opt: options.theme.notification.labelicon, + title: 'Label Icon', + subtitle: "Icon that accompanies the label. Doesn't apply if icon is an app icon.", + type: 'color', + }), + Option({ + opt: options.theme.notification.close_button.background, + title: 'Dismiss Button', + type: 'color', + }), + Option({ + opt: options.theme.notification.close_button.label, + title: 'Dismiss Button Text', + type: 'color', + }), + ], + }), + }); +}; diff --git a/widget/settings/pages/theme/osd/index.ts b/widget/settings/pages/theme/osd/index.ts index 9c578d4..def8846 100644 --- a/widget/settings/pages/theme/osd/index.ts +++ b/widget/settings/pages/theme/osd/index.ts @@ -1,27 +1,41 @@ -import { Option } from "widget/settings/shared/Option"; -import { Header } from "widget/settings/shared/Header"; +import { Option } from 'widget/settings/shared/Option'; +import { Header } from 'widget/settings/shared/Header'; -import options from "options"; +import options from 'options'; +import Scrollable from 'types/widgets/scrollable'; +import { Attribute, Child } from 'lib/types/widget'; -export const OsdTheme = () => { +export const OsdTheme = (): Scrollable => { return Widget.Scrollable({ - vscroll: "automatic", - hscroll: "automatic", - class_name: "osd-theme-page paged-container", + vscroll: 'automatic', + hscroll: 'automatic', + class_name: 'osd-theme-page paged-container', vexpand: true, child: Widget.Box({ vertical: true, children: [ Header('On Screen Display Settings'), - Option({ opt: options.theme.osd.opacity, title: 'OSD Opacity', type: 'number', increment: 5, min: 0, max: 100 }), + Option({ + opt: options.theme.osd.opacity, + title: 'OSD Opacity', + type: 'number', + increment: 5, + min: 0, + max: 100, + }), Option({ opt: options.theme.osd.bar_color, title: 'Bar', type: 'color' }), - Option({ opt: options.theme.osd.bar_overflow_color, title: 'Bar Overflow', subtitle: 'Overflow color is for when the volume goes over a 100', type: 'color' }), + Option({ + opt: options.theme.osd.bar_overflow_color, + title: 'Bar Overflow', + subtitle: 'Overflow color is for when the volume goes over a 100', + type: 'color', + }), Option({ opt: options.theme.osd.bar_empty_color, title: 'Bar Background', type: 'color' }), Option({ opt: options.theme.osd.bar_container, title: 'Bar Container', type: 'color' }), Option({ opt: options.theme.osd.icon, title: 'Icon', type: 'color' }), Option({ opt: options.theme.osd.icon_container, title: 'Icon Container', type: 'color' }), Option({ opt: options.theme.osd.label, title: 'Value Text', type: 'color' }), - ] - }) - }) -} + ], + }), + }); +}; diff --git a/widget/settings/shared/FileChooser.ts b/widget/settings/shared/FileChooser.ts index 49ea131..b7b5f32 100644 --- a/widget/settings/shared/FileChooser.ts +++ b/widget/settings/shared/FileChooser.ts @@ -1,44 +1,42 @@ -import Gtk from "gi://Gtk?version=3.0"; -import Gio from "gi://Gio" -import { bash, Notify } from "lib/utils"; -import icons from "lib/icons" +import Gtk from 'gi://Gtk?version=3.0'; +import Gio from 'gi://Gio'; +import { bash, Notify } from 'lib/utils'; +import icons from 'lib/icons'; +import { Config } from 'lib/types/filechooser'; +import { hexColorPattern } from 'globals/useTheme'; +import { isHexColor } from 'globals/variables'; -const whiteListedThemeProp = [ - "theme.bar.buttons.style" -]; +const whiteListedThemeProp = ['theme.bar.buttons.style']; - -// Helper functions -export const loadJsonFile = (filePath: string): object | null => { - let file = Gio.File.new_for_path(filePath as string); - let [success, content] = file.load_contents(null); +export const loadJsonFile = (filePath: string): Config | null => { + const file = Gio.File.new_for_path(filePath as string); + const [success, content] = file.load_contents(null); if (!success) { console.error(`Failed to import: ${filePath}`); return null; } - let jsonString = new TextDecoder("utf-8").decode(content); + const jsonString = new TextDecoder('utf-8').decode(content); return JSON.parse(jsonString); -} +}; export const saveConfigToFile = (config: object, filePath: string): void => { - let file = Gio.File.new_for_path(filePath); - let outputStream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); - let dataOutputStream = new Gio.DataOutputStream({ base_stream: outputStream }); + const file = Gio.File.new_for_path(filePath); + const outputStream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); + const dataOutputStream = new Gio.DataOutputStream({ base_stream: outputStream }); - let jsonString = JSON.stringify(config, null, 2); + const jsonString = JSON.stringify(config, null, 2); dataOutputStream.put_string(jsonString, null); dataOutputStream.close(null); -} +}; -export const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/; +export const filterConfigForThemeOnly = (config: Config): Config => { + const filteredConfig: Config = {}; -export const filterConfigForThemeOnly = (config: object) => { - let filteredConfig = {}; - - for (let key in config) { - if (typeof config[key] === 'string' && hexColorPattern.test(config[key])) { + for (const key in config) { + const value = config[key]; + if (typeof value === 'string' && hexColorPattern.test(value)) { filteredConfig[key] = config[key]; } else if (whiteListedThemeProp.includes(key)) { filteredConfig[key] = config[key]; @@ -47,13 +45,15 @@ export const filterConfigForThemeOnly = (config: object) => { return filteredConfig; }; -export const filterConfigForNonTheme = (config: object) => { - let filteredConfig = {}; - for (let key in config) { +export const filterConfigForNonTheme = (config: Config): Config => { + const filteredConfig: Config = {}; + for (const key in config) { if (whiteListedThemeProp.includes(key)) { continue; } - if (!(typeof config[key] === 'string' && hexColorPattern.test(config[key]))) { + + const value = config[key]; + if (!(typeof value === 'string' && hexColorPattern.test(value))) { filteredConfig[key] = config[key]; } } @@ -63,46 +63,45 @@ export const filterConfigForNonTheme = (config: object) => { export const saveFileDialog = (filePath: string, themeOnly: boolean): void => { const original_file_path = filePath; - let file = Gio.File.new_for_path(original_file_path); - let [success, content] = file.load_contents(null); + const file = Gio.File.new_for_path(original_file_path); + const [success, content] = file.load_contents(null); if (!success) { console.error(`Could not find 'config.json' at ${TMP}`); return; } - let jsonString = new TextDecoder("utf-8").decode(content); - let jsonObject = JSON.parse(jsonString); + const jsonString = new TextDecoder('utf-8').decode(content); + const jsonObject = JSON.parse(jsonString); // Function to filter hex color pairs - const filterHexColorPairs = (jsonObject: object) => { - const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/; - let filteredObject = {}; + const filterHexColorPairs = (jsonObject: Config): Config => { + const filteredObject: Config = {}; - for (let key in jsonObject) { - if (typeof jsonObject[key] === 'string' && hexColorPattern.test(jsonObject[key])) { + for (const key in jsonObject) { + const value = jsonObject[key]; + if (typeof value === 'string' && isHexColor(value)) { filteredObject[key] = jsonObject[key]; } else if (whiteListedThemeProp.includes(key)) { filteredObject[key] = jsonObject[key]; } - } return filteredObject; }; // Function to filter out hex color pairs (keep only non-hex color value) - const filterOutHexColorPairs = (jsonObject: object) => { - const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/; - let filteredObject = {}; + const filterOutHexColorPairs = (jsonObject: Config): Config => { + const filteredObject: Config = {}; - for (let key in jsonObject) { + for (const key in jsonObject) { // do not add key-value pair if its in whiteListedThemeProp if (whiteListedThemeProp.includes(key)) { continue; } - if (!(typeof jsonObject[key] === 'string' && hexColorPattern.test(jsonObject[key]))) { + const value = jsonObject[key]; + if (!(typeof value === 'string' && isHexColor(value))) { filteredObject[key] = jsonObject[key]; } } @@ -111,29 +110,29 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => { }; // Filter the JSON object based on the themeOnly flag - let filteredJsonObject = themeOnly ? filterHexColorPairs(jsonObject) : filterOutHexColorPairs(jsonObject); - let filteredContent = JSON.stringify(filteredJsonObject, null, 2); + const filteredJsonObject = themeOnly ? filterHexColorPairs(jsonObject) : filterOutHexColorPairs(jsonObject); + const filteredContent = JSON.stringify(filteredJsonObject, null, 2); - let dialog = new Gtk.FileChooserDialog({ - title: "Save File As", + const dialog = new Gtk.FileChooserDialog({ + title: 'Save File As', action: Gtk.FileChooserAction.SAVE, }); dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL); dialog.add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT); - dialog.set_current_name(themeOnly ? "hyprpanel_theme.json" : "hyprpanel_config.json"); + dialog.set_current_name(themeOnly ? 'hyprpanel_theme.json' : 'hyprpanel_config.json'); - let response = dialog.run(); + const response = dialog.run(); if (response === Gtk.ResponseType.ACCEPT) { - let file_path = dialog.get_filename(); + const file_path = dialog.get_filename(); console.info(`Original file path: ${file_path}`); - const getIncrementedFilePath = (filePath: string) => { + const getIncrementedFilePath = (filePath: string): string => { let increment = 1; - let baseName = filePath.replace(/(\.\w+)$/, ''); - let match = filePath.match(/(\.\w+)$/); - let extension = match ? match[0] : ''; + const baseName = filePath.replace(/(\.\w+)$/, ''); + const match = filePath.match(/(\.\w+)$/); + const extension = match ? match[0] : ''; let newFilePath = filePath; let file = Gio.File.new_for_path(newFilePath); @@ -147,14 +146,14 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => { return newFilePath; }; - let finalFilePath = getIncrementedFilePath(file_path as string); + const finalFilePath = getIncrementedFilePath(file_path as string); console.info(`File will be saved at: ${finalFilePath}`); try { - let save_file = Gio.File.new_for_path(finalFilePath); - let outputStream = save_file.replace(null, false, Gio.FileCreateFlags.NONE, null); - let dataOutputStream = new Gio.DataOutputStream({ - base_stream: outputStream + const save_file = Gio.File.new_for_path(finalFilePath); + const outputStream = save_file.replace(null, false, Gio.FileCreateFlags.NONE, null); + const dataOutputStream = new Gio.DataOutputStream({ + base_stream: outputStream, }); dataOutputStream.put_string(filteredContent, null); @@ -162,38 +161,50 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => { dataOutputStream.close(null); Notify({ - summary: "File Saved Successfully", + summary: 'File Saved Successfully', body: `At ${finalFilePath}.`, iconName: icons.ui.info, - timeout: 5000 + timeout: 5000, }); - - } catch (e: any) { - console.error("Failed to write to file:", e.message); + } catch (e) { + if (e instanceof Error) { + console.error('Failed to write to file:', e.message); + } } } dialog.destroy(); -} +}; export const importFiles = (themeOnly: boolean = false): void => { - let dialog = new Gtk.FileChooserDialog({ - title: `Import ${themeOnly ? "Theme" : "Config"}`, + const dialog = new Gtk.FileChooserDialog({ + title: `Import ${themeOnly ? 'Theme' : 'Config'}`, action: Gtk.FileChooserAction.OPEN, }); - dialog.set_current_folder(`${App.configDir}/themes`) + dialog.set_current_folder(`${App.configDir}/themes`); dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL); dialog.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT); - let response = dialog.run(); + const response = dialog.run(); if (response === Gtk.ResponseType.CANCEL) { dialog.destroy(); return; } if (response === Gtk.ResponseType.ACCEPT) { - let filePath = dialog.get_filename(); - let importedConfig = loadJsonFile(filePath as string); + const filePath: string | null = dialog.get_filename(); + + if (filePath === null) { + Notify({ + summary: 'Failed to import', + body: 'No file selected.', + iconName: icons.ui.warning, + timeout: 5000, + }); + return; + } + + const importedConfig = loadJsonFile(filePath); if (!importedConfig) { dialog.destroy(); @@ -201,26 +212,26 @@ export const importFiles = (themeOnly: boolean = false): void => { } Notify({ - summary: `Importing ${themeOnly ? "Theme" : "Config"}`, + summary: `Importing ${themeOnly ? 'Theme' : 'Config'}`, body: `Importing: ${filePath}`, iconName: icons.ui.info, - timeout: 7000 + timeout: 7000, }); - let tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`); - let optionsConfigFile = Gio.File.new_for_path(OPTIONS); + const tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`); + const optionsConfigFile = Gio.File.new_for_path(OPTIONS); - let [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null); - let [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null); + const [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null); + const [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null); if (!tmpSuccess || !optionsSuccess) { - console.error("Failed to read existing configuration files."); + console.error('Failed to read existing configuration files.'); dialog.destroy(); return; } - let tmpConfig = JSON.parse(new TextDecoder("utf-8").decode(tmpContent)); - let optionsConfig = JSON.parse(new TextDecoder("utf-8").decode(optionsContent)); + let tmpConfig = JSON.parse(new TextDecoder('utf-8').decode(tmpContent)); + let optionsConfig = JSON.parse(new TextDecoder('utf-8').decode(optionsContent)); if (themeOnly) { const filteredConfig = filterConfigForThemeOnly(importedConfig); @@ -236,5 +247,5 @@ export const importFiles = (themeOnly: boolean = false): void => { saveConfigToFile(optionsConfig, OPTIONS); } dialog.destroy(); - bash("pkill ags && ags"); -} + bash('pkill ags && ags'); +}; diff --git a/widget/settings/shared/Header.ts b/widget/settings/shared/Header.ts index 32079f1..ac25e01 100644 --- a/widget/settings/shared/Header.ts +++ b/widget/settings/shared/Header.ts @@ -1,16 +1,18 @@ -export const Header = (headerName: string) => { - return Widget.Box({ - class_name: "options-header", - children: [ - Widget.Label({ - class_name: "label-name", - label: headerName, - }), - Widget.Separator({ - vpack: "center", - hexpand: true, - class_name: "menu-separator", - }), - ], - }); +import { GBox } from 'lib/types/widget'; + +export const Header = (headerName: string): GBox => { + return Widget.Box({ + class_name: 'options-header', + children: [ + Widget.Label({ + class_name: 'label-name', + label: headerName, + }), + Widget.Separator({ + vpack: 'center', + hexpand: true, + class_name: 'menu-separator', + }), + ], + }); }; diff --git a/widget/settings/shared/Inputter.ts b/widget/settings/shared/Inputter.ts index 13c18d9..ed52dc1 100644 --- a/widget/settings/shared/Inputter.ts +++ b/widget/settings/shared/Inputter.ts @@ -1,220 +1,63 @@ -import { Opt } from "lib/option" -import Gdk from "gi://Gdk" -import icons from "lib/icons" -import { RowProps } from "lib/types/options" -import { Variable } from "types/variable"; -import Wallpaper from "services/Wallpaper"; -import { dependencies as checkDependencies } from "lib/utils"; -import options from "options"; -import { importFiles, saveFileDialog } from "./FileChooser"; +import { RowProps } from 'lib/types/options'; +import { Variable } from 'types/variable'; +import { BoxWidget } from 'lib/types/widget'; +import { numberInputter } from './components/number'; +import { objectInputter } from './components/object'; +import { stringInputter } from './components/string'; +import { booleanInputter } from './components/boolean'; +import { imageInputter } from './components/image'; +import { importInputter } from './components/import'; +import { wallpaperInputter } from './components/wallpaper'; +import { colorInputter } from './components/color'; +import { enumInputter } from './components/enum'; +import { fontInputter } from './components/font'; -const EnumSetter = (opt: Opt, values: string[]) => { - const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) }) - const step = (dir: 1 | -1) => { - const i = values.findIndex(i => i === lbl.label) - opt.setValue(dir > 0 - ? i + dir > values.length - 1 ? values[0] : values[i + dir] - : i + dir < 0 ? values[values.length - 1] : values[i + dir], - ) - } - const next = Widget.Button({ - child: Widget.Icon(icons.ui.arrow.right), - on_clicked: () => step(+1), - }) - const prev = Widget.Button({ - child: Widget.Icon(icons.ui.arrow.left), - on_clicked: () => step(-1), - }) - return Widget.Box({ - class_name: "enum-setter", - children: [lbl, prev, next], - }) -} - -export const Inputter = ({ - opt, - type = typeof opt.value as RowProps["type"], - enums, - max = 1000000, - min = 0, - increment = 1, - disabledBinding, - dependencies, - exportData, -}: RowProps, +export const Inputter = ( + { + opt, + type = typeof opt.value as RowProps['type'], + enums, + disabledBinding, + dependencies, + exportData, + min = 0, + max = 1000000, + increment = 1, + }: RowProps, className: string, - isUnsaved: Variable -) => { + isUnsaved: Variable, +): BoxWidget => { return Widget.Box({ - vpack: "center", - class_name: /export|import/.test(type || "") ? "" : "inputter-container", - setup: self => { - + vpack: 'center', + class_name: /export|import/.test(type || '') ? '' : 'inputter-container', + setup: (self) => { switch (type) { - case "number": return self.children = [ - Widget.Box({ - class_name: "unsaved-icon-container", - child: isUnsaved.bind("value").as(unsvd => { - if (unsvd) { - return Widget.Icon({ - class_name: "unsaved-icon", - icon: icons.ui.warning, - tooltipText: "Press 'Enter' to apply your changes." - }) - } - return Widget.Box(); - }), - }), - Widget.SpinButton({ - setup(self) { - self.set_range(min, max) - self.set_increments(1 * increment, 5 * increment) - self.on("value-changed", () => { - opt.value = self.value as T; - }) - self.hook(opt, () => { - self.value = opt.value as number; - isUnsaved.value = Number(self.text) !== opt.value as number; - }) - self.connect("key-release-event", () => { - isUnsaved.value = Number(self.text) !== opt.value as number; - }) - }, - }) - ] - - case "float": - case "object": return self.children = [ - Widget.Box({ - class_name: "unsaved-icon-container", - child: isUnsaved.bind("value").as(unsvd => { - if (unsvd) { - return Widget.Icon({ - class_name: "unsaved-icon", - icon: icons.ui.warning, - tooltipText: "Press 'Enter' to apply your changes." - }) - } - return Widget.Box(); - }), - }), - Widget.Entry({ - class_name: className, - on_change: self => isUnsaved.value = self.text !== JSON.stringify(opt.value), - on_accept: self => opt.value = JSON.parse(self.text || ""), - setup: self => self.hook(opt, () => { - self.text = JSON.stringify(opt.value); - isUnsaved.value = self.text !== JSON.stringify(opt.value); - }) - }) - ] - - - case "string": return self.children = [ - Widget.Box({ - class_name: "unsaved-icon-container", - child: isUnsaved.bind("value").as(unsvd => { - if (unsvd) { - return Widget.Icon({ - class_name: "unsaved-icon", - icon: icons.ui.warning, - tooltipText: "Press 'Enter' to apply your changes." - }) - } - return Widget.Box(); - }), - }), - Widget.Entry({ - class_name: isUnsaved.bind("value").as(unsaved => unsaved ? "unsaved" : ""), - on_change: self => isUnsaved.value = self.text !== opt.value, - on_accept: self => { - opt.value = self.text as T; - }, - setup: self => self.hook(opt, () => { - isUnsaved.value = self.text !== opt.value; - self.text = opt.value as string; - }), - }) - ] - - case "enum": return self.child = EnumSetter(opt as unknown as Opt, enums!) - case "boolean": return self.child = Widget.Switch({ - sensitive: disabledBinding !== undefined ? disabledBinding.bind("value").as(disabled => !disabled) : true, - }) - .on("notify::active", self => { - if (disabledBinding !== undefined && disabledBinding.value) { - return; - } - if (self.active && dependencies !== undefined && !dependencies.every(d => checkDependencies(d))) { - self.active = false; - return; - } - opt.value = self.active as T - }) - .hook(opt, self => { - self.active = opt.value as boolean - }) - - case "img": return self.child = Widget.FileChooserButton({ - class_name: "image-chooser", - on_file_set: ({ uri }) => { opt.value = uri!.replace("file://", "") as T }, - }) - - case "config_import": return self.child = Widget.Box({ - children: [ - Widget.Button({ - class_name: "options-import", - label: "import", - on_clicked: () => { - importFiles(exportData?.themeOnly as boolean); - } - }), - Widget.Button({ - class_name: "options-export", - label: "export", - on_clicked: () => { - saveFileDialog(exportData?.filePath as string, exportData?.themeOnly as boolean); - } - }), - ] - }) - - case "wallpaper": return self.child = Widget.FileChooserButton({ - on_file_set: ({ uri }) => { - opt.value = uri!.replace("file://", "") as T; - if (options.wallpaper.enable.value) { - Wallpaper.set(uri!.replace("file://", "")); - } - }, - }) - - case "font": return self.child = Widget.FontButton({ - show_size: false, - use_size: false, - setup: self => self - .hook(opt, () => self.font = opt.value as string) - .on("font-set", ({ font }) => opt.value = font! - .split(" ").slice(0, -1).join(" ") as T), - }) - - case "color": return self.child = Widget.ColorButton() - .hook(opt, self => { - const rgba = new Gdk.RGBA() - rgba.parse(opt.value as string) - self.rgba = rgba - }) - .on("color-set", ({ rgba: { red, green, blue } }) => { - const hex = (n: number) => { - const c = Math.floor(255 * n).toString(16) - return c.length === 1 ? `0${c}` : c - } - opt.value = `#${hex(red)}${hex(green)}${hex(blue)}` as T - }) - - default: return self.child = Widget.Label({ - label: `no setter with type ${type}`, - }) + case 'number': + return numberInputter(self, opt, min, max, increment, isUnsaved); + case 'float': + case 'object': + return objectInputter(self, opt, isUnsaved, className); + case 'string': + return stringInputter(self, opt, isUnsaved); + case 'enum': + return enumInputter(self, opt, enums!); + case 'boolean': + return booleanInputter(self, opt, disabledBinding, dependencies); + case 'img': + return imageInputter(self, opt); + case 'config_import': + return importInputter(self, exportData); + case 'wallpaper': + return wallpaperInputter(self, opt); + case 'font': + return fontInputter(self, opt); + case 'color': + return colorInputter(self, opt); + default: + return (self.child = Widget.Label({ + label: `No setter with type ${type}`, + })); } - } - }) -} + }, + }); +}; diff --git a/widget/settings/shared/Label.ts b/widget/settings/shared/Label.ts index eb2145c..320ac57 100644 --- a/widget/settings/shared/Label.ts +++ b/widget/settings/shared/Label.ts @@ -1,33 +1,34 @@ -export const Label = (name: string, sub = "", subtitleLink = '') => { - const subTitle = () => { +import { GBox, GButton, GLabel } from 'lib/types/widget'; + +export const Label = (name: string, sub = '', subtitleLink = ''): GBox => { + const subTitle = (): GButton | GLabel => { if (subtitleLink.length) { return Widget.Button({ - hpack: "start", - vpack: "center", - class_name: "options-sublabel-link", + hpack: 'start', + vpack: 'center', + class_name: 'options-sublabel-link', label: sub, - // run a bash command to open the link in the default browswer on_primary_click: () => Utils.execAsync(`bash -c 'xdg-open ${subtitleLink}'`), - }) + }); } return Widget.Label({ - hpack: "start", - vpack: "center", - class_name: "options-sublabel", - label: sub - }) - } + hpack: 'start', + vpack: 'center', + class_name: 'options-sublabel', + label: sub, + }); + }; return Widget.Box({ vertical: true, - hpack: "start", + hpack: 'start', children: [ Widget.Label({ - hpack: "start", - vpack: "center", - class_name: "options-label", - label: name + hpack: 'start', + vpack: 'center', + class_name: 'options-label', + label: name, }), - subTitle() - ] - }) -} + subTitle(), + ], + }); +}; diff --git a/widget/settings/shared/Option.ts b/widget/settings/shared/Option.ts index 2a3abf3..1807469 100644 --- a/widget/settings/shared/Option.ts +++ b/widget/settings/shared/Option.ts @@ -1,29 +1,33 @@ -import { Label } from "./Label"; -import { Inputter } from "./Inputter"; -import icons from "lib/icons"; -import { RowProps } from "lib/types/options"; +import { Label } from './Label'; +import { Inputter } from './Inputter'; +import icons from 'lib/icons'; +import { RowProps } from 'lib/types/options'; +import { GBox } from 'lib/types/widget'; -export const Option = (props: RowProps, className: string = '') => { +export const Option = ( + props: RowProps, + className: string = '', +): GBox => { const isUnsaved = Variable(false); return Widget.Box({ - class_name: "option-item", + class_name: 'option-item', hexpand: true, children: [ Widget.Box({ - hpack: "start", - vpack: "center", + hpack: 'start', + vpack: 'center', hexpand: true, - child: Label(props.title, props.subtitle || "", props.subtitleLink), + child: Label(props.title, props.subtitle || '', props.subtitleLink), }), Inputter(props, className, isUnsaved), Widget.Button({ - vpack: "center", - class_name: "reset-options", + vpack: 'center', + class_name: 'reset-options', child: Widget.Icon(icons.ui.refresh), on_clicked: () => props.opt.reset(), - sensitive: props.opt.bind().as(v => v !== props.opt.initial), + sensitive: props.opt.bind().as((v) => v !== props.opt.initial), }), - ] - }) -} + ], + }); +}; diff --git a/widget/settings/shared/components/boolean.ts b/widget/settings/shared/components/boolean.ts new file mode 100644 index 0000000..cb8255a --- /dev/null +++ b/widget/settings/shared/components/boolean.ts @@ -0,0 +1,29 @@ +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import { Variable } from 'types/variable'; + +import { dependencies as checkDependencies } from 'lib/utils'; + +export const booleanInputter = ( + self: BoxWidget, + opt: Opt, + disabledBinding: Variable | undefined, + dependencies: string[] | undefined, +): Attribute => { + return (self.child = Widget.Switch({ + sensitive: disabledBinding !== undefined ? disabledBinding.bind('value').as((disabled) => !disabled) : true, + }) + .on('notify::active', (self) => { + if (disabledBinding !== undefined && disabledBinding.value) { + return; + } + if (self.active && dependencies !== undefined && !dependencies.every((d) => checkDependencies(d))) { + self.active = false; + return; + } + opt.value = self.active as T; + }) + .hook(opt, (self) => { + self.active = opt.value as boolean; + })); +}; diff --git a/widget/settings/shared/components/color.ts b/widget/settings/shared/components/color.ts new file mode 100644 index 0000000..4212e7f --- /dev/null +++ b/widget/settings/shared/components/color.ts @@ -0,0 +1,19 @@ +import Gdk from 'gi://Gdk'; +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; + +export const colorInputter = (self: BoxWidget, opt: Opt): Attribute => { + return (self.child = Widget.ColorButton() + .hook(opt, (self) => { + const rgba = new Gdk.RGBA(); + rgba.parse(opt.value as string); + self.rgba = rgba; + }) + .on('color-set', ({ rgba: { red, green, blue } }) => { + const hex = (n: number): string => { + const c = Math.floor(255 * n).toString(16); + return c.length === 1 ? `0${c}` : c; + }; + opt.value = `#${hex(red)}${hex(green)}${hex(blue)}` as T; + })); +}; diff --git a/widget/settings/shared/components/enum.ts b/widget/settings/shared/components/enum.ts new file mode 100644 index 0000000..423dba0 --- /dev/null +++ b/widget/settings/shared/components/enum.ts @@ -0,0 +1,36 @@ +import { Opt } from 'lib/option'; +import { BoxWidget } from 'lib/types/widget'; +import icons from 'lib/icons'; +import { Box } from 'types/@girs/gtk-3.0/gtk-3.0.cjs'; + +export const enumInputter = ( + self: BoxWidget, + opt: Opt, + values: T[], +): Box => { + const lbl = Widget.Label({ label: opt.bind().as((v) => `${v}`) }); + const step = (dir: 1 | -1): void => { + const i = values.findIndex((i) => i === lbl.label); + opt.setValue( + dir > 0 + ? i + dir > values.length - 1 + ? values[0] + : values[i + dir] + : i + dir < 0 + ? values[values.length - 1] + : values[i + dir], + ); + }; + const next = Widget.Button({ + child: Widget.Icon(icons.ui.arrow.right), + on_clicked: () => step(+1), + }); + const prev = Widget.Button({ + child: Widget.Icon(icons.ui.arrow.left), + on_clicked: () => step(-1), + }); + return (self.child = Widget.Box({ + class_name: 'enum-setter', + children: [lbl, prev, next], + })); +}; diff --git a/widget/settings/shared/components/font.ts b/widget/settings/shared/components/font.ts new file mode 100644 index 0000000..4428888 --- /dev/null +++ b/widget/settings/shared/components/font.ts @@ -0,0 +1,14 @@ +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget, Child } from 'lib/types/widget'; +import FontButton from 'types/widgets/fontbutton'; + +export const fontInputter = (self: BoxWidget, opt: Opt): FontButton => { + return (self.child = Widget.FontButton({ + show_size: false, + use_size: false, + setup: (self) => + self + .hook(opt, () => (self.font = opt.value as string)) + .on('font-set', ({ font }) => (opt.value = font!.split(' ').slice(0, -1).join(' ') as T)), + })); +}; diff --git a/widget/settings/shared/components/image.ts b/widget/settings/shared/components/image.ts new file mode 100644 index 0000000..94f838a --- /dev/null +++ b/widget/settings/shared/components/image.ts @@ -0,0 +1,11 @@ +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; + +export const imageInputter = (self: BoxWidget, opt: Opt): Attribute => { + return (self.child = Widget.FileChooserButton({ + class_name: 'image-chooser', + on_file_set: ({ uri }) => { + opt.value = uri!.replace('file://', '') as T; + }, + })); +}; diff --git a/widget/settings/shared/components/import.ts b/widget/settings/shared/components/import.ts new file mode 100644 index 0000000..7ab779a --- /dev/null +++ b/widget/settings/shared/components/import.ts @@ -0,0 +1,24 @@ +import { ThemeExportData } from 'lib/types/options'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import { importFiles, saveFileDialog } from '../FileChooser'; + +export const importInputter = (self: BoxWidget, exportData?: ThemeExportData): Attribute => { + return (self.child = Widget.Box({ + children: [ + Widget.Button({ + class_name: 'options-import', + label: 'import', + on_clicked: () => { + importFiles(exportData?.themeOnly as boolean); + }, + }), + Widget.Button({ + class_name: 'options-export', + label: 'export', + on_clicked: () => { + saveFileDialog(exportData?.filePath as string, exportData?.themeOnly as boolean); + }, + }), + ], + })); +}; diff --git a/widget/settings/shared/components/number.ts b/widget/settings/shared/components/number.ts new file mode 100644 index 0000000..893de70 --- /dev/null +++ b/widget/settings/shared/components/number.ts @@ -0,0 +1,45 @@ +import icons from 'lib/icons'; +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import { Variable } from 'types/variable'; + +export const numberInputter = ( + self: BoxWidget, + opt: Opt, + min: number, + max: number, + increment = 1, + isUnsaved: Variable, +): Attribute => { + return (self.children = [ + Widget.Box({ + class_name: 'unsaved-icon-container', + child: isUnsaved.bind('value').as((unsvd) => { + if (unsvd) { + return Widget.Icon({ + class_name: 'unsaved-icon', + icon: icons.ui.warning, + tooltipText: "Press 'Enter' to apply your changes.", + }); + } + return Widget.Box(); + }), + }), + Widget.SpinButton({ + setup(self) { + self.set_range(min, max); + self.set_increments(1 * increment, 5 * increment); + self.on('value-changed', () => { + opt.value = self.value as T; + }); + self.hook(opt, () => { + self.value = opt.value as number; + isUnsaved.value = Number(self.text) !== (opt.value as number); + }); + self.connect('key-release-event', () => { + isUnsaved.value = Number(self.text) !== (opt.value as number); + }); + }, + }), + ]); +}; diff --git a/widget/settings/shared/components/object.ts b/widget/settings/shared/components/object.ts new file mode 100644 index 0000000..b6efeec --- /dev/null +++ b/widget/settings/shared/components/object.ts @@ -0,0 +1,37 @@ +import icons from 'lib/icons'; +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import { Variable } from 'types/variable'; + +export const objectInputter = ( + self: BoxWidget, + opt: Opt, + isUnsaved: Variable, + className: string, +): Attribute => { + return (self.children = [ + Widget.Box({ + class_name: 'unsaved-icon-container', + child: isUnsaved.bind('value').as((unsvd) => { + if (unsvd) { + return Widget.Icon({ + class_name: 'unsaved-icon', + icon: icons.ui.warning, + tooltipText: "Press 'Enter' to apply your changes.", + }); + } + return Widget.Box(); + }), + }), + Widget.Entry({ + class_name: className, + on_change: (self) => (isUnsaved.value = self.text !== JSON.stringify(opt.value)), + on_accept: (self) => (opt.value = JSON.parse(self.text || '')), + setup: (self) => + self.hook(opt, () => { + self.text = JSON.stringify(opt.value); + isUnsaved.value = self.text !== JSON.stringify(opt.value); + }), + }), + ]); +}; diff --git a/widget/settings/shared/components/string.ts b/widget/settings/shared/components/string.ts new file mode 100644 index 0000000..56d18c2 --- /dev/null +++ b/widget/settings/shared/components/string.ts @@ -0,0 +1,34 @@ +import icons from 'lib/icons'; +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import { Variable } from 'types/variable'; + +export const stringInputter = (self: BoxWidget, opt: Opt, isUnsaved: Variable): Attribute => { + return (self.children = [ + Widget.Box({ + class_name: 'unsaved-icon-container', + child: isUnsaved.bind('value').as((unsvd) => { + if (unsvd) { + return Widget.Icon({ + class_name: 'unsaved-icon', + icon: icons.ui.warning, + tooltipText: "Press 'Enter' to apply your changes.", + }); + } + return Widget.Box(); + }), + }), + Widget.Entry({ + class_name: isUnsaved.bind('value').as((unsaved) => (unsaved ? 'unsaved' : '')), + on_change: (self) => (isUnsaved.value = self.text !== opt.value), + on_accept: (self) => { + opt.value = self.text as T; + }, + setup: (self) => + self.hook(opt, () => { + isUnsaved.value = self.text !== opt.value; + self.text = opt.value as string; + }), + }), + ]); +}; diff --git a/widget/settings/shared/components/wallpaper.ts b/widget/settings/shared/components/wallpaper.ts new file mode 100644 index 0000000..5e11165 --- /dev/null +++ b/widget/settings/shared/components/wallpaper.ts @@ -0,0 +1,20 @@ +import { Opt } from 'lib/option'; +import { Attribute, BoxWidget } from 'lib/types/widget'; +import Wallpaper from 'services/Wallpaper'; + +export const wallpaperInputter = ( + self: BoxWidget, + opt: Opt, +): Attribute | void => { + if (typeof opt.value === 'string') { + return (self.child = Widget.FileChooserButton({ + on_file_set: ({ uri }) => { + const newValue: string = uri!.replace('file://', ''); + opt.value = newValue as T; + if (options.wallpaper.enable.value) { + Wallpaper.set(newValue); + } + }, + })); + } +}; diff --git a/widget/settings/side_effects/index.ts b/widget/settings/side_effects/index.ts index 281e05d..6e268c1 100644 --- a/widget/settings/side_effects/index.ts +++ b/widget/settings/side_effects/index.ts @@ -1,25 +1,34 @@ -import options from "options"; +import options from 'options'; -const { show_numbered, show_icons } = options.bar.workspaces; +const { show_numbered, show_icons, showWsIcons } = options.bar.workspaces; const { monochrome: monoBar } = options.theme.bar.buttons; const { monochrome: monoMenu } = options.theme.bar.menus; const { matugen } = options.theme; -show_numbered.connect("changed", ({ value }) => { +show_numbered.connect('changed', ({ value }) => { if (value === true) { show_icons.value = false; + showWsIcons.value = false; } -}) +}); -show_icons.connect("changed", ({ value }) => { +show_icons.connect('changed', ({ value }) => { if (value === true) { show_numbered.value = false; + showWsIcons.value = false; } -}) +}); -matugen.connect("changed", ({ value }) => { +showWsIcons.connect('changed', ({ value }) => { + if (value === true) { + show_numbered.value = false; + show_icons.value = false; + } +}); + +matugen.connect('changed', ({ value }) => { if (value === true) { monoBar.value = false; monoMenu.value = false; } -}) +});