Implemented strict linting standards and prettier formatting config. (#248)

* Implemented strict linting standards and prettier formatting config.

* More linter fixes and type updates.

* More linter updates and type fixes

* Remove noisy comments

* Linter and type updates

* Linter, formatting and type updates.

* Linter updates

* Type updates

* Type updates

* fixed all linter errors

* Fixed all linting, formatting and type issues.

* Resolve merge conflicts.
This commit is contained in:
Jas Singh
2024-09-14 16:20:05 -07:00
committed by GitHub
parent ff13e3dd3c
commit 2c72cc66d8
222 changed files with 13141 additions and 8433 deletions

2
.eslintignore Normal file
View File

@@ -0,0 +1,2 @@
types
node_modules

25
.eslintrc.js Normal file
View File

@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
root: true,
ignorePatterns: ['.eslintrc.js', 'types/**/*.ts'],
env: {
es6: true,
browser: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'import/extensions': ['off'],
'import/no-unresolved': 'off',
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
},
};

View File

@@ -1,130 +0,0 @@
env:
es2022: true
extends:
- "eslint:recommended"
- "plugin:@typescript-eslint/recommended"
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: 2022
sourceType: "module"
project: "./tsconfig.json"
warnOnUnsupportedTypeScriptVersion: false
root: true
ignorePatterns:
- types/
plugins:
- "@typescript-eslint"
rules:
"@typescript-eslint/ban-ts-comment":
- "off"
"@typescript-eslint/no-non-null-assertion":
- "off"
# "@typescript-eslint/no-explicit-any":
# - "off"
"@typescript-eslint/no-unused-vars":
- error
- varsIgnorePattern: (^unused|_$)
argsIgnorePattern: ^(unused|_)
"@typescript-eslint/no-empty-interface":
- "off"
arrow-parens:
- error
- as-needed
comma-dangle:
- error
- always-multiline
comma-spacing:
- error
- before: false
after: true
comma-style:
- error
- last
curly:
- error
- multi-or-nest
- consistent
dot-location:
- error
- property
eol-last:
- error
eqeqeq:
- error
- always
indent:
- error
- 4
- SwitchCase: 1
keyword-spacing:
- error
- before: true
lines-between-class-members:
- error
- always
- exceptAfterSingleLine: true
padded-blocks:
- error
- never
- allowSingleLineBlocks: false
prefer-const:
- error
quotes:
- error
- double
- avoidEscape: true
semi:
- error
- never
nonblock-statement-body-position:
- error
- below
no-trailing-spaces:
- error
no-useless-escape:
- off
max-len:
- error
- code: 100
func-call-spacing:
- error
array-bracket-spacing:
- error
space-before-function-paren:
- error
- anonymous: never
named: never
asyncArrow: ignore
space-before-blocks:
- error
key-spacing:
- error
object-curly-spacing:
- error
- always
globals:
Widget: readonly
Utils: readonly
App: readonly
Variable: readonly
Service: readonly
pkg: readonly
ARGV: readonly
Debugger: readonly
GIRepositoryGType: readonly
globalThis: readonly
imports: readonly
Intl: readonly
log: readonly
logError: readonly
print: readonly
printerr: readonly
window: readonly
TextEncoder: readonly
TextDecoder: readonly
console: readonly
setTimeout: readonly
setInterval: readonly
clearTimeout: readonly
clearInterval: readonly

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.weather.json
node_modules

2
.prettierignore Normal file
View File

@@ -0,0 +1,2 @@
.eslintrc.js
types/**/*.ts

8
.prettierrc Normal file
View File

@@ -0,0 +1,8 @@
{
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"printWidth": 120,
"tabWidth": 4,
"useTabs": false
}

View File

@@ -1,46 +1,58 @@
import GLib from "gi://GLib"
import GLib from 'gi://GLib';
const main = "/tmp/ags/hyprpanel/main.js"
const entry = `${App.configDir}/main.ts`
const bundler = GLib.getenv("AGS_BUNDLER") || "bun"
const main = '/tmp/ags/hyprpanel/main.js';
const entry = `${App.configDir}/main.ts`;
const bundler = GLib.getenv('AGS_BUNDLER') || 'bun';
const v = {
ags: pkg.version?.split(".").map(Number) || [],
ags: pkg.version?.split('.').map(Number) || [],
expect: [1, 8, 1],
}
};
try {
switch (bundler) {
case "bun": await Utils.execAsync([
"bun", "build", entry,
"--outfile", main,
"--external", "resource://*",
"--external", "gi://*",
"--external", "file://*",
]); break
case 'bun':
await Utils.execAsync([
'bun',
'build',
entry,
'--outfile',
main,
'--external',
'resource://*',
'--external',
'gi://*',
'--external',
'file://*',
]);
break;
case "esbuild": await Utils.execAsync([
"esbuild", "--bundle", entry,
"--format=esm",
`--outfile=${main}`,
"--external:resource://*",
"--external:gi://*",
"--external:file://*",
]); break
case 'esbuild':
await Utils.execAsync([
'esbuild',
'--bundle',
entry,
'--format=esm',
`--outfile=${main}`,
'--external:resource://*',
'--external:gi://*',
'--external:file://*',
]);
break;
default:
throw `"${bundler}" is not a valid bundler`
throw `"${bundler}" is not a valid bundler`;
}
if (v.ags[1] < v.expect[1] || v.ags[2] < v.expect[2]) {
print(`HyprPanel needs atleast v${v.expect.join(".")} of AGS, yours is v${v.ags.join(".")}`)
App.quit()
print(`HyprPanel needs atleast v${v.expect.join('.')} of AGS, yours is v${v.ags.join('.')}`);
App.quit();
}
await import(`file://${main}`)
await import(`file://${main}`);
} catch (error) {
console.error(error)
App.quit()
console.error(error);
App.quit();
}
export { }
export {};

View File

@@ -1,26 +1,25 @@
import GLib from "gi://GLib?version=2.0";
import { Binding } from "types/service";
import { Variable as VariableType } from "types/variable";
type GenericFunction = (...args: any[]) => any;
import GLib from 'gi://GLib?version=2.0';
import { GenericFunction } from 'lib/types/customModules/generic';
import { Bind } from 'lib/types/variable';
import { Variable as VariableType } from 'types/variable';
/**
* @param {VariableType<T>} targetVariable - The Variable to update with the function's result.
* @param {Array<VariableType<any>>} trackers - Array of trackers to watch.
* @param {Binding<any, any, unknown>} pollingInterval - The polling interval in milliseconds.
* @param {GenericFunction} someFunc - The function to execute at each interval, which updates the Variable.
* @param {...any} params - Parameters to pass to someFunc.
* @param {Array<Bind>} trackers - Array of trackers to watch.
* @param {Bind} pollingInterval - The polling interval in milliseconds.
* @param {GenericFunction<T, P>} someFunc - The function to execute at each interval, which updates the Variable.
* @param {...P} params - Parameters to pass to someFunc.
*/
export const pollVariable = <T>(
export const pollVariable = <T, P extends unknown[], F extends GenericFunction<T, P>>(
targetVariable: VariableType<T>,
trackers: Array<Binding<any, any, unknown>>,
pollingInterval: Binding<any, any, unknown>,
someFunc: GenericFunction,
...params: any[]
trackers: Array<Bind>,
pollingInterval: Bind,
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number) => {
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
@@ -37,34 +36,37 @@ export const pollVariable = <T>(
/**
* @param {VariableType<T>} targetVariable - The Variable to update with the result of the command.
* @param {Binding<any, any, unknown>} pollingInterval - The polling interval in milliseconds.
* @param {Array<Bind>} trackers - Array of trackers to watch.
* @param {Bind} pollingInterval - The polling interval in milliseconds.
* @param {string} someCommand - The bash command to execute.
* @param {GenericFunction} someFunc - The function to execute after processing the command result.
* @param {...any} params - Parameters to pass to someFunc.
* @param {GenericFunction<T, [unknown, ...P]>} someFunc - The function to execute after processing the command result;
* with the first argument being the result of the command execution.
* @param {...P} params - Additional parameters to pass to someFunc.
*/
export const pollVariableBash = <T>(
export const pollVariableBash = <T, P extends unknown[], F extends GenericFunction<T, [string, ...P]>>(
targetVariable: VariableType<T>,
trackers: Array<Binding<any, any, unknown>>,
pollingInterval: Binding<any, any, unknown>,
trackers: Array<Bind>,
pollingInterval: Bind,
someCommand: string,
someFunc: (res: any, ...params: any[]) => T,
...params: any[]
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number) => {
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
intervalInstance = Utils.interval(pollIntrvl, () => {
Utils.execAsync(`bash -c "${someCommand}"`).then((res: any) => {
try {
targetVariable.value = someFunc(res, ...params);
} catch (error) {
console.warn(`An error occurred when running interval bash function: ${error}`);
}
})
Utils.execAsync(`bash -c "${someCommand}"`)
.then((res: string) => {
try {
targetVariable.value = someFunc(res, ...params);
} catch (error) {
console.warn(`An error occurred when running interval bash function: ${error}`);
}
})
.catch((err) => console.error(`Error running command "${someCommand}": ${err}`));
});
};

View File

@@ -2,8 +2,10 @@ import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, GtkWidget } from 'lib/types/widget';
export const CustomModuleSettings = () =>
export const CustomModuleSettings = (): Scrollable<GtkWidget, Attribute> =>
Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
@@ -12,11 +14,11 @@ export const CustomModuleSettings = () =>
class_name: 'menu-theme-page paged-container',
vertical: true,
children: [
/*
************************************
* GENERAL *
************************************
*/
/*
************************************
* GENERAL *
************************************
*/
Header('General'),
Option({
opt: options.bar.customModules.scrollSpeed,
@@ -24,11 +26,11 @@ export const CustomModuleSettings = () =>
type: 'number',
}),
/*
************************************
* RAM *
************************************
*/
/*
************************************
* RAM *
************************************
*/
Header('RAM'),
Option({
opt: options.bar.customModules.ram.label,
@@ -75,11 +77,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* CPU *
************************************
*/
/*
************************************
* CPU *
************************************
*/
Header('CPU'),
Option({
opt: options.bar.customModules.cpu.label,
@@ -130,11 +132,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* STORAGE *
************************************
*/
/*
************************************
* STORAGE *
************************************
*/
Header('Storage'),
Option({
opt: options.bar.customModules.storage.icon,
@@ -187,11 +189,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* NETSTAT *
************************************
*/
/*
************************************
* NETSTAT *
************************************
*/
Header('Netstat'),
Option({
opt: options.bar.customModules.netstat.networkInterface,
@@ -204,17 +206,7 @@ export const CustomModuleSettings = () =>
opt: options.bar.customModules.netstat.icon,
title: 'Netstat Icon',
type: 'enum',
enums: [
'󰖟',
'󰇚',
'󰕒',
'󰛳',
'',
'󰣺',
'󰖩',
'',
'󰈀',
],
enums: ['󰖟', '󰇚', '󰕒', '󰛳', '', '󰣺', '󰖩', '', '󰈀'],
}),
Option({
opt: options.bar.customModules.netstat.label,
@@ -267,11 +259,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* KEYBOARD LAYOUT *
************************************
*/
/*
************************************
* KEYBOARD LAYOUT *
************************************
*/
Header('Keyboard Layout'),
Option({
opt: options.bar.customModules.kbLayout.icon,
@@ -321,11 +313,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* UPDATES *
************************************
*/
/*
************************************
* UPDATES *
************************************
*/
Header('Updates'),
Option({
opt: options.bar.customModules.updates.updateCommand,
@@ -336,17 +328,7 @@ export const CustomModuleSettings = () =>
opt: options.bar.customModules.updates.icon,
title: 'Updates Icon',
type: 'enum',
enums: [
'󰚰',
'󰇚',
'',
'󱑢',
'󱑣',
'󰏖',
'',
'󰏔',
'󰏗',
],
enums: ['󰚰', '󰇚', '', '󱑢', '󱑣', '󰏖', '', '󰏔', '󰏗'],
}),
Option({
opt: options.bar.customModules.updates.label,
@@ -367,7 +349,7 @@ export const CustomModuleSettings = () =>
opt: options.bar.customModules.updates.pollingInterval,
title: 'Polling Interval',
type: 'number',
subtitle: "WARNING: Be careful of your package manager\'s rate limit.",
subtitle: "WARNING: Be careful of your package manager's rate limit.",
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
@@ -398,11 +380,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* WEATHER *
************************************
*/
/*
************************************
* WEATHER *
************************************
*/
Header('Weather'),
Option({
opt: options.bar.customModules.weather.label,
@@ -446,11 +428,11 @@ export const CustomModuleSettings = () =>
type: 'string',
}),
/*
************************************
* POWER *
************************************
*/
/*
************************************
* POWER *
************************************
*/
Header('Power'),
Option({
opt: options.theme.bar.buttons.modules.power.spacing,
@@ -491,4 +473,3 @@ export const CustomModuleSettings = () =>
],
}),
});

View File

@@ -1,10 +1,10 @@
// @ts-expect-error
// @ts-expect-error: This import is a special directive that tells the compiler to use the GTop library
import GTop from 'gi://GTop';
let previousCpuData = new GTop.glibtop_cpu();
GTop.glibtop_get_cpu(previousCpuData);
export const computeCPU = () => {
export const computeCPU = (): number => {
const currentCpuData = new GTop.glibtop_cpu();
GTop.glibtop_get_cpu(currentCpuData);
@@ -17,5 +17,4 @@ export const computeCPU = () => {
previousCpuData = currentCpuData;
return cpuUsagePercentage;
}
};

View File

@@ -1,28 +1,21 @@
import options from "options";
import options from 'options';
// Module initializer
import { module } from "../module"
import { module } from '../module';
// import { CpuData } from "lib/types/customModules/cpu";
import Button from "types/widgets/button";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from 'types/widgets/button';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
// Utility Methods
import { inputHandler } from "customModules/utils";
import { computeCPU } from "./computeCPU";
import { pollVariable } from "customModules/PollVar";
import { inputHandler } from 'customModules/utils';
import { computeCPU } from './computeCPU';
import { pollVariable } from 'customModules/PollVar';
import { Module } from 'lib/types/bar';
// All the user configurable options for the cpu module that are needed
const {
label,
round,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
pollingInterval
} = options.bar.customModules.cpu;
const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown, pollingInterval } =
options.bar.customModules.cpu;
export const cpuUsage = Variable(0);
@@ -37,21 +30,19 @@ pollVariable(
computeCPU,
);
export const Cpu = () => {
const renderLabel = (cpuUsg: number, rnd: boolean) => {
export const Cpu = (): Module => {
const renderLabel = (cpuUsg: number, rnd: boolean): string => {
return rnd ? `${Math.round(cpuUsg)}%` : `${cpuUsg.toFixed(2)}%`;
}
};
const cpuModule = module({
textIcon: "",
label: Utils.merge(
[cpuUsage.bind("value"), round.bind("value")],
(cpuUsg, rnd) => {
return renderLabel(cpuUsg, rnd);
}),
tooltipText: "CPU",
boxClass: "cpu",
showLabelBinding: label.bind("value"),
textIcon: '',
label: Utils.merge([cpuUsage.bind('value'), round.bind('value')], (cpuUsg, rnd) => {
return renderLabel(cpuUsg, rnd);
}),
tooltipText: 'CPU',
boxClass: 'cpu',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -72,9 +63,8 @@ export const Cpu = () => {
},
});
},
}
},
});
return cpuModule;
}
};

View File

@@ -1,12 +1,18 @@
import { HyprctlDeviceLayout, HyprctlKeyboard, KbLabelType, LayoutKeys, LayoutValues } from "lib/types/customModules/kbLayout";
import { layoutMap } from "./layouts";
import {
HyprctlDeviceLayout,
HyprctlKeyboard,
KbLabelType,
LayoutKeys,
LayoutValues,
} from 'lib/types/customModules/kbLayout';
import { layoutMap } from './layouts';
export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
let hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj);
let keyboards = hyprctlDevices['keyboards'];
export const getKeyboardLayout = (obj: string, format: KbLabelType): LayoutKeys | LayoutValues => {
const hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj);
const keyboards = hyprctlDevices['keyboards'];
if (keyboards.length === 0) {
return "No KB!"
return format === 'code' ? 'Unknown' : 'Unknown Layout';
}
let mainKb = keyboards.find((kb: HyprctlKeyboard) => kb.main);
@@ -15,8 +21,8 @@ export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
mainKb = keyboards[keyboards.length - 1];
}
let layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys;
const layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys;
const foundLayout: LayoutValues = layoutMap[layout];
return format === "code" ? foundLayout || layout : layout;
}
return format === 'code' ? foundLayout || layout : layout;
};

View File

@@ -1,49 +1,50 @@
const hyprland = await Service.import("hyprland");
const hyprland = await Service.import('hyprland');
import options from "options";
import { module } from "../module"
import options from 'options';
import { module } from '../module';
import { inputHandler } from "customModules/utils";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from "types/widgets/button";
import Label from "types/widgets/label";
import { getKeyboardLayout } from "./getLayout";
import { inputHandler } from 'customModules/utils';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import Label from 'types/widgets/label';
import { getKeyboardLayout } from './getLayout';
import { Module } from 'lib/types/bar';
const {
label,
labelType,
icon,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
} = options.bar.customModules.kbLayout;
const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } =
options.bar.customModules.kbLayout;
export const KbInput = () => {
export const KbInput = (): Module => {
const keyboardModule = module({
textIcon: icon.bind("value"),
tooltipText: "",
textIcon: icon.bind('value'),
tooltipText: '',
labelHook: (self: Label<Gtk.Widget>): void => {
self.hook(hyprland, () => {
Utils.execAsync('hyprctl devices -j')
.then((obj) => {
self.label = getKeyboardLayout(obj, labelType.value);
})
.catch((err) => { console.error(err); });
}, "keyboard-layout");
self.hook(
hyprland,
() => {
Utils.execAsync('hyprctl devices -j')
.then((obj) => {
self.label = getKeyboardLayout(obj, labelType.value);
})
.catch((err) => {
console.error(err);
});
},
'keyboard-layout',
);
self.hook(labelType, () => {
Utils.execAsync('hyprctl devices -j')
.then((obj) => {
self.label = getKeyboardLayout(obj, labelType.value);
})
.catch((err) => { console.error(err); });
.catch((err) => {
console.error(err);
});
});
},
boxClass: "kblayout",
showLabelBinding: label.bind("value"),
boxClass: 'kblayout',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -68,6 +69,4 @@ export const KbInput = () => {
});
return keyboardModule;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
import { Module } from "lib/types/bar";
import { BarButtonStyles } from "lib/types/options";
import options from "options";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import { Binding } from "types/service";
import { Variable as VariableType } from "types/variable";
import { BarBoxChild, Module } from 'lib/types/bar';
import { BarButtonStyles } from 'lib/types/options';
import { Bind } from 'lib/types/variable';
import { GtkWidget } from 'lib/types/widget';
import options from 'options';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
const { style } = options.theme.bar.buttons;
@@ -16,69 +16,69 @@ export const module = ({
tooltipText,
boxClass,
props = {},
showLabelBinding = undefinedVar.bind("value"),
showLabelBinding = undefinedVar.bind('value'),
showLabel,
labelHook,
hook
}: Module) => {
const getIconWidget = () => {
hook,
}: Module): BarBoxChild => {
const getIconWidget = (): GtkWidget | undefined => {
let iconWidget: Gtk.Widget | undefined;
if (icon !== undefined) {
iconWidget = Widget.Icon({
class_name: `txt-icon bar-button-icon module-icon ${boxClass}`,
icon: icon
icon: icon,
}) as unknown as Gtk.Widget;
} else if (textIcon !== undefined) {
iconWidget = Widget.Label({
class_name: `txt-icon bar-button-icon module-icon ${boxClass}`,
label: textIcon
label: textIcon,
}) as unknown as Gtk.Widget;
}
return iconWidget;
}
};
return {
component: Widget.Box({
className: Utils.merge([style.bind("value"), showLabelBinding], (style: BarButtonStyles, shwLabel: boolean) => {
const shouldShowLabel = shwLabel || showLabel;
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `${boxClass} ${styleMap[style]} ${!shouldShowLabel ? "no-label" : ""}`;
}),
className: Utils.merge(
[style.bind('value'), showLabelBinding],
(style: BarButtonStyles, shwLabel: boolean) => {
const shouldShowLabel = shwLabel || showLabel;
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `${boxClass} ${styleMap[style]} ${!shouldShowLabel ? 'no-label' : ''}`;
},
),
tooltip_text: tooltipText,
children: Utils.merge(
[showLabelBinding],
(showLabelBinding): Gtk.Widget[] => {
const childrenArray: Gtk.Widget[] = [];
const iconWidget = getIconWidget();
children: Utils.merge([showLabelBinding], (showLabelBinding): Gtk.Widget[] => {
const childrenArray: Gtk.Widget[] = [];
const iconWidget = getIconWidget();
if (iconWidget !== undefined) {
childrenArray.push(iconWidget);
}
if (showLabelBinding) {
childrenArray.push(
Widget.Label({
class_name: `bar-button-label module-label ${boxClass}`,
label: label,
setup: labelHook,
}) as unknown as Gtk.Widget
);
}
return childrenArray;
if (iconWidget !== undefined) {
childrenArray.push(iconWidget);
}
) as Binding<VariableType<Gtk.Widget[]>, any, Gtk.Widget[]>,
if (showLabelBinding) {
childrenArray.push(
Widget.Label({
class_name: `bar-button-label module-label ${boxClass}`,
label: label,
setup: labelHook,
}) as unknown as Gtk.Widget,
);
}
return childrenArray;
}) as Bind,
setup: hook,
}),
tooltip_text: tooltipText,
isVisible: true,
boxClass,
props
props,
};
};

View File

@@ -71,7 +71,11 @@ const getNetworkUsage = (interfaceName: string = ''): NetworkUsage => {
return { name: '', rx: 0, tx: 0 };
};
export const computeNetwork = (round: VariableType<boolean>, interfaceNameVar: VariableType<string>, dataType: VariableType<RateUnit>): NetworkResourceData => {
export const computeNetwork = (
round: VariableType<boolean>,
interfaceNameVar: VariableType<string>,
dataType: VariableType<RateUnit>,
): NetworkResourceData => {
const rateUnit = dataType.value;
const interfaceName = interfaceNameVar ? interfaceNameVar.value : '';

View File

@@ -2,7 +2,7 @@ import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import { computeNetwork } from './computeNetwork';
import { NetstatLabelType } from 'lib/types/bar';
import { Module, NetstatLabelType } from 'lib/types/bar';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import { NetworkResourceData } from 'lib/types/customModules/network';
@@ -23,9 +23,7 @@ const {
pollingInterval,
} = options.bar.customModules.netstat;
export const networkUsage = Variable<NetworkResourceData>(
GET_DEFAULT_NETSTAT_DATA(rateUnit.value),
);
export const networkUsage = Variable<NetworkResourceData>(GET_DEFAULT_NETSTAT_DATA(rateUnit.value));
pollVariable(
// Variable to poll and update with the result of the function passed in
@@ -48,11 +46,8 @@ pollVariable(
rateUnit,
);
export const Netstat = () => {
const renderNetworkLabel = (
lblType: NetstatLabelType,
network: NetworkResourceData,
): string => {
export const Netstat = (): Module => {
const renderNetworkLabel = (lblType: NetstatLabelType, network: NetworkResourceData): string => {
switch (lblType) {
case 'in':
return `${network.in}`;
@@ -88,19 +83,17 @@ export const Netstat = () => {
},
onScrollUp: {
fn: () => {
labelType.value =
NETWORK_LABEL_TYPES[
labelType.value = NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.value) + 1) % NETWORK_LABEL_TYPES.length
] as NetstatLabelType;
] as NetstatLabelType;
},
},
onScrollDown: {
fn: () => {
labelType.value =
NETWORK_LABEL_TYPES[
labelType.value = NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.value) - 1 + NETWORK_LABEL_TYPES.length) %
NETWORK_LABEL_TYPES.length
] as NetstatLabelType;
NETWORK_LABEL_TYPES.length
] as NetstatLabelType;
},
},
});
@@ -110,4 +103,3 @@ export const Netstat = () => {
return netstatModule;
};

View File

@@ -1,24 +1,18 @@
import options from "options";
import { module } from "../module"
import options from 'options';
import { module } from '../module';
import { inputHandler } from "customModules/utils";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from "types/widgets/button";
import { inputHandler } from 'customModules/utils';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import { Module } from 'lib/types/bar';
const {
icon,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
} = options.bar.customModules.power;
const { icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.power;
export const Power = () => {
export const Power = (): Module => {
const powerModule = module({
tooltipText: "Power Menu",
textIcon: icon.bind("value"),
boxClass: "powermodule",
tooltipText: 'Power Menu',
textIcon: icon.bind('value'),
boxClass: 'powermodule',
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -43,4 +37,4 @@ export const Power = () => {
});
return powerModule;
}
};

View File

@@ -1,9 +1,10 @@
const GLib = imports.gi.GLib;
import { divide } from 'customModules/utils';
import { GenericResourceData } from 'lib/types/customModules/generic';
import { Variable as VariableType } from 'types/variable';
export const calculateRamUsage = (round: VariableType<boolean>) => {
export const calculateRamUsage = (round: VariableType<boolean>): GenericResourceData => {
try {
const [success, meminfoBytes] = GLib.file_get_contents('/proc/meminfo');
@@ -26,17 +27,14 @@ export const calculateRamUsage = (round: VariableType<boolean>) => {
let usedRam = totalRamInBytes - availableRamInBytes;
usedRam = isNaN(usedRam) || usedRam < 0 ? 0 : usedRam;
return {
percentage: divide([totalRamInBytes, usedRam], round.value),
total: totalRamInBytes,
used: usedRam,
free: availableRamInBytes,
};
} catch (error) {
console.error('Error calculating RAM usage:', error);
return { total: 0, used: 0, percentage: 0 };
return { total: 0, used: 0, percentage: 0, free: 0 };
}
};

View File

@@ -1,62 +1,48 @@
import options from "options";
import options from 'options';
// Module initializer
import { module } from "../module"
import { module } from '../module';
// Types
import { GenericResourceData } from "lib/types/customModules/generic";
import Button from "types/widgets/button";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import { GenericResourceData } from 'lib/types/customModules/generic';
import Button from 'types/widgets/button';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
// Helper Methods
import { calculateRamUsage } from "./computeRam";
import { calculateRamUsage } from './computeRam';
// Utility Methods
import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils";
import { ResourceLabelType } from "lib/types/bar";
import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils';
import { Module, ResourceLabelType } from 'lib/types/bar';
// Global Constants
import { LABEL_TYPES } from "lib/types/defaults/bar";
import { pollVariable } from "customModules/PollVar";
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
// All the user configurable options for the ram module that are needed
const {
label,
labelType,
round,
leftClick,
rightClick,
middleClick,
pollingInterval
} = options.bar.customModules.ram;
const { label, labelType, round, leftClick, rightClick, middleClick, pollingInterval } = options.bar.customModules.ram;
const defaultRamData: GenericResourceData = { total: 0, used: 0, percentage: 0, free: 0 };
const ramUsage = Variable(defaultRamData);
const ramUsage = Variable<GenericResourceData>(defaultRamData);
pollVariable(
ramUsage,
[round.bind('value')],
pollingInterval.bind('value'),
calculateRamUsage,
round,
);
export const Ram = () => {
pollVariable(ramUsage, [round.bind('value')], pollingInterval.bind('value'), calculateRamUsage, round);
export const Ram = (): Module => {
const ramModule = module({
textIcon: "",
textIcon: '',
label: Utils.merge(
[ramUsage.bind("value"), labelType.bind("value"), round.bind("value")],
[ramUsage.bind('value'), labelType.bind('value'), round.bind('value')],
(rmUsg: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => {
const returnValue = renderResourceLabel(lblTyp, rmUsg, round);
return returnValue;
}),
tooltipText: labelType.bind("value").as(lblTyp => {
},
),
tooltipText: labelType.bind('value').as((lblTyp) => {
return formatTooltip('RAM', lblTyp);
}),
boxClass: "ram",
showLabelBinding: label.bind("value"),
boxClass: 'ram',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -71,18 +57,22 @@ export const Ram = () => {
},
onScrollUp: {
fn: () => {
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType;
}
labelType.value = LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length
] as ResourceLabelType;
},
},
onScrollDown: {
fn: () => {
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType;
}
labelType.value = LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length
] as ResourceLabelType;
},
},
});
},
}
},
});
return ramModule;
}
};

View File

@@ -1,23 +1,20 @@
// @ts-expect-error
// @ts-expect-error is a special directive that tells the compiler to use the GTop library
import GTop from 'gi://GTop';
import { divide } from 'customModules/utils';
import { Variable as VariableType } from 'types/variable';
import { GenericResourceData } from 'lib/types/customModules/generic';
let previousFsUsage = new GTop.glibtop_fsusage();
export const computeStorage = (round: VariableType<boolean>) => {
export const computeStorage = (round: VariableType<boolean>): GenericResourceData => {
try {
const currentFsUsage = new GTop.glibtop_fsusage();
GTop.glibtop_get_fsusage(currentFsUsage, "/");
GTop.glibtop_get_fsusage(currentFsUsage, '/');
const total = currentFsUsage.blocks * currentFsUsage.block_size;
const available = currentFsUsage.bavail * currentFsUsage.block_size;
const used = total - available;
previousFsUsage = currentFsUsage;
return {
total,
used,
@@ -26,7 +23,6 @@ export const computeStorage = (round: VariableType<boolean>) => {
};
} catch (error) {
console.error('Error calculating RAM usage:', error);
return { total: 0, used: 0, percentage: 0 };
return { total: 0, used: 0, percentage: 0, free: 0 };
}
};

View File

@@ -1,52 +1,38 @@
import options from "options";
import { module } from "../module"
import options from 'options';
import { module } from '../module';
import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils";
import { computeStorage } from "./computeStorage";
import { ResourceLabelType } from "lib/types/bar";
import { GenericResourceData } from "lib/types/customModules/generic";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from "types/widgets/button";
import { LABEL_TYPES } from "lib/types/defaults/bar";
import { pollVariable } from "customModules/PollVar";
import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils';
import { computeStorage } from './computeStorage';
import { Module, ResourceLabelType } from 'lib/types/bar';
import { GenericResourceData } from 'lib/types/customModules/generic';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
const {
label,
labelType,
icon,
round,
leftClick,
rightClick,
middleClick,
pollingInterval
} = options.bar.customModules.storage;
const { label, labelType, icon, round, leftClick, rightClick, middleClick, pollingInterval } =
options.bar.customModules.storage;
const defaultStorageData = { total: 0, used: 0, percentage: 0, free: 0 };
const storageUsage = Variable(defaultStorageData);
const storageUsage = Variable<GenericResourceData>(defaultStorageData);
pollVariable(
storageUsage,
[round.bind('value')],
pollingInterval.bind('value'),
computeStorage,
round,
);
pollVariable(storageUsage, [round.bind('value')], pollingInterval.bind('value'), computeStorage, round);
export const Storage = () => {
export const Storage = (): Module => {
const storageModule = module({
textIcon: icon.bind("value"),
textIcon: icon.bind('value'),
label: Utils.merge(
[storageUsage.bind("value"), labelType.bind("value"), round.bind("value")],
[storageUsage.bind('value'), labelType.bind('value'), round.bind('value')],
(storage: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => {
return renderResourceLabel(lblTyp, storage, round);
}),
tooltipText: labelType.bind("value").as(lblTyp => {
},
),
tooltipText: labelType.bind('value').as((lblTyp) => {
return formatTooltip('Storage', lblTyp);
}),
boxClass: "storage",
showLabelBinding: label.bind("value"),
boxClass: 'storage',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -61,18 +47,22 @@ export const Storage = () => {
},
onScrollUp: {
fn: () => {
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType;
}
labelType.value = LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length
] as ResourceLabelType;
},
},
onScrollDown: {
fn: () => {
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType;
}
labelType.value = LABEL_TYPES[
(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length
] as ResourceLabelType;
},
},
});
},
}
},
});
return storageModule;
}
};

