delete ags

This commit is contained in:
wt
2024-11-19 10:14:17 +07:00
parent cb80d84e7e
commit 0a38035dd0
353 changed files with 14 additions and 42215 deletions
-2
View File
@@ -1,2 +0,0 @@
types
node_modules
-25
View File
@@ -1,25 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
root: true,
ignorePatterns: ['.eslintrc.js', 'types/**/*.ts', 'scripts/**/*.js'],
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 }],
},
};
-27
View File
@@ -1,27 +0,0 @@
---
name: Bug report
about: Create a report to help HyprPanel improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- Distribution: [e.g. Arch Linux]
- Window Manager/Desktop Environment: [e.g. Hyprland]
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-41
View File
@@ -1,41 +0,0 @@
name: CI
on:
pull_request:
branches:
- master
jobs:
code_quality:
runs-on: ubuntu-latest
steps:
- name: Checkout main repository
uses: actions/checkout@v3
- name: Clone ags-types to temp dir
uses: actions/checkout@v3
with:
repository: Jas-SinghFSU/ags-types
path: temp-ags-types
- name: Copy types to types/
run: |
rm -rf types
mkdir -p types
cp -R temp-ags-types/types/* types/
rm -rf temp-ags-types
- name: Node Setup
uses: actions/setup-node@v3
with:
node-version: '21'
- name: Install Dependencies
run: npm install
- name: ESLint
run: npm run lint
- name: Type Check
run: npx tsc --noEmit --pretty --extendedDiagnostics
-2
View File
@@ -1,2 +0,0 @@
.weather.json
node_modules
-3
View File
@@ -1,3 +0,0 @@
[submodule "external/ags-types"]
path = external/ags-types
url = https://github.com/Jas-SinghFSU/ags-types.git
-2
View File
@@ -1,2 +0,0 @@
.eslintrc.js
types/**/*.ts
-8
View File
@@ -1,8 +0,0 @@
{
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"printWidth": 120,
"tabWidth": 4,
"useTabs": false
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 Jas Singh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-317
View File
@@ -1,317 +0,0 @@
<p align="center">
<a href="https://hyprpanel.com" target="_blank" rel="noopener noreferrer">
<img width="180" src="./assets/hyprpanel.png" alt="Hyprpanel logo">
</a>
</p>
<br/>
<p align="center">
<a href="https://hyprpanel.com/getting_started/installation.html"><img src="https://img.shields.io/badge/Wiki-hyprpanel-orange?style=flat&logo=wiki" alt="wiki"></a>
<a href="https://discord.gg/MNpg7Z2b3a"><img src="https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord" alt="discord chat"></a>
</p>
<br/>
# HyprPanel 🚀
A panel built for Hyprland with [AGS](https://github.com/Aylur/ags)
![HyprPanel1](./assets/hp1.png)
![HyprPanel2](./assets/hp2.png)
## Installation
The [HyprPanel Wiki](https://hyprpanel.com/getting_started/installation.html) contains in depth instructions for installing the panel and all of its dependencies. The instructions below are general instructions for installing the panel.
## Requirements
Bun
```sh
curl -fsSL https://bun.sh/install | bash && \
sudo ln -s $HOME/.bun/bin/bun /usr/local/bin/bun
```
Additional dependencies:
```sh
pipewire
libgtop
bluez
bluez-utils
grimblast
gpu-screen-recorder
hyprpicker
btop
networkmanager
matugen
wl-clipboard
swww
dart-sass
brightnessctl
gnome-bluetooth-3.0
```
Optional Dependencies:
```sh
## Used for Tracking GPU Usage in your Dashboard (NVidia only)
python
python-gpustat
## Only if a pywal hook from wallpaper changes applied through settings is desired
pywal
## To check for pacman updates in the default script used in the updates module
pacman-contrib
## To switch between power profiles in battery module
power-profiles-daemon
```
### Arch
pacman:
```bash
sudo pacman -S pipewire libgtop bluez bluez-utils btop networkmanager dart-sass wl-clipboard brightnessctl swww python gnome-bluetooth-3.0 pacman-contrib power-profiles-daemon
```
AUR:
```bash
yay -S grimblast-git gpu-screen-recorder hyprpicker matugen-bin python-gpustat aylurs-gtk-shell-git
```
### Fedora
COPR - Add [solopasha/hyprland](https://copr.fedorainfracloud.org/coprs/solopasha/hyprland/) for most hyprland-related dependencies, and [hues-sueh/packages](https://copr.fedorainfracloud.org/coprs/heus-sueh/packages/) for matugen. Both provide the `swww` package, so prioritise the former repo:
```bash
sudo dnf copr enable solopasha/hyprland
sudo dnf copr enable heus-sueh/packages
sudo dnf config-manager --save --setopt=copr:copr.fedorainfracloud.org:heus-sueh:packages.priority=200
```
DNF:
```bash
sudo dnf install pipewire libgtop2 bluez bluez-tools grimblast hyprpicker btop NetworkManager wl-clipboard swww brightnessctl gnome-bluetooth aylurs-gtk-shell power-profiles-daemon gvfs
```
bun:
```bash
bun install -g sass
```
flatpak:
```bash
flatpak install flathub --system com.dec05eba.gpu_screen_recorder
```
#### Optional Dependencies
pip:
```bash
sudo dnf install python python3-pip; pip install gpustat pywal
```
### NixOS
For NixOS/Home-Manager, see [NixOS & Home-Manager instructions](#nixos--home-manager).
## Instructions
### AGS
Once everything is installed you need to put the contents of this repo in `~/.config/ags`.
If you already have something in `~/.config/ags`, it's recommended that you back it up with:
```bash
mv $HOME/.config/ags $HOME/.config/ags.bkup
```
Otherwise you can use this command to install the panel:
```bash
git clone https://github.com/Jas-SinghFSU/HyprPanel.git && \
ln -s $(pwd)/HyprPanel $HOME/.config/ags
```
### Nerd Fonts
Additionally, you need to ensure that you have a [Nerd Font](https://www.nerdfonts.com/font-downloads) installed for your icons to render properly.
### Launch the panel
Afterwards you can run the panel with the following command in your terminal:
```bash
ags
```
Or you can add it to your Hyprland config (hyprland.conf) to auto-start with:
```bash
exec-once = ags
```
### NixOS & Home-Manager
Alternatively, if you're using NixOS and/or Home-Manager, you can setup AGS using the provided Nix Flake. First, add the repository to your Flake's inputs, and enable the overlay.
```nix
# flake.nix
{
inputs.hyprpanel.url = "github:Jas-SinghFSU/HyprPanel";
# ...
outputs = { self, nixpkgs, ... }@inputs:
let
# ...
system = "x86_64-linux"; # change to whatever your system should be.
pkgs = import nixpkgs {
inherit system;
# ...
overlays = [
inputs.hyprpanel.overlay
];
};
in {
# ...
}
}
```
Once you've set up the overlay, you can reference HyprPanel with `pkgs.hyprpanel` as if it were any other Nix package. This means you can reference it as a NixOS system/user package, a Home-Manager user package, or as a direct reference in your Hyprland configuration (if your configuration is managed by Home-Manager). The first three methods will add it to your `$PATH` (first globally, second two user-only), however the final will not.
```nix
# configuration.nix
# install it as a system package
environment.systemPackages = with pkgs; [
# ...
hyprpanel
# ...
];
# or install it as a user package
users.users.<username>.packages = with pkgs; [
# ...
hyprpanel
# ...
];
# home.nix
# install it as a user package with home-manager
home.packages = with pkgs; [
# ...
hyprpanel
# ...
];
# or reference it directly in your Hyprland configuration
wayland.windowManager.hyprland.settings.exec-once = [
"${pkgs.hyprpanel}/bin/hyprpanel"
];
```
### Notifications
HyprPanel handles notifications through the AGS built-in notification service. If you're already using a notification daemon such as Dunst or Mako, you may have to stop them to prevent conflicts with HyprPanel.
> NOTE: If your system is in a language other than English, the resource monitor in the dashboard may not work properly.
## Configuration
The HyprPanel comes with a configuration menu which is available by opening the Dashboard menu (click the button in the bar with the default - Arch - icon) and then clicking the Gear icon.
### Size
The panel is automatically scaled based on your font size in `Configuration > General`.
### Specifying bar layouts per monitor
To specify layouts for each monitor you can create a JSON object such as:
```JSON
{
"0": {
"left": [
"dashboard",
"workspaces",
"windowtitle"
],
"middle": [
"media"
],
"right": [
"volume",
"clock",
"notifications"
]
},
"1": {
"left": [
"dashboard",
"workspaces",
"windowtitle"
],
"middle": [
"media"
],
"right": [
"volume",
"clock",
"notifications"
]
},
"2": {
"left": [
"dashboard",
"workspaces",
"windowtitle"
],
"middle": [
"media"
],
"right": [
"volume",
"network",
"bluetooth",
"systray",
"clock",
"notifications"
]
}
}
```
Where each monitor is defined by its index (0, 1, 2 in this case) and each section (left, middle, right) contains one or more of the following modules:
```js
'battery';
'dashboard';
'workspaces';
'windowtitle';
'media';
'notifications';
'volume';
'network';
'bluetooth';
'clock';
'systray';
```
Since the text-box in the options dialog isn't sufficient, it is recommended that you create this JSON configuration in a text editor elsewhere and paste it into the layout text-box under Configuration > Bar > "Bar Layouts for Monitors".
### Additional Configuration
#### GPU Tracking
If you have an NVidia GPU, you can track your GPU usage in your Dashboard by going to your `Settings > Configuration > Dashboard Menu > Track GPU` and turning it on.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 KiB

-58
View File
@@ -1,58 +0,0 @@
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 v = {
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 '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`;
}
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();
}
await import(`file://${main}`);
} catch (error) {
console.error(error);
App.quit();
}
export {};
-77
View File
@@ -1,77 +0,0 @@
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<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, P extends unknown[], F extends GenericFunction<T, P>>(
targetVariable: VariableType<T>,
trackers: Array<Bind>,
pollingInterval: Bind,
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
intervalInstance = Utils.interval(pollIntrvl, () => {
targetVariable.value = someFunc(...params);
});
};
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
intervalFn(pollIntrvl);
});
};
/**
* @param {VariableType<T>} targetVariable - The Variable to update with the result of the command.
* @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<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, P extends unknown[], F extends GenericFunction<T, [string, ...P]>>(
targetVariable: VariableType<T>,
trackers: Array<Bind>,
pollingInterval: Bind,
someCommand: string,
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
intervalInstance = Utils.interval(pollIntrvl, () => {
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}`));
});
};
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
intervalFn(pollIntrvl);
});
};
-595
View File
@@ -1,595 +0,0 @@
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 = (): Scrollable<GtkWidget, Attribute> =>
Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page customModules paged-container',
child: Widget.Box({
class_name: 'menu-theme-page paged-container',
vertical: true,
children: [
/*
************************************
* GENERAL *
************************************
*/
Header('General'),
Option({
opt: options.bar.customModules.scrollSpeed,
title: 'Scrolling Speed',
type: 'number',
}),
/*
************************************
* RAM *
************************************
*/
Header('RAM'),
Option({
opt: options.theme.bar.buttons.modules.ram.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.ram.icon,
title: 'Ram Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.ram.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.ram.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.ram.labelType,
title: 'Label Type',
type: 'enum',
enums: ['used/total', 'used', 'free', 'percentage'],
}),
Option({
opt: options.bar.customModules.ram.round,
title: 'Round',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.ram.pollingInterval,
title: 'Polling Interval',
type: 'number',
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
}),
Option({
opt: options.bar.customModules.ram.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.ram.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.ram.middleClick,
title: 'Middle Click',
type: 'string',
}),
/*
************************************
* CPU *
************************************
*/
Header('CPU'),
Option({
opt: options.theme.bar.buttons.modules.cpu.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.cpu.icon,
title: 'Cpu Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.cpu.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.round,
title: 'Round',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.cpu.pollingInterval,
title: 'Polling Interval',
type: 'number',
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
}),
Option({
opt: options.bar.customModules.cpu.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.cpu.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
************************************
* STORAGE *
************************************
*/
Header('Storage'),
Option({
opt: options.theme.bar.buttons.modules.storage.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.storage.icon,
title: 'Storage Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.storage.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.storage.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.storage.labelType,
title: 'Label Type',
type: 'enum',
enums: ['used/total', 'used', 'free', 'percentage'],
}),
Option({
opt: options.bar.customModules.storage.round,
title: 'Round',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.storage.pollingInterval,
title: 'Polling Interval',
type: 'number',
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
}),
Option({
opt: options.bar.customModules.storage.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.storage.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.storage.middleClick,
title: 'Middle Click',
type: 'string',
}),
/*
************************************
* NETSTAT *
************************************
*/
Header('Netstat'),
Option({
opt: options.theme.bar.buttons.modules.netstat.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.netstat.networkInterface,
title: 'Network Interface',
subtitle:
"Name of the network interface to poll.\nHINT: Get list of interfaces with 'cat /proc/net/dev'",
type: 'string',
}),
Option({
opt: options.bar.customModules.netstat.icon,
title: 'Netstat Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.netstat.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.netstat.rateUnit,
title: 'Rate Unit',
type: 'enum',
enums: ['GiB', 'MiB', 'KiB', 'auto'],
}),
Option({
opt: options.theme.bar.buttons.modules.netstat.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.netstat.labelType,
title: 'Label Type',
type: 'enum',
enums: ['full', 'in', 'out'],
}),
Option({
opt: options.bar.customModules.netstat.round,
title: 'Round',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.netstat.pollingInterval,
title: 'Polling Interval',
type: 'number',
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
}),
Option({
opt: options.bar.customModules.netstat.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.netstat.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.netstat.middleClick,
title: 'Middle Click',
type: 'string',
}),
/*
************************************
* KEYBOARD LAYOUT *
************************************
*/
Header('Keyboard Layout'),
Option({
opt: options.theme.bar.buttons.modules.kbLayout.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.kbLayout.icon,
title: 'Keyboard Layout Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.kbLayout.labelType,
title: 'Label Type',
type: 'enum',
enums: ['layout', 'code'],
}),
Option({
opt: options.theme.bar.buttons.modules.kbLayout.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.kbLayout.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
************************************
* UPDATES *
************************************
*/
Header('Updates'),
Option({
opt: options.theme.bar.buttons.modules.updates.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.updates.updateCommand,
title: 'Check Updates Command',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.icon,
title: 'Updates Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.updates.padZero,
title: 'Pad with 0',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.updates.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.pollingInterval,
title: 'Polling Interval',
type: 'number',
subtitle: "WARNING: Be careful of your package manager's rate limit.",
min: 100,
max: 60 * 24 * 1000,
increment: 1000,
}),
Option({
opt: options.bar.customModules.updates.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.updates.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
************************************
* SUBMAP *
************************************
*/
Header('Submap'),
Option({
opt: options.theme.bar.buttons.modules.submap.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.submap.showSubmapName,
title: 'Show Submap Name',
subtitle:
'When enabled, the name of the current submap will be displayed' +
' instead of the Submap Enabled or Disabled text.',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.submap.enabledIcon,
title: 'Enabled Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.disabledIcon,
title: 'Disabled Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.enabledText,
title: 'Enabled Text',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.disabledText,
title: 'Disabled Text',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.submap.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.submap.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
************************************
* WEATHER *
************************************
*/
Header('Weather'),
Option({
opt: options.theme.bar.buttons.modules.weather.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.weather.label,
title: 'Show Label',
type: 'boolean',
}),
Option({
opt: options.bar.customModules.weather.unit,
title: 'Units',
type: 'enum',
enums: ['imperial', 'metric'],
}),
Option({
opt: options.theme.bar.buttons.modules.weather.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.weather.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.weather.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.weather.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.weather.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.weather.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
************************************
* POWER *
************************************
*/
Header('Power'),
Option({
opt: options.theme.bar.buttons.modules.power.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.modules.power.spacing,
title: 'Spacing',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.icon,
title: 'Power Button Icon',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.customModules.power.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
],
}),
});
@@ -1,20 +0,0 @@
// @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 = (): number => {
const currentCpuData = new GTop.glibtop_cpu();
GTop.glibtop_get_cpu(currentCpuData);
// Calculate the differences from the previous to current data
const totalDiff = currentCpuData.total - previousCpuData.total;
const idleDiff = currentCpuData.idle - previousCpuData.idle;
const cpuUsagePercentage = totalDiff > 0 ? ((totalDiff - idleDiff) / totalDiff) * 100 : 0;
previousCpuData = currentCpuData;
return cpuUsagePercentage;
};
-69
View File
@@ -1,69 +0,0 @@
import options from 'options';
// Module initializer
import { module } from '../module';
import Button from 'types/widgets/button';
// Utility Methods
import { inputHandler } from 'customModules/utils';
import { computeCPU } from './computeCPU';
import { pollVariable } from 'customModules/PollVar';
import { BarBoxChild } from 'lib/types/bar';
import { Attribute, Child } from 'lib/types/widget';
// All the user configurable options for the cpu module that are needed
const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown, pollingInterval, icon } =
options.bar.customModules.cpu;
export const cpuUsage = Variable(0);
pollVariable(
// Variable to poll and update with the result of the function passed in
cpuUsage,
// Variables that should trigger the polling function to update when they change
[round.bind('value')],
// Interval at which to poll
pollingInterval.bind('value'),
// Function to execute to get the network data
computeCPU,
);
export const Cpu = (): BarBoxChild => {
const renderLabel = (cpuUsg: number, rnd: boolean): string => {
return rnd ? `${Math.round(cpuUsg)}%` : `${cpuUsg.toFixed(2)}%`;
};
const cpuModule = module({
textIcon: icon.bind('value'),
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<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return cpuModule;
};
@@ -1,28 +0,0 @@
import {
HyprctlDeviceLayout,
HyprctlKeyboard,
KbLabelType,
LayoutKeys,
LayoutValues,
} from 'lib/types/customModules/kbLayout';
import { layoutMap } from './layouts';
export const getKeyboardLayout = (obj: string, format: KbLabelType): LayoutKeys | LayoutValues => {
const hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj);
const keyboards = hyprctlDevices['keyboards'];
if (keyboards.length === 0) {
return format === 'code' ? 'Unknown' : 'Unknown Layout';
}
let mainKb = keyboards.find((kb: HyprctlKeyboard) => kb.main);
if (!mainKb) {
mainKb = keyboards[keyboards.length - 1];
}
const layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys;
const foundLayout: LayoutValues = layoutMap[layout];
return format === 'code' ? foundLayout || layout : layout;
};
@@ -1,73 +0,0 @@
const hyprland = await Service.import('hyprland');
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 { BarBoxChild } from 'lib/types/bar';
import { Attribute, Child } from 'lib/types/widget';
const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } =
options.bar.customModules.kbLayout;
export const KbInput = (): BarBoxChild => {
const keyboardModule = module({
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(labelType, () => {
Utils.execAsync('hyprctl devices -j')
.then((obj) => {
self.label = getKeyboardLayout(obj, labelType.value);
})
.catch((err) => {
console.error(err);
});
});
},
boxClass: 'kblayout',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return keyboardModule;
};
@@ -1,585 +0,0 @@
export const layoutMap = {
'Abkhazian (Russia)': 'RU (Ab)',
Akan: 'GH (Akan)',
Albanian: 'AL',
'Albanian (Plisi)': 'AL (Plisi)',
'Albanian (Veqilharxhi)': 'AL (Veqilharxhi)',
Amharic: 'ET',
Arabic: 'ARA',
'Arabic (Algeria)': 'DZ (Ar)',
'Arabic (AZERTY, Eastern Arabic numerals)': 'ARA (Azerty Digits)',
'Arabic (AZERTY)': 'ARA (Azerty)',
'Arabic (Buckwalter)': 'ARA (Buckwalter)',
'Arabic (Eastern Arabic numerals)': 'ARA (Digits)',
'Arabic (Macintosh)': 'ARA (Mac)',
'Arabic (Morocco)': 'MA',
'Arabic (OLPC)': 'ARA (Olpc)',
'Arabic (Pakistan)': 'PK (Ara)',
'Arabic (QWERTY, Eastern Arabic numerals)': 'ARA (Qwerty Digits)',
'Arabic (QWERTY)': 'ARA (Qwerty)',
'Arabic (Syria)': 'SY',
Armenian: 'AM',
'Armenian (alt. eastern)': 'AM (Eastern-Alt)',
'Armenian (alt. phonetic)': 'AM (Phonetic-Alt)',
'Armenian (eastern)': 'AM (Eastern)',
'Armenian (phonetic)': 'AM (Phonetic)',
'Armenian (western)': 'AM (Western)',
'Asturian (Spain, with bottom-dot H and L)': 'ES (Ast)',
Avatime: 'GH (Avn)',
Azerbaijani: 'AZ',
'Azerbaijani (Cyrillic)': 'AZ (Cyrillic)',
'Azerbaijani (Iran)': 'IR (Azb)',
Bambara: 'ML',
Bangla: 'BD',
'Bangla (India, Baishakhi InScript)': 'IN (Ben Inscript)',
'Bangla (India, Baishakhi)': 'IN (Ben Baishakhi)',
'Bangla (India, Bornona)': 'IN (Ben Bornona)',
'Bangla (India, Gitanjali)': 'IN (Ben Gitanjali)',
'Bangla (India, Probhat)': 'IN (Ben Probhat)',
'Bangla (India)': 'IN (Ben)',
'Bangla (Probhat)': 'BD (Probhat)',
Bashkirian: 'RU (Bak)',
Belarusian: 'BY',
'Belarusian (intl.)': 'BY (Intl)',
'Belarusian (Latin)': 'BY (Latin)',
'Belarusian (legacy)': 'BY (Legacy)',
'Belarusian (phonetic)': 'BY (Phonetic)',
Belgian: 'BE',
'Belgian (alt.)': 'BE (Oss)',
'Belgian (ISO, alt.)': 'BE (Iso-Alternate)',
'Belgian (Latin-9 only, alt.)': 'BE (Oss Latin9)',
'Belgian (no dead keys)': 'BE (Nodeadkeys)',
'Belgian (Wang 724 AZERTY)': 'BE (Wang)',
'Berber (Algeria, Latin)': 'DZ',
'Berber (Algeria, Tifinagh)': 'DZ (Ber)',
'Berber (Morocco, Tifinagh alt.)': 'MA (Tifinagh-Alt)',
'Berber (Morocco, Tifinagh extended phonetic)': 'MA (Tifinagh-Extended-Phonetic)',
'Berber (Morocco, Tifinagh extended)': 'MA (Tifinagh-Extended)',
'Berber (Morocco, Tifinagh phonetic, alt.)': 'MA (Tifinagh-Alt-Phonetic)',
'Berber (Morocco, Tifinagh phonetic)': 'MA (Tifinagh-Phonetic)',
'Berber (Morocco, Tifinagh)': 'MA (Tifinagh)',
Bosnian: 'BA',
'Bosnian (US, with Bosnian digraphs)': 'BA (Unicodeus)',
'Bosnian (US)': 'BA (Us)',
'Bosnian (with Bosnian digraphs)': 'BA (Unicode)',
'Bosnian (with guillemets)': 'BA (Alternatequotes)',
Braille: 'BRAI',
'Braille (left-handed inverted thumb)': 'BRAI (Left Hand Invert)',
'Braille (left-handed)': 'BRAI (Left Hand)',
'Braille (right-handed inverted thumb)': 'BRAI (Right Hand Invert)',
'Braille (right-handed)': 'BRAI (Right Hand)',
'Breton (France)': 'FR (Bre)',
Bulgarian: 'BG',
'Bulgarian (enhanced)': 'BG (Bekl)',
'Bulgarian (new phonetic)': 'BG (Bas Phonetic)',
'Bulgarian (traditional phonetic)': 'BG (Phonetic)',
Burmese: 'MM',
'Burmese Zawgyi': 'MM (Zawgyi)',
'Cameroon (AZERTY, intl.)': 'CM (Azerty)',
'Cameroon (Dvorak, intl.)': 'CM (Dvorak)',
'Cameroon Multilingual (QWERTY, intl.)': 'CM (Qwerty)',
'Canadian (CSA)': 'CA (Multix)',
'Catalan (Spain, with middle-dot L)': 'ES (Cat)',
Cherokee: 'US (Chr)',
Chinese: 'CN',
Chuvash: 'RU (Cv)',
'Chuvash (Latin)': 'RU (Cv Latin)',
CloGaelach: 'IE (CloGaelach)',
'Crimean Tatar (Turkish Alt-Q)': 'UA (Crh Alt)',
'Crimean Tatar (Turkish F)': 'UA (Crh F)',
'Crimean Tatar (Turkish Q)': 'UA (Crh)',
Croatian: 'HR',
'Croatian (US, with Croatian digraphs)': 'HR (Unicodeus)',
'Croatian (US)': 'HR (Us)',
'Croatian (with Croatian digraphs)': 'HR (Unicode)',
'Croatian (with guillemets)': 'HR (Alternatequotes)',
Czech: 'CZ',
'Czech (QWERTY, extended backslash)': 'CZ (Qwerty Bksl)',
'Czech (QWERTY, Macintosh)': 'CZ (Qwerty-Mac)',
'Czech (QWERTY)': 'CZ (Qwerty)',
'Czech (UCW, only accented letters)': 'CZ (Ucw)',
'Czech (US, Dvorak, UCW support)': 'CZ (Dvorak-Ucw)',
'Czech (with <\\|> key)': 'CZ (Bksl)',
Danish: 'DK',
'Danish (Dvorak)': 'DK (Dvorak)',
'Danish (Macintosh, no dead keys)': 'DK (Mac Nodeadkeys)',
'Danish (Macintosh)': 'DK (Mac)',
'Danish (no dead keys)': 'DK (Nodeadkeys)',
'Danish (Windows)': 'DK (Winkeys)',
Dari: 'AF',
'Dari (Afghanistan, OLPC)': 'AF (Fa-Olpc)',
Dhivehi: 'MV',
Dutch: 'NL',
'Dutch (Macintosh)': 'NL (Mac)',
'Dutch (standard)': 'NL (Std)',
'Dutch (US)': 'NL (Us)',
Dzongkha: 'BT',
'English (Australian)': 'AU',
'English (Cameroon)': 'CM',
'English (Canada)': 'CA (Eng)',
'English (classic Dvorak)': 'US (Dvorak-Classic)',
'English (Colemak-DH ISO)': 'US (Colemak Dh Iso)',
'English (Colemak-DH)': 'US (Colemak Dh)',
'English (Colemak)': 'US (Colemak)',
'English (Dvorak, alt. intl.)': 'US (Dvorak-Alt-Intl)',
'English (Dvorak, intl., with dead keys)': 'US (Dvorak-Intl)',
'English (Dvorak, left-handed)': 'US (Dvorak-L)',
'English (Dvorak, Macintosh)': 'US (Dvorak-Mac)',
'English (Dvorak, right-handed)': 'US (Dvorak-R)',
'English (Dvorak)': 'US (Dvorak)',
'English (Ghana, GILLBT)': 'GH (Gillbt)',
'English (Ghana, multilingual)': 'GH (Generic)',
'English (Ghana)': 'GH',
'English (India, with rupee)': 'IN (Eng)',
'English (intl., with AltGr dead keys)': 'US (Altgr-Intl)',
'English (Macintosh)': 'US (Mac)',
'English (Mali, US, intl.)': 'ML (Us-Intl)',
'English (Mali, US, Macintosh)': 'ML (Us-Mac)',
'English (Nigeria)': 'NG',
'English (Norman)': 'US (Norman)',
'English (programmer Dvorak)': 'US (Dvp)',
'English (South Africa)': 'ZA',
'English (the divide/multiply toggle the layout)': 'US (Olpc2)',
'English (UK, Colemak-DH)': 'GB (Colemak Dh)',
'English (UK, Colemak)': 'GB (Colemak)',
'English (UK, Dvorak, with UK punctuation)': 'GB (Dvorakukp)',
'English (UK, Dvorak)': 'GB (Dvorak)',
'English (UK, extended, Windows)': 'GB (Extd)',
'English (UK, intl., with dead keys)': 'GB (Intl)',
'English (UK, Macintosh, intl.)': 'GB (Mac Intl)',
'English (UK, Macintosh)': 'GB (Mac)',
'English (UK)': 'GB',
'English (US, alt. intl.)': 'US (Alt-Intl)',
'English (US, euro on 5)': 'US (Euro)',
'English (US, intl., with dead keys)': 'US (Intl)',
'English (US, Symbolic)': 'US (Symbolic)',
'English (US)': 'US',
'English (Workman, intl., with dead keys)': 'US (Workman-Intl)',
'English (Workman)': 'US (Workman)',
Esperanto: 'EPO',
'Esperanto (Brazil, Nativo)': 'BR (Nativo-Epo)',
'Esperanto (legacy)': 'EPO (Legacy)',
'Esperanto (Portugal, Nativo)': 'PT (Nativo-Epo)',
Estonian: 'EE',
'Estonian (Dvorak)': 'EE (Dvorak)',
'Estonian (no dead keys)': 'EE (Nodeadkeys)',
'Estonian (US)': 'EE (Us)',
Ewe: 'GH (Ewe)',
Faroese: 'FO',
'Faroese (no dead keys)': 'FO (Nodeadkeys)',
Filipino: 'PH',
'Filipino (Capewell-Dvorak, Baybayin)': 'PH (Capewell-Dvorak-Bay)',
'Filipino (Capewell-Dvorak, Latin)': 'PH (Capewell-Dvorak)',
'Filipino (Capewell-QWERF 2006, Baybayin)': 'PH (Capewell-Qwerf2k6-Bay)',
'Filipino (Capewell-QWERF 2006, Latin)': 'PH (Capewell-Qwerf2k6)',
'Filipino (Colemak, Baybayin)': 'PH (Colemak-Bay)',
'Filipino (Colemak, Latin)': 'PH (Colemak)',
'Filipino (Dvorak, Baybayin)': 'PH (Dvorak-Bay)',
'Filipino (Dvorak, Latin)': 'PH (Dvorak)',
'Filipino (QWERTY, Baybayin)': 'PH (Qwerty-Bay)',
Finnish: 'FI',
'Finnish (classic, no dead keys)': 'FI (Nodeadkeys)',
'Finnish (classic)': 'FI (Classic)',
'Finnish (Macintosh)': 'FI (Mac)',
'Finnish (Windows)': 'FI (Winkeys)',
French: 'FR',
'French (alt., Latin-9 only)': 'FR (Oss Latin9)',
'French (alt., no dead keys)': 'FR (Oss Nodeadkeys)',
'French (alt.)': 'FR (Oss)',
'French (AZERTY, AFNOR)': 'FR (Afnor)',
'French (AZERTY)': 'FR (Azerty)',
'French (BEPO, AFNOR)': 'FR (Bepo Afnor)',
'French (BEPO, Latin-9 only)': 'FR (Bepo Latin9)',
'French (BEPO)': 'FR (Bepo)',
'French (Cameroon)': 'CM (French)',
'French (Canada, Dvorak)': 'CA (Fr-Dvorak)',
'French (Canada, legacy)': 'CA (Fr-Legacy)',
'French (Canada)': 'CA',
'French (Democratic Republic of the Congo)': 'CD',
'French (Dvorak)': 'FR (Dvorak)',
'French (legacy, alt., no dead keys)': 'FR (Latin9 Nodeadkeys)',
'French (legacy, alt.)': 'FR (Latin9)',
'French (Macintosh)': 'FR (Mac)',
'French (Mali, alt.)': 'ML (Fr-Oss)',
'French (Morocco)': 'MA (French)',
'French (no dead keys)': 'FR (Nodeadkeys)',
'French (Switzerland, Macintosh)': 'CH (Fr Mac)',
'French (Switzerland, no dead keys)': 'CH (Fr Nodeadkeys)',
'French (Switzerland)': 'CH (Fr)',
'French (Togo)': 'TG',
'French (US)': 'FR (Us)',
'Friulian (Italy)': 'IT (Fur)',
Fula: 'GH (Fula)',
Ga: 'GH (Ga)',
Georgian: 'GE',
'Georgian (ergonomic)': 'GE (Ergonomic)',
'Georgian (France, AZERTY Tskapo)': 'FR (Geo)',
'Georgian (Italy)': 'IT (Geo)',
'Georgian (MESS)': 'GE (Mess)',
German: 'DE',
'German (Austria, Macintosh)': 'AT (Mac)',
'German (Austria, no dead keys)': 'AT (Nodeadkeys)',
'German (Austria)': 'AT',
'German (dead acute)': 'DE (Deadacute)',
'German (dead grave acute)': 'DE (Deadgraveacute)',
'German (dead tilde)': 'DE (Deadtilde)',
'German (Dvorak)': 'DE (Dvorak)',
'German (E1)': 'DE (E1)',
'German (E2)': 'DE (E2)',
'German (Macintosh, no dead keys)': 'DE (Mac Nodeadkeys)',
'German (Macintosh)': 'DE (Mac)',
'German (Neo 2)': 'DE (Neo)',
'German (no dead keys)': 'DE (Nodeadkeys)',
'German (QWERTY)': 'DE (Qwerty)',
'German (Switzerland, legacy)': 'CH (Legacy)',
'German (Switzerland, Macintosh)': 'CH (De Mac)',
'German (Switzerland, no dead keys)': 'CH (De Nodeadkeys)',
'German (Switzerland)': 'CH',
'German (T3)': 'DE (T3)',
'German (US)': 'DE (Us)',
Greek: 'GR',
'Greek (extended)': 'GR (Extended)',
'Greek (no dead keys)': 'GR (Nodeadkeys)',
'Greek (polytonic)': 'GR (Polytonic)',
'Greek (simple)': 'GR (Simple)',
Gujarati: 'IN (Guj)',
'Hanyu Pinyin Letters (with AltGr dead keys)': 'CN (Altgr-Pinyin)',
'Hausa (Ghana)': 'GH (Hausa)',
'Hausa (Nigeria)': 'NG (Hausa)',
Hawaiian: 'US (Haw)',
Hebrew: 'IL',
'Hebrew (Biblical, Tiro)': 'IL (Biblical)',
'Hebrew (lyx)': 'IL (Lyx)',
'Hebrew (phonetic)': 'IL (Phonetic)',
'Hindi (Bolnagri)': 'IN (Bolnagri)',
'Hindi (KaGaPa, phonetic)': 'IN (Hin-Kagapa)',
'Hindi (Wx)': 'IN (Hin-Wx)',
Hungarian: 'HU',
'Hungarian (no dead keys)': 'HU (Nodeadkeys)',
'Hungarian (QWERTY, 101-key, comma, dead keys)': 'HU (101 Qwerty Comma Dead)',
'Hungarian (QWERTY, 101-key, comma, no dead keys)': 'HU (101 Qwerty Comma Nodead)',
'Hungarian (QWERTY, 101-key, dot, dead keys)': 'HU (101 Qwerty Dot Dead)',
'Hungarian (QWERTY, 101-key, dot, no dead keys)': 'HU (101 Qwerty Dot Nodead)',
'Hungarian (QWERTY, 102-key, comma, dead keys)': 'HU (102 Qwerty Comma Dead)',
'Hungarian (QWERTY, 102-key, comma, no dead keys)': 'HU (102 Qwerty Comma Nodead)',
'Hungarian (QWERTY, 102-key, dot, dead keys)': 'HU (102 Qwerty Dot Dead)',
'Hungarian (QWERTY, 102-key, dot, no dead keys)': 'HU (102 Qwerty Dot Nodead)',
'Hungarian (QWERTY)': 'HU (Qwerty)',
'Hungarian (QWERTZ, 101-key, comma, dead keys)': 'HU (101 Qwertz Comma Dead)',
'Hungarian (QWERTZ, 101-key, comma, no dead keys)': 'HU (101 Qwertz Comma Nodead)',
'Hungarian (QWERTZ, 101-key, dot, dead keys)': 'HU (101 Qwertz Dot Dead)',
'Hungarian (QWERTZ, 101-key, dot, no dead keys)': 'HU (101 Qwertz Dot Nodead)',
'Hungarian (QWERTZ, 102-key, comma, dead keys)': 'HU (102 Qwertz Comma Dead)',
'Hungarian (QWERTZ, 102-key, comma, no dead keys)': 'HU (102 Qwertz Comma Nodead)',
'Hungarian (QWERTZ, 102-key, dot, dead keys)': 'HU (102 Qwertz Dot Dead)',
'Hungarian (QWERTZ, 102-key, dot, no dead keys)': 'HU (102 Qwertz Dot Nodead)',
'Hungarian (standard)': 'HU (Standard)',
Icelandic: 'IS',
'Icelandic (Dvorak)': 'IS (Dvorak)',
'Icelandic (Macintosh, legacy)': 'IS (Mac Legacy)',
'Icelandic (Macintosh)': 'IS (Mac)',
Igbo: 'NG (Igbo)',
Indian: 'IN',
'Indic IPA': 'IN (Iipa)',
'Indonesian (Arab Melayu, extended phonetic)': 'ID (Melayu-Phoneticx)',
'Indonesian (Arab Melayu, phonetic)': 'ID (Melayu-Phonetic)',
'Indonesian (Arab Pegon, phonetic)': 'ID (Pegon-Phonetic)',
'Indonesian (Latin)': 'ID',
Inuktitut: 'CA (Ike)',
Iraqi: 'IQ',
Irish: 'IE',
'Irish (UnicodeExpert)': 'IE (UnicodeExpert)',
Italian: 'IT',
'Italian (IBM 142)': 'IT (Ibm)',
'Italian (intl., with dead keys)': 'IT (Intl)',
'Italian (Macintosh)': 'IT (Mac)',
'Italian (no dead keys)': 'IT (Nodeadkeys)',
'Italian (US)': 'IT (Us)',
'Italian (Windows)': 'IT (Winkeys)',
Japanese: 'JP',
'Japanese (Dvorak)': 'JP (Dvorak)',
'Japanese (Kana 86)': 'JP (Kana86)',
'Japanese (Kana)': 'JP (Kana)',
'Japanese (Macintosh)': 'JP (Mac)',
'Japanese (OADG 109A)': 'JP (OADG109A)',
Javanese: 'ID (Javanese)',
'Kabyle (AZERTY, with dead keys)': 'DZ (Azerty-Deadkeys)',
'Kabyle (QWERTY, UK, with dead keys)': 'DZ (Qwerty-Gb-Deadkeys)',
'Kabyle (QWERTY, US, with dead keys)': 'DZ (Qwerty-Us-Deadkeys)',
Kalmyk: 'RU (Xal)',
Kannada: 'IN (Kan)',
'Kannada (KaGaPa, phonetic)': 'IN (Kan-Kagapa)',
Kashubian: 'PL (Csb)',
Kazakh: 'KZ',
'Kazakh (extended)': 'KZ (Ext)',
'Kazakh (Latin)': 'KZ (Latin)',
'Kazakh (with Russian)': 'KZ (Kazrus)',
'Khmer (Cambodia)': 'KH',
Kikuyu: 'KE (Kik)',
Komi: 'RU (Kom)',
Korean: 'KR',
'Korean (101/104-key compatible)': 'KR (Kr104)',
'Kurdish (Iran, Arabic-Latin)': 'IR (Ku Ara)',
'Kurdish (Iran, F)': 'IR (Ku F)',
'Kurdish (Iran, Latin Alt-Q)': 'IR (Ku Alt)',
'Kurdish (Iran, Latin Q)': 'IR (Ku)',
'Kurdish (Iraq, Arabic-Latin)': 'IQ (Ku Ara)',
'Kurdish (Iraq, F)': 'IQ (Ku F)',
'Kurdish (Iraq, Latin Alt-Q)': 'IQ (Ku Alt)',
'Kurdish (Iraq, Latin Q)': 'IQ (Ku)',
'Kurdish (Syria, F)': 'SY (Ku F)',
'Kurdish (Syria, Latin Alt-Q)': 'SY (Ku Alt)',
'Kurdish (Syria, Latin Q)': 'SY (Ku)',
'Kurdish (Turkey, F)': 'TR (Ku F)',
'Kurdish (Turkey, Latin Alt-Q)': 'TR (Ku Alt)',
'Kurdish (Turkey, Latin Q)': 'TR (Ku)',
Kyrgyz: 'KG',
'Kyrgyz (phonetic)': 'KG (Phonetic)',
Lao: 'LA',
'Lao (STEA)': 'LA (Stea)',
Latvian: 'LV',
'Latvian (adapted)': 'LV (Adapted)',
'Latvian (apostrophe)': 'LV (Apostrophe)',
'Latvian (ergonomic, ŪGJRMV)': 'LV (Ergonomic)',
'Latvian (F)': 'LV (Fkey)',
'Latvian (modern)': 'LV (Modern)',
'Latvian (tilde)': 'LV (Tilde)',
Lithuanian: 'LT',
'Lithuanian (IBM LST 1205-92)': 'LT (Ibm)',
'Lithuanian (LEKP)': 'LT (Lekp)',
'Lithuanian (LEKPa)': 'LT (Lekpa)',
'Lithuanian (Ratise)': 'LT (Ratise)',
'Lithuanian (standard)': 'LT (Std)',
'Lithuanian (US)': 'LT (Us)',
'Lower Sorbian': 'DE (Dsb)',
'Lower Sorbian (QWERTZ)': 'DE (Dsb Qwertz)',
Macedonian: 'MK',
'Macedonian (no dead keys)': 'MK (Nodeadkeys)',
'Malay (Jawi, Arabic Keyboard)': 'MY',
'Malay (Jawi, phonetic)': 'MY (Phonetic)',
Malayalam: 'IN (Mal)',
'Malayalam (enhanced InScript, with rupee)': 'IN (Mal Enhanced)',
'Malayalam (Lalitha)': 'IN (Mal Lalitha)',
Maltese: 'MT',
'Maltese (UK, with AltGr overrides)': 'MT (Alt-Gb)',
'Maltese (US, with AltGr overrides)': 'MT (Alt-Us)',
'Maltese (US)': 'MT (Us)',
'Manipuri (Eeyek)': 'IN (Eeyek)',
Maori: 'MAO',
'Marathi (enhanced InScript)': 'IN (Marathi)',
'Marathi (KaGaPa, phonetic)': 'IN (Mar-Kagapa)',
Mari: 'RU (Chm)',
Mmuock: 'CM (Mmuock)',
Moldavian: 'MD',
'Moldavian (Gagauz)': 'MD (Gag)',
Mon: 'MM (Mnw)',
'Mon (A1)': 'MM (Mnw-A1)',
Mongolian: 'MN',
'Mongolian (Bichig)': 'CN (Mon Trad)',
'Mongolian (Galik)': 'CN (Mon Trad Galik)',
'Mongolian (Manchu Galik)': 'CN (Mon Manchu Galik)',
'Mongolian (Manchu)': 'CN (Mon Trad Manchu)',
'Mongolian (Todo Galik)': 'CN (Mon Todo Galik)',
'Mongolian (Todo)': 'CN (Mon Trad Todo)',
'Mongolian (Xibe)': 'CN (Mon Trad Xibe)',
Montenegrin: 'ME',
'Montenegrin (Cyrillic, with guillemets)': 'ME (Cyrillicalternatequotes)',
'Montenegrin (Cyrillic, ZE and ZHE swapped)': 'ME (Cyrillicyz)',
'Montenegrin (Cyrillic)': 'ME (Cyrillic)',
'Montenegrin (Latin, QWERTY)': 'ME (Latinyz)',
'Montenegrin (Latin, Unicode, QWERTY)': 'ME (Latinunicodeyz)',
'Montenegrin (Latin, Unicode)': 'ME (Latinunicode)',
'Montenegrin (Latin, with guillemets)': 'ME (Latinalternatequotes)',
"N'Ko (AZERTY)": 'GN',
Nepali: 'NP',
'Northern Saami (Finland)': 'FI (Smi)',
'Northern Saami (Norway, no dead keys)': 'NO (Smi Nodeadkeys)',
'Northern Saami (Norway)': 'NO (Smi)',
'Northern Saami (Sweden)': 'SE (Smi)',
Norwegian: 'NO',
'Norwegian (Colemak)': 'NO (Colemak)',
'Norwegian (Dvorak)': 'NO (Dvorak)',
'Norwegian (Macintosh, no dead keys)': 'NO (Mac Nodeadkeys)',
'Norwegian (Macintosh)': 'NO (Mac)',
'Norwegian (no dead keys)': 'NO (Nodeadkeys)',
'Norwegian (Windows)': 'NO (Winkeys)',
Occitan: 'FR (Oci)',
Ogham: 'IE (Ogam)',
'Ogham (IS434)': 'IE (Ogam Is434)',
'Ol Chiki': 'IN (Olck)',
'Old Turkic': 'TR (Otk)',
'Old Turkic (F)': 'TR (Otkf)',
Oriya: 'IN (Ori)',
'Oriya (Bolnagri)': 'IN (Ori-Bolnagri)',
'Oriya (Wx)': 'IN (Ori-Wx)',
'Ossetian (Georgia)': 'GE (Os)',
'Ossetian (legacy)': 'RU (Os Legacy)',
'Ossetian (Windows)': 'RU (Os Winkeys)',
'Ottoman (F)': 'TR (Otf)',
'Ottoman (Q)': 'TR (Ot)',
'Pannonian Rusyn': 'RS (Rue)',
Pashto: 'AF (Ps)',
'Pashto (Afghanistan, OLPC)': 'AF (Ps-Olpc)',
Persian: 'IR',
'Persian (with Persian keypad)': 'IR (Pes Keypad)',
Polish: 'PL',
'Polish (British keyboard)': 'GB (Pl)',
'Polish (Dvorak, with Polish quotes on key 1)': 'PL (Dvorak Altquotes)',
'Polish (Dvorak, with Polish quotes on quotemark key)': 'PL (Dvorak Quotes)',
'Polish (Dvorak)': 'PL (Dvorak)',
'Polish (legacy)': 'PL (Legacy)',
'Polish (programmer Dvorak)': 'PL (Dvp)',
'Polish (QWERTZ)': 'PL (Qwertz)',
Portuguese: 'PT',
'Portuguese (Brazil, Dvorak)': 'BR (Dvorak)',
'Portuguese (Brazil, IBM/Lenovo ThinkPad)': 'BR (Thinkpad)',
'Portuguese (Brazil, Nativo for US keyboards)': 'BR (Nativo-Us)',
'Portuguese (Brazil, Nativo)': 'BR (Nativo)',
'Portuguese (Brazil, no dead keys)': 'BR (Nodeadkeys)',
'Portuguese (Brazil)': 'BR',
'Portuguese (Macintosh, no dead keys)': 'PT (Mac Nodeadkeys)',
'Portuguese (Macintosh)': 'PT (Mac)',
'Portuguese (Nativo for US keyboards)': 'PT (Nativo-Us)',
'Portuguese (Nativo)': 'PT (Nativo)',
'Portuguese (no dead keys)': 'PT (Nodeadkeys)',
'Punjabi (Gurmukhi Jhelum)': 'IN (Jhelum)',
'Punjabi (Gurmukhi)': 'IN (Guru)',
Romanian: 'RO',
'Romanian (Germany, no dead keys)': 'DE (Ro Nodeadkeys)',
'Romanian (Germany)': 'DE (Ro)',
'Romanian (standard)': 'RO (Std)',
'Romanian (Windows)': 'RO (Winkeys)',
Russian: 'RU',
'Russian (Belarus)': 'BY (Ru)',
'Russian (Czech, phonetic)': 'CZ (Rus)',
'Russian (DOS)': 'RU (Dos)',
'Russian (engineering, EN)': 'RU (Ruchey En)',
'Russian (engineering, RU)': 'RU (Ruchey Ru)',
'Russian (Georgia)': 'GE (Ru)',
'Russian (Germany, phonetic)': 'DE (Ru)',
'Russian (Kazakhstan, with Kazakh)': 'KZ (Ruskaz)',
'Russian (legacy)': 'RU (Legacy)',
'Russian (Macintosh)': 'RU (Mac)',
'Russian (phonetic, AZERTY)': 'RU (Phonetic Azerty)',
'Russian (phonetic, Dvorak)': 'RU (Phonetic Dvorak)',
'Russian (phonetic, French)': 'RU (Phonetic Fr)',
'Russian (phonetic, Windows)': 'RU (Phonetic Winkeys)',
'Russian (phonetic, YAZHERTY)': 'RU (Phonetic YAZHERTY)',
'Russian (phonetic)': 'RU (Phonetic)',
'Russian (Poland, phonetic Dvorak)': 'PL (Ru Phonetic Dvorak)',
'Russian (Sweden, phonetic, no dead keys)': 'SE (Rus Nodeadkeys)',
'Russian (Sweden, phonetic)': 'SE (Rus)',
'Russian (typewriter, legacy)': 'RU (Typewriter-Legacy)',
'Russian (typewriter)': 'RU (Typewriter)',
'Russian (Ukraine, standard RSTU)': 'UA (Rstu Ru)',
'Russian (US, phonetic)': 'US (Rus)',
'Saisiyat (Taiwan)': 'TW (Saisiyat)',
Samogitian: 'LT (Sgs)',
'Sanskrit (KaGaPa, phonetic)': 'IN (San-Kagapa)',
'Scottish Gaelic': 'GB (Gla)',
Serbian: 'RS',
'Serbian (Cyrillic, with guillemets)': 'RS (Alternatequotes)',
'Serbian (Cyrillic, ZE and ZHE swapped)': 'RS (Yz)',
'Serbian (Latin, QWERTY)': 'RS (Latinyz)',
'Serbian (Latin, Unicode, QWERTY)': 'RS (Latinunicodeyz)',
'Serbian (Latin, Unicode)': 'RS (Latinunicode)',
'Serbian (Latin, with guillemets)': 'RS (Latinalternatequotes)',
'Serbian (Latin)': 'RS (Latin)',
'Serbian (Russia)': 'RU (Srp)',
'Serbo-Croatian (US)': 'US (Hbs)',
Shan: 'MM (Shn)',
'Shan (Zawgyi Tai)': 'MM (Zgt)',
Sicilian: 'IT (Scn)',
Silesian: 'PL (Szl)',
Sindhi: 'PK (Snd)',
'Sinhala (phonetic)': 'LK',
'Sinhala (US)': 'LK (Us)',
Slovak: 'SK',
'Slovak (extended backslash)': 'SK (Bksl)',
'Slovak (QWERTY, extended backslash)': 'SK (Qwerty Bksl)',
'Slovak (QWERTY)': 'SK (Qwerty)',
Slovenian: 'SI',
'Slovenian (US)': 'SI (Us)',
'Slovenian (with guillemets)': 'SI (Alternatequotes)',
Spanish: 'ES',
'Spanish (dead tilde)': 'ES (Deadtilde)',
'Spanish (Dvorak)': 'ES (Dvorak)',
'Spanish (Latin American, Colemak)': 'LATAM (Colemak)',
'Spanish (Latin American, dead tilde)': 'LATAM (Deadtilde)',
'Spanish (Latin American, Dvorak)': 'LATAM (Dvorak)',
'Spanish (Latin American, no dead keys)': 'LATAM (Nodeadkeys)',
'Spanish (Latin American)': 'LATAM',
'Spanish (Macintosh)': 'ES (Mac)',
'Spanish (no dead keys)': 'ES (Nodeadkeys)',
'Spanish (Windows)': 'ES (Winkeys)',
'Swahili (Kenya)': 'KE',
'Swahili (Tanzania)': 'TZ',
Swedish: 'SE',
'Swedish (Dvorak, intl.)': 'SE (Us Dvorak)',
'Swedish (Dvorak)': 'SE (Dvorak)',
'Swedish (Macintosh)': 'SE (Mac)',
'Swedish (no dead keys)': 'SE (Nodeadkeys)',
'Swedish (Svdvorak)': 'SE (Svdvorak)',
'Swedish (US)': 'SE (Us)',
'Swedish Sign Language': 'SE (Swl)',
Syriac: 'SY (Syc)',
'Syriac (phonetic)': 'SY (Syc Phonetic)',
Taiwanese: 'TW',
'Taiwanese (indigenous)': 'TW (Indigenous)',
Tajik: 'TJ',
'Tajik (legacy)': 'TJ (Legacy)',
'Tamil (InScript, with Arabic numerals)': 'IN (Tam)',
'Tamil (InScript, with Tamil numerals)': 'IN (Tam Tamilnumbers)',
"Tamil (Sri Lanka, TamilNet '99, TAB encoding)": 'LK (Tam TAB)',
"Tamil (Sri Lanka, TamilNet '99)": 'LK (Tam Unicode)',
"Tamil (TamilNet '99 with Tamil numerals)": 'IN (Tamilnet Tamilnumbers)',
"Tamil (TamilNet '99, TAB encoding)": 'IN (Tamilnet TAB)',
"Tamil (TamilNet '99, TSCII encoding)": 'IN (Tamilnet TSCII)',
"Tamil (TamilNet '99)": 'IN (Tamilnet)',
Tarifit: 'MA (Rif)',
Tatar: 'RU (Tt)',
Telugu: 'IN (Tel)',
'Telugu (KaGaPa, phonetic)': 'IN (Tel-Kagapa)',
'Telugu (Sarala)': 'IN (Tel-Sarala)',
Thai: 'TH',
'Thai (Pattachote)': 'TH (Pat)',
'Thai (TIS-820.2538)': 'TH (Tis)',
Tibetan: 'CN (Tib)',
'Tibetan (with ASCII numerals)': 'CN (Tib Asciinum)',
Tswana: 'BW',
Turkish: 'TR',
'Turkish (Alt-Q)': 'TR (Alt)',
'Turkish (E)': 'TR (E)',
'Turkish (F)': 'TR (F)',
'Turkish (Germany)': 'DE (Tr)',
'Turkish (intl., with dead keys)': 'TR (Intl)',
Turkmen: 'TM',
'Turkmen (Alt-Q)': 'TM (Alt)',
Udmurt: 'RU (Udm)',
Ukrainian: 'UA',
'Ukrainian (homophonic)': 'UA (Homophonic)',
'Ukrainian (legacy)': 'UA (Legacy)',
'Ukrainian (macOS)': 'UA (MacOS)',
'Ukrainian (phonetic)': 'UA (Phonetic)',
'Ukrainian (standard RSTU)': 'UA (Rstu)',
'Ukrainian (typewriter)': 'UA (Typewriter)',
'Ukrainian (Windows)': 'UA (Winkeys)',
'Urdu (alt. phonetic)': 'IN (Urd-Phonetic3)',
'Urdu (Pakistan, CRULP)': 'PK (Urd-Crulp)',
'Urdu (Pakistan, NLA)': 'PK (Urd-Nla)',
'Urdu (Pakistan)': 'PK',
'Urdu (phonetic)': 'IN (Urd-Phonetic)',
'Urdu (Windows)': 'IN (Urd-Winkeys)',
Uyghur: 'CN (Ug)',
Uzbek: 'UZ',
'Uzbek (Afghanistan, OLPC)': 'AF (Uz-Olpc)',
'Uzbek (Afghanistan)': 'AF (Uz)',
'Uzbek (Latin)': 'UZ (Latin)',
Vietnamese: 'VN',
'Vietnamese (France)': 'VN (Fr)',
'Vietnamese (US)': 'VN (Us)',
Wolof: 'SN',
Yakut: 'RU (Sah)',
Yoruba: 'NG (Yoruba)',
'Unknown Layout': 'Unknown',
} as const;
-84
View File
@@ -1,84 +0,0 @@
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;
const undefinedVar = Variable(undefined);
export const module = ({
icon,
textIcon,
label,
tooltipText,
boxClass,
props = {},
showLabelBinding = undefinedVar.bind('value'),
showLabel,
labelHook,
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,
}) as unknown as Gtk.Widget;
} else if (textIcon !== undefined) {
iconWidget = Widget.Label({
class_name: `txt-icon bar-button-icon module-icon ${boxClass}`,
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' : ''}`;
},
),
tooltip_text: tooltipText,
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;
}) as Bind,
setup: hook,
}),
tooltip_text: tooltipText,
isVisible: true,
boxClass,
props,
};
};
@@ -1,110 +0,0 @@
import GLib from 'gi://GLib';
import { Variable as VariableType } from 'types/variable';
import { NetworkResourceData } from 'lib/types/customModules/network';
import { GET_DEFAULT_NETSTAT_DATA } from 'lib/types/defaults/netstat';
import { RateUnit } from 'lib/types/bar';
let previousNetUsage = { rx: 0, tx: 0, time: 0 };
const formatRate = (rate: number, type: string, round: boolean): string => {
const fixed = round ? 0 : 2;
switch (true) {
case type === 'KiB':
return `${(rate / 1e3).toFixed(fixed)} KiB/s`;
case type === 'MiB':
return `${(rate / 1e6).toFixed(fixed)} MiB/s`;
case type === 'GiB':
return `${(rate / 1e9).toFixed(fixed)} GiB/s`;
case rate >= 1e9:
return `${(rate / 1e9).toFixed(fixed)} GiB/s`;
case rate >= 1e6:
return `${(rate / 1e6).toFixed(fixed)} MiB/s`;
case rate >= 1e3:
return `${(rate / 1e3).toFixed(fixed)} KiB/s`;
default:
return `${rate.toFixed(fixed)} bytes/s`;
}
};
interface NetworkUsage {
name: string;
rx: number;
tx: number;
}
const parseInterfaceData = (line: string): NetworkUsage | null => {
const trimmedLine = line.trim();
if (!trimmedLine || trimmedLine.startsWith('Inter-') || trimmedLine.startsWith('face')) {
return null;
}
const [iface, rx, , , , , , , , tx] = trimmedLine.split(/\s+/);
const rxValue = parseInt(rx, 10);
const txValue = parseInt(tx, 10);
const cleanedIface = iface.replace(':', '');
return { name: cleanedIface, rx: rxValue, tx: txValue };
};
const isValidInterface = (iface: NetworkUsage | null, interfaceName: string): boolean => {
if (!iface) return false;
if (interfaceName) return iface.name === interfaceName;
return iface.name !== 'lo' && iface.rx > 0 && iface.tx > 0;
};
const getNetworkUsage = (interfaceName: string = ''): NetworkUsage => {
const [success, data] = GLib.file_get_contents('/proc/net/dev');
if (!success) {
console.error('Failed to read /proc/net/dev');
return { name: '', rx: 0, tx: 0 };
}
const lines = new TextDecoder('utf-8').decode(data).split('\n');
for (const line of lines) {
const iface = parseInterfaceData(line);
if (isValidInterface(iface, interfaceName)) {
return iface!;
}
}
return { name: '', rx: 0, tx: 0 };
};
export const computeNetwork = (
round: VariableType<boolean>,
interfaceNameVar: VariableType<string>,
dataType: VariableType<RateUnit>,
): NetworkResourceData => {
const rateUnit = dataType.value;
const interfaceName = interfaceNameVar ? interfaceNameVar.value : '';
const DEFAULT_NETSTAT_DATA = GET_DEFAULT_NETSTAT_DATA(rateUnit);
try {
const { rx, tx, name } = getNetworkUsage(interfaceName);
const currentTime = Date.now();
if (!name) {
return DEFAULT_NETSTAT_DATA;
}
if (previousNetUsage.time === 0) {
previousNetUsage = { rx, tx, time: currentTime };
return DEFAULT_NETSTAT_DATA;
}
const timeDiff = Math.max((currentTime - previousNetUsage.time) / 1000, 1);
const rxRate = (rx - previousNetUsage.rx) / timeDiff;
const txRate = (tx - previousNetUsage.tx) / timeDiff;
previousNetUsage = { rx, tx, time: currentTime };
return {
in: formatRate(rxRate, rateUnit, round.value),
out: formatRate(txRate, rateUnit, round.value),
};
} catch (error) {
console.error('Error calculating network usage:', error);
return DEFAULT_NETSTAT_DATA;
}
};
@@ -1,105 +0,0 @@
import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import { computeNetwork } from './computeNetwork';
import { BarBoxChild, NetstatLabelType } from 'lib/types/bar';
import Button from 'types/widgets/button';
import { NetworkResourceData } from 'lib/types/customModules/network';
import { NETWORK_LABEL_TYPES } from 'lib/types/defaults/bar';
import { GET_DEFAULT_NETSTAT_DATA } from 'lib/types/defaults/netstat';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
const {
label,
labelType,
networkInterface,
rateUnit,
icon,
round,
leftClick,
rightClick,
middleClick,
pollingInterval,
} = options.bar.customModules.netstat;
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
networkUsage,
// Variables that should trigger the polling function to update when they change
[rateUnit.bind('value'), networkInterface.bind('value'), round.bind('value')],
// Interval at which to poll
pollingInterval.bind('value'),
// Function to execute to get the network data
computeNetwork,
// Optional parameters to pass to the function
// round is a boolean that determines whether to round the values
round,
// Optional parameters to pass to the function
// networkInterface is the interface name to filter the data
networkInterface,
// Optional parameters to pass to the function
// rateUnit is the unit to display the data in
// e.g. KiB, MiB, GiB, etc.
rateUnit,
);
export const Netstat = (): BarBoxChild => {
const renderNetworkLabel = (lblType: NetstatLabelType, network: NetworkResourceData): string => {
switch (lblType) {
case 'in':
return `↓ ${network.in}`;
case 'out':
return `↑ ${network.out}`;
default:
return `↓ ${network.in} ↑ ${network.out}`;
}
};
const netstatModule = module({
textIcon: icon.bind('value'),
label: Utils.merge(
[networkUsage.bind('value'), labelType.bind('value')],
(network: NetworkResourceData, lblTyp: NetstatLabelType) => renderNetworkLabel(lblTyp, network),
),
tooltipText: labelType.bind('value').as((lblTyp) => {
return lblTyp === 'full' ? 'Ingress / Egress' : lblTyp === 'in' ? 'Ingress' : 'Egress';
}),
boxClass: 'netstat',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
fn: () => {
labelType.value = NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.value) + 1) % NETWORK_LABEL_TYPES.length
] as NetstatLabelType;
},
},
onScrollDown: {
fn: () => {
labelType.value = NETWORK_LABEL_TYPES[
(NETWORK_LABEL_TYPES.indexOf(labelType.value) - 1 + NETWORK_LABEL_TYPES.length) %
NETWORK_LABEL_TYPES.length
] as NetstatLabelType;
},
},
});
},
},
});
return netstatModule;
};
@@ -1,40 +0,0 @@
import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
const { icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.power;
export const Power = (): BarBoxChild => {
const powerModule = module({
tooltipText: 'Power Menu',
textIcon: icon.bind('value'),
boxClass: 'powermodule',
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return powerModule;
};
@@ -1,40 +0,0 @@
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>): GenericResourceData => {
try {
const [success, meminfoBytes] = GLib.file_get_contents('/proc/meminfo');
if (!success || !meminfoBytes) {
throw new Error('Failed to read /proc/meminfo or file content is null.');
}
const meminfo = new TextDecoder('utf-8').decode(meminfoBytes);
const totalMatch = meminfo.match(/MemTotal:\s+(\d+)/);
const availableMatch = meminfo.match(/MemAvailable:\s+(\d+)/);
if (!totalMatch || !availableMatch) {
throw new Error('Failed to parse /proc/meminfo for memory values.');
}
const totalRamInBytes = parseInt(totalMatch[1], 10) * 1024;
const availableRamInBytes = parseInt(availableMatch[1], 10) * 1024;
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, free: 0 };
}
};
-79
View File
@@ -1,79 +0,0 @@
import options from 'options';
// Module initializer
import { module } from '../module';
// Types
import { GenericResourceData } from 'lib/types/customModules/generic';
import Button from 'types/widgets/button';
// Helper Methods
import { calculateRamUsage } from './computeRam';
// Utility Methods
import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils';
import { BarBoxChild, ResourceLabelType } from 'lib/types/bar';
// Global Constants
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
// All the user configurable options for the ram module that are needed
const { label, labelType, round, leftClick, rightClick, middleClick, pollingInterval, icon } =
options.bar.customModules.ram;
const defaultRamData: GenericResourceData = { total: 0, used: 0, percentage: 0, free: 0 };
const ramUsage = Variable<GenericResourceData>(defaultRamData);
pollVariable(ramUsage, [round.bind('value')], pollingInterval.bind('value'), calculateRamUsage, round);
export const Ram = (): BarBoxChild => {
const ramModule = module({
textIcon: icon.bind('value'),
label: Utils.merge(
[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) => {
return formatTooltip('RAM', lblTyp);
}),
boxClass: 'ram',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
fn: () => {
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;
},
},
});
},
},
});
return ramModule;
};
@@ -1,28 +0,0 @@
// @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';
export const computeStorage = (round: VariableType<boolean>): GenericResourceData => {
try {
const currentFsUsage = new GTop.glibtop_fsusage();
GTop.glibtop_get_fsusage(currentFsUsage, '/');
const total = currentFsUsage.blocks * currentFsUsage.block_size;
const available = currentFsUsage.bavail * currentFsUsage.block_size;
const used = total - available;
return {
total,
used,
free: available,
percentage: divide([total, used], round.value),
};
} catch (error) {
console.error('Error calculating RAM usage:', error);
return { total: 0, used: 0, percentage: 0, free: 0 };
}
};
@@ -1,68 +0,0 @@
import options from 'options';
import { module } from '../module';
import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils';
import { computeStorage } from './computeStorage';
import { BarBoxChild, ResourceLabelType } from 'lib/types/bar';
import { GenericResourceData } from 'lib/types/customModules/generic';
import Button from 'types/widgets/button';
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
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<GenericResourceData>(defaultStorageData);
pollVariable(storageUsage, [round.bind('value')], pollingInterval.bind('value'), computeStorage, round);
export const Storage = (): BarBoxChild => {
const storageModule = module({
textIcon: icon.bind('value'),
label: Utils.merge(
[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) => {
return formatTooltip('Storage', lblTyp);
}),
boxClass: 'storage',
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
fn: () => {
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;
},
},
});
},
},
});
return storageModule;
};
@@ -1,20 +0,0 @@
import { Variable } from 'types/variable';
const hyprland = await Service.import('hyprland');
export const isSubmapEnabled = (submap: string, enabled: string, disabled: string): string => {
return submap !== 'default' ? enabled : disabled;
};
export const getInitialSubmap = (submapStatus: Variable<string>): void => {
let submap = hyprland.message('submap');
const newLineCarriage = /\n/g;
submap = submap.replace(newLineCarriage, '');
if (submap === 'unknown request') {
submap = 'default';
}
submapStatus.value = submap;
};
-101
View File
@@ -1,101 +0,0 @@
const hyprland = await Service.import('hyprland');
import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Variable as VariableType } from 'types/variable';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
import { capitalizeFirstLetter } from 'lib/utils';
import { getInitialSubmap, isSubmapEnabled } from './helpers';
const {
label,
showSubmapName,
enabledIcon,
disabledIcon,
enabledText,
disabledText,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
} = options.bar.customModules.submap;
const submapStatus: VariableType<string> = Variable('default');
hyprland.connect('submap', (_, currentSubmap) => {
if (currentSubmap.length === 0) {
submapStatus.value = 'default';
} else {
submapStatus.value = currentSubmap;
}
});
getInitialSubmap(submapStatus);
export const Submap = (): BarBoxChild => {
const submapModule = module({
textIcon: Utils.merge(
[submapStatus.bind('value'), enabledIcon.bind('value'), disabledIcon.bind('value')],
(status, enabled, disabled) => {
return isSubmapEnabled(status, enabled, disabled);
},
),
tooltipText: Utils.merge(
[
submapStatus.bind('value'),
enabledText.bind('value'),
disabledText.bind('value'),
showSubmapName.bind('value'),
],
(status, enabled, disabled, showSmName) => {
if (showSmName) {
return capitalizeFirstLetter(status);
}
return isSubmapEnabled(status, enabled, disabled);
},
),
boxClass: 'submap',
label: Utils.merge(
[
submapStatus.bind('value'),
enabledText.bind('value'),
disabledText.bind('value'),
showSubmapName.bind('value'),
],
(status, enabled, disabled, showSmName) => {
if (showSmName) {
return capitalizeFirstLetter(status);
}
return isSubmapEnabled(status, enabled, disabled);
},
),
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return submapModule;
};
-171
View File
@@ -1,171 +0,0 @@
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 CustomModuleTheme = (): Scrollable<GtkWidget, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page customModules paged-container',
child: Widget.Box({
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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.ram.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.cpu.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.storage.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.netstat.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.kbLayout.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.updates.border, title: 'Border', type: 'color' }),
Header('Submap'),
Option({ opt: options.theme.bar.buttons.modules.submap.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.modules.submap.icon, title: 'Icon', type: 'color' }),
Option({
opt: options.theme.bar.buttons.modules.submap.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.modules.submap.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.submap.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.weather.border, title: 'Border', 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.icon_background,
title: 'Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.modules.power.border, title: 'Border', type: 'color' }),
],
}),
});
};
@@ -1,70 +0,0 @@
import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Variable as VariableType } from 'types/variable';
import { pollVariableBash } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
const {
updateCommand,
label,
padZero,
pollingInterval,
icon,
leftClick,
rightClick,
middleClick,
scrollUp,
scrollDown,
} = options.bar.customModules.updates;
const pendingUpdates: VariableType<string> = Variable(' 0');
const processUpdateCount = (updateCount: string): string => {
if (!padZero.value) return updateCount;
return `${updateCount.padStart(2, '0')}`;
};
pollVariableBash(
pendingUpdates,
[padZero.bind('value')],
pollingInterval.bind('value'),
updateCommand.value,
processUpdateCount,
);
export const Updates = (): BarBoxChild => {
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'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return updatesModule;
};
-197
View File
@@ -1,197 +0,0 @@
import { ResourceLabelType } from 'lib/types/bar';
import { GenericResourceData, Postfix } from 'lib/types/customModules/generic';
import { InputHandlerEvents, RunAsyncCommand } from 'lib/types/customModules/utils';
import { ThrottleFn, ThrottleFnCallback } from 'lib/types/utils';
import { Attribute, Child, EventArgs } from 'lib/types/widget';
import { Binding } from 'lib/utils';
import { openMenu } from 'modules/bar/utils';
import options from 'options';
import Gdk from 'types/@girs/gdk-3.0/gdk-3.0';
import { Variable as VariableType } from 'types/variable';
import Button from 'types/widgets/button';
const { scrollSpeed } = options.bar.customModules;
export const runAsyncCommand: RunAsyncCommand = (cmd, events, fn): void => {
if (cmd.startsWith('menu:')) {
const menuName = cmd.split(':')[1].trim().toLowerCase();
openMenu(events.clicked, events.event, `${menuName}menu`);
return;
}
Utils.execAsync(`bash -c "${cmd}"`)
.then((output) => {
if (fn !== undefined) {
fn(output);
}
})
.catch((err) => console.error(`Error running command "${cmd}": ${err})`));
};
export function throttle<T extends ThrottleFn>(func: T, limit: number): T {
let inThrottle: boolean;
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
} as T;
}
export const throttledScrollHandler = (interval: number): ThrottleFn =>
throttle((cmd: string, events: EventArgs, fn: ThrottleFnCallback) => {
runAsyncCommand(cmd, events, fn);
}, 200 / interval);
const dummyVar = Variable('');
export const inputHandler = (
self: Button<Child, Attribute>,
{ onPrimaryClick, onSecondaryClick, onMiddleClick, onScrollUp, onScrollDown }: InputHandlerEvents,
): void => {
const sanitizeInput = (input: VariableType<string>): string => {
if (input === undefined) {
return '';
}
return input.value;
};
const updateHandlers = (): void => {
const interval = scrollSpeed.value;
const throttledHandler = throttledScrollHandler(interval);
self.on_primary_click = (clicked: Button<Child, Attribute>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onPrimaryClick?.cmd || dummyVar), { clicked, event }, onPrimaryClick.fn);
self.on_secondary_click = (clicked: Button<Child, Attribute>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onSecondaryClick?.cmd || dummyVar), { clicked, event }, onSecondaryClick.fn);
self.on_middle_click = (clicked: Button<Child, Attribute>, event: Gdk.Event): void =>
runAsyncCommand(sanitizeInput(onMiddleClick?.cmd || dummyVar), { clicked, event }, onMiddleClick.fn);
self.on_scroll_up = (clicked: Button<Child, Attribute>, event: Gdk.Event): void =>
throttledHandler(sanitizeInput(onScrollUp?.cmd || dummyVar), { clicked, event }, onScrollUp.fn);
self.on_scroll_down = (clicked: Button<Child, Attribute>, event: Gdk.Event): void =>
throttledHandler(sanitizeInput(onScrollDown?.cmd || dummyVar), { clicked, event }, onScrollDown.fn);
};
// Initial setup of event handlers
updateHandlers();
const sanitizeVariable = (someVar: VariableType<string> | undefined): Binding<string> => {
if (someVar === undefined || typeof someVar.bind !== 'function') {
return dummyVar.bind('value');
}
return someVar.bind('value');
};
// Re-run the update whenever scrollSpeed changes
Utils.merge(
[
scrollSpeed.bind('value'),
sanitizeVariable(onPrimaryClick),
sanitizeVariable(onSecondaryClick),
sanitizeVariable(onMiddleClick),
sanitizeVariable(onScrollUp),
sanitizeVariable(onScrollDown),
],
updateHandlers,
);
};
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): number => {
const sizeInGiB = sizeInBytes / 1024 ** 1;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(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): number => {
const sizeInGiB = sizeInBytes / 1024 ** 3;
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
};
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): 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);
if (sizeInBytes >= 1024 ** 2) return formatSizeInMiB(sizeInBytes, round);
if (sizeInBytes >= 1024 ** 1) return formatSizeInKiB(sizeInBytes, round);
return sizeInBytes;
};
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): string => {
const { used, total, percentage, free } = rmUsg;
const formatFunctions = {
TiB: formatSizeInTiB,
GiB: formatSizeInGiB,
MiB: formatSizeInMiB,
KiB: formatSizeInKiB,
B: (size: number): number => size,
};
// Get them datas in proper GiB, MiB, KiB, TiB, or bytes
const totalSizeFormatted = autoFormatSize(total, round);
// get the postfix: one of [TiB, GiB, MiB, KiB, B]
const postfix = getPostfix(total);
// Determine which format function to use
const formatUsed = formatFunctions[postfix] || formatFunctions['B'];
const usedSizeFormatted = formatUsed(used, round);
if (lblType === 'used/total') {
return `${usedSizeFormatted}/${totalSizeFormatted} ${postfix}`;
}
if (lblType === 'used') {
return `${autoFormatSize(used, round)} ${getPostfix(used)}`;
}
if (lblType === 'free') {
return `${autoFormatSize(free, round)} ${getPostfix(free)}`;
}
return `${percentage}%`;
};
export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType): string => {
switch (lblTyp) {
case 'used':
return `Used ${dataType}`;
case 'free':
return `Free ${dataType}`;
case 'used/total':
return `Used/Total ${dataType}`;
case 'percentage':
return `Percentage ${dataType} Usage`;
default:
return '';
}
};
@@ -1,52 +0,0 @@
import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import Button from 'types/widgets/button';
import { getWeatherStatusTextIcon, globalWeatherVar } from 'globals/weather';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
const { label, unit, leftClick, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.weather;
export const Weather = (): BarBoxChild => {
const weatherModule = module({
textIcon: Utils.merge([globalWeatherVar.bind('value')], (wthr) => {
const weatherStatusIcon = getWeatherStatusTextIcon(wthr);
return weatherStatusIcon;
}),
tooltipText: globalWeatherVar.bind('value').as((v) => `Weather Status: ${v.current.condition.text}`),
boxClass: 'weather-custom',
label: Utils.merge([globalWeatherVar.bind('value'), unit.bind('value')], (wthr, unt) => {
if (unt === 'imperial') {
return `${Math.ceil(wthr.current.temp_f)}° F`;
} else {
return `${Math.ceil(wthr.current.temp_c)}° C`;
}
}),
showLabelBinding: label.bind('value'),
props: {
setup: (self: Button<Child, Attribute>) => {
inputHandler(self, {
onPrimaryClick: {
cmd: leftClick,
},
onSecondaryClick: {
cmd: rightClick,
},
onMiddleClick: {
cmd: middleClick,
},
onScrollUp: {
cmd: scrollUp,
},
onScrollDown: {
cmd: scrollDown,
},
});
},
},
});
return weatherModule;
};
-38
View File
@@ -1,38 +0,0 @@
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 {
Service.register(this, {}, {});
}
constructor() {
super();
this.recursiveDirectoryMonitor(`${App.configDir}/scss`);
}
recursiveDirectoryMonitor(directoryPath: string): void {
monitorFile(directoryPath, (_, eventType) => {
if (eventType === Gio.FileMonitorEvent.CHANGES_DONE_HINT) {
this.emit('changed');
}
});
const directory = Gio.File.new_for_path(directoryPath);
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();
if (fileInfo.get_file_type() === Gio.FileType.DIRECTORY) {
this.recursiveDirectoryMonitor(childPath);
}
}
}
}
const service = new DirectoryMonitorService();
export default service;
-78
View File
@@ -1,78 +0,0 @@
{
"nodes": {
"ags": {
"inputs": {
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1727303518,
"narHash": "sha256-lO5JMynTxNZxg/+E4/Y9reeLGIz/M8cEue+Re4cP6qo=",
"owner": "Aylur",
"repo": "ags",
"rev": "cd543afec4d9ed708990e5ba52184716809bf248",
"type": "github"
},
"original": {
"owner": "Aylur",
"repo": "ags",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1725634671,
"narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1727634051,
"narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"ags": "ags",
"nixpkgs": "nixpkgs_2"
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-78
View File
@@ -1,78 +0,0 @@
{
description = "A Bar/Panel for Hyprland with extensive customizability.";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
ags.url = "github:Aylur/ags";
};
outputs = inputs: let
systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-darwin"
"aarch64-linux"
];
forEachSystem = inputs.nixpkgs.lib.genAttrs systems;
pkgsFor = forEachSystem (
system:
import inputs.nixpkgs {
inherit system;
config.allowUnfree = true;
}
);
devShellFor = system:
inputs.nixpkgs.lib.genAttrs ["default"] (
_:
inputs.nixpkgs.legacyPackages.${system}.mkShell {
buildInputs = [
pkgsFor.${system}.esbuild
pkgsFor.${system}.fish
pkgsFor.${system}.typescript
pkgsFor.${system}.bun
pkgsFor.${system}.libnotify
pkgsFor.${system}.dart-sass
pkgsFor.${system}.fd
pkgsFor.${system}.btop
pkgsFor.${system}.bluez
pkgsFor.${system}.libgtop
pkgsFor.${system}.gobject-introspection
pkgsFor.${system}.glib
pkgsFor.${system}.bluez-tools
pkgsFor.${system}.grimblast
pkgsFor.${system}.gpu-screen-recorder
pkgsFor.${system}.brightnessctl
pkgsFor.${system}.gnome-bluetooth
pkgsFor.${system}.python3
pkgsFor.${system}.matugen
inputs.ags.packages.${system}.agsWithTypes
];
nativeBuildInputs = with pkgsFor.${system}; [
nixfmt-rfc-style
nil
];
shellHook = ''
export GDK_BACKEND=wayland
export GI_TYPELIB_PATH=${pkgsFor.${system}.libgtop}/lib/girepository-1.0:${pkgsFor.${system}.glib}/lib/girepository-1.0:$GI_TYPELIB_PATH
'';
}
);
in {
devShells = forEachSystem devShellFor;
overlay = final: prev: {
hyprpanel =
if final ? callPackage
then (final.callPackage ./nix {inherit inputs;}).desktop.script
else inputs.self.packages.${prev.stdenv.system}.default;
};
packages = forEachSystem (
system: let
pkgs = pkgsFor.${system};
in {
default = (pkgs.callPackage ./nix {inherit inputs;}).desktop.script;
}
);
};
}
-14
View File
@@ -1,14 +0,0 @@
// globals.d.ts
/* eslint-disable no-var */
import { Options, Variable as VariableType } from 'types/variable';
declare global {
var useTheme: (filePath: string) => void;
var isWindowVisible: (windowName: string) => boolean;
var globalWeatherVar: VariableType<Weather>;
var options: Options;
var removingNotifications: VariableType<boolean>;
}
export {};
-6
View File
@@ -1,6 +0,0 @@
import { Variable as VariableType } from 'types/variable';
type GlobalEventBoxes = {
[key: string]: unknown;
};
export const globalEventBoxes: VariableType<GlobalEventBoxes> = Variable({});
-15
View File
@@ -1,15 +0,0 @@
export const WIFI_STATUS_MAP = {
unknown: 'Status Unknown',
unmanaged: 'Unmanaged',
unavailable: 'Unavailable',
disconnected: 'Disconnected',
prepare: 'Preparing Connecting',
config: 'Connecting',
need_auth: 'Needs Authentication',
ip_config: 'Requesting IP',
ip_check: 'Checking Access',
secondaries: 'Waiting on Secondaries',
activated: 'Connected',
deactivating: 'Disconnecting',
failed: 'Connection Failed',
} as const;
-37
View File
@@ -1,37 +0,0 @@
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): string => {
let icon: string = icons.fallback.notification;
if (Utils.lookUpIcon(app_name) || Utils.lookUpIcon(app_name.toLowerCase() || '')) {
icon = Utils.lookUpIcon(app_name)
? app_name
: Utils.lookUpIcon(app_name.toLowerCase())
? app_name.toLowerCase()
: '';
}
if (Utils.lookUpIcon(app_icon) && icon === '') {
icon = app_icon;
}
if (Utils.lookUpIcon(app_entry || '') && icon === '') {
icon = app_entry || '';
}
return icon;
};
export const closeNotifications = async (notifications: Notification[], delay: number): Promise<void> => {
removingNotifications.value = true;
for (const notif of notifications) {
notif.close();
await new Promise((resolve) => setTimeout(resolve, delay));
}
removingNotifications.value = false;
};
globalThis['removingNotifications'] = removingNotifications;
-43
View File
@@ -1,43 +0,0 @@
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 => {
const importedConfig = loadJsonFile(filePath);
if (!importedConfig) {
return;
}
Notify({
summary: `Importing Theme`,
body: `Importing: ${filePath}`,
iconName: icons.ui.info,
timeout: 7000,
});
const tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`);
const optionsConfigFile = Gio.File.new_for_path(OPTIONS);
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.');
return;
}
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 };
optionsConfig = { ...optionsConfig, ...filteredConfig };
saveConfigToFile(tmpConfig, `${TMP}/config.json`);
saveConfigToFile(optionsConfig, OPTIONS);
bash('pkill ags && ags');
};
-8
View File
@@ -1,8 +0,0 @@
globalThis.isWindowVisible = (windowName: string): boolean => {
const appWindow = App.getWindow(windowName);
if (appWindow === undefined) {
return false;
}
return appWindow.visible;
};
-29
View File
@@ -1,29 +0,0 @@
import { Opt } from 'lib/option';
import { HexColor, MatugenTheme, 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 isOptString = (value: unknown): value is Opt<string> => {
return value instanceof Opt && typeof value.value === 'string';
};
export const isOptNumber = (value: unknown): value is Opt<number> => {
return value instanceof Opt && typeof value.value === 'number';
};
export const isOptBoolean = (value: unknown): value is Opt<boolean> => {
return value instanceof Opt && typeof value.value === 'boolean';
};
export const isOptMatugenTheme = (value: unknown): value is Opt<MatugenTheme> => {
return value instanceof Opt && typeof value.value === 'object' && 'specificProperty' in value.value; // Replace 'specificProperty' with an actual property of MatugenTheme
};
export const isRecursiveOptionsObject = (value: unknown): value is RecursiveOptionsObject => {
return typeof value === 'object' && value !== null && !(value instanceof Opt);
};
export const isHexColor = (val: unknown): val is HexColor => {
return typeof val === 'string' && /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(val);
};
-122
View File
@@ -1,122 +0,0 @@
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;
export const globalWeatherVar = Variable<Weather>(DEFAULT_WEATHER);
let weatherIntervalInstance: null | number = null;
const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: string): void => {
if (weatherIntervalInstance !== null) {
GLib.source_remove(weatherIntervalInstance);
}
const formattedLocation = loc.replace(' ', '%20');
weatherIntervalInstance = Utils.interval(weatherInterval, () => {
Utils.execAsync(
`curl "https://api.weatherapi.com/v1/forecast.json?key=${weatherKey}&q=${formattedLocation}&days=1&aqi=no&alerts=no"`,
)
.then((res) => {
try {
if (typeof res !== 'string') {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
const parsedWeather = JSON.parse(res);
if (Object.keys(parsedWeather).includes('error')) {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
return (globalWeatherVar.value = parsedWeather);
} catch (error) {
globalWeatherVar.value = DEFAULT_WEATHER;
console.warn(`Failed to parse weather data: ${error}`);
}
})
.catch((err) => {
console.error(`Failed to fetch weather: ${err}`);
globalWeatherVar.value = DEFAULT_WEATHER;
});
});
};
Utils.merge([key.bind('value'), interval.bind('value'), location.bind('value')], (weatherKey, weatherInterval, loc) => {
if (!weatherKey) {
return (globalWeatherVar.value = DEFAULT_WEATHER);
}
weatherIntervalFn(weatherInterval, loc, weatherKey);
});
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): Record<string, string> => {
const icons = {
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',
} 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;
const icon = icons[threshold || 50];
const color = colors[threshold || 50];
return {
icon,
color,
};
};
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): string => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`;
export const isValidWeatherIconTitle = (title: string): title is WeatherIconTitle => {
return title in weatherIcons;
};
export const getWeatherStatusTextIcon = (wthr: Weather): WeatherIcon => {
let iconQuery = wthr.current.condition.text.trim().toLowerCase().replaceAll(' ', '_');
if (!wthr.current.is_day && iconQuery === 'partly_cloudy') {
iconQuery = 'partly_cloudy_night';
}
if (isValidWeatherIconTitle(iconQuery)) {
return weatherIcons[iconQuery];
} else {
console.warn(`Unknown weather icon title: ${iconQuery}`);
return weatherIcons['warning'];
}
};
globalThis['globalWeatherVar'] = globalWeatherVar;
-10
View File
@@ -1,10 +0,0 @@
export const WINDOW_LAYOUTS: string[] = [
'center',
'top',
'top-right',
'top-center',
'top-left',
'bottom-left',
'bottom-center',
'bottom-right',
];
-28
View File
@@ -1,28 +0,0 @@
#!/bin/bash
SOURCE_DIR="./assets/fonts"
DEST_DIR="$HOME/.local/share/fonts"
DEST_PATH="$DEST_DIR/NFP"
if [ ! -d "$SOURCE_DIR" ]; then
echo "Source directory '$SOURCE_DIR' does not exist."
exit 1
fi
if [ ! -d "$DEST_PATH" ]; then
echo "Destination directory '$DEST_PATH' does not exist. Creating it..."
mkdir -p "$DEST_PATH"
fi
if [ -z "$(ls -A "$SOURCE_DIR")" ]; then
echo "Source directory '$SOURCE_DIR' is empty. No files to copy."
exit 1
fi
echo "Copying fonts from '$SOURCE_DIR' to '$DEST_PATH'..."
cp -r "$SOURCE_DIR"/* "$DEST_PATH"
echo "Updating font cache..."
fc-cache -fv
echo "Fonts installed successfully."
-142
View File
@@ -1,142 +0,0 @@
export const namedColors = new Set([
'alice blue',
'antique white',
'aqua',
'aquamarine',
'azure',
'beige',
'bisque',
'black',
'blanched almond',
'blue',
'blue violet',
'brown',
'burlywood',
'cadet blue',
'chartreuse',
'chocolate',
'coral',
'cornflower blue',
'cornsilk',
'crimson',
'cyan',
'dark blue',
'dark cyan',
'dark goldenrod',
'dark gray',
'dark green',
'dark khaki',
'dark magenta',
'dark olive green',
'dark orange',
'dark orchid',
'dark red',
'dark salmon',
'dark sea green',
'dark slate blue',
'dark slate gray',
'dark turquoise',
'dark violet',
'deep pink',
'deep sky blue',
'dim gray',
'dodger blue',
'firebrick',
'floral white',
'forest green',
'fuchsia',
'gainsboro',
'ghost white',
'gold',
'goldenrod',
'gray',
'green',
'green yellow',
'honeydew',
'hot pink',
'indian red',
'indigo',
'ivory',
'khaki',
'lavender',
'lavender blush',
'lawn green',
'lemon chiffon',
'light blue',
'light coral',
'light cyan',
'light goldenrod yellow',
'light green',
'light grey',
'light pink',
'light salmon',
'light sea green',
'light sky blue',
'light slate gray',
'light steel blue',
'light yellow',
'lime',
'lime green',
'linen',
'magenta',
'maroon',
'medium aquamarine',
'medium blue',
'medium orchid',
'medium purple',
'medium sea green',
'medium slate blue',
'medium spring green',
'medium turquoise',
'medium violet red',
'midnight blue',
'mint cream',
'misty rose',
'moccasin',
'navajo white',
'navy',
'old lace',
'olive',
'olive drab',
'orange',
'orange red',
'orchid',
'pale goldenrod',
'pale green',
'pale turquoise',
'pale violet red',
'papaya whip',
'peach puff',
'peru',
'pink',
'plum',
'powder blue',
'purple',
'red',
'rosy brown',
'royal blue',
'saddle brown',
'salmon',
'sandy brown',
'sea green',
'seashell',
'sienna',
'silver',
'sky blue',
'slate blue',
'slate gray',
'snow',
'spring green',
'steel blue',
'tan',
'teal',
'thistle',
'tomato',
'turquoise',
'violet',
'wheat',
'white',
'white smoke',
'yellow',
'yellow green',
]);
-145
View File
@@ -1,145 +0,0 @@
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',
} as const;
export default {
missing: 'image-missing-symbolic',
nix: {
nix: 'nix-snowflake-symbolic',
},
app: {
terminal: 'terminal-symbolic',
},
fallback: {
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',
arrow: {
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',
},
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',
},
type: {
headset: 'audio-headphones-symbolic',
speaker: 'audio-speakers-symbolic',
card: 'audio-card-symbolic',
},
mixer: 'mixer-symbolic',
},
powerprofile: {
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',
},
mode: {
Integrated: 'processor-symbolic',
Hybrid: 'controller-symbolic',
},
},
battery: {
charging: 'battery-flash-symbolic',
warning: 'battery-empty-symbolic',
},
bluetooth: {
enabled: 'bluetooth-active-symbolic',
disabled: 'bluetooth-disabled-symbolic',
},
brightness: {
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',
},
recorder: {
recording: 'media-record-symbolic',
},
notifications: {
noisy: 'org.gnome.Settings-notifications-symbolic',
silent: 'notifications-disabled-symbolic',
message: 'chat-bubbles-symbolic',
},
trash: {
full: 'user-trash-full-symbolic',
empty: 'user-trash-symbolic',
},
mpris: {
shuffle: {
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',
},
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',
},
color: {
dark: 'dark-mode-symbolic',
light: 'light-mode-symbolic',
},
};
-147
View File
@@ -1,147 +0,0 @@
import { isHexColor } from 'globals/variables';
import { Variable } from 'resource:///com/github/Aylur/ags/variable.js';
import { MkOptionsResult } from './types/options';
type OptProps = {
persistent?: boolean;
};
export class Opt<T = unknown> extends Variable<T> {
static {
Service.register(this);
}
constructor(initial: T, { persistent = false }: OptProps = {}) {
super(initial);
this.initial = initial;
this.persistent = persistent;
}
initial: T;
id = '';
persistent: boolean;
toString(): string {
return `${this.value}`;
}
toJSON(): string {
return `opt:${this.value}`;
}
getValue = (): T => {
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);
});
}
reset(): string | undefined {
if (this.persistent) return;
if (JSON.stringify(this.value) !== JSON.stringify(this.initial)) {
this.value = this.initial;
return this.id;
}
}
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;
}
return;
}
}
export const opt = <T>(initial: T, opts?: OptProps): Opt<T> => new Opt(initial, opts);
const getOptions = (object: Record<string, unknown>, path = ''): Opt[] => {
return Object.keys(object).flatMap((key) => {
const obj = object[key];
const id = path ? path + '.' + key : key;
if (obj instanceof Variable) {
const optValue = obj as Opt;
optValue.id = id;
return optValue;
}
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',
): T & MkOptionsResult {
for (const opt of getOptions(object as Record<string, unknown>)) opt.init(cacheFile);
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);
Utils.monitorFile(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];
}
});
function sleep(ms = 0): Promise<T> {
return new Promise((r) => setTimeout(r, ms));
}
const reset = async (
[opt, ...list] = getOptions(object as Record<string, unknown>),
id = opt?.reset(),
): Promise<Array<string>> => {
if (!opt) return sleep().then(() => []);
return id ? [id, ...(await sleep(50).then(() => reset(list)))] : await sleep().then(() => reset(list));
};
const resetTheme = async (
[opt, ...list] = getOptions(object as Record<string, unknown>),
id = opt?.doResetColor(),
): Promise<Array<string>> => {
if (!opt) return sleep().then(() => []);
return id
? [id, ...(await sleep(50).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');
},
async resetTheme() {
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);
}
},
});
}
-16
View File
@@ -1,16 +0,0 @@
import GLib from 'gi://GLib?version=2.0';
declare global {
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`);
-28
View File
@@ -1,28 +0,0 @@
import { MprisPlayer } from 'types/service/mpris';
const mpris = await Service.import('mpris');
export const getCurrentPlayer = (activePlayer: MprisPlayer = mpris.players[0]): MprisPlayer => {
const statusOrder = {
Playing: 1,
Paused: 2,
Stopped: 3,
};
if (mpris.players.length === 0) {
return mpris.players[0];
}
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 nextPlayerUp = mpris.players.sort(
(a: MprisPlayer, b: MprisPlayer) => statusOrder[a.play_back_status] - statusOrder[b.play_back_status],
)[0];
if (isPlaying || !playerStillExists) {
return nextPlayerUp;
}
return activePlayer;
};
-12
View File
@@ -1,12 +0,0 @@
import { Notification } from 'types/service/notifications';
export const filterNotifications = (notifications: Notification[], filter: string[]): Notification[] => {
const notifFilter = new Set(filter.map((name: string) => name.toLowerCase().replace(/\s+/g, '_')));
const filteredNotifications = notifications.filter((notif: Notification) => {
const normalizedAppName = notif.app_name.toLowerCase().replace(/\s+/g, '_');
return !notifFilter.has(normalizedAppName);
});
return filteredNotifications;
};
-5
View File
@@ -1,5 +0,0 @@
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;
-42
View File
@@ -1,42 +0,0 @@
import { Binding, Connectable } from 'types/service';
import { Variable } from 'types/variable';
import Box from 'types/widgets/box';
import Button, { ButtonProps } from 'types/widgets/button';
import Label from 'types/widgets/label';
import { Attribute, Child } from './widget';
export type BarBoxChild = {
component: Box<Gtk.Widget, unknown>;
isVisible?: boolean;
isVis?: Variable<boolean>;
boxClass: string;
tooltip_text?: string | Binding<string>;
props: 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>;
};
export type ResourceLabelType = 'used/total' | 'used' | 'percentage' | 'free';
export type NetstatLabelType = 'full' | 'in' | 'out';
export type RateUnit = 'GiB' | 'MiB' | 'KiB' | 'auto';
-13
View File
@@ -1,13 +0,0 @@
export type GenericFunction<T, P extends unknown[] = unknown[]> = (...args: P) => T;
export type GenericResourceMetrics = {
total: number;
used: number;
percentage: number;
};
type GenericResourceData = ResourceUsage & {
free: number;
};
export type Postfix = 'TiB' | 'GiB' | 'MiB' | 'KiB' | 'B';
-32
View File
@@ -1,32 +0,0 @@
import { layoutMap } from 'customModules/kblayout/layouts';
export type KbLabelType = 'layout' | 'code';
export type HyprctlKeyboard = {
address: string;
name: string;
rules: string;
model: string;
layout: string;
variant: string;
options: string;
active_keymap: string;
main: boolean;
};
export type HyprctlMouse = {
address: string;
name: string;
defaultSpeed: number;
};
export type HyprctlDeviceLayout = {
mice: HyprctlMouse[];
keyboards: HyprctlKeyboard[];
tablets: unknown[];
touch: unknown[];
switches: unknown[];
};
export type LayoutKeys = keyof typeof layoutMap;
export type LayoutValues = (typeof layoutMap)[LayoutKeys];
-4
View File
@@ -1,4 +0,0 @@
export type NetworkResourceData = {
in: string;
out: string;
};
-11
View File
@@ -1,11 +0,0 @@
import { Binding } from 'lib/utils';
export type InputHandlerEvents = {
onPrimaryClick?: Binding;
onSecondaryClick?: Binding;
onMiddleClick?: Binding;
onScrollUp?: Binding;
onScrollDown?: Binding;
};
export type RunAsyncCommand = (cmd: string, args: EventArgs, fn?: (output: string) => void) => void;
-5
View File
@@ -1,5 +0,0 @@
import { NetstatLabelType, ResourceLabelType } from '../bar';
export const LABEL_TYPES: ResourceLabelType[] = ['used/total', 'used', 'free', 'percentage'];
export const NETWORK_LABEL_TYPES: NetstatLabelType[] = ['full', 'in', 'out'];
@@ -1,10 +0,0 @@
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 ${dataType}/s`, out: `0 ${dataType}/s` };
};
@@ -1,60 +0,0 @@
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',
} as const;
File diff suppressed because it is too large Load Diff
-12
View File
@@ -1,12 +0,0 @@
import { WindowProps } from 'types/widgets/window';
import { GtkWidget, Transition } from './widget';
import { Binding } from 'types/service';
export type DropdownMenuProps = {
name: string;
child: GtkWidget;
layout?: string;
transition?: Transition | Binding<Transition>;
exclusivity?: Exclusivity;
fixed?: boolean;
} & WindowProps;
-3
View File
@@ -1,3 +0,0 @@
export type Config = {
[key: string]: string | number | boolean | object;
};
-4
View File
@@ -1,4 +0,0 @@
export type MousePos = {
source: string;
pos: number[];
};
-25
View File
@@ -1,25 +0,0 @@
export type GPU_Stat_Process = {
username: string;
command: string;
full_command: string[];
gpu_memory_usage: number;
cpu_percent: number;
cpu_memory_usage: number;
pid: number;
};
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;
processes: Process[];
};
-2
View File
@@ -1,2 +0,0 @@
export type LoopStatus = 'none' | 'track' | 'playlist';
export type PlaybackStatus = 'playing' | 'paused' | 'stopped';
-16
View File
@@ -1,16 +0,0 @@
import { WIFI_STATUS_MAP } from 'globals/network';
export type AccessPoint = {
bssid: string | null;
address: string | null;
lastSeen: number;
ssid: string | null;
active: boolean;
strength: number;
frequency: number;
iconName: string | undefined;
};
export type WifiStatus = keyof typeof WIFI_STATUS_MAP;
export type WifiIcon = '󰤩' | '󰤨' | '󰤪' | '󰤨' | '󰤩' | '󰤮' | '󰤨' | '󰤥' | '󰤢' | '󰤟' | '󰤯';
-15
View File
@@ -1,15 +0,0 @@
import icons from 'modules/icons/index';
export interface NotificationArgs {
appName?: string;
body?: string;
iconName?: string;
id?: number;
summary?: string;
urgency?: Urgency;
category?: string;
timeout?: number;
transient?: boolean;
}
export type NotificationIcon = keyof typeof icons.notifications;
-217
View File
@@ -1,217 +0,0 @@
import { Opt } from 'lib/option';
import { Variable } from 'types/variable';
import { defaultColorMap } from './defaults/options';
export type MkOptionsResult = {
configFile: string;
array: () => Opt[];
reset: () => Promise<string>;
resetTheme: () => Promise<string>;
handler: (deps: string[], callback: () => void) => void;
};
export type RecursiveOptionsObject = {
[key: string]: RecursiveOptionsObject | Opt<string> | Opt<number> | Opt<boolean>;
};
export type BarLocation = 'top' | 'bottom';
export type Unit = 'imperial' | 'metric';
export type PowerOptions = 'sleep' | 'reboot' | 'logout' | 'shutdown';
export type NotificationAnchor =
| 'top'
| 'top right'
| 'top left'
| 'bottom'
| 'bottom right'
| 'bottom left'
| 'left'
| 'right';
export type OSDAnchor = 'top left' | 'top' | 'top right' | 'right' | 'bottom right' | 'bottom' | 'bottom left' | 'left';
export type BarButtonStyles = 'default' | 'split' | 'wave' | 'wave2';
export type ThemeExportData = {
filePath: string;
themeOnly: boolean;
};
export type RowProps<T> = {
opt: Opt<T>;
title: string;
note?: string;
type?:
| '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<string> | Opt;
subtitleLink?: string;
dependencies?: string[];
increment?: number;
};
export type OSDOrientation = 'horizontal' | 'vertical';
export type HexColor = `#${string}`;
export type WindowLayer = 'top' | 'bottom' | 'overlay' | 'background';
export type ActiveWsIndicator = 'underline' | 'highlight' | 'color';
export type MatugenColors = {
background: HexColor;
error: HexColor;
error_container: HexColor;
inverse_on_surface: HexColor;
inverse_primary: HexColor;
inverse_surface: HexColor;
on_background: HexColor;
on_error: HexColor;
on_error_container: HexColor;
on_primary: HexColor;
on_primary_container: HexColor;
on_primary_fixed: HexColor;
on_primary_fixed_variant: HexColor;
on_secondary: HexColor;
on_secondary_container: HexColor;
on_secondary_fixed: HexColor;
on_secondary_fixed_variant: HexColor;
on_surface: HexColor;
on_surface_variant: HexColor;
on_tertiary: HexColor;
on_tertiary_container: HexColor;
on_tertiary_fixed: HexColor;
on_tertiary_fixed_variant: HexColor;
outline: HexColor;
outline_variant: HexColor;
primary: HexColor;
primary_container: HexColor;
primary_fixed: HexColor;
primary_fixed_dim: HexColor;
scrim: HexColor;
secondary: HexColor;
secondary_container: HexColor;
secondary_fixed: HexColor;
secondary_fixed_dim: HexColor;
shadow: HexColor;
surface: HexColor;
surface_bright: HexColor;
surface_container: HexColor;
surface_container_high: HexColor;
surface_container_highest: HexColor;
surface_container_low: HexColor;
surface_container_lowest: HexColor;
surface_dim: HexColor;
surface_variant: HexColor;
tertiary: HexColor;
tertiary_container: HexColor;
tertiary_fixed: HexColor;
tertiary_fixed_dim: HexColor;
};
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';
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';
export type ColorMapKey = keyof typeof defaultColorMap;
export type ColorMapValue = (typeof defaultColorMap)[ColorMapKey];
export type ScalingPriority = 'gdk' | 'hyprland' | 'both';
-35
View File
@@ -1,35 +0,0 @@
import { Widget } from 'types/widgets/widget';
import { WindowProps } from 'types/widgets/window';
import { Transition } from './widget';
export type PopupWindowProps = {
name: string;
child: Widget;
layout?: Layouts;
transition?: Transition | Binding<Transition>;
exclusivity?: Exclusivity;
} & WindowProps;
export type LayoutFunction = (
name: string,
child: Widget,
transition: Transition,
) => {
center: () => Widget;
top: () => Widget;
'top-right': () => Widget;
'top-center': () => Widget;
'top-left': () => Widget;
'bottom-left': () => Widget;
'bottom-center': () => Widget;
'bottom-right': () => Widget;
};
export type Layouts =
| 'center'
| 'top'
| 'top-right'
| 'top-center'
| 'top-left'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';
-1
View File
@@ -1 +0,0 @@
export type Action = 'sleep' | 'reboot' | 'logout' | 'shutdown';
-7
View File
@@ -1,7 +0,0 @@
import PowerProfiles from 'types/service/powerprofiles.js';
export type PowerProfiles = InstanceType<typeof PowerProfiles>;
export type PowerProfile = 'power-saver' | 'balanced' | 'performance';
export type PowerProfileObject = {
[key: string]: string;
};
View File
-7
View File
@@ -1,7 +0,0 @@
import { substitutes } from 'lib/icons';
import { EventArgs } from './widget';
type SubstituteKeys = keyof typeof substitutes;
export type ThrottleFn = (cmd: string, args: EventArgs, fn?: (output: string) => void) => void;
export type ThrottleFnCallback = ((output: string) => void) | undefined;
-1
View File
@@ -1 +0,0 @@
export type Bind = OriginalBinding<GObject.Object, keyof Props<GObject.Object>, unknown>;
-3
View File
@@ -1,3 +0,0 @@
export type VolumeIcons = {
[index: number]: string;
};
-118
View File
@@ -1,118 +0,0 @@
import { weatherIcons } from 'modules/icons/weather';
export type UnitType = 'imperial' | 'metric';
export type Weather = {
location: Location;
current: Current;
forecast: Forecast;
};
export type Current = {
last_updated_epoch?: number;
last_updated?: string;
temp_c: number;
temp_f: number;
is_day: number;
condition: Condition;
wind_mph: number;
wind_kph: number;
wind_degree: number;
wind_dir: string;
pressure_mb: number;
pressure_in: number;
precip_mm: number;
precip_in: number;
humidity: number;
cloud: number;
feelslike_c: number;
feelslike_f: number;
windchill_c: number;
windchill_f: number;
heatindex_c: number;
heatindex_f: number;
dewpoint_c: number;
dewpoint_f: number;
vis_km: number;
vis_miles: number;
uv: number;
gust_mph: number;
gust_kph: number;
time_epoch?: number;
time?: string;
snow_cm?: number;
will_it_rain?: number;
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;
date_epoch: number;
day: Day;
astro: Astro;
hour: Current[];
};
export type Astro = {
sunrise: string;
sunset: string;
moonrise: string;
moonset: string;
moon_phase: string;
moon_illumination: number;
is_moon_up: number;
is_sun_up: number;
};
export type Day = {
maxtemp_c: number;
maxtemp_f: number;
mintemp_c: number;
mintemp_f: number;
avgtemp_c: number;
avgtemp_f: number;
maxwind_mph: number;
maxwind_kph: number;
totalprecip_mm: number;
totalprecip_in: number;
totalsnow_cm: number;
avgvis_km: number;
avgvis_miles: number;
avghumidity: number;
daily_will_it_rain: number;
daily_chance_of_rain: number;
daily_will_it_snow: number;
daily_chance_of_snow: number;
condition: Condition;
uv: number;
};
export type Location = {
name: string;
region: string;
country: string;
lat: number;
lon: number;
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];
-29
View File
@@ -1,29 +0,0 @@
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
import Box from 'types/widgets/box';
export type Exclusivity = 'normal' | 'ignore' | 'exclusive';
export type Anchor = 'left' | 'right' | 'top' | 'down';
export type Transition = 'none' | 'crossfade' | 'slide_right' | 'slide_left' | 'slide_up' | 'slide_down';
export type 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;
export type EventArgs = { clicked: Button<Child, Attribute>; event: Gdk.Event };
-25
View File
@@ -1,25 +0,0 @@
export type WorkspaceRule = {
workspaceString: string;
monitor: string;
};
export type WorkspaceMap = {
[key: string]: number[];
};
export type MonitorMap = {
[key: number]: string;
};
export type WorkspaceIcons = {
[key: string]: string;
};
export type WorkspaceIconsColored = {
[key: string]: {
color: string;
icon: string;
};
};
export type WorkspaceIconMap = WorkspaceIcons | WorkspaceIconsColored;
-195
View File
@@ -1,195 +0,0 @@
/* 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';
import { Window } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import { namedColors } from './constants/colors';
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): string {
const validateSubstitute = (name: string): name is SubstituteKeys => name in substitutes;
if (!name) return fallback || '';
if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name;
let icon: string = name;
if (validateSubstitute(name)) {
icon = substitutes[name];
}
if (Utils.lookUpIcon(icon)) return icon;
print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`);
return fallback;
}
/**
* @returns execAsync(["bash", "-c", cmd])
*/
export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]): 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 '';
});
}
/**
* @returns execAsync(cmd)
*/
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): Window[] {
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
return range(n, 0).flatMap(widget);
}
/**
* @returns [start...length]
*/
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[]): 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(', ')}`));
Notify({
summary: 'Dependencies not found!',
body: `The following dependencies are missing: ${missing.join(', ')}`,
iconName: icons.ui.warning,
timeout: 7000,
});
}
return missing.length === 0;
}
/**
* run app detached
*/
export function launchApp(app: Application): void {
const exe = app.executable
.split(/\s+/)
.filter((str) => !str.startsWith('%') && !str.startsWith('@'))
.join(' ');
bash(`${exe} &`);
app.frequency += 1;
}
/**
* to use with drag and drop
*/
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;
}
/**
* Ensure that the provided filepath is a valid image
*/
export const isAnImage = (imgFilePath: string): boolean => {
try {
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';
command += ` "${notifPayload.summary} "`;
if (notifPayload.body) command += ` "${notifPayload.body}" `;
if (notifPayload.appName) command += ` -a "${notifPayload.appName}"`;
if (notifPayload.iconName) command += ` -i "${notifPayload.iconName}"`;
if (notifPayload.urgency) command += ` -u "${notifPayload.urgency}"`;
if (notifPayload.timeout !== undefined) command += ` -t ${notifPayload.timeout}`;
if (notifPayload.category) command += ` -c "${notifPayload.category}"`;
if (notifPayload.transient) command += ` -e`;
if (notifPayload.id !== undefined) command += ` -r ${notifPayload.id}`;
Utils.execAsync(command);
};
export const 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'];
};
export const isValidGjsColor = (color: string): boolean => {
const colorLower = color.toLowerCase().trim();
if (namedColors.has(colorLower)) {
return true;
}
const hexColorRegex = /^#(?:[a-fA-F0-9]{3,4}|[a-fA-F0-9]{6,8})$/;
const rgbRegex = /^rgb\(\s*(\d{1,3}%?\s*,\s*){2}\d{1,3}%?\s*\)$/;
const rgbaRegex = /^rgba\(\s*(\d{1,3}%?\s*,\s*){3}(0|1|0?\.\d+)\s*\)$/;
if (hexColorRegex.test(color)) {
return true;
}
if (rgbRegex.test(colorLower) || rgbaRegex.test(colorLower)) {
return true;
}
return false;
};
export const capitalizeFirstLetter = (str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
-15
View File
@@ -1,15 +0,0 @@
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, (): DateTime => GLib.DateTime.new_now_local()],
});
export const uptime = Variable(0, {
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'),
};
-22
View File
@@ -1,22 +0,0 @@
import 'lib/session';
import 'scss/style';
import 'globals/useTheme';
import 'globals/dropdown.js';
import 'globals/utilities';
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()],
closeWindowDelay: {
sideright: 350,
launcher: 350,
bar0: 350,
},
});
-315
View File
@@ -1,315 +0,0 @@
const hyprland = await Service.import('hyprland');
import {
Menu,
Workspaces,
ClientTitle,
Media,
Notifications,
Volume,
Network,
Bluetooth,
BatteryLabel,
Clock,
SysTray,
// Custom Modules
Ram,
Cpu,
Storage,
Netstat,
KbInput,
Updates,
Submap,
Weather,
Power,
} 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 './SideEffects';
import { WindowLayer } from 'lib/types/options.js';
import { Attribute, Child } from 'lib/types/widget.js';
import Window from 'types/widgets/window.js';
const { layouts } = options.bar;
const { location } = options.theme.bar;
export type BarWidget = keyof typeof widget;
type Section =
| 'battery'
| 'dashboard'
| 'workspaces'
| 'windowtitle'
| 'media'
| 'notifications'
| 'volume'
| 'network'
| 'bluetooth'
| 'clock'
| 'ram'
| 'cpu'
| 'storage'
| 'netstat'
| 'kbinput'
| 'updates'
| 'submap'
| 'weather'
| 'power'
| 'systray';
type Layout = {
left: Section[];
middle: Section[];
right: Section[];
};
type BarLayout = {
[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 === '*');
if (matchingKey) {
return layouts[matchingKey];
}
if (wildcard) {
return layouts[wildcard];
}
return {
left: ['dashboard', 'workspaces', 'windowtitle'],
middle: ['media'],
right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'],
};
};
const widget = {
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()),
submap: (): Button<Child, Attribute> => WidgetContainer(Submap()),
weather: (): Button<Child, Attribute> => WidgetContainer(Weather()),
power: (): Button<Child, Attribute> => WidgetContainer(Power()),
};
type GdkMonitors = {
[key: string]: {
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.');
return {};
}
const numGdkMonitors = display.get_n_monitors();
const gdkMonitors: GdkMonitors = {};
for (let i = 0; i < numGdkMonitors; i++) {
const curMonitor = display.get_monitor(i);
if (curMonitor === null) {
console.warn(`Monitor at index ${i} is null.`);
continue;
}
const model = curMonitor.get_model() || '';
const geometry = curMonitor.get_geometry();
const scaleFactor = curMonitor.get_scale_factor();
const key = `${model}_${geometry.width}x${geometry.height}_${scaleFactor}`;
gdkMonitors[i] = { key, model, used: false };
}
return 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
* 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:
*/
const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<number>): number => {
const gdkMonitors = getGdkMonitors();
if (Object.keys(gdkMonitors).length === 0) {
console.error('No GDK monitors were found.');
return monitor;
}
// Get the GDK monitor for the given monitor index
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 hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`;
return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id) && hypMon.id === monitor;
});
if (directMatch) {
usedHyprlandMonitors.add(directMatch.id);
return directMatch.id;
}
// Second pass: Relaxed matching without considering the monitor index
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);
});
if (hyprlandMonitor) {
usedHyprlandMonitors.add(hyprlandMonitor.id);
return hyprlandMonitor.id;
}
// Fallback: Find the first available monitor ID that hasn't been used
const fallbackMonitor = hyprland.monitors.find((hypMon) => !usedHyprlandMonitors.has(hypMon.id));
if (fallbackMonitor) {
usedHyprlandMonitors.add(fallbackMonitor.id);
return fallbackMonitor.id;
}
// Ensure we return a valid monitor ID that actually exists
for (let i = 0; i < hyprland.monitors.length; i++) {
if (!usedHyprlandMonitors.has(i)) {
usedHyprlandMonitors.add(i);
return i;
}
}
// As a last resort, return the original monitor index if no unique monitor can be found
console.warn(`Returning original monitor index as a last resort: ${monitor}`);
return monitor;
};
export const Bar = (() => {
const usedHyprlandMonitors = new Set<number>();
return (monitor: number): Window<Child, Attribute> => {
const hyprlandMonitor = gdkMonitorIdToHyprlandId(monitor, usedHyprlandMonitors);
return Widget.Window({
name: `bar-${hyprlandMonitor}`,
class_name: 'bar',
monitor,
visible: true,
anchor: location.bind('value').as((ln) => [ln, 'left', 'right']),
exclusivity: 'exclusive',
layer: Utils.merge(
[options.theme.bar.layer.bind('value'), options.tear.bind('value')],
(barLayer: WindowLayer, tear: boolean) => {
if (tear && barLayer === 'overlay') {
return 'top';
}
return barLayer;
},
),
child: Widget.Box({
class_name: 'bar-panel-container',
child: Widget.CenterBox({
class_name: 'bar-panel',
css: 'padding: 1px',
startWidget: Widget.Box({
class_name: 'box-left',
hexpand: true,
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>);
});
},
}),
centerWidget: Widget.Box({
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>);
});
},
}),
endWidget: Widget.Box({
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>);
});
},
}),
}),
}),
});
};
})();
-47
View File
@@ -1,47 +0,0 @@
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 { Submap } from 'customModules/submap/index';
import { Weather } from 'customModules/weather/index';
import { Power } from 'customModules/power/index';
export {
Menu,
Workspaces,
ClientTitle,
Media,
Notifications,
Volume,
Network,
Bluetooth,
BatteryLabel,
Clock,
SysTray,
// Custom Modules
Ram,
Cpu,
Storage,
Netstat,
KbInput,
Updates,
Submap,
Weather,
Power,
};

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