Fix: An issue that would cause Matugen colors to not apply. (#929)

* Eslint updates

* linter fixes

* Type fixes

* More type fixes

* Fix isvis

* More type fixes

* Type Fixes

* Consolidate logic to manage options

* Linter fixes

* Package lock update

* Update configs

* Version checker

* Debug pipeline

* Package lock update

* Update ci

* Strict check

* Revert ci

* Eslint

* Remove rule since it causes issues in CI

* Actual matugen fix
This commit is contained in:
Jas Singh
2025-05-11 23:01:55 -07:00
committed by GitHub
parent 0c82ce9704
commit 2bb1449fb6
275 changed files with 4363 additions and 2505 deletions

View File

@@ -1,25 +0,0 @@
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: ["/*", "!/src"],
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 }],
},
};

72
.eslintrc.json Normal file
View File

@@ -0,0 +1,72 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"tsconfigRootDir": ".",
"sourceType": "module"
},
"plugins": ["@typescript-eslint/eslint-plugin", "prettier"],
"extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
"root": true,
"env": {
"node": true,
"jest": true
},
"ignorePatterns": ["/*", "!/src"],
"rules": {
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/prefer-enum-initializers": "error",
"@typescript-eslint/no-mixed-enums": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "explicit",
"overrides": {
"constructors": "no-public"
}
}
],
"quotes": [
"error",
"single",
{
"allowTemplateLiterals": false,
"avoidEscape": true
}
],
"prettier/prettier": [
"error",
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 110,
"tabWidth": 4
}
],
"no-param-reassign": [
"error",
{ "props": true, "ignorePropertyModificationsFor": ["self", "ref", "el"] }
],
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeLike",
"format": ["PascalCase"]
},
{
"selector": "enumMember",
"format": ["UPPER_CASE"]
},
{
"selector": "memberLike",
"modifiers": ["private"],
"format": null,
"leadingUnderscore": "require"
}
]
}
}

View File

@@ -2,7 +2,7 @@
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"printWidth": 120,
"printWidth": 110,
"tabWidth": 4,
"useTabs": false,
"overrides": [

10
app.ts
View File

@@ -1,10 +1,10 @@
import './src/lib/session';
import './src/scss/style';
import './src/globals/useTheme';
import './src/globals/wallpaper';
import './src/globals/systray';
import './src/globals/dropdown';
import './src/globals/utilities';
import './src/shared/useTheme';
import './src/shared/wallpaper';
import './src/shared/systray';
import './src/shared/dropdown';
import './src/shared/utilities';
import './src/components/bar/utils/sideEffects';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';

1392
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,8 @@
"description": "A customizable panel built for Hyprland.",
"main": "app.ts",
"scripts": {
"lint": "eslint --config .eslintrc.js .",
"lint:fix": "eslint --config .eslintrc.js . --fix",
"lint": "eslint --config .eslintrc.json .",
"lint:fix": "eslint --config .eslintrc.json . --fix",
"format": "prettier --write 'modules/**/*.ts'"
},
"keywords": [],
@@ -24,6 +24,6 @@
"eslint-plugin-prettier": "^5.2.1",
"prettier": "^3.3.3",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.6.2"
"typescript": "5.7.3"
}
}

308
scripts/makeTheme.ts Normal file → Executable file
View File