View File

@@ -1,106 +1,146 @@
import { Option } from "widget/settings/shared/Option";
import { Header } from "widget/settings/shared/Header";
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from "options";
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, GtkWidget } from 'lib/types/widget';
export const CustomModuleTheme = () => {
export const CustomModuleTheme = (): Scrollable<GtkWidget, Attribute> => {
return Widget.Scrollable({
vscroll: "automatic",
hscroll: "automatic",
class_name: "menu-theme-page customModules paged-container",
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page customModules paged-container',
child: Widget.Box({
class_name: "bar-theme-page paged-container",
class_name: 'bar-theme-page paged-container',
vertical: true,
children: [
Header('RAM'),
Option({ opt: options.theme.bar.buttons.modules.ram.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.ram.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.ram.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.ram.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.ram.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('CPU'),
Option({ opt: options.theme.bar.buttons.modules.cpu.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.cpu.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.cpu.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.cpu.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.cpu.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Storage'),
Option({ opt: options.theme.bar.buttons.modules.storage.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.storage.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.storage.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.storage.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.storage.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Netstat'),
Option({ opt: options.theme.bar.buttons.modules.netstat.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.netstat.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.netstat.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.netstat.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.netstat.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Keyboard Layout'),
Option({ opt: options.theme.bar.buttons.modules.kbLayout.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.kbLayout.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.kbLayout.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.kbLayout.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.kbLayout.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Updates'),
Option({ opt: options.theme.bar.buttons.modules.updates.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.updates.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.updates.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.updates.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.updates.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Weather'),
Option({ opt: options.theme.bar.buttons.modules.weather.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.weather.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.weather.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.weather.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.weather.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Power'),
Option({ opt: options.theme.bar.buttons.modules.power.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.power.background, title: 'Label Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.power.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.power.icon_background,
title: 'Icon Background',
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
type: 'color'
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
]
})
})
}
],
}),
});
};

View File

@@ -1,11 +1,12 @@
import options from "options";
import { module } from "../module"
import options from 'options';
import { module } from '../module';
import { inputHandler } from "customModules/utils";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from "types/widgets/button";
import { Variable as VariableType } from "types/variable";
import { pollVariableBash } from "customModules/PollVar";
import { inputHandler } from 'customModules/utils';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import { Variable as VariableType } from 'types/variable';
import { pollVariableBash } from 'customModules/PollVar';
import { Module } from 'lib/types/bar';
const {
updateCommand,
@@ -20,12 +21,12 @@ const {
scrollDown,
} = options.bar.customModules.updates;
const pendingUpdates: VariableType<string> = Variable(" 0");
const pendingUpdates: VariableType<string> = Variable(' 0');
const processUpdateCount = (updateCount: string) => {
const processUpdateCount = (updateCount: string): string => {
if (!padZero.value) return updateCount;
return `${updateCount.padStart(2, '0')}`;
}
};
pollVariableBash(
pendingUpdates,
@@ -35,13 +36,13 @@ pollVariableBash(
processUpdateCount,
);
export const Updates = () => {
export const Updates = (): Module => {
const updatesModule = module({
textIcon: icon.bind("value"),
tooltipText: pendingUpdates.bind("value").as(v => `${v} updates available`),
boxClass: "updates",
label: pendingUpdates.bind("value"),
showLabelBinding: label.bind("value"),
textIcon: icon.bind('value'),
tooltipText: pendingUpdates.bind('value').as((v) => `${v} updates available`),
boxClass: 'updates',
label: pendingUpdates.bind('value'),
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -66,7 +67,4 @@ export const Updates = () => {
});
return updatesModule;
}
};

View File

@@ -1,6 +1,8 @@
import { ResourceLabelType } from 'lib/types/bar';
import { GenericResourceData } from 'lib/types/customModules/generic';
import { GenericResourceData, Postfix } from 'lib/types/customModules/generic';
import { InputHandlerEvents } from 'lib/types/customModules/utils';
import { ThrottleFn, ThrottleFnCallback } from 'lib/types/utils';
import { GtkWidget } from 'lib/types/widget';
import { Binding } from 'lib/utils';
import { openMenu } from 'modules/bar/utils';
import options from 'options';
@@ -13,14 +15,11 @@ const { scrollSpeed } = options.bar.customModules;
export const runAsyncCommand = (
cmd: string,
fn: Function,
events: { clicked: any; event: Gdk.Event }
fn: (output: string) => void,
events: { clicked: Button<GtkWidget, GtkWidget>; event: Gdk.Event },
): void => {
if (cmd.startsWith('menu:')) {
// if the command starts with 'menu:', then it is a menu command
// and we should App.toggleMenu("menuName") based on the input menu:menuName. Ignoring spaces and case
const menuName = cmd.split(':')[1].trim().toLowerCase();
openMenu(events.clicked, events.event, `${menuName}menu`);
return;
@@ -32,15 +31,10 @@ export const runAsyncCommand = (
fn(output);
}
})
.catch((err) =>
console.error(`Error running command "${cmd}": ${err})`)
);
.catch((err) => console.error(`Error running command "${cmd}": ${err})`));
};
export function throttle<T extends (...args: any[]) => void>(
func: T,
limit: number
): T {
export function throttle<T extends ThrottleFn>(func: T, limit: number): T {
let inThrottle: boolean;
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
if (!inThrottle) {
@@ -53,31 +47,23 @@ export function throttle<T extends (...args: any[]) => void>(
} as T;
}
export const throttledScrollHandler = (interval: number) =>
throttle((cmd: string, fn: Function | undefined) => {
export const throttledScrollHandler = (interval: number): ThrottleFn =>
throttle((cmd: string, fn: ThrottleFnCallback) => {
Utils.execAsync(`bash -c "${cmd}"`)
.then((output) => {
if (fn !== undefined) {
fn(output);
}
})
.catch((err) =>
console.error(`Error running command "${cmd}": ${err}`)
);
.catch((err) => console.error(`Error running command "${cmd}": ${err}`));
}, 200 / interval);
const dummyVar = Variable('');
export const inputHandler = (
self: Button<Gtk.Widget, Gtk.Widget>,
{
onPrimaryClick,
onSecondaryClick,
onMiddleClick,
onScrollUp,
onScrollDown,
}: InputHandlerEvents
) => {
{ onPrimaryClick, onSecondaryClick, onMiddleClick, onScrollUp, onScrollDown }: InputHandlerEvents,
): void => {
const sanitizeInput = (input: VariableType<string>): string => {
if (input === undefined) {
return '';
@@ -89,46 +75,25 @@ export const inputHandler = (
const interval = scrollSpeed.value;
const throttledHandler = throttledScrollHandler(interval);
self.on_primary_click = (clicked: any, event: Gdk.Event) =>
runAsyncCommand(
sanitizeInput(onPrimaryClick?.cmd || dummyVar),
onPrimaryClick.fn,
{ clicked, event }
);
self.on_primary_click = (clicked: Button<GtkWidget, GtkWidget>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onPrimaryClick?.cmd || dummyVar), onPrimaryClick.fn, { clicked, event });
self.on_secondary_click = (clicked: any, event: Gdk.Event) =>
runAsyncCommand(
sanitizeInput(onSecondaryClick?.cmd || dummyVar),
onSecondaryClick.fn,
{ clicked, event }
);
self.on_secondary_click = (clicked: Button<GtkWidget, GtkWidget>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onSecondaryClick?.cmd || dummyVar), onSecondaryClick.fn, { clicked, event });
self.on_middle_click = (clicked: any, event: Gdk.Event) =>
runAsyncCommand(
sanitizeInput(onMiddleClick?.cmd || dummyVar),
onMiddleClick.fn,
{ clicked, event }
);
self.on_middle_click = (clicked: Button<GtkWidget, GtkWidget>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onMiddleClick?.cmd || dummyVar), onMiddleClick.fn, { clicked, event });
self.on_scroll_up = () =>
throttledHandler(
sanitizeInput(onScrollUp?.cmd || dummyVar),
onScrollUp.fn
);
self.on_scroll_up = (): void => throttledHandler(sanitizeInput(onScrollUp?.cmd || dummyVar), onScrollUp.fn);
self.on_scroll_down = () =>
throttledHandler(
sanitizeInput(onScrollDown?.cmd || dummyVar),
onScrollDown.fn
);
self.on_scroll_down = (): void =>
throttledHandler(sanitizeInput(onScrollDown?.cmd || dummyVar), onScrollDown.fn);
};
// Initial setup of event handlers
updateHandlers();
const sanitizeVariable = (
someVar: VariableType<string> | undefined
): Binding<string> => {
const sanitizeVariable = (someVar: VariableType<string> | undefined): Binding<string> => {
if (someVar === undefined || typeof someVar.bind !== 'function') {
return dummyVar.bind('value');
}
@@ -145,37 +110,36 @@ export const inputHandler = (
sanitizeVariable(onScrollUp),
sanitizeVariable(onScrollDown),
],
updateHandlers
updateHandlers,
);
};
export const divide = ([total, used]: number[], round: boolean) => {
export const divide = ([total, used]: number[], round: boolean): number => {
const percentageTotal = (used / total) * 100;
if (round) {
return total > 0 ? Math.round(percentageTotal) : 0;
}
return total > 0 ? parseFloat(percentageTotal.toFixed(2)) : 0;
};
export const formatSizeInKiB = (sizeInBytes: number, round: boolean) => {
const sizeInGiB = sizeInBytes / (1024 ** 1);
export const formatSizeInKiB = (sizeInBytes: number, round: boolean): number => {
const sizeInGiB = sizeInBytes / 1024 ** 1;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
};
export const formatSizeInMiB = (sizeInBytes: number, round: boolean) => {
const sizeInGiB = sizeInBytes / (1024 ** 2);
export const formatSizeInMiB = (sizeInBytes: number, round: boolean): number => {
const sizeInGiB = sizeInBytes / 1024 ** 2;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
};
export const formatSizeInGiB = (sizeInBytes: number, round: boolean) => {
const sizeInGiB = sizeInBytes / (1024 ** 3);
export const formatSizeInGiB = (sizeInBytes: number, round: boolean): number => {
const sizeInGiB = sizeInBytes / 1024 ** 3;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
};
export const formatSizeInTiB = (sizeInBytes: number, round: boolean) => {
const sizeInGiB = sizeInBytes / (1024 ** 4);
export const formatSizeInTiB = (sizeInBytes: number, round: boolean): number => {
const sizeInGiB = sizeInBytes / 1024 ** 4;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
};
export const autoFormatSize = (sizeInBytes: number, round: boolean) => {
export const autoFormatSize = (sizeInBytes: number, round: boolean): number => {
// auto convert to GiB, MiB, KiB, TiB, or bytes
if (sizeInBytes >= 1024 ** 4) return formatSizeInTiB(sizeInBytes, round);
if (sizeInBytes >= 1024 ** 3) return formatSizeInGiB(sizeInBytes, round);
@@ -183,22 +147,18 @@ export const autoFormatSize = (sizeInBytes: number, round: boolean) => {
if (sizeInBytes >= 1024 ** 1) return formatSizeInKiB(sizeInBytes, round);
return sizeInBytes;
}
};
export const getPostfix = (sizeInBytes: number) => {
export const getPostfix = (sizeInBytes: number): Postfix => {
if (sizeInBytes >= 1024 ** 4) return 'TiB';
if (sizeInBytes >= 1024 ** 3) return 'GiB';
if (sizeInBytes >= 1024 ** 2) return 'MiB';
if (sizeInBytes >= 1024 ** 1) return 'KiB';
return 'B';
}
};
export const renderResourceLabel = (
lblType: ResourceLabelType,
rmUsg: GenericResourceData,
round: boolean
) => {
export const renderResourceLabel = (lblType: ResourceLabelType, rmUsg: GenericResourceData, round: boolean): string => {
const { used, total, percentage, free } = rmUsg;
const formatFunctions = {
@@ -206,7 +166,7 @@ export const renderResourceLabel = (
GiB: formatSizeInGiB,
MiB: formatSizeInMiB,
KiB: formatSizeInKiB,
B: (size: number, _: boolean) => size
B: (size: number): number => size,
};
// Get them datas in proper GiB, MiB, KiB, TiB, or bytes
@@ -218,20 +178,20 @@ export const renderResourceLabel = (
const formatUsed = formatFunctions[postfix] || formatFunctions['B'];
const usedSizeFormatted = formatUsed(used, round);
if (lblType === "used/total") {
if (lblType === 'used/total') {
return `${usedSizeFormatted}/${totalSizeFormatted} ${postfix}`;
}
if (lblType === "used") {
if (lblType === 'used') {
return `${autoFormatSize(used, round)} ${getPostfix(used)}`;
}
if (lblType === "free") {
if (lblType === 'free') {
return `${autoFormatSize(free, round)} ${getPostfix(free)}`;
}
return `${percentage}%`;
};
export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType) => {
export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType): string => {
switch (lblTyp) {
case 'used':
return `Used ${dataType}`;
@@ -244,4 +204,4 @@ export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType) => {
default:
return '';
}
}
};

View File

@@ -1,40 +1,30 @@
import options from "options";
import { module } from "../module"
import options from 'options';
import { module } from '../module';
import { inputHandler } from "customModules/utils";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
import Button from "types/widgets/button";
import { getWeatherStatusTextIcon, globalWeatherVar } from "globals/weather";
import { inputHandler } from 'customModules/utils';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Button from 'types/widgets/button';
import { getWeatherStatusTextIcon, globalWeatherVar } from 'globals/weather';
import { Module } from 'lib/types/bar';
const {
label,
unit,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
} = options.bar.customModules.weather;
const { label, unit, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.weather;
export const Weather = () => {
export const Weather = (): Module => {
const weatherModule = module({
textIcon: Utils.merge([globalWeatherVar.bind("value")], (wthr) => {
textIcon: Utils.merge([globalWeatherVar.bind('value')], (wthr) => {
const weatherStatusIcon = getWeatherStatusTextIcon(wthr);
return weatherStatusIcon;
}),
tooltipText: globalWeatherVar.bind("value").as(v => `Weather Status: ${v.current.condition.text}`),
boxClass: "weather-custom",
label: Utils.merge(
[globalWeatherVar.bind("value"), unit.bind("value")],
(wthr, unt) => {
if (unt === "imperial") {
return `${Math.ceil(wthr.current.temp_f)}° F`;
} else {
return `${Math.ceil(wthr.current.temp_c)}° C`;
}
},
),
showLabelBinding: label.bind("value"),
tooltipText: globalWeatherVar.bind('value').as((v) => `Weather Status: ${v.current.condition.text}`),
boxClass: 'weather-custom',
label: Utils.merge([globalWeatherVar.bind('value'), unit.bind('value')], (wthr, unt) => {
if (unt === 'imperial') {
return `${Math.ceil(wthr.current.temp_f)}° F`;
} else {
return `${Math.ceil(wthr.current.temp_c)}° C`;
}
}),
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
inputHandler(self, {
@@ -59,8 +49,4 @@ export const Weather = () => {
});
return weatherModule;
}
};

View File

@@ -1,8 +1,8 @@
import Service from "resource:///com/github/Aylur/ags/service.js";
import App from "resource:///com/github/Aylur/ags/app.js";
import { monitorFile } from "resource:///com/github/Aylur/ags/utils.js";
import Gio from "gi://Gio";
import { FileInfo } from "types/@girs/gio-2.0/gio-2.0.cjs";
import Service from 'resource:///com/github/Aylur/ags/service.js';
import App from 'resource:///com/github/Aylur/ags/app.js';
import { monitorFile } from 'resource:///com/github/Aylur/ags/utils.js';
import Gio from 'gi://Gio';
import { FileInfo } from 'types/@girs/gio-2.0/gio-2.0.cjs';
class DirectoryMonitorService extends Service {
static {
@@ -14,23 +14,19 @@ class DirectoryMonitorService extends Service {
this.recursiveDirectoryMonitor(`${App.configDir}/scss`);
}
recursiveDirectoryMonitor(directoryPath: string) {
recursiveDirectoryMonitor(directoryPath: string): void {
monitorFile(directoryPath, (_, eventType) => {
if (eventType === Gio.FileMonitorEvent.CHANGES_DONE_HINT) {
this.emit("changed");
this.emit('changed');
}
});
const directory = Gio.File.new_for_path(directoryPath);
const enumerator = directory.enumerate_children(
"standard::*",
Gio.FileQueryInfoFlags.NONE,
null,
);
const enumerator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
let fileInfo: FileInfo;
while ((fileInfo = enumerator.next_file(null) as FileInfo) !== null) {
const childPath = directoryPath + "/" + fileInfo.get_name();
const childPath = directoryPath + '/' + fileInfo.get_name();
if (fileInfo.get_file_type() === Gio.FileType.DIRECTORY) {
this.recursiveDirectoryMonitor(childPath);
}

9
globals.d.ts vendored
View File

@@ -1,13 +1,14 @@
// globals.d.ts
/* eslint-disable no-var */
import { Options, Variable as VariableType } from "types/variable";
import { Options, Variable as VariableType } from 'types/variable';
declare global {
var globalMousePos: VariableType<number[]>;
var useTheme: Function;
var useTheme: (filePath: string) => void;
var globalWeatherVar: VariableType<Weather>;
var options: Options
var options: Options;
var removingNotifications: VariableType<boolean>;
}
export { };
export {};

View File

@@ -1,5 +1,5 @@
import { Variable as VariableType } from "types/variable";
import { Variable as VariableType } from 'types/variable';
const globalMousePosVar: VariableType<number[]> = Variable([0, 0]);
globalThis["globalMousePos"] = globalMousePosVar;
globalThis['globalMousePos'] = globalMousePosVar;

View File

@@ -1,16 +1,15 @@
export const WIFI_STATUS_MAP = {
unknown: "Status Unknown",
unmanaged: "Unmanaged",
unavailable: "Unavailable",
disconnected: "Disconnected",
prepare: "Preparing Connecting",
config: "Connecting",
need_auth: "Needs Authentication",
ip_config: "Requesting IP",
ip_check: "Checking Access",
secondaries: "Waiting on Secondaries",
activated: "Connected",
deactivating: "Disconnecting",
failed: "Connection Failed",
unknown: 'Status Unknown',
unmanaged: 'Unmanaged',
unavailable: 'Unavailable',
disconnected: 'Disconnected',
prepare: 'Preparing Connecting',
config: 'Connecting',
need_auth: 'Needs Authentication',
ip_config: 'Requesting IP',
ip_check: 'Checking Access',
secondaries: 'Waiting on Secondaries',
activated: 'Connected',
deactivating: 'Disconnecting',
failed: 'Connection Failed',
} as const;

View File

@@ -1,37 +1,37 @@
import icons from "modules/icons/index";
import { Notification } from "types/service/notifications";
import icons from 'modules/icons/index';
import { Notification } from 'types/service/notifications';
export const removingNotifications = Variable<boolean>(false);
export const getNotificationIcon = (app_name: string, app_icon: string, app_entry: string) => {
export const getNotificationIcon = (app_name: string, app_icon: string, app_entry: string): string => {
let icon: string = icons.fallback.notification;
if (Utils.lookUpIcon(app_name) || Utils.lookUpIcon(app_name.toLowerCase() || "")) {
if (Utils.lookUpIcon(app_name) || Utils.lookUpIcon(app_name.toLowerCase() || '')) {
icon = Utils.lookUpIcon(app_name)
? app_name
: Utils.lookUpIcon(app_name.toLowerCase())
? app_name.toLowerCase()
: "";
? app_name.toLowerCase()
: '';
}
if (Utils.lookUpIcon(app_icon) && icon === "") {
if (Utils.lookUpIcon(app_icon) && icon === '') {
icon = app_icon;
}
if (Utils.lookUpIcon(app_entry || "") && icon === "") {
icon = app_entry || "";
if (Utils.lookUpIcon(app_entry || '') && icon === '') {
icon = app_entry || '';
}
return icon;
};
export const closeNotifications = async (notifications: Notification[]) => {
export const closeNotifications = async (notifications: Notification[]): Promise<void> => {
removingNotifications.value = true;
for (const notif of notifications) {
notif.close();
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 100));
}
removingNotifications.value = false;
}
};
globalThis["removingNotifications"] = removingNotifications;
globalThis['removingNotifications'] = removingNotifications;

View File

@@ -1,12 +1,12 @@
import Gio from "gi://Gio"
import { bash, Notify } from "lib/utils";
import icons from "lib/icons"
import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from "widget/settings/shared/FileChooser";
import Gio from 'gi://Gio';
import { bash, Notify } from 'lib/utils';
import icons from 'lib/icons';
import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from 'widget/settings/shared/FileChooser';
export const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
globalThis.useTheme = (filePath: string): void => {
let importedConfig = loadJsonFile(filePath);
const importedConfig = loadJsonFile(filePath);
if (!importedConfig) {
return;
@@ -16,22 +16,22 @@ globalThis.useTheme = (filePath: string): void => {
summary: `Importing Theme`,
body: `Importing: ${filePath}`,
iconName: icons.ui.info,
timeout: 7000
timeout: 7000,
});
let tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`);
let optionsConfigFile = Gio.File.new_for_path(OPTIONS);
const tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`);
const optionsConfigFile = Gio.File.new_for_path(OPTIONS);
let [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null);
let [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null);
const [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null);
const [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null);
if (!tmpSuccess || !optionsSuccess) {
console.error("Failed to read existing configuration files.");
console.error('Failed to read existing configuration files.');
return;
}
let tmpConfig = JSON.parse(new TextDecoder("utf-8").decode(tmpContent));
let optionsConfig = JSON.parse(new TextDecoder("utf-8").decode(optionsContent));
let tmpConfig = JSON.parse(new TextDecoder('utf-8').decode(tmpContent));
let optionsConfig = JSON.parse(new TextDecoder('utf-8').decode(optionsContent));
const filteredConfig = filterConfigForThemeOnly(importedConfig);
tmpConfig = { ...tmpConfig, ...filteredConfig };
@@ -39,6 +39,5 @@ globalThis.useTheme = (filePath: string): void => {
saveConfigToFile(tmpConfig, `${TMP}/config.json`);
saveConfigToFile(optionsConfig, OPTIONS);
bash("pkill ags && ags");
}
bash('pkill ags && ags');
};

View File

@@ -1,13 +1,13 @@
import { Opt } from "lib/option";
import { HexColor, RecursiveOptionsObject } from "lib/types/options";
import { Opt } from 'lib/option';
import { HexColor, RecursiveOptionsObject } from 'lib/types/options';
export const isOpt = <T>(value: unknown): value is Opt<T> =>
typeof value === 'object' && value !== null && 'value' in value && value instanceof Opt;
export const isRecursiveOptionsObject = (value: unknown): value is RecursiveOptionsObject => {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
};
export const isHexColor = (value: string): value is HexColor => {
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
}
};

View File

@@ -1,8 +1,8 @@
import options from "options";
import { UnitType, Weather, WeatherIconTitle, WeatherIcon } from "lib/types/weather.js";
import { DEFAULT_WEATHER } from "lib/types/defaults/weather.js";
import GLib from "gi://GLib?version=2.0";
import { weatherIcons } from "modules/icons/weather.js";
import options from 'options';
import { UnitType, Weather, WeatherIconTitle, WeatherIcon } from 'lib/types/weather.js';
import { DEFAULT_WEATHER } from 'lib/types/defaults/weather.js';
import GLib from 'gi://GLib?version=2.0';
import { weatherIcons } from 'modules/icons/weather.js';
const { key, interval, location } = options.menus.clock.weather;
@@ -10,12 +10,12 @@ export const globalWeatherVar = Variable<Weather>(DEFAULT_WEATHER);
let weatherIntervalInstance: null | number = null;
const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: string) => {
const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: string): void => {
if (weatherIntervalInstance !== null) {
GLib.source_remove(weatherIntervalInstance);
}
const formattedLocation = loc.replace(" ", "%20");
const formattedLocation = loc.replace(' ', '%20');
weatherIntervalInstance = Utils.interval(weatherInterval, () => {
Utils.execAsync(
@@ -23,13 +23,13 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str
)
.then((res) => {
try {
if (typeof res !== "string") {
if (typeof res !== 'string') {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
const parsedWeather = JSON.parse(res);
if (Object.keys(parsedWeather).includes("error")) {
if (Object.keys(parsedWeather).includes('error')) {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
@@ -46,43 +46,41 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str
});
};
Utils.merge([key.bind("value"), interval.bind("value"), location.bind("value")], (weatherKey, weatherInterval, loc) => {
Utils.merge([key.bind('value'), interval.bind('value'), location.bind('value')], (weatherKey, weatherInterval, loc) => {
if (!weatherKey) {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
weatherIntervalFn(weatherInterval, loc, weatherKey);
});
export const getTemperature = (wthr: Weather, unt: UnitType) => {
if (unt === "imperial") {
export const getTemperature = (wthr: Weather, unt: UnitType): string => {
if (unt === 'imperial') {
return `${Math.ceil(wthr.current.temp_f)}° F`;
} else {
return `${Math.ceil(wthr.current.temp_c)}° C`;
}
};
export const getWeatherIcon = (fahren: number) => {
export const getWeatherIcon = (fahren: number): Record<string, string> => {
const icons = {
100: "",
75: "",
50: "",
25: "",
0: "",
100: '',
75: '',
50: '',
25: '',
0: '',
} as const;
const colors = {
100: "weather-color red",
75: "weather-color orange",
50: "weather-color lavender",
25: "weather-color blue",
0: "weather-color sky",
100: 'weather-color red',
75: 'weather-color orange',
50: 'weather-color lavender',
25: 'weather-color blue',
0: 'weather-color sky',
} as const;
type IconKeys = keyof typeof icons;
const threshold: IconKeys =
fahren < 0
? 0
: ([100, 75, 50, 25, 0] as IconKeys[]).find((threshold) => threshold <= fahren) || 0;
fahren < 0 ? 0 : ([100, 75, 50, 25, 0] as IconKeys[]).find((threshold) => threshold <= fahren) || 0;
const icon = icons[threshold || 50];
const color = colors[threshold || 50];
@@ -93,35 +91,32 @@ export const getWeatherIcon = (fahren: number) => {
};
};
export const getWindConditions = (wthr: Weather, unt: UnitType) => {
if (unt === "imperial") {
export const getWindConditions = (wthr: Weather, unt: UnitType): string => {
if (unt === 'imperial') {
return `${Math.floor(wthr.current.wind_mph)} mph`;
}
return `${Math.floor(wthr.current.wind_kph)} kph`;
};
export const getRainChance = (wthr: Weather) => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`;
export const getRainChance = (wthr: Weather): string => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`;
export const isValidWeatherIconTitle = (title: string): title is WeatherIconTitle => {
return title in weatherIcons;
};
export const getWeatherStatusTextIcon = (wthr: Weather): WeatherIcon => {
let iconQuery = wthr.current.condition.text
.trim()
.toLowerCase()
.replaceAll(" ", "_");
let iconQuery = wthr.current.condition.text.trim().toLowerCase().replaceAll(' ', '_');
if (!wthr.current.is_day && iconQuery === "partly_cloudy") {
iconQuery = "partly_cloudy_night";
if (!wthr.current.is_day && iconQuery === 'partly_cloudy') {
iconQuery = 'partly_cloudy_night';
}
if (isValidWeatherIconTitle(iconQuery)) {
return weatherIcons[iconQuery];
} else {
console.warn(`Unknown weather icon title: ${iconQuery}`);
return weatherIcons["warning"];
return weatherIcons['warning'];
}
};
globalThis["globalWeatherVar"] = globalWeatherVar;
globalThis['globalWeatherVar'] = globalWeatherVar;

View File

@@ -6,5 +6,5 @@ export const WINDOW_LAYOUTS: string[] = [
'top-left',
'bottom-left',
'bottom-center',
'bottom-right'
'bottom-right',
];

View File

@@ -1,145 +1,145 @@
export const substitutes = {
"transmission-gtk": "transmission",
"blueberry.py": "blueberry",
"Caprine": "facebook-messenger",
"com.raggesilver.BlackBox-symbolic": "terminal-symbolic",
"org.wezfurlong.wezterm-symbolic": "terminal-symbolic",
"audio-headset-bluetooth": "audio-headphones-symbolic",
"audio-card-analog-usb": "audio-speakers-symbolic",
"audio-card-analog-pci": "audio-card-symbolic",
"preferences-system": "emblem-system-symbolic",
"com.github.Aylur.ags-symbolic": "controls-symbolic",
"com.github.Aylur.ags": "controls-symbolic",
'transmission-gtk': 'transmission',
'blueberry.py': 'blueberry',
Caprine: 'facebook-messenger',
'com.raggesilver.BlackBox-symbolic': 'terminal-symbolic',
'org.wezfurlong.wezterm-symbolic': 'terminal-symbolic',
'audio-headset-bluetooth': 'audio-headphones-symbolic',
'audio-card-analog-usb': 'audio-speakers-symbolic',
'audio-card-analog-pci': 'audio-card-symbolic',
'preferences-system': 'emblem-system-symbolic',
'com.github.Aylur.ags-symbolic': 'controls-symbolic',
'com.github.Aylur.ags': 'controls-symbolic',
} as const;
export default {
missing: "image-missing-symbolic",
missing: 'image-missing-symbolic',
nix: {
nix: "nix-snowflake-symbolic",
nix: 'nix-snowflake-symbolic',
},
app: {
terminal: "terminal-symbolic",
terminal: 'terminal-symbolic',
},
fallback: {
executable: "application-x-executable",
notification: "dialog-information-symbolic",
video: "video-x-generic-symbolic",
audio: "audio-x-generic-symbolic",
executable: 'application-x-executable',
notification: 'dialog-information-symbolic',
video: 'video-x-generic-symbolic',
audio: 'audio-x-generic-symbolic',
},
ui: {
close: "window-close-symbolic",
colorpicker: "color-select-symbolic",
info: "info-symbolic",
link: "external-link-symbolic",
lock: "system-lock-screen-symbolic",
menu: "open-menu-symbolic",
refresh: "view-refresh-symbolic",
search: "system-search-symbolic",
settings: "emblem-system-symbolic",
themes: "preferences-desktop-theme-symbolic",
tick: "object-select-symbolic",
time: "hourglass-symbolic",
toolbars: "toolbars-symbolic",
warning: "dialog-warning-symbolic",
avatar: "avatar-default-symbolic",
close: 'window-close-symbolic',
colorpicker: 'color-select-symbolic',
info: 'info-symbolic',
link: 'external-link-symbolic',
lock: 'system-lock-screen-symbolic',
menu: 'open-menu-symbolic',
refresh: 'view-refresh-symbolic',
search: 'system-search-symbolic',
settings: 'emblem-system-symbolic',
themes: 'preferences-desktop-theme-symbolic',
tick: 'object-select-symbolic',
time: 'hourglass-symbolic',
toolbars: 'toolbars-symbolic',
warning: 'dialog-warning-symbolic',
avatar: 'avatar-default-symbolic',
arrow: {
right: "pan-end-symbolic",
left: "pan-start-symbolic",
down: "pan-down-symbolic",
up: "pan-up-symbolic",
right: 'pan-end-symbolic',
left: 'pan-start-symbolic',
down: 'pan-down-symbolic',
up: 'pan-up-symbolic',
},
},
audio: {
mic: {
muted: "microphone-disabled-symbolic",
low: "microphone-sensitivity-low-symbolic",
medium: "microphone-sensitivity-medium-symbolic",
high: "microphone-sensitivity-high-symbolic",
muted: 'microphone-disabled-symbolic',
low: 'microphone-sensitivity-low-symbolic',
medium: 'microphone-sensitivity-medium-symbolic',
high: 'microphone-sensitivity-high-symbolic',
},
volume: {
muted: "audio-volume-muted-symbolic",
low: "audio-volume-low-symbolic",
medium: "audio-volume-medium-symbolic",
high: "audio-volume-high-symbolic",
overamplified: "audio-volume-overamplified-symbolic",
muted: 'audio-volume-muted-symbolic',
low: 'audio-volume-low-symbolic',
medium: 'audio-volume-medium-symbolic',
high: 'audio-volume-high-symbolic',
overamplified: 'audio-volume-overamplified-symbolic',
},
type: {
headset: "audio-headphones-symbolic",
speaker: "audio-speakers-symbolic",
card: "audio-card-symbolic",
headset: 'audio-headphones-symbolic',
speaker: 'audio-speakers-symbolic',
card: 'audio-card-symbolic',
},
mixer: "mixer-symbolic",
mixer: 'mixer-symbolic',
},
powerprofile: {
balanced: "power-profile-balanced-symbolic",
"power-saver": "power-profile-power-saver-symbolic",
performance: "power-profile-performance-symbolic",
balanced: 'power-profile-balanced-symbolic',
'power-saver': 'power-profile-power-saver-symbolic',
performance: 'power-profile-performance-symbolic',
},
asusctl: {
profile: {
Balanced: "power-profile-balanced-symbolic",
Quiet: "power-profile-power-saver-symbolic",
Performance: "power-profile-performance-symbolic",
Balanced: 'power-profile-balanced-symbolic',
Quiet: 'power-profile-power-saver-symbolic',
Performance: 'power-profile-performance-symbolic',
},
mode: {
Integrated: "processor-symbolic",
Hybrid: "controller-symbolic",
Integrated: 'processor-symbolic',
Hybrid: 'controller-symbolic',
},
},
battery: {
charging: "battery-flash-symbolic",
warning: "battery-empty-symbolic",
charging: 'battery-flash-symbolic',
warning: 'battery-empty-symbolic',
},
bluetooth: {
enabled: "bluetooth-active-symbolic",
disabled: "bluetooth-disabled-symbolic",
enabled: 'bluetooth-active-symbolic',
disabled: 'bluetooth-disabled-symbolic',
},
brightness: {
indicator: "display-brightness-symbolic",
keyboard: "keyboard-brightness-symbolic",
screen: "display-brightness-symbolic",
indicator: 'display-brightness-symbolic',
keyboard: 'keyboard-brightness-symbolic',
screen: 'display-brightness-symbolic',
},
powermenu: {
sleep: "weather-clear-night-symbolic",
reboot: "system-reboot-symbolic",
logout: "system-log-out-symbolic",
shutdown: "system-shutdown-symbolic",
sleep: 'weather-clear-night-symbolic',
reboot: 'system-reboot-symbolic',
logout: 'system-log-out-symbolic',
shutdown: 'system-shutdown-symbolic',
},
recorder: {
recording: "media-record-symbolic",
recording: 'media-record-symbolic',
},
notifications: {
noisy: "org.gnome.Settings-notifications-symbolic",
silent: "notifications-disabled-symbolic",
message: "chat-bubbles-symbolic",
noisy: 'org.gnome.Settings-notifications-symbolic',
silent: 'notifications-disabled-symbolic',
message: 'chat-bubbles-symbolic',
},
trash: {
full: "user-trash-full-symbolic",
empty: "user-trash-symbolic",
full: 'user-trash-full-symbolic',
empty: 'user-trash-symbolic',
},
mpris: {
shuffle: {
enabled: "media-playlist-shuffle-symbolic",
disabled: "media-playlist-consecutive-symbolic",
enabled: 'media-playlist-shuffle-symbolic',
disabled: 'media-playlist-consecutive-symbolic',
},
loop: {
none: "media-playlist-repeat-symbolic",
track: "media-playlist-repeat-song-symbolic",
playlist: "media-playlist-repeat-symbolic",
none: 'media-playlist-repeat-symbolic',
track: 'media-playlist-repeat-song-symbolic',
playlist: 'media-playlist-repeat-symbolic',
},
playing: "media-playback-pause-symbolic",
paused: "media-playback-start-symbolic",
stopped: "media-playback-start-symbolic",
prev: "media-skip-backward-symbolic",
next: "media-skip-forward-symbolic",
playing: 'media-playback-pause-symbolic',
paused: 'media-playback-start-symbolic',
stopped: 'media-playback-start-symbolic',
prev: 'media-skip-backward-symbolic',
next: 'media-skip-forward-symbolic',
},
system: {
cpu: "org.gnome.SystemMonitor-symbolic",
ram: "drive-harddisk-solidstate-symbolic",
temp: "temperature-symbolic",
cpu: 'org.gnome.SystemMonitor-symbolic',
ram: 'drive-harddisk-solidstate-symbolic',
temp: 'temperature-symbolic',
},
color: {
dark: "dark-mode-symbolic",
light: "light-mode-symbolic",
dark: 'dark-mode-symbolic',
light: 'light-mode-symbolic',
},
}
};

View File

@@ -1,70 +1,73 @@
import { isHexColor } from "globals/variables"
import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
import { isHexColor } from 'globals/variables';
import { Variable } from 'resource:///com/github/Aylur/ags/variable.js';
import { MkOptionsResult } from './types/options';
type OptProps = {
persistent?: boolean
}
persistent?: boolean;
};
export class Opt<T = unknown> extends Variable<T> {
static { Service.register(this) }
static {
Service.register(this);
}
constructor(initial: T, { persistent = false }: OptProps = {}) {
super(initial)
this.initial = initial
this.persistent = persistent
super(initial);
this.initial = initial;
this.persistent = persistent;
}
initial: T
id = ""
persistent: boolean
toString() { return `${this.value}` }
toJSON() { return `opt:${this.value}` }
initial: T;
id = '';
persistent: boolean;
toString(): string {
return `${this.value}`;
}
toJSON(): string {
return `opt:${this.value}`;
}
getValue = (): T => {
return super.getValue()
return super.getValue();
};
init(cacheFile: string): void {
const cacheV = JSON.parse(Utils.readFile(cacheFile) || '{}')[this.id];
if (cacheV !== undefined) this.value = cacheV;
this.connect('changed', () => {
const cache = JSON.parse(Utils.readFile(cacheFile) || '{}');
cache[this.id] = this.value;
Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile);
});
}
init(cacheFile: string) {
const cacheV = JSON.parse(Utils.readFile(cacheFile) || "{}")[this.id]
if (cacheV !== undefined)
this.value = cacheV
this.connect("changed", () => {
const cache = JSON.parse(Utils.readFile(cacheFile) || "{}")
cache[this.id] = this.value
Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile)
})
}
reset() {
if (this.persistent)
return;
reset(): string | undefined {
if (this.persistent) return;
if (JSON.stringify(this.value) !== JSON.stringify(this.initial)) {
this.value = this.initial
this.value = this.initial;
return this.id;
}
}
doResetColor() {
if (this.persistent)
return;
doResetColor(): string | undefined {
if (this.persistent) return;
const isColor = isHexColor(this.value as string);
if ((JSON.stringify(this.value) !== JSON.stringify(this.initial)) && isColor) {
this.value = this.initial
return this.id
if (JSON.stringify(this.value) !== JSON.stringify(this.initial) && isColor) {
this.value = this.initial;
return this.id;
}
return;
}
}
export const opt = <T>(initial: T, opts?: OptProps) => new Opt(initial, opts)
export const opt = <T>(initial: T, opts?: OptProps): Opt<T> => new Opt(initial, opts);
function getOptions(object: Record<string, unknown>, path = ""): Opt[] {
return Object.keys(object).flatMap(key => {
const getOptions = (object: Record<string, unknown>, path = ''): Opt[] => {
return Object.keys(object).flatMap((key) => {
const obj = object[key];
const id = path ? path + "." + key : key;
const id = path ? path + '.' + key : key;
if (obj instanceof Variable) {
const optValue = obj as Opt;
@@ -72,74 +75,73 @@ function getOptions(object: Record<string, unknown>, path = ""): Opt[] {
return optValue;
}
if (typeof obj === "object" && obj !== null) {
if (typeof obj === 'object' && obj !== null) {
return getOptions(obj as Record<string, unknown>, id); // Recursively process nested objects
}
return [];
});
}
};
export function mkOptions<T extends object>(cacheFile: string, object: T, confFile: string = "config.json") {
for (const opt of getOptions(object as Record<string, unknown>))
opt.init(cacheFile)
export function mkOptions<T extends object>(
cacheFile: string,
object: T,
confFile: string = 'config.json',
): T & MkOptionsResult<T> {
for (const opt of getOptions(object as Record<string, unknown>)) opt.init(cacheFile);
Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
Utils.ensureDirectory(cacheFile.split('/').slice(0, -1).join('/'));
const configFile = `${TMP}/${confFile}`
const values = getOptions(object as Record<string, unknown>).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {})
Utils.writeFileSync(JSON.stringify(values, null, 2), configFile)
const configFile = `${TMP}/${confFile}`;
const values = getOptions(object as Record<string, unknown>).reduce(
(obj, { id, value }) => ({ [id]: value, ...obj }),
{},
);
Utils.writeFileSync(JSON.stringify(values, null, 2), configFile);
Utils.monitorFile(configFile, () => {
const cache = JSON.parse(Utils.readFile(configFile) || "{}")
const cache = JSON.parse(Utils.readFile(configFile) || '{}');
for (const opt of getOptions(object as Record<string, unknown>)) {
if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value))
opt.value = cache[opt.id]
if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value)) opt.value = cache[opt.id];
}
})
});
function sleep(ms = 0): Promise<T> {
return new Promise(r => setTimeout(r, ms))
return new Promise((r) => setTimeout(r, ms));
}
async function reset(
const reset = async (
[opt, ...list] = getOptions(object as Record<string, unknown>),
id = opt?.reset(),
): Promise<Array<string>> {
if (!opt)
return sleep().then(() => [])
): Promise<Array<string>> => {
if (!opt) return sleep().then(() => []);
return id
? [id, ...(await sleep(50).then(() => reset(list)))]
: await sleep().then(() => reset(list))
}
return id ? [id, ...(await sleep(50).then(() => reset(list)))] : await sleep().then(() => reset(list));
};
async function resetTheme(
const resetTheme = async (
[opt, ...list] = getOptions(object as Record<string, unknown>),
id = opt?.doResetColor(),
): Promise<Array<string>> {
if (!opt)
return sleep().then(() => [])
): Promise<Array<string>> => {
if (!opt) return sleep().then(() => []);
return id
? [id, ...(await sleep(50).then(() => resetTheme(list)))]
: await sleep().then(() => resetTheme(list))
}
: await sleep().then(() => resetTheme(list));
};
return Object.assign(object, {
configFile,
array: () => getOptions(object as Record<string, unknown>),
async reset() {
return (await reset()).join("\n")
return (await reset()).join('\n');
},
async resetTheme() {
return (await resetTheme()).join("\n")
return (await resetTheme()).join('\n');
},
handler(deps: string[], callback: () => void) {
for (const opt of getOptions(object as Record<string, unknown>)) {
if (deps.some(i => opt.id.startsWith(i)))
opt.connect("changed", callback)
if (deps.some((i) => opt.id.startsWith(i))) opt.connect('changed', callback);
}
},
})
});
}

View File

@@ -1,16 +1,16 @@
import GLib from "gi://GLib?version=2.0"
import GLib from 'gi://GLib?version=2.0';
declare global {
const OPTIONS: string
const TMP: string
const USER: string
const OPTIONS: string;
const TMP: string;
const USER: string;
}
Object.assign(globalThis, {
OPTIONS: `${GLib.get_user_cache_dir()}/ags/hyprpanel/options.json`,
TMP: `${GLib.get_tmp_dir()}/ags/hyprpanel`,
USER: GLib.get_user_name(),
})
});
Utils.ensureDirectory(TMP)
App.addIcons(`${App.configDir}/assets`)
Utils.ensureDirectory(TMP);
App.addIcons(`${App.configDir}/assets`);

View File

@@ -1,5 +1,5 @@
import { MprisPlayer } from "types/service/mpris";
const mpris = await Service.import("mpris");
import { MprisPlayer } from 'types/service/mpris';
const mpris = await Service.import('mpris');
export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]): MprisPlayer => {
const statusOrder = {
@@ -12,18 +12,12 @@ export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]):
return mpris.players[0];
}
const isPlaying = mpris.players.some(
(p: MprisPlayer) => p.play_back_status === "Playing",
);
const isPlaying = mpris.players.some((p: MprisPlayer) => p.play_back_status === 'Playing');
const playerStillExists = mpris.players.some(
(p) => activePlayer.bus_name === p.bus_name
);
const playerStillExists = mpris.players.some((p) => activePlayer.bus_name === p.bus_name);
const nextPlayerUp = mpris.players.sort(
(a: MprisPlayer, b: MprisPlayer) =>
statusOrder[a.play_back_status] -
statusOrder[b.play_back_status],
(a: MprisPlayer, b: MprisPlayer) => statusOrder[a.play_back_status] - statusOrder[b.play_back_status],
)[0];
if (isPlaying || !playerStillExists) {
@@ -31,4 +25,4 @@ export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]):
}
return activePlayer;
}
};

View File

@@ -1,10 +1,7 @@
import { Notification } from "types/service/notifications";
import { Notification } from 'types/service/notifications';
export const filterNotifications = (notifications: Notification[], filter: string[]): Notification[] => {
const notifFilter = new Set(
filter.map((name: string) => name.toLowerCase().replace(/\s+/g, '_'))
);
const notifFilter = new Set(filter.map((name: string) => name.toLowerCase().replace(/\s+/g, '_')));
const filteredNotifications = notifications.filter((notif: Notification) => {
const normalizedAppName = notif.app_name.toLowerCase().replace(/\s+/g, '_');
@@ -12,4 +9,4 @@ export const filterNotifications = (notifications: Notification[], filter: strin
});
return filteredNotifications;
}
};

5
lib/types/audio.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
export type InputDevices = Button<Box<Box<Label<Attribute>, Attribute>, Attribute>, Attribute>[];
type DummyDevices = Button<Box<Box<Label<Attribute>, Attribute>, Attribute>, Attribute>[];
type RealPlaybackDevices = Button<Box<Box<Label<Attribute>, Attribute>, Attribute>, Attribute>[];
export type PlaybackDevices = DummyDevices | RealPlaybackDevices;

58
lib/types/bar.d.ts vendored
View File

@@ -1,43 +1,45 @@
import { Binding, Connectable } from "types/service"
import { Variable } from "types/variable"
import Box from "types/widgets/box";
import Label from "types/widgets/label";
import { Widget as WidgetType } from "types/widgets/widget"
import { Binding, Connectable } from 'types/service';
import { Variable } from 'types/variable';
import Box from 'types/widgets/box';
import Button from 'types/widgets/button';
import Label from 'types/widgets/label';
import { Attribute, Child } from './widget';
export type Child = {
export type BarBoxChild = {
component: Box<Gtk.Widget, unknown>;
isVisible?: boolean;
isVis?: Variable<boolean>;
boxClass: string;
props: ButtonProps;
};
} & ButtonProps;
export type SelfButton = Button<Child, Attribute>;
export type BoxHook = (self: Box<Gtk.Widget, Gtk.Widget>) => void;
export type LabelHook = (self: Label<Gtk.Widget>) => void;
export type Module = {
icon?: string | Binding<string>,
textIcon?: string | Binding<string>,
label?: string | Binding<string>,
labelHook?: LabelHook,
boundLabel?: string,
tooltipText?: string | Binding<string>,
boxClass: string,
props?: ButtonProps,
showLabel?: boolean,
showLabelBinding?: Binding,
hook?: BoxHook,
connection?: Binding<Connectable>
}
icon?: string | Binding<string>;
textIcon?: string | Binding<string>;
label?: string | Binding<string>;
labelHook?: LabelHook;
boundLabel?: string;
tooltipText?: string | Binding<string>;
boxClass: string;
props?: ButtonProps;
showLabel?: boolean;
showLabelBinding?: Binding;
hook?: BoxHook;
connection?: Binding<Connectable>;
};
export type ResourceLabelType = "used/total" | "used" | "percentage" | "free";
export type ResourceLabelType = 'used/total' | 'used' | 'percentage' | 'free';
export type StorageIcon = "󰋊" | "" | "󱛟" | "" | "" | "";
export type StorageIcon = '󰋊' | '' | '󱛟' | '' | '' | '';
export type NetstatIcon = "󰖟" | "󰇚" | "󰕒" | "󰛳" | "" | "󰣺" | "󰖩" | "" | "󰈀";
export type NetstatLabelType = "full" | "in" | "out";
export type RateUnit = "GiB" | "MiB" | "KiB" | "auto";
export type NetstatIcon = '󰖟' | '󰇚' | '󰕒' | '󰛳' | '' | '󰣺' | '󰖩' | '' | '󰈀';
export type NetstatLabelType = 'full' | 'in' | 'out';
export type RateUnit = 'GiB' | 'MiB' | 'KiB' | 'auto';
export type UpdatesIcon = "󰚰" | "󰇚" | "" | "󱑢" | "󱑣" | "󰏖" | "" | "󰏔" | "󰏗";
export type UpdatesIcon = '󰚰' | '󰇚' | '' | '󱑢' | '󱑣' | '󰏖' | '' | '󰏔' | '󰏗';
export type PowerIcon = "" | "" | "󰍃" | "󰿅" | "󰒲" | "󰤄";
export type PowerIcon = '' | '' | '󰍃' | '󰿅' | '󰒲' | '󰤄';

View File

@@ -1,6 +1,13 @@
export type GenericResourceData = {
export type GenericFunction<T, P extends unknown[] = unknown[]> = (...args: P) => T;
export type GenericResourceMetrics = {
total: number;
used: number;
free: number;
percentage: number;
}
};
type GenericResourceData = ResourceUsage & {
free: number;
};
export type Postfix = 'TiB' | 'GiB' | 'MiB' | 'KiB' | 'B';

View File

@@ -1,7 +1,7 @@
import { layoutMap } from "customModules/kblayout/layouts";
import { layoutMap } from 'customModules/kblayout/layouts';
export type KbLabelType = "layout" | "code";
export type KbIcon = "" | "󰌌" | "" | "󰬴" | "󰗊";
export type KbLabelType = 'layout' | 'code';
export type KbIcon = '' | '󰌌' | '' | '󰬴' | '󰗊';
export type HyprctlKeyboard = {
address: string;
@@ -24,10 +24,10 @@ export type HyprctlMouse = {
export type HyprctlDeviceLayout = {
mice: HyprctlMouse[];
keyboards: HyprctlKeyboard[];
tablets: any[];
touch: any[];
switches: any[];
tablets: unknown[];
touch: unknown[];
switches: unknown[];
};
export type LayoutKeys = keyof typeof layoutMap;
export type LayoutValues = typeof layoutMap[LayoutKeys];
export type LayoutValues = (typeof layoutMap)[LayoutKeys];

View File

@@ -1,5 +1,4 @@
export type NetworkResourceData = {
in: string;
out: string;
}
};

View File

@@ -1,9 +1,9 @@
import { Binding } from "lib/utils";
import { Binding } from 'lib/utils';
export type InputHandlerEvents = {
onPrimaryClick?: Binding,
onSecondaryClick?: Binding,
onMiddleClick?: Binding,
onScrollUp?: Binding,
onScrollDown?: Binding,
}
onPrimaryClick?: Binding;
onSecondaryClick?: Binding;
onMiddleClick?: Binding;
onScrollUp?: Binding;
onScrollDown?: Binding;
};

View File

@@ -1,5 +1,5 @@
import { NetstatLabelType, ResourceLabelType } from "../bar";
import { NetstatLabelType, ResourceLabelType } from '../bar';
export const LABEL_TYPES: ResourceLabelType[] = ["used/total", "used", "free", "percentage"];
export const LABEL_TYPES: ResourceLabelType[] = ['used/total', 'used', 'free', 'percentage'];
export const NETWORK_LABEL_TYPES: NetstatLabelType[] = ["full", "in", "out"];
export const NETWORK_LABEL_TYPES: NetstatLabelType[] = ['full', 'in', 'out'];

View File

@@ -1,10 +1,10 @@
import { RateUnit } from "../bar";
import { NetworkResourceData } from "../customModules/network";
import { RateUnit } from '../bar';
import { NetworkResourceData } from '../customModules/network';
export const GET_DEFAULT_NETSTAT_DATA = (dataType: RateUnit): NetworkResourceData => {
if (dataType === 'auto') {
return { in: `0 Kib/s`, out: `0 Kib/s` }
return { in: `0 Kib/s`, out: `0 Kib/s` };
}
return { in: `0 ${dataType}/s`, out: `0 ${dataType}/s` }
return { in: `0 ${dataType}/s`, out: `0 ${dataType}/s` };
};

View File

@@ -1,60 +1,60 @@
export const defaultColorMap = {
"rosewater": "#f5e0dc",
"flamingo": "#f2cdcd",
"pink": "#f5c2e7",
"mauve": "#cba6f7",
"red": "#f38ba8",
"maroon": "#eba0ac",
"peach": "#fab387",
"yellow": "#f9e2af",
"green": "#a6e3a1",
"teal": "#94e2d5",
"sky": "#89dceb",
"sapphire": "#74c7ec",
"blue": "#89b4fa",
"lavender": "#b4befe",
"text": "#cdd6f4",
"subtext1": "#bac2de",
"subtext2": "#a6adc8",
"overlay2": "#9399b2",
"overlay1": "#7f849c",
"overlay0": "#6c7086",
"surface2": "#585b70",
"surface1": "#45475a",
"surface0": "#313244",
"base2": "#242438",
"base": "#1e1e2e",
"mantle": "#181825",
"crust": "#11111b",
"surface1_2": "#454759",
"text2": "#cdd6f3",
"pink2": "#f5c2e6",
"red2": "#f38ba7",
"peach2": "#fab386",
"mantle2": "#181824",
"surface0_2": "#313243",
"surface2_2": "#585b69",
"overlay1_2": "#7f849b",
"lavender2": "#b4befd",
"mauve2": "#cba6f6",
"green2": "#a6e3a0",
"sky2": "#89dcea",
"teal2": "#94e2d4",
"yellow2": "#f9e2ad",
"maroon2": "#eba0ab",
"crust2": "#11111a",
"pink3": "#f5c2e8",
"red3": "#f38ba9",
"mantle3": "#181826",
"surface0_3": "#313245",
"surface2_3": "#585b71",
"overlay1_3": "#7f849d",
"lavender3": "#b4beff",
"mauve3": "#cba6f8",
"green3": "#a6e3a2",
"sky3": "#89dcec",
"teal3": "#94e2d6",
"yellow3": "#f9e2ae",
"maroon3": "#eba0ad",
"crust3": "#11111c",
};
rosewater: '#f5e0dc',
flamingo: '#f2cdcd',
pink: '#f5c2e7',
mauve: '#cba6f7',
red: '#f38ba8',
maroon: '#eba0ac',
peach: '#fab387',
yellow: '#f9e2af',
green: '#a6e3a1',
teal: '#94e2d5',
sky: '#89dceb',
sapphire: '#74c7ec',
blue: '#89b4fa',
lavender: '#b4befe',
text: '#cdd6f4',
subtext1: '#bac2de',
subtext2: '#a6adc8',
overlay2: '#9399b2',
overlay1: '#7f849c',
overlay0: '#6c7086',
surface2: '#585b70',
surface1: '#45475a',
surface0: '#313244',
base2: '#242438',
base: '#1e1e2e',
mantle: '#181825',
crust: '#11111b',
surface1_2: '#454759',
text2: '#cdd6f3',
pink2: '#f5c2e6',
red2: '#f38ba7',
peach2: '#fab386',
mantle2: '#181824',
surface0_2: '#313243',
surface2_2: '#585b69',
overlay1_2: '#7f849b',
lavender2: '#b4befd',
mauve2: '#cba6f6',
green2: '#a6e3a0',
sky2: '#89dcea',
teal2: '#94e2d4',
yellow2: '#f9e2ad',
maroon2: '#eba0ab',
crust2: '#11111a',
pink3: '#f5c2e8',
red3: '#f38ba9',
mantle3: '#181826',
surface0_3: '#313245',
surface2_3: '#585b71',
overlay1_3: '#7f849d',
lavender3: '#b4beff',
mauve3: '#cba6f8',
green3: '#a6e3a2',
sky3: '#89dcec',
teal3: '#94e2d6',
yellow3: '#f9e2ae',
maroon3: '#eba0ad',
crust3: '#11111c',
} as const;

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
import { WindowProps } from "types/widgets/window";
import { WindowProps } from 'types/widgets/window';
import { GtkWidget, Transition } from './widget';
export type DropdownMenuProps = {
name: string;
child: any;
child: GtkWidget;
layout?: string;
transition?: any;
transition?: Transition;
exclusivity?: Exclusivity;
fixed?: boolean;
} & WindowProps;

View File

@@ -1,3 +1,3 @@
export type Config = {
[key: string]: string | number | boolean | object;
}
};

View File

@@ -12,14 +12,14 @@ export type GPU_Stat = {
index: number;
uuid: string;
name: string;
"temperature.gpu": number;
"fan.speed": number;
"utilization.gpu": number;
"utilization.enc": number;
"utilization.dec": number;
"power.draw": number;
"enforced.power.limit": number;
"memory.used": number;
"memory.total": number;
'temperature.gpu': number;
'fan.speed': number;
'utilization.gpu': number;
'utilization.enc': number;
'utilization.dec': number;
'power.draw': number;
'enforced.power.limit': number;
'memory.used': number;
'memory.total': number;
processes: Process[];
};

View File

@@ -1,3 +1,2 @@
export type LoopStatus = 'none' | 'track' | 'playlist';
export type PlaybackStatus = 'playing' | 'paused' | 'stopped';

View File

@@ -1,4 +1,4 @@
import { WIFI_STATUS_MAP } from "globals/network";
import { WIFI_STATUS_MAP } from 'globals/network';
export type AccessPoint = {
bssid: string | null;
@@ -9,6 +9,8 @@ export type AccessPoint = {
strength: number;
frequency: number;
iconName: string | undefined;
}
};
export type WifiStatus = keyof typeof WIFI_STATUS_MAP;
export type WifiIcon = '󰤩' | '󰤨' | '󰤪' | '󰤨' | '󰤩' | '󰤮' | '󰤨' | '󰤥' | '󰤢' | '󰤟' | '󰤯';

View File

@@ -1,4 +1,4 @@
import icons from "modules/icons/index";
import icons from 'modules/icons/index';
export interface NotificationArgs {
appName?: string;

301
lib/types/options.d.ts vendored
View File

@@ -1,126 +1,213 @@
import { Opt } from "lib/option";
import { Variable } from "types/variable";
import { Opt } from 'lib/option';
import { Variable } from 'types/variable';
import { defaultColorMap } from './defaults/options';
export type MkOptionsResult<T> = {
configFile: string;
array: () => Opt[];
reset: () => Promise<string>;
resetTheme: () => Promise<string>;
handler: (deps: string[], callback: () => void) => void;
};
export type RecursiveOptionsObject = {
[key: string]: RecursiveOptionsObject | Opt<string | number | boolean> | Opt<any>;
};
export type Unit = "imperial" | "metric";
export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown";
export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left" | "left" | "right";
export type OSDAnchor = "top left" | "top" | "top right" | "right" | "bottom right" | "bottom" | "bottom left" | "left";
export type BarButtonStyles = "default" | "split" | "wave" | "wave2";
export type Unit = 'imperial' | 'metric';
export type PowerOptions = 'sleep' | 'reboot' | 'logout' | 'shutdown';
export type NotificationAnchor =
| 'top'
| 'top right'
| 'top left'
| 'bottom'
| 'bottom right'
| 'bottom left'
| 'left'
| 'right';
export type OSDAnchor = 'top left' | 'top' | 'top right' | 'right' | 'bottom right' | 'bottom' | 'bottom left' | 'left';
export type BarButtonStyles = 'default' | 'split' | 'wave' | 'wave2';
export type ThemeExportData = {
filePath: string,
themeOnly: boolean
}
filePath: string;
themeOnly: boolean;
};
export type RowProps<T> = {
opt: Opt<T>
title: string
note?: string
opt: Opt<T>;
title: string;
note?: string;
type?:
| "number"
| "color"
| "float"
| "object"
| "string"
| "enum"
| "boolean"
| "img"
| "wallpaper"
| "export"
| "import"
| "config_import"
| "font"
enums?: string[]
max?: number
min?: number
disabledBinding?: Variable<boolean>
exportData?: ThemeExportData
subtitle?: string | VarType<any> | Opt,
subtitleLink?: string,
dependencies?: string[],
increment?: number
}
| 'number'
| 'color'
| 'float'
| 'object'
| 'string'
| 'enum'
| 'boolean'
| 'img'
| 'wallpaper'
| 'export'
| 'import'
| 'config_import'
| 'font';
enums?: T[];
max?: number;
min?: number;
disabledBinding?: Variable<boolean>;
exportData?: ThemeExportData;
subtitle?: string | VarType<any> | Opt;
subtitleLink?: string;
dependencies?: string[];
increment?: number;
};
export type OSDOrientation = "horizontal" | "vertical";
export type OSDOrientation = 'horizontal' | 'vertical';
export type HexColor = `#${string}`;
export type WindowLayer = "top" | "bottom" | "overlay" | "background";
export type WindowLayer = 'top' | 'bottom' | 'overlay' | 'background';
export type ActiveWsIndicator = 'underline' | 'highlight' | 'color';
export type MatugenColors = {
"background": HexColor,
"error": HexColor,
"error_container": HexColor,
"inverse_on_surface": HexColor,
"inverse_primary": HexColor,
"inverse_surface": HexColor,
"on_background": HexColor,
"on_error": HexColor,
"on_error_container": HexColor,
"on_primary": HexColor,
"on_primary_container": HexColor,
"on_primary_fixed": HexColor,
"on_primary_fixed_variant": HexColor,
"on_secondary": HexColor,
"on_secondary_container": HexColor,
"on_secondary_fixed": HexColor,
"on_secondary_fixed_variant": HexColor,
"on_surface": HexColor,
"on_surface_variant": HexColor,
"on_tertiary": HexColor,
"on_tertiary_container": HexColor,
"on_tertiary_fixed": HexColor,
"on_tertiary_fixed_variant": HexColor,
"outline": HexColor,
"outline_variant": HexColor,
"primary": HexColor,
"primary_container": HexColor,
"primary_fixed": HexColor,
"primary_fixed_dim": HexColor,
"scrim": HexColor,
"secondary": HexColor,
"secondary_container": HexColor,
"secondary_fixed": HexColor,
"secondary_fixed_dim": HexColor,
"shadow": HexColor,
"surface": HexColor,
"surface_bright": HexColor,
"surface_container": HexColor,
"surface_container_high": HexColor,
"surface_container_highest": HexColor,
"surface_container_low": HexColor,
"surface_container_lowest": HexColor,
"surface_dim": HexColor,
"surface_variant": HexColor,
"tertiary": HexColor,
"tertiary_container": HexColor,
"tertiary_fixed": HexColor,
"tertiary_fixed_dim": HexColor
}
background: HexColor;
error: HexColor;
error_container: HexColor;
inverse_on_surface: HexColor;
inverse_primary: HexColor;
inverse_surface: HexColor;
on_background: HexColor;
on_error: HexColor;
on_error_container: HexColor;
on_primary: HexColor;
on_primary_container: HexColor;
on_primary_fixed: HexColor;
on_primary_fixed_variant: HexColor;
on_secondary: HexColor;
on_secondary_container: HexColor;
on_secondary_fixed: HexColor;
on_secondary_fixed_variant: HexColor;
on_surface: HexColor;
on_surface_variant: HexColor;
on_tertiary: HexColor;
on_tertiary_container: HexColor;
on_tertiary_fixed: HexColor;
on_tertiary_fixed_variant: HexColor;
outline: HexColor;
outline_variant: HexColor;
primary: HexColor;
primary_container: HexColor;
primary_fixed: HexColor;
primary_fixed_dim: HexColor;
scrim: HexColor;
secondary: HexColor;
secondary_container: HexColor;
secondary_fixed: HexColor;
secondary_fixed_dim: HexColor;
shadow: HexColor;
surface: HexColor;
surface_bright: HexColor;
surface_container: HexColor;
surface_container_high: HexColor;
surface_container_highest: HexColor;
surface_container_low: HexColor;
surface_container_lowest: HexColor;
surface_dim: HexColor;
surface_variant: HexColor;
tertiary: HexColor;
tertiary_container: HexColor;
tertiary_fixed: HexColor;
tertiary_fixed_dim: HexColor;
};
type MatugenScheme =
| "content"
| "expressive"
| "fidelity"
| "fruit-salad"
| "monochrome"
| "neutral"
| "rainbow"
| "tonal-spot";
export type MatugenVariation = {
rosewater: HexColor;
flamingo: HexColor;
pink: HexColor;
mauve: HexColor;
red: HexColor;
maroon: HexColor;
peach: HexColor;
yellow: HexColor;
green: HexColor;
teal: HexColor;
sky: HexColor;
sapphire: HexColor;
blue: HexColor;
lavender: HexColor;
text: HexColor;
subtext1: HexColor;
subtext2: HexColor;
overlay2: HexColor;
overlay1: HexColor;
overlay0: HexColor;
surface2: HexColor;
surface1: HexColor;
surface0: HexColor;
base2: HexColor;
base: HexColor;
mantle: HexColor;
crust: HexColor;
notifications_closer: HexColor;
notifications_background: HexColor;
dashboard_btn_text: HexColor;
red2: HexColor;
peach2: HexColor;
pink2: HexColor;
mantle2: HexColor;
surface1_2: HexColor;
surface0_2: HexColor;
overlay1_2: HexColor;
text2: HexColor;
lavender2: HexColor;
crust2: HexColor;
maroon2: HexColor;
mauve2: HexColor;
green2: HexColor;
surface2_2: HexColor;
sky2: HexColor;
teal2: HexColor;
yellow2: HexColor;
pink3: HexColor;
red3: HexColor;
mantle3: HexColor;
surface0_3: HexColor;
surface2_3: HexColor;
overlay1_3: HexColor;
lavender3: HexColor;
mauve3: HexColor;
green3: HexColor;
sky3: HexColor;
teal3: HexColor;
yellow3: HexColor;
maroon3: HexColor;
crust3: HexColor;
notifications_closer?: HexColor;
notifications_background?: HexColor;
dashboard_btn_text?: HexColor;
};
export type MatugenScheme =
| 'content'
| 'expressive'
| 'fidelity'
| 'fruit-salad'
| 'monochrome'
| 'neutral'
| 'rainbow'
| 'tonal-spot';
type MatugenVariation =
| "standard_1"
| "standard_2"
| "standard_3"
| "monochrome_1"
| "monochrome_2"
| "monochrome_3"
| "vivid_1"
| "vivid_2"
| "vivid_3"
export type MatugenVariations =
| 'standard_1'
| 'standard_2'
| 'standard_3'
| 'monochrome_1'
| 'monochrome_2'
| 'monochrome_3'
| 'vivid_1'
| 'vivid_2'
| 'vivid_3';
type MatugenTheme = "light" | "dark";
type MatugenTheme = 'light' | 'dark';
export type ColorMapKey = keyof typeof defaultColorMap;
export type ColorMapValue = (typeof defaultColorMap)[ColorMapKey];

View File

@@ -1,6 +1,6 @@
import { Widget } from "types/widgets/widget";
import { WindowProps } from "types/widgets/window";
import { Transition } from "./widget";
import { Widget } from 'types/widgets/widget';
import { WindowProps } from 'types/widgets/window';
import { Transition } from './widget';
export type PopupWindowProps = {
name: string;
@@ -13,15 +13,23 @@ export type PopupWindowProps = {
export type LayoutFunction = (
name: string,
child: Widget,
transition: Transition
transition: Transition,
) => {
center: () => Widget;
top: () => Widget;
"top-right": () => Widget;
"top-center": () => Widget;
"top-left": () => Widget;
"bottom-left": () => Widget;
"bottom-center": () => Widget;
"bottom-right": () => Widget;
'top-right': () => Widget;
'top-center': () => Widget;
'top-left': () => Widget;
'bottom-left': () => Widget;
'bottom-center': () => Widget;
'bottom-right': () => Widget;
};
export type Layouts = 'center' | 'top' | 'top-right' | 'top-center' | 'top-left' | 'bottom-left' | 'bottom-center' | 'bottom-right';
export type Layouts =
| 'center'
| 'top'
| 'top-right'
| 'top-center'
| 'top-left'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';

View File

@@ -1 +1 @@
export type Action = "sleep" | "reboot" | "logout" | "shutdown";
export type Action = 'sleep' | 'reboot' | 'logout' | 'shutdown';

View File

@@ -1,8 +1,8 @@
import icons from "modules/icons/index";
import PowerProfiles from "types/service/powerprofiles.js"
import icons from 'modules/icons/index';
import PowerProfiles from 'types/service/powerprofiles.js';
export type PowerProfiles = InstanceType<typeof PowerProfiles>;
export type PowerProfile = "power-saver" | "balanced" | "performance";
export type PowerProfile = 'power-saver' | 'balanced' | 'performance';
export type PowerProfileObject = {
[key: string]: string;
}
};

0
lib/types/systray.d.ts vendored Normal file
View File

View File

@@ -1,3 +1,6 @@
import { substitutes } from "lib/icons";
import { substitutes } from 'lib/icons';
type SubstituteKeys = keyof typeof substitutes;
export type ThrottleFn = (cmd: string, fn: ((output: string) => void) | undefined) => void;
export type ThrottleFnCallback = ((output: string) => void) | undefined;

1
lib/types/variable.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export type Bind = OriginalBinding<GObject.Object, keyof Props<GObject.Object>, unknown>;

View File

@@ -1,3 +1,3 @@
export type VolumeIcons = {
[index: number]: string
}
[index: number]: string;
};

View File

@@ -1,12 +1,12 @@
import { weatherIcons } from "modules/icons/weather";
import { weatherIcons } from 'modules/icons/weather';
export type UnitType = "imperial" | "metric";
export type UnitType = 'imperial' | 'metric';
export type Weather = {
location: Location;
current: Current;
forecast: Forecast;
}
};
export type Current = {
last_updated_epoch?: number;
@@ -45,17 +45,17 @@ export type Current = {
chance_of_rain?: number;
will_it_snow?: number;
chance_of_snow?: number;
}
};
export type Condition = {
text: string;
icon: string;
code: number;
}
};
export type Forecast = {
forecastday: Forecastday[];
}
};
export type Forecastday = {
date: string;
@@ -63,7 +63,7 @@ export type Forecastday = {
day: Day;
astro: Astro;
hour: Current[];
}
};
export type Astro = {
sunrise: string;
@@ -74,7 +74,7 @@ export type Astro = {
moon_illumination: number;
is_moon_up: number;
is_sun_up: number;
}
};
export type Day = {
maxtemp_c: number;
@@ -97,7 +97,7 @@ export type Day = {
daily_chance_of_snow: number;
condition: Condition;
uv: number;
}
};
export type Location = {
name: string;
@@ -108,11 +108,11 @@ export type Location = {
tz_id: string;
localtime_epoch: number;
localtime: string;
}
};
export type TemperatureIconColorMap = {
[key: number]: string;
}
};
export type WeatherIconTitle = keyof typeof weatherIcons;
export type WeatherIcon = typeof weatherIcons[WeatherIconTitle];
export type WeatherIcon = (typeof weatherIcons)[WeatherIconTitle];

32
lib/types/widget.d.ts vendored
View File

@@ -1,6 +1,28 @@
export type Exclusivity = 'normal' | 'ignore' | 'exclusive';
export type Anchor = "left" | "right" | "top" | "down";
export type Transition = "none" | "crossfade" | "slide_right" | "slide_left" | "slide_up" | "slide_down";
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Box from 'types/widgets/box';
// Window
export type Layouts = 'center' | 'top' | 'top-right' | 'top-center' | 'top-left' | 'bottom-left' | 'bottom-center' | 'bottom-right';
export type Exclusivity = 'normal' | 'ignore' | 'exclusive';
export type Anchor = 'left' | 'right' | 'top' | 'down';
export type Transition = 'none' | 'crossfade' | 'slide_right' | 'slide_left' | 'slide_up' | 'slide_down';
export type Layouts =
| 'center'
| 'top'
| 'top-right'
| 'top-center'
| 'top-left'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';
export type Attribute = unknown;
export type Child = Gtk.Widget;
export type GtkWidget = Gtk.Widget;
export type BoxWidget = Box<GtkWidget, Child>;
export type GButton = Gtk.Button;
export type GBox = Gtk.Box;
export type GLabel = Gtk.Label;
export type GCenterBox = Gtk.Box;
export type EventHandler<Self> = (self: Self, event: Gdk.Event) => boolean | unknown;

View File

@@ -1,8 +1,12 @@
export type WorkspaceRule = {
workspaceString: string,
monitor: string,
}
workspaceString: string;
monitor: string;
};
export type WorkspaceMap = {
[key: string]: number[],
}
[key: string]: number[];
};
export type MonitorMap = {
[key: number]: string;
};

View File

@@ -1,29 +1,27 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { type Application } from "types/service/applications"
import { NotificationAnchor } from "./types/options"
import { OSDAnchor } from "lib/types/options";
import icons, { substitutes } from "./icons"
import Gtk from "gi://Gtk?version=3.0"
import Gdk from "gi://Gdk"
import GLib from "gi://GLib?version=2.0"
import GdkPixbuf from "gi://GdkPixbuf";
import { NotificationArgs } from "types/utils/notify"
import { SubstituteKeys } from "./types/utils";
export type Binding<T> = import("types/service").Binding<any, any, T>
import { type Application } from 'types/service/applications';
import { NotificationAnchor } from './types/options';
import { OSDAnchor } from 'lib/types/options';
import icons, { substitutes } from './icons';
import Gtk from 'gi://Gtk?version=3.0';
import Gdk from 'gi://Gdk';
import GLib from 'gi://GLib?version=2.0';
import GdkPixbuf from 'gi://GdkPixbuf';
import { NotificationArgs } from 'types/utils/notify';
import { SubstituteKeys } from './types/utils';
import { Window } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
export type Binding<T> = import('types/service').Binding<any, any, T>;
/**
* @returns substitute icon || name || fallback icon
*/
export function icon(name: string | null, fallback = icons.missing) {
* @returns substitute icon || name || fallback icon
*/
export function icon(name: string | null, fallback = icons.missing): string {
const validateSubstitute = (name: string): name is SubstituteKeys => name in substitutes;
if (!name)
return fallback || ""
if (!name) return fallback || '';
if (GLib.file_test(name, GLib.FileTest.EXISTS))
return name
if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name;
let icon: string = name;
@@ -31,38 +29,36 @@ export function icon(name: string | null, fallback = icons.missing) {
icon = substitutes[name];
}
if (Utils.lookUpIcon(icon))
return icon
if (Utils.lookUpIcon(icon)) return icon;
print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`)
return fallback
print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`);
return fallback;
}
/**
* @returns execAsync(["bash", "-c", cmd])
*/
export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]) {
const cmd = typeof strings === "string" ? strings : strings
.flatMap((str, i) => str + `${values[i] ?? ""}`)
.join("")
export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]): Promise<string> {
const cmd =
typeof strings === 'string' ? strings : strings.flatMap((str, i) => str + `${values[i] ?? ''}`).join('');
return Utils.execAsync(["bash", "-c", cmd]).catch(err => {
console.error(cmd, err)
return ""
})
return Utils.execAsync(['bash', '-c', cmd]).catch((err) => {
console.error(cmd, err);
return '';
});
}
/**
* @returns execAsync(cmd)
*/
export async function sh(cmd: string | string[]) {
return Utils.execAsync(cmd).catch(err => {
console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err)
return ""
})
export async function sh(cmd: string | string[]): Promise<string> {
return Utils.execAsync(cmd).catch((err) => {
console.error(typeof cmd === 'string' ? cmd : cmd.join(' '), err);
return '';
});
}
export function forMonitors(widget: (monitor: number) => Gtk.Window) {
export function forMonitors(widget: (monitor: number) => Gtk.Window): Window[] {
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
return range(n, 0).flatMap(widget);
}
@@ -70,64 +66,62 @@ export function forMonitors(widget: (monitor: number) => Gtk.Window) {
/**
* @returns [start...length]
*/
export function range(length: number, start = 1) {
return Array.from({ length }, (_, i) => i + start)
export function range(length: number, start = 1): number[] {
return Array.from({ length }, (_, i) => i + start);
}
/**
* @returns true if all of the `bins` are found
*/
export function dependencies(...bins: string[]) {
const missing = bins.filter(bin => Utils.exec({
cmd: `which ${bin}`,
out: () => false,
err: () => true,
}))
export function dependencies(...bins: string[]): boolean {
const missing = bins.filter((bin) =>
Utils.exec({
cmd: `which ${bin}`,
out: () => false,
err: () => true,
}),
);
if (missing.length > 0) {
console.warn(Error(`missing dependencies: ${missing.join(", ")}`))
console.warn(Error(`missing dependencies: ${missing.join(', ')}`));
Notify({
summary: "Dependencies not found!",
body: `The following dependencies are missing: ${missing.join(", ")}`,
summary: 'Dependencies not found!',
body: `The following dependencies are missing: ${missing.join(', ')}`,
iconName: icons.ui.warning,
timeout: 7000
timeout: 7000,
});
}
return missing.length === 0
return missing.length === 0;
}
/**
* run app detached
*/
export function launchApp(app: Application) {
export function launchApp(app: Application): void {
const exe = app.executable
.split(/\s+/)
.filter(str => !str.startsWith("%") && !str.startsWith("@"))
.join(" ")
.filter((str) => !str.startsWith('%') && !str.startsWith('@'))
.join(' ');
bash(`${exe} &`)
app.frequency += 1
bash(`${exe} &`);
app.frequency += 1;
}
/**
* to use with drag and drop
*/
export function createSurfaceFromWidget(widget: Gtk.Widget) {
export function createSurfaceFromWidget(widget: Gtk.Widget): GdkPixbuf.Pixbuf {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const cairo = imports.gi.cairo as any
const alloc = widget.get_allocation()
const surface = new cairo.ImageSurface(
cairo.Format.ARGB32,
alloc.width,
alloc.height,
)
const cr = new cairo.Context(surface)
cr.setSourceRGBA(255, 255, 255, 0)
cr.rectangle(0, 0, alloc.width, alloc.height)
cr.fill()
widget.draw(cr)
return surface
const cairo = imports.gi.cairo as any;
const alloc = widget.get_allocation();
const surface = new cairo.ImageSurface(cairo.Format.ARGB32, alloc.width, alloc.height);
const cr = new cairo.Context(surface);
cr.setSourceRGBA(255, 255, 255, 0);
cr.rectangle(0, 0, alloc.width, alloc.height);
cr.fill();
widget.draw(cr);
return surface;
}
/**
@@ -138,9 +132,10 @@ export const isAnImage = (imgFilePath: string): boolean => {
GdkPixbuf.Pixbuf.new_from_file(imgFilePath);
return true;
} catch (error) {
console.error(error);
return false;
}
}
};
export const Notify = (notifPayload: NotificationArgs): void => {
let command = 'notify-send';
@@ -154,20 +149,20 @@ export const Notify = (notifPayload: NotificationArgs): void => {
if (notifPayload.transient) command += ` -e`;
if (notifPayload.id !== undefined) command += ` -r ${notifPayload.id}`;
Utils.execAsync(command)
}
Utils.execAsync(command);
};
export function getPosition(pos: NotificationAnchor | OSDAnchor): ("top" | "bottom" | "left" | "right")[] {
const positionMap: { [key: string]: ("top" | "bottom" | "left" | "right")[] } = {
"top": ["top"],
"top right": ["top", "right"],
"top left": ["top", "left"],
"bottom": ["bottom"],
"bottom right": ["bottom", "right"],
"bottom left": ["bottom", "left"],
"right": ["right"],
"left": ["left"],
export function getPosition(pos: NotificationAnchor | OSDAnchor): ('top' | 'bottom' | 'left' | 'right')[] {
const positionMap: { [key: string]: ('top' | 'bottom' | 'left' | 'right')[] } = {
top: ['top'],
'top right': ['top', 'right'],
'top left': ['top', 'left'],
bottom: ['bottom'],
'bottom right': ['bottom', 'right'],
'bottom left': ['bottom', 'left'],
right: ['right'],
left: ['left'],
};
return positionMap[pos] || ["top"];
return positionMap[pos] || ['top'];
}

View File

@@ -1,16 +1,15 @@
import GLib from "gi://GLib"
import GLib from 'gi://GLib';
import { DateTime } from 'types/@girs/glib-2.0/glib-2.0.cjs';
export const clock = Variable(GLib.DateTime.new_now_local(), {
poll: [1000, () => GLib.DateTime.new_now_local()],
})
poll: [1000, (): DateTime => GLib.DateTime.new_now_local()],
});
export const uptime = Variable(0, {
poll: [60_000, "cat /proc/uptime", line =>
Number.parseInt(line.split(".")[0]) / 60,
],
})
poll: [60_000, 'cat /proc/uptime', (line): number => Number.parseInt(line.split('.')[0]) / 60],
});
export const distro = {
id: GLib.get_os_info("ID"),
logo: GLib.get_os_info("LOGO"),
}
id: GLib.get_os_info('ID'),
logo: GLib.get_os_info('LOGO'),
};

30
main.ts
View File

@@ -1,27 +1,21 @@
import "lib/session";
import "scss/style";
import "globals/useTheme";
import "globals/mousePos";
import 'lib/session';
import 'scss/style';
import 'globals/useTheme';
import 'globals/mousePos';
import { Bar } from "modules/bar/Bar";
import MenuWindows from "./modules/menus/main.js";
import SettingsDialog from "widget/settings/SettingsDialog";
import Notifications from "./modules/notifications/index.js";
import { forMonitors } from "lib/utils";
import OSD from "modules/osd/index";
import { Bar } from 'modules/bar/Bar';
import MenuWindows from './modules/menus/main.js';
import SettingsDialog from 'widget/settings/SettingsDialog';
import Notifications from './modules/notifications/index.js';
import { forMonitors } from 'lib/utils';
import OSD from 'modules/osd/index';
App.config({
onConfigParsed: () => Utils.execAsync(`python3 ${App.configDir}/services/bluetooth.py`),
windows: [
...MenuWindows,
Notifications(),
SettingsDialog(),
...forMonitors(Bar),
OSD(),
],
windows: [...MenuWindows, Notifications(), SettingsDialog(), ...forMonitors(Bar), OSD()],
closeWindowDelay: {
sideright: 350,
launcher: 350,
bar0: 350,
},
})
});

View File

@@ -1,8 +1,10 @@
const hyprland = await Service.import("hyprland");
const hyprland = await Service.import('hyprland');
import {
Menu,
Workspaces, ClientTitle, Media,
Workspaces,
ClientTitle,
Media,
Notifications,
Volume,
Network,
@@ -20,54 +22,57 @@ import {
Updates,
Weather,
Power,
} from "./Exports"
} from './Exports';
import { BarItemBox as WidgetContainer } from "../shared/barItemBox.js";
import options from "options";
import Gdk from "gi://Gdk?version=3.0";
import Button from "types/widgets/button.js";
import Gtk from "types/@girs/gtk-3.0/gtk-3.0.js";
import { BarItemBox as WidgetContainer } from '../shared/barItemBox.js';
import options from 'options';
import Gdk from 'gi://Gdk?version=3.0';
import Button from 'types/widgets/button.js';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js';
import './SideEffects';
import { WindowLayer } from "lib/types/options.js";
import { WindowLayer } from 'lib/types/options.js';
import { Attribute, Child } from 'lib/types/widget.js';
import Window from 'types/widgets/window.js';
const { layouts } = options.bar;
export type BarWidget = keyof typeof widget;
type Section = "battery"
| "dashboard"
| "workspaces"
| "windowtitle"
| "media"
| "notifications"
| "volume"
| "network"
| "bluetooth"
| "clock"
| "ram"
| "cpu"
| "storage"
| "netstat"
| "kbinput"
| "updates"
| "weather"
| "power"
| "systray";
type Section =
| 'battery'
| 'dashboard'
| 'workspaces'
| 'windowtitle'
| 'media'
| 'notifications'
| 'volume'
| 'network'
| 'bluetooth'
| 'clock'
| 'ram'
| 'cpu'
| 'storage'
| 'netstat'
| 'kbinput'
| 'updates'
| 'weather'
| 'power'
| 'systray';
type Layout = {
left: Section[],
middle: Section[],
right: Section[],
}
left: Section[];
middle: Section[];
right: Section[];
};
type BarLayout = {
[key: string]: Layout
}
[key: string]: Layout;
};
const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
const matchingKey = Object.keys(layouts).find(key => key === monitor.toString());
const wildcard = Object.keys(layouts).find(key => key === "*");
const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString());
const wildcard = Object.keys(layouts).find((key) => key === '*');
if (matchingKey) {
return layouts[matchingKey];
@@ -77,62 +82,48 @@ const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
return layouts[wildcard];
}
return {
left: [
"dashboard",
"workspaces",
"windowtitle"
],
middle: [
"media"
],
right: [
"volume",
"network",
"bluetooth",
"battery",
"systray",
"clock",
"notifications"
]
return {
left: ['dashboard', 'workspaces', 'windowtitle'],
middle: ['media'],
right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'],
};
}
};
const widget = {
battery: () => WidgetContainer(BatteryLabel()),
dashboard: () => WidgetContainer(Menu()),
workspaces: (monitor: number) => WidgetContainer(Workspaces(monitor)),
windowtitle: () => WidgetContainer(ClientTitle()),
media: () => WidgetContainer(Media()),
notifications: () => WidgetContainer(Notifications()),
volume: () => WidgetContainer(Volume()),
network: () => WidgetContainer(Network()),
bluetooth: () => WidgetContainer(Bluetooth()),
clock: () => WidgetContainer(Clock()),
systray: () => WidgetContainer(SysTray()),
ram: () => WidgetContainer(Ram()),
cpu: () => WidgetContainer(Cpu()),
storage: () => WidgetContainer(Storage()),
netstat: () => WidgetContainer(Netstat()),
kbinput: () => WidgetContainer(KbInput()),
updates: () => WidgetContainer(Updates()),
weather: () => WidgetContainer(Weather()),
power: () => WidgetContainer(Power()),
battery: (): Button<Child, Attribute> => WidgetContainer(BatteryLabel()),
dashboard: (): Button<Child, Attribute> => WidgetContainer(Menu()),
workspaces: (monitor: number): Button<Child, Attribute> => WidgetContainer(Workspaces(monitor)),
windowtitle: (): Button<Child, Attribute> => WidgetContainer(ClientTitle()),
media: (): Button<Child, Attribute> => WidgetContainer(Media()),
notifications: (): Button<Child, Attribute> => WidgetContainer(Notifications()),
volume: (): Button<Child, Attribute> => WidgetContainer(Volume()),
network: (): Button<Child, Attribute> => WidgetContainer(Network()),
bluetooth: (): Button<Child, Attribute> => WidgetContainer(Bluetooth()),
clock: (): Button<Child, Attribute> => WidgetContainer(Clock()),
systray: (): Button<Child, Attribute> => WidgetContainer(SysTray()),
ram: (): Button<Child, Attribute> => WidgetContainer(Ram()),
cpu: (): Button<Child, Attribute> => WidgetContainer(Cpu()),
storage: (): Button<Child, Attribute> => WidgetContainer(Storage()),
netstat: (): Button<Child, Attribute> => WidgetContainer(Netstat()),
kbinput: (): Button<Child, Attribute> => WidgetContainer(KbInput()),
updates: (): Button<Child, Attribute> => WidgetContainer(Updates()),
weather: (): Button<Child, Attribute> => WidgetContainer(Weather()),
power: (): Button<Child, Attribute> => WidgetContainer(Power()),
};
type GdkMonitors = {
[key: string]: {
key: string,
model: string,
used: boolean
}
key: string;
model: string;
used: boolean;
};
};
function getGdkMonitors(): GdkMonitors {
const display = Gdk.Display.get_default();
if (display === null) {
console.error("Failed to get Gdk display.");
console.error('Failed to get Gdk display.');
return {};
}
@@ -162,35 +153,35 @@ function getGdkMonitors(): GdkMonitors {
* NOTE: Some more funky stuff being done by GDK.
* We render windows/bar based on the monitor ID. So if you have 3 monitors, then your
* monitor IDs will be [0, 1, 2]. Hyprland will NEVER change what ID belongs to what monitor.
*
*
* So if hyprland determines id 0 = DP-1, even after you unplug, shut off or restart your monitor,
* the id 0 will ALWAYS be DP-1.
*
*
* However, GDK (the righteous genius that it is) will change the order of ID anytime your monitor
* setup is changed. So if you unplug your monitor and plug it back it, it now becomes the last id.
* So if DP-1 was id 0 and you unplugged it, it will reconfigure to id 2. This sucks because now
* there's a mismtach between what GDK determines the monitor is at id 2 and what Hyprland determines
* is at id 2.
*
*
* So for that reason, we need to redirect the input `monitor` that the Bar module takes in, to the
* proper Hyprland monitor. So when monitor id 0 comes in, we need to find what the id of that monitor
* is being determined as by Hyprland so the bars show up on the right monitors.
*
*
* Since GTK3 doesn't contain connection names and only monitor models, we have to make the best guess
* in the case that there are multiple models in the same resolution with the same scale. We find the
* 'right' monitor by checking if the model matches along with the resolution and scale. If monitor at
* ID 0 for GDK is being reported as 'MSI MAG271CQR' we find the same model in the Hyprland monitor list
* and check if the resolution and scaling is the same... if it is then we determine it's a match.
*
*
* The edge-case that we just can't handle is if you have the same monitors in the same resolution at the same
* scale. So if you've got 2 'MSI MAG271CQR' monitors at 2560x1440 at scale 1, then we just match the first
* monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration.
* You may have the bar showing up on the wrong one in this case because we don't know what the connector id
* monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration.
* You may have the bar showing up on the wrong one in this case because we don't know what the connector id
* is of either of these monitors (DP-1, DP-2) which are unique values - as these are only in GTK4.
*
*
* Keep in mind though, this is ONLY an issue if you change your monitor setup by plugging in a new one, restarting
* an existing one or shutting it off.
*
*
* If your monitors aren't changed in the current session you're in then none of this safeguarding is relevant.
*
* Fun stuff really... :facepalm:
@@ -200,7 +191,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
const gdkMonitors = getGdkMonitors();
if (Object.keys(gdkMonitors).length === 0) {
console.error("No GDK monitors were found.");
console.error('No GDK monitors were found.');
return monitor;
}
@@ -208,7 +199,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
const gdkMonitor = gdkMonitors[monitor];
// First pass: Strict matching including the monitor index (i.e., hypMon.id === monitor + resolution+scale criteria)
const directMatch = hyprland.monitors.find(hypMon => {
const directMatch = hyprland.monitors.find((hypMon) => {
const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`;
return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id) && hypMon.id === monitor;
});
@@ -219,7 +210,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
}
// Second pass: Relaxed matching without considering the monitor index
const hyprlandMonitor = hyprland.monitors.find(hypMon => {
const hyprlandMonitor = hyprland.monitors.find((hypMon) => {
const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`;
return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id);
});
@@ -230,7 +221,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
}
// Fallback: Find the first available monitor ID that hasn't been used
const fallbackMonitor = hyprland.monitors.find(hypMon => !usedHyprlandMonitors.has(hypMon.id));
const fallbackMonitor = hyprland.monitors.find((hypMon) => !usedHyprlandMonitors.has(hypMon.id));
if (fallbackMonitor) {
usedHyprlandMonitors.add(fallbackMonitor.id);
@@ -253,62 +244,63 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
export const Bar = (() => {
const usedHyprlandMonitors = new Set<number>();
return (monitor: number) => {
return (monitor: number): Window<Child, Attribute> => {
const hyprlandMonitor = gdkMonitorIdToHyprlandId(monitor, usedHyprlandMonitors);
return Widget.Window({
name: `bar-${hyprlandMonitor}`,
class_name: "bar",
class_name: 'bar',
monitor,
visible: true,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
anchor: ['top', 'left', 'right'],
exclusivity: 'exclusive',
layer: Utils.merge(
[
options.theme.bar.layer.bind("value"),
options.tear.bind("value")
],
(
barLayer: WindowLayer,
tear: boolean
) => {
if (tear && barLayer === "overlay") {
return "top";
[options.theme.bar.layer.bind('value'), options.tear.bind('value')],
(barLayer: WindowLayer, tear: boolean) => {
if (tear && barLayer === 'overlay') {
return 'top';
}
return barLayer;
}),
},
),
child: Widget.Box({
class_name: 'bar-panel-container',
child: Widget.CenterBox({
class_name: 'bar-panel',
css: 'padding: 1px',
startWidget: Widget.Box({
class_name: "box-left",
class_name: 'box-left',
hexpand: true,
setup: self => {
setup: (self) => {
self.hook(layouts, (self) => {
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
self.children = foundLayout.left.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
self.children = foundLayout.left
.filter((mod) => Object.keys(widget).includes(mod))
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
});
},
}),
centerWidget: Widget.Box({
class_name: "box-center",
hpack: "center",
setup: self => {
class_name: 'box-center',
hpack: 'center',
setup: (self) => {
self.hook(layouts, (self) => {
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
self.children = foundLayout.middle.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
self.children = foundLayout.middle
.filter((mod) => Object.keys(widget).includes(mod))
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
});
},
}),
endWidget: Widget.Box({
class_name: "box-right",
hpack: "end",
setup: self => {
class_name: 'box-right',
hpack: 'end',
setup: (self) => {
self.hook(layouts, (self) => {
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
self.children = foundLayout.right.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
self.children = foundLayout.right
.filter((mod) => Object.keys(widget).includes(mod))
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
});
},
}),

View File

@@ -1,24 +1,24 @@
import { Menu } from "./menu/index";
import { Workspaces } from "./workspaces/index";
import { ClientTitle } from "./window_title/index";
import { Media } from "./media/index";
import { Notifications } from "./notifications/index";
import { Volume } from "./volume/index";
import { Network } from "./network/index";
import { Bluetooth } from "./bluetooth/index";
import { BatteryLabel } from "./battery/index";
import { Clock } from "./clock/index";
import { SysTray } from "./systray/index";
import { Menu } from './menu/index';
import { Workspaces } from './workspaces/index';
import { ClientTitle } from './window_title/index';
import { Media } from './media/index';
import { Notifications } from './notifications/index';
import { Volume } from './volume/index';
import { Network } from './network/index';
import { Bluetooth } from './bluetooth/index';
import { BatteryLabel } from './battery/index';
import { Clock } from './clock/index';
import { SysTray } from './systray/index';
// Custom Modules
import { Ram } from "../../customModules/ram/index";
import { Cpu } from "../../customModules/cpu/index";
import { Storage } from "customModules/storage/index";
import { Netstat } from "customModules/netstat/index";
import { KbInput } from "customModules/kblayout/index";
import { Updates } from "customModules/updates/index";
import { Weather } from "customModules/weather/index";
import { Power } from "customModules/power/index";
import { Ram } from '../../customModules/ram/index';
import { Cpu } from '../../customModules/cpu/index';
import { Storage } from 'customModules/storage/index';
import { Netstat } from 'customModules/netstat/index';
import { KbInput } from 'customModules/kblayout/index';
import { Updates } from 'customModules/updates/index';
import { Weather } from 'customModules/weather/index';
import { Power } from 'customModules/power/index';
export {
Menu,

View File

@@ -1,14 +1,14 @@
import options from "options";
import options from 'options';
const { showIcon, showTime } = options.bar.clock;
showIcon.connect("changed", () => {
showIcon.connect('changed', () => {
if (!showTime.value && !showIcon.value) {
showTime.value = true;
}
});
showTime.connect("changed", () => {
showTime.connect('changed', () => {
if (!showTime.value && !showIcon.value) {
showIcon.value = true;
}

View File

@@ -1,34 +1,37 @@
const battery = await Service.import("battery");
const battery = await Service.import('battery');
import Gdk from 'gi://Gdk?version=3.0';
import { openMenu } from "../utils.js";
import options from "options";
import { openMenu } from '../utils.js';
import options from 'options';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { label: show_label } = options.bar.battery;
const BatteryLabel = () => {
const BatteryLabel = (): BarBoxChild => {
const isVis = Variable(battery.available);
const batIcon = Utils.merge([battery.bind("percent"), battery.bind("charging"), battery.bind("charged")],
const batIcon = Utils.merge(
[battery.bind('percent'), battery.bind('charging'), battery.bind('charged')],
(batPercent: number, batCharging, batCharged) => {
if (batCharged)
return `battery-level-100-charged-symbolic`;
else
return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`;
});
if (batCharged) return `battery-level-100-charged-symbolic`;
else return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`;
},
);
battery.connect("changed", ({ available }) => {
battery.connect('changed', ({ available }) => {
isVis.value = available;
});
const formatTime = (seconds: number) => {
const formatTime = (seconds: number): Record<string, number> => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return { hours, minutes };
};
const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean) => {
const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean): string => {
if (isCharged) {
return "Fully Charged!!!";
return 'Fully Charged!!!';
}
const { hours, minutes } = formatTime(timeSeconds);
@@ -41,60 +44,56 @@ const BatteryLabel = () => {
return {
component: Widget.Box({
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `battery ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
}),
visible: battery.bind("available"),
tooltip_text: battery.bind("time_remaining").as((t) => t.toString()),
children: Utils.merge(
[battery.bind("available"), show_label.bind("value")],
(batAvail, showLabel) => {
if (batAvail && showLabel) {
return [
Widget.Icon({
class_name: "bar-button-icon battery",
icon: batIcon
}),
Widget.Label({
class_name: "bar-button-label battery",
label: battery.bind("percent").as((p) => `${Math.floor(p)}%`),
}),
];
} else if (batAvail && !showLabel) {
return [
Widget.Icon({
class_name: "bar-button-icon battery",
icon: batIcon
})
];
} else {
return [];
}
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), show_label.bind('value')],
(style, showLabel) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `battery ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
},
),
visible: battery.bind('available'),
tooltip_text: battery.bind('time_remaining').as((t) => t.toString()),
children: Utils.merge([battery.bind('available'), show_label.bind('value')], (batAvail, showLabel) => {
if (batAvail && showLabel) {
return [
Widget.Icon({
class_name: 'bar-button-icon battery',
icon: batIcon,
}),
Widget.Label({
class_name: 'bar-button-label battery',
label: battery.bind('percent').as((p) => `${Math.floor(p)}%`),
}),
];
} else if (batAvail && !showLabel) {
return [
Widget.Icon({
class_name: 'bar-button-icon battery',
icon: batIcon,
}),
];
} else {
return [];
}
}),
setup: (self) => {
self.hook(battery, () => {
if (battery.available) {
self.tooltip_text = generateTooltip(
battery.time_remaining,
battery.charging,
battery.charged,
);
self.tooltip_text = generateTooltip(battery.time_remaining, battery.charging, battery.charged);
}
});
},
}),
isVis,
boxClass: "battery",
boxClass: 'battery',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "energymenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'energymenu');
},
},
};

View File

@@ -1,42 +1,41 @@
const bluetooth = await Service.import('bluetooth')
const bluetooth = await Service.import('bluetooth');
import Gdk from 'gi://Gdk?version=3.0';
import options from "options";
import { openMenu } from "../utils.js";
import options from 'options';
import { openMenu } from '../utils.js';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { label } = options.bar.bluetooth;
const Bluetooth = () => {
const Bluetooth = (): BarBoxChild => {
const btIcon = Widget.Label({
label: bluetooth.bind("enabled").as((v) => v ? "󰂯" : "󰂲"),
class_name: "bar-button-icon bluetooth txt-icon bar",
label: bluetooth.bind('enabled').as((v) => (v ? '󰂯' : '󰂲')),
class_name: 'bar-button-icon bluetooth txt-icon bar',
});
const btText = Widget.Label({
label: Utils.merge([
bluetooth.bind("enabled"),
bluetooth.bind("connected_devices"),
],
(btEnabled, btDevices) => {
return btEnabled && btDevices.length ? ` Connected (${btDevices.length})`
: btEnabled ? "On"
: "Off"
}),
class_name: "bar-button-label bluetooth",
label: Utils.merge([bluetooth.bind('enabled'), bluetooth.bind('connected_devices')], (btEnabled, btDevices) => {
return btEnabled && btDevices.length ? ` Connected (${btDevices.length})` : btEnabled ? 'On' : 'Off';
}),
class_name: 'bar-button-label bluetooth',
});
return {
component: Widget.Box({
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), label.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `bluetooth ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
}),
children: options.bar.bluetooth.label.bind("value").as((showLabel) => {
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), label.bind('value')],
(style, showLabel) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `bluetooth ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
},
),
children: options.bar.bluetooth.label.bind('value').as((showLabel) => {
if (showLabel) {
return [btIcon, btText];
}
@@ -44,14 +43,13 @@ const Bluetooth = () => {
}),
}),
isVisible: true,
boxClass: "bluetooth",
boxClass: 'bluetooth',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "bluetoothmenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'bluetoothmenu');
},
},
};
};
}
export { Bluetooth }
export { Bluetooth };

View File

@@ -1,45 +1,46 @@
import Gdk from 'gi://Gdk?version=3.0';
import GLib from "gi://GLib";
import { openMenu } from "../utils.js";
import options from "options";
import GLib from 'gi://GLib';
import { openMenu } from '../utils.js';
import options from 'options';
import { DateTime } from 'types/@girs/glib-2.0/glib-2.0.cjs';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { format, icon, showIcon, showTime } = options.bar.clock;
const { style } = options.theme.bar.buttons;
const date = Variable(GLib.DateTime.new_now_local(), {
poll: [1000, () => GLib.DateTime.new_now_local()],
poll: [1000, (): DateTime => GLib.DateTime.new_now_local()],
});
const time = Utils.derive([date, format], (c, f) => c.format(f) || "");
const Clock = () => {
const time = Utils.derive([date, format], (c, f) => c.format(f) || '');
const Clock = (): BarBoxChild => {
const clockTime = Widget.Label({
class_name: "bar-button-label clock bar",
class_name: 'bar-button-label clock bar',
label: time.bind(),
});
const clockIcon = Widget.Label({
label: icon.bind("value"),
class_name: "bar-button-icon clock txt-icon bar",
label: icon.bind('value'),
class_name: 'bar-button-icon clock txt-icon bar',
});
return {
component: Widget.Box({
className: Utils.merge([
style.bind("value"),
showIcon.bind("value"), showTime.bind("value")
], (btnStyle, shwIcn, shwLbl) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? "no-label" : ""} ${!shwIcn ? "no-icon" : ""}`;
}),
children: Utils.merge([showIcon.bind("value"), showTime.bind("value")], (shIcn, shTm) => {
className: Utils.merge(
[style.bind('value'), showIcon.bind('value'), showTime.bind('value')],
(btnStyle, shwIcn, shwLbl) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? 'no-label' : ''} ${!shwIcn ? 'no-icon' : ''}`;
},
),
children: Utils.merge([showIcon.bind('value'), showTime.bind('value')], (shIcn, shTm) => {
if (shIcn && !shTm) {
return [clockIcon];
} else if (shTm && !shIcn) {
@@ -47,13 +48,13 @@ const Clock = () => {
}
return [clockIcon, clockTime];
})
}),
}),
isVisible: true,
boxClass: "clock",
boxClass: 'clock',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "calendarmenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'calendarmenu');
},
},
};

View File

@@ -1,20 +1,23 @@
import Gdk from 'gi://Gdk?version=3.0';
const mpris = await Service.import("mpris");
import { openMenu } from "../utils.js";
import options from "options";
const mpris = await Service.import('mpris');
import { openMenu } from '../utils.js';
import options from 'options';
import { getCurrentPlayer } from 'lib/shared/media.js';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { show_artist, truncation, truncation_size, show_label, show_active_only } = options.bar.media;
const Media = () => {
const Media = (): BarBoxChild => {
const activePlayer = Variable(mpris.players[0]);
const isVis = Variable(!show_active_only.value);
show_active_only.connect("changed", () => {
show_active_only.connect('changed', () => {
isVis.value = !show_active_only.value || mpris.players.length > 0;
});
mpris.connect("changed", () => {
mpris.connect('changed', () => {
const curPlayer = getCurrentPlayer(activePlayer.value);
activePlayer.value = curPlayer;
isVis.value = !show_active_only.value || mpris.players.length > 0;
@@ -22,41 +25,37 @@ const Media = () => {
const getIconForPlayer = (playerName: string): string => {
const windowTitleMap = [
["Firefox", "󰈹"],
["Microsoft Edge", "󰇩"],
["Discord", ""],
["Plex", "󰚺"],
["Spotify", "󰓇"],
["(.*)", "󰝚"],
['Firefox', '󰈹'],
['Microsoft Edge', '󰇩'],
['Discord', ''],
['Plex', '󰚺'],
['Spotify', '󰓇'],
['(.*)', '󰝚'],
];
const foundMatch = windowTitleMap.find((wt) =>
RegExp(wt[0], "i").test(playerName),
);
const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0], 'i').test(playerName));
return foundMatch ? foundMatch[1] : "󰝚";
return foundMatch ? foundMatch[1] : '󰝚';
};
const songIcon = Variable("");
const songIcon = Variable('');
const mediaLabel = Utils.watch("Media", [mpris, show_artist, truncation, truncation_size, show_label], () => {
const mediaLabel = Utils.watch('Media', [mpris, show_artist, truncation, truncation_size, show_label], () => {
if (activePlayer.value && show_label.value) {
const { track_title, identity, track_artists } = activePlayer.value;
songIcon.value = getIconForPlayer(identity);
const trackArtist = show_artist.value
? ` - ${track_artists.join(', ')}`
: ``;
const trackArtist = show_artist.value ? ` - ${track_artists.join(', ')}` : ``;
const truncatedLabel = truncation.value
? `${track_title + trackArtist}`.substring(0, truncation_size.value)
: `${track_title + trackArtist}`;
return track_title.length === 0
? `No media playing...`
: ((truncatedLabel.length < truncation_size.value) || !truncation.value)
? `${truncatedLabel}`
: `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`;
: truncatedLabel.length < truncation_size.value || !truncation.value
? `${truncatedLabel}`
: `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`;
} else {
songIcon.value = getIconForPlayer(activePlayer.value?.identity || "");
songIcon.value = getIconForPlayer(activePlayer.value?.identity || '');
return `Media`;
}
});
@@ -65,23 +64,26 @@ const Media = () => {
component: Widget.Box({
visible: false,
child: Widget.Box({
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `media ${styleMap[style]}`;
}),
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), show_label.bind('value')],
(style) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `media ${styleMap[style]}`;
},
),
child: Widget.Box({
children: [
Widget.Label({
class_name: "bar-button-icon media txt-icon bar",
label: songIcon.bind("value").as(v => v || "󰝚"),
class_name: 'bar-button-icon media txt-icon bar',
label: songIcon.bind('value').as((v) => v || '󰝚'),
}),
Widget.Label({
class_name: "bar-button-label media",
class_name: 'bar-button-label media',
label: mediaLabel,
}),
],
@@ -89,13 +91,13 @@ const Media = () => {
}),
}),
isVis,
boxClass: "media",
name: "media",
boxClass: 'media',
name: 'media',
props: {
on_scroll_up: () => activePlayer.value?.next(),
on_scroll_down: () => activePlayer.value?.previous(),
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "mediamenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'mediamenu');
},
},
};

View File

@@ -1,29 +1,32 @@
import Gdk from 'gi://Gdk?version=3.0';
import { openMenu } from "../utils.js";
import options from "options";
import { openMenu } from '../utils.js';
import options from 'options';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const Menu = () => {
const Menu = (): BarBoxChild => {
return {
component: Widget.Box({
className: Utils.merge([options.theme.bar.buttons.style.bind("value")], (style) => {
className: Utils.merge([options.theme.bar.buttons.style.bind('value')], (style) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `dashboard ${styleMap[style]}`;
}),
child: Widget.Label({
class_name: "bar-menu_label bar-button_icon txt-icon bar",
label: options.bar.launcher.icon.bind("value"),
class_name: 'bar-menu_label bar-button_icon txt-icon bar',
label: options.bar.launcher.icon.bind('value'),
}),
}),
isVisible: true,
boxClass: "dashboard",
boxClass: 'dashboard',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "dashboardmenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'dashboardmenu');
},
},
};

View File

@@ -1,70 +1,77 @@
import Gdk from 'gi://Gdk?version=3.0';
const network = await Service.import("network");
import options from "options";
import { openMenu } from "../utils.js";
const network = await Service.import('network');
import options from 'options';
import { openMenu } from '../utils.js';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { label: networkLabel, truncation, truncation_size } = options.bar.network;
const Network = () => {
const Network = (): BarBoxChild => {
return {
component: Widget.Box({
vpack: "fill",
vpack: 'fill',
vexpand: true,
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), networkLabel.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `network ${styleMap[style]}${!showLabel ? " no-label" : ""}`;
}),
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), networkLabel.bind('value')],
(style, showLabel) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `network ${styleMap[style]}${!showLabel ? ' no-label' : ''}`;
},
),
children: [
Widget.Icon({
class_name: "bar-button-icon network",
icon: Utils.merge([
network.bind("primary"),
network.bind("wifi"),
network.bind("wired")
], (pmry, wfi, wrd) => {
if (pmry === "wired") {
return wrd.icon_name;
}
return wfi.icon_name;
})
class_name: 'bar-button-icon network',
icon: Utils.merge(
[network.bind('primary'), network.bind('wifi'), network.bind('wired')],
(pmry, wfi, wrd) => {
if (pmry === 'wired') {
return wrd.icon_name;
}
return wfi.icon_name;
},
),
}),
Widget.Box({
vpack: "center",
child: Utils.merge([
network.bind("primary"),
network.bind("wifi"),
networkLabel.bind("value"),
truncation.bind("value"),
truncation_size.bind("value")
], (pmry, wfi, showLbl, trunc, tSize) => {
if (!showLbl) {
return Widget.Box();
}
if (pmry === "wired") {
vpack: 'center',
child: Utils.merge(
[
network.bind('primary'),
network.bind('wifi'),
networkLabel.bind('value'),
truncation.bind('value'),
truncation_size.bind('value'),
],
(pmry, wfi, showLbl, trunc, tSize) => {
if (!showLbl) {
return Widget.Box();
}
if (pmry === 'wired') {
return Widget.Label({
class_name: 'bar-button-label network',
label: 'Wired'.substring(0, tSize),
});
}
return Widget.Label({
class_name: "bar-button-label network",
label: "Wired".substring(0, tSize),
})
}
return Widget.Label({
class_name: "bar-button-label network",
label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : "--",
})
})
class_name: 'bar-button-label network',
label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : '--',
});
},
),
}),
]
],
}),
isVisible: true,
boxClass: "network",
boxClass: 'network',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "networkmenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'networkmenu');
},
},
};

View File

@@ -1,51 +1,49 @@
import Gdk from 'gi://Gdk?version=3.0';
import { openMenu } from "../utils.js";
import options from "options";
import { openMenu } from '../utils.js';
import options from 'options';
import { filterNotifications } from 'lib/shared/notifications.js';
import { BarBoxChild } from 'lib/types/bar.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const { show_total } = options.bar.notifications;
const { ignore } = options.notifications;
const notifs = await Service.import("notifications");
const notifs = await Service.import('notifications');
export const Notifications = () => {
export const Notifications = (): BarBoxChild => {
return {
component: Widget.Box({
hpack: "start",
hpack: 'start',
className: Utils.merge(
[
options.theme.bar.buttons.style.bind("value"),
show_total.bind("value")
],
(
style,
showTotal
) => {
[options.theme.bar.buttons.style.bind('value'), show_total.bind('value')],
(style, showTotal) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `notifications ${styleMap[style]} ${!showTotal ? "no-label" : ""}`;
}),
return `notifications ${styleMap[style]} ${!showTotal ? 'no-label' : ''}`;
},
),
child: Widget.Box({
hpack: "start",
class_name: "bar-notifications",
hpack: 'start',
class_name: 'bar-notifications',
children: Utils.merge(
[notifs.bind("notifications"), notifs.bind("dnd"), show_total.bind("value"), ignore.bind("value")],
[notifs.bind('notifications'), notifs.bind('dnd'), show_total.bind('value'), ignore.bind('value')],
(notif, dnd, showTotal, ignoredNotifs) => {
const filteredNotifications = filterNotifications(notif, ignoredNotifs);
const notifIcon = Widget.Label({
hpack: "center",
class_name: "bar-button-icon notifications txt-icon bar",
label: dnd ? "󰂛" : filteredNotifications.length > 0 ? "󱅫" : "󰂚",
hpack: 'center',
class_name: 'bar-button-icon notifications txt-icon bar',
label: dnd ? '󰂛' : filteredNotifications.length > 0 ? '󱅫' : '󰂚',
});
const notifLabel = Widget.Label({
hpack: "center",
class_name: "bar-button-label notifications",
hpack: 'center',
class_name: 'bar-button-label notifications',
label: filteredNotifications.length.toString(),
});
@@ -58,10 +56,10 @@ export const Notifications = () => {
}),
}),
isVisible: true,
boxClass: "notifications",
boxClass: 'notifications',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "notificationsmenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'notificationsmenu');
},
},
};

View File

@@ -1,49 +1,43 @@
import Gdk from 'gi://Gdk?version=3.0';
import { BarBoxChild, SelfButton } from 'lib/types/bar';
import { Notify } from 'lib/utils';
const systemtray = await Service.import("systemtray");
import options from "options";
const systemtray = await Service.import('systemtray');
import options from 'options';
const { ignore } = options.bar.systray;
const SysTray = () => {
const SysTray = (): BarBoxChild => {
const isVis = Variable(false);
const items = Utils.merge(
[systemtray.bind("items"), ignore.bind("value")],
(items, ignored) => {
const filteredTray = items.filter(({ id }) => !ignored.includes(id));
const items = Utils.merge([systemtray.bind('items'), ignore.bind('value')], (items, ignored) => {
const filteredTray = items.filter(({ id }) => !ignored.includes(id));
isVis.value = filteredTray.length > 0;
isVis.value = filteredTray.length > 0;
return filteredTray.map((item) => {
if (item.menu !== undefined) {
item.menu["class_name"] = "systray-menu";
}
return Widget.Button({
cursor: "pointer",
child: Widget.Icon({
class_name: "systray-icon",
icon: item.bind("icon"),
}),
on_primary_click: (_: any, event: Gdk.Event) => item.activate(event),
on_secondary_click: (_, event) => item.openMenu(event),
onMiddleClick: () => Notify({ summary: "App Name", body: item.id }),
tooltip_markup: item.bind("tooltip_markup"),
});
return filteredTray.map((item) => {
return Widget.Button({
cursor: 'pointer',
child: Widget.Icon({
class_name: 'systray-icon',
icon: item.bind('icon'),
}),
on_primary_click: (_: SelfButton, event: Gdk.Event) => item.activate(event),
on_secondary_click: (_, event) => item.openMenu(event),
onMiddleClick: () => Notify({ summary: 'App Name', body: item.id }),
tooltip_markup: item.bind('tooltip_markup'),
});
},
);
});
});
return {
component: Widget.Box({
class_name: "systray",
class_name: 'systray',
children: items,
}),
isVisible: true,
boxClass: "systray",
boxClass: 'systray',
isVis,
props: {}
props: {},
};
};

View File

@@ -1,6 +1,8 @@
import Gdk from 'gi://Gdk?version=3.0';
import { Child } from 'lib/types/widget';
import Button from 'types/widgets/button';
export const closeAllMenus = () => {
export const closeAllMenus = (): void => {
const menuWindows = App.windows
.filter((w) => {
if (w.name) {
@@ -18,7 +20,7 @@ export const closeAllMenus = () => {
});
};
export const openMenu = (clicked: any, event: Gdk.Event, window: string) => {
export const openMenu = (clicked: Button<Child, Child>, event: Gdk.Event, window: string): void => {
/*
* NOTE: We have to make some adjustments so the menu pops up relatively
* to the center of the button clicked. We don't want the menu to spawn

View File

@@ -1,59 +1,63 @@
import Gdk from 'gi://Gdk?version=3.0';
const audio = await Service.import("audio");
import { openMenu } from "../utils.js";
import options from "options";
const audio = await Service.import('audio');
import { openMenu } from '../utils.js';
import options from 'options';
import { Binding } from 'lib/utils.js';
import { VolumeIcons } from 'lib/types/volume.js';
import { BarBoxChild } from 'lib/types/bar.js';
import { Bind } from 'lib/types/variable.js';
import Button from 'types/widgets/button.js';
import { Child } from 'lib/types/widget.js';
const Volume = () => {
const Volume = (): BarBoxChild => {
const icons: VolumeIcons = {
101: "󰕾",
66: "󰕾",
34: "󰖀",
1: "󰕿",
0: "󰝟",
101: '󰕾',
66: '󰕾',
34: '󰖀',
1: '󰕿',
0: '󰝟',
};
const getIcon = () => {
const getIcon = (): Bind => {
const icon: Binding<number> = Utils.merge(
[audio.speaker.bind("is_muted"), audio.speaker.bind("volume")],
[audio.speaker.bind('is_muted'), audio.speaker.bind('volume')],
(isMuted, vol) => {
return isMuted
? 0
: [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100) || 101;
return isMuted ? 0 : [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100) || 101;
},
);
return icon.as((i: number) => i !== undefined ? icons[i] : icons[101]);
return icon.as((i: number) => (i !== undefined ? icons[i] : icons[101]));
};
const volIcn = Widget.Label({
hexpand: true,
label: getIcon(),
class_name: "bar-button-icon volume txt-icon bar",
class_name: 'bar-button-icon volume txt-icon bar',
});
const volPct = Widget.Label({
hexpand: true,
label: audio.speaker.bind("volume").as((v) => `${Math.round(v * 100)}%`),
class_name: "bar-button-label volume",
label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`),
class_name: 'bar-button-label volume',
});
return {
component: Widget.Box({
hexpand: true,
vexpand: true,
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), options.bar.volume.label.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `volume ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
}),
children: options.bar.volume.label.bind("value").as((showLabel) => {
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), options.bar.volume.label.bind('value')],
(style, showLabel) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `volume ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
},
),
children: options.bar.volume.label.bind('value').as((showLabel) => {
if (showLabel) {
return [volIcn, volPct];
}
@@ -61,10 +65,10 @@ const Volume = () => {
}),
}),
isVisible: true,
boxClass: "volume",
boxClass: 'volume',
props: {
on_primary_click: (clicked: any, event: Gdk.Event) => {
openMenu(clicked, event, "audiomenu");
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
openMenu(clicked, event, 'audiomenu');
},
},
};

View File

@@ -1,122 +1,119 @@
const hyprland = await Service.import("hyprland");
const hyprland = await Service.import('hyprland');
import { BarBoxChild } from 'lib/types/bar';
import options from 'options';
import { ActiveClient } from 'types/service/hyprland'
import Label from "types/widgets/label";
import { ActiveClient } from 'types/service/hyprland';
const filterTitle = (windowtitle: ActiveClient) => {
const filterTitle = (windowtitle: ActiveClient): Record<string, string> => {
const windowTitleMap = [
// user provided values
...options.bar.windowtitle.title_map.value,
// Original Entries
["kitty", "󰄛", "Kitty Terminal"],
["firefox", "󰈹", "Firefox"],
["microsoft-edge", "󰇩", "Edge"],
["discord", "", "Discord"],
["vesktop", "", "Vesktop"],
["org.kde.dolphin", "", "Dolphin"],
["plex", "󰚺", "Plex"],
["steam", "", "Steam"],
["spotify", "󰓇", "Spotify"],
["ristretto", "󰋩", "Ristretto"],
["obsidian", "󱓧", "Obsidian"],
['kitty', '󰄛', 'Kitty Terminal'],
['firefox', '󰈹', 'Firefox'],
['microsoft-edge', '󰇩', 'Edge'],
['discord', '', 'Discord'],
['vesktop', '', 'Vesktop'],
['org.kde.dolphin', '', 'Dolphin'],
['plex', '󰚺', 'Plex'],
['steam', '', 'Steam'],
['spotify', '󰓇', 'Spotify'],
['ristretto', '󰋩', 'Ristretto'],
['obsidian', '󱓧', 'Obsidian'],
// Browsers
["google-chrome", "", "Google Chrome"],
["brave-browser", "󰖟", "Brave Browser"],
["chromium", "", "Chromium"],
["opera", "", "Opera"],
["vivaldi", "󰖟", "Vivaldi"],
["waterfox", "󰖟", "Waterfox"],
["thorium", "󰖟", "Waterfox"],
["tor-browser", "", "Tor Browser"],
["floorp", "󰈹", "Floorp"],
['google-chrome', '', 'Google Chrome'],
['brave-browser', '󰖟', 'Brave Browser'],
['chromium', '', 'Chromium'],
['opera', '', 'Opera'],
['vivaldi', '󰖟', 'Vivaldi'],
['waterfox', '󰖟', 'Waterfox'],
['thorium', '󰖟', 'Waterfox'],
['tor-browser', '', 'Tor Browser'],
['floorp', '󰈹', 'Floorp'],
// Terminals
["gnome-terminal", "", "GNOME Terminal"],
["konsole", "", "Konsole"],
["alacritty", "", "Alacritty"],
["wezterm", "", "Wezterm"],
["foot", "󰽒", "Foot Terminal"],
["tilix", "", "Tilix"],
["xterm", "", "XTerm"],
["urxvt", "", "URxvt"],
["st", "", "st Terminal"],
['gnome-terminal', '', 'GNOME Terminal'],
['konsole', '', 'Konsole'],
['alacritty', '', 'Alacritty'],
['wezterm', '', 'Wezterm'],
['foot', '󰽒', 'Foot Terminal'],
['tilix', '', 'Tilix'],
['xterm', '', 'XTerm'],
['urxvt', '', 'URxvt'],
['st', '', 'st Terminal'],
// Development Tools
["code", "󰨞", "Visual Studio Code"],
["vscode", "󰨞", "VS Code"],
["sublime-text", "", "Sublime Text"],
["atom", "", "Atom"],
["android-studio", "󰀴", "Android Studio"],
["intellij-idea", "", "IntelliJ IDEA"],
["pycharm", "󱃖", "PyCharm"],
["webstorm", "󱃖", "WebStorm"],
["phpstorm", "󱃖", "PhpStorm"],
["eclipse", "", "Eclipse"],
["netbeans", "", "NetBeans"],
["docker", "", "Docker"],
["vim", "", "Vim"],
["neovim", "", "Neovim"],
["neovide", "", "Neovide"],
["emacs", "", "Emacs"],
['code', '󰨞', 'Visual Studio Code'],
['vscode', '󰨞', 'VS Code'],
['sublime-text', '', 'Sublime Text'],
['atom', '', 'Atom'],
['android-studio', '󰀴', 'Android Studio'],
['intellij-idea', '', 'IntelliJ IDEA'],
['pycharm', '󱃖', 'PyCharm'],
['webstorm', '󱃖', 'WebStorm'],
['phpstorm', '󱃖', 'PhpStorm'],
['eclipse', '', 'Eclipse'],
['netbeans', '', 'NetBeans'],
['docker', '', 'Docker'],
['vim', '', 'Vim'],
['neovim', '', 'Neovim'],
['neovide', '', 'Neovide'],
['emacs', '', 'Emacs'],
// Communication Tools
["slack", "󰒱", "Slack"],
["telegram-desktop", "", "Telegram"],
["org.telegram.desktop", "", "Telegram"],
["whatsapp", "󰖣", "WhatsApp"],
["teams", "󰊻", "Microsoft Teams"],
["skype", "󰒯", "Skype"],
["thunderbird", "", "Thunderbird"],
['slack', '󰒱', 'Slack'],
['telegram-desktop', '', 'Telegram'],
['org.telegram.desktop', '', 'Telegram'],
['whatsapp', '󰖣', 'WhatsApp'],
['teams', '󰊻', 'Microsoft Teams'],
['skype', '󰒯', 'Skype'],
['thunderbird', '', 'Thunderbird'],
// File Managers
["nautilus", "󰝰", "Files (Nautilus)"],
["thunar", "󰝰", "Thunar"],
["pcmanfm", "󰝰", "PCManFM"],
["nemo", "󰝰", "Nemo"],
["ranger", "󰝰", "Ranger"],
["doublecmd", "󰝰", "Double Commander"],
["krusader", "󰝰", "Krusader"],
['nautilus', '󰝰', 'Files (Nautilus)'],
['thunar', '󰝰', 'Thunar'],
['pcmanfm', '󰝰', 'PCManFM'],
['nemo', '󰝰', 'Nemo'],
['ranger', '󰝰', 'Ranger'],
['doublecmd', '󰝰', 'Double Commander'],
['krusader', '󰝰', 'Krusader'],
// Media Players
["vlc", "󰕼", "VLC Media Player"],
["mpv", "", "MPV"],
["rhythmbox", "󰓃", "Rhythmbox"],
['vlc', '󰕼', 'VLC Media Player'],
['mpv', '', 'MPV'],
['rhythmbox', '󰓃', 'Rhythmbox'],
// Graphics Tools
["gimp", "", "GIMP"],
["inkscape", "", "Inkscape"],
["krita", "", "Krita"],
["blender", "󰂫", "Blender"],
['gimp', '', 'GIMP'],
['inkscape', '', 'Inkscape'],
['krita', '', 'Krita'],
['blender', '󰂫', 'Blender'],
// Video Editing
["kdenlive", "", "Kdenlive"],
['kdenlive', '', 'Kdenlive'],
// Games and Gaming Platforms
["lutris", "󰺵", "Lutris"],
["heroic", "󰺵", "Heroic Games Launcher"],
["minecraft", "󰍳", "Minecraft"],
["csgo", "󰺵", "CS:GO"],
["dota2", "󰺵", "Dota 2"],
['lutris', '󰺵', 'Lutris'],
['heroic', '󰺵', 'Heroic Games Launcher'],
['minecraft', '󰍳', 'Minecraft'],
['csgo', '󰺵', 'CS:GO'],
['dota2', '󰺵', 'Dota 2'],
// Office and Productivity
["evernote", "", "Evernote"],
["sioyek", "", "Sioyek"],
['evernote', '', 'Evernote'],
['sioyek', '', 'Sioyek'],
// Cloud Services and Sync
["dropbox", "󰇣", "Dropbox"],
['dropbox', '󰇣', 'Dropbox'],
// Desktop
["^$", "󰇄", "Desktop"],
['^$', '󰇄', 'Desktop'],
// Fallback icon
["(.+)", "󰣆", `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`],
['(.+)', '󰣆', `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`],
];
const foundMatch = windowTitleMap.find((wt) =>
RegExp(wt[0]).test(windowtitle.class.toLowerCase()),
);
const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0]).test(windowtitle.class.toLowerCase()));
// return the default icon if no match is found or
// if the array element matched is not of size 3
@@ -129,15 +126,15 @@ const filterTitle = (windowtitle: ActiveClient) => {
return {
icon: foundMatch[1],
label: foundMatch[2]
label: foundMatch[2],
};
};
const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: boolean) => {
const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: boolean): string => {
if (useCustomTitle) return filterTitle(client).label;
if (useClassName) return client.class;
let title = client.title;
const title = client.title;
// If the title is empty or only filled with spaces, fallback to the class name
if (title.length === 0 || title.match(/^ *$/)) {
return client.class;
@@ -145,51 +142,76 @@ const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: b
return title;
};
const truncateTitle = (title: string, max_size: number) => {
const truncateTitle = (title: string, max_size: number): string => {
if (max_size > 0 && title.length > max_size) {
return title.substring(0, max_size).trim() + "...";
return title.substring(0, max_size).trim() + '...';
}
return title;
};
const ClientTitle = () => {
const ClientTitle = (): BarBoxChild => {
const { custom_title, class_name, label, icon, truncation, truncation_size } = options.bar.windowtitle;
return {
component: Widget.Box({
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), label.bind("value")], (style, showLabel) => {
const styleMap = {
default: "style1",
split: "style2",
wave: "style3",
wave2: "style3",
};
return `windowtitle ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
}),
children:
Utils.merge(
[hyprland.active.bind("client"), custom_title.bind("value"), class_name.bind("value"), label.bind("value"),
icon.bind("value"), truncation.bind("value"), truncation_size.bind("value")],
(client, useCustomTitle, useClassName, showLabel, showIcon, truncate, truncationSize) => {
const children: Label<any>[] = [];
if (showIcon) {
children.push(Widget.Label({
class_name: "bar-button-icon windowtitle txt-icon bar",
className: Utils.merge(
[options.theme.bar.buttons.style.bind('value'), label.bind('value')],
(style, showLabel) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `windowtitle ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
},
),
children: Utils.merge(
[
hyprland.active.bind('client'),
custom_title.bind('value'),
class_name.bind('value'),
label.bind('value'),
icon.bind('value'),
truncation.bind('value'),
truncation_size.bind('value'),
],
(client, useCustomTitle, useClassName, showLabel, showIcon, truncate, truncationSize) => {
if (showIcon) {
return [
Widget.Label({
class_name: 'bar-button-icon windowtitle txt-icon bar',
label: filterTitle(client).icon,
}));
}
if (showLabel) {
children.push(Widget.Label({
class_name: `bar-button-label windowtitle ${showIcon ? "" : "no-icon"}`,
label: truncateTitle(getTitle(client, useCustomTitle, useClassName), truncate ? truncationSize : -1),
}));
}
return children;
}),
}),
Widget.Label({
class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`,
label: truncateTitle(
getTitle(client, useCustomTitle, useClassName),
truncate ? truncationSize : -1,
),
}),
];
}
if (showLabel) {
return [
Widget.Label({
class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`,
label: truncateTitle(
getTitle(client, useCustomTitle, useClassName),
truncate ? truncationSize : -1,
),
}),
];
}
return [];
},
),
}),
isVisible: true,
boxClass: "windowtitle",
props: {}
boxClass: 'windowtitle',
props: {},
};
};

View File

@@ -1,23 +1,21 @@
const hyprland = await Service.import("hyprland");
const hyprland = await Service.import('hyprland');
import { WorkspaceMap, WorkspaceRule } from "lib/types/workspace";
import options from "options";
import { Variable } from "types/variable";
const {
workspaces,
reverse_scroll,
} = options.bar.workspaces;
import { MonitorMap, WorkspaceMap, WorkspaceRule } from 'lib/types/workspace';
import options from 'options';
import { Variable } from 'types/variable';
const { workspaces, reverse_scroll } = options.bar.workspaces;
export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, monitor: number): boolean => {
if (!wsRules || !Object.keys(wsRules).length) {
return true;
}
const monitorMap = {};
const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor }));
const monitors = [...new Map([...workspaceMonitorList, ...hyprland.monitors].map(item => [item.id, item])).values()];
const monitorMap: MonitorMap = {};
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ id: m.monitorID, name: m.monitor }));
const monitors = [
...new Map([...workspaceMonitorList, ...hyprland.monitors].map((item) => [item.id, item])).values(),
];
monitors.forEach((m) => (monitorMap[m.id] = m.name));
@@ -32,9 +30,9 @@ export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, mo
export const getWorkspaceRules = (): WorkspaceMap => {
try {
const rules = Utils.exec("hyprctl workspacerules -j");
const rules = Utils.exec('hyprctl workspacerules -j');
const workspaceRules = {};
const workspaceRules: WorkspaceMap = {};
JSON.parse(rules).forEach((rule: WorkspaceRule, index: number) => {
if (Object.hasOwnProperty.call(workspaceRules, rule.monitor)) {
@@ -60,13 +58,13 @@ export const getCurrentMonitorWorkspaces = (monitor: number): number[] => {
}
const monitorWorkspaces = getWorkspaceRules();
const monitorMap = {};
const monitorMap: MonitorMap = {};
hyprland.monitors.forEach((m) => (monitorMap[m.id] = m.name));
const currentMonitorName = monitorMap[monitor];
return monitorWorkspaces[currentMonitorName];
}
};
export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean): void => {
if (activeWorkspaces === true) {
@@ -74,18 +72,17 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
let nextIndex = hyprland.active.workspace.id + 1;
if (nextIndex > activeWses[activeWses.length - 1].id) {
nextIndex = activeWses[0].id;
}
hyprland.messageAsync(`dispatch workspace ${nextIndex}`)
hyprland.messageAsync(`dispatch workspace ${nextIndex}`);
} else if (currentMonitorWorkspaces.value === undefined) {
let nextIndex = hyprland.active.workspace.id + 1;
if (nextIndex > workspaces.value) {
nextIndex = 0;
}
hyprland.messageAsync(`dispatch workspace ${nextIndex}`)
hyprland.messageAsync(`dispatch workspace ${nextIndex}`);
} else {
const curWorkspace = hyprland.active.workspace.id;
const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace);
@@ -94,9 +91,9 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
nextIndex = 0;
}
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`)
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`);
}
}
};
export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean): void => {
if (activeWorkspaces === true) {
@@ -104,11 +101,10 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
let prevIndex = hyprland.active.workspace.id - 1;
if (prevIndex < activeWses[0].id) {
prevIndex = activeWses[activeWses.length - 1].id;
}
hyprland.messageAsync(`dispatch workspace ${prevIndex}`)
hyprland.messageAsync(`dispatch workspace ${prevIndex}`);
} else if (currentMonitorWorkspaces.value === undefined) {
let prevIndex = hyprland.active.workspace.id - 1;
@@ -116,7 +112,7 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
prevIndex = workspaces.value;
}
hyprland.messageAsync(`dispatch workspace ${prevIndex}`)
hyprland.messageAsync(`dispatch workspace ${prevIndex}`);
} else {
const curWorkspace = hyprland.active.workspace.id;
const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace);
@@ -125,11 +121,11 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
prevIndex = currentMonitorWorkspaces.value.length - 1;
}
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`)
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`);
}
}
};
export function throttle<T extends (...args: any[]) => void>(func: T, limit: number): T {
export function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number): T {
let inThrottle: boolean;
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
if (!inThrottle) {
@@ -147,7 +143,11 @@ type ThrottledScrollHandlers = {
throttledScrollDown: () => void;
};
export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean = false): ThrottledScrollHandlers => {
export const createThrottledScrollHandlers = (
scrollSpeed: number,
currentMonitorWorkspaces: Variable<number[]>,
activeWorkspaces: boolean = false,
): ThrottledScrollHandlers => {
const throttledScrollUp = throttle(() => {
if (reverse_scroll.value === true) {
goToPrevWS(currentMonitorWorkspaces, activeWorkspaces);
@@ -165,4 +165,4 @@ export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonito
}, 200 / scrollSpeed);
return { throttledScrollUp, throttledScrollDown };
}
};

View File

@@ -1,42 +1,54 @@
const hyprland = await Service.import("hyprland");
import options from "options";
import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces, getWorkspaceRules, getWorkspacesForMonitor } from "./helpers";
import { Workspace } from "types/service/hyprland";
const hyprland = await Service.import('hyprland');
import options from 'options';
import {
createThrottledScrollHandlers,
getCurrentMonitorWorkspaces,
getWorkspaceRules,
getWorkspacesForMonitor,
} from './helpers';
import { Workspace } from 'types/service/hyprland';
import { BoxWidget } from 'lib/types/widget';
import { BarBoxChild, SelfButton } from 'lib/types/bar';
const {
workspaces,
monitorSpecific,
workspaceMask,
scroll_speed,
spacing
} = options.bar.workspaces;
const { workspaces, monitorSpecific, workspaceMask, scroll_speed, spacing } = options.bar.workspaces;
function range(length: number, start = 1) {
function range(length: number, start = 1): number[] {
return Array.from({ length }, (_, i) => i + start);
}
const Workspaces = (monitor = -1) => {
const Workspaces = (monitor = -1): BarBoxChild => {
const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces(monitor));
workspaces.connect("changed", () => {
currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor)
})
workspaces.connect('changed', () => {
currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor);
});
const renderClassnames = (showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string, i: number) => {
const renderClassnames = (
showIcons: boolean,
showNumbered: boolean,
numberedActiveIndicator: string,
i: number,
): string => {
if (showIcons) {
return `workspace-icon txt-icon bar`;
}
if (showNumbered) {
const numActiveInd = hyprland.active.workspace.id === i
? numberedActiveIndicator
: "";
const numActiveInd = hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
}
return "default";
}
return 'default';
};
const renderLabel = (showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, i: number, index: number) => {
const renderLabel = (
showIcons: boolean,
available: string,
active: string,
occupied: string,
workspaceMask: boolean,
i: number,
index: number,
): string => {
if (showIcons) {
if (hyprland.active.workspace.id === i) {
return active;
@@ -44,20 +56,16 @@ const Workspaces = (monitor = -1) => {
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
return occupied;
}
if (
monitor !== -1
) {
if (monitor !== -1) {
return available;
}
}
return workspaceMask
? `${index + 1}`
: `${i}`;
}
const defaultWses = () => {
return workspaceMask ? `${index + 1}` : `${i}`;
};
const defaultWses = (): BoxWidget => {
return Widget.Box({
children: Utils.merge(
[workspaces.bind("value"), monitorSpecific.bind()],
[workspaces.bind('value'), monitorSpecific.bind()],
(workspaces: number, monitorSpecific: boolean) => {
return range(workspaces || 8)
.filter((i) => {
@@ -72,49 +80,57 @@ const Workspaces = (monitor = -1) => {
})
.map((i, index) => {
return Widget.Button({
class_name: "workspace-button",
class_name: 'workspace-button',
on_primary_click: () => {
hyprland.messageAsync(`dispatch workspace ${i}`)
hyprland.messageAsync(`dispatch workspace ${i}`);
},
child: Widget.Label({
attribute: i,
vpack: "center",
css: spacing.bind("value").as(sp => `margin: 0rem ${0.375 * sp}rem;`),
vpack: 'center',
css: spacing.bind('value').as((sp) => `margin: 0rem ${0.375 * sp}rem;`),
class_name: Utils.merge(
[
options.bar.workspaces.show_icons.bind("value"),
options.bar.workspaces.show_numbered.bind("value"),
options.bar.workspaces.numbered_active_indicator.bind("value"),
options.bar.workspaces.icons.available.bind("value"),
options.bar.workspaces.icons.active.bind("value"),
options.bar.workspaces.icons.occupied.bind("value"),
hyprland.active.workspace.bind("id")
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.show_numbered.bind('value'),
options.bar.workspaces.numbered_active_indicator.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
hyprland.active.workspace.bind('id'),
],
(showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string) => {
(
showIcons: boolean,
showNumbered: boolean,
numberedActiveIndicator: string,
) => {
if (showIcons) {
return `workspace-icon txt-icon bar`;
}
if (showNumbered) {
const numActiveInd = hyprland.active.workspace.id === i
? numberedActiveIndicator
: "";
const numActiveInd =
hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
}
return "default";
return 'default';
},
),
label: Utils.merge(
[
options.bar.workspaces.show_icons.bind("value"),
options.bar.workspaces.icons.available.bind("value"),
options.bar.workspaces.icons.active.bind("value"),
options.bar.workspaces.icons.occupied.bind("value"),
workspaceMask.bind("value"),
hyprland.active.workspace.bind("id")
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
workspaceMask.bind('value'),
hyprland.active.workspace.bind('id'),
],
(showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, _: number) => {
(
showIcons: boolean,
available: string,
active: string,
occupied: string,
workspaceMask: boolean,
) => {
if (showIcons) {
if (hyprland.active.workspace.id === i) {
return active;
@@ -122,52 +138,45 @@ const Workspaces = (monitor = -1) => {
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
return occupied;
}
if (
monitor !== -1
) {
if (monitor !== -1) {
return available;
}
}
return workspaceMask
? `${index + 1}`
: `${i}`;
return workspaceMask ? `${index + 1}` : `${i}`;
},
),
setup: (self) => {
self.hook(hyprland, () => {
self.toggleClassName('active', hyprland.active.workspace.id === i);
self.toggleClassName(
"active",
hyprland.active.workspace.id === i,
);
self.toggleClassName(
"occupied",
'occupied',
(hyprland.getWorkspace(i)?.windows || 0) > 0,
);
});
},
})
}),
});
});
},
)
})
}
const occupiedWses = () => {
),
});
};
const occupiedWses = (): BoxWidget => {
return Widget.Box({
children: Utils.merge(
[
monitorSpecific.bind("value"),
hyprland.bind("workspaces"),
workspaceMask.bind("value"),
workspaces.bind("value"),
options.bar.workspaces.show_icons.bind("value"),
options.bar.workspaces.icons.available.bind("value"),
options.bar.workspaces.icons.active.bind("value"),
options.bar.workspaces.icons.occupied.bind("value"),
options.bar.workspaces.show_numbered.bind("value"),
options.bar.workspaces.numbered_active_indicator.bind("value"),
spacing.bind("value"),
hyprland.active.workspace.bind("id"),
monitorSpecific.bind('value'),
hyprland.bind('workspaces'),
workspaceMask.bind('value'),
workspaces.bind('value'),
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
options.bar.workspaces.show_numbered.bind('value'),
options.bar.workspaces.numbered_active_indicator.bind('value'),
spacing.bind('value'),
hyprland.active.workspace.bind('id'),
],
(
monitorSpecific: boolean,
@@ -185,29 +194,37 @@ const Workspaces = (monitor = -1) => {
) => {
let allWkspcs = range(totalWkspcs || 8);
const activeWorkspaces = wkSpaces.map(w => w.id);
const activeWorkspaces = wkSpaces.map((w) => w.id);
const workspaceRules = getWorkspaceRules();
// Sometimes hyprland doesn't have all the monitors in the list
// so we complement it with monitors from the workspace list
const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor }));
const curMonitor = hyprland.monitors.find(m => m.id === monitor)
|| workspaceMonitorList.find(m => m.id === monitor);
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({
id: m.monitorID,
name: m.monitor,
}));
const curMonitor =
hyprland.monitors.find((m) => m.id === monitor) ||
workspaceMonitorList.find((m) => m.id === monitor);
// go through each key in workspaceRules and flatten the array
const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => {
return [...acc, ...workspaceRules[k]];
}, [] as number[]);
const activesForMonitor = activeWorkspaces.filter(w => {
if (curMonitor && Object.hasOwnProperty.call(workspaceRules, curMonitor.name) && workspacesWithRules.includes(w)) {
const activesForMonitor = activeWorkspaces.filter((w) => {
if (
curMonitor &&
Object.hasOwnProperty.call(workspaceRules, curMonitor.name) &&
workspacesWithRules.includes(w)
) {
return workspaceRules[curMonitor.name].includes(w);
}
return true;
});
if (monitorSpecific) {
const wrkspcsInRange = range(totalWkspcs).filter(w => {
const wrkspcsInRange = range(totalWkspcs).filter((w) => {
return getWorkspacesForMonitor(w, workspaceRules, monitor);
});
allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])];
@@ -221,51 +238,53 @@ const Workspaces = (monitor = -1) => {
})
.map((i, index) => {
return Widget.Button({
class_name: "workspace-button",
class_name: 'workspace-button',
on_primary_click: () => {
hyprland.messageAsync(`dispatch workspace ${i}`)
hyprland.messageAsync(`dispatch workspace ${i}`);
},
child: Widget.Label({
attribute: i,
vpack: "center",
vpack: 'center',
css: `margin: 0rem ${0.375 * spacing}rem;`,
class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i),
label: renderLabel(showIcons, available, active, occupied, workspaceMask, i, index),
setup: (self) => {
self.toggleClassName(
"active",
activeId === i,
);
self.toggleClassName(
"occupied",
(hyprland.getWorkspace(i)?.windows || 0) > 0,
);
self.toggleClassName('active', activeId === i);
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
},
})
}),
});
});
},
)
})
}
),
});
};
return {
component: Widget.Box({
class_name: "workspaces",
child: options.bar.workspaces.hideUnoccupied.bind("value").as(hideUnoccupied => hideUnoccupied ? occupiedWses() : defaultWses()),
class_name: 'workspaces',
child: options.bar.workspaces.hideUnoccupied
.bind('value')
.as((hideUnoccupied) => (hideUnoccupied ? occupiedWses() : defaultWses())),
}),
isVisible: true,
boxClass: "workspaces",
boxClass: 'workspaces',
props: {
setup: (self: any) => {
Utils.merge([scroll_speed.bind("value"), options.bar.workspaces.hideUnoccupied.bind("value")], (scroll_speed, hideUnoccupied) => {
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(scroll_speed, currentMonitorWorkspaces, hideUnoccupied)
self.on_scroll_up = throttledScrollUp;
self.on_scroll_down = throttledScrollDown;
});
}
}
setup: (self: SelfButton): void => {
Utils.merge(
[scroll_speed.bind('value'), options.bar.workspaces.hideUnoccupied.bind('value')],
(scroll_speed, hideUnoccupied) => {
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
scroll_speed,
currentMonitorWorkspaces,
hideUnoccupied,
);
self.on_scroll_up = throttledScrollUp;
self.on_scroll_down = throttledScrollDown;
},
);
},
},
};
};

View File

@@ -0,0 +1,43 @@
const hyprland = await Service.import('hyprland');
export const renderClassnames = (
showIcons: boolean,
showNumbered: boolean,
numberedActiveIndicator: string,
i: number,
): string => {
if (showIcons) {
return `workspace-icon txt-icon bar`;
}
if (showNumbered) {
const numActiveInd = hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
}
return 'default';
};
export const renderLabel = (
showIcons: boolean,
available: string,
active: string,
occupied: string,
workspaceMask: boolean,
i: number,
index: number,
monitor: number,
): string => {
if (showIcons) {
if (hyprland.active.workspace.id === i) {
return active;
}
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
return occupied;
}
if (monitor !== -1) {
return available;
}
}
return workspaceMask ? `${index + 1}` : `${i}`;
};

View File

@@ -0,0 +1,99 @@
const hyprland = await Service.import('hyprland');
import options from 'options';
import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers';
import { range } from 'lib/utils';
import { BoxWidget } from 'lib/types/widget';
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
export const defaultWses = (monitor: number): BoxWidget => {
return Widget.Box({
children: Utils.merge(
[workspaces.bind('value'), monitorSpecific.bind()],
(workspaces: number, monitorSpecific: boolean) => {
return range(workspaces || 8)
.filter((i) => {
if (!monitorSpecific) {
return true;
}
const workspaceRules = getWorkspaceRules();
return getWorkspacesForMonitor(i, workspaceRules, monitor);
})
.sort((a, b) => {
return a - b;
})
.map((i, index) => {
return Widget.Button({
class_name: 'workspace-button',
on_primary_click: () => {
hyprland.messageAsync(`dispatch workspace ${i}`);
},
child: Widget.Label({
attribute: i,
vpack: 'center',
css: spacing.bind('value').as((sp) => `margin: 0rem ${0.375 * sp}rem;`),
class_name: Utils.merge(
[
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.show_numbered.bind('value'),
options.bar.workspaces.numbered_active_indicator.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
hyprland.active.workspace.bind('id'),
],
(showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string) => {
if (showIcons) {
return `workspace-icon txt-icon bar`;
}
if (showNumbered) {
const numActiveInd =
hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
}
return 'default';
},
),
label: Utils.merge(
[
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
workspaceMask.bind('value'),
hyprland.active.workspace.bind('id'),
],
(
showIcons: boolean,
available: string,
active: string,
occupied: string,
workspaceMask: boolean,
) => {
if (showIcons) {
if (hyprland.active.workspace.id === i) {
return active;
}
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
return occupied;
}
if (monitor !== -1) {
return available;
}
}
return workspaceMask ? `${index + 1}` : `${i}`;
},
),
setup: (self) => {
self.hook(hyprland, () => {
self.toggleClassName('active', hyprland.active.workspace.id === i);
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
});
},
}),
});
});
},
),
});
};

View File

@@ -0,0 +1,114 @@
const hyprland = await Service.import('hyprland');
import options from 'options';
import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers';
import { Workspace } from 'types/service/hyprland';
import { renderClassnames, renderLabel } from '../utils';
import { range } from 'lib/utils';
import { BoxWidget } from 'lib/types/widget';
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
export const occupiedWses = (monitor: number): BoxWidget => {
return Widget.Box({
children: Utils.merge(
[
monitorSpecific.bind('value'),
hyprland.bind('workspaces'),
workspaceMask.bind('value'),
workspaces.bind('value'),
options.bar.workspaces.show_icons.bind('value'),
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
options.bar.workspaces.show_numbered.bind('value'),
options.bar.workspaces.numbered_active_indicator.bind('value'),
spacing.bind('value'),
hyprland.active.workspace.bind('id'),
],
(
monitorSpecific: boolean,
wkSpaces: Workspace[],
workspaceMask: boolean,
totalWkspcs: number,
showIcons: boolean,
available: string,
active: string,
occupied: string,
showNumbered: boolean,
numberedActiveIndicator: string,
spacing: number,
activeId: number,
) => {
let allWkspcs = range(totalWkspcs || 8);
const activeWorkspaces = wkSpaces.map((w) => w.id);
const workspaceRules = getWorkspaceRules();
// Sometimes hyprland doesn't have all the monitors in the list
// so we complement it with monitors from the workspace list
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ id: m.monitorID, name: m.monitor }));
const curMonitor =
hyprland.monitors.find((m) => m.id === monitor) ||
workspaceMonitorList.find((m) => m.id === monitor);
// go through each key in workspaceRules and flatten the array
const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => {
return [...acc, ...workspaceRules[k]];
}, [] as number[]);
const activesForMonitor = activeWorkspaces.filter((w) => {
if (
curMonitor &&
Object.hasOwnProperty.call(workspaceRules, curMonitor.name) &&
workspacesWithRules.includes(w)
) {
return workspaceRules[curMonitor.name].includes(w);
}
return true;
});
if (monitorSpecific) {
const wrkspcsInRange = range(totalWkspcs).filter((w) => {
return getWorkspacesForMonitor(w, workspaceRules, monitor);
});
allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])];
} else {
allWkspcs = [...new Set([...allWkspcs, ...activeWorkspaces])];
}
return allWkspcs
.sort((a, b) => {
return a - b;
})
.map((i, index) => {
return Widget.Button({
class_name: 'workspace-button',
on_primary_click: () => {
hyprland.messageAsync(`dispatch workspace ${i}`);
},
child: Widget.Label({
attribute: i,
vpack: 'center',
css: `margin: 0rem ${0.375 * spacing}rem;`,
class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i),
label: renderLabel(
showIcons,
available,
active,
occupied,
workspaceMask,
i,
index,
monitor,
),
setup: (self) => {
self.toggleClassName('active', activeId === i);
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
},
}),
});
});
},
),
});
};

View File

@@ -1,199 +1,199 @@
export const substitutes = {
"transmission-gtk": "transmission",
"blueberry.py": "blueberry",
Caprine: "facebook-messenger",
"com.raggesilver.BlackBox-symbolic": "terminal-symbolic",
"org.wezfurlong.wezterm-symbolic": "terminal-symbolic",
"audio-headset-bluetooth": "audio-headphones-symbolic",
"audio-card-analog-usb": "audio-speakers-symbolic",
"audio-card-analog-pci": "audio-card-symbolic",
"preferences-system": "emblem-system-symbolic",
"com.github.Aylur.ags-symbolic": "controls-symbolic",
"com.github.Aylur.ags": "controls-symbolic",
'transmission-gtk': 'transmission',
'blueberry.py': 'blueberry',
Caprine: 'facebook-messenger',
'com.raggesilver.BlackBox-symbolic': 'terminal-symbolic',
'org.wezfurlong.wezterm-symbolic': 'terminal-symbolic',
'audio-headset-bluetooth': 'audio-headphones-symbolic',
'audio-card-analog-usb': 'audio-speakers-symbolic',
'audio-card-analog-pci': 'audio-card-symbolic',
'preferences-system': 'emblem-system-symbolic',
'com.github.Aylur.ags-symbolic': 'controls-symbolic',
'com.github.Aylur.ags': 'controls-symbolic',
};
export default {
missing: "image-missing-symbolic",
missing: 'image-missing-symbolic',
nix: {
nix: "nix-snowflake-symbolic",
nix: 'nix-snowflake-symbolic',
},
app: {
terminal: "terminal-symbolic",
terminal: 'terminal-symbolic',
},
fallback: {
executable: "application-x-executable",
notification: "dialog-information-symbolic",
video: "video-x-generic-symbolic",
audio: "audio-x-generic-symbolic",
executable: 'application-x-executable',
notification: 'dialog-information-symbolic',
video: 'video-x-generic-symbolic',
audio: 'audio-x-generic-symbolic',
},
ui: {
close: "window-close-symbolic",
colorpicker: "color-select-symbolic",
info: "info-symbolic",
link: "external-link-symbolic",
lock: "system-lock-screen-symbolic",
menu: "open-menu-symbolic",
refresh: "view-refresh-symbolic",
search: "system-search-symbolic",
settings: "emblem-system-symbolic",
themes: "preferences-desktop-theme-symbolic",
tick: "object-select-symbolic",
time: "hourglass-symbolic",
toolbars: "toolbars-symbolic",
warning: "dialog-warning-symbolic",
avatar: "avatar-default-symbolic",
close: 'window-close-symbolic',
colorpicker: 'color-select-symbolic',
info: 'info-symbolic',
link: 'external-link-symbolic',
lock: 'system-lock-screen-symbolic',
menu: 'open-menu-symbolic',
refresh: 'view-refresh-symbolic',
search: 'system-search-symbolic',
settings: 'emblem-system-symbolic',
themes: 'preferences-desktop-theme-symbolic',
tick: 'object-select-symbolic',
time: 'hourglass-symbolic',
toolbars: 'toolbars-symbolic',
warning: 'dialog-warning-symbolic',
avatar: 'avatar-default-symbolic',
arrow: {
right: "pan-end-symbolic",
left: "pan-start-symbolic",
down: "pan-down-symbolic",
up: "pan-up-symbolic",
right: 'pan-end-symbolic',
left: 'pan-start-symbolic',
down: 'pan-down-symbolic',
up: 'pan-up-symbolic',
},
},
audio: {
mic: {
muted: "microphone-disabled-symbolic",
low: "microphone-sensitivity-low-symbolic",
medium: "microphone-sensitivity-medium-symbolic",
high: "microphone-sensitivity-high-symbolic",
muted: 'microphone-disabled-symbolic',
low: 'microphone-sensitivity-low-symbolic',
medium: 'microphone-sensitivity-medium-symbolic',
high: 'microphone-sensitivity-high-symbolic',
},
volume: {
muted: "audio-volume-muted-symbolic",
low: "audio-volume-low-symbolic",
medium: "audio-volume-medium-symbolic",
high: "audio-volume-high-symbolic",
overamplified: "audio-volume-overamplified-symbolic",
muted: 'audio-volume-muted-symbolic',
low: 'audio-volume-low-symbolic',
medium: 'audio-volume-medium-symbolic',
high: 'audio-volume-high-symbolic',
overamplified: 'audio-volume-overamplified-symbolic',
},
type: {
headset: "audio-headphones-symbolic",
speaker: "audio-speakers-symbolic",
card: "audio-card-symbolic",
headset: 'audio-headphones-symbolic',
speaker: 'audio-speakers-symbolic',
card: 'audio-card-symbolic',
},
mixer: "mixer-symbolic",
mixer: 'mixer-symbolic',
},
powerprofile: {
balanced: "power-profile-balanced-symbolic",
"power-saver": "power-profile-power-saver-symbolic",
performance: "power-profile-performance-symbolic",
balanced: 'power-profile-balanced-symbolic',
'power-saver': 'power-profile-power-saver-symbolic',
performance: 'power-profile-performance-symbolic',
},
asusctl: {
profile: {
Balanced: "power-profile-balanced-symbolic",
Quiet: "power-profile-power-saver-symbolic",
Performance: "power-profile-performance-symbolic",
Balanced: 'power-profile-balanced-symbolic',
Quiet: 'power-profile-power-saver-symbolic',
Performance: 'power-profile-performance-symbolic',
},
mode: {
Integrated: "processor-symbolic",
Hybrid: "controller-symbolic",
Integrated: 'processor-symbolic',
Hybrid: 'controller-symbolic',
},
},
battery: {
charging: "battery-flash-symbolic",
warning: "battery-empty-symbolic",
charging: 'battery-flash-symbolic',
warning: 'battery-empty-symbolic',
},
bluetooth: {
enabled: "bluetooth-active-symbolic",
disabled: "bluetooth-disabled-symbolic",
enabled: 'bluetooth-active-symbolic',
disabled: 'bluetooth-disabled-symbolic',
},
brightness: {
indicator: "display-brightness-symbolic",
keyboard: "keyboard-brightness-symbolic",
screen: "display-brightness-symbolic",
indicator: 'display-brightness-symbolic',
keyboard: 'keyboard-brightness-symbolic',
screen: 'display-brightness-symbolic',
},
powermenu: {
sleep: "weather-clear-night-symbolic",
reboot: "system-reboot-symbolic",
logout: "system-log-out-symbolic",
shutdown: "system-shutdown-symbolic",
sleep: 'weather-clear-night-symbolic',
reboot: 'system-reboot-symbolic',
logout: 'system-log-out-symbolic',
shutdown: 'system-shutdown-symbolic',
},
recorder: {
recording: "media-record-symbolic",
recording: 'media-record-symbolic',
},
notifications: {
noisy: "org.gnome.Settings-notifications-symbolic",
silent: "notifications-disabled-symbolic",
message: "chat-bubbles-symbolic",
noisy: 'org.gnome.Settings-notifications-symbolic',
silent: 'notifications-disabled-symbolic',
message: 'chat-bubbles-symbolic',
},
trash: {
full: "user-trash-full-symbolic",
empty: "user-trash-symbolic",
full: 'user-trash-full-symbolic',
empty: 'user-trash-symbolic',
},
mpris: {
shuffle: {
enabled: "media-playlist-shuffle-symbolic",
disabled: "media-playlist-consecutive-symbolic",
enabled: 'media-playlist-shuffle-symbolic',
disabled: 'media-playlist-consecutive-symbolic',
},
loop: {
none: "media-playlist-repeat-symbolic",
track: "media-playlist-repeat-song-symbolic",
playlist: "media-playlist-repeat-symbolic",
none: 'media-playlist-repeat-symbolic',
track: 'media-playlist-repeat-song-symbolic',
playlist: 'media-playlist-repeat-symbolic',
},
playing: "media-playback-pause-symbolic",
paused: "media-playback-start-symbolic",
stopped: "media-playback-start-symbolic",
prev: "media-skip-backward-symbolic",
next: "media-skip-forward-symbolic",
playing: 'media-playback-pause-symbolic',
paused: 'media-playback-start-symbolic',
stopped: 'media-playback-start-symbolic',
prev: 'media-skip-backward-symbolic',
next: 'media-skip-forward-symbolic',
},
system: {
cpu: "org.gnome.SystemMonitor-symbolic",
ram: "drive-harddisk-solidstate-symbolic",
temp: "temperature-symbolic",
cpu: 'org.gnome.SystemMonitor-symbolic',
ram: 'drive-harddisk-solidstate-symbolic',
temp: 'temperature-symbolic',
},
color: {
dark: "dark-mode-symbolic",
light: "light-mode-symbolic",
dark: 'dark-mode-symbolic',
light: 'light-mode-symbolic',
},
weather: {
warning: "dialog-warning-symbolic",
sunny: "weather-clear-symbolic",
clear: "weather-clear-night-symbolic",
partly_cloudy: "weather-few-clouds-symbolic",
partly_cloudy_night: "weather-few-clouds-night-symbolic",
cloudy: "weather-overcast-symbolic",
overcast: "weather-overcast-symbolic",
mist: "weather-overcast-symbolic",
patchy_rain_nearby: "weather-showers-scattered-symbolic",
patchy_rain_possible: "weather-showers-scattered-symbolic",
patchy_snow_possible: "weather-snow-symbolic",
patchy_sleet_possible: "weather-snow-symbolic",
patchy_freezing_drizzle_possible: "weather-showers-scattered-symbolic",
thundery_outbreaks_possible: "weather-overcast-symbolic",
blowing_snow: "weather-snow-symbolic",
blizzard: "weather-snow-symbolic",
fog: "weather-fog-symbolic",
freezing_fog: "weather-fog-symbolic",
patchy_light_drizzle: "weather-showers-scattered-symbolic",
light_drizzle: "weather-showers-symbolic",
freezing_drizzle: "weather-showers-symbolic",
heavy_freezing_drizzle: "weather-showers-symbolic",
patchy_light_rain: "weather-showers-scattered-symbolic",
light_rain: "weather-showers-symbolic",
moderate_rain_at_times: "weather-showers-symbolic",
moderate_rain: "weather-showers-symbolic",
heavy_rain_at_times: "weather-showers-symbolic",
heavy_rain: "weather-showers-symbolic",
light_freezing_rain: "weather-showers-symbolic",
moderate_or_heavy_freezing_rain: "weather-showers-symbolic",
light_sleet: "weather-snow-symbolic",
moderate_or_heavy_sleet: "weather-snow-symbolic",
patchy_light_snow: "weather-snow-symbolic",
light_snow: "weather-snow-symbolic",
patchy_moderate_snow: "weather-snow-symbolic",
moderate_snow: "weather-snow-symbolic",
patchy_heavy_snow: "weather-snow-symbolic",
heavy_snow: "weather-snow-symbolic",
ice_pellets: "weather-showers-symbolic",
light_rain_shower: "weather-showers-symbolic",
moderate_or_heavy_rain_shower: "weather-showers-symbolic",
torrential_rain_shower: "weather-showers-symbolic",
light_sleet_showers: "weather-showers-symbolic",
moderate_or_heavy_sleet_showers: "weather-showers-symbolic",
light_snow_showers: "weather-snow-symbolic",
moderate_or_heavy_snow_showers: "weather-snow-symbolic",
light_showers_of_ice_pellets: "weather-showers-symbolic",
moderate_or_heavy_showers_of_ice_pellets: "weather-showers-symbolic",
patchy_light_rain_with_thunder: "weather-showers-scattered-symbolic",
moderate_or_heavy_rain_with_thunder: "weather-showers-symbolic",
patchy_light_snow_with_thunder: "weather-snow-symbolic",
moderate_or_heavy_snow_with_thunder: "weather-snow-symbolic",
warning: 'dialog-warning-symbolic',
sunny: 'weather-clear-symbolic',
clear: 'weather-clear-night-symbolic',
partly_cloudy: 'weather-few-clouds-symbolic',
partly_cloudy_night: 'weather-few-clouds-night-symbolic',
cloudy: 'weather-overcast-symbolic',
overcast: 'weather-overcast-symbolic',
mist: 'weather-overcast-symbolic',
patchy_rain_nearby: 'weather-showers-scattered-symbolic',
patchy_rain_possible: 'weather-showers-scattered-symbolic',
patchy_snow_possible: 'weather-snow-symbolic',
patchy_sleet_possible: 'weather-snow-symbolic',
patchy_freezing_drizzle_possible: 'weather-showers-scattered-symbolic',
thundery_outbreaks_possible: 'weather-overcast-symbolic',
blowing_snow: 'weather-snow-symbolic',
blizzard: 'weather-snow-symbolic',
fog: 'weather-fog-symbolic',
freezing_fog: 'weather-fog-symbolic',
patchy_light_drizzle: 'weather-showers-scattered-symbolic',
light_drizzle: 'weather-showers-symbolic',
freezing_drizzle: 'weather-showers-symbolic',
heavy_freezing_drizzle: 'weather-showers-symbolic',
patchy_light_rain: 'weather-showers-scattered-symbolic',
light_rain: 'weather-showers-symbolic',
moderate_rain_at_times: 'weather-showers-symbolic',
moderate_rain: 'weather-showers-symbolic',
heavy_rain_at_times: 'weather-showers-symbolic',
heavy_rain: 'weather-showers-symbolic',
light_freezing_rain: 'weather-showers-symbolic',
moderate_or_heavy_freezing_rain: 'weather-showers-symbolic',
light_sleet: 'weather-snow-symbolic',
moderate_or_heavy_sleet: 'weather-snow-symbolic',
patchy_light_snow: 'weather-snow-symbolic',
light_snow: 'weather-snow-symbolic',
patchy_moderate_snow: 'weather-snow-symbolic',
moderate_snow: 'weather-snow-symbolic',
patchy_heavy_snow: 'weather-snow-symbolic',
heavy_snow: 'weather-snow-symbolic',
ice_pellets: 'weather-showers-symbolic',
light_rain_shower: 'weather-showers-symbolic',
moderate_or_heavy_rain_shower: 'weather-showers-symbolic',
torrential_rain_shower: 'weather-showers-symbolic',
light_sleet_showers: 'weather-showers-symbolic',
moderate_or_heavy_sleet_showers: 'weather-showers-symbolic',
light_snow_showers: 'weather-snow-symbolic',
moderate_or_heavy_snow_showers: 'weather-snow-symbolic',
light_showers_of_ice_pellets: 'weather-showers-symbolic',
moderate_or_heavy_showers_of_ice_pellets: 'weather-showers-symbolic',
patchy_light_rain_with_thunder: 'weather-showers-scattered-symbolic',
moderate_or_heavy_rain_with_thunder: 'weather-showers-symbolic',
patchy_light_snow_with_thunder: 'weather-snow-symbolic',
moderate_or_heavy_snow_with_thunder: 'weather-snow-symbolic',
},
} as const;

View File

@@ -1,54 +1,54 @@
export const weatherIcons = {
warning: "󰼯",
sunny: "󰖙",
clear: "󰖔",
partly_cloudy: "󰖕",
partly_cloudy_night: "󰼱",
cloudy: "󰖐",
overcast: "󰖕",
mist: "󰖑",
patchy_rain_nearby: "󰼳",
patchy_rain_possible: "󰼳",
patchy_snow_possible: "󰼴",
patchy_sleet_possible: "󰙿",
patchy_freezing_drizzle_possible: "󰙿",
thundery_outbreaks_possible: "󰙾",
blowing_snow: "󰼶",
blizzard: "󰼶",
fog: "󰖑",
freezing_fog: "󰖑",
patchy_light_drizzle: "󰼳",
light_drizzle: "󰼳",
freezing_drizzle: "󰙿",
heavy_freezing_drizzle: "󰙿",
patchy_light_rain: "󰼳",
light_rain: "󰼳",
moderate_rain_at_times: "󰖗",
moderate_rain: "󰼳",
heavy_rain_at_times: "󰖖",
heavy_rain: "󰖖",
light_freezing_rain: "󰙿",
moderate_or_heavy_freezing_rain: "󰙿",
light_sleet: "󰙿",
moderate_or_heavy_sleet: "󰙿",
patchy_light_snow: "󰼴",
light_snow: "󰼴",
patchy_moderate_snow: "󰼴",
moderate_snow: "󰼶",
patchy_heavy_snow: "󰼶",
heavy_snow: "󰼶",
ice_pellets: "󰖒",
light_rain_shower: "󰖖",
moderate_or_heavy_rain_shower: "󰖖",
torrential_rain_shower: "󰖖",
light_sleet_showers: "󰼵",
moderate_or_heavy_sleet_showers: "󰼵",
light_snow_showers: "󰼵",
moderate_or_heavy_snow_showers: "󰼵",
light_showers_of_ice_pellets: "󰖒",
moderate_or_heavy_showers_of_ice_pellets: "󰖒",
patchy_light_rain_with_thunder: "󰙾",
moderate_or_heavy_rain_with_thunder: "󰙾",
patchy_light_snow_with_thunder: "󰼶",
moderate_or_heavy_snow_with_thunder: "󰼶",
warning: '󰼯',
sunny: '󰖙',
clear: '󰖔',
partly_cloudy: '󰖕',
partly_cloudy_night: '󰼱',
cloudy: '󰖐',
overcast: '󰖕',
mist: '󰖑',
patchy_rain_nearby: '󰼳',
patchy_rain_possible: '󰼳',
patchy_snow_possible: '󰼴',
patchy_sleet_possible: '󰙿',
patchy_freezing_drizzle_possible: '󰙿',
thundery_outbreaks_possible: '󰙾',
blowing_snow: '󰼶',
blizzard: '󰼶',
fog: '󰖑',
freezing_fog: '󰖑',
patchy_light_drizzle: '󰼳',
light_drizzle: '󰼳',
freezing_drizzle: '󰙿',
heavy_freezing_drizzle: '󰙿',
patchy_light_rain: '󰼳',
light_rain: '󰼳',
moderate_rain_at_times: '󰖗',
moderate_rain: '󰼳',
heavy_rain_at_times: '󰖖',
heavy_rain: '󰖖',
light_freezing_rain: '󰙿',
moderate_or_heavy_freezing_rain: '󰙿',
light_sleet: '󰙿',
moderate_or_heavy_sleet: '󰙿',
patchy_light_snow: '󰼴',
light_snow: '󰼴',
patchy_moderate_snow: '󰼴',
moderate_snow: '󰼶',
patchy_heavy_snow: '󰼶',
heavy_snow: '󰼶',
ice_pellets: '󰖒',
light_rain_shower: '󰖖',
moderate_or_heavy_rain_shower: '󰖖',
torrential_rain_shower: '󰖖',
light_sleet_showers: '󰼵',
moderate_or_heavy_sleet_showers: '󰼵',
light_snow_showers: '󰼵',
moderate_or_heavy_snow_showers: '󰼵',
light_showers_of_ice_pellets: '󰖒',
moderate_or_heavy_showers_of_ice_pellets: '󰖒',
patchy_light_rain_with_thunder: '󰙾',
moderate_or_heavy_rain_with_thunder: '󰙾',
patchy_light_snow_with_thunder: '󰼶',
moderate_or_heavy_snow_with_thunder: '󰼶',
} as const;

View File

@@ -1,25 +1,34 @@
const hyprland = await Service.import("hyprland");
import { DropdownMenuProps } from "lib/types/dropdownmenu";
import { Exclusivity } from "lib/types/widget";
import { bash } from "lib/utils";
import { Monitor } from "types/service/hyprland";
const hyprland = await Service.import('hyprland');
import { DropdownMenuProps } from 'lib/types/dropdownmenu';
import { Attribute, Child, Exclusivity, GtkWidget } from 'lib/types/widget';
import { bash } from 'lib/utils';
import { Widget as TWidget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import { Monitor } from 'types/service/hyprland';
import Box from 'types/widgets/box';
import EventBox from 'types/widgets/eventbox';
import Revealer from 'types/widgets/revealer';
import Window from 'types/widgets/window';
export const Padding = (name: string) =>
type NestedRevealer = Revealer<Box<TWidget, unknown>, unknown>;
type NestedBox = Box<NestedRevealer, unknown>;
type NestedEventBox = EventBox<NestedBox, unknown>;
export const Padding = (name: string): EventBox<Box<GtkWidget, Attribute>, Attribute> =>
Widget.EventBox({
hexpand: true,
vexpand: true,
can_focus: true,
child: Widget.Box(),
setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)),
setup: (w) => w.on('button-press-event', () => App.toggleWindow(name)),
});
const moveBoxToCursor = (self: any, fixed: boolean) => {
const moveBoxToCursor = <T extends NestedEventBox>(self: T, fixed: boolean): void => {
if (fixed) {
return;
}
globalMousePos.connect("changed", async ({ value }) => {
const curHyprlandMonitor = hyprland.monitors.find(m => m.id === hyprland.active.monitor.id);
globalMousePos.connect('changed', async ({ value }) => {
const curHyprlandMonitor = hyprland.monitors.find((m) => m.id === hyprland.active.monitor.id);
const dropdownWidth = self.child.get_allocation().width;
let hyprScaling = 1;
@@ -27,8 +36,8 @@ const moveBoxToCursor = (self: any, fixed: boolean) => {
const monitorInfo = await bash('hyprctl monitors -j');
const parsedMonitorInfo = JSON.parse(monitorInfo);
const foundMonitor = parsedMonitorInfo.find((monitor: Monitor) =>
monitor.id === hyprland.active.monitor.id
const foundMonitor = parsedMonitorInfo.find(
(monitor: Monitor) => monitor.id === hyprland.active.monitor.id,
);
hyprScaling = foundMonitor?.scale || 1;
} catch (error) {
@@ -58,9 +67,7 @@ const moveBoxToCursor = (self: any, fixed: boolean) => {
}
// If monitor is vertical (transform = 1 || 3) swap height and width
const isVertical = curHyprlandMonitor?.transform !== undefined
? curHyprlandMonitor.transform % 2 !== 0
: false;
const isVertical = curHyprlandMonitor?.transform !== undefined ? curHyprlandMonitor.transform % 2 !== 0 : false;
if (isVertical) {
[monWidth, monHeight] = [monHeight, monWidth];
@@ -100,58 +107,55 @@ setTimeout(() => {
initRender.value = false;
}, 2000);
export default (
{
name,
child,
layout = "center",
transition,
exclusivity = "ignore" as Exclusivity,
fixed = false,
...props
}: DropdownMenuProps
) =>
export default ({
name,
child,
transition,
exclusivity = 'ignore' as Exclusivity,
fixed = false,
...props
}: DropdownMenuProps): Window<Child, Attribute> =>
Widget.Window({
name,
class_names: [name, "dropdown-menu"],
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
visible: initRender.bind("value"),
keymode: "on-demand",
class_names: [name, 'dropdown-menu'],
setup: (w) => w.keybind('Escape', () => App.closeWindow(name)),
visible: initRender.bind('value'),
keymode: 'on-demand',
exclusivity,
layer: "top",
anchor: ["top", "left"],
layer: 'top',
anchor: ['top', 'left'],
child: Widget.EventBox({
class_name: "parent-event",
class_name: 'parent-event',
on_primary_click: () => App.closeWindow(name),
on_secondary_click: () => App.closeWindow(name),
child: Widget.Box({
class_name: "top-eb",
class_name: 'top-eb',
vertical: true,
children: [
Widget.EventBox({
class_name: "mid-eb event-top-padding-static",
class_name: 'mid-eb event-top-padding-static',
hexpand: true,
vexpand: false,
can_focus: false,
child: Widget.Box(),
setup: (w) => {
w.on("button-press-event", () => App.toggleWindow(name));
w.on('button-press-event', () => App.toggleWindow(name));
w.set_margin_top(1);
},
}),
Widget.EventBox({
class_name: "mid-eb event-top-padding",
class_name: 'mid-eb event-top-padding',
hexpand: true,
vexpand: false,
can_focus: false,
child: Widget.Box(),
setup: (w) => {
w.on("button-press-event", () => App.toggleWindow(name));
w.on('button-press-event', () => App.toggleWindow(name));
w.set_margin_top(1);
},
}),
Widget.EventBox({
class_name: "in-eb menu-event-box",
class_name: 'in-eb menu-event-box',
on_primary_click: () => {
return true;
},
@@ -162,18 +166,18 @@ export default (
moveBoxToCursor(self, fixed);
},
child: Widget.Box({
class_name: "dropdown-menu-container",
css: "padding: 1px; margin: -1px;",
class_name: 'dropdown-menu-container',
css: 'padding: 1px; margin: -1px;',
child: Widget.Revealer({
revealChild: false,
setup: (self) =>
self.hook(App, (_, wname, visible) => {
if (wname === name) self.reveal_child = visible;
}),
transition: "crossfade",
transition,
transitionDuration: 350,
child: Widget.Box({
class_name: "dropdown-menu-container",
class_name: 'dropdown-menu-container',
can_focus: true,
children: [child],
}),

View File

@@ -1,25 +1,32 @@
import { WINDOW_LAYOUTS } from "globals/window";
import { LayoutFunction, Layouts, PopupWindowProps } from "lib/types/popupwindow";
import { Exclusivity, Transition } from "lib/types/widget";
import { WINDOW_LAYOUTS } from 'globals/window';
import { LayoutFunction, Layouts, PopupWindowProps } from 'lib/types/popupwindow';
import { Attribute, Child, Exclusivity, GtkWidget, Transition } from 'lib/types/widget';
import Box from 'types/widgets/box';
import EventBox from 'types/widgets/eventbox';
import Window from 'types/widgets/window';
type Opts = {
className: string
vexpand: boolean
}
className: string;
vexpand: boolean;
};
export const Padding = (name: string, opts: Opts) =>
export const Padding = (name: string, opts: Opts): EventBox<Box<GtkWidget, Attribute>, unknown> =>
Widget.EventBox({
class_name: opts?.className || "",
class_name: opts?.className || '',
hexpand: true,
vexpand: typeof opts?.vexpand === "boolean" ? opts.vexpand : true,
vexpand: typeof opts?.vexpand === 'boolean' ? opts.vexpand : true,
can_focus: false,
child: Widget.Box(),
setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)),
setup: (w) => w.on('button-press-event', () => App.toggleWindow(name)),
});
const PopupRevealer = (name: string, child: any, transition = "slide_down" as Transition) =>
const PopupRevealer = (
name: string,
child: GtkWidget,
transition = 'slide_down' as Transition,
): Box<Child, Attribute> =>
Widget.Box(
{ css: "padding: 1px;" },
{ css: 'padding: 1px;' },
Widget.Revealer({
transition,
child: Widget.Box({
@@ -34,7 +41,7 @@ const PopupRevealer = (name: string, child: any, transition = "slide_down" as Tr
}),
);
const Layout: LayoutFunction = (name: string, child: any, transition: Transition) => ({
const Layout: LayoutFunction = (name: string, child: GtkWidget, transition: Transition) => ({
center: () =>
Widget.CenterBox(
{},
@@ -51,14 +58,10 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
Widget.CenterBox(
{},
Padding(name, {} as Opts),
Widget.Box(
{ vertical: true },
PopupRevealer(name, child, transition),
Padding(name, {} as Opts),
),
Widget.Box({ vertical: true }, PopupRevealer(name, child, transition), Padding(name, {} as Opts)),
Padding(name, {} as Opts),
),
"top-right": () =>
'top-right': () =>
Widget.Box(
{},
Padding(name, {} as Opts),
@@ -69,13 +72,13 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
},
Padding(name, {
vexpand: false,
className: "event-top-padding",
className: 'event-top-padding',
}),
PopupRevealer(name, child, transition),
Padding(name, {} as Opts),
),
),
"top-center": () =>
'top-center': () =>
Widget.Box(
{},
Padding(name, {} as Opts),
@@ -86,14 +89,14 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
},
Padding(name, {
vexpand: false,
className: "event-top-padding",
className: 'event-top-padding',
}),
PopupRevealer(name, child, transition),
Padding(name, {} as Opts),
),
Padding(name, {} as Opts),
),
"top-left": () =>
'top-left': () =>
Widget.Box(
{},
Widget.Box(
@@ -103,14 +106,14 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
},
Padding(name, {
vexpand: false,
className: "event-top-padding",
className: 'event-top-padding',
}),
PopupRevealer(name, child, transition),
Padding(name, {} as Opts),
),
Padding(name, {} as Opts),
),
"bottom-left": () =>
'bottom-left': () =>
Widget.Box(
{},
Widget.Box(
@@ -123,7 +126,7 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
),
Padding(name, {} as Opts),
),
"bottom-center": () =>
'bottom-center': () =>
Widget.Box(
{},
Padding(name, {} as Opts),
@@ -137,7 +140,7 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
),
Padding(name, {} as Opts),
),
"bottom-right": () =>
'bottom-right': () =>
Widget.Box(
{},
Padding(name, {} as Opts),
@@ -159,25 +162,25 @@ const isValidLayout = (layout: string): layout is Layouts => {
export default ({
name,
child,
layout = "center",
layout = 'center',
transition,
exclusivity = "ignore" as Exclusivity,
exclusivity = 'ignore' as Exclusivity,
...props
}: PopupWindowProps) => {
const layoutFn = isValidLayout(layout) ? layout : "center";
}: PopupWindowProps): Window<Child, Attribute> => {
const layoutFn = isValidLayout(layout) ? layout : 'center';
const layoutWidget = Layout(name, child, transition)[layoutFn]();
return Widget.Window({
name,
class_names: [name, "popup-window"],
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
class_names: [name, 'popup-window'],
setup: (w) => w.keybind('Escape', () => App.closeWindow(name)),
visible: false,
keymode: "on-demand",
keymode: 'on-demand',
exclusivity,
layer: "top",
anchor: ["top", "bottom", "right", "left"],
layer: 'top',
anchor: ['top', 'bottom', 'right', 'left'],
child: layoutWidget,
...props,
});
}
};

View File

@@ -1,31 +1,36 @@
const audio = await Service.import("audio");
const audio = await Service.import('audio');
import { BarBoxChild } from 'lib/types/bar.js';
import { getIcon } from '../utils.js';
const renderActiveInput = () => {
const renderActiveInput = (): BarBoxChild => {
return [
Widget.Box({
class_name: "menu-slider-container input",
class_name: 'menu-slider-container input',
children: [
Widget.Button({
vexpand: false,
vpack: "end",
vpack: 'end',
setup: (self) => {
self.hook(audio, () => {
const mic = audio.microphone;
const className = `menu-active-button input ${mic.is_muted ? "muted" : ""}`;
const className = `menu-active-button input ${mic.is_muted ? 'muted' : ''}`;
return (self.class_name = className);
});
},
on_primary_click: () =>
(audio.microphone.is_muted = !audio.microphone.is_muted),
on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted),
child: Widget.Icon({
class_name: "menu-active-icon input",
class_name: 'menu-active-icon input',
setup: (self) => {
self.hook(audio, () => {
self.icon = getIcon(
audio.microphone.volume,
audio.microphone.is_muted,
)["mic"];
const isMicMuted =
audio.microphone.is_muted !== null ? audio.microphone.is_muted : true;
if (audio.microphone.volume > 0) {
self.icon = getIcon(audio.microphone.volume, isMicMuted)['mic'];
return;
}
self.icon = getIcon(100, false)['mic'];
});
},
}),
@@ -34,15 +39,17 @@ const renderActiveInput = () => {
vertical: true,
children: [
Widget.Label({
class_name: "menu-active input",
hpack: "start",
truncate: "end",
class_name: 'menu-active input',
hpack: 'start',
truncate: 'end',
wrap: true,
label: audio.bind("microphone").as((v) => v.description === null ? "No input device found..." : v.description),
label: audio
.bind('microphone')
.as((v) => (v.description === null ? 'No input device found...' : v.description)),
}),
Widget.Slider({
value: audio.microphone.bind("volume").as((v) => v),
class_name: "menu-active-slider menu-slider inputs",
value: audio.microphone.bind('volume').as((v) => v),
class_name: 'menu-active-slider menu-slider inputs',
draw_value: false,
hexpand: true,
min: 0,
@@ -52,11 +59,9 @@ const renderActiveInput = () => {
],
}),
Widget.Label({
class_name: "menu-active-percentage input",
vpack: "end",
label: audio.microphone
.bind("volume")
.as((v) => `${Math.round(v * 100)}%`),
class_name: 'menu-active-percentage input',
vpack: 'end',
label: audio.microphone.bind('volume').as((v) => `${Math.round(v * 100)}%`),
}),
],
}),

View File

@@ -1,31 +1,29 @@
const audio = await Service.import("audio");
import { getIcon } from "../utils.js";
const audio = await Service.import('audio');
import { BarBoxChild } from 'lib/types/bar.js';
import { getIcon } from '../utils.js';
const renderActivePlayback = () => {
const renderActivePlayback = (): BarBoxChild => {
return [
Widget.Box({
class_name: "menu-slider-container playback",
class_name: 'menu-slider-container playback',
children: [
Widget.Button({
vexpand: false,
vpack: "end",
vpack: 'end',
setup: (self) => {
self.hook(audio, () => {
const spkr = audio.speaker;
const className = `menu-active-button playback ${spkr.is_muted ? "muted" : ""}`;
const className = `menu-active-button playback ${spkr.is_muted ? 'muted' : ''}`;
return (self.class_name = className);
});
},
on_primary_click: () =>
(audio.speaker.is_muted = !audio.speaker.is_muted),
on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted),
child: Widget.Icon({
class_name: "menu-active-icon playback",
class_name: 'menu-active-icon playback',
setup: (self) => {
self.hook(audio, () => {
self.icon = getIcon(
audio.speaker.volume,
audio.speaker.is_muted,
)["spkr"];
const isSpeakerMuted = audio.speaker.is_muted !== null ? audio.speaker.is_muted : true;
self.icon = getIcon(audio.speaker.volume, isSpeakerMuted)['spkr'];
});
},
}),
@@ -34,16 +32,16 @@ const renderActivePlayback = () => {
vertical: true,
children: [
Widget.Label({
class_name: "menu-active playback",
hpack: "start",
truncate: "end",
class_name: 'menu-active playback',
hpack: 'start',
truncate: 'end',
expand: true,
wrap: true,
label: audio.bind("speaker").as((v) => v.description || ""),
label: audio.bind('speaker').as((v) => v.description || ''),
}),
Widget.Slider({
value: audio["speaker"].bind("volume"),
class_name: "menu-active-slider menu-slider playback",
value: audio['speaker'].bind('volume'),
class_name: 'menu-active-slider menu-slider playback',
draw_value: false,
hexpand: true,
min: 0,
@@ -53,11 +51,9 @@ const renderActivePlayback = () => {
],
}),
Widget.Label({
vpack: "end",
class_name: "menu-active-percentage playback",
label: audio.speaker
.bind("volume")
.as((v) => `${Math.round(v * 100)}%`),
vpack: 'end',
class_name: 'menu-active-percentage playback',
label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`),
}),
],
}),

View File

@@ -1,39 +1,40 @@
import { renderActiveInput } from "./SelectedInput.js";
import { renderActivePlayback } from "./SelectedPlayback.js";
import { BarBoxChild } from 'lib/types/bar.js';
import { renderActiveInput } from './SelectedInput.js';
import { renderActivePlayback } from './SelectedPlayback.js';
const activeDevices = () => {
return Widget.Box({
class_name: "menu-section-container volume",
vertical: true,
children: [
Widget.Box({
class_name: "menu-label-container volume selected",
hpack: "fill",
child: Widget.Label({
class_name: "menu-label audio volume",
hexpand: true,
hpack: "start",
label: "Volume",
}),
}),
Widget.Box({
class_name: "menu-items-section selected",
const activeDevices = (): BarBoxChild => {
return Widget.Box({
class_name: 'menu-section-container volume',
vertical: true,
children: [
Widget.Box({
class_name: "menu-active-container playback",
vertical: true,
children: renderActivePlayback(),
}),
Widget.Box({
class_name: "menu-active-container input",
vertical: true,
children: renderActiveInput(),
}),
Widget.Box({
class_name: 'menu-label-container volume selected',
hpack: 'fill',
child: Widget.Label({
class_name: 'menu-label audio volume',
hexpand: true,
hpack: 'start',
label: 'Volume',
}),
}),
Widget.Box({
class_name: 'menu-items-section selected',
vertical: true,
children: [
Widget.Box({
class_name: 'menu-active-container playback',
vertical: true,
children: renderActivePlayback(),
}),
Widget.Box({
class_name: 'menu-active-container input',
vertical: true,
children: renderActiveInput(),
}),
],
}),
],
}),
],
});
});
};
export { activeDevices };

View File

@@ -1,7 +1,8 @@
const audio = await Service.import("audio");
import { Stream } from "types/service/audio";
const audio = await Service.import('audio');
import { InputDevices } from 'lib/types/audio';
import { Stream } from 'types/service/audio';
const renderInputDevices = (inputDevices: Stream[]) => {
const renderInputDevices = (inputDevices: Stream[]): InputDevices => {
if (inputDevices.length === 0) {
return [
Widget.Button({
@@ -9,11 +10,11 @@ const renderInputDevices = (inputDevices: Stream[]) => {
child: Widget.Box({
children: [
Widget.Box({
hpack: "start",
hpack: 'start',
children: [
Widget.Label({
class_name: "menu-button-name input",
label: "No input devices found...",
class_name: 'menu-button-name input',
label: 'No input devices found...',
}),
],
}),
@@ -29,28 +30,28 @@ const renderInputDevices = (inputDevices: Stream[]) => {
child: Widget.Box({
children: [
Widget.Box({
hpack: "start",
hpack: 'start',
children: [
Widget.Label({
wrap: true,
class_name: audio.microphone
.bind("description")
.bind('description')
.as((v) =>
device.description === v
? "menu-button-icon active input txt-icon"
: "menu-button-icon input txt-icon",
? 'menu-button-icon active input txt-icon'
: 'menu-button-icon input txt-icon',
),
label: "",
label: '',
}),
Widget.Label({
truncate: "end",
truncate: 'end',
wrap: true,
class_name: audio.microphone
.bind("description")
.bind('description')
.as((v) =>
device.description === v
? "menu-button-name active input"
: "menu-button-name input",
? 'menu-button-name active input'
: 'menu-button-name input',
),
label: device.description,
}),

View File

@@ -1,16 +1,17 @@
const audio = await Service.import("audio");
import { Stream } from "types/service/audio";
const audio = await Service.import('audio');
import { PlaybackDevices } from 'lib/types/audio';
import { Stream } from 'types/service/audio';
const renderPlaybacks = (playbackDevices: Stream[]) => {
const renderPlaybacks = (playbackDevices: Stream[]): PlaybackDevices => {
return playbackDevices.map((device) => {
if (device.description === "Dummy Output") {
if (device.description === 'Dummy Output') {
return Widget.Box({
class_name: "menu-unfound-button playback",
class_name: 'menu-unfound-button playback',
child: Widget.Box({
children: [
Widget.Label({
class_name: "menu-button-name playback",
label: "No playback devices found...",
class_name: 'menu-button-name playback',
label: 'No playback devices found...',
}),
],
}),
@@ -22,29 +23,29 @@ const renderPlaybacks = (playbackDevices: Stream[]) => {
child: Widget.Box({
children: [
Widget.Box({
hpack: "start",
hpack: 'start',
children: [
Widget.Label({
truncate: "end",
truncate: 'end',
wrap: true,
class_name: audio.speaker
.bind("description")
.bind('description')
.as((v) =>
device.description === v
? "menu-button-icon active playback txt-icon"
: "menu-button-icon playback txt-icon",
? 'menu-button-icon active playback txt-icon'
: 'menu-button-icon playback txt-icon',
),
label: "",
label: '',
}),
Widget.Label({
truncate: "end",
truncate: 'end',
wrap: true,
class_name: audio.speaker
.bind("description")
.bind('description')
.as((v) =>
device.description === v
? "menu-button-name active playback"
: "menu-button-name playback",
? 'menu-button-name active playback'
: 'menu-button-name playback',
),
label: device.description,
}),

View File

@@ -1,66 +1,63 @@
const audio = await Service.import("audio");
import { renderInputDevices } from "./InputDevices.js";
import { renderPlaybacks } from "./PlaybackDevices.js";
const audio = await Service.import('audio');
import { BoxWidget } from 'lib/types/widget.js';
import { renderInputDevices } from './InputDevices.js';
import { renderPlaybacks } from './PlaybackDevices.js';
const availableDevices = () => {
const availableDevices = (): BoxWidget => {
return Widget.Box({
vertical: true,
children: [
Widget.Box({
class_name: "menu-section-container playback",
class_name: 'menu-section-container playback',
vertical: true,
children: [
Widget.Box({
class_name: "menu-label-container playback",
hpack: "fill",
class_name: 'menu-label-container playback',
hpack: 'fill',
child: Widget.Label({
class_name: "menu-label audio playback",
class_name: 'menu-label audio playback',
hexpand: true,
hpack: "start",
label: "Playback Devices",
hpack: 'start',
label: 'Playback Devices',
}),
}),
Widget.Box({
class_name: "menu-items-section playback",
class_name: 'menu-items-section playback',
vertical: true,
children: [
Widget.Box({
class_name: "menu-container playback",
class_name: 'menu-container playback',
vertical: true,
children: [
Widget.Box({
vertical: true,
children: audio
.bind("speakers")
.as((v) => renderPlaybacks(v)),
children: audio.bind('speakers').as((v) => renderPlaybacks(v)),
}),
],
}),
],
}),
Widget.Box({
class_name: "menu-label-container input",
hpack: "fill",
class_name: 'menu-label-container input',
hpack: 'fill',
child: Widget.Label({
class_name: "menu-label audio input",
class_name: 'menu-label audio input',
hexpand: true,
hpack: "start",
label: "Input Devices",
hpack: 'start',
label: 'Input Devices',
}),
}),
Widget.Box({
class_name: "menu-items-section input",
class_name: 'menu-items-section input',
vertical: true,
children: [
Widget.Box({
class_name: "menu-container input",
class_name: 'menu-container input',
vertical: true,
children: [
Widget.Box({
vertical: true,
children: audio
.bind("microphones")
.as((v) => renderInputDevices(v)),
children: audio.bind('microphones').as((v) => renderInputDevices(v)),
}),
],
}),

View File

@@ -1,24 +1,23 @@
import DropdownMenu from "../DropdownMenu.js";
import { activeDevices } from "./active/index.js";
import { availableDevices } from "./available/index.js";
import Window from 'types/widgets/window.js';
import DropdownMenu from '../DropdownMenu.js';
import { activeDevices } from './active/index.js';
import { availableDevices } from './available/index.js';
import { Attribute, Child } from 'lib/types/widget.js';
export default () => {
export default (): Window<Child, Attribute> => {
return DropdownMenu({
name: "audiomenu",
transition: "crossfade",
name: 'audiomenu',
transition: 'crossfade',
child: Widget.Box({
class_name: "menu-items audio",
hpack: "fill",
class_name: 'menu-items audio',
hpack: 'fill',
hexpand: true,
child: Widget.Box({
vertical: true,
hpack: "fill",
hpack: 'fill',
hexpand: true,
class_name: "menu-items-container audio",
children: [
activeDevices(),
availableDevices(),
],
class_name: 'menu-items-container audio',
children: [activeDevices(), availableDevices()],
}),
}),
});

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