delete ags
@@ -1,2 +0,0 @@
|
||||
types
|
||||
node_modules
|
||||
@@ -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 }],
|
||||
},
|
||||
};
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
.weather.json
|
||||
node_modules
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "external/ags-types"]
|
||||
path = external/ags-types
|
||||
url = https://github.com/Jas-SinghFSU/ags-types.git
|
||||
@@ -1,2 +0,0 @@
|
||||
.eslintrc.js
|
||||
types/**/*.ts
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
|
||||

|
||||

|
||||
|
||||
## 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.
|
||||
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 558 KiB |
|
Before Width: | Height: | Size: 6.5 MiB |
|
Before Width: | Height: | Size: 425 KiB |
|
Before Width: | Height: | Size: 706 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 430 KiB |
@@ -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 {};
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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 };
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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 {};
|
||||
@@ -1,6 +0,0 @@
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
|
||||
type GlobalEventBoxes = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
export const globalEventBoxes: VariableType<GlobalEventBoxes> = Variable({});
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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');
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
globalThis.isWindowVisible = (windowName: string): boolean => {
|
||||
const appWindow = App.getWindow(windowName);
|
||||
|
||||
if (appWindow === undefined) {
|
||||
return false;
|
||||
}
|
||||
return appWindow.visible;
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -1,10 +0,0 @@
|
||||
export const WINDOW_LAYOUTS: string[] = [
|
||||
'center',
|
||||
'top',
|
||||
'top-right',
|
||||
'top-center',
|
||||
'top-left',
|
||||
'bottom-left',
|
||||
'bottom-center',
|
||||
'bottom-right',
|
||||
];
|
||||
@@ -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."
|
||||
@@ -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',
|
||||
]);
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -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`);
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -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];
|
||||
@@ -1,4 +0,0 @@
|
||||
export type NetworkResourceData = {
|
||||
in: string;
|
||||
out: string;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,3 +0,0 @@
|
||||
export type Config = {
|
||||
[key: string]: string | number | boolean | object;
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
export type MousePos = {
|
||||
source: string;
|
||||
pos: number[];
|
||||
};
|
||||
@@ -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[];
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
export type LoopStatus = 'none' | 'track' | 'playlist';
|
||||
export type PlaybackStatus = 'playing' | 'paused' | 'stopped';
|
||||
@@ -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 = '' | '' | '' | '' | '' | '' | '' | '' | '' | '' | '';
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
@@ -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 +0,0 @@
|
||||
export type Action = 'sleep' | 'reboot' | 'logout' | 'shutdown';
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 +0,0 @@
|
||||
export type Bind = OriginalBinding<GObject.Object, keyof Props<GObject.Object>, unknown>;
|
||||
@@ -1,3 +0,0 @@
|
||||
export type VolumeIcons = {
|
||||
[index: number]: string;
|
||||
};
|
||||
@@ -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];
|
||||
@@ -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 };
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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'),
|
||||
};
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
@@ -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>);
|
||||
});
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
})();
|
||||
@@ -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,
|
||||
};
|
||||