@@ -21,179 +21,181 @@
* ts-node makeTheme.ts theme.json updatedTheme.json --vivid
*/
const fs = require('fs');
const path = require('path');
(() => {
const fs = require('fs');
const path = require('path');
/**
* Prints the usage/help text.
*/
function printUsage(): void {
console.log(`
Usage:
ts-node makeTheme.ts <input.json> <output.json> [--vivid | --base | --both]
/**
* Prints the usage/help text.
*/
function printUsage(): void {
console.log(`
Usage:
ts-node makeTheme.ts <input.json> <output.json> [--vivid | --base | --both]
Options:
--help Show this help message
Options:
--help Show this help message
Transforms:
--vivid Swap .background with .text (or .total), set .icon = old background
--base Set .icon = .text (or .total), leave .background alone
--both Apply makeVividTheme, then makeBaseTheme
`);
}
/**
* Reads a file as UTF-8 and returns a parsed JSON object.
*/
async function readJson(filePath: string): Promise<Record<string, unknown>> {
const raw = await fs.promises.readFile(path.resolve(filePath), 'utf8');
return JSON.parse(raw) as Record<string, unknown>;
}
/**
* Executes the "vivid" transformation on the input data.
* For each key matching "theme.bar.buttons.*":
* - If it has .background and .text (or .total), swap them.
* - Set .icon to the old background.
*/
function makeVividTheme(data: Record<string, unknown>): void {
data['theme.bar.buttons.style'] = 'default';
const prefix = 'theme.bar.buttons.';
const grouped: Record<string, Record<string, unknown>> = {};
for (const key of Object.keys(data)) {
if (!key.startsWith(prefix)) continue;
// Split: ["theme", "bar", "buttons", ...theRest]
const parts = key.split('.');
const rest = parts.slice(3);
if (rest.length < 2) continue; // need at least <name> and <prop>
// Last segment is the property (e.g. "text", "background")
// The rest form the "name" (e.g. "modules.updates")
const prop = rest.pop()!;
const name = rest.join('.');
if (!grouped[name]) grouped[name] = {};
grouped[name][prop] = data[key];
Transforms:
--vivid Swap .background with .text (or .total), set .icon = old background
--base Set .icon = .text (or .total), leave .background alone
--both Apply makeVividTheme, then makeBaseTheme
`);
}
for (const name of Object.keys(grouped)) {
const props = grouped[name];
const hasBg = Object.prototype.hasOwnProperty.call(props, 'background');
const hasTxt = Object.prototype.hasOwnProperty.call(props, 'text');
const hasTot = Object.prototype.hasOwnProperty.call(props, 'total');
if (!hasBg || (!hasTxt && !hasTot)) continue;
const oldBackground = props['background'];
let textKey: 'text' | 'total' | undefined;
if (hasTxt) textKey = 'text';
else if (hasTot) textKey = 'total';
if (!textKey) continue;
const oldText = props[textKey];
props[textKey] = oldBackground;
props['icon'] = oldBackground;
props['background'] = oldText;
/**
* Reads a file as UTF-8 and returns a parsed JSON object.
*/
async function readJson(filePath: string): Promise<Record<string, unknown>> {
const raw = await fs.promises.readFile(path.resolve(filePath), 'utf8');
return JSON.parse(raw);
}
// Write the changes back to the original data
for (const name of Object.keys(grouped)) {
for (const prop of Object.keys(grouped[name])) {
const fullKey = prefix + name + '.' + prop;
data[fullKey] = grouped[name][prop];
/**
* Executes the "vivid" transformation on the input data.
* For each key matching "theme.bar.buttons.*":
* - If it has .background and .text (or .total), swap them.
* - Set .icon to the old background.
*/
function makeVividTheme(data: Record<string, unknown>): void {
data['theme.bar.buttons.style'] = 'default';
const prefix = 'theme.bar.buttons.';
const grouped: Record<string, Record<string, unknown>> = {};
for (const key of Object.keys(data)) {
if (!key.startsWith(prefix)) continue;
// Split: ["theme", "bar", "buttons", ...theRest]
const parts = key.split('.');
const rest = parts.slice(3);
if (rest.length < 2) continue; // need at least <name> and <prop>
// Last segment is the property (e.g. "text", "background")
// The rest form the "name" (e.g. "modules.updates")
const prop = rest.pop()!;
const name = rest.join('.');
if (!grouped[name]) grouped[name] = {};
grouped[name][prop] = data[key];
}
for (const name of Object.keys(grouped)) {
const props = grouped[name];
const hasBg = Object.prototype.hasOwnProperty.call(props, 'background');
const hasTxt = Object.prototype.hasOwnProperty.call(props, 'text');
const hasTot = Object.prototype.hasOwnProperty.call(props, 'total');
if (!hasBg || (!hasTxt && !hasTot)) continue;
const oldBackground = props['background'];
let textKey: 'text' | 'total' | undefined;
if (hasTxt) textKey = 'text';
else if (hasTot) textKey = 'total';
if (!textKey) continue;
const oldText = props[textKey];
props[textKey] = oldBackground;
props['icon'] = oldBackground;
props['background'] = oldText;
}
// Write the changes back to the original data
for (const name of Object.keys(grouped)) {
for (const prop of Object.keys(grouped[name])) {
const fullKey = prefix + name + '.' + prop;
data[fullKey] = grouped[name][prop];
}
}
}
}
/**
* Executes the "base" transformation on the input data.
* For each key matching "theme.bar.buttons.*":
* - If it has .text or .total, set .icon to that value.
* - Leave .background alone.
*/
function makeBaseTheme(data: Record<string, unknown>): void {
data['theme.bar.buttons.style'] = 'default';
/**
* Executes the "base" transformation on the input data.
* For each key matching "theme.bar.buttons.*":
* - If it has .text or .total, set .icon to that value.
* - Leave .background alone.
*/
function makeBaseTheme(data: Record<string, unknown>): void {
data['theme.bar.buttons.style'] = 'default';
const prefix = 'theme.bar.buttons.';
const grouped: Record<string, Record<string, unknown>> = {};
const prefix = 'theme.bar.buttons.';
const grouped: Record<string, Record<string, unknown>> = {};
for (const key of Object.keys(data)) {
if (!key.startsWith(prefix)) continue;
for (const key of Object.keys(data)) {
if (!key.startsWith(prefix)) continue;
const parts = key.split('.');
const rest = parts.slice(3);
if (rest.length < 2) continue;
const parts = key.split('.');
const rest = parts.slice(3);
if (rest.length < 2) continue;
const prop = rest.pop()!;
const name = rest.join('.');
if (!grouped[name]) grouped[name] = {};
grouped[name][prop] = data[key];
}
const prop = rest.pop()!;
const name = rest.join('.');
if (!grouped[name]) grouped[name] = {};
grouped[name][prop] = data[key];
}
for (const name of Object.keys(grouped)) {
const props = grouped[name];
const hasTxt = Object.prototype.hasOwnProperty.call(props, 'text');
const hasTot = Object.prototype.hasOwnProperty.call(props, 'total');
if (!hasTxt && !hasTot) continue;
for (const name of Object.keys(grouped)) {
const props = grouped[name];
const hasTxt = Object.prototype.hasOwnProperty.call(props, 'text');
const hasTot = Object.prototype.hasOwnProperty.call(props, 'total');
if (!hasTxt && !hasTot) continue;
const value = hasTxt ? props['text'] : props['total'];
props['icon'] = value;
}
const value = hasTxt ? props['text'] : props['total'];
props['icon'] = value;
}
for (const name of Object.keys(grouped)) {
for (const prop of Object.keys(grouped[name])) {
const fullKey = prefix + name + '.' + prop;
data[fullKey] = grouped[name][prop];
for (const name of Object.keys(grouped)) {
for (const prop of Object.keys(grouped[name])) {
const fullKey = prefix + name + '.' + prop;
data[fullKey] = grouped[name][prop];
}
}
}
}
/**
* Main CLI entry point.
*/
async function main(): Promise<void> {
const [, , ...args] = process.argv;
if (args.includes('--help') || args.length < 2) {
printUsage();
process.exit(0);
/**
* Main CLI entry point.
*/
async function main(): Promise<void> {
const [, , ...args] = process.argv;
if (args.includes('--help') || args.length < 2) {
printUsage();
process.exit(0);
}
const input = args[0];
const output = args[1];
const mode = args[2] || '--vivid';
let data: Record<string, unknown>;
try {
data = await readJson(input);
} catch (e) {
console.error(`Failed to read/parse: ${input}`, e);
process.exit(1);
}
if (mode === '--vivid') makeVividTheme(data);
else if (mode === '--base') makeBaseTheme(data);
else if (mode === '--both') {
makeVividTheme(data);
makeBaseTheme(data);
} else {
console.error(`Unknown mode: ${mode}`);
process.exit(1);
}
try {
fs.writeFileSync(path.resolve(output), JSON.stringify(data, null, 2), 'utf8');
console.log(`Wrote updated theme to: ${output}`);
} catch (e) {
console.error(`Failed to write file: ${output}`, e);
process.exit(1);
}
}
const input = args[0];
const output = args[1];
const mode = args[2] || '--vivid';
let data: Record<string, unknown>;
try {
data = await readJson(input);
} catch (e) {
console.error(`Failed to read/parse: ${input}`, e);
process.exit(1);
if (require.main === module) {
main().catch((e) => {
console.error(e);
process.exit(1);
});
}
if (mode === '--vivid') makeVividTheme(data);
else if (mode === '--base') makeBaseTheme(data);
else if (mode === '--both') {
makeVividTheme(data);
makeBaseTheme(data);
} else {
console.error(`Unknown mode: ${mode}`);
process.exit(1);
}
try {
fs.writeFileSync(path.resolve(output), JSON.stringify(data, null, 2), 'utf8');
console.log(`Wrote updated theme to: ${output}`);
} catch (e) {
console.error(`Failed to write file: ${output}`, e);
process.exit(1);
}
}
if (require.main === module) {
main().catch((e) => {
console.error(e);
process.exit(1);
});
}
})();

View File

@@ -1,176 +1,178 @@
#!/usr/bin/env ts-node
/// <reference types="node" />
/**
* Prints usage/help information to the console.
* Called whenever the user passes `--help` or when arguments are missing/invalid.
*/
function printUsage(): void {
console.log(`
Usage:
ts-node updateColors.ts <theme.json> <original_palette.json> <new_palette.json> <output.json>
(() => {
/**
* Prints usage/help information to the console.
* Called whenever the user passes `--help` or when arguments are missing/invalid.
*/
function printUsage(): void {
console.log(`
Usage:
ts-node updateColors.ts <theme.json> <original_palette.json> <new_palette.json> <output.json>
Options:
--help Show this help message
Options:
--help Show this help message
Description:
This script reads a theme JSON file containing old color codes, an "original" palette JSON
(which maps color name → old hex code), and a "new" palette JSON (mapping the same color name
→ new hex code). It then replaces all old hex codes in the theme with the corresponding new hex codes,
and saves the result to the specified output JSON file.
Description:
This script reads a theme JSON file containing old color codes, an "original" palette JSON
(which maps color name → old hex code), and a "new" palette JSON (mapping the same color name
→ new hex code). It then replaces all old hex codes in the theme with the corresponding new hex codes,
and saves the result to the specified output JSON file.
Examples:
ts-node updateColors.ts theme.json original_palette.json new_palette.json updated_theme.json
Examples:
ts-node updateColors.ts theme.json original_palette.json new_palette.json updated_theme.json
node updateColors.js theme.json original_palette.json new_palette.json updated_theme.json
`);
}
node updateColors.js theme.json original_palette.json new_palette.json updated_theme.json
`);
}
/**
* Recursively walks a palette to build a map from old hex → new hex,
* keyed by the same property name in the new palette if it exists.
*
* Example:
* originalPalette = { bg: "#24283b", git: { add: "#449dab" } }
* newPalette = { bg: "#222436", git: { add: "#b8db87" } }
*
* => colorMap = {
* "#24283b": "#222436",
* "#449dab": "#b8db87"
* }
*
* @param original - The "old" palette.
* @param updated - The "new" palette containing updated hex codes.
* @returns A map of { oldHex.toLowerCase(): newHex }.
*/
function buildColorMap(original: Palette, updated: Palette): ColorMap {
const map: ColorMap = {};
type Palette = Record<string, string | Record<string, string>>;
type ColorMap = Record<string, string>;
for (const [key, oldVal] of Object.entries(original)) {
const newVal = updated[key];
/**
* Recursively walks a palette to build a map from old hex → new hex,
* keyed by the same property name in the new palette if it exists.
*
* Example:
* originalPalette = { bg: "#24283b", git: { add: "#449dab" } }
* newPalette = { bg: "#222436", git: { add: "#b8db87" } }
*
* => colorMap = {
* "#24283b": "#222436",
* "#449dab": "#b8db87"
* }
*
* @param original - The "old" palette.
* @param updated - The "new" palette containing updated hex codes.
* @returns A map of { oldHex.toLowerCase(): newHex }.
*/
function buildColorMap(original: Palette, updated: Palette): ColorMap {
const map: ColorMap = {};
if (typeof oldVal === 'string' && typeof newVal === 'string') {
map[oldVal.toLowerCase()] = newVal;
} else if (oldVal && typeof oldVal === 'object') {
const oldNested = oldVal as Record<string, string>;
const newNested = newVal && typeof newVal === 'object' ? (newVal as Record<string, string>) : {};
for (const [key, oldVal] of Object.entries(original)) {
const newVal = updated[key];
for (const [nestedKey, nestedOldHex] of Object.entries(oldNested)) {
const nestedNewHex = newNested[nestedKey];
if (typeof oldVal === 'string' && typeof newVal === 'string') {
map[oldVal.toLowerCase()] = newVal;
} else if (oldVal && typeof oldVal === 'object') {
const oldNested = oldVal as Record<string, string>;
const newNested =
newVal && typeof newVal === 'object' ? (newVal as Record<string, string>) : {};
if (typeof nestedOldHex === 'string' && typeof nestedNewHex === 'string') {
map[nestedOldHex.toLowerCase()] = nestedNewHex;
for (const [nestedKey, nestedOldHex] of Object.entries(oldNested)) {
const nestedNewHex = newNested[nestedKey];
if (typeof nestedOldHex === 'string' && typeof nestedNewHex === 'string') {
map[nestedOldHex.toLowerCase()] = nestedNewHex;
}
}
}
}
return map;
}
return map;
}
/**
* Recursively replace all string values in a data structure (object, array, etc.)
* if they appear in colorMap.
*
* @param node - The current portion of JSON to transform (can be object, array, string, etc.).
* @param colorMap - An object with oldHex.toLowerCase() → newHex mappings.
* @returns The transformed data with replaced colors.
*/
function replaceColorsInTheme(node: unknown, colorMap: ColorMap): unknown {
if (Array.isArray(node)) {
return node.map((item) => replaceColorsInTheme(item, colorMap));
} else if (node && typeof node === 'object') {
const result: Record<string, unknown> = {};
/**
* Recursively replace all string values in a data structure (object, array, etc.)
* if they appear in colorMap.
*
* @param node - The current portion of JSON to transform (can be object, array, string, etc.).
* @param colorMap - An object with oldHex.toLowerCase() → newHex mappings.
* @returns The transformed data with replaced colors.
*/
function replaceColorsInTheme(node: unknown, colorMap: ColorMap): unknown {
if (Array.isArray(node)) {
return node.map((item) => replaceColorsInTheme(item, colorMap));
} else if (node && typeof node === 'object') {
const result: Record<string, unknown> = {};
for (const [key, val] of Object.entries(node)) {
result[key] = replaceColorsInTheme(val, colorMap);
}
return result;
} else if (typeof node === 'string') {
const lower = node.toLowerCase();
return colorMap[lower] ? colorMap[lower] : node;
} else {
return node;
}
}
/**
* A minimal utility wrapper to read a file asynchronously.
*
* @param filePath - The file path to read from.
* @param encoding - The file encoding (default 'utf8').
* @returns A Promise resolving to the file contents as a string.
*/
async function readFile(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
return new Promise((resolve, reject) => {
const fs = require('fs');
fs.readFile(filePath, encoding, (err: Error, data: string) => {
if (err) {
reject(err);
} else {
resolve(data);
for (const [key, val] of Object.entries(node)) {
result[key] = replaceColorsInTheme(val, colorMap);
}
return result;
} else if (typeof node === 'string') {
const lower = node.toLowerCase();
return colorMap[lower] ? colorMap[lower] : node;
} else {
return node;
}
}
/**
* A minimal utility wrapper to read a file asynchronously.
*
* @param filePath - The file path to read from.
* @param encoding - The file encoding (default 'utf8').
* @returns A Promise resolving to the file contents as a string.
*/
async function readFile(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
return new Promise((resolve, reject) => {
const fs = require('fs');
fs.readFile(filePath, encoding, (err: Error, data: string) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
});
}
/**
* Main function that:
* 1. Checks for --help or missing arguments.
* 2. Reads and parses the theme, original palette, and new palette.
* 3. Builds the color map and applies color replacements.
* 4. Writes the result as JSON to a new file.
*
* @example
* ```bash
* ts-node updateColors.ts theme.json original_palette.json new_palette.json output.json
* ```
*/
async function main(): Promise<void> {
const [, , ...args] = process.argv;
if (args.length === 0) {
printUsage();
process.exit(0);
}
if (args.length < 4) {
console.error('Error: Not enough arguments provided.\n');
printUsage();
process.exit(1);
} else if (args.length > 4) {
console.error('Error: Too many arguments provided.\n');
printUsage();
process.exit(1);
/**
* Main function that:
* 1. Checks for --help or missing arguments.
* 2. Reads and parses the theme, original palette, and new palette.
* 3. Builds the color map and applies color replacements.
* 4. Writes the result as JSON to a new file.
*
* @example
* ```bash
* ts-node updateColors.ts theme.json original_palette.json new_palette.json output.json
* ```
*/
async function main(): Promise<void> {
const [, , ...args] = process.argv;
if (args.length === 0) {
printUsage();
process.exit(0);
}
if (args.length < 4) {
console.error('Error: Not enough arguments provided.\n');
printUsage();
process.exit(1);
} else if (args.length > 4) {
console.error('Error: Too many arguments provided.\n');
printUsage();
process.exit(1);
}
const [themePath, originalPath, newPath, outputPath] = args;
try {
const themeData = JSON.parse(await readFile(themePath, 'utf8'));
const originalPalette: Palette = JSON.parse(await readFile(originalPath, 'utf8'));
const newPalette: Palette = JSON.parse(await readFile(newPath, 'utf8'));
const fs = require('fs');
const colorMap = buildColorMap(originalPalette, newPalette);
const updatedTheme = replaceColorsInTheme(themeData, colorMap);
fs.writeFileSync(outputPath, JSON.stringify(updatedTheme, null, 2), 'utf8');
console.log(`Successfully wrote updated theme to: ${outputPath}`);
} catch (err) {
console.error('Error:', err);
process.exit(1);
}
}
const [themePath, originalPath, newPath, outputPath] = args;
try {
const themeData = JSON.parse(await readFile(themePath, 'utf8'));
const originalPalette: Palette = JSON.parse(await readFile(originalPath, 'utf8'));
const newPalette: Palette = JSON.parse(await readFile(newPath, 'utf8'));
const fs = require('fs');
const colorMap = buildColorMap(originalPalette, newPalette);
const updatedTheme = replaceColorsInTheme(themeData, colorMap);
fs.writeFileSync(outputPath, JSON.stringify(updatedTheme, null, 2), 'utf8');
console.log(`Successfully wrote updated theme to: ${outputPath}`);
} catch (err) {
console.error('Error:', err);
process.exit(1);
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}
}
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}
type Palette = Record<string, string | Record<string, string>>;
type ColorMap = Record<string, string>;
})();

View File

@@ -14,7 +14,7 @@ import { Command, ParsedCommand } from './types';
* 5. Validates required arguments.
*/
export class CommandParser {
private registry: CommandRegistry;
private _registry: CommandRegistry;
/**
* Constructs a CommandParser with the provided command registry.
@@ -22,7 +22,7 @@ export class CommandParser {
* @param registry - The command registry containing available commands.
*/
constructor(registry: CommandRegistry) {
this.registry = registry;
this._registry = registry;
}
/**
@@ -33,20 +33,22 @@ export class CommandParser {
* @throws If no command token is found.
* @throws If the command token is not registered.
*/
parse(input: string): ParsedCommand {
const tokens = this.tokenize(input);
public parse(input: string): ParsedCommand {
const tokens = this._tokenize(input);
if (tokens.length === 0) {
throw new Error('No command provided.');
}
const commandName = tokens.shift()!;
const command = this.registry.get(commandName);
const commandName = tokens.shift() ?? 'non-existent-command';
const command = this._registry.get(commandName);
if (!command) {
throw new Error(`Unknown command: "${commandName}". Use "hyprpanel explain" for available commands.`);
throw new Error(
`Unknown command: "${commandName}". Use "hyprpanel explain" for available commands.`,
);
}
const args = this.parseArgs(command, tokens);
const args = this._parseArgs(command, tokens);
return { command, args };
}
@@ -56,10 +58,10 @@ export class CommandParser {
* @param input - The raw input string to break into tokens.
* @returns An array of tokens.
*/
private tokenize(input: string): string[] {
private _tokenize(input: string): string[] {
const regex = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g;
const matches = input.match(regex);
return matches ? matches.map((token) => this.stripQuotes(token)) : [];
return matches ? matches.map((token) => this._stripQuotes(token)) : [];
}
/**
@@ -68,7 +70,7 @@ export class CommandParser {
* @param str - The token from which to strip leading or trailing quotes.
* @returns The token without its outer quotes.
*/
private stripQuotes(str: string): string {
private _stripQuotes(str: string): string {
return str.replace(/^["'](.+(?=["']$))["']$/, '$1');
}
@@ -81,13 +83,13 @@ export class CommandParser {
* @throws If required arguments are missing.
* @throws If there are too many tokens for the command definition.
*/
private parseArgs(command: Command, tokens: string[]): Record<string, unknown> {
private _parseArgs(command: Command, tokens: string[]): Record<string, unknown> {
const args: Record<string, unknown> = {};
let currentIndex = 0;
for (const argDef of command.args) {
if (currentIndex >= tokens.length) {
if (argDef.required) {
if (argDef.required === true) {
throw new Error(`Missing required argument: "${argDef.name}".`);
}
if (argDef.default !== undefined) {
@@ -97,13 +99,13 @@ export class CommandParser {
}
if (argDef.type === 'object') {
const { objectValue, nextIndex } = this.parseObjectTokens(tokens, currentIndex);
const { objectValue, nextIndex } = this._parseObjectTokens(tokens, currentIndex);
args[argDef.name] = objectValue;
currentIndex = nextIndex;
} else {
const value = tokens[currentIndex];
currentIndex++;
args[argDef.name] = this.convertType(value, argDef.type);
args[argDef.name] = this._convertType(value, argDef.type);
}
}
@@ -125,7 +127,10 @@ export class CommandParser {
* @returns An object containing the parsed JSON object and the next token index.
* @throws If the reconstructed JSON is invalid.
*/
private parseObjectTokens(tokens: string[], startIndex: number): { objectValue: unknown; nextIndex: number } {
private _parseObjectTokens(
tokens: string[],
startIndex: number,
): { objectValue: unknown; nextIndex: number } {
let braceCount = 0;
let started = false;
const objectTokens: string[] = [];
@@ -142,7 +147,6 @@ export class CommandParser {
objectTokens.push(token);
// Once we've started and braceCount returns to 0, we assume the object is complete
if (started && braceCount === 0) break;
if (token.includes('{')) started = true;
}
@@ -166,7 +170,7 @@ export class CommandParser {
* @returns The converted value.
* @throws If the token cannot be converted to the expected type.
*/
private convertType(value: string, type: 'string' | 'number' | 'boolean' | 'object'): unknown {
private _convertType(value: string, type: 'string' | 'number' | 'boolean' | 'object'): unknown {
switch (type) {
case 'number': {
const num = Number(value);

View File

@@ -6,7 +6,7 @@ import { Command } from './types';
* and retrieval of all commands for listing and help functionalities.
*/
export class CommandRegistry {
private commands: Map<string, Command> = new Map();
private _commands: Map<string, Command> = new Map();
/**
* Registers a command. If a command with the same name or alias already exists,
@@ -15,18 +15,18 @@ export class CommandRegistry {
* @param command - The command to register.
* @throws If a command with the same name or alias already exists.
*/
register(command: Command): void {
if (this.commands.has(command.name)) {
public register(command: Command): void {
if (this._commands.has(command.name)) {
throw new Error(`Command "${command.name}" is already registered.`);
}
this.commands.set(command.name, command);
this._commands.set(command.name, command);
if (command.aliases) {
for (const alias of command.aliases) {
if (this.commands.has(alias)) {
if (this._commands.has(alias)) {
throw new Error(`Alias "${alias}" is already in use.`);
}
this.commands.set(alias, command);
this._commands.set(alias, command);
}
}
}
@@ -37,8 +37,8 @@ export class CommandRegistry {
* @param commandName - The name or alias of the command to retrieve.
* @returns The command if found, otherwise undefined.
*/
get(commandName: string): Command | undefined {
return this.commands.get(commandName);
public get(commandName: string): Command | undefined {
return this._commands.get(commandName);
}
/**
@@ -46,8 +46,8 @@ export class CommandRegistry {
*
* @returns An array of all registered commands.
*/
getAll(): Command[] {
const unique = new Set<Command>(this.commands.values());
public getAll(): Command[] {
const unique = new Set<Command>(this._commands.values());
return Array.from(unique);
}
}

View File

@@ -8,7 +8,7 @@ import { ResponseCallback } from './types';
* 3. Handles any errors and passes the result back via the response callback.
*/
export class RequestHandler {
private parser: CommandParser;
private _parser: CommandParser;
/**
* Creates an instance of RequestHandler.
@@ -16,7 +16,7 @@ export class RequestHandler {
* @param parser - The CommandParser instance to use.
*/
constructor(parser: CommandParser) {
this.parser = parser;
this._parser = parser;
}
/**
@@ -26,20 +26,20 @@ export class RequestHandler {
* @param response - The callback to handle the response.
* @returns A promise that resolves when the request is handled.
*/
async initializeRequestHandler(input: string, response: ResponseCallback): Promise<void> {
public async initializeRequestHandler(input: string, response: ResponseCallback): Promise<void> {
try {
const parsed = this.parser.parse(input);
const parsed = this._parser.parse(input);
const { command, args } = parsed;
const result = command.handler(args);
if (result instanceof Promise) {
const resolved = await result;
response(this.formatOutput(resolved));
response(this._formatOutput(resolved));
} else {
response(this.formatOutput(result));
response(this._formatOutput(result));
}
} catch (error) {
response(this.formatError(error));
response(this._formatError(error));
}
}
@@ -49,7 +49,7 @@ export class RequestHandler {
* @param output - The output to format.
* @returns A string representation of the output.
*/
private formatOutput(output: unknown): string {
private _formatOutput(output: unknown): string {
if (typeof output === 'string') {
return output;
} else if (typeof output === 'number' || typeof output === 'boolean') {
@@ -71,7 +71,7 @@ export class RequestHandler {
* @param error - The error to format.
* @returns A string representation of the error.
*/
private formatError(error: unknown): string {
private _formatError(error: unknown): string {
if (error instanceof Error) {
return `Error: ${error.message}`;
} else if (typeof error === 'string') {

View File

@@ -1,6 +1,9 @@
import { errorHandler } from 'src/lib/utils';
import { BarLayouts } from 'src/lib/types/options';
import { Command } from '../../types';
import { setWallpaper } from 'src/shared/wallpaper';
import { useTheme } from 'src/shared/useTheme';
import { setLayout } from 'src/shared/utilities';
import { BarLayouts } from 'src/lib/options/options.types';
export const appearanceCommands: Command[] = [
{
@@ -58,7 +61,8 @@ export const appearanceCommands: Command[] = [
args: [
{
name: 'layout',
description: 'Bar layout to apply. Wiki: https://hyprpanel.com/configuration/panel.html#layouts',
description:
'Bar layout to apply. Wiki: https://hyprpanel.com/configuration/panel.html#layouts',
type: 'object',
required: true,
},

View File

@@ -110,9 +110,9 @@ function checkServiceStatus(services: string[]): ServiceStatus {
const enabledResult = runCommand(`systemctl is-enabled ${svc}`);
const enabledStatus = enabledResult.stdout;
if (enabledResult && (enabledStatus === 'enabled' || enabledStatus === 'static')) {
if (enabledResult !== undefined && (enabledStatus === 'enabled' || enabledStatus === 'static')) {
return 'INSTALLED';
} else if (enabledResult && enabledStatus === 'disabled') {
} else if (enabledResult !== undefined && enabledStatus === 'disabled') {
return 'DISABLED';
} else {
return 'MISSING';
@@ -174,7 +174,7 @@ function getDependencyStatus(dep: Dependency): string {
break;
}
if (!dep.description) {
if (dep.description === undefined) {
return ` ${colorText(textStatus, color)} ${dep.package}`;
}

View File

@@ -3,6 +3,10 @@ import { errorHandler } from 'src/lib/utils';
import { Command } from '../../types';
import { execAsync, Gio, GLib } from 'astal';
import { checkDependencies } from './checkDependencies';
import options from 'src/options';
import { clearAllNotifications } from 'src/shared/notification';
import { getSystrayItems } from 'src/shared/systray';
import { idleInhibit } from 'src/shared/utilities';
const audio = AstalWp.get_default();
@@ -15,7 +19,7 @@ export const utilityCommands: Command[] = [
args: [],
handler: (): string => {
try {
return getSystrayItems();
return getSystrayItems() ?? 'No items found!';
} catch (error) {
errorHandler(error);
}
@@ -88,7 +92,8 @@ export const utilityCommands: Command[] = [
{
name: 'idleInhibit',
aliases: ['idi'],
description: 'Enables/Disables the Idle Inhibitor. Toggles the Inhibitor if no parameter is provided.',
description:
'Enables/Disables the Idle Inhibitor. Toggles the Inhibitor if no parameter is provided.',
category: 'Utility',
args: [
{
@@ -100,7 +105,7 @@ export const utilityCommands: Command[] = [
],
handler: (args: Record<string, unknown>): boolean => {
try {
const shouldInhibit = args['shouldInhibit'] ?? !idleInhibit.get();
const shouldInhibit = args['shouldInhibit'] ?? idleInhibit.get() === false;
idleInhibit.set(Boolean(shouldInhibit));
return idleInhibit.get();

View File

@@ -2,6 +2,7 @@ import { errorHandler } from 'src/lib/utils';
import { Command } from '../../types';
import { App } from 'astal/gtk3';
import { BarVisibility } from 'src/cli/utils/BarVisibility';
import { isWindowVisible } from 'src/shared/utilities';
export const windowManagementCommands: Command[] = [
{

View File

@@ -58,7 +58,7 @@ export function createExplainCommand(registry: CommandRegistry): Command {
handler: (args: Record<string, unknown>): string => {
const commandName = args['commandName'] as string | undefined;
if (commandName) {
if (commandName !== undefined) {
return formatCommandExplain(registry, commandName);
}
@@ -134,7 +134,7 @@ function organizeCommandsByCategory(commands: Command[]): CategoryMap {
const categoryMap: CategoryMap = {};
commands.forEach((cmd) => {
if (!categoryMap[cmd.category]) {
if (categoryMap[cmd.category] === undefined) {
categoryMap[cmd.category] = [];
}
categoryMap[cmd.category].push(cmd);
@@ -183,7 +183,8 @@ function formatArguments(args: PositionalArg[]): string {
return (
args
.map((arg) => {
const requirement = arg.required ? `${ANSI_FG_RED}(required)` : `${ANSI_FG_CYAN}(optional)`;
const requirement =
arg.required === true ? `${ANSI_FG_RED}(required)` : `${ANSI_FG_CYAN}(optional)`;
const defaultValue =
arg.default !== undefined
? ` ${ANSI_FG_MAGENTA}[default: ${JSON.stringify(arg.default)}]${ANSI_RESET}`

View File

@@ -1,4 +1,4 @@
import { BarToggleStates } from 'src/lib/types/cli';
import { BarToggleStates } from 'src/lib/types/cli.types';
export class BarVisibility {
private static _toggleStates: BarToggleStates = {};

View File

@@ -23,7 +23,7 @@ export class CustomModules {
return customModuleComponents;
} catch (error) {
console.log(`Failed to build custom modules in ${CONFIG_DIR}: ${error}`);
console.error(`Failed to build custom modules in ${CONFIG_DIR}: ${error}`);
throw new Error(`Failed to build custom modules in ${CONFIG_DIR}: ${error}`);
}
}

View File

@@ -48,7 +48,11 @@ export function getIcon(moduleName: string, commandOutput: string, moduleIcon: C
* - No matching icon is found for the alt text
* - Corresponding icon value is not a string
*/
function getIconFromObject(moduleName: string, commandOutput: string, iconObject: Record<string, unknown>): string {
function getIconFromObject(
moduleName: string,
commandOutput: string,
iconObject: Record<string, unknown>,
): string {
try {
const commandResults: CommandResults = parseCommandOutputJson(moduleName, commandOutput);
@@ -115,7 +119,7 @@ function getIconFromArray(moduleName: string, commandOutput: string, iconArray:
const iconForStep = iconArray.find((_, index) => resultsPercentage <= step * (index + 1));
return iconForStep || ERROR_ICON;
return iconForStep ?? ERROR_ICON;
} catch {
return ERROR_ICON;
}

View File

@@ -29,7 +29,10 @@ export function getLabel(moduleName: string, commandOutput: string, labelConfig:
* @param commandOutput - The processed command output (either a string or object)
* @returns The extracted value as a string, or empty string if not found
*/
function getValueForTemplateVariable(templatePath: string, commandOutput: string | Record<string, unknown>): string {
function getValueForTemplateVariable(
templatePath: string,
commandOutput: string | Record<string, unknown>,
): string {
if (typeof commandOutput === 'string') {
return getTemplateValueForStringOutput(templatePath, commandOutput);
}
@@ -62,7 +65,10 @@ function getTemplateValueForStringOutput(templatePath: string, commandOutput: st
* @param commandOutput - The object representing parsed command output
* @returns The extracted value as a string, or empty string if path is invalid or value is not primitive
*/
function getTemplateValueForObjectOutput(templatePath: string, commandOutput: Record<string, unknown>): string {
function getTemplateValueForObjectOutput(
templatePath: string,
commandOutput: Record<string, unknown>,
): string {
const pathParts = templatePath.split('.');
function isRecord(value: unknown): value is Record<string, unknown> {

View File

@@ -1,4 +1,3 @@
import { BarBoxChild } from 'src/lib/types/bar.js';
import { CustomBarModule } from '../types';
import { Module } from '../../shared/Module';
import { Astal } from 'astal/gtk3';
@@ -6,6 +5,7 @@ import { bind, Variable } from 'astal';
import { getIcon } from './helpers/icon';
import { getLabel } from './helpers/label';
import { initActionListener, initCommandPoller, setupModuleInteractions } from './setup';
import { BarBoxChild } from 'src/lib/types/bar.types';
export const ModuleContainer = (moduleName: string, moduleMetadata: CustomBarModule): BarBoxChild => {
const {

View File

@@ -37,6 +37,7 @@ import { bind, Variable } from 'astal';
import { getLayoutForMonitor, isLayoutEmpty } from './utils/monitors';
import { GdkMonitorMapper } from './utils/GdkMonitorMapper';
import { CustomModules } from './custom_modules/CustomModules';
import { idleInhibit } from 'src/shared/utilities';
const { layouts } = options.bar;
const { location } = options.theme.bar;
@@ -82,7 +83,7 @@ export const Bar = async (monitor: number): Promise<JSX.Element> => {
...customWidgets,
};
} catch (error) {
console.log(error);
console.error(error);
}
const hyprlandMonitor = gdkMonitorMapper.mapGdkToHyprland(monitor);
@@ -93,7 +94,7 @@ export const Bar = async (monitor: number): Promise<JSX.Element> => {
const computeClassName = bind(layouts).as(() => {
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.get());
return !isLayoutEmpty(foundLayout) ? `bar` : '';
return !isLayoutEmpty(foundLayout) ? 'bar' : '';
});
const computeAnchor = bind(location).as((loc) => {
@@ -104,19 +105,22 @@ export const Bar = async (monitor: number): Promise<JSX.Element> => {
return Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
});
const computeLayer = Variable.derive([bind(options.theme.bar.layer), bind(options.tear)], (barLayer, tear) => {
if (tear && barLayer === 'overlay') {
return Astal.Layer.TOP;
}
const layerMap = {
overlay: Astal.Layer.OVERLAY,
top: Astal.Layer.TOP,
bottom: Astal.Layer.BOTTOM,
background: Astal.Layer.BACKGROUND,
};
const computeLayer = Variable.derive(
[bind(options.theme.bar.layer), bind(options.tear)],
(barLayer, tear) => {
if (tear && barLayer === 'overlay') {
return Astal.Layer.TOP;
}
const layerMap = {
overlay: Astal.Layer.OVERLAY,
top: Astal.Layer.TOP,
bottom: Astal.Layer.BOTTOM,
background: Astal.Layer.BACKGROUND,
};
return layerMap[barLayer];
});
return layerMap[barLayer];
},
);
const computeBorderLocation = bind(borderLocation).as((brdrLcn) =>
brdrLcn !== 'none' ? 'bar-panel withBorder' : 'bar-panel',

View File

@@ -1,4 +1,4 @@
import { BatteryIconKeys, BatteryIcons } from 'src/lib/types/battery';
import { BatteryIconKeys, BatteryIcons } from 'src/lib/types/battery.types';
const batteryIcons: BatteryIcons = {
0: '󰂎',

View File

@@ -2,15 +2,22 @@ import AstalBattery from 'gi://AstalBattery?version=0.1';
import { Astal } from 'astal/gtk3';
import { openMenu } from '../../utils/menu';
import options from 'src/options';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers.js';
import Variable from 'astal/variable';
import { bind } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { getBatteryIcon } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar.types';
const batteryService = AstalBattery.get_default();
const { label: show_label, rightClick, middleClick, scrollUp, scrollDown, hideLabelWhenFull } = options.bar.battery;
const {
label: show_label,
rightClick,
middleClick,
scrollUp,
scrollDown,
hideLabelWhenFull,
} = options.bar.battery;
const BatteryLabel = (): BarBoxChild => {
const batIcon = Variable.derive(
@@ -55,10 +62,18 @@ const BatteryLabel = (): BarBoxChild => {
);
const componentTooltip = Variable.derive(
[bind(batteryService, 'charging'), bind(batteryService, 'timeToFull'), bind(batteryService, 'timeToEmpty')],
[
bind(batteryService, 'charging'),
bind(batteryService, 'timeToFull'),
bind(batteryService, 'timeToEmpty'),
],
(isCharging, timeToFull, timeToEmpty) => {
const timeRemaining = isCharging ? timeToFull : timeToEmpty;
return generateTooltip(timeRemaining, isCharging, Math.floor(batteryService.percentage * 100) === 100);
return generateTooltip(
timeRemaining,
isCharging,
Math.floor(batteryService.percentage * 100) === 100,
);
},
);
@@ -68,7 +83,9 @@ const BatteryLabel = (): BarBoxChild => {
const isCharged = Math.round(percentage) === 100;
const icon = <label className={'bar-button-icon battery txt-icon'} label={batIcon()} />;
const label = <label className={'bar-button-label battery'} label={`${Math.floor(percentage * 100)}%`} />;
const label = (
<label className={'bar-button-label battery'} label={`${Math.floor(percentage * 100)}%`} />
);
const children = [icon];
@@ -135,7 +152,9 @@ const BatteryLabel = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,11 +1,11 @@
import options from 'src/options.js';
import { openMenu } from '../../utils/menu.js';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers.js';
import { Variable, bind } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers.js';
import AstalBluetooth from 'gi://AstalBluetooth?version=0.1';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types.js';
const bluetoothService = AstalBluetooth.get_default();
@@ -20,7 +20,11 @@ const Bluetooth = (): BarBoxChild => {
const connectDevices = devices.filter((device) => device.connected);
const label =
isPowered && connectDevices.length ? ` Connected (${connectDevices.length})` : isPowered ? 'On' : 'Off';
isPowered && connectDevices.length
? ` Connected (${connectDevices.length})`
: isPowered
? 'On'
: 'Off';
return <label label={label} className={'bar-button-label bluetooth'} />;
};
@@ -102,7 +106,9 @@ const Bluetooth = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,11 +1,11 @@
import { Variable, bind } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar';
import { Module } from '../../shared/Module';
import { inputHandler } from '../../utils/helpers';
import options from 'src/options';
import { initSettingsTracker, initVisibilityTracker } from './helpers';
import AstalCava from 'gi://AstalCava?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types';
const {
icon,
@@ -46,7 +46,7 @@ export const Cava = (): BarBoxChild => {
}
return Module({
isVis,
isVis: bind(isVis),
label: labelBinding(),
showIconBinding: bind(label),
textIcon: bind(icon),

View File

@@ -1,20 +1,24 @@
import { openMenu } from '../../utils/menu';
import options from 'src/options';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers.js';
import { bind, Variable } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { Astal } from 'astal/gtk3';
import { systemTime } from 'src/globals/time';
import { systemTime } from 'src/shared/time';
import { BarBoxChild } from 'src/lib/types/bar.types';
const { format, icon, showIcon, showTime, rightClick, middleClick, scrollUp, scrollDown } = options.bar.clock;
const { style } = options.theme.bar.buttons;
const time = Variable.derive([systemTime, format], (c, f) => c.format(f) || '');
const time = Variable.derive([systemTime, format], (c, f) => c.format(f) ?? '');
const Clock = (): BarBoxChild => {
const ClockTime = (): JSX.Element => <label className={'bar-button-label clock bar'} label={bind(time)} />;
const ClockIcon = (): JSX.Element => <label className={'bar-button-icon clock txt-icon bar'} label={bind(icon)} />;
const ClockTime = (): JSX.Element => (
<label className={'bar-button-label clock bar'} label={bind(time)} />
);
const ClockIcon = (): JSX.Element => (
<label className={'bar-button-icon clock txt-icon bar'} label={bind(icon)} />
);
const componentClassName = Variable.derive(
[bind(style), bind(showIcon), bind(showTime)],
@@ -95,7 +99,9 @@ const Clock = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -15,7 +15,6 @@ export const computeCPU = (): number => {
const currentCpuData = new GTop.glibtop_cpu();
GTop.glibtop_get_cpu(currentCpuData);
// Calculate the differences from the previous to current data
const totalDiff = currentCpuData.total - previousCpuData.total;
const idleDiff = currentCpuData.idle - previousCpuData.idle;

View File

@@ -2,10 +2,10 @@ import { Module } from '../../shared/Module';
import options from 'src/options';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { computeCPU } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown, pollingInterval, icon } =
options.bar.customModules.cpu;

View File

@@ -1,8 +1,8 @@
import { Variable } from 'astal';
import GLib from 'gi://GLib?version=2.0';
import { convertCelsiusToFahrenheit } from 'src/globals/weather';
import { UnitType } from 'src/lib/types/weather';
import { convertCelsiusToFahrenheit } from 'src/shared/weather';
import options from 'src/options';
import { UnitType } from 'src/lib/types/weather.types';
const { sensor } = options.bar.customModules.cpuTemp;
/**
@@ -25,7 +25,7 @@ export const getCPUTemperature = (round: Variable<boolean>, unit: Variable<UnitT
const [success, tempInfoBytes] = GLib.file_get_contents(sensor.get());
const tempInfo = new TextDecoder('utf-8').decode(tempInfoBytes);
if (!success || !tempInfoBytes) {
if (!success || tempInfoBytes === null) {
console.error(`Failed to read ${sensor.get()} or file content is null.`);
return 0;
}

View File

@@ -2,11 +2,11 @@ import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { getCPUTemperature } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { UnitType } from 'src/lib/types/weather';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { UnitType } from 'src/lib/types/weather.types';
import { BarBoxChild } from 'src/lib/types/bar.types';
const {
label,

View File

@@ -3,31 +3,37 @@ import { Module } from '../../shared/Module';
import { inputHandler } from '../../utils/helpers';
import Variable from 'astal/variable';
import { bind } from 'astal';
import { BarBoxChild } from 'src/lib/types/bar';
import { Astal } from 'astal/gtk3';
import { idleInhibit } from 'src/shared/utilities';
import { BarBoxChild } from 'src/lib/types/bar.types';
const { label, onIcon, offIcon, onLabel, offLabel, rightClick, middleClick, scrollUp, scrollDown } =
options.bar.customModules.hypridle;
function toggleInhibit(): void {
idleInhibit.set(!idleInhibit.get());
idleInhibit.set(idleInhibit.get() === false);
}
export const Hypridle = (): BarBoxChild => {
const iconBinding = Variable.derive([bind(idleInhibit), bind(onIcon), bind(offIcon)], (active, onIcn, offIcn) => {
return active ? onIcn : offIcn;
});
const iconBinding = Variable.derive(
[bind(idleInhibit), bind(onIcon), bind(offIcon)],
(active, onIcn, offIcn) => {
return active === true ? onIcn : offIcn;
},
);
const labelBinding = Variable.derive(
[bind(idleInhibit), bind(onLabel), bind(offLabel)],
(active, onLbl, offLbl) => {
return active ? onLbl : offLbl;
return active === true ? onLbl : offLbl;
},
);
const hypridleModule = Module({
textIcon: iconBinding(),
tooltipText: bind(idleInhibit).as((active) => `Idle Inhibitor: ${active ? 'Enabled' : 'Disabled'}`),
tooltipText: bind(idleInhibit).as(
(active) => `Idle Inhibitor: ${active === true ? 'Enabled' : 'Disabled'}`,
),
boxClass: 'hypridle',
label: labelBinding(),
showLabelBinding: bind(label),

View File

@@ -9,7 +9,7 @@ const { temperature } = options.bar.customModules.hyprsunset;
* This command checks if the hyprsunset process is currently running by using the `pgrep` command.
* It returns 'yes' if the process is found and 'no' otherwise.
*/
export const isActiveCommand = `bash -c "pgrep -x 'hyprsunset' > /dev/null && echo 'yes' || echo 'no'"`;
export const isActiveCommand = "bash -c \"pgrep -x 'hyprsunset' > /dev/null && echo 'yes' || echo 'no'\"";
/**
* A variable to track the active state of the hyprsunset process.
@@ -33,7 +33,7 @@ export const toggleSunset = (isActive: Variable<boolean>): void => {
});
});
} else {
execAsync(`bash -c "pkill hyprsunset "`).then(() => {
execAsync('bash -c "pkill hyprsunset "').then(() => {
execAsync(isActiveCommand).then((res) => {
isActive.set(res === 'yes');
});

View File

@@ -1,11 +1,11 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler, throttleInput } from 'src/components/bar/utils/helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { checkSunsetStatus, isActive, toggleSunset } from './helpers';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const {
label,
@@ -25,24 +25,35 @@ const dummyVar = Variable(undefined);
checkSunsetStatus();
const sunsetPoller = new FunctionPoller<undefined, []>(dummyVar, [], bind(pollingInterval), checkSunsetStatus);
const sunsetPoller = new FunctionPoller<undefined, []>(
dummyVar,
[],
bind(pollingInterval),
checkSunsetStatus,
);
sunsetPoller.initialize('hyprsunset');
const throttledToggleSunset = throttleInput(() => toggleSunset(isActive), 1000);
export const Hyprsunset = (): BarBoxChild => {
const iconBinding = Variable.derive([bind(isActive), bind(onIcon), bind(offIcon)], (active, onIcn, offIcn) => {
return active ? onIcn : offIcn;
});
const iconBinding = Variable.derive(
[bind(isActive), bind(onIcon), bind(offIcon)],
(active, onIcn, offIcn) => {
return active ? onIcn : offIcn;
},
);
const tooltipBinding = Variable.derive([isActive, temperature], (active, temp) => {
return `Hyprsunset ${active ? 'enabled' : 'disabled'}\nTemperature: ${temp}`;
});
const labelBinding = Variable.derive([bind(isActive), bind(onLabel), bind(offLabel)], (active, onLbl, offLbl) => {
return active ? onLbl : offLbl;
});
const labelBinding = Variable.derive(
[bind(isActive), bind(onLabel), bind(offLabel)],
(active, onLbl, offLbl) => {
return active ? onLbl : offLbl;
},
);
const hyprsunsetModule = Module({
textIcon: iconBinding(),

View File

@@ -2,10 +2,8 @@ import {
HyprctlDeviceLayout,
HyprctlKeyboard,
KbLabelType,
LayoutKeys,
LayoutValues,
} from 'src/lib/types/customModules/kbLayout';
import { layoutMap } from './layouts';
} from 'src/lib/types/customModules/kbLayout.types.js';
import { LayoutKeys, layoutMap, LayoutValues } from './layouts';
/**
* Retrieves the keyboard layout from a given JSON string and format.
@@ -32,8 +30,21 @@ export const getKeyboardLayout = (layoutData: string, format: KbLabelType): Layo
mainKb = keyboards[keyboards.length - 1];
}
const layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys;
if (!isValidLayout(mainKb.active_keymap)) {
return layoutMap['Unknown Layout'];
}
const layout: LayoutKeys = mainKb.active_keymap;
const foundLayout: LayoutValues = layoutMap[layout];
return format === 'code' ? foundLayout || layout : layout;
return format === 'code' ? (foundLayout ?? layout) : layout;
};
function isValidLayout(kbLayout: string): kbLayout is LayoutKeys {
if (!Object.keys(layoutMap).includes(kbLayout)) {
return false;
}
return true;
}

View File

@@ -1,6 +1,5 @@
import { LayoutKeys, LayoutValues } from 'src/lib/types/customModules/kbLayout';
export const layoutMap: Record<LayoutKeys, LayoutValues> = {
// Create a const object with all layouts
const layoutMapObj = {
'Abkhazian (Russia)': 'RU (Ab)',
Akan: 'GH (Akan)',
Albanian: 'AL',
@@ -585,3 +584,8 @@ export const layoutMap: Record<LayoutKeys, LayoutValues> = {
Yoruba: 'NG (Yoruba)',
'Unknown Layout': 'Unknown',
} as const;
export type LayoutKeys = keyof typeof layoutMapObj;
export type LayoutValues = (typeof layoutMapObj)[LayoutKeys];
export const layoutMap = layoutMapObj;

View File

@@ -2,11 +2,11 @@ import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { getKeyboardLayout } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { bind } from 'astal';
import { useHook } from 'src/lib/shared/hookHandler';
import { Astal } from 'astal/gtk3';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types';
const hyprlandService = AstalHyprland.get_default();
const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } =

View File

@@ -1,7 +1,7 @@
import { MediaTags } from 'src/lib/types/audio.js';
import { Opt } from 'src/lib/option';
import AstalMpris from 'gi://AstalMpris?version=0.1';
import { Variable } from 'astal';
import { MediaTags } from 'src/lib/types/audio.types';
import { Opt } from 'src/lib/options';
/**
* Retrieves the icon for a given media player.
@@ -82,8 +82,8 @@ export const generateMediaLabel = (
const currentPlayer = activePlayer.get();
if (!currentPlayer || !show_label.get()) {
songIcon.set(getIconForPlayer(activePlayer.get()?.identity || ''));
return `Media`;
songIcon.set(getIconForPlayer(activePlayer.get()?.identity ?? ''));
return 'Media';
}
const { title, identity, artist, album, busName } = currentPlayer;
@@ -107,7 +107,7 @@ export const generateMediaLabel = (
return '';
}
const value = p1 !== undefined ? mediaTags[p1] : '';
const suffix = p2?.length ? p2.slice(1) : '';
const suffix = p2 !== undefined && p2.length > 0 ? p2.slice(1) : '';
return value ? value + suffix : '';
},
);

View File

@@ -4,10 +4,10 @@ import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/util
import { generateMediaLabel } from './helpers/index.js';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers.js';
import { bind, Variable } from 'astal';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { Astal } from 'astal/gtk3';
import { activePlayer, mediaAlbum, mediaArtist, mediaTitle } from 'src/globals/media.js';
import { activePlayer, mediaAlbum, mediaArtist, mediaTitle } from 'src/shared/media.js';
import AstalMpris from 'gi://AstalMpris?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types.js';
const mprisService = AstalMpris.get_default();
const {
@@ -49,15 +49,18 @@ const Media = (): BarBoxChild => {
},
);
const componentClassName = Variable.derive([options.theme.bar.buttons.style, show_label], (style: string) => {
const styleMap: Record<string, string> = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `media-container ${styleMap[style]}`;
});
const componentClassName = Variable.derive(
[options.theme.bar.buttons.style, show_label],
(style: string) => {
const styleMap: Record<string, string> = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `media-container ${styleMap[style]}`;
},
);
const component = (
<box
@@ -68,14 +71,17 @@ const Media = (): BarBoxChild => {
componentClassName.drop();
}}
>
<label className={'bar-button-icon media txt-icon bar'} label={bind(songIcon).as((icn) => icn || '󰝚')} />
<label
className={'bar-button-icon media txt-icon bar'}
label={bind(songIcon).as((icn) => icn || '󰝚')}
/>
<label className={'bar-button-label media'} label={mediaLabel()} />
</box>
);
return {
component,
isVis,
isVis: bind(isVis),
boxClass: 'media',
props: {
setup: (self: Astal.Button): void => {
@@ -113,7 +119,9 @@ const Media = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -4,14 +4,15 @@ import { openMenu } from '../../utils/menu.js';
import { getDistroIcon } from '../../../../lib/utils.js';
import { Variable, bind } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers.js';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types.js';
const { rightClick, middleClick, scrollUp, scrollDown, autoDetectIcon, icon } = options.bar.launcher;
const Menu = (): BarBoxChild => {
const iconBinding = Variable.derive([autoDetectIcon, icon], (autoDetect: boolean, iconValue: string): string =>
autoDetect ? getDistroIcon() : iconValue,
const iconBinding = Variable.derive(
[autoDetectIcon, icon],
(autoDetect: boolean, iconValue: string): string => (autoDetect ? getDistroIcon() : iconValue),
);
const componentClassName = bind(options.theme.bar.buttons.style).as((style: string) => {
@@ -75,7 +76,9 @@ const Menu = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,10 +1,10 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { bind, Variable } from 'astal';
import { BarBoxChild } from 'src/lib/types/bar';
import { Astal } from 'astal/gtk3';
import { inputHandler } from '../../utils/helpers';
import AstalWp from 'gi://AstalWp?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types';
const wireplumber = AstalWp.get_default() as AstalWp.Wp;
const audioService = wireplumber.audio;

View File

@@ -1,8 +1,8 @@
import GLib from 'gi://GLib';
import { Variable } from 'astal';
import { NetworkResourceData } from 'src/lib/types/customModules/network';
import { GET_DEFAULT_NETSTAT_DATA } from 'src/lib/types/defaults/netstat';
import { RateUnit } from 'src/lib/types/bar';
import { RateUnit } from 'src/lib/types/bar.types';
import { NetworkResourceData } from 'src/lib/types/customModules/network.types';
import { getDefaultNetstatData } from 'src/lib/types/defaults/netstat.types';
let previousNetUsage = { rx: 0, tx: 0, time: 0 };
@@ -97,16 +97,20 @@ const isValidInterface = (iface: NetworkUsage | null, interfaceName: string): bo
*/
const getNetworkUsage = (interfaceName: string = ''): NetworkUsage => {
const [success, data] = GLib.file_get_contents('/proc/net/dev');
const defaultStats = { name: '', rx: 0, tx: 0 };
if (!success) {
console.error('Failed to read /proc/net/dev');
return { name: '', rx: 0, tx: 0 };
return defaultStats;
}
const lines = new TextDecoder('utf-8').decode(data).split('\n');
for (const line of lines) {
const iface = parseInterfaceData(line);
if (isValidInterface(iface, interfaceName)) {
return iface!;
return iface ?? defaultStats;
}
}
@@ -131,9 +135,9 @@ export const computeNetwork = (
dataType: Variable<RateUnit>,
): NetworkResourceData => {
const rateUnit = dataType.get();
const interfaceName = interfaceNameVar ? interfaceNameVar.get() : '';
const interfaceName = interfaceNameVar.get();
const DEFAULT_NETSTAT_DATA = GET_DEFAULT_NETSTAT_DATA(rateUnit);
const DEFAULT_NETSTAT_DATA = getDefaultNetstatData(rateUnit);
try {
const { rx, tx, name } = getNetworkUsage(interfaceName);
const currentTime = Date.now();

View File

@@ -2,14 +2,14 @@ import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { computeNetwork } from './helpers';
import { BarBoxChild, NetstatLabelType, RateUnit } from 'src/lib/types/bar';
import { NetworkResourceData } from 'src/lib/types/customModules/network';
import { NETWORK_LABEL_TYPES } from 'src/lib/types/defaults/bar';
import { GET_DEFAULT_NETSTAT_DATA } from 'src/lib/types/defaults/netstat';
import { NETWORK_LABEL_TYPES } from 'src/lib/types/defaults/bar.types';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { bind, Variable } from 'astal';
import AstalNetwork from 'gi://AstalNetwork?version=0.1';
import { Astal } from 'astal/gtk3';
import { RateUnit, BarBoxChild, NetstatLabelType } from 'src/lib/types/bar.types';
import { NetworkResourceData } from 'src/lib/types/customModules/network.types';
import { getDefaultNetstatData } from 'src/lib/types/defaults/netstat.types';
const networkService = AstalNetwork.get_default();
const {
@@ -28,7 +28,7 @@ const {
pollingInterval,
} = options.bar.customModules.netstat;
export const networkUsage = Variable<NetworkResourceData>(GET_DEFAULT_NETSTAT_DATA(rateUnit.get()));
export const networkUsage = Variable<NetworkResourceData>(getDefaultNetstatData(rateUnit.get()));
const netstatPoller = new FunctionPoller<
NetworkResourceData,
@@ -69,7 +69,8 @@ export const Netstat = (): BarBoxChild => {
const labelBinding = Variable.derive(
[bind(networkUsage), bind(labelType)],
(networkService: NetworkResourceData, lblTyp: NetstatLabelType) => renderNetworkLabel(lblTyp, networkService),
(networkService: NetworkResourceData, lblTyp: NetstatLabelType) =>
renderNetworkLabel(lblTyp, networkService),
);
const netstatModule = Module({
@@ -98,7 +99,8 @@ export const Netstat = (): BarBoxChild => {
fn: () => {
labelType.set(
NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.get()) + 1) % NETWORK_LABEL_TYPES.length
(NETWORK_LABEL_TYPES.indexOf(labelType.get()) + 1) %
NETWORK_LABEL_TYPES.length
] as NetstatLabelType,
);
},
@@ -107,7 +109,9 @@ export const Netstat = (): BarBoxChild => {
fn: () => {
labelType.set(
NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.get()) - 1 + NETWORK_LABEL_TYPES.length) %
(NETWORK_LABEL_TYPES.indexOf(labelType.get()) -
1 +
NETWORK_LABEL_TYPES.length) %
NETWORK_LABEL_TYPES.length
] as NetstatLabelType,
);

View File

@@ -19,7 +19,7 @@ const handleWiredIcon = (): void => {
wiredIconBinding?.drop();
wiredIconBinding = undefined;
if (!networkService.wired) {
if (networkService.wired === null) {
return;
}
@@ -38,7 +38,7 @@ const handleWirelessIcon = (): void => {
wirelessIconBinding?.drop();
wirelessIconBinding = undefined;
if (!networkService.wifi) {
if (networkService.wifi === null) {
return;
}

View File

@@ -5,8 +5,8 @@ import { bind, Variable } from 'astal';
import { onPrimaryClick, onSecondaryClick, onMiddleClick, onScroll } from 'src/lib/shared/eventHandlers';
import { Astal, Gtk } from 'astal/gtk3';
import AstalNetwork from 'gi://AstalNetwork?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { formatWifiInfo, wiredIcon, wirelessIcon } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar.types';
const networkService = AstalNetwork.get_default();
const { label, truncation, truncation_size, rightClick, middleClick, scrollDown, scrollUp, showWifiInfo } =
@@ -20,7 +20,9 @@ const Network = (): BarBoxChild => {
},
);
const NetworkIcon = (): JSX.Element => <icon className={'bar-button-icon network-icon'} icon={iconBinding()} />;
const NetworkIcon = (): JSX.Element => (
<icon className={'bar-button-icon network-icon'} icon={iconBinding()} />
);
const networkLabel = Variable.derive(
[
@@ -32,14 +34,16 @@ const Network = (): BarBoxChild => {
bind(networkService, 'state'),
bind(networkService, 'connectivity'),
...(networkService.wifi ? [bind(networkService.wifi, 'enabled')] : []),
...(networkService.wifi !== null ? [bind(networkService.wifi, 'enabled')] : []),
],
(primaryNetwork, showLabel, trunc, tSize, showWifiInfo) => {
if (!showLabel) {
return <box />;
}
if (primaryNetwork === AstalNetwork.Primary.WIRED) {
return <label className={'bar-button-label network-label'} label={'Wired'.substring(0, tSize)} />;
return (
<label className={'bar-button-label network-label'} label={'Wired'.substring(0, tSize)} />
);
}
const networkWifi = networkService.wifi;
if (networkWifi != null) {
@@ -54,11 +58,15 @@ const Network = (): BarBoxChild => {
<label
className={'bar-button-label network-label'}
label={
networkWifi.active_access_point
networkWifi.active_access_point !== null
? `${trunc ? networkWifi.ssid.substring(0, tSize) : networkWifi.ssid}`
: '--'
}
tooltipText={showWifiInfo && networkWifi.active_access_point ? formatWifiInfo(networkWifi) : ''}
tooltipText={
showWifiInfo && networkWifi.active_access_point !== null
? formatWifiInfo(networkWifi)
: ''
}
/>
);
}
@@ -135,7 +143,9 @@ const Network = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -3,13 +3,14 @@ import { Astal, Gtk } from 'astal/gtk3';
import { openMenu } from '../../utils/menu';
import options from 'src/options';
import { filterNotifications } from 'src/lib/shared/notifications.js';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers.js';
import { bind, Variable } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { BarBoxChild } from 'src/lib/types/bar.types';
const notifdService = AstalNotifd.get_default();
const { show_total, rightClick, middleClick, scrollUp, scrollDown, hideCountWhenZero } = options.bar.notifications;
const { show_total, rightClick, middleClick, scrollUp, scrollDown, hideCountWhenZero } =
options.bar.notifications;
const { ignore } = options.notifications;
export const Notifications = (): BarBoxChild => {
@@ -122,7 +123,9 @@ export const Notifications = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,9 +1,9 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const { icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.power;
@@ -11,7 +11,7 @@ export const Power = (): BarBoxChild => {
const powerModule = Module({
tooltipText: 'Power Menu',
textIcon: bind(icon),
showLabelBinding: Variable(false),
showLabelBinding: bind(Variable(false)),
boxClass: 'powermodule',
props: {
setup: (self: Astal.Button) => {

View File

@@ -1,6 +1,6 @@
import { divide } from 'src/components/bar/utils/helpers';
import { GenericResourceData } from 'src/lib/types/customModules/generic';
import { GLib, Variable } from 'astal';
import { GenericResourceData } from 'src/lib/types/customModules/generic.types';
/**
* Calculates the RAM usage.
@@ -16,7 +16,7 @@ export const calculateRamUsage = (round: Variable<boolean>): GenericResourceData
try {
const [success, meminfoBytes] = GLib.file_get_contents('/proc/meminfo');
if (!success || !meminfoBytes) {
if (!success || meminfoBytes === null) {
throw new Error('Failed to read /proc/meminfo or file content is null.');
}

View File

@@ -2,12 +2,12 @@ import options from 'src/options';
import { Module } from '../../shared/Module';
import { calculateRamUsage } from './helpers';
import { formatTooltip, inputHandler, renderResourceLabel } from 'src/components/bar/utils/helpers';
import { LABEL_TYPES } from 'src/lib/types/defaults/bar';
import { LABEL_TYPES } from 'src/lib/types/defaults/bar.types';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { GenericResourceData } from 'src/lib/types/customModules/generic';
import { bind, Variable } from 'astal';
import { BarBoxChild, ResourceLabelType } from 'src/lib/types/bar';
import { Astal } from 'astal/gtk3';
import { BarBoxChild, ResourceLabelType } from 'src/lib/types/bar.types';
import { GenericResourceData } from 'src/lib/types/customModules/generic.types';
const { label, labelType, round, leftClick, rightClick, middleClick, pollingInterval, icon } =
options.bar.customModules.ram;
@@ -68,7 +68,8 @@ export const Ram = (): BarBoxChild => {
fn: () => {
labelType.set(
LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.get()) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length
(LABEL_TYPES.indexOf(labelType.get()) - 1 + LABEL_TYPES.length) %
LABEL_TYPES.length
] as ResourceLabelType,
);
},

View File

@@ -2,7 +2,7 @@ import GTop from 'gi://GTop';
import { divide } from 'src/components/bar/utils/helpers';
import { Variable } from 'astal';
import { GenericResourceData } from 'src/lib/types/customModules/generic';
import { GenericResourceData } from 'src/lib/types/customModules/generic.types';
/**
* Computes the storage usage for the root filesystem.

View File

@@ -2,12 +2,12 @@ import options from 'src/options';
import { Module } from '../../shared/Module';
import { formatTooltip, inputHandler, renderResourceLabel } from 'src/components/bar/utils/helpers';
import { computeStorage } from './helpers';
import { BarBoxChild, ResourceLabelType } from 'src/lib/types/bar';
import { GenericResourceData } from 'src/lib/types/customModules/generic';
import { LABEL_TYPES } from 'src/lib/types/defaults/bar';
import { LABEL_TYPES } from 'src/lib/types/defaults/bar.types';
import { FunctionPoller } from 'src/lib/poller/FunctionPoller';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild, ResourceLabelType } from 'src/lib/types/bar.types';
import { GenericResourceData } from 'src/lib/types/customModules/generic.types';
const { label, labelType, icon, round, leftClick, rightClick, middleClick, pollingInterval } =
options.bar.customModules.storage;
@@ -66,7 +66,8 @@ export const Storage = (): BarBoxChild => {
fn: () => {
labelType.set(
LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.get()) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length
(LABEL_TYPES.indexOf(labelType.get()) - 1 + LABEL_TYPES.length) %
LABEL_TYPES.length
] as ResourceLabelType,
);
},

View File

@@ -1,12 +1,12 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { capitalizeFirstLetter } from 'src/lib/utils';
import { getInitialSubmap, isSubmapEnabled } from './helpers';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types';
const hyprlandService = AstalHyprland.get_default();
const {

View File

@@ -2,8 +2,8 @@ import { isMiddleClick, isPrimaryClick, isSecondaryClick, Notify } from '../../.
import options from '../../../../options';
import AstalTray from 'gi://AstalTray?version=0.1';
import { bind, Gio, Variable } from 'astal';
import { BarBoxChild } from 'src/lib/types/bar';
import { Gdk, Gtk } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const systemtray = AstalTray.get_default();
const { ignore, customIcons } = options.bar.systray;
@@ -28,7 +28,13 @@ const MenuCustomIcon = ({ iconLabel, iconColor, item }: MenuCustomIconProps): JS
};
const MenuDefaultIcon = ({ item }: MenuEntryProps): JSX.Element => {
return <icon className={'systray-icon'} gIcon={bind(item, 'gicon')} tooltipMarkup={bind(item, 'tooltipMarkup')} />;
return (
<icon
className={'systray-icon'}
gIcon={bind(item, 'gicon')}
tooltipMarkup={bind(item, 'tooltipMarkup')}
/>
);
};
const MenuEntry = ({ item, child }: MenuEntryProps): JSX.Element => {
@@ -37,10 +43,10 @@ const MenuEntry = ({ item, child }: MenuEntryProps): JSX.Element => {
const entryBinding = Variable.derive(
[bind(item, 'menuModel'), bind(item, 'actionGroup')],
(menuModel, actionGroup) => {
if (!menuModel) {
if (menuModel === null) {
return console.error(`Menu Model not found for ${item.id}`);
}
if (!actionGroup) {
if (actionGroup === null) {
return console.error(`Action Group not found for ${item.id}`);
}
@@ -85,7 +91,9 @@ const SysTray = (): BarBoxChild => {
isVis.set(filteredTray.length > 0);
return filteredTray.map((item) => {
const matchedCustomIcon = Object.keys(custIcons).find((iconRegex) => item.id.match(iconRegex));
const matchedCustomIcon = Object.keys(custIcons).find((iconRegex) =>
item.id.match(iconRegex),
);
if (matchedCustomIcon !== undefined) {
const iconLabel = custIcons[matchedCustomIcon].icon || '󰠫';
@@ -122,7 +130,7 @@ const SysTray = (): BarBoxChild => {
component,
isVisible: true,
boxClass: 'systray',
isVis,
isVis: bind(isVis),
isBox: true,
props: {},
};

View File

@@ -1,10 +1,10 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { BashPoller } from 'src/lib/poller/BashPoller';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const {
updateCommand,
@@ -75,7 +75,7 @@ export const Updates = (): BarBoxChild => {
textIcon: updatesIcon(),
tooltipText: bind(pendingUpdatesTooltip),
boxClass: 'updates',
isVis: isVis,
isVis: bind(isVis),
label: bind(pendingUpdates),
showLabelBinding: bind(label),
props: {

View File

@@ -1,6 +1,4 @@
import { VolumeIcons } from 'src/lib/types/volume';
const icons: VolumeIcons = {
const icons: Record<number, string> = {
101: '󰕾',
66: '󰕾',
34: '󰖀',

View File

@@ -4,9 +4,9 @@ import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/util
import { bind, Variable } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers.js';
import { getIcon } from './helpers/index.js';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { Astal } from 'astal/gtk3';
import AstalWp from 'gi://AstalWp?version=0.1';
import { BarBoxChild } from 'src/lib/types/bar.types.js';
const wireplumber = AstalWp.get_default() as AstalWp.Wp;
const audioService = wireplumber?.audio;
@@ -118,7 +118,9 @@ const Volume = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,12 +1,13 @@
import options from 'src/options';
import { Module } from '../../shared/Module';
import { inputHandler } from 'src/components/bar/utils/helpers';
import { getWeatherStatusTextIcon, globalWeatherVar } from 'src/globals/weather';
import { BarBoxChild } from 'src/lib/types/bar';
import { getWeatherStatusTextIcon, globalWeatherVar } from 'src/shared/weather';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
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 = (): BarBoxChild => {
const iconBinding = Variable.derive([bind(globalWeatherVar)], (wthr) => {

View File

@@ -15,7 +15,7 @@ function trackClientUpdates(client: AstalHyprland.Client): void {
clientBinding?.drop();
clientBinding = undefined;
if (!client) {
if (client === null) {
return;
}
@@ -68,7 +68,11 @@ export const getWindowMatch = (hyprlandClient: AstalHyprland.Client): Record<str
*
* @returns The title of the window as a string.
*/
export const getTitle = (client: AstalHyprland.Client, useCustomTitle: boolean, useClassName: boolean): string => {
export const getTitle = (
client: AstalHyprland.Client,
useCustomTitle: boolean,
useClassName: boolean,
): string => {
if (client === null || useCustomTitle) return getWindowMatch(client).label;
const title = client.title;

View File

@@ -1,11 +1,11 @@
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import options from 'src/options';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { bind, Variable } from 'astal';
import { clientTitle, getTitle, getWindowMatch, truncateTitle } from './helpers/title';
import { Astal } from 'astal/gtk3';
import { BarBoxChild } from 'src/lib/types/bar.types';
const hyprlandService = AstalHyprland.get_default();
const { leftClick, rightClick, middleClick, scrollDown, scrollUp } = options.bar.windowtitle;
@@ -14,7 +14,12 @@ const ClientTitle = (): BarBoxChild => {
const { custom_title, class_name, label, icon, truncation, truncation_size } = options.bar.windowtitle;
const ClientIcon = ({ client }: ClientIconProps): JSX.Element => {
return <label className={'bar-button-icon windowtitle txt-icon bar'} label={getWindowMatch(client).icon} />;
return (
<label
className={'bar-button-icon windowtitle txt-icon bar'}
label={getWindowMatch(client).icon}
/>
);
};
const ClientLabel = ({
@@ -28,7 +33,10 @@ const ClientTitle = (): BarBoxChild => {
return (
<label
className={`bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`}
label={truncateTitle(getTitle(client, useCustomTitle, useClassName), truncate ? truncationSize : -1)}
label={truncateTitle(
getTitle(client, useCustomTitle, useClassName),
truncate ? truncationSize : -1,
)}
/>
);
};
@@ -131,7 +139,9 @@ const ClientTitle = (): BarBoxChild => {
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
disconnectFunctions.push(
onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()),
);
},
);
},

View File

@@ -1,6 +1,6 @@
import { Variable } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { MonitorMap, WorkspaceMonitorMap, WorkspaceRule } from 'src/lib/types/workspace';
import { MonitorMap, WorkspaceMonitorMap, WorkspaceRule } from 'src/lib/types/workspace.types';
import { range } from 'src/lib/utils';
import options from 'src/options';
@@ -172,7 +172,7 @@ function isWorkspaceIgnored(ignoredWorkspacesVariable: Variable<string>, workspa
* @param ignoredWorkspacesVariable - A Variable that contains the ignored workspaces pattern.
*/
function navigateWorkspace(direction: 'next' | 'prev', ignoredWorkspacesVariable: Variable<string>): void {
const allHyprlandWorkspaces = hyprlandService.get_workspaces() || [];
const allHyprlandWorkspaces = hyprlandService.get_workspaces() ?? [];
const activeWorkspaceIds = allHyprlandWorkspaces
.filter((workspaceInstance) => hyprlandService.focusedMonitor.id === workspaceInstance.monitor?.id)
@@ -187,7 +187,8 @@ function navigateWorkspace(direction: 'next' | 'prev', ignoredWorkspacesVariable
const workspaceIndex = assignedOrOccupiedWorkspaces.indexOf(hyprlandService.focusedWorkspace?.id);
const step = direction === 'next' ? 1 : -1;
let newIndex = (workspaceIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
let newIndex =
(workspaceIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
let attempts = 0;
while (attempts < assignedOrOccupiedWorkspaces.length) {
@@ -196,7 +197,8 @@ function navigateWorkspace(direction: 'next' | 'prev', ignoredWorkspacesVariable
hyprlandService.dispatch('workspace', targetWorkspaceNumber.toString());
return;
}
newIndex = (newIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
newIndex =
(newIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
attempts++;
}
}
@@ -321,7 +323,9 @@ export function getWorkspacesToRender(
);
const activeWorkspacesForCurrentMonitor = activeWorkspaceIds.filter((workspaceId) => {
const metadataForWorkspace = allWorkspaceInstances.find((workspaceObj) => workspaceObj.id === workspaceId);
const metadataForWorkspace = allWorkspaceInstances.find(
(workspaceObj) => workspaceObj.id === workspaceId,
);
if (metadataForWorkspace) {
return metadataForWorkspace?.monitor?.id === monitorId;
@@ -334,6 +338,8 @@ export function getWorkspacesToRender(
) {
return workspaceMonitorRules[currentMonitorInstance.name].includes(workspaceId);
}
return false;
});
if (isMonitorSpecific) {
@@ -347,12 +353,16 @@ export function getWorkspacesToRender(
);
});
allPotentialWorkspaces = [...new Set([...activeWorkspacesForCurrentMonitor, ...validWorkspaceNumbers])];
allPotentialWorkspaces = [
...new Set([...activeWorkspacesForCurrentMonitor, ...validWorkspaceNumbers]),
];
} else {
allPotentialWorkspaces = [...new Set([...allPotentialWorkspaces, ...activeWorkspaceIds])];
}
return allPotentialWorkspaces.filter((workspace) => !isWorkspaceIgnored(ignored, workspace)).sort((a, b) => a - b);
return allPotentialWorkspaces
.filter((workspace) => !isWorkspaceIgnored(ignored, workspace))
.sort((a, b) => a - b);
}
/**

View File

@@ -1,6 +1,6 @@
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { defaultApplicationIconMap } from 'src/lib/constants/appIcons';
import { AppIconOptions, WorkspaceIconMap } from 'src/lib/types/workspace';
import { WorkspaceIconMap, AppIconOptions } from 'src/lib/types/workspace.types';
import { isValidGjsColor } from 'src/lib/utils';
import options from 'src/options';
@@ -40,7 +40,7 @@ const getWsIcon = (wsIconMap: WorkspaceIconMap, i: number): string => {
const iconEntry = wsIconMap[i];
const defaultIcon = `${i}`;
if (!iconEntry) {
if (iconEntry === undefined) {
return defaultIcon;
}
@@ -77,8 +77,10 @@ export const getWsColor = (
monitor: number,
): string => {
const iconEntry = wsIconMap[i];
const hasColor = typeof iconEntry === 'object' && 'color' in iconEntry && isValidGjsColor(iconEntry.color);
if (!iconEntry) {
const hasColor =
typeof iconEntry === 'object' && 'color' in iconEntry && isValidGjsColor(iconEntry.color);
if (iconEntry === undefined) {
return '';
}
@@ -150,7 +152,7 @@ export const getAppIcon = (
let icons = workspaceClients.reduce((iconAccumulator, [clientClass, clientTitle]) => {
const icon = findIconForClient(clientClass, clientTitle);
if (icon) {
if (icon !== undefined) {
iconAccumulator.push(icon);
}
@@ -193,7 +195,8 @@ export const renderClassnames = (
monitor: number,
i: number,
): string => {
const isWorkspaceActive = hyprlandService.focusedWorkspace?.id === i || isWorkspaceActiveOnMonitor(monitor, i);
const isWorkspaceActive =
hyprlandService.focusedWorkspace?.id === i || isWorkspaceActiveOnMonitor(monitor, i);
const isActive = isWorkspaceActive ? 'active' : '';
if (showIcons) {

View File

@@ -1,11 +1,11 @@
import options from 'src/options';
import { initThrottledScrollHandlers } from './helpers';
import { BarBoxChild } from 'src/lib/types/bar';
import { WorkspaceModule } from './workspaces';
import { bind, Variable } from 'astal';
import { GtkWidget } from 'src/lib/types/widget';
import { Astal, Gdk } from 'astal/gtk3';
import { isScrollDown, isScrollUp } from 'src/lib/utils';
import { BarBoxChild } from 'src/lib/types/bar.types';
import { GtkWidget } from 'src/lib/types/widget.types';
const { scroll_speed } = options.bar.workspaces;
@@ -29,7 +29,8 @@ const Workspaces = (monitor = -1): BarBoxChild => {
self.disconnect(scrollHandlers);
}
const { throttledScrollUp, throttledScrollDown } = initThrottledScrollHandlers(scroll_speed);
const { throttledScrollUp, throttledScrollDown } =
initThrottledScrollHandlers(scroll_speed);
scrollHandlers = self.connect('scroll-event', (_: GtkWidget, event: Gdk.Event) => {
if (isScrollUp(event)) {

View File

@@ -1,11 +1,11 @@
import options from 'src/options';
import { forceUpdater, getWorkspacesToRender, initWorkspaceEvents, workspaceRules } from './helpers';
import { getAppIcon, getWsColor, renderClassnames, renderLabel } from './helpers/utils';
import { ApplicationIcons, WorkspaceIconMap } from 'src/lib/types/workspace';
import { bind, Variable } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { Gtk } from 'astal/gtk3';
import { isPrimaryClick } from 'src/lib/utils';
import { WorkspaceIconMap, ApplicationIcons } from 'src/lib/types/workspace.types';
const hyprlandService = AstalHyprland.get_default();
const {
@@ -145,7 +145,9 @@ export const WorkspaceModule = ({ monitor }: WorkspaceModuleProps): JSX.Element
monitor,
)}
setup={(self) => {
const currentWsClients = clients.filter((client) => client?.workspace?.id === wsId);
const currentWsClients = clients.filter(
(client) => client?.workspace?.id === wsId,
);
self.toggleClassName('occupied', currentWsClients.length > 0);
}}
/>

View File

@@ -1,11 +1,11 @@
import options from 'src/options';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { inputHandler } from 'src/components/bar/utils/helpers.js';
import { bind, Variable } from 'astal';
import { Astal } from 'astal/gtk3';
import { systemTime } from 'src/globals/time';
import { systemTime } from 'src/shared/time';
import { GLib } from 'astal';
import { Module } from '../../shared/Module';
import { BarBoxChild } from 'src/lib/types/bar.types';
const {
format,

View File

@@ -24,18 +24,50 @@ export const CustomModuleSettings = (): JSX.Element => {
type="boolean"
/>
<Option opt={options.bar.customModules.microphone.label} title="Show Label" type="boolean" />
<Option opt={options.bar.customModules.microphone.mutedIcon} title="Muted Icon" type="string" />
<Option opt={options.bar.customModules.microphone.unmutedIcon} title="Unmuted Icon" type="string" />
<Option opt={options.theme.bar.buttons.modules.microphone.spacing} title="Spacing" type="string" />
<Option opt={options.bar.customModules.microphone.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.microphone.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.microphone.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.microphone.mutedIcon}
title="Muted Icon"
type="string"
/>
<Option
opt={options.bar.customModules.microphone.unmutedIcon}
title="Unmuted Icon"
type="string"
/>
<Option
opt={options.theme.bar.buttons.modules.microphone.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.microphone.leftClick}
title="Left Click"
type="string"
/>
<Option
opt={options.bar.customModules.microphone.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.microphone.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.microphone.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.microphone.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.microphone.scrollDown}
title="Scroll Down"
type="string"
/>
{/* RAM Section */}
<Header title="RAM" />
<Option opt={options.theme.bar.buttons.modules.ram.enableBorder} title="Button Border" type="boolean" />
<Option
opt={options.theme.bar.buttons.modules.ram.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.ram.icon} title="Ram Icon" type="string" />
<Option opt={options.bar.customModules.ram.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.ram.spacing} title="Spacing" type="string" />
@@ -60,7 +92,11 @@ export const CustomModuleSettings = (): JSX.Element => {
{/* CPU Section */}
<Header title="CPU" />
<Option opt={options.theme.bar.buttons.modules.cpu.enableBorder} title="Button Border" type="boolean" />
<Option
opt={options.theme.bar.buttons.modules.cpu.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.cpu.icon} title="Cpu Icon" type="string" />
<Option opt={options.bar.customModules.cpu.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.cpu.spacing} title="Spacing" type="string" />
@@ -100,9 +136,17 @@ export const CustomModuleSettings = (): JSX.Element => {
enums={['imperial', 'metric']}
/>
<Option opt={options.bar.customModules.cpuTemp.showUnit} title="Show Unit" type="boolean" />
<Option opt={options.bar.customModules.cpuTemp.icon} title="Cpu Temperature Icon" type="string" />
<Option
opt={options.bar.customModules.cpuTemp.icon}
title="Cpu Temperature Icon"
type="string"
/>
<Option opt={options.bar.customModules.cpuTemp.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.cpuTemp.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.cpuTemp.spacing}
title="Spacing"
type="string"
/>
<Option opt={options.bar.customModules.cpuTemp.round} title="Round" type="boolean" />
<Option
opt={options.bar.customModules.cpuTemp.pollingInterval}
@@ -113,10 +157,22 @@ export const CustomModuleSettings = (): JSX.Element => {
increment={1000}
/>
<Option opt={options.bar.customModules.cpuTemp.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.cpuTemp.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.cpuTemp.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.cpuTemp.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.cpuTemp.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.cpuTemp.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.cpuTemp.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.cpuTemp.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Storage Section */}
<Header title="Storage" />
@@ -127,7 +183,11 @@ export const CustomModuleSettings = (): JSX.Element => {
/>
<Option opt={options.bar.customModules.storage.icon} title="Storage Icon" type="string" />
<Option opt={options.bar.customModules.storage.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.storage.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.storage.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.storage.labelType}
title="Label Type"
@@ -144,8 +204,16 @@ export const CustomModuleSettings = (): JSX.Element => {
increment={1000}
/>
<Option opt={options.bar.customModules.storage.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.storage.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.storage.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.storage.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.storage.middleClick}
title="Middle Click"
type="string"
/>
{/* Netstat Section */}
<Header title="Netstat" />
@@ -171,7 +239,11 @@ export const CustomModuleSettings = (): JSX.Element => {
/>
<Option opt={options.bar.customModules.netstat.icon} title="Netstat Icon" type="string" />
<Option opt={options.bar.customModules.netstat.label} title="Show Label" type="boolean" />
<Option opt={options.bar.customModules.netstat.networkInLabel} title="Network In Label" type="string" />
<Option
opt={options.bar.customModules.netstat.networkInLabel}
title="Network In Label"
type="string"
/>
<Option
opt={options.bar.customModules.netstat.networkOutLabel}
title="Network Out Label"
@@ -183,7 +255,11 @@ export const CustomModuleSettings = (): JSX.Element => {
type="enum"
enums={['GiB', 'MiB', 'KiB', 'auto']}
/>
<Option opt={options.theme.bar.buttons.modules.netstat.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.netstat.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.netstat.labelType}
title="Label Type"
@@ -200,8 +276,16 @@ export const CustomModuleSettings = (): JSX.Element => {
increment={1000}
/>
<Option opt={options.bar.customModules.netstat.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.netstat.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.netstat.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.netstat.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.netstat.middleClick}
title="Middle Click"
type="string"
/>
{/* Keyboard Layout Section */}
<Header title="Keyboard Layout" />
@@ -210,7 +294,11 @@ export const CustomModuleSettings = (): JSX.Element => {
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.kbLayout.icon} title="Keyboard Layout Icon" type="string" />
<Option
opt={options.bar.customModules.kbLayout.icon}
title="Keyboard Layout Icon"
type="string"
/>
<Option opt={options.bar.customModules.kbLayout.label} title="Show Label" type="boolean" />
<Option
opt={options.bar.customModules.kbLayout.labelType}
@@ -218,12 +306,28 @@ export const CustomModuleSettings = (): JSX.Element => {
type="enum"
enums={['layout', 'code']}
/>
<Option opt={options.theme.bar.buttons.modules.kbLayout.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.kbLayout.spacing}
title="Spacing"
type="string"
/>
<Option opt={options.bar.customModules.kbLayout.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.kbLayout.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.kbLayout.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.kbLayout.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.kbLayout.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.kbLayout.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.kbLayout.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.kbLayout.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Updates Section */}
<Header title="Updates" />
@@ -253,7 +357,11 @@ export const CustomModuleSettings = (): JSX.Element => {
title="Updates Available Icon"
type="string"
/>
<Option opt={options.bar.customModules.updates.icon.updated} title="No Updates Icon" type="string" />
<Option
opt={options.bar.customModules.updates.icon.updated}
title="No Updates Icon"
type="string"
/>
<Option opt={options.bar.customModules.updates.label} title="Show Label" type="boolean" />
<Option
opt={options.bar.customModules.updates.autoHide}
@@ -262,7 +370,11 @@ export const CustomModuleSettings = (): JSX.Element => {
type="boolean"
/>
<Option opt={options.bar.customModules.updates.padZero} title="Pad with 0" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.updates.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.updates.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.updates.pollingInterval}
title="Polling Interval"
@@ -273,10 +385,22 @@ export const CustomModuleSettings = (): JSX.Element => {
increment={1000}
/>
<Option opt={options.bar.customModules.updates.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.updates.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.updates.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.updates.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.updates.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.updates.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.updates.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.updates.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Submap Section */}
<Header title="Submap" />
@@ -291,15 +415,39 @@ export const CustomModuleSettings = (): JSX.Element => {
subtitle="Displays current submap name instead of Enabled/Disabled text."
type="boolean"
/>
<Option opt={options.bar.customModules.submap.enabledIcon} title="Enabled Icon" type="string" />
<Option opt={options.bar.customModules.submap.disabledIcon} title="Disabled Icon" type="string" />
<Option opt={options.bar.customModules.submap.enabledText} title="Enabled Text" type="string" />
<Option opt={options.bar.customModules.submap.disabledText} title="Disabled Text" type="string" />
<Option
opt={options.bar.customModules.submap.enabledIcon}
title="Enabled Icon"
type="string"
/>
<Option
opt={options.bar.customModules.submap.disabledIcon}
title="Disabled Icon"
type="string"
/>
<Option
opt={options.bar.customModules.submap.enabledText}
title="Enabled Text"
type="string"
/>
<Option
opt={options.bar.customModules.submap.disabledText}
title="Disabled Text"
type="string"
/>
<Option opt={options.bar.customModules.submap.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.submap.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.submap.spacing}
title="Spacing"
type="string"
/>
<Option opt={options.bar.customModules.submap.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.submap.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.submap.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.submap.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.submap.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.submap.scrollDown} title="Scroll Down" type="string" />
@@ -317,12 +465,28 @@ export const CustomModuleSettings = (): JSX.Element => {
type="enum"
enums={['imperial', 'metric']}
/>
<Option opt={options.theme.bar.buttons.modules.weather.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.weather.spacing}
title="Spacing"
type="string"
/>
<Option opt={options.bar.customModules.weather.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.weather.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.weather.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.weather.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.weather.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.weather.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.weather.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.weather.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Hyprsunset Section */}
<Header title="Hyprsunset" />
@@ -337,12 +501,32 @@ export const CustomModuleSettings = (): JSX.Element => {
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.hyprsunset.onIcon} title="Enabled Icon" type="string" />
<Option opt={options.bar.customModules.hyprsunset.offIcon} title="Disabled Icon" type="string" />
<Option opt={options.bar.customModules.hyprsunset.onLabel} title="Enabled Label" type="string" />
<Option opt={options.bar.customModules.hyprsunset.offLabel} title="Disabled Label" type="string" />
<Option
opt={options.bar.customModules.hyprsunset.onIcon}
title="Enabled Icon"
type="string"
/>
<Option
opt={options.bar.customModules.hyprsunset.offIcon}
title="Disabled Icon"
type="string"
/>
<Option
opt={options.bar.customModules.hyprsunset.onLabel}
title="Enabled Label"
type="string"
/>
<Option
opt={options.bar.customModules.hyprsunset.offLabel}
title="Disabled Label"
type="string"
/>
<Option opt={options.bar.customModules.hyprsunset.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.hyprsunset.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.hyprsunset.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.hyprsunset.pollingInterval}
title="Polling Interval"
@@ -351,10 +535,22 @@ export const CustomModuleSettings = (): JSX.Element => {
max={60 * 24 * 1000}
increment={1000}
/>
<Option opt={options.bar.customModules.hyprsunset.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.hyprsunset.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.hyprsunset.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.hyprsunset.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.hyprsunset.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.hyprsunset.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.hyprsunset.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Hypridle Section */}
<Header title="Hypridle" />
@@ -364,11 +560,27 @@ export const CustomModuleSettings = (): JSX.Element => {
type="boolean"
/>
<Option opt={options.bar.customModules.hypridle.onIcon} title="Enabled Icon" type="string" />
<Option opt={options.bar.customModules.hypridle.offIcon} title="Disabled Icon" type="string" />
<Option opt={options.bar.customModules.hypridle.onLabel} title="Enabled Label" type="string" />
<Option opt={options.bar.customModules.hypridle.offLabel} title="Disabled Label" type="string" />
<Option
opt={options.bar.customModules.hypridle.offIcon}
title="Disabled Icon"
type="string"
/>
<Option
opt={options.bar.customModules.hypridle.onLabel}
title="Enabled Label"
type="string"
/>
<Option
opt={options.bar.customModules.hypridle.offLabel}
title="Disabled Label"
type="string"
/>
<Option opt={options.bar.customModules.hypridle.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.hypridle.spacing} title="Spacing" type="string" />
<Option
opt={options.theme.bar.buttons.modules.hypridle.spacing}
title="Spacing"
type="string"
/>
<Option
opt={options.bar.customModules.hypridle.pollingInterval}
title="Polling Interval"
@@ -377,10 +589,22 @@ export const CustomModuleSettings = (): JSX.Element => {
max={60 * 24 * 1000}
increment={1000}
/>
<Option opt={options.bar.customModules.hypridle.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.hypridle.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.hypridle.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.hypridle.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.hypridle.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.hypridle.scrollDown} title="Scroll Down" type="string" />
<Option
opt={options.bar.customModules.hypridle.scrollDown}
title="Scroll Down"
type="string"
/>
{/* Cava Section */}
<Header title="Cava" />
@@ -392,8 +616,16 @@ export const CustomModuleSettings = (): JSX.Element => {
<Option opt={options.bar.customModules.cava.icon} title="Icon" type="string" />
<Option opt={options.bar.customModules.cava.showIcon} title="Show Icon" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.cava.spacing} title="Spacing" type="string" />
<Option opt={options.bar.customModules.cava.barCharacters} title="Bar Characters" type="object" />
<Option opt={options.bar.customModules.cava.spaceCharacter} title="Bar Separator" type="string" />
<Option
opt={options.bar.customModules.cava.barCharacters}
title="Bar Characters"
type="object"
/>
<Option
opt={options.bar.customModules.cava.spaceCharacter}
title="Bar Separator"
type="string"
/>
<Option
opt={options.bar.customModules.cava.showActiveOnly}
title="Auto Hide"
@@ -411,7 +643,11 @@ export const CustomModuleSettings = (): JSX.Element => {
/>
<Option opt={options.bar.customModules.cava.lowCutoff} title="Low Cutoff" type="number" />
<Option opt={options.bar.customModules.cava.highCutoff} title="High Cutoff" type="number" />
<Option opt={options.bar.customModules.cava.noiseReduction} title="Noise Reduction" type="float" />
<Option
opt={options.bar.customModules.cava.noiseReduction}
title="Noise Reduction"
type="float"
/>
<Option opt={options.bar.customModules.cava.stereo} title="Stereo" type="boolean" />
<Option opt={options.bar.customModules.cava.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.cava.rightClick} title="Right Click" type="string" />
@@ -427,8 +663,16 @@ export const CustomModuleSettings = (): JSX.Element => {
type="boolean"
/>
<Option opt={options.bar.customModules.worldclock.icon} title="Icon" type="string" />
<Option opt={options.bar.customModules.worldclock.showIcon} title="Show Icon" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.worldclock.spacing} title="Spacing" type="string" />
<Option
opt={options.bar.customModules.worldclock.showIcon}
title="Show Icon"
type="boolean"
/>
<Option
opt={options.theme.bar.buttons.modules.worldclock.spacing}
title="Spacing"
type="string"
/>
<Option opt={options.bar.customModules.worldclock.format} title="Format" type="string" />
<Option
opt={options.bar.customModules.worldclock.formatDiffDate}
@@ -436,13 +680,37 @@ export const CustomModuleSettings = (): JSX.Element => {
subtitle="Format to use when the timezone is on a different calendar day than the local timezone."
type="string"
/>
<Option opt={options.bar.customModules.worldclock.divider} title="Date Divider" type="string" />
<Option opt={options.bar.customModules.worldclock.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.worldclock.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.worldclock.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.worldclock.divider}
title="Date Divider"
type="string"
/>
<Option
opt={options.bar.customModules.worldclock.leftClick}
title="Left Click"
type="string"
/>
<Option
opt={options.bar.customModules.worldclock.rightClick}
title="Right Click"
type="string"
/>
<Option
opt={options.bar.customModules.worldclock.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.worldclock.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.worldclock.scrollDown} title="Scroll Down" type="string" />
<Option opt={options.bar.customModules.worldclock.tz} title="Timezones Displayed" type="object" />
<Option
opt={options.bar.customModules.worldclock.scrollDown}
title="Scroll Down"
type="string"
/>
<Option
opt={options.bar.customModules.worldclock.tz}
title="Timezones Displayed"
type="object"
/>
{/* Power Section */}
<Header title="Power" />
@@ -455,7 +723,11 @@ export const CustomModuleSettings = (): JSX.Element => {
<Option opt={options.bar.customModules.power.icon} title="Power Button Icon" type="string" />
<Option opt={options.bar.customModules.power.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.power.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.power.middleClick} title="Middle Click" type="string" />
<Option
opt={options.bar.customModules.power.middleClick}
title="Middle Click"
type="string"
/>
<Option opt={options.bar.customModules.power.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.power.scrollDown} title="Scroll Down" type="string" />
</box>

View File

@@ -32,13 +32,21 @@ export const CustomModuleTheme = (): JSX.Element => {
}
type="color"
/>
<Option opt={options.theme.bar.buttons.modules.microphone.border} title="Border" type="color" />
<Option
opt={options.theme.bar.buttons.modules.microphone.border}
title="Border"
type="color"
/>
{/* RAM Module Section */}
<Header title="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"
@@ -54,7 +62,11 @@ export const CustomModuleTheme = (): JSX.Element => {
<Header title="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"
@@ -224,7 +236,11 @@ export const CustomModuleTheme = (): JSX.Element => {
}
type="color"
/>
<Option opt={options.theme.bar.buttons.modules.hyprsunset.border} title="Border" type="color" />
<Option
opt={options.theme.bar.buttons.modules.hyprsunset.border}
title="Border"
type="color"
/>
{/* Hypridle Module Section */}
<Header title="Hypridle" />
@@ -250,7 +266,11 @@ export const CustomModuleTheme = (): JSX.Element => {
<Header title="Cava" />
<Option opt={options.theme.bar.buttons.modules.cava.text} title="Bars" type="color" />
<Option opt={options.theme.bar.buttons.modules.cava.icon} title="Icon" type="color" />
<Option opt={options.theme.bar.buttons.modules.cava.background} title="Label Background" type="color" />
<Option
opt={options.theme.bar.buttons.modules.cava.background}
title="Label Background"
type="color"
/>
<Option
opt={options.theme.bar.buttons.modules.cava.icon_background}
title="Icon Background"
@@ -280,7 +300,11 @@ export const CustomModuleTheme = (): JSX.Element => {
}
type="color"
/>
<Option opt={options.theme.bar.buttons.modules.worldclock.border} title="Border" type="color" />
<Option
opt={options.theme.bar.buttons.modules.worldclock.border}
title="Border"
type="color"
/>
{/* Power Module Section */}
<Header title="Power" />

View File

@@ -1,6 +1,6 @@
import { bind, Variable } from 'astal';
import { BarBoxChild, BarModule } from 'src/lib/types/bar';
import { BarButtonStyles } from 'src/lib/types/options';
import { BarBoxChild, BarModuleProps } from 'src/lib/types/bar.types';
import { BarButtonStyles } from 'src/lib/options/options.types';
import options from 'src/options';
const { style } = options.theme.bar.buttons;
@@ -17,20 +17,20 @@ export const Module = ({
props = {},
showLabelBinding = bind(Variable(true)),
showIconBinding = bind(Variable(true)),
showLabel,
showLabel = true,
labelHook,
hook,
}: BarModule): BarBoxChild => {
}: BarModuleProps): BarBoxChild => {
const getIconWidget = (useTxtIcn: boolean): JSX.Element | undefined => {
const className = `txt-icon bar-button-icon module-icon ${boxClass}`;
const icn = typeof icon === 'string' ? icon : icon?.get();
if (!useTxtIcn && icn?.length) {
if (!useTxtIcn && icn !== undefined && icn.length > 0) {
return <icon className={className} icon={icon} />;
}
const textIcn = typeof textIcon === 'string' ? textIcon : textIcon?.get();
if (textIcn?.length) {
if (textIcn !== undefined && textIcn.length > 0) {
return <label className={className} label={textIcon} />;
}
};

View File

@@ -1,15 +1,14 @@
import { BarBoxChild } from 'src/lib/types/bar';
import { Bind } from '../../../lib/types/variable';
import { BarBoxChild } from 'src/lib/types/bar.types';
import options from '../../../options';
import { bind } from 'astal';
import { bind, Binding } from 'astal';
const computeVisible = (child: BarBoxChild): Bind | boolean => {
const computeVisible = (child: BarBoxChild): Binding<boolean> | boolean => {
if (child.isVis !== undefined) {
return bind(child.isVis);
return child.isVis;
}
return child.isVisible;
};
return child.isVisible ?? true;
};
export const WidgetContainer = (child: BarBoxChild): JSX.Element => {
const buttonClassName = bind(options.theme.bar.buttons.style).as((style) => {
const styleMap = {
@@ -24,7 +23,7 @@ export const WidgetContainer = (child: BarBoxChild): JSX.Element => {
return `bar_item_box_visible ${styleMap[style]} ${boxClassName}`;
});
if (child.isBox) {
if (child.isBox === true) {
return (
<eventbox visible={computeVisible(child)} {...child.props}>
<box className={buttonClassName}>{child.component}</box>

View File

@@ -8,20 +8,20 @@ const hyprlandService = AstalHyprland.get_default();
* It maintains internal state for monitors that have already been used so that duplicate assignments are avoided.
*/
export class GdkMonitorMapper {
private usedGdkMonitors: Set<number>;
private usedHyprlandMonitors: Set<number>;
private _usedGdkMonitors: Set<number>;
private _usedHyprlandMonitors: Set<number>;
constructor() {
this.usedGdkMonitors = new Set();
this.usedHyprlandMonitors = new Set();
this._usedGdkMonitors = new Set();
this._usedHyprlandMonitors = new Set();
}
/**
* Resets the internal state for both GDK and Hyprland monitor mappings.
*/
public reset(): void {
this.usedGdkMonitors.clear();
this.usedHyprlandMonitors.clear();
this._usedGdkMonitors.clear();
this._usedHyprlandMonitors.clear();
}
/**
@@ -44,7 +44,7 @@ export class GdkMonitorMapper {
hyprlandMonitors,
gdkMonitor,
monitor,
this.usedHyprlandMonitors,
this._usedHyprlandMonitors,
(mon) => mon.id,
(mon, gdkMon) => this._matchMonitorKey(mon, gdkMon),
);
@@ -68,13 +68,14 @@ export class GdkMonitorMapper {
}
const hyprlandMonitors = hyprlandService.get_monitors();
const foundHyprlandMonitor = hyprlandMonitors.find((mon) => mon.id === monitor) || hyprlandMonitors[0];
const foundHyprlandMonitor =
hyprlandMonitors.find((mon) => mon.id === monitor) || hyprlandMonitors[0];
return this._matchMonitor(
gdkCandidates,
foundHyprlandMonitor,
monitor,
this.usedGdkMonitors,
this._usedGdkMonitors,
(candidate) => candidate.id,
(candidate, hyprlandMonitor) => this._matchMonitorKey(hyprlandMonitor, candidate.monitor),
);
@@ -105,7 +106,9 @@ export class GdkMonitorMapper {
// Direct match: candidate matches the source and has the same id as the target.
const directMatch = candidates.find(
(candidate) =>
compare(candidate, source) && !usedMonitors.has(getId(candidate)) && getId(candidate) === target,
compare(candidate, source) &&
!usedMonitors.has(getId(candidate)) &&
getId(candidate) === target,
);
if (directMatch !== undefined) {
@@ -202,7 +205,7 @@ export class GdkMonitorMapper {
continue;
}
const model = curMonitor.get_model() || '';
const model = curMonitor.get_model() ?? '';
const geometry = curMonitor.get_geometry();
const scaleFactor = curMonitor.get_scale_factor();

View File

@@ -1,14 +1,18 @@
import { ResourceLabelType } from 'src/lib/types/bar';
import { GenericResourceData, Postfix, UpdateHandlers } from 'src/lib/types/customModules/generic';
import { InputHandlerEventArgs, InputHandlerEvents, RunAsyncCommand } from 'src/lib/types/customModules/utils';
import { ThrottleFn } from 'src/lib/types/utils';
import { bind, Binding, execAsync, Variable } from 'astal';
import { openMenu } from 'src/components/bar/utils/menu';
import options from 'src/options';
import { Gdk } from 'astal/gtk3';
import { GtkWidget } from 'src/lib/types/widget';
import { onMiddleClick, onPrimaryClick, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { isScrollDown, isScrollUp } from 'src/lib/utils';
import { ResourceLabelType } from 'src/lib/types/bar.types';
import { UpdateHandlers, Postfix, GenericResourceData } from 'src/lib/types/customModules/generic.types';
import {
RunAsyncCommand,
InputHandlerEvents,
InputHandlerEventArgs,
} from 'src/lib/types/customModules/utils.types';
import { ThrottleFn } from 'src/lib/types/utils.types';
import { GtkWidget } from 'src/lib/types/widget.types';
const { scrollSpeed } = options.bar.customModules;
@@ -38,7 +42,12 @@ const handlePostInputUpdater = (postInputUpdater?: Variable<boolean>): void => {
* @param fn An optional callback function to handle the command output.
* @param postInputUpdater An optional Variable<boolean> that tracks the post input update state.
*/
export const runAsyncCommand: RunAsyncCommand = (cmd, events, fn, postInputUpdater?: Variable<boolean>): void => {
export const runAsyncCommand: RunAsyncCommand = (
cmd,
events,
fn,
postInputUpdater?: Variable<boolean>,
): void => {
if (cmd.startsWith('menu:')) {
const menuName = cmd.split(':')[1].trim().toLowerCase();
openMenu(events.clicked, events.event, `${menuName}menu`);
@@ -61,7 +70,8 @@ export const runAsyncCommand: RunAsyncCommand = (cmd, events, fn, postInputUpdat
* which undo the toggle.
*/
const throttledAsyncCommand = throttleInput(
(cmd, events, fn, postInputUpdater?: Variable<boolean>) => runAsyncCommand(cmd, events, fn, postInputUpdater),
(cmd, events, fn, postInputUpdater?: Variable<boolean>) =>
runAsyncCommand(cmd, events, fn, postInputUpdater),
50,
);
@@ -165,7 +175,12 @@ export const inputHandler = (
const id = self.connect('scroll-event', (self: GtkWidget, event: Gdk.Event) => {
const handleScroll = (input?: InputHandlerEventArgs): void => {
if (input) {
throttledHandler(sanitizeInput(input.cmd), { clicked: self, event }, input.fn, postInputUpdater);
throttledHandler(
sanitizeInput(input.cmd),
{ clicked: self, event },
input.fn,
postInputUpdater,
);
}
};
@@ -344,7 +359,11 @@ export const getPostfix = (sizeInBytes: number): Postfix => {
*
* @returns The rendered resource label as a string.
*/
export const renderResourceLabel = (lblType: ResourceLabelType, rmUsg: GenericResourceData, round: boolean): string => {
export const renderResourceLabel = (
lblType: ResourceLabelType,
rmUsg: GenericResourceData,
round: boolean,
): string => {
const { used, total, percentage, free } = rmUsg;
const formatFunctions = {
@@ -361,7 +380,7 @@ export const renderResourceLabel = (lblType: ResourceLabelType, rmUsg: GenericRe
const postfix = getPostfix(total);
// Determine which format function to use
const formatUsed = formatFunctions[postfix] || formatFunctions['B'];
const formatUsed = formatFunctions[postfix] ?? formatFunctions['B'];
const usedSizeFormatted = formatUsed(used, round);
if (lblType === 'used/total') {

View File

@@ -1,5 +1,5 @@
import { App, Gdk } from 'astal/gtk3';
import { GtkWidget } from 'src/lib/types/widget';
import { GtkWidget } from 'src/lib/types/widget.types';
import { calculateMenuPosition } from 'src/components/menus/shared/dropdown/locationHandler';
export const closeAllMenus = (): void => {

View File

@@ -1,4 +1,4 @@
import { BarLayout, BarLayouts } from 'src/lib/types/options';
import { BarLayout, BarLayouts } from 'src/lib/options/options.types';
/**
* Returns the bar layout configuration for a specific monitor
@@ -11,7 +11,7 @@ export const getLayoutForMonitor = (monitor: number, layouts: BarLayouts): BarLa
const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString());
const wildcard = Object.keys(layouts).find((key) => key === '*');
if (matchingKey) {
if (matchingKey !== undefined) {
return layouts[matchingKey];
}

View File

@@ -7,7 +7,7 @@ const audioService = wireplumber.audio;
const ActiveDeviceContainer = ({ children }: ActiveDeviceContainerProps): JSX.Element => {
return (
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.Devices} vertical>
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.DEVICES} vertical>
{children}
</box>
);

View File

@@ -5,11 +5,11 @@ import { bind, Variable } from 'astal';
import { isPrimaryClick } from 'src/lib/utils.js';
export enum ActiveDeviceMenu {
Devices = 'devices',
Playbacks = 'playbacks',
DEVICES = 'devices',
PLAYBACKS = 'playbacks',
}
const activeMenu: Variable<ActiveDeviceMenu> = Variable(ActiveDeviceMenu.Devices);
const activeMenu: Variable<ActiveDeviceMenu> = Variable(ActiveDeviceMenu.DEVICES);
const Header = (): JSX.Element => (
<box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}>
@@ -21,15 +21,15 @@ const Header = (): JSX.Element => (
return;
}
if (activeMenu.get() === ActiveDeviceMenu.Devices) {
activeMenu.set(ActiveDeviceMenu.Playbacks);
if (activeMenu.get() === ActiveDeviceMenu.DEVICES) {
activeMenu.set(ActiveDeviceMenu.PLAYBACKS);
} else {
activeMenu.set(ActiveDeviceMenu.Devices);
activeMenu.set(ActiveDeviceMenu.DEVICES);
}
}}
halign={Gtk.Align.END}
hexpand
label={bind(activeMenu).as((menu) => (menu === ActiveDeviceMenu.Devices ? '' : '󰤽'))}
label={bind(activeMenu).as((menu) => (menu === ActiveDeviceMenu.DEVICES ? '' : '󰤽'))}
/>
</box>
);
@@ -40,13 +40,13 @@ export const VolumeSliders = (): JSX.Element => {
<Header />
<revealer
transitionType={Gtk.RevealerTransitionType.NONE}
revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.Devices)}
revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.DEVICES)}
>
<ActiveDevices />
</revealer>
<revealer
transitionType={Gtk.RevealerTransitionType.NONE}
revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.Playbacks)}
revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.PLAYBACKS)}
>
<ActivePlaybacks />
</revealer>

View File

@@ -12,11 +12,11 @@ const NoStreams = (): JSX.Element => {
export const ActivePlaybacks = (): JSX.Element => {
return (
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.Playbacks} vertical>
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.PLAYBACKS} vertical>
<scrollable className={'menu-scroller active-playbacks-scrollable'}>
<box vertical>
{bind(audioService, 'streams').as((streams) => {
if (!streams || streams.length === 0) {
if (streams === null || streams.length === 0) {
return <NoStreams />;
}

View File

@@ -28,8 +28,8 @@ export const Slider = ({ device, type }: SliderProps): JSX.Element => {
max={type === 'playback' ? bind(raiseMaximumVolume).as((raise) => (raise ? 1.5 : 1)) : 1}
onDragged={({ value, dragging }) => {
if (dragging) {
device.volume = value;
device.mute = false;
device.set_volume(value);
device.set_mute(false);
}
}}
setup={(self) => {

View File

@@ -16,12 +16,14 @@ export const SliderIcon = ({ type, device }: SliderIconProps): JSX.Element => {
return (
<button
className={bind(device, 'mute').as((isMuted) => `menu-active-button ${type} ${isMuted ? 'muted' : ''}`)}
className={bind(device, 'mute').as(
(isMuted) => `menu-active-button ${type} ${isMuted ? 'muted' : ''}`,
)}
vexpand={false}
valign={Gtk.Align.END}
onClick={(_, event) => {
if (isPrimaryClick(event)) {
device.mute = !device.mute;
device.set_mute(!device.mute);
}
}}
onDestroy={() => {

View File

@@ -20,7 +20,9 @@ const DeviceName = ({ device, type }: Omit<AudioDeviceProps, 'icon'>): JSX.Eleme
truncate
wrap
className={bind(device, 'description').as((currentDesc) =>
device.description === currentDesc ? `menu-button-name active ${type}` : `menu-button-name ${type}`,
device.description === currentDesc
? `menu-button-name active ${type}`
: `menu-button-name ${type}`,
)}
label={device.description}
/>

View File

@@ -13,7 +13,7 @@ export const InputDevices = (): JSX.Element => {
<box className={'menu-items-section input'} vertical>
<box className={'menu-container input'} vertical>
{inputDevices.as((devices) => {
if (!devices || devices.length === 0) {
if (devices === null || devices.length === 0) {
return <NotFoundButton type={'input'} />;
}

View File

@@ -13,7 +13,7 @@ export const PlaybackDevices = (): JSX.Element => {
<box className={'menu-items-section playback'} vertical>
<box className={'menu-container playback'} vertical>
{playbackDevices.as((devices) => {
if (!devices || devices.length === 0) {
if (devices === null || devices.length === 0) {
return <NotFoundButton type={'playback'} />;
}

View File

@@ -29,7 +29,7 @@ type IconVolumes = keyof typeof speakerIcons;
*/
const getIcon = (audioVol: number, isMuted: boolean): Record<string, string> => {
const thresholds: IconVolumes[] = [101, 66, 34, 1, 0];
const icon = isMuted ? 0 : thresholds.find((threshold) => threshold <= audioVol * 100) || 0;
const icon = isMuted ? 0 : (thresholds.find((threshold) => threshold <= audioVol * 100) ?? 0);
return {
spkr: speakerIcons[icon],

View File

@@ -2,7 +2,13 @@ import { Gtk } from 'astal/gtk3';
export const BluetoothDisabled = (): JSX.Element => {
return (
<box className={'bluetooth-items'} vertical expand valign={Gtk.Align.CENTER} halign={Gtk.Align.CENTER}>
<box
className={'bluetooth-items'}
vertical
expand
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
>
<label className={'bluetooth-disabled dim'} hexpand label={'Bluetooth is disabled'} />
</box>
);

View File

@@ -2,7 +2,13 @@ import { Gtk } from 'astal/gtk3';
export const NoBluetoothDevices = (): JSX.Element => {
return (
<box className={'bluetooth-items'} vertical expand valign={Gtk.Align.CENTER} halign={Gtk.Align.CENTER}>
<box
className={'bluetooth-items'}
vertical
expand
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
>
<label className={'no-bluetooth-devices dim'} hexpand label={'No devices currently found'} />
<label className={'search-bluetooth-label dim'} hexpand label={"Press '󰑐' to search"} />
</box>

View File

@@ -1,7 +1,12 @@
import { Binding } from 'astal';
import { ButtonProps } from 'astal/gtk3/widget';
export const ActionButton = ({ name = '', tooltipText = '', label = '', ...props }: ActionButtonProps): JSX.Element => {
export const ActionButton = ({
name = '',
tooltipText = '',
label = '',
...props
}: ActionButtonProps): JSX.Element => {
return (
<button className={`menu-icon-button ${name} bluetooth`} {...props}>
<label

View File

@@ -41,7 +41,9 @@ export const getAvailableBluetoothDevices = (): AstalBluetooth.Device[] => {
*/
export const getConnectedBluetoothDevices = (): string[] => {
const availableDevices = getAvailableBluetoothDevices();
const connectedDeviceNames = availableDevices.filter((d) => d.connected || d.paired).map((d) => d.address);
const connectedDeviceNames = availableDevices
.filter((d) => d.connected || d.paired)
.map((d) => d.address);
return connectedDeviceNames;
};

View File

@@ -10,7 +10,7 @@ Variable.derive([bind(bluetoothService, 'adapter')], () => {
discoveringBinding?.drop();
discoveringBinding = undefined;
if (!bluetoothService.adapter) {
if (bluetoothService.adapter === null) {
return;
}

View File

@@ -3,7 +3,14 @@ import { Controls } from './Controls';
export const Header = (): JSX.Element => {
const MenuLabel = (): JSX.Element => {
return <label className="menu-label" valign={Gtk.Align.CENTER} halign={Gtk.Align.START} label="Bluetooth" />;
return (
<label
className="menu-label"
valign={Gtk.Align.CENTER}
halign={Gtk.Align.START}
label="Bluetooth"
/>
);
};
return (

View File

@@ -3,7 +3,12 @@ import Calendar from 'src/components/shared/Calendar';
export const CalendarWidget = (): JSX.Element => {
return (
<box className={'calendar-menu-item-container calendar'} halign={Gtk.Align.FILL} valign={Gtk.Align.FILL} expand>
<box
className={'calendar-menu-item-container calendar'}
halign={Gtk.Align.FILL}
valign={Gtk.Align.FILL}
expand
>
<box className={'calendar-container-box'}>
<Calendar
className={'calendar-menu-widget'}

View File

@@ -1,7 +1,7 @@
import options from 'src/options';
import { bind, Variable } from 'astal';
import { Gtk } from 'astal/gtk3';
import { systemTime } from 'src/globals/time';
import { systemTime } from 'src/shared/time';
const { military, hideSeconds } = options.menus.clock.time;
@@ -16,7 +16,7 @@ export const MilitaryTime = (): JSX.Element => {
<label
className={'clock-content-time'}
label={bind(systemTime).as((time) => {
return time?.format(hideSeconds ? '%H:%M' : '%H:%M:%S') || '';
return time?.format(hideSeconds ? '%H:%M' : '%H:%M:%S') ?? '';
})}
/>
</box>

View File

@@ -1,11 +1,11 @@
import options from 'src/options';
import { bind, GLib, Variable } from 'astal';
import { Gtk } from 'astal/gtk3';
import { systemTime } from 'src/globals/time';
import { systemTime } from 'src/shared/time';
const { military, hideSeconds } = options.menus.clock.time;
const period = Variable('').poll(1000, (): string => GLib.DateTime.new_now_local().format('%p') || '');
const period = Variable('').poll(1000, (): string => GLib.DateTime.new_now_local().format('%p') ?? '');
export const StandardTime = (): JSX.Element => {
const CurrentTime = ({ hideSeconds }: CurrentTimeProps): JSX.Element => {
@@ -14,7 +14,7 @@ export const StandardTime = (): JSX.Element => {
<label
className={'clock-content-time'}
label={bind(systemTime).as((time) => {
return time?.format(hideSeconds ? '%I:%M' : '%I:%M:%S') || '';
return time?.format(hideSeconds ? '%I:%M' : '%I:%M:%S') ?? '';
})}
/>
</box>

View File

@@ -4,8 +4,18 @@ import { StandardTime } from './StandardTime';
export const TimeWidget = (): JSX.Element => {
return (
<box className={'calendar-menu-item-container clock'} valign={Gtk.Align.CENTER} halign={Gtk.Align.FILL} hexpand>
<box className={'clock-content-items'} valign={Gtk.Align.CENTER} halign={Gtk.Align.CENTER} hexpand>
<box
className={'calendar-menu-item-container clock'}
valign={Gtk.Align.CENTER}
halign={Gtk.Align.FILL}
hexpand
>
<box
className={'clock-content-items'}
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
hexpand
>
<StandardTime />
<MilitaryTime />
</box>

View File

@@ -1,5 +1,5 @@
import { isValidWeatherIconTitle } from 'src/globals/weather';
import { Weather, WeatherIconTitle } from 'src/lib/types/weather';
import { Weather, WeatherIconTitle } from 'src/lib/types/weather.types';
import { isValidWeatherIconTitle } from 'src/shared/weather';
/**
* Retrieves the next epoch time for weather data.

View File

@@ -1,5 +1,5 @@
import { bind } from 'astal';
import { globalWeatherVar } from 'src/globals/weather';
import { globalWeatherVar } from 'src/shared/weather';
import { Gtk } from 'astal/gtk3';
import { weatherIcons } from 'src/lib/icons/weather.js';
import { getIconQuery } from '../helpers';

View File

@@ -1,5 +1,5 @@
import options from 'src/options';
import { globalWeatherVar } from 'src/globals/weather';
import { globalWeatherVar } from 'src/shared/weather';
import { getNextEpoch } from '../helpers';
import { bind, Variable } from 'astal';

View File

@@ -1,4 +1,4 @@
import { globalWeatherVar } from 'src/globals/weather';
import { globalWeatherVar } from 'src/shared/weather';
import { getNextEpoch } from '../helpers';
import { bind } from 'astal';

View File

@@ -1,6 +1,6 @@
import { bind } from 'astal';
import { Gtk } from 'astal/gtk3';
import { getWeatherStatusTextIcon } from 'src/globals/weather.js';
import { getWeatherStatusTextIcon, globalWeatherVar } from 'src/shared/weather';
export const TodayIcon = (): JSX.Element => {
return (
@@ -11,7 +11,7 @@ export const TodayIcon = (): JSX.Element => {
>
<label
className={'calendar-menu-weather today icon txt-icon'}
label={bind(globalWeatherVar).as(getWeatherStatusTextIcon)}
label={bind(globalWeatherVar).as((weather) => getWeatherStatusTextIcon(weather))}
/>
</box>
);

View File

@@ -1,6 +1,6 @@
import { getTemperature, globalWeatherVar } from 'src/globals/weather';
import { getTemperature, globalWeatherVar } from 'src/shared/weather';
import options from 'src/options';
import { getRainChance } from 'src/globals/weather';
import { getRainChance } from 'src/shared/weather';
import { Gtk } from 'astal/gtk3';
import { bind, Variable } from 'astal';

View File

@@ -1,6 +1,6 @@
import options from 'src/options';
import { globalWeatherVar } from 'src/globals/weather';
import { getTemperature, getWeatherIcon } from 'src/globals/weather';
import { globalWeatherVar } from 'src/shared/weather';
import { getTemperature, getWeatherIcon } from 'src/shared/weather';
import { Gtk } from 'astal/gtk3';
import { bind, Variable } from 'astal';
const { unit } = options.menus.clock.weather;
@@ -35,7 +35,9 @@ const Temperature = (): JSX.Element => {
(weather) =>
`calendar-menu-weather today temp label icon txt-icon ${getWeatherIcon(Math.ceil(weather.current.temp_f)).color}`,
)}
label={bind(globalWeatherVar).as((weather) => getWeatherIcon(Math.ceil(weather.current.temp_f)).icon)}
label={bind(globalWeatherVar).as(
(weather) => getWeatherIcon(Math.ceil(weather.current.temp_f)).icon,
)}
/>
);
};

View File

@@ -18,7 +18,9 @@ const notifdService = AstalNotifd.get_default();
export const WifiButton = (): JSX.Element => {
return (
<button
className={bind(isWifiEnabled).as((isEnabled) => `dashboard-button wifi ${!isEnabled ? 'disabled' : ''}`)}
className={bind(isWifiEnabled).as(
(isEnabled) => `dashboard-button wifi ${!isEnabled ? 'disabled' : ''}`,
)}
onClick={(_, event) => {
if (isPrimaryClick(event)) {
networkService.wifi?.set_enabled(!networkService.wifi.enabled);
@@ -27,7 +29,10 @@ export const WifiButton = (): JSX.Element => {
tooltipText={'Toggle Wifi'}
expand
>
<label className={'txt-icon'} label={bind(isWifiEnabled).as((isEnabled) => (isEnabled ? '󰤨' : '󰤭'))} />
<label
className={'txt-icon'}
label={bind(isWifiEnabled).as((isEnabled) => (isEnabled ? '󰤨' : '󰤭'))}
/>
</button>
);
};
@@ -68,7 +73,10 @@ export const NotificationsButton = (): JSX.Element => {
tooltipText={'Toggle Notifications'}
expand
>
<label className={'txt-icon'} label={bind(notifdService, 'dontDisturb').as((dnd) => (dnd ? '󰂛' : '󰂚'))} />
<label
className={'txt-icon'}
label={bind(notifdService, 'dontDisturb').as((dnd) => (dnd ? '󰂛' : '󰂚'))}
/>
</button>
);
};

View File

@@ -10,7 +10,7 @@ Variable.derive([bind(networkService, 'wifi')], () => {
wifiEnabledBinding?.drop();
wifiEnabledBinding = undefined;
if (!networkService.wifi) {
if (networkService.wifi === null) {
return;
}

Some files were not shown because too many files have changed in this diff Show More