horror
100
flake.lock
|
@ -2,20 +2,20 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"ags": {
|
"ags": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714991158,
|
"lastModified": 1721306136,
|
||||||
"narHash": "sha256-QRtQD3YqbambczxavUUkRv7iGxoJ1J6e6mD20mZv3a4=",
|
"narHash": "sha256-VKPsIGf3/a+RONBipx4lEE4LXG2sdMNkWQu22LNQItg=",
|
||||||
"owner": "Aylur",
|
"owner": "Aylur",
|
||||||
"repo": "ags",
|
"repo": "ags",
|
||||||
"rev": "05e0f23534fa30c1db2a142664ee8f71e38db260",
|
"rev": "344ea72cd3b8d4911f362fec34bce7d8fb37028c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Aylur",
|
"owner": "Aylur",
|
||||||
"repo": "ags",
|
"repo": "ags",
|
||||||
"rev": "05e0f23534fa30c1db2a142664ee8f71e38db260",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"jovian": "jovian",
|
"jovian": "jovian",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"systems": "systems",
|
"systems": "systems_2",
|
||||||
"yafas": "yafas"
|
"yafas": "yafas"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
@ -114,6 +114,22 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696426674,
|
||||||
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-schemas": {
|
"flake-schemas": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693491534,
|
"lastModified": 1693491534,
|
||||||
|
@ -206,15 +222,15 @@
|
||||||
"hyprutils": "hyprutils",
|
"hyprutils": "hyprutils",
|
||||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"nixpkgs": "nixpkgs_3",
|
||||||
"systems": "systems_2",
|
"systems": "systems_3",
|
||||||
"xdph": "xdph"
|
"xdph": "xdph"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1725032272,
|
"lastModified": 1725109672,
|
||||||
"narHash": "sha256-A0AiFlPoqDK/IwUiV7SGIi8uW2zOqmmiZY6j8i367LU=",
|
"narHash": "sha256-YEYhBaoaYqCc3nNd/XdmmtmQy63FPuMInHCtHkZdmJQ=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "c5fd5771814958ad274ea9abb961fff621a35b9c",
|
"rev": "838ed87d6ffae0dbdc8a3ecaac2c8be006f6d053",
|
||||||
"revCount": 5165,
|
"revCount": 5167,
|
||||||
"submodules": true,
|
"submodules": true,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/hyprwm/Hyprland"
|
"url": "https://github.com/hyprwm/Hyprland"
|
||||||
|
@ -353,6 +369,22 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"more-waita": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1714324684,
|
||||||
|
"narHash": "sha256-E8zkq8Zfn90Qrn5clXquHAQhAI4K4/7JV7fjujf1P7U=",
|
||||||
|
"owner": "somepaulo",
|
||||||
|
"repo": "MoreWaita",
|
||||||
|
"rev": "0f4ed19f51b5ca8f0622c9e154886efb2a5ff59b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "somepaulo",
|
||||||
|
"repo": "MoreWaita",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix-github-actions": {
|
"nix-github-actions": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -378,11 +410,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1708475490,
|
"lastModified": 1718714799,
|
||||||
"narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
|
"narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0e74ca98a74bc7270d28838369593635a5db3260",
|
"rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -462,8 +494,10 @@
|
||||||
"chaotic": "chaotic",
|
"chaotic": "chaotic",
|
||||||
"home-manager": "home-manager_2",
|
"home-manager": "home-manager_2",
|
||||||
"hyprland": "hyprland",
|
"hyprland": "hyprland",
|
||||||
|
"more-waita": "more-waita",
|
||||||
"nixpkgs": "nixpkgs_4",
|
"nixpkgs": "nixpkgs_4",
|
||||||
"sops-nix": "sops-nix"
|
"sops-nix": "sops-nix",
|
||||||
|
"spicetify-nix": "spicetify-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
|
@ -504,6 +538,27 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"spicetify-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1725077747,
|
||||||
|
"narHash": "sha256-4SwFxe789QXOtGNFbwldpqFzkKxIFuyDysFttFWB5eM=",
|
||||||
|
"owner": "Gerg-L",
|
||||||
|
"repo": "spicetify-nix",
|
||||||
|
"rev": "305b6034ad329268eb33c079e2d33740ccf329ea",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Gerg-L",
|
||||||
|
"repo": "spicetify-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689347949,
|
"lastModified": 1689347949,
|
||||||
|
@ -534,6 +589,21 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systems_3": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
"xdph": {
|
"xdph": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"hyprland-protocols": "hyprland-protocols",
|
"hyprland-protocols": "hyprland-protocols",
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable";
|
chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable";
|
||||||
|
|
||||||
ags.url = "github:Aylur/ags/05e0f23534fa30c1db2a142664ee8f71e38db260";
|
# this commit seems to be fucked on nixos now
|
||||||
|
# ags.url = "github:Aylur/ags/05e0f23534fa30c1db2a142664ee8f71e38db260";
|
||||||
|
ags.url = "github:Aylur/ags";
|
||||||
|
|
||||||
spicetify-nix = {
|
spicetify-nix = {
|
||||||
url = "github:Gerg-L/spicetify-nix";
|
url = "github:Gerg-L/spicetify-nix";
|
||||||
|
@ -31,6 +33,11 @@
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
more-waita = {
|
||||||
|
url = "github:somepaulo/MoreWaita";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager";
|
url = "github:nix-community/home-manager";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
boot = {
|
boot = {
|
||||||
|
tmp.cleanOnBoot = true;
|
||||||
loader = {
|
loader = {
|
||||||
systemd-boot = {
|
systemd-boot = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -77,6 +78,7 @@
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
extraSpecialArgs = { inherit inputs; };
|
extraSpecialArgs = { inherit inputs; };
|
||||||
|
useGlobalPkgs = true;
|
||||||
users = {
|
users = {
|
||||||
"pingu" = import ./home.nix;
|
"pingu" = import ./home.nix;
|
||||||
};
|
};
|
||||||
|
@ -121,6 +123,8 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-ld.enable = true;
|
||||||
|
|
||||||
git = {
|
git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
lfs.enable = true;
|
lfs.enable = true;
|
||||||
|
@ -147,7 +151,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
hyprcursor
|
hyprcursor
|
||||||
|
grimblast
|
||||||
neovim
|
neovim
|
||||||
wget
|
wget
|
||||||
git
|
git
|
||||||
|
@ -162,7 +167,6 @@
|
||||||
clang
|
clang
|
||||||
go
|
go
|
||||||
nixfmt-classic
|
nixfmt-classic
|
||||||
python3
|
|
||||||
zip
|
zip
|
||||||
nodejs
|
nodejs
|
||||||
typescript
|
typescript
|
||||||
|
@ -170,6 +174,40 @@
|
||||||
rustup
|
rustup
|
||||||
vscode-fhs
|
vscode-fhs
|
||||||
bibata-cursors
|
bibata-cursors
|
||||||
|
spotify
|
||||||
|
jq
|
||||||
|
gnome.gnome-control-center
|
||||||
|
pavucontrol
|
||||||
|
icon-library
|
||||||
|
bat
|
||||||
|
fzf
|
||||||
|
fd
|
||||||
|
eza
|
||||||
|
glib
|
||||||
|
cliphist
|
||||||
|
playerctl
|
||||||
|
socat
|
||||||
|
adwaita-qt6
|
||||||
|
material-icons
|
||||||
|
material-design-icons
|
||||||
|
material-symbols
|
||||||
|
ddcutil
|
||||||
|
(python312.withPackages (ps: with ps; [
|
||||||
|
pillow
|
||||||
|
material-color-utilities
|
||||||
|
materialyoucolor
|
||||||
|
wheel
|
||||||
|
setuptools-scm
|
||||||
|
libsass
|
||||||
|
pywayland
|
||||||
|
psutil
|
||||||
|
numpy
|
||||||
|
requests
|
||||||
|
pyxdg
|
||||||
|
]))
|
||||||
|
pywal
|
||||||
|
dart-sass
|
||||||
|
imagemagick
|
||||||
];
|
];
|
||||||
|
|
||||||
fonts.packages = with pkgs; [
|
fonts.packages = with pkgs; [
|
||||||
|
@ -179,6 +217,9 @@
|
||||||
noto-fonts-cjk
|
noto-fonts-cjk
|
||||||
noto-fonts-emoji
|
noto-fonts-emoji
|
||||||
nerdfonts
|
nerdfonts
|
||||||
|
google-fonts
|
||||||
|
material-symbols
|
||||||
|
material-icons
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.gnupg.agent = {
|
programs.gnupg.agent = {
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
{ inputs, config, pkgs, ... }:
|
{ inputs, config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
moreWaita = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "MoreWaita";
|
||||||
|
src = inputs.more-waita;
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/share/icons
|
||||||
|
mv * $out/share/icons
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
../../modules/home-manager/hypr/default.nix
|
../../modules/home-manager/hypr/default.nix
|
||||||
|
../../modules/home-manager/spotify/default.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# Home Manager needs a bit of information about you and the paths it should
|
# Home Manager needs a bit of information about you and the paths it should
|
||||||
|
@ -10,6 +20,13 @@
|
||||||
home.username = "pingu";
|
home.username = "pingu";
|
||||||
home.homeDirectory = "/home/pingu";
|
home.homeDirectory = "/home/pingu";
|
||||||
|
|
||||||
|
home.pointerCursor = {
|
||||||
|
gtk.enable = true;
|
||||||
|
package = pkgs.bibata-cursors;
|
||||||
|
name = "Bibata-Modern-Classic";
|
||||||
|
size = 24;
|
||||||
|
};
|
||||||
|
|
||||||
# This value determines the Home Manager release that your configuration is
|
# This value determines the Home Manager release that your configuration is
|
||||||
# compatible with. This helps avoid breakage when a new Home Manager release
|
# compatible with. This helps avoid breakage when a new Home Manager release
|
||||||
# introduces backwards incompatible changes.
|
# introduces backwards incompatible changes.
|
||||||
|
@ -22,9 +39,31 @@
|
||||||
# The home.packages option allows you to install Nix packages into your
|
# The home.packages option allows you to install Nix packages into your
|
||||||
# environment.
|
# environment.
|
||||||
home.packages = [
|
home.packages = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
home.file = {
|
||||||
|
".local/share/fonts" = {
|
||||||
|
recursive = true;
|
||||||
|
source = "${pkgs.nerdfonts}/share/fonts/truetype/NerdFonts";
|
||||||
|
};
|
||||||
|
".fonts" = {
|
||||||
|
recursive = true;
|
||||||
|
source = "${pkgs.nerdfonts}/share/fonts/truetype/NerdFonts";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
gtk = {
|
||||||
|
enable = true;
|
||||||
|
font.name = "Rubik";
|
||||||
|
theme.name = "adw-gtk3-dark";
|
||||||
|
};
|
||||||
|
|
||||||
|
qt = {
|
||||||
|
enable = true;
|
||||||
|
platformTheme.name = "kde";
|
||||||
|
};
|
||||||
|
|
||||||
# Home Manager is pretty good at managing dotfiles. The primary way to manage
|
# Home Manager is pretty good at managing dotfiles. The primary way to manage
|
||||||
# plain files is through 'home.file'.
|
# plain files is through 'home.file'.
|
||||||
home.file = {
|
home.file = {
|
||||||
|
@ -61,7 +100,6 @@
|
||||||
XMODIFIERS = "@im=ibus";
|
XMODIFIERS = "@im=ibus";
|
||||||
GTK_IM_MODULE = "ibus";
|
GTK_IM_MODULE = "ibus";
|
||||||
QT_IM_MODULE = "ibus";
|
QT_IM_MODULE = "ibus";
|
||||||
QT_QPA_PLATFORMTHEME = "qt5ct";
|
|
||||||
WLR_NO_HARDWARE_CURSORS = "1";
|
WLR_NO_HARDWARE_CURSORS = "1";
|
||||||
GSK_RENDERER = "cairo";
|
GSK_RENDERER = "cairo";
|
||||||
|
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
version="1.0"
|
|
||||||
width="20"
|
|
||||||
height="19.999941"
|
|
||||||
id="svg2424"
|
|
||||||
sodipodi:docname="archlinux-logo-black-scalable.f931920e6cdb.svg"
|
|
||||||
viewBox="0 0 166.18749 166.187"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="16.650008"
|
|
||||||
inkscape:cx="13.093087"
|
|
||||||
inkscape:cy="16.366359"
|
|
||||||
inkscape:window-width="1340"
|
|
||||||
inkscape:window-height="768"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="g2424" />
|
|
||||||
<defs
|
|
||||||
id="defs2426">
|
|
||||||
<linearGradient
|
|
||||||
x1="112.49854"
|
|
||||||
y1="6.1372099"
|
|
||||||
x2="112.49853"
|
|
||||||
y2="129.3468"
|
|
||||||
id="path1082_2_"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(287,-83)">
|
|
||||||
<stop
|
|
||||||
id="stop193"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop195"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0.27450982"
|
|
||||||
offset="1" />
|
|
||||||
<midPointStop
|
|
||||||
offset="0"
|
|
||||||
style="stop-color:#FFFFFF"
|
|
||||||
id="midPointStop197" />
|
|
||||||
<midPointStop
|
|
||||||
offset="0.5"
|
|
||||||
style="stop-color:#FFFFFF"
|
|
||||||
id="midPointStop199" />
|
|
||||||
<midPointStop
|
|
||||||
offset="1"
|
|
||||||
style="stop-color:#000000"
|
|
||||||
id="midPointStop201" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
x1="541.33502"
|
|
||||||
y1="104.50665"
|
|
||||||
x2="606.91248"
|
|
||||||
y2="303.14029"
|
|
||||||
id="linearGradient2544"
|
|
||||||
xlink:href="#path1082_2_"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(-0.3937741,0,0,0.393752,357.51969,122.00151)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient3388">
|
|
||||||
<stop
|
|
||||||
id="stop3390"
|
|
||||||
style="stop-color:#000000;stop-opacity:0"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop3392"
|
|
||||||
style="stop-color:#000000;stop-opacity:0.37113401"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
x1="490.72305"
|
|
||||||
y1="237.72447"
|
|
||||||
x2="490.72305"
|
|
||||||
y2="183.9644"
|
|
||||||
id="linearGradient4416"
|
|
||||||
xlink:href="#linearGradient3388"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.749107,0,0,0.749107,-35.459862,91.44108)" />
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
transform="translate(-57.527313,-146.42741)"
|
|
||||||
id="layer1">
|
|
||||||
<g
|
|
||||||
transform="matrix(0.8746356,0,0,0.8746356,14.730518,23.408954)"
|
|
||||||
id="g2424"
|
|
||||||
style="fill:#000000">
|
|
||||||
<g
|
|
||||||
transform="matrix(0.6378586,0,0,0.6378586,36.486487,2.17139)"
|
|
||||||
id="g2809"
|
|
||||||
style="fill:#000000;fill-opacity:1" />
|
|
||||||
<path
|
|
||||||
d="m 143.91698,140.65081 c -8.45709,20.73453 -13.55799,34.29734 -22.97385,54.41552 5.7731,6.11948 12.85931,13.24593 24.36729,21.29458 -12.37221,-5.09109 -20.81157,-10.20242 -27.11844,-15.50646 -12.0505,25.14523 -30.930177,60.96349 -69.243121,129.80406 30.112687,-17.38458 53.455511,-28.10236 75.209891,-32.19198 -0.93414,-4.01773 -1.46524,-8.36369 -1.42916,-12.89823 l 0.0357,-0.96469 c 0.47781,-19.2924 10.51371,-34.12825 22.40218,-33.12093 11.88848,1.00732 21.12927,17.4729 20.65146,36.76531 -0.0899,3.63022 -0.49934,7.12245 -1.21479,10.36146 21.51819,4.20934 44.61141,14.89968 74.31666,32.04906 -5.85729,-10.78369 -11.08544,-20.5044 -16.07812,-29.7624 -7.86429,-6.09535 -16.06714,-14.02847 -32.79938,-22.61656 11.50078,2.98839 19.73519,6.43619 26.15375,10.29 -50.76203,-94.51003 -54.87267,-107.06846 -72.2801,-147.91874 z"
|
|
||||||
id="path2518"
|
|
||||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.14333" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.1 KiB |
|
@ -1,318 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
id="SVGRoot"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
version="1.1"
|
|
||||||
viewBox="0 0 17.921003 17.921002"
|
|
||||||
sodipodi:docname="cachyos-symbolic.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview30"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:zoom="32"
|
|
||||||
inkscape:cx="10.671875"
|
|
||||||
inkscape:cy="11.234375"
|
|
||||||
inkscape:window-width="1687"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="SVGRoot" />
|
|
||||||
<defs
|
|
||||||
id="defs6">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient939"
|
|
||||||
x1="237.19"
|
|
||||||
x2="237.07001"
|
|
||||||
y1="296.20001"
|
|
||||||
y2="304.07999"
|
|
||||||
gradientTransform="matrix(0.04476,0,0,0.044679,-8.5042241,-4.351186)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient937" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient937">
|
|
||||||
<stop
|
|
||||||
stop-color="#001313"
|
|
||||||
offset="0"
|
|
||||||
id="stop1" />
|
|
||||||
<stop
|
|
||||||
stop-color="#001313"
|
|
||||||
stop-opacity="0"
|
|
||||||
offset="1"
|
|
||||||
id="stop2" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient5185"
|
|
||||||
x1="994.81"
|
|
||||||
x2="982.34003"
|
|
||||||
y1="1533.3"
|
|
||||||
y2="1556.8"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4353">
|
|
||||||
<stop
|
|
||||||
stop-color="#020202"
|
|
||||||
offset="0"
|
|
||||||
id="stop3" />
|
|
||||||
<stop
|
|
||||||
stop-color="#020202"
|
|
||||||
stop-opacity="0"
|
|
||||||
offset="1"
|
|
||||||
id="stop4" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient9102"
|
|
||||||
x1="1022.5"
|
|
||||||
x2="1018.6"
|
|
||||||
y1="1582.4"
|
|
||||||
y2="1575.6"
|
|
||||||
gradientTransform="matrix(0.086381,0,0,0.081808,-79.103924,-124.69099)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient11890"
|
|
||||||
x1="940.42999"
|
|
||||||
x2="930.59003"
|
|
||||||
y1="1612.5"
|
|
||||||
y2="1594.5"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient11670"
|
|
||||||
x1="965.59998"
|
|
||||||
x2="951.65997"
|
|
||||||
y1="1571.4"
|
|
||||||
y2="1571.3"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient13770"
|
|
||||||
x1="946.22998"
|
|
||||||
x2="961.37"
|
|
||||||
y1="1655.9"
|
|
||||||
y2="1655.8"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient2816"
|
|
||||||
x1="366.14999"
|
|
||||||
x2="350.92001"
|
|
||||||
y1="427.32001"
|
|
||||||
y2="419.64001"
|
|
||||||
gradientTransform="matrix(0.04476,0,0,0.044679,-10.832924,-4.155886)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient937" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient12421"
|
|
||||||
x1="936.34003"
|
|
||||||
x2="933.38"
|
|
||||||
y1="1628.8"
|
|
||||||
y2="1623"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient13391"
|
|
||||||
x1="950.33002"
|
|
||||||
x2="941.96997"
|
|
||||||
y1="1618.6"
|
|
||||||
y2="1645.8"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-76.242924,-126.39098)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient13599"
|
|
||||||
x1="1008.2"
|
|
||||||
x2="1015.7"
|
|
||||||
y1="1681.3"
|
|
||||||
y2="1668.4"
|
|
||||||
gradientTransform="matrix(0.084141,0,0,0.083989,-77.884838,-124.43841)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient4353" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient18175"
|
|
||||||
x1="1148.3"
|
|
||||||
x2="1145.4"
|
|
||||||
y1="1585.5"
|
|
||||||
y2="1630"
|
|
||||||
gradientTransform="matrix(0.34992,0,0,0.34992,-282.87,-491.67)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient18299" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient18299">
|
|
||||||
<stop
|
|
||||||
stop-color="#008066"
|
|
||||||
stop-opacity="0"
|
|
||||||
offset="0"
|
|
||||||
id="stop5" />
|
|
||||||
<stop
|
|
||||||
stop-color="#0fc"
|
|
||||||
offset="1"
|
|
||||||
id="stop6" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient18632"
|
|
||||||
x1="1148.3"
|
|
||||||
x2="1145.4"
|
|
||||||
y1="1585.5"
|
|
||||||
y2="1630"
|
|
||||||
gradientTransform="matrix(0.26565,0,0,0.26565,-211.15,-375.49)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient18299" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient18659"
|
|
||||||
x1="1148.3"
|
|
||||||
x2="1145.4"
|
|
||||||
y1="1585.5"
|
|
||||||
y2="1630"
|
|
||||||
gradientTransform="matrix(0.13679,0,0,0.13679,-53.624,-195.03)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient18299" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient3254"
|
|
||||||
x1="348.04999"
|
|
||||||
x2="361.20999"
|
|
||||||
y1="194.78"
|
|
||||||
y2="187.24001"
|
|
||||||
gradientTransform="matrix(0.04476,0,0,0.044679,-10.832924,-4.155886)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
xlink:href="#linearGradient937" />
|
|
||||||
</defs>
|
|
||||||
<circle
|
|
||||||
cx="87.449997"
|
|
||||||
cy="87.449997"
|
|
||||||
r="87.449997"
|
|
||||||
opacity="0"
|
|
||||||
stroke-width="0.27971"
|
|
||||||
id="circle6" />
|
|
||||||
<path
|
|
||||||
d="m 4.0610759,2.168314 6.5887001,3.6879 2.1127,-3.6528 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path7"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m 6.1499759,12.423014 -1.9125,3.7456 h 8.5747001 l 2.1664,-3.7456 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path8"
|
|
||||||
style="fill:#7a7a7a;fill-opacity:0.68506807" />
|
|
||||||
<path
|
|
||||||
d="m 4.0610759,2.168314 6.5887001,3.6879 H 6.1237759 l -1.8859,3.2605 1.9121,3.306 -1.9125,3.7456 -4.13689997,-7.1525 3.96049997,-6.8475"
|
|
||||||
fill="#00ccff"
|
|
||||||
id="path9"
|
|
||||||
style="fill:#7a7a7a;fill-opacity:0.69262218" />
|
|
||||||
<path
|
|
||||||
d="m 6.0909759,5.821714 6.7111001,-3.7832 -2.169,3.5579 z"
|
|
||||||
fill="url(#linearGradient9102)"
|
|
||||||
id="path12"
|
|
||||||
style="fill:url(#linearGradient9102)" />
|
|
||||||
<path
|
|
||||||
d="m 6.1236759,5.856214 6.6388001,-3.6528 -2.1127,3.6528 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path13"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m 0.10057593,9.015814 6.02309997,-3.1596 -1.8859,3.2605 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path14"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 6.1236759,5.856214 -2.0626,-3.6879 0.17673,6.9484 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path16"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 4.2378759,9.116714 -3.1586,1.5811 3.1583,5.4705 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path19"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 1.0792259,10.698014 5.0708,1.7248 -1.9121,-3.306 z"
|
|
||||||
fill="#00aa88"
|
|
||||||
id="path23"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<g
|
|
||||||
transform="matrix(0.14699,0,0,0.14672,-0.75949407,-0.14715599)"
|
|
||||||
id="g26"
|
|
||||||
style="fill:#1a1a1a">
|
|
||||||
<circle
|
|
||||||
cx="117.95"
|
|
||||||
cy="75.441002"
|
|
||||||
r="9.6893997"
|
|
||||||
fill="#00ccff"
|
|
||||||
id="circle25"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<circle
|
|
||||||
cx="118.08"
|
|
||||||
cy="75.341003"
|
|
||||||
r="9.6893997"
|
|
||||||
fill="url(#linearGradient18175)"
|
|
||||||
id="circle26"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
transform="matrix(0.14699,0,0,0.14672,-0.11248407,-0.47061599)"
|
|
||||||
id="g28"
|
|
||||||
style="fill:#1a1a1a">
|
|
||||||
<circle
|
|
||||||
cx="93.138"
|
|
||||||
cy="55.044998"
|
|
||||||
r="7.3558998"
|
|
||||||
fill="#00ccff"
|
|
||||||
id="circle27"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<circle
|
|
||||||
cx="93.238998"
|
|
||||||
cy="54.969002"
|
|
||||||
r="7.3558998"
|
|
||||||
fill="url(#linearGradient18632)"
|
|
||||||
id="circle28"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
transform="matrix(0.14699,0,0,0.14672,-0.08243407,-0.04714599)"
|
|
||||||
id="g30"
|
|
||||||
style="fill:#000000">
|
|
||||||
<circle
|
|
||||||
cx="103.06"
|
|
||||||
cy="26.657"
|
|
||||||
r="3.7876999"
|
|
||||||
fill="#00ccff"
|
|
||||||
id="circle29"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<circle
|
|
||||||
cx="103.11"
|
|
||||||
cy="26.618"
|
|
||||||
r="3.7876999"
|
|
||||||
fill="url(#linearGradient18659)"
|
|
||||||
id="circle30"
|
|
||||||
style="fill:#000000" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
d="m 6.1236759,5.856214 -2.0626,-3.6879 0.52544,-0.0074 1.9387,3.4465 z"
|
|
||||||
fill="url(#linearGradient3254)"
|
|
||||||
id="path30"
|
|
||||||
style="fill:url(#linearGradient3254)" />
|
|
||||||
<path
|
|
||||||
d="M 12.808567,16.168211 6.1524738,12.428132 4.2457352,16.165572 Z"
|
|
||||||
fill="#00ccff"
|
|
||||||
id="path17"
|
|
||||||
style="fill:#1a1a1a;fill-opacity:1"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 9 KiB |
|
@ -1,10 +0,0 @@
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g clip-path="url(#clip0_26_18)">
|
|
||||||
<path d="M3 7.06015V4.74436C4.12 4.69624 4.90412 4.62466 5.35237 4.52962C6.06664 4.37804 6.64769 4.07549 7.09553 3.62195C7.40205 3.31158 7.63448 2.89754 7.7928 2.37985C7.8839 2.06947 7.92946 1.8387 7.92946 1.68752H10.9001V19H7.25588V7.06015H3ZM12.4228 5.65444V4.52962L15.0783 1H16.1879V4.64211H17V5.65444H16.1879V7.05594H15.0084V5.65444H12.4228ZM14.9823 2.53985L13.4031 4.64511H15.0102V2.53985H14.9823Z" fill="black"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_26_18">
|
|
||||||
<rect width="20" height="20" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 660 B |
|
@ -1,65 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
fill="#000000"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 380.95238 380.95238"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
sodipodi:docname="crosshair-symbolic.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:zoom="35"
|
|
||||||
inkscape:cx="10.371429"
|
|
||||||
inkscape:cy="7.9571429"
|
|
||||||
inkscape:window-width="1430"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg1" />
|
|
||||||
<title
|
|
||||||
id="title1">ionicons-v5_logos</title>
|
|
||||||
<metadata
|
|
||||||
id="metadata1">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:title>ionicons-v5_logos</dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;stroke-width:23.0377"
|
|
||||||
id="rect1"
|
|
||||||
width="380.95239"
|
|
||||||
height="57.142857"
|
|
||||||
x="-3.5527137e-15"
|
|
||||||
y="161.90475" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;stroke-width:23.0451;stroke-dasharray:none"
|
|
||||||
id="rect1-5"
|
|
||||||
width="57.142857"
|
|
||||||
height="380.95239"
|
|
||||||
x="161.90475"
|
|
||||||
y="0" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
|
@ -1,91 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="-30.5 0 317.00242 317.00243"
|
|
||||||
version="1.1"
|
|
||||||
preserveAspectRatio="xMidYMid"
|
|
||||||
id="svg12"
|
|
||||||
sodipodi:docname="debian-symbolic.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs12" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview12"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="17.5"
|
|
||||||
inkscape:cx="13.742857"
|
|
||||||
inkscape:cy="12.628571"
|
|
||||||
inkscape:window-width="1295"
|
|
||||||
inkscape:window-height="867"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg12" />
|
|
||||||
<g
|
|
||||||
fill="#A80030"
|
|
||||||
id="g12"
|
|
||||||
style="fill:#1a1a1a"
|
|
||||||
transform="translate(0.05835351,0.0538775)">
|
|
||||||
<path
|
|
||||||
d="m 152.79662,167.42537 c -5.25095,0.0731 0.9935,2.70583 7.84865,3.76069 1.8935,-1.47856 3.61167,-2.97466 5.14283,-4.42984 -4.26913,1.04609 -8.61424,1.06947 -12.99148,0.66915"
|
|
||||||
id="path1"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 180.9799,160.40073 c 3.12661,-4.31588 5.40581,-9.04086 6.20938,-13.92654 -0.70129,3.4831 -2.59187,6.4899 -4.3714,9.66326 -9.81521,6.18016 -0.92337,-3.67011 -0.006,-7.41328 -10.55448,13.2837 -1.44934,7.96554 -1.83213,11.67656"
|
|
||||||
id="path2"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 191.38244,133.33075 c 0.63409,-9.45579 -1.86135,-6.46652 -2.69999,-2.85777 0.9789,0.50844 1.75324,6.66522 2.69999,2.85777"
|
|
||||||
id="path3"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 132.88569,4.0879643 c 2.80225,0.5025946 6.05451,0.8883068 5.59867,1.5574589 3.06524,-0.6720742 3.76069,-1.2915513 -5.59867,-1.5574589"
|
|
||||||
id="path4"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 138.48436,5.6454232 -1.98116,0.4090887 1.84382,-0.1636355 0.13734,-0.2454532"
|
|
||||||
id="path5"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 225.86569,136.91612 c 0.31266,8.49151 -2.48375,12.61162 -5.00549,19.90509 l -4.53796,2.26752 c -3.71394,7.21165 0.35941,4.57887 -2.29967,10.31487 -5.79737,5.15452 -17.59373,16.12979 -21.36903,17.13205 -2.75551,-0.0614 1.86719,-3.25225 2.47206,-4.50289 -7.76099,5.32984 -6.22691,8.0006 -18.09633,11.23824 l -0.34772,-0.77142 c -29.27322,13.77168 -69.93663,-13.52038 -69.40189,-50.75913 -0.31266,2.36394 -0.88831,1.77369 -1.537,2.7292 -1.51071,-19.15996 8.848,-38.40465 26.31901,-46.262078 17.08821,-8.459369 37.12187,-4.987959 49.36238,6.419768 -6.72366,-8.807092 -20.1067,-18.14308 -35.96765,-17.269383 -15.53661,0.245453 -30.07094,10.1191 -34.92156,20.837223 -7.9597,5.01133 -8.88307,19.31775 -12.351558,21.93592 -4.666532,34.29623 8.777878,49.11401 31.520278,66.54411 3.57953,2.41362 1.00811,2.77888 1.49318,4.61685 -7.55646,-3.53861 -14.4759,-8.88014 -20.16515,-15.41972 3.01849,4.41816 6.27659,8.71359 10.48728,12.08857 -7.12399,-2.41362 -16.64115,-17.26354 -19.42003,-17.8684 12.28143,21.98851 49.827,38.56245 69.48663,30.33976 -9.09638,0.33604 -20.65313,0.18702 -30.8745,-3.59121 -4.29251,-2.20908 -10.13079,-6.78503 -9.08761,-7.64119 26.83037,10.02267 54.54612,7.59151 77.7619,-11.0191 5.90549,-4.59932 12.3574,-12.4246 14.22168,-12.53272 -2.8081,4.22238 0.47921,2.03083 -1.67727,5.75938 5.88504,-9.49085 -2.5568,-3.86296 6.08374,-16.38984 l 3.19089,4.39478 c -1.18636,-7.87788 9.78306,-17.44471 8.66975,-29.90438 2.5159,-3.81037 2.80811,4.09965 0.13734,12.86584 3.70517,-9.72462 0.97597,-11.28793 1.92856,-19.31191 1.02857,2.69707 2.37856,5.56361 3.07109,8.4097 -2.41362,-9.39735 2.47791,-15.82589 3.68764,-21.28722 -1.1922,-0.52889 -3.72563,4.15517 -4.3042,-6.94574 0.0847,-4.8214 1.34123,-2.52759 1.82629,-3.71394 -0.94675,-0.54351 -3.4305,-4.23991 -4.9412,-11.328836 1.09577,-1.665575 2.9279,4.318806 4.41815,4.564256 -0.95843,-5.636653 -2.6094,-9.935006 -2.67661,-14.259657 -4.35387,-9.0993 -1.53992,1.212656 -5.07269,-3.906796 -4.63439,-14.45544 3.84543,-3.354527 4.41815,-9.923322 7.02464,10.177541 11.03079,25.950835 12.86876,32.484565 -1.40259,-7.965545 -3.67011,-15.68271 -6.4373,-23.148578 2.13311,0.897073 -3.43634,-16.389844 2.77304,-4.941206 -6.63308,-24.40506 -28.38783,-47.208829 -48.40103,-57.909419 2.44869,2.241221 5.54023,5.055166 4.42984,5.496398 -9.95254,-5.925941 -8.20223,-6.387627 -9.62819,-8.891834 -8.10872,-3.299008 -8.64054,0.265908 -14.01129,0.0058 -15.28238,-8.105755 -18.22782,-7.243747 -32.2917,-12.32229 l 0.63993,2.9892691 c -10.12494,-3.3720592 -11.79636,1.279863 -22.73948,0.011688 -0.66623,-0.520127 3.50647,-1.8818077 6.93989,-2.3814803 -9.7889,1.2915513 -9.33014,-1.9285607 -18.90866,0.3564916 2.36103,-1.656809 4.85647,-2.7525822 7.37529,-4.1610159 -7.98308,0.4850622 -19.05769,4.6460781 -15.63888,0.8620082 C 96.316085,8.9298206 73.190888,17.085295 60.214012,29.25276 L 59.804924,26.526476 C 53.858528,33.665073 33.874548,47.845838 32.282025,57.091242 l -1.589602,0.371102 C 27.59796,62.7016 25.596347,68.63923 23.141816,74.030433 19.09476,80.926499 17.21003,76.683665 17.785676,77.764828 9.8259803,93.903375 5.8724309,107.46466 2.4565407,118.58603 4.8906182,122.224 2.514982,140.48688 3.4354314,155.10304 -0.56194899,227.28965 54.098137,297.37822 113.84553,313.5606 c 8.75742,3.13245 21.78105,3.01264 32.85859,3.33407 -13.07039,-3.73732 -14.75934,-1.98116 -27.49076,-6.41977 -9.18404,-4.32465 -11.19734,-9.26293 -17.70185,-14.90836 l 2.57434,4.54965 c -12.757724,-4.51458 -7.419118,-5.58698 -17.798281,-8.8743 l 2.74966,-3.59121 c -4.134717,-0.31266 -10.951887,-6.96912 -12.816162,-10.65384 l -4.523352,0.17825 C 66.26268,270.46895 63.366917,265.63586 63.577306,261.8927 l -1.461031,2.60356 c -1.656809,-2.84317 -19.995669,-25.15019 -10.481436,-19.95768 -1.767847,-1.6159 -4.117185,-2.62986 -6.665222,-7.2584 l 1.937326,-2.21493 c -4.57887,-5.89087 -8.427226,-13.44148 -8.135019,-15.95737 2.442843,3.299 4.137639,3.91556 5.814902,4.47952 -11.562598,-28.68881 -12.211295,-1.58084 -20.968714,-29.20309 l 1.852587,-0.14902 c -1.420122,-2.13895 -2.28213,-4.46199 -3.424657,-6.7412 l 0.80649,-8.03567 c -8.324954,-9.62527 -2.328884,-40.9264 -1.127916,-58.09351 0.832787,-6.9808 6.948662,-14.41161 11.600585,-26.064789 l -2.8344,-0.487985 c 5.417502,-9.449947 30.932945,-37.951737 42.749763,-36.484862 5.724319,-7.191194 -1.136683,-0.0263 -2.255832,-1.837977 12.573631,-13.011941 16.527181,-9.192806 25.012848,-11.533378 9.1519,-5.432112 -7.854502,2.118495 -3.51524,-2.071741 15.82004,-4.041212 11.21195,-9.186962 31.85047,-11.23825 2.17694,1.238955 -5.05224,1.913951 -6.86684,3.521085 13.18142,-6.448991 41.71243,-4.982116 60.24414,3.579525 21.50346,10.04897 45.66306,39.75465 46.61565,67.704172 l 1.08409,0.2922 c -0.54935,11.10968 1.70064,23.95799 -2.19739,35.76019 l 2.65323,-5.58698"
|
|
||||||
id="path6"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 95.483297,174.6341 -0.736359,3.68179 c 3.450955,4.68699 6.188932,9.76553 10.595392,13.4298 -3.17043,-6.18893 -5.525615,-8.74573 -9.859033,-17.11159"
|
|
||||||
id="path7"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 103.64169,174.31267 c -1.82629,-2.01915 -2.90745,-4.4503 -4.117181,-6.87269 1.157141,4.25744 3.526931,7.91586 5.733081,11.63565 l -1.6159,-4.76296"
|
|
||||||
id="path8"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 248.00323,142.93557 -0.77142,1.9344 c -1.41428,10.04605 -4.46784,19.98691 -9.14898,29.20309 5.17205,-9.72462 8.51781,-20.36093 9.9204,-31.13749"
|
|
||||||
id="path9"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="M 133.92302,1.5691471 C 137.47332,0.26882968 142.65122,0.85616408 146.41775,0 141.50869,0.4120107 136.623,0.65746388 131.79868,1.279863 l 2.12434,0.2892841"
|
|
||||||
id="path10"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="m 9.2824769,67.847351 c 0.8181771,7.573984 -5.6980203,10.513578 1.4434981,5.519774 3.827901,-8.623004 -1.4960952,-2.38148 -1.4434981,-5.519774"
|
|
||||||
id="path11"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
<path
|
|
||||||
d="M 0.89031567,102.9004 C 2.5354364,97.85108 2.8334867,94.81798 3.46173,91.895919 -1.084998,97.707899 1.3695338,98.946854 0.89031567,102.9004"
|
|
||||||
id="path12"
|
|
||||||
style="fill:#1a1a1a" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 8.3 KiB |
|
@ -1,96 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
width="20mm"
|
|
||||||
height="20mm"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
sodipodi:docname="EndeavourOS Logo.svg"
|
|
||||||
version="1.1"
|
|
||||||
viewBox="0 0 48.231007 48.231007"
|
|
||||||
id="svg8"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="cvfa"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:current-layer="g3"
|
|
||||||
inkscape:cx="52.728754"
|
|
||||||
inkscape:cy="60.739468"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:window-width="1316"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:zoom="4.930896"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1" />
|
|
||||||
<title
|
|
||||||
id="title1">EndeavourOS Logo</title>
|
|
||||||
<metadata
|
|
||||||
id="metadata1">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title>EndeavourOS Logo</dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
transform="translate(-66.790568,-123.01834)"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
id="g8">
|
|
||||||
<g
|
|
||||||
transform="translate(76.2,-12.7)"
|
|
||||||
id="g7">
|
|
||||||
<g
|
|
||||||
transform="matrix(1.47,0,0,1.47,-519,105)"
|
|
||||||
id="g6">
|
|
||||||
<g
|
|
||||||
transform="matrix(0.963,0,0,0.983,13.5,0.76)"
|
|
||||||
id="g5">
|
|
||||||
<g
|
|
||||||
transform="matrix(0.678,0,0,0.678,452,49.2)"
|
|
||||||
id="g3">
|
|
||||||
<g
|
|
||||||
id="g9"
|
|
||||||
transform="translate(6.8384014e-4,3.6851185)">
|
|
||||||
<path
|
|
||||||
d="m -127,-42.3 c 4.57,6.45 23.8,31.4 10.7,36.6 -6.12,2.81 -34,-1.65 -33.6,-0.921 -2,3.28 -3.59,5.92 -3.59,5.92 0,0 21.5,0.967 38.1,-1.27 23.7,-3.18 -4.88,-33.5 -11.6,-40.3 z"
|
|
||||||
style="fill:#333333;fill-opacity:0.7;stroke-width:0.585"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path1" />
|
|
||||||
<path
|
|
||||||
d="m -127,-42.3 c -1.52,0.209 -29.4,34.5 -29.4,34.5 0,0 2.01,0.57 6.58,1.23 1.48,-1.15 22.3,-36.2 22.9,-35.7 -0.0107,-0.0141 -0.028,-0.0193 -0.0522,-0.016 z"
|
|
||||||
style="fill:#333333;fill-opacity:0.7;stroke-width:0.585"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path2" />
|
|
||||||
<path
|
|
||||||
d="m -127,-42.3 c -0.96,-0.156 -22.9,35.7 -22.9,35.7 0,0 19.9,2.1 28.1,1.96 23.1,-0.39 0.176,-30.6 -5.16,-37.7 -0.007,-0.007 -0.0151,-0.0108 -0.0248,-0.0124 z"
|
|
||||||
style="fill:#1a1a1a;stroke-width:0.585"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path3" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.3 KiB |
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 448.00288 448.00288"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
sodipodi:docname="Fa-Team-Fontawesome-Brands-FontAwesome-Brands-Fedora.svg"
|
|
||||||
width="19.999744"
|
|
||||||
height="19.999744"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="13.671875"
|
|
||||||
inkscape:cx="19.2"
|
|
||||||
inkscape:cy="17.664"
|
|
||||||
inkscape:window-width="1313"
|
|
||||||
inkscape:window-height="908"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg1" />
|
|
||||||
<!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
|
|
||||||
<path
|
|
||||||
d="M 0.0413,223.8 C 0.1219,100.2 100.3,0 224,0 347.7,0 448,100.3 448,224 448,347.7 347.8,447.9 224.1,448 H 50.93 C 22.84,448 0.0832,425.3 0.0416,397.2 H 0 V 223.8 Z M 342.6,160.7 c 0,-39.7 -35.6,-68.5 -73.2,-68.5 -34.9,0 -65.8,26.3 -70.1,59.9 -0.2,3.8 -0.4,5 -0.4,8.5 -0.1,21.1 0,42.8 -0.8,64.4 0.9,26.1 1,52.1 0,76.6 0,27.1 -19.4,45.5 -44.7,45.5 -25.3,0 -45.8,-20.2 -45.8,-45.5 0.5,-27.7 22.6,-45.3 48.5,-46.1 h 0.2 l 26.3,-0.2 V 218 l -26.3,0.2 c -47.1,-0.4 -84.58,36.5 -85.94,83.4 0,45.6 37.54,82.9 83.04,82.9 43,0 78.7,-33.6 82.6,-75.6 l 0.2,-53.5 32.6,-0.3 c 25.3,0.2 25,-37.8 -0.2,-37.3 l -32.4,0.3 c 0,-6.4 0.1,-12.8 0.1,-19.2 0.1,-12.7 0.1,-25.4 -0.1,-38.2 0.1,-16.5 15.8,-31.2 33.2,-31.2 17.5,0 35.9,8.7 35.9,31.2 0,3.2 -0.1,5.1 -0.3,6.3 -1.9,10.5 5.2,20.4 15.7,21.9 10.6,1.5 20.2,-6.1 21.2,-16.6 0.6,-4.2 0.7,-7.9 0.7,-11.6 z"
|
|
||||||
id="path1" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
|
@ -1,52 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
role="img"
|
|
||||||
viewBox="0 0 24.000009 24.000009"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
sodipodi:docname="flatpak_logo_icon_248537.svg"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="18.229167"
|
|
||||||
inkscape:cx="11.190857"
|
|
||||||
inkscape:cy="11.766857"
|
|
||||||
inkscape:window-width="1164"
|
|
||||||
inkscape:window-height="648"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg1" />
|
|
||||||
<title
|
|
||||||
id="title1">Flatpak</title>
|
|
||||||
<path
|
|
||||||
d="m 12.000004,7.3336774e-5 c -0.556,0 -1.110999,0.143999993226 -1.609999,0.431999993226 L 2.7870046,4.8220733 a 3.217,3.217 0 0 0 -1.61,2.788 v 8.7799997 c 0,1.151 0.612,2.212 1.61,2.788 l 7.6030004,4.39 a 3.217,3.217 0 0 0 3.219999,0 l 7.603,-4.39 a 3.217,3.217 0 0 0 1.61,-2.788 V 7.6100733 a 3.217,3.217 0 0 0 -1.61,-2.788 l -7.603,-4.38999997 a 3.218,3.218 0 0 0 -1.61,-0.431999993226 z m 0,2.357999963226 c 0.15,0 0.299,0.039 0.431,0.115 l 7.604,4.39 c 0.132,0.077 0.24,0.187 0.315,0.316 l -8.35,4.8209997 v 9.642 a 0.863,0.863 0 0 1 -0.431,-0.116 l -7.6039994,-4.39 a 0.866,0.866 0 0 1 -0.431,-0.746 V 7.6100733 c 0,-0.153 0.041,-0.302 0.116,-0.43 l 8.3499994,4.8199997 z"
|
|
||||||
id="path1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata1">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:title>Flatpak</dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
|
@ -1,40 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
width="98"
|
|
||||||
height="96"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
sodipodi:docname="github-symbolic.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:zoom="2.4081633"
|
|
||||||
inkscape:cx="32.597458"
|
|
||||||
inkscape:cy="46.716102"
|
|
||||||
inkscape:window-width="1339"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg1" />
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
|
||||||
fill="#fff"
|
|
||||||
id="path1"
|
|
||||||
style="fill:#000000" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 16 KiB |
|
@ -1,77 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
height="20"
|
|
||||||
width="19.999744"
|
|
||||||
version="1.1"
|
|
||||||
id="svg8"
|
|
||||||
sodipodi:docname="nixos-symbolic.svg"
|
|
||||||
viewBox="0 0 512.00001 512.00656"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview8"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="27.34375"
|
|
||||||
inkscape:cx="8.832"
|
|
||||||
inkscape:cy="15.817143"
|
|
||||||
inkscape:window-width="1075"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg8" />
|
|
||||||
<g
|
|
||||||
fill-rule="evenodd"
|
|
||||||
transform="matrix(1.2756532,0,0,-1.2756532,9.0810546e-6,478.03773)"
|
|
||||||
id="g8"
|
|
||||||
style="fill:#000000">
|
|
||||||
<path
|
|
||||||
d="m 122.453,169.761 97.758,-169.34 -44.926,-0.422 -26.101,45.496 -26.286,-45.25 -22.32,0.008 -11.433,19.75 37.449,64.394 -26.582,46.258 z"
|
|
||||||
fill="#5277c3"
|
|
||||||
id="path1"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="M 157.738,239.515 59.961,70.183 37.133,108.882 63.484,154.229 11.152,154.366 0,173.702 l 11.391,19.777 74.488,-0.234 26.769,46.152 z"
|
|
||||||
fill="#7ebae4"
|
|
||||||
id="path2"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="M 165.238,104.155 360.77,104.143 338.672,65.026 286.223,65.171 312.27,19.784 301.102,0.456 278.277,0.429 241.238,65.058 187.883,65.167 Z"
|
|
||||||
fill="#7ebae4"
|
|
||||||
id="path3"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m 279.043,178.35 -97.758,169.34 44.926,0.422 26.101,-45.496 26.286,45.254 22.32,-0.008 11.434,-19.754 -37.45,-64.39 26.582,-46.262 z"
|
|
||||||
fill="#7ebae4"
|
|
||||||
id="path4"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<g
|
|
||||||
fill="#5277c3"
|
|
||||||
id="g7"
|
|
||||||
style="fill:#000000">
|
|
||||||
<path
|
|
||||||
d="m 122.453,169.761 97.758,-169.34 -44.926,-0.422 -26.101,45.496 -26.286,-45.25 -22.32,0.008 -11.433,19.75 37.449,64.394 -26.582,46.258 z"
|
|
||||||
id="path5"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m 236,244.386 -195.535,0.011 22.101,39.118 52.45,-0.149 -26.047,45.391 11.168,19.328 22.82,0.023 37.043,-64.625 53.352,-0.109 z"
|
|
||||||
id="path6"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m 243.625,108.636 97.777,169.328 22.825,-38.696 -26.348,-45.351 52.332,-0.137 11.152,-19.336 -11.39,-19.777 -74.489,0.238 -26.769,-46.152 z"
|
|
||||||
id="path7"
|
|
||||||
style="fill:#000000" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -1,60 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20.000149"
|
|
||||||
viewBox="0 0 853.78869 853.79504"
|
|
||||||
fill="none"
|
|
||||||
version="1.1"
|
|
||||||
id="svg5"
|
|
||||||
sodipodi:docname="ollama-symbolic.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs5" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview5"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#eeeeee"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
showguides="false"
|
|
||||||
inkscape:zoom="23.606557"
|
|
||||||
inkscape:cx="13.004861"
|
|
||||||
inkscape:cy="9.8065973"
|
|
||||||
inkscape:window-width="1374"
|
|
||||||
inkscape:window-height="848"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg5" />
|
|
||||||
<g
|
|
||||||
id="g5"
|
|
||||||
transform="translate(103.8943,1.5259608e-5)">
|
|
||||||
<path
|
|
||||||
d="M 140.629,0.23994313 C 132.66,1.5272641 123.097,5.6956941 116.354,10.845014 c -20.413,15.5091 -36.2287,48.4278 -42.9105,89.437996 -2.5133,15.509 -4.2297,37.026 -4.2297,53.455 0,19.371 2.2681,44.136 5.5171,61.239 0.7356,3.801 1.1034,7.173 0.7969,7.418 -0.2452,0.245 -3.249,2.697 -6.6206,5.394 -11.5245,9.195 -24.7043,23.356 -33.7768,36.291 -17.4095,24.704 -28.6888898,52.78 -33.4090698,83.185 -1.83902799,12.015 -2.32943599,36.29 -0.85821199,48.305 3.24895199,27.708 11.58588179,51.125 25.86898179,72.581 l 4.6589,6.927 -1.3486,2.268 c -9.563,16.061 -17.716,39.294 -21.5166498,61.607 -3.00375,17.655 -3.37156,22.375 -3.37156,46.037 0,23.847 0.30651,28.567 3.12635,45.057 3.3715598,19.739 10.2372598,40.642 17.8998598,54.558 2.5134,4.536 8.6435,13.976 9.3791,14.467 0.2452,0.122 -0.4904,2.39 -1.6551,5.026 -8.8274,19.31 -16.3674,44.995 -19.4938,66.635 -2.2068,14.834 -2.5133,19.616 -2.5133,35.248 0,19.922 1.1034,29.608 5.2719,45.485 l 0.613,2.329 H 44.019 70.3172 l -1.7165,-3.249 c -10.605,-19.616 -11.5858,-56.029 -2.452,-92.38 4.1685,-16.797 8.8887,-29.118 17.716,-46.099 l 5.2719,-10.298 v -6.314 c 0,-5.885 -0.1226,-6.559 -2.0229,-10.421 -1.4713,-2.943 -3.4329,-5.456 -6.9271,-8.889 -5.9462,-5.762 -10.2372,-11.831 -13.6701,-19.31 -15.08,-32.735 -18.0225,-81.346 -7.4174,-122.786 4.4137,-17.287 11.7085,-32.673 19.3711,-41.071 5.2106,-5.763 7.9078,-12.199 7.9078,-18.881 0,-6.927 -2.452,-12.628 -7.9691,-18.574 -15.8157,-16.919 -25.5625,-37.517 -29.0567,-61.485 -4.9654,-34.145 4.0459,-71.355 24.5204,-100.84 20.0455,-28.935 48.1824,-47.509 79.6304,-52.474 7.049,-1.165 20.229,-0.981 27.585,0.368 8.031,1.41 13.057,0.98 18.207,-1.472 6.375,-3.003 9.563,-6.743 13.302,-15.325 3.31,-7.662 5.885,-11.831 12.812,-20.474 8.337,-10.36 16.367,-17.41 29.24,-25.931 14.713,-9.624 31.448,-16.612 48.122,-19.984 6.068,-1.226 8.888,-1.41 20.229,-1.41 11.341,0 14.161,0.184 20.229,1.41 24.459,4.966 48.735,17.594 68.106,35.493 4.168,3.862 14.16,16.245 17.348,21.395 1.226,2.022 3.372,6.314 4.72,9.501 3.739,8.582 6.927,12.322 13.302,15.325 4.966,2.391 10.176,2.882 17.9,1.594 12.199,-2.084 21.578,-1.9 33.532,0.552 40.704,8.214 76.136,41.746 91.829,86.68 13.67,39.416 9.808,80.672 -10.544,112.18 -3.433,5.334 -6.866,9.625 -11.831,14.897 -10.728,11.463 -10.728,25.685 -0.061,37.455 17.532,19.187 28.505,66.389 25.194,108.012 -2.206,27.463 -9.256,52.045 -18.942,65.96 -1.716,2.452 -5.271,6.62 -7.969,9.195 -3.494,3.433 -5.455,5.946 -6.927,8.889 -1.9,3.862 -2.023,4.536 -2.023,10.421 v 6.314 l 5.272,10.298 c 8.828,16.981 13.548,29.302 17.716,46.099 9.012,35.861 8.215,71.538 -2.084,91.829 -0.858,1.716 -1.594,3.31 -1.594,3.494 0,0.184 11.709,0.306 26.053,0.306 h 25.992 l 0.674,-2.636 c 0.368,-1.409 0.981,-3.555 1.287,-4.781 0.675,-2.697 2.023,-10.666 3.127,-18.329 1.042,-7.724 1.042,-36.168 0,-44.75 -3.923,-31.141 -10.483,-55.845 -21.21,-79.201 -1.165,-2.636 -1.901,-4.904 -1.656,-5.026 0.307,-0.184 2.023,-2.636 3.862,-5.395 13.364,-20.229 21.578,-45.669 25.747,-79.262 1.103,-9.257 1.103,-49.041 0,-57.93 -2.943,-22.926 -6.498,-38.497 -12.383,-54.251 -2.452,-6.559 -8.95,-20.413 -11.708,-24.888 l -1.349,-2.268 4.659,-6.927 c 14.283,-21.456 22.62,-44.873 25.869,-72.581 1.471,-12.015 0.981,-36.29 -0.858,-48.305 -4.782,-30.467 -16,-58.42 -33.409,-83.185 -9.073,-12.935 -22.253,-27.096 -33.777,-36.291 -3.372,-2.697 -6.376,-5.149 -6.621,-5.394 -0.306,-0.245 0.062,-3.617 0.797,-7.418 7.418,-38.681 7.172,-86.924 -0.613,-124.624596 -6.743,-32.8573 -19.003,-58.9716 -34.819,-74.0516 C 523.209,4.2857941 510.336,-0.86349287 494.888,0.11732413 459.456,2.2015541 430.89,42.966714 419.61,107.21001 c -1.839,10.36 -3.432,22.498 -3.432,25.808 0,1.287 -0.246,2.329 -0.552,2.329 -0.307,0 -2.697,-1.226 -5.272,-2.758 -27.34,-16.184 -57.746,-24.827 -87.354,-24.827 -29.608,0 -60.014,8.643 -87.354,24.827 -2.575,1.532 -4.965,2.758 -5.272,2.758 -0.306,0 -0.552,-1.042 -0.552,-2.329 0,-3.433 -1.655,-15.938 -3.432,-25.808 C 216.152,49.525914 192.674,11.335414 161.472,1.7111341 157.181,0.42381313 144.982,-0.43436787 140.629,0.23994313 Z M 151.051,50.139014 c 8.827,6.9883 18.635,26.9724 24.275,49.3473 1.042,4.045696 2.145,8.704696 2.452,10.420696 0.245,1.656 0.919,5.395 1.471,8.276 2.391,12.996 3.494,27.034 3.617,44.137 l 0.061,16.858 -4.23,6.252 -4.229,6.314 h -9.87 c -11.524,0 -22.988,1.472 -33.961,4.414 -3.923,0.981 -7.724,1.962 -8.459,2.146 -1.165,0.245 -1.349,-0.123 -2.023,-5.15 -3.617,-27.279 -3.433,-57.5 0.552,-82.634 4.413,-28.014096 14.712,-53.392696 24.765,-60.871396 2.391,-1.7778 2.82,-1.7165 5.579,0.4904 z m 349.538,-0.4292 c 6.069,4.475 12.751,16.3674 17.716,31.57 9.992,30.405196 12.812,72.151196 7.54,111.874196 -0.674,5.027 -0.858,5.395 -2.023,5.15 -0.735,-0.184 -4.536,-1.165 -8.459,-2.146 -10.973,-2.942 -22.437,-4.414 -33.961,-4.414 h -9.87 l -4.229,-6.314 -4.23,-6.252 0.061,-16.858 c 0.123,-23.785 2.33,-42.359 7.601,-63.017596 5.579,-22.191 15.448,-42.1751 24.214,-49.1634 2.759,-2.2069 3.188,-2.2682 5.64,-0.4292 z"
|
|
||||||
fill="#000000"
|
|
||||||
id="path1" />
|
|
||||||
<path
|
|
||||||
d="m 313.498,358.23701 c -13.303,1.288 -16.919,1.778 -23.295,3.066 -10.36,2.145 -24.214,6.927 -33.838,11.647 -33.47,16.367 -56.519,43.646 -63.569,75.216 -1.41,6.253 -1.594,8.337 -1.594,18.881 0,10.421 0.184,12.689 1.533,18.635 9.379,41.256 47.385,71.723 96.549,77.301 10.666,1.165 56.765,1.165 67.431,0 39.478,-4.475 73.439,-25.869 88.703,-55.907 4.045,-8.03 6.007,-13.241 7.846,-21.394 1.349,-5.946 1.533,-8.214 1.533,-18.635 0,-10.544 -0.184,-12.628 -1.594,-18.881 -10.238,-45.853 -54.742,-81.959 -109.3,-88.825 -7.111,-0.858 -25.746,-1.594 -30.405,-1.104 z m 22.926,33.348 c 18.207,1.962 36.536,8.46 51.248,18.268 7.908,5.272 19.065,16.306 23.846,23.54 5.885,8.949 9.256,18.083 10.789,29.179 0.674,5.088 0.307,8.95 -1.533,17.164 -2.881,12.26 -11.831,25.072 -23.907,34.022 -5.64,4.107 -17.348,10.054 -24.52,12.383 -13.609,4.352 -22.498,5.149 -54.252,4.904 -20.719,-0.184 -24.398,-0.368 -30.344,-1.471 -20.29,-3.801 -36.351,-11.893 -47.998,-24.214 -9.441,-9.931 -13.732,-19.003 -16.061,-33.654 -1.042,-6.805 0.919,-18.084 4.904,-27.586 4.843,-11.586 17.348,-25.991 29.731,-34.267 14.344,-9.563 33.225,-16.367 50.573,-18.206 6.682,-0.736 20.842,-0.736 27.524,-0.062 z"
|
|
||||||
fill="#000000"
|
|
||||||
id="path2" />
|
|
||||||
<path
|
|
||||||
d="m 299.584,436.33601 c -4.659,2.513 -7.908,8.888 -6.927,13.608 1.103,5.088 5.578,10.238 12.566,14.468 3.74,2.268 3.985,2.574 4.169,4.842 0.122,1.349 -0.368,5.211 -1.042,8.644 -0.736,3.371 -1.288,6.927 -1.288,7.908 0.062,2.636 2.514,6.927 5.088,9.011 2.269,1.839 2.698,1.9 9.073,2.084 5.824,0.184 7.05,0.061 9.379,-1.042 6.008,-2.943 7.54,-8.337 5.333,-18.697 -1.839,-8.643 -1.471,-9.992 3.127,-12.628 4.842,-2.82 9.992,-7.785 11.524,-11.157 2.943,-6.436 0.245,-13.731 -6.253,-17.103 -1.593,-0.797 -3.555,-1.164 -6.436,-1.164 -4.475,0 -7.356,1.042 -12.628,4.413 l -3.004,1.901 -1.9,-1.165 c -7.785,-4.598 -9.195,-5.149 -13.916,-5.088 -3.371,0 -5.21,0.306 -6.865,1.165 z"
|
|
||||||
fill="#000000"
|
|
||||||
id="path3" />
|
|
||||||
<path
|
|
||||||
d="m 150.744,365.16501 c -10.85,3.433 -18.942,11.402 -23.11,22.743 -2.023,5.395 -3.004,13.916 -2.146,18.513 2.023,10.973 11.034,20.965 21.272,23.724 12.873,3.371 22.497,1.164 31.018,-7.295 4.965,-4.843 7.663,-9.073 10.36,-15.939 1.961,-4.842 2.084,-5.7 2.084,-12.566 l 0.061,-7.356 -2.574,-5.272 c -4.108,-8.337 -11.525,-14.529 -20.107,-16.797 -4.843,-1.226 -12.628,-1.164 -16.858,0.245 z"
|
|
||||||
fill="#000000"
|
|
||||||
id="path4" />
|
|
||||||
<path
|
|
||||||
d="m 478.153,364.98201 c -8.398,2.268 -15.877,8.52 -19.862,16.735 l -2.574,5.272 0.061,7.356 c 0,6.866 0.123,7.724 2.084,12.566 2.698,6.866 5.395,11.096 10.36,15.939 8.521,8.459 18.145,10.666 31.019,7.295 7.417,-1.962 14.834,-8.215 18.39,-15.51 3.065,-6.191 3.8,-10.666 2.82,-17.716 -2.268,-16.122 -11.709,-27.83 -25.747,-31.937 -4.107,-1.226 -12.076,-1.226 -16.551,0 z"
|
|
||||||
fill="#000000"
|
|
||||||
id="path5" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 8.8 KiB |
|
@ -1,39 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
version="1.1"
|
|
||||||
width="20.000015"
|
|
||||||
height="20"
|
|
||||||
id="svg1"
|
|
||||||
sodipodi:docname="openrouter-symbolic.svg"
|
|
||||||
viewBox="0 0 47.999998 47.999962"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#eeeeee"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="19.666667"
|
|
||||||
inkscape:cx="6.7118644"
|
|
||||||
inkscape:cy="10.805085"
|
|
||||||
inkscape:window-width="1908"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg1" />
|
|
||||||
<path
|
|
||||||
d="m 32.999999,2.9999808 c 3.980422,0.5880984 6.862472,1.8203168 10.3125,3.875 0.883008,0.5130469 1.766016,1.0260937 2.675781,1.5546875 0.663867,0.5182031 1.327735,1.0364062 2.011719,1.5703125 0,0.9900002 0,1.9800002 0,3.0000002 -2.311036,1.335928 -4.623718,2.668844 -6.9375,4 -0.659355,0.381562 -1.318711,0.763125 -1.998047,1.15625 -4.951172,2.84375 -4.951172,2.84375 -6.064453,2.84375 0,-1.65 0,-3.3 0,-5 -6.609294,1.004133 -10.792298,2.789518 -16,7 3.696448,3.823912 6.292078,6.155833 11.6875,6.625 0.808242,0.07477 1.616484,0.149531 2.449219,0.226562 0.614883,0.04898 1.229765,0.09797 1.863281,0.148438 0,-0.99 0,-1.98 0,-3 4.062255,0.578231 6.847624,1.891177 10.3125,4.0625 0.883008,0.547852 1.766016,1.095703 2.675781,1.660156 0.663867,0.421524 1.327735,0.843047 2.011719,1.277344 -1.156668,3.470003 -1.857267,3.847015 -4.875,5.6875 -0.698672,0.436992 -1.397344,0.873984 -2.117188,1.324219 -2.007812,0.988281 -2.007812,0.988281 -5.007812,0.988281 0,0.66 0,1.32 0,2 -0.99,0.33 -1.98,0.66 -3,1 0,-1.32 0,-2.64 0,-4 C 32.359335,40.962601 31.718671,40.925211 31.058593,40.8867 22.287404,40.220037 16.393127,37.373973 9.2226552,32.429668 6.2608222,30.524517 3.2830902,29.257335 -8.4473381e-7,27.999981 c 0,-3.3 0,-6.6 0,-10 C 1.6232682,17.32911 3.2486752,16.663411 4.8749992,15.999981 c 0.904922,-0.37125 1.809844,-0.7425 2.742188,-1.125 2.382812,-0.875 2.382812,-0.875 4.3828118,-0.875 0,-0.66 0,-1.32 0,-2 0.99,0 1.98,0 3,0 0,-0.66 0,-1.32 0,-2.0000002 6.284532,-3.3279791 10.886587,-4.4516452 18,-4 0,-0.99 0,-1.98 0,-3 z"
|
|
||||||
fill="#93a2b8"
|
|
||||||
id="path1"
|
|
||||||
style="fill:#000000" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,85 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
fill="#000000"
|
|
||||||
height="20.000002"
|
|
||||||
width="20"
|
|
||||||
version="1.1"
|
|
||||||
id="Capa_1"
|
|
||||||
viewBox="0 0 493.42511 493.42516"
|
|
||||||
xml:space="preserve"
|
|
||||||
sodipodi:docname="ubuntu-logo-svgrepo-com.svg"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
|
||||||
id="defs9" /><sodipodi:namedview
|
|
||||||
id="namedview9"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#ffffff"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="1"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:zoom="12.374369"
|
|
||||||
inkscape:cx="24.76894"
|
|
||||||
inkscape:cy="15.515943"
|
|
||||||
inkscape:window-width="1415"
|
|
||||||
inkscape:window-height="753"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="Capa_1" />
|
|
||||||
<g
|
|
||||||
id="ubuntu"
|
|
||||||
transform="translate(5.5573583e-4,0.00107926)">
|
|
||||||
<g
|
|
||||||
id="g2">
|
|
||||||
<g
|
|
||||||
id="g1">
|
|
||||||
<path
|
|
||||||
d="m 168.839,241.198 c 0,-38.117 17.894,-72.05 45.685,-93.896 L 171.988,79.22 c -35.648,25.603 -62.472,62.66 -75.127,105.796 19.811,12.751 32.949,35.031 32.949,60.353 0,24.424 -12.143,45.957 -30.783,58.918 13.606,40.86 40.12,75.838 74.706,100.113 l 39.559,-70.358 c -27.105,-21.838 -44.453,-55.318 -44.453,-92.844 z"
|
|
||||||
id="path1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g3">
|
|
||||||
<path
|
|
||||||
d="m 109.704,245.368 c 0,28.484 -23.132,51.592 -51.609,51.592 -28.491,0 -51.606,-23.107 -51.606,-51.592 0,-28.47 23.115,-51.577 51.606,-51.577 28.477,0 51.609,23.107 51.609,51.577 z"
|
|
||||||
id="path2" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g5">
|
|
||||||
<g
|
|
||||||
id="g4">
|
|
||||||
<path
|
|
||||||
d="m 399.494,370.126 c 12.002,0 23.301,2.936 33.23,8.149 30.924,-32.591 50.906,-75.595 54.211,-123.228 l -80.148,-1.551 c -6.171,60.111 -56.954,106.941 -118.677,106.941 -17.084,0 -33.388,-3.594 -48.101,-10.093 l -39.841,69.704 c 26.56,13.069 56.376,20.411 87.941,20.411 13.622,0 26.981,-1.379 39.854,-4.006 2.746,-37.072 33.717,-66.327 71.531,-66.327 z"
|
|
||||||
id="path3" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g6">
|
|
||||||
<path
|
|
||||||
d="m 451.071,441.847 c 0,28.478 -23.084,51.576 -51.577,51.576 -28.493,0 -51.594,-23.098 -51.594,-51.576 0,-28.5 23.101,-51.592 51.594,-51.592 28.493,0 51.577,23.092 51.577,51.592 z"
|
|
||||||
id="path5" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g8">
|
|
||||||
<g
|
|
||||||
id="g7">
|
|
||||||
<path
|
|
||||||
d="m 438.211,110.152 c -11.677,8.269 -25.968,13.163 -41.399,13.163 -39.637,0 -71.73,-32.102 -71.73,-71.715 0,-2.104 0.094,-4.139 0.25,-6.181 -12.05,-2.307 -24.503,-3.491 -37.222,-3.491 -31.859,0 -61.988,7.498 -88.689,20.777 l 39.607,69.75 c 14.979,-6.748 31.593,-10.544 49.082,-10.544 61.177,0 111.601,46.074 118.491,105.414 l 80.147,-2.447 C 483.209,181.12 465.487,141.372 438.211,110.152 Z"
|
|
||||||
id="path6" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g9">
|
|
||||||
<path
|
|
||||||
d="m 448.374,51.601 c 0,28.492 -23.038,51.592 -51.561,51.592 -28.491,0 -51.592,-23.1 -51.592,-51.592 C 345.22,23.107 368.321,0 396.812,0 c 28.523,0 51.562,23.107 51.562,51.601 z"
|
|
||||||
id="path8" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,339 +0,0 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Lesser General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
this service if you wish), that you receive source code or can get it
|
|
||||||
if you want it, that you can change the software or use pieces of it
|
|
||||||
in new free programs; and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
does not normally print such an announcement, your work based on
|
|
||||||
the Program is not required to print an announcement.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Program,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License.
|
|
|
@ -1,95 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<style-scheme id="custom-light" _name="Custom" version="1.0">
|
|
||||||
<author>end_4</author>
|
|
||||||
<_description>Catppuccin port but very random</_description>
|
|
||||||
|
|
||||||
<style name="bracket-match" background="#E3E6EB" bold="true"/>
|
|
||||||
<style name="bracket-mismatch" background="#E3E6EB" underline="true"/>
|
|
||||||
<style name="c:preprocessor" foreground="#DF8E1D"/>
|
|
||||||
<style name="css:at-rules" foreground="#8839EF"/>
|
|
||||||
<style name="css:color" foreground="#DF8E1D"/>
|
|
||||||
<style name="css:keyword" foreground="#256BF5"/>
|
|
||||||
<style name="current-line" background="#E3E6EB"/>
|
|
||||||
<style name="cursor" foreground="#DC8A78"/>
|
|
||||||
<style name="def:base-n-integer" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:boolean" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:builtin" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:character" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:comment" foreground="#9DA1B1"/>
|
|
||||||
<style name="def:complex" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:decimal" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:doc-comment" foreground="#9DA1B1"/>
|
|
||||||
<style name="def:doc-comment-element" foreground="#9DA1B1"/>
|
|
||||||
<style name="def:error" foreground="#D53055" background="#EAEDF2"/>
|
|
||||||
<style name="def:floating-point" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:function" foreground="#256BF5"/>
|
|
||||||
<style name="def:identifier" foreground="#000000"/>
|
|
||||||
<style name="def:keyword" foreground="#8839EF"/>
|
|
||||||
<style name="def:note" foreground="#9DA1B1"/>
|
|
||||||
<style name="def:number" foreground="#FE640B"/>
|
|
||||||
<style name="def:operator" foreground="#8839EF"/>
|
|
||||||
<style name="def:preprocessor" foreground="#256BF5"/>
|
|
||||||
<style name="def:reserved" foreground="#8839EF"/>
|
|
||||||
<style name="def:shebang" foreground="#9DA1B1"/>
|
|
||||||
<style name="def:special-char" foreground="#256BF5"/>
|
|
||||||
<style name="def:special-constant" foreground="#DF8E1D"/>
|
|
||||||
<style name="def:statement" foreground="#8839EF"/>
|
|
||||||
<style name="def:string" foreground="#4AA537"/>
|
|
||||||
<style name="def:type" foreground="#256BF5" italic="true"/>
|
|
||||||
<style name="diff:added-line" foreground="#282D32" background="#ACF2BD"/>
|
|
||||||
<style name="diff:changed-line" foreground="#282D32" background="#F1F2C3"/>
|
|
||||||
<style name="diff:location" foreground="#9DA1B1"/>
|
|
||||||
<style name="diff:removed-line" foreground="#282D32" background="#FFEEF0"/>
|
|
||||||
<style name="draw-spaces" foreground="#3b3a32"/>
|
|
||||||
<style name="html:dtd" foreground="#4AA537"/>
|
|
||||||
<style name="html:tag" foreground="#8839EF"/>
|
|
||||||
<style name="js:function" foreground="#256BF5"/>
|
|
||||||
<style name="line-numbers" foreground="#9699AA" background="#EAEDF2"/>
|
|
||||||
<style name="perl:builtin" foreground="#256BF5"/>
|
|
||||||
<style name="perl:include-statement" foreground="#8839EF"/>
|
|
||||||
<style name="perl:special-variable" foreground="#DF8E1D"/>
|
|
||||||
<style name="perl:variable" foreground="#000000"/>
|
|
||||||
<style name="php:string" foreground="#4AA537"/>
|
|
||||||
<style name="python:builtin-constant" foreground="#8839EF"/>
|
|
||||||
<style name="python:builtin-function" foreground="#256BF5"/>
|
|
||||||
<style name="python:module-handler" foreground="#8839EF"/>
|
|
||||||
<style name="python:special-variable" foreground="#8839EF"/>
|
|
||||||
<style name="ruby:attribute-definition" foreground="#8839EF"/>
|
|
||||||
<style name="ruby:builtin" foreground="#000000"/>
|
|
||||||
<style name="ruby:class-variable" foreground="#000000"/>
|
|
||||||
<style name="ruby:constant" foreground="#000000"/>
|
|
||||||
<style name="ruby:global-variable" foreground="#256BF5"/>
|
|
||||||
<style name="ruby:instance-variable" foreground="#000000"/>
|
|
||||||
<style name="ruby:module-handler" foreground="#8839EF"/>
|
|
||||||
<style name="ruby:predefined-variable" foreground="#DF8E1D"/>
|
|
||||||
<style name="ruby:regex" foreground="#f6aa11"/>
|
|
||||||
<style name="ruby:special-variable" foreground="#8839EF"/>
|
|
||||||
<style name="ruby:symbol" foreground="#DF8E1D"/>
|
|
||||||
<style name="rubyonrails:attribute-definition" foreground="#8839EF"/>
|
|
||||||
<style name="rubyonrails:block-parameter" foreground="#fd971f" italic="true"/>
|
|
||||||
<style name="rubyonrails:builtin" foreground="#000000"/>
|
|
||||||
<style name="rubyonrails:class-inherit" foreground="#256BF5" underline="true" italic="true"/>
|
|
||||||
<style name="rubyonrails:class-name" foreground="#256BF5"/>
|
|
||||||
<style name="rubyonrails:class-variable" foreground="#000000"/>
|
|
||||||
<style name="rubyonrails:complex-interpolation" foreground="#DF8E1D"/>
|
|
||||||
<style name="rubyonrails:constant" foreground="#000000"/>
|
|
||||||
<style name="rubyonrails:global-variable" foreground="#256BF5"/>
|
|
||||||
<style name="rubyonrails:instance-variable" foreground="#000000"/>
|
|
||||||
<style name="rubyonrails:module-handler" foreground="#8839EF"/>
|
|
||||||
<style name="rubyonrails:module-name" foreground="#256BF5"/>
|
|
||||||
<style name="rubyonrails:predefined-variable" foreground="#DF8E1D"/>
|
|
||||||
<style name="rubyonrails:rails" foreground="#000000"/>
|
|
||||||
<style name="rubyonrails:regex" foreground="#f6aa11"/>
|
|
||||||
<style name="rubyonrails:simple-interpolation" foreground="#DF8E1D"/>
|
|
||||||
<style name="rubyonrails:special-variable" foreground="#8839EF"/>
|
|
||||||
<style name="rubyonrails:symbol" foreground="#DF8E1D"/>
|
|
||||||
<style name="search-match" background="#E3E6EB" bold="true" underline="true"/>
|
|
||||||
<style name="selection" foreground="#f8f8f2" background="#444444"/>
|
|
||||||
<style name="text" foreground="#f8f8f2" background="#222222"/>
|
|
||||||
<style name="xml:attribute-name" foreground="#256BF5"/>
|
|
||||||
<style name="xml:element-name" foreground="#8839EF"/>
|
|
||||||
<style name="xml:entity" foreground="#c8cecc"/>
|
|
||||||
<style name="xml:namespace" foreground="#8839EF"/>
|
|
||||||
<style name="xml:tag" foreground="#8839EF"/>
|
|
||||||
|
|
||||||
</style-scheme>
|
|
|
@ -1,121 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2014 Leo Iannacone <info@leoiannacone.com>
|
|
||||||
|
|
||||||
This file was generated from a textmate theme named Monokai Extended
|
|
||||||
with tm2gtksw2 tool. (Alexandre da Silva)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
Boston, MA 02111-1307, USA.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- MODIFIED -->
|
|
||||||
|
|
||||||
<style-scheme id="custom" _name="Custom" version="1.0">
|
|
||||||
<author>Leo Iannacone</author>
|
|
||||||
<_description>Based on SublimeText Monokai Extended - Generated with tm2gtksw2</_description>
|
|
||||||
|
|
||||||
<style name="bracket-match" background="#333333" bold="true"/>
|
|
||||||
<style name="bracket-mismatch" background="#333333" underline="true"/>
|
|
||||||
<style name="c:preprocessor" foreground="#be84ff"/>
|
|
||||||
<style name="css:at-rules" foreground="#f92672"/>
|
|
||||||
<style name="css:color" foreground="#be84ff"/>
|
|
||||||
<style name="css:keyword" foreground="#66d9ef"/>
|
|
||||||
<style name="current-line" background="#333333"/>
|
|
||||||
<style name="cursor" foreground="#f8f8f0"/>
|
|
||||||
<style name="def:base-n-integer" foreground="#be84ff"/>
|
|
||||||
<style name="def:boolean" foreground="#be84ff"/>
|
|
||||||
<style name="def:builtin" foreground="#be84ff"/>
|
|
||||||
<style name="def:character" foreground="#be84ff"/>
|
|
||||||
<style name="def:comment" foreground="#75715e"/>
|
|
||||||
<style name="def:complex" foreground="#be84ff"/>
|
|
||||||
<style name="def:decimal" foreground="#be84ff"/>
|
|
||||||
<style name="def:doc-comment" foreground="#75715e"/>
|
|
||||||
<style name="def:doc-comment-element" foreground="#75715e"/>
|
|
||||||
<style name="def:error" foreground="#f8f8f0" background="#f92672"/>
|
|
||||||
<style name="def:floating-point" foreground="#be84ff"/>
|
|
||||||
<style name="def:function" foreground="#a6e22e"/>
|
|
||||||
<style name="def:identifier" foreground="#ffffff"/>
|
|
||||||
<style name="def:keyword" foreground="#f92672"/>
|
|
||||||
<style name="def:note" foreground="#75715e"/>
|
|
||||||
<style name="def:number" foreground="#be84ff"/>
|
|
||||||
<style name="def:operator" foreground="#f92672"/>
|
|
||||||
<style name="def:preprocessor" foreground="#66d9ef"/>
|
|
||||||
<style name="def:reserved" foreground="#f92672"/>
|
|
||||||
<style name="def:shebang" foreground="#75715e"/>
|
|
||||||
<style name="def:special-char" foreground="#66d9ef"/>
|
|
||||||
<style name="def:special-constant" foreground="#be84ff"/>
|
|
||||||
<style name="def:statement" foreground="#f92672"/>
|
|
||||||
<style name="def:string" foreground="#e6db74"/>
|
|
||||||
<style name="def:type" foreground="#66d9ef" italic="true"/>
|
|
||||||
<style name="diff:added-line" foreground="#a6e22e"/>
|
|
||||||
<style name="diff:changed-line" foreground="#e6db74"/>
|
|
||||||
<style name="diff:location" foreground="#75715e"/>
|
|
||||||
<style name="diff:removed-line" foreground="#f92672"/>
|
|
||||||
<style name="draw-spaces" foreground="#3b3a32"/>
|
|
||||||
<style name="html:dtd" foreground="#e6db74"/>
|
|
||||||
<style name="html:tag" foreground="#f92672"/>
|
|
||||||
<style name="js:function" foreground="#66d9ef"/>
|
|
||||||
<style name="line-numbers" foreground="#bebeba" background="#333333"/>
|
|
||||||
<style name="perl:builtin" foreground="#a6e22e"/>
|
|
||||||
<style name="perl:include-statement" foreground="#f92672"/>
|
|
||||||
<style name="perl:special-variable" foreground="#be84ff"/>
|
|
||||||
<style name="perl:variable" foreground="#ffffff"/>
|
|
||||||
<style name="php:string" foreground="#e6db74"/>
|
|
||||||
<style name="python:builtin-constant" foreground="#f92672"/>
|
|
||||||
<style name="python:builtin-function" foreground="#a6e22e"/>
|
|
||||||
<style name="python:module-handler" foreground="#f92672"/>
|
|
||||||
<style name="python:special-variable" foreground="#f92672"/>
|
|
||||||
<style name="ruby:attribute-definition" foreground="#f92672"/>
|
|
||||||
<style name="ruby:builtin" foreground="#ffffff"/>
|
|
||||||
<style name="ruby:class-variable" foreground="#ffffff"/>
|
|
||||||
<style name="ruby:constant" foreground="#ffffff"/>
|
|
||||||
<style name="ruby:global-variable" foreground="#a6e22e"/>
|
|
||||||
<style name="ruby:instance-variable" foreground="#ffffff"/>
|
|
||||||
<style name="ruby:module-handler" foreground="#f92672"/>
|
|
||||||
<style name="ruby:predefined-variable" foreground="#be84ff"/>
|
|
||||||
<style name="ruby:regex" foreground="#f6aa11"/>
|
|
||||||
<style name="ruby:special-variable" foreground="#f92672"/>
|
|
||||||
<style name="ruby:symbol" foreground="#be84ff"/>
|
|
||||||
<style name="rubyonrails:attribute-definition" foreground="#f92672"/>
|
|
||||||
<style name="rubyonrails:block-parameter" foreground="#fd971f" italic="true"/>
|
|
||||||
<style name="rubyonrails:builtin" foreground="#ffffff"/>
|
|
||||||
<style name="rubyonrails:class-inherit" foreground="#a6e22e" underline="true" italic="true"/>
|
|
||||||
<style name="rubyonrails:class-name" foreground="#66d9ef"/>
|
|
||||||
<style name="rubyonrails:class-variable" foreground="#ffffff"/>
|
|
||||||
<style name="rubyonrails:complex-interpolation" foreground="#be84ff"/>
|
|
||||||
<style name="rubyonrails:constant" foreground="#ffffff"/>
|
|
||||||
<style name="rubyonrails:global-variable" foreground="#a6e22e"/>
|
|
||||||
<style name="rubyonrails:instance-variable" foreground="#ffffff"/>
|
|
||||||
<style name="rubyonrails:module-handler" foreground="#f92672"/>
|
|
||||||
<style name="rubyonrails:module-name" foreground="#66d9ef"/>
|
|
||||||
<style name="rubyonrails:predefined-variable" foreground="#be84ff"/>
|
|
||||||
<style name="rubyonrails:rails" foreground="#ffffff"/>
|
|
||||||
<style name="rubyonrails:regex" foreground="#f6aa11"/>
|
|
||||||
<style name="rubyonrails:simple-interpolation" foreground="#be84ff"/>
|
|
||||||
<style name="rubyonrails:special-variable" foreground="#f92672"/>
|
|
||||||
<style name="rubyonrails:symbol" foreground="#be84ff"/>
|
|
||||||
<style name="search-match" background="#333333" bold="true" underline="true"/>
|
|
||||||
<style name="selection" foreground="#f8f8f2" background="#444444"/>
|
|
||||||
<style name="text" foreground="#f8f8f2" background="#222222"/>
|
|
||||||
<style name="xml:attribute-name" foreground="#a6e22e"/>
|
|
||||||
<style name="xml:element-name" foreground="#f92672"/>
|
|
||||||
<style name="xml:entity" foreground="#c8cecc"/>
|
|
||||||
<style name="xml:namespace" foreground="#f92672"/>
|
|
||||||
<style name="xml:tag" foreground="#f92672"/>
|
|
||||||
|
|
||||||
|
|
||||||
</style-scheme>
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
"use strict";
|
|
||||||
// Import
|
|
||||||
import Gdk from 'gi://Gdk';
|
|
||||||
import GLib from 'gi://GLib';
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
// Stuff
|
|
||||||
import userOptions from './modules/.configuration/user_options.js';
|
|
||||||
import { firstRunWelcome, startBatteryWarningService } from './services/messages.js';
|
|
||||||
import { startAutoDarkModeService } from './services/darkmode.js';
|
|
||||||
// Widgets
|
|
||||||
import { Bar, BarCornerTopleft, BarCornerTopright } from './modules/bar/main.js';
|
|
||||||
import Cheatsheet from './modules/cheatsheet/main.js';
|
|
||||||
// import DesktopBackground from './modules/desktopbackground/main.js';
|
|
||||||
import Dock from './modules/dock/main.js';
|
|
||||||
import Corner from './modules/screencorners/main.js';
|
|
||||||
import Crosshair from './modules/crosshair/main.js';
|
|
||||||
import Indicator from './modules/indicators/main.js';
|
|
||||||
import Osk from './modules/onscreenkeyboard/main.js';
|
|
||||||
import Overview from './modules/overview/main.js';
|
|
||||||
import Session from './modules/session/main.js';
|
|
||||||
import SideLeft from './modules/sideleft/main.js';
|
|
||||||
import SideRight from './modules/sideright/main.js';
|
|
||||||
import { COMPILED_STYLE_DIR } from './init.js';
|
|
||||||
|
|
||||||
const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start);
|
|
||||||
function forMonitors(widget) {
|
|
||||||
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
|
|
||||||
return range(n, 0).map(widget).flat(1);
|
|
||||||
}
|
|
||||||
function forMonitorsAsync(widget) {
|
|
||||||
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
|
|
||||||
return range(n, 0).forEach((n) => widget(n).catch(print))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start stuff
|
|
||||||
handleStyles(true);
|
|
||||||
startAutoDarkModeService().catch(print);
|
|
||||||
firstRunWelcome().catch(print);
|
|
||||||
startBatteryWarningService().catch(print)
|
|
||||||
|
|
||||||
const Windows = () => [
|
|
||||||
// forMonitors(DesktopBackground),
|
|
||||||
forMonitors(Crosshair),
|
|
||||||
Overview(),
|
|
||||||
forMonitors(Indicator),
|
|
||||||
forMonitors(Cheatsheet),
|
|
||||||
SideLeft(),
|
|
||||||
SideRight(),
|
|
||||||
forMonitors(Osk),
|
|
||||||
forMonitors(Session),
|
|
||||||
...(userOptions.dock.enabled ? [forMonitors(Dock)] : []),
|
|
||||||
...(userOptions.appearance.fakeScreenRounding !== 0 ? [
|
|
||||||
forMonitors((id) => Corner(id, 'top left', true)),
|
|
||||||
forMonitors((id) => Corner(id, 'top right', true)),
|
|
||||||
forMonitors((id) => Corner(id, 'bottom left', true)),
|
|
||||||
forMonitors((id) => Corner(id, 'bottom right', true)),
|
|
||||||
] : []),
|
|
||||||
...(userOptions.appearance.barRoundCorners ? [
|
|
||||||
forMonitors(BarCornerTopleft),
|
|
||||||
forMonitors(BarCornerTopright),
|
|
||||||
] : []),
|
|
||||||
];
|
|
||||||
|
|
||||||
const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully
|
|
||||||
const closeWindowDelays = {}; // For animations
|
|
||||||
for (let i = 0; i < (Gdk.Display.get_default()?.get_n_monitors() || 1); i++) {
|
|
||||||
closeWindowDelays[`osk${i}`] = CLOSE_ANIM_TIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
App.config({
|
|
||||||
css: `${COMPILED_STYLE_DIR}/style.css`,
|
|
||||||
stackTraceOnError: true,
|
|
||||||
closeWindowDelay: closeWindowDelays,
|
|
||||||
windows: Windows().flat(1),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stuff that don't need to be toggled. And they're async so ugh...
|
|
||||||
forMonitorsAsync(Bar);
|
|
||||||
// Bar().catch(print); // Use this to debug the bar. Single monitor only.
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Want only the overview from my config? this is what you're looking for!
|
|
||||||
// Remember to install: `dart-sass`, `ags`, `material-symbols`, and `xorg-xrandr`
|
|
||||||
// To launch this, run the following
|
|
||||||
// ags -c ~/.config/ags/config_overviewOnly.js
|
|
||||||
// To toggle the overview, run:
|
|
||||||
// ags -t overview
|
|
||||||
// You might wanna add that as a keybind (in hyprland.conf)
|
|
||||||
// bind = Super, Tab, exec, ags -t overview
|
|
||||||
|
|
||||||
// Import
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
|
||||||
// Widgets
|
|
||||||
import Overview from './modules/overview/main.js';
|
|
||||||
import { COMPILED_STYLE_DIR } from './init.js';
|
|
||||||
|
|
||||||
handleStyles(true);
|
|
||||||
|
|
||||||
App.config({
|
|
||||||
css: `${COMPILED_STYLE_DIR}/style.css`,
|
|
||||||
stackTraceOnError: true,
|
|
||||||
windows: [
|
|
||||||
Overview(),
|
|
||||||
],
|
|
||||||
});
|
|
|
@ -1,32 +0,0 @@
|
||||||
import GLib from 'gi://GLib';
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
import { darkMode } from './modules/.miscutils/system.js';
|
|
||||||
|
|
||||||
export const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
|
|
||||||
|
|
||||||
globalThis['handleStyles'] = (resetMusic) => {
|
|
||||||
// Reset
|
|
||||||
Utils.exec(`mkdir -p "${GLib.get_user_state_dir()}/ags/scss"`);
|
|
||||||
if (resetMusic) {
|
|
||||||
Utils.exec(`bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`); // reset music styles
|
|
||||||
Utils.exec(`bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss'`); // reset music styles
|
|
||||||
}
|
|
||||||
// Generate overrides
|
|
||||||
let lightdark = darkMode.value ? "dark" : "light";
|
|
||||||
Utils.writeFileSync(
|
|
||||||
`@mixin symbolic-icon {
|
|
||||||
-gtk-icon-theme: '${userOptions.icons.symbolicIconTheme[lightdark]}';
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
`${GLib.get_user_state_dir()}/ags/scss/_lib_mixins_overrides.scss`)
|
|
||||||
// Compile and apply
|
|
||||||
async function applyStyle() {
|
|
||||||
Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`);
|
|
||||||
Utils.exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/main.scss" "${COMPILED_STYLE_DIR}/style.css"`);
|
|
||||||
App.resetCss();
|
|
||||||
App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
|
|
||||||
console.log('[LOG] Styles loaded')
|
|
||||||
}
|
|
||||||
applyStyle().catch(print);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
const { Gdk } = imports.gi;
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
|
|
||||||
export let monitors;
|
|
||||||
|
|
||||||
// Mixes with Gdk monitor size cuz it reports monitor size scaled
|
|
||||||
async function updateStuff() {
|
|
||||||
monitors = JSON.parse(exec('hyprctl monitors -j'))
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
monitors.forEach((monitor, i) => {
|
|
||||||
const gdkMonitor = display.get_monitor(i);
|
|
||||||
monitor.realWidth = monitor.width;
|
|
||||||
monitor.realHeight = monitor.height;
|
|
||||||
if (userOptions.monitors.scaleMethod.toLowerCase == "gdk") {
|
|
||||||
monitor.width = gdkMonitor.get_geometry().width;
|
|
||||||
monitor.height = gdkMonitor.get_geometry().height;
|
|
||||||
}
|
|
||||||
else { // == "division"
|
|
||||||
monitor.width = Math.ceil(monitor.realWidth / monitor.scale);
|
|
||||||
monitor.height = Math.ceil(monitor.realHeight / monitor.scale);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStuff().catch(print);
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
export const quotes = [
|
|
||||||
{
|
|
||||||
quote: 'Nvidia, fuck you',
|
|
||||||
author: 'Linus Torvalds',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quote: 'reproducible system? cock and vagina?',
|
|
||||||
author: 'vaxry',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quote: "haha pointers hee hee i love pointe-\\\nProcess Vaxry exited with signal SIGSEGV",
|
|
||||||
author: 'vaxry',
|
|
||||||
}
|
|
||||||
];
|
|
|
@ -1,94 +0,0 @@
|
||||||
export const WWO_CODE = {
|
|
||||||
"113": "Sunny",
|
|
||||||
"116": "PartlyCloudy",
|
|
||||||
"119": "Cloudy",
|
|
||||||
"122": "VeryCloudy",
|
|
||||||
"143": "Fog",
|
|
||||||
"176": "LightShowers",
|
|
||||||
"179": "LightSleetShowers",
|
|
||||||
"182": "LightSleet",
|
|
||||||
"185": "LightSleet",
|
|
||||||
"200": "ThunderyShowers",
|
|
||||||
"227": "LightSnow",
|
|
||||||
"230": "HeavySnow",
|
|
||||||
"248": "Fog",
|
|
||||||
"260": "Fog",
|
|
||||||
"263": "LightShowers",
|
|
||||||
"266": "LightRain",
|
|
||||||
"281": "LightSleet",
|
|
||||||
"284": "LightSleet",
|
|
||||||
"293": "LightRain",
|
|
||||||
"296": "LightRain",
|
|
||||||
"299": "HeavyShowers",
|
|
||||||
"302": "HeavyRain",
|
|
||||||
"305": "HeavyShowers",
|
|
||||||
"308": "HeavyRain",
|
|
||||||
"311": "LightSleet",
|
|
||||||
"314": "LightSleet",
|
|
||||||
"317": "LightSleet",
|
|
||||||
"320": "LightSnow",
|
|
||||||
"323": "LightSnowShowers",
|
|
||||||
"326": "LightSnowShowers",
|
|
||||||
"329": "HeavySnow",
|
|
||||||
"332": "HeavySnow",
|
|
||||||
"335": "HeavySnowShowers",
|
|
||||||
"338": "HeavySnow",
|
|
||||||
"350": "LightSleet",
|
|
||||||
"353": "LightShowers",
|
|
||||||
"356": "HeavyShowers",
|
|
||||||
"359": "HeavyRain",
|
|
||||||
"362": "LightSleetShowers",
|
|
||||||
"365": "LightSleetShowers",
|
|
||||||
"368": "LightSnowShowers",
|
|
||||||
"371": "HeavySnowShowers",
|
|
||||||
"374": "LightSleetShowers",
|
|
||||||
"377": "LightSleet",
|
|
||||||
"386": "ThunderyShowers",
|
|
||||||
"389": "ThunderyHeavyRain",
|
|
||||||
"392": "ThunderySnowShowers",
|
|
||||||
"395": "HeavySnowShowers",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WEATHER_SYMBOL = {
|
|
||||||
"Unknown": "air",
|
|
||||||
"Cloudy": "cloud",
|
|
||||||
"Fog": "foggy",
|
|
||||||
"HeavyRain": "rainy",
|
|
||||||
"HeavyShowers": "rainy",
|
|
||||||
"HeavySnow": "snowing",
|
|
||||||
"HeavySnowShowers": "snowing",
|
|
||||||
"LightRain": "rainy",
|
|
||||||
"LightShowers": "rainy",
|
|
||||||
"LightSleet": "rainy",
|
|
||||||
"LightSleetShowers": "rainy",
|
|
||||||
"LightSnow": "cloudy_snowing",
|
|
||||||
"LightSnowShowers": "cloudy_snowing",
|
|
||||||
"PartlyCloudy": "partly_cloudy_day",
|
|
||||||
"Sunny": "clear_day",
|
|
||||||
"ThunderyHeavyRain": "thunderstorm",
|
|
||||||
"ThunderyShowers": "thunderstorm",
|
|
||||||
"ThunderySnowShowers": "thunderstorm",
|
|
||||||
"VeryCloudy": "cloud",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NIGHT_WEATHER_SYMBOL = {
|
|
||||||
"Unknown": "air",
|
|
||||||
"Cloudy": "cloud",
|
|
||||||
"Fog": "foggy",
|
|
||||||
"HeavyRain": "rainy",
|
|
||||||
"HeavyShowers": "rainy",
|
|
||||||
"HeavySnow": "snowing",
|
|
||||||
"HeavySnowShowers": "snowing",
|
|
||||||
"LightRain": "rainy",
|
|
||||||
"LightShowers": "rainy",
|
|
||||||
"LightSleet": "rainy",
|
|
||||||
"LightSleetShowers": "rainy",
|
|
||||||
"LightSnow": "cloudy_snowing",
|
|
||||||
"LightSnowShowers": "cloudy_snowing",
|
|
||||||
"PartlyCloudy": "partly_cloudy_night",
|
|
||||||
"Sunny": "clear_night",
|
|
||||||
"ThunderyHeavyRain": "thunderstorm",
|
|
||||||
"ThunderyShowers": "thunderstorm",
|
|
||||||
"ThunderySnowShowers": "thunderstorm",
|
|
||||||
"VeryCloudy": "cloud",
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
|
|
||||||
// -- Styling --
|
|
||||||
// min-height for diameter
|
|
||||||
// min-width for trough stroke
|
|
||||||
// padding for space between trough and progress
|
|
||||||
// margin for space between widget and parent
|
|
||||||
// background-color for trough color
|
|
||||||
// color for progress color
|
|
||||||
// -- Usage --
|
|
||||||
// font size for progress value (0-100px) (hacky i know, but i want animations)
|
|
||||||
export const AnimatedCircProg = ({
|
|
||||||
initFrom = 0,
|
|
||||||
initTo = 0,
|
|
||||||
initAnimTime = 2900,
|
|
||||||
initAnimPoints = 1,
|
|
||||||
extraSetup = () => { },
|
|
||||||
...rest
|
|
||||||
}) => Widget.DrawingArea({
|
|
||||||
...rest,
|
|
||||||
css: `${initFrom != initTo ? 'font-size: ' + initFrom + 'px; transition: ' + initAnimTime + 'ms linear;' : ''}`,
|
|
||||||
setup: (area) => {
|
|
||||||
const styleContext = area.get_style_context();
|
|
||||||
const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
|
||||||
const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left;
|
|
||||||
const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right;
|
|
||||||
const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top;
|
|
||||||
const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom;
|
|
||||||
area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom);
|
|
||||||
area.connect('draw', Lang.bind(area, (area, cr) => {
|
|
||||||
const styleContext = area.get_style_context();
|
|
||||||
const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
|
||||||
const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left;
|
|
||||||
const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right;
|
|
||||||
const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top;
|
|
||||||
const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom;
|
|
||||||
area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom);
|
|
||||||
|
|
||||||
const progressValue = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100.0;
|
|
||||||
|
|
||||||
const bg_stroke = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
const fg_stroke = bg_stroke - padding;
|
|
||||||
const radius = Math.min(width, height) / 2.0 - Math.max(bg_stroke, fg_stroke) / 2.0;
|
|
||||||
const center_x = width / 2.0 + marginLeft;
|
|
||||||
const center_y = height / 2.0 + marginTop;
|
|
||||||
const start_angle = -Math.PI / 2.0;
|
|
||||||
const end_angle = start_angle + (2 * Math.PI * progressValue);
|
|
||||||
const start_x = center_x + Math.cos(start_angle) * radius;
|
|
||||||
const start_y = center_y + Math.sin(start_angle) * radius;
|
|
||||||
const end_x = center_x + Math.cos(end_angle) * radius;
|
|
||||||
const end_y = center_y + Math.sin(end_angle) * radius;
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha);
|
|
||||||
cr.arc(center_x, center_y, radius, 0, 2 * Math.PI);
|
|
||||||
cr.setLineWidth(bg_stroke);
|
|
||||||
cr.stroke();
|
|
||||||
|
|
||||||
if (progressValue == 0) return;
|
|
||||||
|
|
||||||
// Draw progress
|
|
||||||
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
|
|
||||||
cr.arc(center_x, center_y, radius, start_angle, end_angle);
|
|
||||||
cr.setLineWidth(fg_stroke);
|
|
||||||
cr.stroke();
|
|
||||||
|
|
||||||
// Draw rounded ends for progress arcs
|
|
||||||
cr.setLineWidth(0);
|
|
||||||
cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01);
|
|
||||||
cr.fill();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Init animation
|
|
||||||
if (initFrom != initTo) {
|
|
||||||
area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`;
|
|
||||||
Utils.timeout(20, () => {
|
|
||||||
area.css = `font-size: ${initTo}px;`;
|
|
||||||
}, area)
|
|
||||||
const transitionDistance = initTo - initFrom;
|
|
||||||
const oneStep = initAnimTime / initAnimPoints;
|
|
||||||
area.css = `
|
|
||||||
font-size: ${initFrom}px;
|
|
||||||
transition: ${oneStep}ms linear;
|
|
||||||
`;
|
|
||||||
for (let i = 0; i < initAnimPoints; i++) {
|
|
||||||
Utils.timeout(Math.max(10, i * oneStep), () => {
|
|
||||||
if(!area) return;
|
|
||||||
area.css = `${initFrom != initTo ? 'font-size: ' + (initFrom + (transitionDistance / initAnimPoints * (i + 1))) + 'px;' : ''}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else area.css = 'font-size: 0px;';
|
|
||||||
extraSetup(area);
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,71 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
|
|
||||||
// min-height/min-width for height/width
|
|
||||||
// background-color/color for background/indicator color
|
|
||||||
// padding for pad of indicator
|
|
||||||
// font-size for selected index (0-based)
|
|
||||||
export const NavigationIndicator = ({count, vertical, ...props}) => Widget.DrawingArea({
|
|
||||||
...props,
|
|
||||||
setup: (area) => {
|
|
||||||
const styleContext = area.get_style_context();
|
|
||||||
const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width());
|
|
||||||
const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height());
|
|
||||||
area.set_size_request(width, height);
|
|
||||||
|
|
||||||
area.connect('draw', Lang.bind(area, (area, cr) => {
|
|
||||||
const styleContext = area.get_style_context();
|
|
||||||
const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width());
|
|
||||||
const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height());
|
|
||||||
// console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height())
|
|
||||||
area.set_size_request(width, height);
|
|
||||||
const paddingLeft = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
|
||||||
const paddingRight = styleContext.get_padding(Gtk.StateFlags.NORMAL).right;
|
|
||||||
const paddingTop = styleContext.get_padding(Gtk.StateFlags.NORMAL).top;
|
|
||||||
const paddingBottom = styleContext.get_padding(Gtk.StateFlags.NORMAL).bottom;
|
|
||||||
|
|
||||||
const selectedCell = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
let cellWidth = width;
|
|
||||||
let cellHeight = height;
|
|
||||||
if (vertical) cellHeight /= count;
|
|
||||||
else cellWidth /= count;
|
|
||||||
const indicatorWidth = cellWidth - paddingLeft - paddingRight;
|
|
||||||
const indicatorHeight = cellHeight - paddingTop - paddingBottom;
|
|
||||||
|
|
||||||
const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
cr.setLineWidth(2);
|
|
||||||
// Background
|
|
||||||
cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha);
|
|
||||||
cr.rectangle(0, 0, width, height);
|
|
||||||
cr.fill();
|
|
||||||
|
|
||||||
// The indicator line
|
|
||||||
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
|
|
||||||
if (vertical) {
|
|
||||||
cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth);
|
|
||||||
cr.stroke();
|
|
||||||
cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth / 2, Math.PI, 2 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorHeight - indicatorWidth / 2, indicatorWidth / 2, 0, Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight);
|
|
||||||
cr.stroke();
|
|
||||||
cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, 0.5 * Math.PI, 1.5 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(paddingLeft + cellWidth * selectedCell + indicatorWidth - indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, -0.5 * Math.PI, 0.5 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
export const RoundedCorner = (place, props) => Widget.DrawingArea({
|
|
||||||
...props,
|
|
||||||
hpack: place.includes('left') ? 'start' : 'end',
|
|
||||||
vpack: place.includes('top') ? 'start' : 'end',
|
|
||||||
setup: (widget) => Utils.timeout(1, () => {
|
|
||||||
const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
widget.set_size_request(r, r);
|
|
||||||
widget.connect('draw', Lang.bind(widget, (widget, cr) => {
|
|
||||||
const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
// const borderColor = widget.get_style_context().get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
// const borderWidth = widget.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway
|
|
||||||
widget.set_size_request(r, r);
|
|
||||||
|
|
||||||
switch (place) {
|
|
||||||
case 'topleft':
|
|
||||||
cr.arc(r, r, r, Math.PI, 3 * Math.PI / 2);
|
|
||||||
cr.lineTo(0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'topright':
|
|
||||||
cr.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI);
|
|
||||||
cr.lineTo(r, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'bottomleft':
|
|
||||||
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
|
|
||||||
cr.lineTo(0, r);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'bottomright':
|
|
||||||
cr.arc(0, 0, r, 0, Math.PI / 2);
|
|
||||||
cr.lineTo(r, r);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cr.closePath();
|
|
||||||
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
|
|
||||||
cr.fill();
|
|
||||||
// cr.setLineWidth(borderWidth);
|
|
||||||
// cr.setSourceRGBA(borderColor.red, borderColor.green, borderColor.blue, borderColor.alpha);
|
|
||||||
// cr.stroke();
|
|
||||||
}));
|
|
||||||
}),
|
|
||||||
});
|
|
|
@ -1,49 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
export const AnimatedSlider = ({
|
|
||||||
className,
|
|
||||||
value,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
return Widget.DrawingArea({
|
|
||||||
className: `${className}`,
|
|
||||||
setup: (self) => {
|
|
||||||
self.connect('draw', Lang.bind(self, (self, cr) => {
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
const allocatedWidth = self.get_allocated_width();
|
|
||||||
const allocatedHeight = self.get_allocated_height();
|
|
||||||
console.log(allocatedHeight, allocatedWidth)
|
|
||||||
const minWidth = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
const radius = styleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
const bg = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const fg = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
const value = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100;
|
|
||||||
self.set_size_request(-1, minHeight);
|
|
||||||
const width = allocatedHeight;
|
|
||||||
const height = minHeight;
|
|
||||||
|
|
||||||
cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
|
|
||||||
cr.arc(width - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
|
|
||||||
cr.arc(width - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
|
|
||||||
cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
|
|
||||||
cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
|
|
||||||
cr.closePath();
|
|
||||||
cr.fill();
|
|
||||||
|
|
||||||
// const valueWidth = width * value;
|
|
||||||
// cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
|
|
||||||
// cr.arc(valueWidth - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
|
|
||||||
// cr.arc(valueWidth - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
|
|
||||||
// cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
|
|
||||||
// cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
|
|
||||||
// cr.closePath();
|
|
||||||
// cr.fill();
|
|
||||||
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { monitors } from '../.commondata/hyprlanddata.js';
|
|
||||||
const { Box, EventBox } = Widget;
|
|
||||||
|
|
||||||
export const clickCloseRegion = ({ name, multimonitor = true, monitor = 0, expand = true, fillMonitor = '' }) => {
|
|
||||||
return EventBox({
|
|
||||||
child: Box({
|
|
||||||
expand: expand,
|
|
||||||
css: `
|
|
||||||
min-width: ${fillMonitor.includes('h') ? monitors[monitor].width : 0}px;
|
|
||||||
min-height: ${fillMonitor.includes('v') ? monitors[monitor].height : 0}px;
|
|
||||||
`,
|
|
||||||
}),
|
|
||||||
setup: (self) => self.on('button-press-event', (self, event) => { // Any mouse button
|
|
||||||
if (multimonitor) closeWindowOnAllMonitors(name);
|
|
||||||
else App.closeWindow(name);
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default clickCloseRegion;
|
|
||||||
|
|
|
@ -1,219 +0,0 @@
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
import { MaterialIcon } from './materialicon.js';
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
const { Box, Button, Label, Revealer, SpinButton } = Widget;
|
|
||||||
|
|
||||||
export const ConfigToggle = ({
|
|
||||||
icon, name, desc = '', initValue,
|
|
||||||
expandWidget = true,
|
|
||||||
onChange = () => { }, extraSetup = () => { },
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const enabled = Variable(initValue);
|
|
||||||
const toggleIcon = Label({
|
|
||||||
className: `icon-material txt-bold ${enabled.value ? '' : 'txt-poof'}`,
|
|
||||||
label: `${enabled.value ? 'check' : ''}`,
|
|
||||||
setup: (self) => self.hook(enabled, (self) => {
|
|
||||||
self.toggleClassName('switch-fg-toggling-false', false);
|
|
||||||
if (!enabled.value) {
|
|
||||||
self.label = '';
|
|
||||||
self.toggleClassName('txt-poof', true);
|
|
||||||
}
|
|
||||||
else Utils.timeout(1, () => {
|
|
||||||
toggleIcon.label = 'check';
|
|
||||||
toggleIcon.toggleClassName('txt-poof', false);
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
const toggleButtonIndicator = Box({
|
|
||||||
className: `switch-fg ${enabled.value ? 'switch-fg-true' : ''}`,
|
|
||||||
vpack: 'center',
|
|
||||||
hpack: 'start',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [toggleIcon,],
|
|
||||||
setup: (self) => self.hook(enabled, (self) => {
|
|
||||||
self.toggleClassName('switch-fg-true', enabled.value);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const toggleButton = Box({
|
|
||||||
hpack: 'end',
|
|
||||||
className: `switch-bg ${enabled.value ? 'switch-bg-true' : ''}`,
|
|
||||||
homogeneous: true,
|
|
||||||
children: [toggleButtonIndicator],
|
|
||||||
setup: (self) => self.hook(enabled, (self) => {
|
|
||||||
self.toggleClassName('switch-bg-true', enabled.value);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const widgetContent = Box({
|
|
||||||
tooltipText: desc,
|
|
||||||
className: 'txt spacing-h-5 configtoggle-box',
|
|
||||||
children: [
|
|
||||||
...(icon !== undefined ? [MaterialIcon(icon, 'norm')] : []),
|
|
||||||
...(name !== undefined ? [Label({
|
|
||||||
className: 'txt txt-small',
|
|
||||||
label: name,
|
|
||||||
})] : []),
|
|
||||||
...(expandWidget ? [Box({ hexpand: true })] : []),
|
|
||||||
toggleButton,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const interactionWrapper = Button({
|
|
||||||
attribute: {
|
|
||||||
enabled: enabled,
|
|
||||||
toggle: (newValue) => {
|
|
||||||
enabled.value = !enabled.value;
|
|
||||||
onChange(interactionWrapper, enabled.value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: widgetContent,
|
|
||||||
onClicked: (self) => self.attribute.toggle(self),
|
|
||||||
setup: (self) => {
|
|
||||||
setupCursorHover(self);
|
|
||||||
self.connect('pressed', () => { // mouse down
|
|
||||||
toggleIcon.toggleClassName('txt-poof', true);
|
|
||||||
toggleIcon.toggleClassName('switch-fg-true', false);
|
|
||||||
if (!enabled.value) toggleIcon.toggleClassName('switch-fg-toggling-false', true);
|
|
||||||
});
|
|
||||||
extraSetup(self)
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
interactionWrapper.enabled = enabled;
|
|
||||||
return interactionWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConfigSegmentedSelection = ({
|
|
||||||
icon, name, desc = '',
|
|
||||||
options = [{ name: 'Option 1', value: 0 }, { name: 'Option 2', value: 1 }],
|
|
||||||
initIndex = 0,
|
|
||||||
onChange,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
let lastSelected = initIndex;
|
|
||||||
let value = options[initIndex].value;
|
|
||||||
const widget = Box({
|
|
||||||
tooltipText: desc,
|
|
||||||
className: 'segment-container',
|
|
||||||
// homogeneous: true,
|
|
||||||
children: options.map((option, id) => {
|
|
||||||
const selectedIcon = Revealer({
|
|
||||||
revealChild: id == initIndex,
|
|
||||||
transition: 'slide_right',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
child: MaterialIcon('check', 'norm')
|
|
||||||
});
|
|
||||||
return Button({
|
|
||||||
setup: setupCursorHover,
|
|
||||||
className: `segment-btn ${id == initIndex ? 'segment-btn-enabled' : ''}`,
|
|
||||||
child: Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
selectedIcon,
|
|
||||||
Label({
|
|
||||||
label: option.name,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onClicked: (self) => {
|
|
||||||
value = option.value;
|
|
||||||
const kids = widget.get_children();
|
|
||||||
kids[lastSelected].toggleClassName('segment-btn-enabled', false);
|
|
||||||
kids[lastSelected].get_children()[0].get_children()[0].revealChild = false;
|
|
||||||
lastSelected = id;
|
|
||||||
self.toggleClassName('segment-btn-enabled', true);
|
|
||||||
selectedIcon.revealChild = true;
|
|
||||||
onChange(option.value, option.name);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
return widget;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConfigMulipleSelection = ({
|
|
||||||
icon, name, desc = '',
|
|
||||||
optionsArr = [
|
|
||||||
[{ name: 'Option 1', value: 0 }, { name: 'Option 2', value: 1 }],
|
|
||||||
[{ name: 'Option 3', value: 0 }, { name: 'Option 4', value: 1 }],
|
|
||||||
],
|
|
||||||
initIndex = [0, 0],
|
|
||||||
onChange,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
let lastSelected = initIndex;
|
|
||||||
const widget = Box({
|
|
||||||
tooltipText: desc,
|
|
||||||
className: 'multipleselection-container spacing-v-3',
|
|
||||||
vertical: true,
|
|
||||||
children: optionsArr.map((options, grp) => Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: options.map((option, id) => Button({
|
|
||||||
setup: setupCursorHover,
|
|
||||||
className: `multipleselection-btn ${id == initIndex[1] && grp == initIndex[0] ? 'multipleselection-btn-enabled' : ''}`,
|
|
||||||
label: option.name,
|
|
||||||
onClicked: (self) => {
|
|
||||||
const kidsg = widget.get_children();
|
|
||||||
const kids = kidsg.flatMap(widget => widget.get_children());
|
|
||||||
kids.forEach(kid => {
|
|
||||||
kid.toggleClassName('multipleselection-btn-enabled', false);
|
|
||||||
});
|
|
||||||
lastSelected = id;
|
|
||||||
self.toggleClassName('multipleselection-btn-enabled', true);
|
|
||||||
onChange(option.value, option.name);
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
})),
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
return widget;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConfigGap = ({ vertical = true, size = 5, ...rest }) => Box({
|
|
||||||
className: `gap-${vertical ? 'v' : 'h'}-${size}`,
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const ConfigSpinButton = ({
|
|
||||||
icon, name, desc = '', initValue,
|
|
||||||
minValue = 0, maxValue = 100, step = 1,
|
|
||||||
expandWidget = true,
|
|
||||||
onChange = () => { }, extraSetup = () => { },
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const value = Variable(initValue);
|
|
||||||
const spinButton = SpinButton({
|
|
||||||
className: 'spinbutton',
|
|
||||||
range: [minValue, maxValue],
|
|
||||||
increments: [step, step],
|
|
||||||
onValueChanged: ({ value: newValue }) => {
|
|
||||||
value.value = newValue;
|
|
||||||
onChange(spinButton, newValue);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
spinButton.value = value.value;
|
|
||||||
const widgetContent = Box({
|
|
||||||
tooltipText: desc,
|
|
||||||
className: 'txt spacing-h-5 configtoggle-box',
|
|
||||||
children: [
|
|
||||||
...(icon !== undefined ? [MaterialIcon(icon, 'norm')] : []),
|
|
||||||
...(name !== undefined ? [Label({
|
|
||||||
className: 'txt txt-small',
|
|
||||||
label: name,
|
|
||||||
})] : []),
|
|
||||||
...(expandWidget ? [Box({ hexpand: true })] : []),
|
|
||||||
spinButton,
|
|
||||||
],
|
|
||||||
setup: (self) => {
|
|
||||||
extraSetup(self);
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
return widgetContent;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
|
|
||||||
export const MaterialIcon = (icon, size, props = {}) => Widget.Label({
|
|
||||||
className: `icon-material txt-${size}`,
|
|
||||||
label: icon,
|
|
||||||
...props,
|
|
||||||
})
|
|
|
@ -1,462 +0,0 @@
|
||||||
// This file is for the actual widget for each single notification
|
|
||||||
const { GLib, Gdk, Gtk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
const { Box, EventBox, Icon, Overlay, Label, Button, Revealer } = Widget;
|
|
||||||
import { MaterialIcon } from './materialicon.js';
|
|
||||||
import { setupCursorHover } from "../.widgetutils/cursorhover.js";
|
|
||||||
import { AnimatedCircProg } from "./cairo_circularprogress.js";
|
|
||||||
|
|
||||||
function guessMessageType(summary) {
|
|
||||||
const str = summary.toLowerCase();
|
|
||||||
if (str.includes('reboot')) return 'restart_alt';
|
|
||||||
if (str.includes('recording')) return 'screen_record';
|
|
||||||
if (str.includes('battery') || summary.includes('power')) return 'power';
|
|
||||||
if (str.includes('screenshot')) return 'screenshot_monitor';
|
|
||||||
if (str.includes('welcome')) return 'waving_hand';
|
|
||||||
if (str.includes('time')) return 'scheduleb';
|
|
||||||
if (str.includes('installed')) return 'download';
|
|
||||||
if (str.includes('update')) return 'update';
|
|
||||||
if (str.startsWith('file')) return 'folder_copy';
|
|
||||||
return 'chat';
|
|
||||||
}
|
|
||||||
|
|
||||||
function exists(widget) {
|
|
||||||
return widget !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFriendlyNotifTimeString = (timeObject) => {
|
|
||||||
const messageTime = GLib.DateTime.new_from_unix_local(timeObject);
|
|
||||||
const oneMinuteAgo = GLib.DateTime.new_now_local().add_seconds(-60);
|
|
||||||
if (messageTime.compare(oneMinuteAgo) > 0)
|
|
||||||
return 'Now';
|
|
||||||
else if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year())
|
|
||||||
return messageTime.format(userOptions.time.format);
|
|
||||||
else if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year() - 1)
|
|
||||||
return 'Yesterday';
|
|
||||||
else
|
|
||||||
return messageTime.format(userOptions.time.dateFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NotificationIcon = (notifObject) => {
|
|
||||||
// { appEntry, appIcon, image }, urgency = 'normal'
|
|
||||||
if (notifObject.image) {
|
|
||||||
return Box({
|
|
||||||
valign: Gtk.Align.CENTER,
|
|
||||||
hexpand: false,
|
|
||||||
className: 'notif-icon',
|
|
||||||
css: `
|
|
||||||
background-image: url("${notifObject.image}");
|
|
||||||
background-size: auto 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon = 'NO_ICON';
|
|
||||||
if (Utils.lookUpIcon(notifObject.appIcon))
|
|
||||||
icon = notifObject.appIcon;
|
|
||||||
if (Utils.lookUpIcon(notifObject.appEntry))
|
|
||||||
icon = notifObject.appEntry;
|
|
||||||
|
|
||||||
return Box({
|
|
||||||
vpack: 'center',
|
|
||||||
hexpand: false,
|
|
||||||
className: `notif-icon notif-icon-material-${notifObject.urgency}`,
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
(icon != 'NO_ICON' ?
|
|
||||||
Icon({
|
|
||||||
vpack: 'center',
|
|
||||||
icon: icon,
|
|
||||||
})
|
|
||||||
:
|
|
||||||
MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : guessMessageType(notifObject.summary.toLowerCase())}`, 'hugerass', {
|
|
||||||
hexpand: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
notifObject,
|
|
||||||
isPopup = false,
|
|
||||||
props = {},
|
|
||||||
} = {}) => {
|
|
||||||
const popupTimeout = notifObject.timeout || (notifObject.urgency == 'critical' ? 8000 : 3000);
|
|
||||||
const command = (isPopup ?
|
|
||||||
() => notifObject.dismiss() :
|
|
||||||
() => notifObject.close()
|
|
||||||
)
|
|
||||||
const destroyWithAnims = () => {
|
|
||||||
widget.sensitive = false;
|
|
||||||
notificationBox.setCss(middleClickClose);
|
|
||||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
|
||||||
if (wholeThing) wholeThing.revealChild = false;
|
|
||||||
}, wholeThing);
|
|
||||||
Utils.timeout(userOptions.animations.durationSmall * 2, () => {
|
|
||||||
command();
|
|
||||||
if (wholeThing) {
|
|
||||||
wholeThing.destroy();
|
|
||||||
wholeThing = null;
|
|
||||||
}
|
|
||||||
}, wholeThing);
|
|
||||||
}
|
|
||||||
const widget = EventBox({
|
|
||||||
onHover: (self) => {
|
|
||||||
self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
|
||||||
if (!wholeThing.attribute.hovered)
|
|
||||||
wholeThing.attribute.hovered = true;
|
|
||||||
},
|
|
||||||
onHoverLost: (self) => {
|
|
||||||
self.window.set_cursor(null);
|
|
||||||
if (wholeThing.attribute.hovered)
|
|
||||||
wholeThing.attribute.hovered = false;
|
|
||||||
if (isPopup) {
|
|
||||||
command();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onMiddleClick: (self) => {
|
|
||||||
destroyWithAnims();
|
|
||||||
},
|
|
||||||
setup: (self) => {
|
|
||||||
self.on("button-press-event", () => {
|
|
||||||
wholeThing.attribute.held = true;
|
|
||||||
notificationContent.toggleClassName(`${isPopup ? 'popup-' : ''}notif-clicked-${notifObject.urgency}`, true);
|
|
||||||
Utils.timeout(800, () => {
|
|
||||||
if (wholeThing?.attribute.held) {
|
|
||||||
Utils.execAsync(['wl-copy', `${notifObject.body}`]).catch(print);
|
|
||||||
notifTextSummary.label = notifObject.summary + " (copied)";
|
|
||||||
Utils.timeout(3000, () => notifTextSummary.label = notifObject.summary)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).on("button-release-event", () => {
|
|
||||||
wholeThing.attribute.held = false;
|
|
||||||
notificationContent.toggleClassName(`${isPopup ? 'popup-' : ''}notif-clicked-${notifObject.urgency}`, false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let wholeThing = Revealer({
|
|
||||||
attribute: {
|
|
||||||
'close': undefined,
|
|
||||||
'destroyWithAnims': destroyWithAnims,
|
|
||||||
'dragging': false,
|
|
||||||
'held': false,
|
|
||||||
'hovered': false,
|
|
||||||
'id': notifObject.id,
|
|
||||||
},
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({ // Box to make sure css-based spacing works
|
|
||||||
homogeneous: true,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const notifTextPreview = Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: true,
|
|
||||||
child: Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: `txt-smallie notif-body-${notifObject.urgency}`,
|
|
||||||
useMarkup: true,
|
|
||||||
xalign: 0,
|
|
||||||
justify: Gtk.Justification.LEFT,
|
|
||||||
maxWidthChars: 1,
|
|
||||||
truncate: 'end',
|
|
||||||
label: notifObject.body.split("\n")[0],
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const notifTextExpanded = Revealer({
|
|
||||||
transition: 'slide_up',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: false,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: `txt-smallie notif-body-${notifObject.urgency}`,
|
|
||||||
useMarkup: true,
|
|
||||||
xalign: 0,
|
|
||||||
justify: Gtk.Justification.LEFT,
|
|
||||||
maxWidthChars: 1,
|
|
||||||
wrap: true,
|
|
||||||
label: notifObject.body,
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
className: 'notif-actions spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Button({
|
|
||||||
hexpand: true,
|
|
||||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
|
||||||
onClicked: () => destroyWithAnims(),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
child: Label({
|
|
||||||
label: 'Close',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
...notifObject.actions.map(action => Widget.Button({
|
|
||||||
hexpand: true,
|
|
||||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
|
||||||
onClicked: () => notifObject.invoke(action.id),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
child: Label({
|
|
||||||
label: action.label,
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const notifIcon = Box({
|
|
||||||
vpack: 'start',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
Overlay({
|
|
||||||
child: NotificationIcon(notifObject),
|
|
||||||
overlays: isPopup ? [AnimatedCircProg({
|
|
||||||
className: `notif-circprog-${notifObject.urgency}`,
|
|
||||||
vpack: 'center', hpack: 'center',
|
|
||||||
initFrom: (isPopup ? 100 : 0),
|
|
||||||
initTo: 0,
|
|
||||||
initAnimTime: popupTimeout,
|
|
||||||
})] : [],
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const notifTextSummary = Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-small txt-semibold titlefont',
|
|
||||||
justify: Gtk.Justification.LEFT,
|
|
||||||
hexpand: true,
|
|
||||||
maxWidthChars: 1,
|
|
||||||
truncate: 'end',
|
|
||||||
ellipsize: 3,
|
|
||||||
useMarkup: notifObject.summary.startsWith('<'),
|
|
||||||
label: notifObject.summary,
|
|
||||||
});
|
|
||||||
const initTimeString = getFriendlyNotifTimeString(notifObject.time);
|
|
||||||
const notifTextBody = Label({
|
|
||||||
vpack: 'center',
|
|
||||||
justification: 'right',
|
|
||||||
className: 'txt-smaller txt-semibold',
|
|
||||||
label: initTimeString,
|
|
||||||
setup: initTimeString == 'Now' ? (self) => {
|
|
||||||
let id = Utils.timeout(60000, () => {
|
|
||||||
self.label = getFriendlyNotifTimeString(notifObject.time);
|
|
||||||
id = null;
|
|
||||||
});
|
|
||||||
self.connect('destroy', () => { if (id) GLib.source_remove(id) });
|
|
||||||
} : () => { },
|
|
||||||
});
|
|
||||||
const notifText = Box({
|
|
||||||
valign: Gtk.Align.CENTER,
|
|
||||||
vertical: true,
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
children: [
|
|
||||||
notifTextSummary,
|
|
||||||
notifTextBody,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
notifTextPreview,
|
|
||||||
notifTextExpanded,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const notifExpandButton = Button({
|
|
||||||
vpack: 'start',
|
|
||||||
className: 'notif-expand-btn',
|
|
||||||
onClicked: (self) => {
|
|
||||||
if (notifTextPreview.revealChild) { // Expanding...
|
|
||||||
notifTextPreview.revealChild = false;
|
|
||||||
notifTextExpanded.revealChild = true;
|
|
||||||
self.child.label = 'expand_less';
|
|
||||||
expanded = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
notifTextPreview.revealChild = true;
|
|
||||||
notifTextExpanded.revealChild = false;
|
|
||||||
self.child.label = 'expand_more';
|
|
||||||
expanded = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: MaterialIcon('expand_more', 'norm', {
|
|
||||||
vpack: 'center',
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
});
|
|
||||||
const notificationContent = Box({
|
|
||||||
...props,
|
|
||||||
className: `${isPopup ? 'popup-' : ''}notif-${notifObject.urgency} spacing-h-10`,
|
|
||||||
children: [
|
|
||||||
notifIcon,
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
notifText,
|
|
||||||
notifExpandButton,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Gesture stuff
|
|
||||||
const gesture = Gtk.GestureDrag.new(widget);
|
|
||||||
var initDirX = 0;
|
|
||||||
var initDirVertical = -1; // -1: unset, 0: horizontal, 1: vertical
|
|
||||||
var expanded = false;
|
|
||||||
// in px
|
|
||||||
const startMargin = 0;
|
|
||||||
const MOVE_THRESHOLD = 10;
|
|
||||||
const DRAG_CONFIRM_THRESHOLD = 100;
|
|
||||||
// in rem
|
|
||||||
const maxOffset = 10.227;
|
|
||||||
const endMargin = 20.455;
|
|
||||||
const disappearHeight = 6.818;
|
|
||||||
const leftAnim1 = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
||||||
margin-left: -${Number(maxOffset + endMargin)}rem;
|
|
||||||
margin-right: ${Number(maxOffset + endMargin)}rem;
|
|
||||||
opacity: 0;`;
|
|
||||||
|
|
||||||
const rightAnim1 = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
||||||
margin-left: ${Number(maxOffset + endMargin)}rem;
|
|
||||||
margin-right: -${Number(maxOffset + endMargin)}rem;
|
|
||||||
opacity: 0;`;
|
|
||||||
|
|
||||||
const middleClickClose = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.85, 0, 0.15, 1);
|
|
||||||
margin-left: ${Number(maxOffset + endMargin)}rem;
|
|
||||||
margin-right: -${Number(maxOffset + endMargin)}rem;
|
|
||||||
opacity: 0;`;
|
|
||||||
|
|
||||||
const notificationBox = Box({
|
|
||||||
attribute: {
|
|
||||||
'leftAnim1': leftAnim1,
|
|
||||||
'rightAnim1': rightAnim1,
|
|
||||||
'middleClickClose': middleClickClose,
|
|
||||||
'ready': false,
|
|
||||||
},
|
|
||||||
homogeneous: true,
|
|
||||||
children: [notificationContent],
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(gesture, self => {
|
|
||||||
var offset_x = gesture.get_offset()[1];
|
|
||||||
var offset_y = gesture.get_offset()[2];
|
|
||||||
// Which dir?
|
|
||||||
if (initDirVertical == -1) {
|
|
||||||
if (Math.abs(offset_y) > MOVE_THRESHOLD)
|
|
||||||
initDirVertical = 1;
|
|
||||||
if (initDirX == 0 && Math.abs(offset_x) > MOVE_THRESHOLD) {
|
|
||||||
initDirVertical = 0;
|
|
||||||
initDirX = (offset_x > 0 ? 1 : -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Horizontal drag
|
|
||||||
if (initDirVertical == 0 && offset_x > MOVE_THRESHOLD) {
|
|
||||||
if (initDirX < 0)
|
|
||||||
self.setCss(`margin-left: 0px; margin-right: 0px;`);
|
|
||||||
else
|
|
||||||
self.setCss(`
|
|
||||||
margin-left: ${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
|
||||||
margin-right: -${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
else if (initDirVertical == 0 && offset_x < -MOVE_THRESHOLD) {
|
|
||||||
if (initDirX > 0)
|
|
||||||
self.setCss(`margin-left: 0px; margin-right: 0px;`);
|
|
||||||
else {
|
|
||||||
offset_x = Math.abs(offset_x);
|
|
||||||
self.setCss(`
|
|
||||||
margin-right: ${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
|
||||||
margin-left: -${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Update dragging
|
|
||||||
wholeThing.attribute.dragging = Math.abs(offset_x) > MOVE_THRESHOLD;
|
|
||||||
if (Math.abs(offset_x) > MOVE_THRESHOLD ||
|
|
||||||
Math.abs(offset_y) > MOVE_THRESHOLD) wholeThing.attribute.held = false;
|
|
||||||
widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
|
||||||
// Vertical drag
|
|
||||||
if (initDirVertical == 1 && offset_y > MOVE_THRESHOLD && !expanded) {
|
|
||||||
notifTextPreview.revealChild = false;
|
|
||||||
notifTextExpanded.revealChild = true;
|
|
||||||
expanded = true;
|
|
||||||
notifExpandButton.child.label = 'expand_less';
|
|
||||||
}
|
|
||||||
else if (initDirVertical == 1 && offset_y < -MOVE_THRESHOLD && expanded) {
|
|
||||||
notifTextPreview.revealChild = true;
|
|
||||||
notifTextExpanded.revealChild = false;
|
|
||||||
expanded = false;
|
|
||||||
notifExpandButton.child.label = 'expand_more';
|
|
||||||
}
|
|
||||||
|
|
||||||
}, 'drag-update')
|
|
||||||
.hook(gesture, self => {
|
|
||||||
if (!self.attribute.ready) {
|
|
||||||
wholeThing.revealChild = true;
|
|
||||||
self.attribute.ready = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const offset_h = gesture.get_offset()[1];
|
|
||||||
|
|
||||||
if (Math.abs(offset_h) > DRAG_CONFIRM_THRESHOLD && offset_h * initDirX > 0) {
|
|
||||||
if (offset_h > 0) {
|
|
||||||
self.setCss(rightAnim1);
|
|
||||||
widget.sensitive = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.setCss(leftAnim1);
|
|
||||||
widget.sensitive = false;
|
|
||||||
}
|
|
||||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
|
||||||
if (wholeThing) wholeThing.revealChild = false;
|
|
||||||
}, wholeThing);
|
|
||||||
Utils.timeout(userOptions.animations.durationSmall * 2, () => {
|
|
||||||
command();
|
|
||||||
if (wholeThing) {
|
|
||||||
wholeThing.destroy();
|
|
||||||
wholeThing = null;
|
|
||||||
}
|
|
||||||
}, wholeThing);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.setCss(`transition: margin 200ms cubic-bezier(0.05, 0.7, 0.1, 1), opacity 200ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
||||||
margin-left: ${startMargin}px;
|
|
||||||
margin-right: ${startMargin}px;
|
|
||||||
margin-bottom: unset; margin-top: unset;
|
|
||||||
opacity: 1;`);
|
|
||||||
if (widget.window)
|
|
||||||
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
|
||||||
|
|
||||||
wholeThing.attribute.dragging = false;
|
|
||||||
}
|
|
||||||
initDirX = 0;
|
|
||||||
initDirVertical = -1;
|
|
||||||
}, 'drag-end')
|
|
||||||
,
|
|
||||||
})
|
|
||||||
widget.add(notificationBox);
|
|
||||||
wholeThing.child.children = [widget];
|
|
||||||
if (isPopup) Utils.timeout(popupTimeout, () => {
|
|
||||||
if (wholeThing) {
|
|
||||||
wholeThing.revealChild = false;
|
|
||||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
|
||||||
if (wholeThing) {
|
|
||||||
wholeThing.destroy();
|
|
||||||
wholeThing = null;
|
|
||||||
}
|
|
||||||
command();
|
|
||||||
}, wholeThing);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return wholeThing;
|
|
||||||
}
|
|
|
@ -1,306 +0,0 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
import { MaterialIcon } from './materialicon.js';
|
|
||||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
|
||||||
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
|
||||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
|
||||||
import { languages } from './statusicons_languages.js';
|
|
||||||
|
|
||||||
// A guessing func to try to support langs not listed in data/languages.js
|
|
||||||
function isLanguageMatch(abbreviation, word) {
|
|
||||||
const lowerAbbreviation = abbreviation.toLowerCase();
|
|
||||||
const lowerWord = word.toLowerCase();
|
|
||||||
let j = 0;
|
|
||||||
for (let i = 0; i < lowerWord.length; i++) {
|
|
||||||
if (lowerWord[i] === lowerAbbreviation[j]) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
if (j === lowerAbbreviation.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MicMuteIndicator = () => Widget.Revealer({
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: false,
|
|
||||||
setup: (self) => self.hook(Audio, (self) => {
|
|
||||||
self.revealChild = Audio.microphone?.stream?.isMuted;
|
|
||||||
}),
|
|
||||||
child: MaterialIcon('mic_off', 'norm'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const NotificationIndicator = (notifCenterName = 'sideright') => {
|
|
||||||
const widget = Widget.Revealer({
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: false,
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(Notifications, (self, id) => {
|
|
||||||
if (!id || Notifications.dnd) return;
|
|
||||||
if (!Notifications.getNotification(id)) return;
|
|
||||||
self.revealChild = true;
|
|
||||||
}, 'notified')
|
|
||||||
.hook(App, (self, currentName, visible) => {
|
|
||||||
if (visible && currentName === notifCenterName) {
|
|
||||||
self.revealChild = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
,
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
MaterialIcon('notifications', 'norm'),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'txt-small titlefont',
|
|
||||||
attribute: {
|
|
||||||
unreadCount: 0,
|
|
||||||
update: (self) => self.label = `${self.attribute.unreadCount}`,
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(Notifications, (self, id) => {
|
|
||||||
if (!id || Notifications.dnd) return;
|
|
||||||
if (!Notifications.getNotification(id)) return;
|
|
||||||
self.attribute.unreadCount++;
|
|
||||||
self.attribute.update(self);
|
|
||||||
}, 'notified')
|
|
||||||
.hook(App, (self, currentName, visible) => {
|
|
||||||
if (visible && currentName === notifCenterName) {
|
|
||||||
self.attribute.unreadCount = 0;
|
|
||||||
self.attribute.update(self);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BluetoothIndicator = () => Widget.Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'false': Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth_disabled' }),
|
|
||||||
'true': Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth' }),
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(Bluetooth, stack => {
|
|
||||||
stack.shown = String(Bluetooth.enabled);
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
const BluetoothDevices = () => Widget.Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
setup: self => self.hook(Bluetooth, self => {
|
|
||||||
self.children = Bluetooth.connected_devices.map((device) => {
|
|
||||||
return Widget.Box({
|
|
||||||
className: 'bar-bluetooth-device spacing-h-5',
|
|
||||||
vpack: 'center',
|
|
||||||
tooltipText: device.name,
|
|
||||||
children: [
|
|
||||||
Widget.Icon(`${device.iconName}-symbolic`),
|
|
||||||
...(device.batteryPercentage ? [Widget.Label({
|
|
||||||
className: 'txt-smallie',
|
|
||||||
label: `${device.batteryPercentage}`,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(device, (self) => {
|
|
||||||
self.label = `${device.batteryPercentage}`;
|
|
||||||
}, 'notify::batteryPercentage')
|
|
||||||
}
|
|
||||||
})] : []),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
self.visible = Bluetooth.connected_devices.length > 0;
|
|
||||||
}, 'notify::connected-devices'),
|
|
||||||
})
|
|
||||||
|
|
||||||
const NetworkWiredIndicator = () => Widget.Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'fallback': SimpleNetworkIndicator(),
|
|
||||||
'unknown': Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' }),
|
|
||||||
'disconnected': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' }),
|
|
||||||
'connected': Widget.Label({ className: 'txt-norm icon-material', label: 'lan' }),
|
|
||||||
'connecting': Widget.Label({ className: 'txt-norm icon-material', label: 'settings_ethernet' }),
|
|
||||||
},
|
|
||||||
setup: (self) => self.hook(Network, stack => {
|
|
||||||
if (!Network.wired)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const { internet } = Network.wired;
|
|
||||||
if (['connecting', 'connected'].includes(internet))
|
|
||||||
stack.shown = internet;
|
|
||||||
else if (Network.connectivity !== 'full')
|
|
||||||
stack.shown = 'disconnected';
|
|
||||||
else
|
|
||||||
stack.shown = 'fallback';
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const SimpleNetworkIndicator = () => Widget.Icon({
|
|
||||||
setup: (self) => self.hook(Network, self => {
|
|
||||||
const icon = Network[Network.primary || 'wifi']?.iconName;
|
|
||||||
self.icon = icon || '';
|
|
||||||
self.visible = icon;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const NetworkWifiIndicator = () => Widget.Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'disabled': Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' }),
|
|
||||||
'disconnected': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' }),
|
|
||||||
'connecting': Widget.Label({ className: 'txt-norm icon-material', label: 'settings_ethernet' }),
|
|
||||||
'0': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_0_bar' }),
|
|
||||||
'1': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_1_bar' }),
|
|
||||||
'2': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_2_bar' }),
|
|
||||||
'3': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_3_bar' }),
|
|
||||||
'4': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_4_bar' }),
|
|
||||||
},
|
|
||||||
setup: (self) => self.hook(Network, (stack) => {
|
|
||||||
if (!Network.wifi) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Network.wifi.internet == 'connected') {
|
|
||||||
stack.shown = String(Math.ceil(Network.wifi.strength / 25));
|
|
||||||
}
|
|
||||||
else if (["disconnected", "connecting"].includes(Network.wifi.internet)) {
|
|
||||||
stack.shown = Network.wifi.internet;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const NetworkIndicator = () => Widget.Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'fallback': SimpleNetworkIndicator(),
|
|
||||||
'wifi': NetworkWifiIndicator(),
|
|
||||||
'wired': NetworkWiredIndicator(),
|
|
||||||
},
|
|
||||||
setup: (self) => self.hook(Network, stack => {
|
|
||||||
if (!Network.primary) {
|
|
||||||
stack.shown = 'wifi';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const primary = Network.primary || 'fallback';
|
|
||||||
if (['wifi', 'wired'].includes(primary))
|
|
||||||
stack.shown = primary;
|
|
||||||
else
|
|
||||||
stack.shown = 'fallback';
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const HyprlandXkbKeyboardLayout = async ({ useFlag } = {}) => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
var languageStackArray = [];
|
|
||||||
|
|
||||||
const updateCurrentKeyboards = () => {
|
|
||||||
var initLangs = [];
|
|
||||||
JSON.parse(Utils.exec('hyprctl -j devices')).keyboards
|
|
||||||
.forEach(keyboard => {
|
|
||||||
initLangs.push(...keyboard.layout.split(',').map(lang => lang.trim()));
|
|
||||||
});
|
|
||||||
initLangs = [...new Set(initLangs)];
|
|
||||||
languageStackArray = Array.from({ length: initLangs.length }, (_, i) => {
|
|
||||||
const lang = languages.find(lang => lang.layout == initLangs[i]);
|
|
||||||
// if (!lang) return [
|
|
||||||
// initLangs[i],
|
|
||||||
// Widget.Label({ label: initLangs[i] })
|
|
||||||
// ];
|
|
||||||
// return [
|
|
||||||
// lang.layout,
|
|
||||||
// Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
|
||||||
// ];
|
|
||||||
// Object
|
|
||||||
if (!lang) return {
|
|
||||||
[initLangs[i]]: Widget.Label({ label: initLangs[i] })
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
[lang.layout]: Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
updateCurrentKeyboards();
|
|
||||||
const widgetRevealer = Widget.Revealer({
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: languageStackArray.length > 1,
|
|
||||||
});
|
|
||||||
const widgetKids = {
|
|
||||||
...languageStackArray.reduce((obj, lang) => {
|
|
||||||
return { ...obj, ...lang };
|
|
||||||
}, {}),
|
|
||||||
'undef': Widget.Label({ label: '?' }),
|
|
||||||
}
|
|
||||||
const widgetContent = Widget.Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: widgetKids,
|
|
||||||
setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => {
|
|
||||||
if (!kbName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var lang = languages.find(lang => layoutName.includes(lang.name));
|
|
||||||
if (lang) {
|
|
||||||
widgetContent.shown = lang.layout;
|
|
||||||
}
|
|
||||||
else { // Attempt to support langs not listed
|
|
||||||
lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName));
|
|
||||||
if (!lang) stack.shown = 'undef';
|
|
||||||
else stack.shown = lang[0];
|
|
||||||
}
|
|
||||||
}, 'keyboard-layout'),
|
|
||||||
});
|
|
||||||
widgetRevealer.child = widgetContent;
|
|
||||||
return widgetRevealer;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const OptionalKeyboardLayout = async () => {
|
|
||||||
try {
|
|
||||||
return await HyprlandXkbKeyboardLayout({ useFlag: userOptions.appearance.keyboardUseFlag });
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createKeyboardLayoutInstances = async () => {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
const monitorsCount = Hyprland.monitors.length
|
|
||||||
const instances = await Promise.all(
|
|
||||||
Array.from({ length: monitorsCount }, () => OptionalKeyboardLayout())
|
|
||||||
);
|
|
||||||
|
|
||||||
return instances;
|
|
||||||
};
|
|
||||||
const optionalKeyboardLayoutInstances = await createKeyboardLayoutInstances()
|
|
||||||
|
|
||||||
export const StatusIcons = (props = {}, monitor = 0) => Widget.Box({
|
|
||||||
...props,
|
|
||||||
child: Widget.Box({
|
|
||||||
className: 'spacing-h-15',
|
|
||||||
children: [
|
|
||||||
MicMuteIndicator(),
|
|
||||||
optionalKeyboardLayoutInstances[monitor],
|
|
||||||
NotificationIndicator(),
|
|
||||||
NetworkIndicator(),
|
|
||||||
Widget.Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [BluetoothIndicator(), BluetoothDevices()]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,67 +0,0 @@
|
||||||
// For keyboard layout in statusicons.js
|
|
||||||
// This list is not exhaustive. It just includes known/possible languages of users of my dotfiles
|
|
||||||
// Add your language here if you use multi-lang xkb input. Else, ignore
|
|
||||||
// Note that something like "French (Canada)" should go before "French"
|
|
||||||
// and "English (US)" should go before "English"
|
|
||||||
export const languages = [
|
|
||||||
{
|
|
||||||
layout: 'us',
|
|
||||||
name: 'English (US)',
|
|
||||||
flag: '🇺🇸'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'fi',
|
|
||||||
name: 'Finnish',
|
|
||||||
flag: '🇫🇮'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'ru',
|
|
||||||
name: 'Russian',
|
|
||||||
flag: '🇷🇺',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'pl',
|
|
||||||
name: 'Polish',
|
|
||||||
flag: '🇷🇵🇵🇱',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'ro',
|
|
||||||
name: 'Romanian',
|
|
||||||
flag: '🇷🇴',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'ca',
|
|
||||||
name: 'French (Canada)',
|
|
||||||
flag: '🇫🇷',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'fr',
|
|
||||||
name: 'French',
|
|
||||||
flag: '🇫🇷',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'tr',
|
|
||||||
name: 'Turkish',
|
|
||||||
flag: '🇹🇷',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'jp',
|
|
||||||
name: 'Japanese',
|
|
||||||
flag: '🇯🇵',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'cn',
|
|
||||||
name: 'Chinese',
|
|
||||||
flag: '🇨🇳',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'vn',
|
|
||||||
name: 'Vietnamese',
|
|
||||||
flag: '🇻🇳',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
layout: 'undef',
|
|
||||||
name: 'Undefined',
|
|
||||||
flag: '🧐',
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -1,279 +0,0 @@
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box, Button, EventBox, Label, Overlay, Stack } = Widget;
|
|
||||||
import { MaterialIcon } from './materialicon.js';
|
|
||||||
import { NavigationIndicator } from './cairo_navigationindicator.js';
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { DoubleRevealer } from '../.widgethacks/advancedrevealers.js';
|
|
||||||
|
|
||||||
export const TabContainer = ({ icons, names, children, className = '', setup = () => { }, ...rest }) => {
|
|
||||||
const shownIndex = Variable(0);
|
|
||||||
let previousShownIndex = 0;
|
|
||||||
const count = Math.min(icons.length, names.length, children.length);
|
|
||||||
const tabs = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: Array.from({ length: count }, (_, i) => Button({ // Tab button
|
|
||||||
className: 'tab-btn',
|
|
||||||
onClicked: () => shownIndex.value = i,
|
|
||||||
setup: setupCursorHover,
|
|
||||||
child: Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'spacing-h-5 txt-small',
|
|
||||||
children: [
|
|
||||||
MaterialIcon(icons[i], 'norm'),
|
|
||||||
Label({
|
|
||||||
label: names[i],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.children[previousShownIndex].toggleClassName('tab-btn-active', false);
|
|
||||||
self.children[shownIndex.value].toggleClassName('tab-btn-active', true);
|
|
||||||
previousShownIndex = shownIndex.value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const tabIndicatorLine = Box({
|
|
||||||
vertical: true,
|
|
||||||
homogeneous: true,
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.children[0].css = `font-size: ${shownIndex.value}px;`;
|
|
||||||
}),
|
|
||||||
children: [NavigationIndicator({
|
|
||||||
className: 'tab-indicator',
|
|
||||||
count: count,
|
|
||||||
css: `font-size: ${shownIndex.value}px;`,
|
|
||||||
})],
|
|
||||||
});
|
|
||||||
const tabSection = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [EventBox({
|
|
||||||
onScrollUp: () => mainBox.prevTab(),
|
|
||||||
onScrollDown: () => mainBox.nextTab(),
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
tabs,
|
|
||||||
tabIndicatorLine
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
const contentStack = Stack({
|
|
||||||
transition: 'slide_left_right',
|
|
||||||
children: children.reduce((acc, currentValue, index) => {
|
|
||||||
acc[index] = currentValue;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.shown = `${shownIndex.value}`;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const mainBox = Box({
|
|
||||||
attribute: {
|
|
||||||
children: children,
|
|
||||||
shown: shownIndex,
|
|
||||||
names: names,
|
|
||||||
},
|
|
||||||
vertical: true,
|
|
||||||
className: `spacing-v-5 ${className}`,
|
|
||||||
setup: (self) => {
|
|
||||||
self.pack_start(tabSection, false, false, 0);
|
|
||||||
self.pack_end(contentStack, true, true, 0);
|
|
||||||
setup(self);
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
|
||||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
|
||||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
|
||||||
|
|
||||||
return mainBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const IconTabContainer = ({
|
|
||||||
iconWidgets, names, children, className = '',
|
|
||||||
setup = () => { }, onChange = () => { },
|
|
||||||
tabsHpack = 'center', tabSwitcherClassName = '',
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const shownIndex = Variable(0);
|
|
||||||
let previousShownIndex = 0;
|
|
||||||
const count = Math.min(iconWidgets.length, names.length, children.length);
|
|
||||||
const tabs = Box({
|
|
||||||
hpack: tabsHpack,
|
|
||||||
className: `spacing-h-5 ${tabSwitcherClassName}`,
|
|
||||||
children: iconWidgets.map((icon, i) => Button({
|
|
||||||
className: 'tab-icon',
|
|
||||||
tooltipText: names[i],
|
|
||||||
child: icon,
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: () => shownIndex.value = i,
|
|
||||||
})),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.children[previousShownIndex].toggleClassName('tab-icon-active', false);
|
|
||||||
self.children[shownIndex.value].toggleClassName('tab-icon-active', true);
|
|
||||||
previousShownIndex = shownIndex.value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const tabSection = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [EventBox({
|
|
||||||
onScrollUp: () => mainBox.prevTab(),
|
|
||||||
onScrollDown: () => mainBox.nextTab(),
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
tabs,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
const contentStack = Stack({
|
|
||||||
transition: 'slide_left_right',
|
|
||||||
children: children.reduce((acc, currentValue, index) => {
|
|
||||||
acc[index] = currentValue;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.shown = `${shownIndex.value}`;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const mainBox = Box({
|
|
||||||
attribute: {
|
|
||||||
children: children,
|
|
||||||
shown: shownIndex,
|
|
||||||
names: names,
|
|
||||||
},
|
|
||||||
vertical: true,
|
|
||||||
className: `spacing-v-5 ${className}`,
|
|
||||||
setup: (self) => {
|
|
||||||
self.pack_start(tabSection, false, false, 0);
|
|
||||||
self.pack_end(contentStack, true, true, 0);
|
|
||||||
setup(self);
|
|
||||||
self.hook(shownIndex, (self) => onChange(self, shownIndex.value));
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
|
||||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
|
||||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
|
||||||
mainBox.shown = shownIndex;
|
|
||||||
|
|
||||||
return mainBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExpandingIconTabContainer = ({
|
|
||||||
icons, names, children, className = '',
|
|
||||||
setup = () => { }, onChange = () => { },
|
|
||||||
tabsHpack = 'center', tabSwitcherClassName = '',
|
|
||||||
transitionDuration = userOptions.animations.durationLarge,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const shownIndex = Variable(0);
|
|
||||||
let previousShownIndex = 0;
|
|
||||||
const count = Math.min(icons.length, names.length, children.length);
|
|
||||||
const tabs = Box({
|
|
||||||
hpack: tabsHpack,
|
|
||||||
className: `spacing-h-5 ${tabSwitcherClassName}`,
|
|
||||||
children: icons.map((icon, i) => {
|
|
||||||
const tabIcon = MaterialIcon(icon, 'norm', { hexpand: true });
|
|
||||||
const tabName = DoubleRevealer({
|
|
||||||
transition1: 'slide_right',
|
|
||||||
transition2: 'crossfade',
|
|
||||||
duration1: 0,
|
|
||||||
duration2: 0,
|
|
||||||
// duration1: userOptions.animations.durationSmall,
|
|
||||||
// duration2: userOptions.animations.durationSmall,
|
|
||||||
child: Label({
|
|
||||||
className: 'margin-left-5 txt-small',
|
|
||||||
label: names[i],
|
|
||||||
}),
|
|
||||||
revealChild: i === shownIndex.value,
|
|
||||||
})
|
|
||||||
const button = Button({
|
|
||||||
className: 'tab-icon-expandable',
|
|
||||||
tooltipText: names[i],
|
|
||||||
child: Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Box({
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
tabIcon,
|
|
||||||
tabName,
|
|
||||||
]
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: () => shownIndex.value = i,
|
|
||||||
});
|
|
||||||
button.toggleFocus = (value) => {
|
|
||||||
tabIcon.hexpand = !value;
|
|
||||||
button.toggleClassName('tab-icon-expandable-active', value);
|
|
||||||
tabName.toggleRevealChild(value);
|
|
||||||
}
|
|
||||||
return button;
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.children[previousShownIndex].toggleFocus(false);
|
|
||||||
self.children[shownIndex.value].toggleFocus(true);
|
|
||||||
previousShownIndex = shownIndex.value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const tabSection = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [EventBox({
|
|
||||||
onScrollUp: () => mainBox.prevTab(),
|
|
||||||
onScrollDown: () => mainBox.nextTab(),
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
tabs,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
const contentStack = Stack({
|
|
||||||
transition: 'slide_left_right',
|
|
||||||
transitionDuration: transitionDuration,
|
|
||||||
children: children.reduce((acc, currentValue, index) => {
|
|
||||||
acc[index] = currentValue;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
setup: (self) => self.hook(shownIndex, (self) => {
|
|
||||||
self.shown = `${shownIndex.value}`;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const mainBox = Box({
|
|
||||||
attribute: {
|
|
||||||
children: children,
|
|
||||||
shown: shownIndex,
|
|
||||||
names: names,
|
|
||||||
},
|
|
||||||
vertical: true,
|
|
||||||
className: `spacing-v-5 ${className}`,
|
|
||||||
setup: (self) => {
|
|
||||||
self.pack_start(tabSection, false, false, 0);
|
|
||||||
self.pack_end(contentStack, true, true, 0);
|
|
||||||
setup(self);
|
|
||||||
self.hook(shownIndex, (self) => onChange(self, shownIndex.value));
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
|
||||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
|
||||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
|
||||||
mainBox.focusName = (name) => {
|
|
||||||
const focusIndex = names.indexOf(name);
|
|
||||||
if (focusIndex !== -1) {
|
|
||||||
shownIndex.value = focusIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mainBox.shown = shownIndex;
|
|
||||||
|
|
||||||
return mainBox;
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
import GLib from 'gi://GLib';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
import userOverrides from '../../user_options.js';
|
|
||||||
|
|
||||||
// Default options.
|
|
||||||
// Add overrides in ~/.config/ags/user_options.js
|
|
||||||
let configOptions = {
|
|
||||||
// General stuff
|
|
||||||
'ai': {
|
|
||||||
'defaultGPTProvider': "ollama",
|
|
||||||
'defaultTemperature': 0.9,
|
|
||||||
'enhancements': true,
|
|
||||||
'useHistory': true,
|
|
||||||
'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering
|
|
||||||
'proxyUrl': null, // Can be "socks5://127.0.0.1:9050" or "http://127.0.0.1:8080" for example. Leave it blank if you don't need it.
|
|
||||||
},
|
|
||||||
'animations': {
|
|
||||||
'choreographyDelay': 35,
|
|
||||||
'durationSmall': 110,
|
|
||||||
'durationLarge': 180,
|
|
||||||
},
|
|
||||||
'appearance': {
|
|
||||||
'autoDarkMode': { // Turns on dark mode in certain hours. Time in 24h format
|
|
||||||
'enabled': false,
|
|
||||||
'from': "18:10",
|
|
||||||
'to': "6:10",
|
|
||||||
},
|
|
||||||
'keyboardUseFlag': false, // Use flag emoji instead of abbreviation letters
|
|
||||||
'layerSmoke': false,
|
|
||||||
'layerSmokeStrength': 0.2,
|
|
||||||
'barRoundedCorners': 1,
|
|
||||||
'fakeScreenRounding': 1, // 0: None | 1: Always | 2: When not fullscreen
|
|
||||||
},
|
|
||||||
'apps': {
|
|
||||||
'bluetooth': "blueberry",
|
|
||||||
'imageViewer': "loupe",
|
|
||||||
'network': "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi",
|
|
||||||
'settings': "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center",
|
|
||||||
'taskManager': "gnome-usage",
|
|
||||||
'terminal': "foot", // This is only for shell actions
|
|
||||||
},
|
|
||||||
'battery': {
|
|
||||||
'low': 20,
|
|
||||||
'critical': 10,
|
|
||||||
'warnLevels': [20, 15, 5],
|
|
||||||
'warnTitles': ["Low battery", "Very low battery", 'Critical Battery'],
|
|
||||||
'warnMessages': ["Plug in the charger", "You there?", 'PLUG THE CHARGER ALREADY'],
|
|
||||||
'suspendThreshold': 3,
|
|
||||||
},
|
|
||||||
'brightness': {
|
|
||||||
// Object of controller names for each monitor, either "brightnessctl" or "ddcutil" or "auto"
|
|
||||||
// 'default' one will be used if unspecified
|
|
||||||
// Examples
|
|
||||||
// 'eDP-1': "brightnessctl",
|
|
||||||
// 'DP-1': "ddcutil",
|
|
||||||
'controllers': {
|
|
||||||
'default': "ddcutil",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'cheatsheet': {
|
|
||||||
'keybinds': {
|
|
||||||
'configPath': "" // Path to hyprland keybind config file. Leave empty for default (~/.config/hypr/hyprland/keybinds.conf)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'gaming': {
|
|
||||||
'crosshair': {
|
|
||||||
'size': 20,
|
|
||||||
'color': 'rgba(113,227,32,0.9)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'monitors': {
|
|
||||||
'scaleMethod': "division", // Either "division" [default] or "gdk"
|
|
||||||
},
|
|
||||||
'music': {
|
|
||||||
'preferredPlayer': "plasma-browser-integration",
|
|
||||||
},
|
|
||||||
'onScreenKeyboard': {
|
|
||||||
'layout': "qwerty_full", // See modules/onscreenkeyboard/onscreenkeyboard.js for available layouts
|
|
||||||
},
|
|
||||||
'overview': {
|
|
||||||
'scale': 0.18, // Relative to screen size
|
|
||||||
'numOfRows': 2,
|
|
||||||
'numOfCols': 5,
|
|
||||||
'wsNumScale': 0.09,
|
|
||||||
'wsNumMarginScale': 0.07,
|
|
||||||
},
|
|
||||||
'sidebar': {
|
|
||||||
'ai': {
|
|
||||||
'extraGptModels': {
|
|
||||||
'oxygen3': {
|
|
||||||
'name': 'Oxygen (GPT-3.5)',
|
|
||||||
'logo_name': 'ai-oxygen-symbolic',
|
|
||||||
'description': 'An API from Tornado Softwares\nPricing: Free: 100/day\nRequires you to join their Discord for a key',
|
|
||||||
'base_url': 'https://app.oxyapi.uk/v1/chat/completions',
|
|
||||||
'key_get_url': 'https://discord.com/invite/kM6MaCqGKA',
|
|
||||||
'key_file': 'oxygen_key.txt',
|
|
||||||
'model': 'gpt-3.5-turbo',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'image': {
|
|
||||||
'columns': 2,
|
|
||||||
'batchCount': 20,
|
|
||||||
'allowNsfw': true,
|
|
||||||
},
|
|
||||||
'pages': {
|
|
||||||
'order': ["apis", "tools"],
|
|
||||||
'apis': {
|
|
||||||
'order': ["gpt", "waifu", "booru"],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'search': {
|
|
||||||
'enableFeatures': {
|
|
||||||
'actions': true,
|
|
||||||
'commands': true,
|
|
||||||
'mathResults': true,
|
|
||||||
'directorySearch': true,
|
|
||||||
'aiSearch': false,
|
|
||||||
'webSearch': false,
|
|
||||||
},
|
|
||||||
'engineBaseUrl': "https://www.google.com/search?q=",
|
|
||||||
'excludedSites': ["quora.com"],
|
|
||||||
},
|
|
||||||
'time': {
|
|
||||||
// See https://docs.gtk.org/glib/method.DateTime.format.html
|
|
||||||
// Here's the 12h format: "%I:%M%P"
|
|
||||||
// For seconds, add "%S" and set interval to 1000
|
|
||||||
'format': "%H:%M",
|
|
||||||
'interval': 5000,
|
|
||||||
'dateFormatLong': "%A, %d/%m", // On bar
|
|
||||||
'dateInterval': 5000,
|
|
||||||
'dateFormat': "%d/%m", // On notif time
|
|
||||||
},
|
|
||||||
'weather': {
|
|
||||||
'city': "",
|
|
||||||
'preferredUnit': "C", // Either C or F
|
|
||||||
},
|
|
||||||
'workspaces': {
|
|
||||||
'shown': 10,
|
|
||||||
},
|
|
||||||
'dock': {
|
|
||||||
'enabled': false,
|
|
||||||
'hiddenThickness': 5,
|
|
||||||
'pinnedApps': ['floorp', 'org.gnome.Nautilus'],
|
|
||||||
'layer': 'top',
|
|
||||||
'monitorExclusivity': true, // Dock will move to other monitor along with focus if enabled
|
|
||||||
'searchPinnedAppIcons': false, // Try to search for the correct icon if the app class isn't an icon name
|
|
||||||
'trigger': ['client-added', 'client-removed'], // client_added, client_move, workspace_active, client_active
|
|
||||||
// Automatically hide dock after `interval` ms since trigger
|
|
||||||
'autoHide': [
|
|
||||||
{
|
|
||||||
'trigger': 'client-added',
|
|
||||||
'interval': 500,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'trigger': 'client-removed',
|
|
||||||
'interval': 500,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Longer stuff
|
|
||||||
'icons': {
|
|
||||||
// Find the window's icon by its class with levenshteinDistance
|
|
||||||
// The file names are processed at startup, so if there
|
|
||||||
// are too many files in the search path it'll affect performance
|
|
||||||
// Example: ['/usr/share/icons/Tela-nord/scalable/apps']
|
|
||||||
'searchPaths': [''],
|
|
||||||
'symbolicIconTheme': {
|
|
||||||
"dark": "Adwaita",
|
|
||||||
"light": "Adwaita",
|
|
||||||
},
|
|
||||||
substitutions: {
|
|
||||||
'code-url-handler': "visual-studio-code",
|
|
||||||
'Code': "visual-studio-code",
|
|
||||||
'GitHub Desktop': "github-desktop",
|
|
||||||
'Minecraft* 1.20.1': "minecraft",
|
|
||||||
'gnome-tweaks': "org.gnome.tweaks",
|
|
||||||
'pavucontrol-qt': "pavucontrol",
|
|
||||||
'wps': "wps-office2019-kprometheus",
|
|
||||||
'wpsoffice': "wps-office2019-kprometheus",
|
|
||||||
'': "image-missing",
|
|
||||||
},
|
|
||||||
regexSubstitutions: [
|
|
||||||
{
|
|
||||||
regex: /^steam_app_(\d+)$/,
|
|
||||||
replace: "steam_icon_$1",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'keybinds': {
|
|
||||||
// Format: Mod1+Mod2+key. CaSe SeNsItIvE!
|
|
||||||
// Modifiers: Shift Ctrl Alt Hyper Meta
|
|
||||||
// See https://docs.gtk.org/gdk3/index.html#constants for the other keys (they are listed as KEY_key)
|
|
||||||
'overview': {
|
|
||||||
'altMoveLeft': "Ctrl+b",
|
|
||||||
'altMoveRight': "Ctrl+f",
|
|
||||||
'deleteToEnd': "Ctrl+k",
|
|
||||||
},
|
|
||||||
'sidebar': {
|
|
||||||
'apis': {
|
|
||||||
'nextTab': "Page_Down",
|
|
||||||
'prevTab': "Page_Up",
|
|
||||||
},
|
|
||||||
'options': { // Right sidebar
|
|
||||||
'nextTab': "Page_Down",
|
|
||||||
'prevTab': "Page_Up",
|
|
||||||
},
|
|
||||||
'pin': "Ctrl+p",
|
|
||||||
'cycleTab': "Ctrl+Tab",
|
|
||||||
'nextTab': "Ctrl+Page_Down",
|
|
||||||
'prevTab': "Ctrl+Page_Up",
|
|
||||||
},
|
|
||||||
'cheatsheet': {
|
|
||||||
'keybinds': {
|
|
||||||
'nextTab': "Page_Down",
|
|
||||||
'prevTab': "Page_Up",
|
|
||||||
},
|
|
||||||
'nextTab': "Ctrl+Page_Down",
|
|
||||||
'prevTab': "Ctrl+Page_Up",
|
|
||||||
'cycleTab': "Ctrl+Tab",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'bar': {
|
|
||||||
// Array of bar modes for each monitor. Hit Ctrl+Alt+Slash to cycle.
|
|
||||||
// Modes: "normal", "focus" (workspace indicator only), "nothing"
|
|
||||||
// Example for four monitors: ["normal", "focus", "normal", "nothing"]
|
|
||||||
'modes': ["normal"]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override defaults with user's options
|
|
||||||
let optionsOkay = true;
|
|
||||||
function overrideConfigRecursive(userOverrides, configOptions = {}, check = true) {
|
|
||||||
for (const [key, value] of Object.entries(userOverrides)) {
|
|
||||||
if (configOptions[key] === undefined && check) {
|
|
||||||
optionsOkay = false;
|
|
||||||
}
|
|
||||||
else if (typeof value === 'object' && !(value instanceof Array)) {
|
|
||||||
if (key === "substitutions" || key === "regexSubstitutions" || key === "extraGptModels") {
|
|
||||||
overrideConfigRecursive(value, configOptions[key], false);
|
|
||||||
} else overrideConfigRecursive(value, configOptions[key]);
|
|
||||||
} else {
|
|
||||||
configOptions[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
overrideConfigRecursive(userOverrides, configOptions);
|
|
||||||
if (!optionsOkay) Utils.timeout(2000, () => Utils.execAsync(['notify-send',
|
|
||||||
'Update your user options',
|
|
||||||
'One or more config options don\'t exist',
|
|
||||||
'-a', 'ags',
|
|
||||||
]).catch(print))
|
|
||||||
|
|
||||||
globalThis['userOptions'] = configOptions;
|
|
||||||
export default configOptions;
|
|
|
@ -1,14 +0,0 @@
|
||||||
const { Gio, GLib, Gtk } = imports.gi;
|
|
||||||
|
|
||||||
export function fileExists(filePath) {
|
|
||||||
let file = Gio.File.new_for_path(filePath);
|
|
||||||
return file.query_exists(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandTilde(path) {
|
|
||||||
if (path.startsWith('~')) {
|
|
||||||
return GLib.get_home_dir() + path.slice(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
|
|
||||||
export function iconExists(iconName) {
|
|
||||||
let iconTheme = Gtk.IconTheme.get_default();
|
|
||||||
return iconTheme.has_icon(iconName);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function substitute(str) {
|
|
||||||
// Normal substitutions
|
|
||||||
if (userOptions.icons.substitutions[str])
|
|
||||||
return userOptions.icons.substitutions[str];
|
|
||||||
|
|
||||||
// Regex substitutions
|
|
||||||
for (let i = 0; i < userOptions.icons.regexSubstitutions.length; i++) {
|
|
||||||
const substitution = userOptions.icons.regexSubstitutions[i];
|
|
||||||
const replacedName = str.replace(
|
|
||||||
substitution.regex,
|
|
||||||
substitution.replace,
|
|
||||||
);
|
|
||||||
if (replacedName != str) return replacedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guess: convert to kebab case
|
|
||||||
if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, "-");
|
|
||||||
|
|
||||||
// Original string
|
|
||||||
return str;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
export function clamp(x, min, max) {
|
|
||||||
return Math.min(Math.max(x, min), max);
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
// Converts from Markdown to Pango. This does not support code blocks.
|
|
||||||
// For illogical-impulse, code blocks are treated separately, in their own GtkSourceView widgets.
|
|
||||||
// Partly inherited from https://github.com/ubunatic/md2pango
|
|
||||||
|
|
||||||
const monospaceFonts = 'JetBrains Mono NF, JetBrains Mono Nerd Font, JetBrains Mono NL, SpaceMono NF, SpaceMono Nerd Font, monospace';
|
|
||||||
|
|
||||||
const replacements = {
|
|
||||||
'indents': [
|
|
||||||
{ name: 'BULLET', re: /^(\s*)([\*\-]\s)(.*)(\s*)$/, sub: ' $1- $3' },
|
|
||||||
{ name: 'NUMBERING', re: /^(\s*[0-9]+\.\s)(.*)(\s*)$/, sub: ' $1 $2' },
|
|
||||||
],
|
|
||||||
'escapes': [
|
|
||||||
{ name: 'COMMENT', re: /<!--.*-->/, sub: '' },
|
|
||||||
{ name: 'AMPERSTAND', re: /&/g, sub: '&' },
|
|
||||||
{ name: 'LESSTHAN', re: /</g, sub: '<' },
|
|
||||||
{ name: 'GREATERTHAN', re: />/g, sub: '>' },
|
|
||||||
],
|
|
||||||
'sections': [
|
|
||||||
{ name: 'H1', re: /^(#\s+)(.*)(\s*)$/, sub: '<span font_weight="bold" size="170%">$2</span>' },
|
|
||||||
{ name: 'H2', re: /^(##\s+)(.*)(\s*)$/, sub: '<span font_weight="bold" size="150%">$2</span>' },
|
|
||||||
{ name: 'H3', re: /^(###\s+)(.*)(\s*)$/, sub: '<span font_weight="bold" size="125%">$2</span>' },
|
|
||||||
{ name: 'H4', re: /^(####\s+)(.*)(\s*)$/, sub: '<span font_weight="bold" size="100%">$2</span>' },
|
|
||||||
{ name: 'H5', re: /^(#####\s+)(.*)(\s*)$/, sub: '<span font_weight="bold" size="90%">$2</span>' },
|
|
||||||
],
|
|
||||||
'styles': [
|
|
||||||
{ name: 'BOLD', re: /(\*\*)(\S[\s\S]*?\S)(\*\*)/g, sub: "<b>$2</b>" },
|
|
||||||
{ name: 'UND', re: /(__)(\S[\s\S]*?\S)(__)/g, sub: "<u>$2</u>" },
|
|
||||||
{ name: 'EMPH', re: /\*(\S.*?\S)\*/g, sub: "<i>$1</i>" },
|
|
||||||
// { name: 'EMPH', re: /_(\S.*?\S)_/g, sub: "<i>$1</i>" },
|
|
||||||
{ name: 'HEXCOLOR', re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: '<span bgcolor="#$1" fgcolor="#000000" font_family="' + monospaceFonts + '">#$1</span>' },
|
|
||||||
{ name: 'INLCODE', re: /(`)([^`]*)(`)/g, sub: '<span font_weight="bold" font_family="' + monospaceFonts + '">$2</span>' },
|
|
||||||
// { name: 'UND', re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "<u>$2</u>" },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
const replaceCategory = (text, replaces) => {
|
|
||||||
for (const type of replaces) {
|
|
||||||
text = text.replace(type.re, type.sub);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main function
|
|
||||||
|
|
||||||
export default (text) => {
|
|
||||||
let lines = text.split('\n')
|
|
||||||
let output = [];
|
|
||||||
// Replace
|
|
||||||
for (const line of lines) {
|
|
||||||
let result = line;
|
|
||||||
result = replaceCategory(result, replacements.indents);
|
|
||||||
result = replaceCategory(result, replacements.escapes);
|
|
||||||
result = replaceCategory(result, replacements.sections);
|
|
||||||
result = replaceCategory(result, replacements.styles);
|
|
||||||
output.push(result)
|
|
||||||
}
|
|
||||||
// Remove trailing whitespaces
|
|
||||||
output = output.map(line => line.replace(/ +$/, ''))
|
|
||||||
return output.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export const markdownTest = `## Inline formatting
|
|
||||||
- **Bold** *Italics* __Underline__
|
|
||||||
- \`Monospace text\` 🤓
|
|
||||||
- Colors
|
|
||||||
- Nvidia green #7ABB08
|
|
||||||
- Soundcloud orange #FF5500
|
|
||||||
## Code block
|
|
||||||
\`\`\`cpp
|
|
||||||
#include <bits/stdc++.h>
|
|
||||||
const std::string GREETING="UwU";
|
|
||||||
int main() { std::cout << GREETING; }
|
|
||||||
\`\`\`
|
|
||||||
## LaTeX
|
|
||||||
\`\`\`latex
|
|
||||||
\\frac{d}{dx} \\left( \\frac{x-438}{x^2+23x-7} \\right) = \\frac{-x^2 + 869}{(x^2+23x-7)^2} \\\\ → \\\\ cos(2x) = 2cos^2(x) - 1 = 1 - 2sin^2(x) = cos^2(x) - sin^2(x)
|
|
||||||
\`\`\`
|
|
||||||
`;
|
|
|
@ -1,61 +0,0 @@
|
||||||
const { GLib } = imports.gi;
|
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
|
|
||||||
export const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2 | sed "s/\\"//g"'`).trim();
|
|
||||||
export const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'popos' || distroID == 'raspbian' || distroID == 'kali');
|
|
||||||
export const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros' || distroID == 'cachyos');
|
|
||||||
export const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`);
|
|
||||||
|
|
||||||
const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/colormode.txt`;
|
|
||||||
export const darkMode = Variable(!(Utils.readFile(LIGHTDARK_FILE_LOCATION).split('\n')[0].trim() == 'light'));
|
|
||||||
darkMode.connect('changed', ({ value }) => {
|
|
||||||
let lightdark = value ? "dark" : "light";
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "1s/.*/${lightdark}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`])
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
|
|
||||||
.then(execAsync(['bash', '-c', `command -v darkman && darkman set ${lightdark}`])) // Optional darkman integration
|
|
||||||
.catch(print);
|
|
||||||
});
|
|
||||||
globalThis['darkMode'] = darkMode;
|
|
||||||
export const hasPlasmaIntegration = !!Utils.exec('bash -c "command -v plasma-browser-integration-host"');
|
|
||||||
|
|
||||||
export const getDistroIcon = () => {
|
|
||||||
// Arches
|
|
||||||
if(distroID == 'arch') return 'arch-symbolic';
|
|
||||||
if(distroID == 'endeavouros') return 'endeavouros-symbolic';
|
|
||||||
if(distroID == 'cachyos') return 'cachyos-symbolic';
|
|
||||||
// Funny flake
|
|
||||||
if(distroID == 'nixos') return 'nixos-symbolic';
|
|
||||||
// Cool thing
|
|
||||||
if(distroID == 'fedora') return 'fedora-symbolic';
|
|
||||||
// Debians
|
|
||||||
if(distroID == 'linuxmint') return 'ubuntu-symbolic';
|
|
||||||
if(distroID == 'ubuntu') return 'ubuntu-symbolic';
|
|
||||||
if(distroID == 'debian') return 'debian-symbolic';
|
|
||||||
if(distroID == 'zorin') return 'ubuntu-symbolic';
|
|
||||||
if(distroID == 'popos') return 'ubuntu-symbolic';
|
|
||||||
if(distroID == 'raspbian') return 'debian-symbolic';
|
|
||||||
if(distroID == 'kali') return 'debian-symbolic';
|
|
||||||
return 'linux-symbolic';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDistroName = () => {
|
|
||||||
// Arches
|
|
||||||
if(distroID == 'arch') return 'Arch Linux';
|
|
||||||
if(distroID == 'endeavouros') return 'EndeavourOS';
|
|
||||||
if(distroID == 'cachyos') return 'CachyOS';
|
|
||||||
// Funny flake
|
|
||||||
if(distroID == 'nixos') return 'NixOS';
|
|
||||||
// Cool thing
|
|
||||||
if(distroID == 'fedora') return 'Fedora';
|
|
||||||
// Debians
|
|
||||||
if(distroID == 'linuxmint') return 'Linux Mint';
|
|
||||||
if(distroID == 'ubuntu') return 'Ubuntu';
|
|
||||||
if(distroID == 'debian') return 'Debian';
|
|
||||||
if(distroID == 'zorin') return 'Zorin';
|
|
||||||
if(distroID == 'popos') return 'Pop!_OS';
|
|
||||||
if(distroID == 'raspbian') return 'Raspbian';
|
|
||||||
if(distroID == 'kali') return 'Kali Linux';
|
|
||||||
return 'Linux';
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
|
|
||||||
const { Revealer, Scrollable } = Widget;
|
|
||||||
|
|
||||||
export const MarginRevealer = ({
|
|
||||||
transition = 'slide_down',
|
|
||||||
child,
|
|
||||||
revealChild,
|
|
||||||
showClass = 'element-show', // These are for animation curve, they don't really hide
|
|
||||||
hideClass = 'element-hide', // Don't put margins in these classes!
|
|
||||||
extraSetup = () => { },
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const widget = Scrollable({
|
|
||||||
...rest,
|
|
||||||
attribute: {
|
|
||||||
'revealChild': true, // It'll be set to false after init if it's supposed to hide
|
|
||||||
'transition': transition,
|
|
||||||
'show': () => {
|
|
||||||
if (widget.attribute.revealChild) return;
|
|
||||||
widget.hscroll = 'never';
|
|
||||||
widget.vscroll = 'never';
|
|
||||||
child.toggleClassName(hideClass, false);
|
|
||||||
child.toggleClassName(showClass, true);
|
|
||||||
widget.attribute.revealChild = true;
|
|
||||||
child.css = 'margin: 0px;';
|
|
||||||
},
|
|
||||||
'hide': () => {
|
|
||||||
if (!widget.attribute.revealChild) return;
|
|
||||||
child.toggleClassName(hideClass, true);
|
|
||||||
child.toggleClassName(showClass, false);
|
|
||||||
widget.attribute.revealChild = false;
|
|
||||||
if (widget.attribute.transition == 'slide_left')
|
|
||||||
child.css = `margin-right: -${child.get_allocated_width()}px;`;
|
|
||||||
else if (widget.attribute.transition == 'slide_right')
|
|
||||||
child.css = `margin-left: -${child.get_allocated_width()}px;`;
|
|
||||||
else if (widget.attribute.transition == 'slide_up')
|
|
||||||
child.css = `margin-bottom: -${child.get_allocated_height()}px;`;
|
|
||||||
else if (widget.attribute.transition == 'slide_down')
|
|
||||||
child.css = `margin-top: -${child.get_allocated_height()}px;`;
|
|
||||||
},
|
|
||||||
'toggle': () => {
|
|
||||||
if (widget.attribute.revealChild) widget.attribute.hide();
|
|
||||||
else widget.attribute.show();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
hscroll: `${revealChild ? 'never' : 'always'}`,
|
|
||||||
vscroll: `${revealChild ? 'never' : 'always'}`,
|
|
||||||
setup: (self) => {
|
|
||||||
extraSetup(self);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
child.toggleClassName(`${revealChild ? showClass : hideClass}`, true);
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Allow reveal update. Currently this just helps at declaration
|
|
||||||
export const DoubleRevealer = ({
|
|
||||||
transition1 = 'slide_right',
|
|
||||||
transition2 = 'slide_left',
|
|
||||||
duration1 = 150,
|
|
||||||
duration2 = 150,
|
|
||||||
child,
|
|
||||||
revealChild,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const r2 = Revealer({
|
|
||||||
transition: transition2,
|
|
||||||
transitionDuration: duration2,
|
|
||||||
revealChild: revealChild,
|
|
||||||
child: child,
|
|
||||||
});
|
|
||||||
const r1 = Revealer({
|
|
||||||
transition: transition1,
|
|
||||||
transitionDuration: duration1,
|
|
||||||
revealChild: revealChild,
|
|
||||||
child: r2,
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
r1.toggleRevealChild = (value) => {
|
|
||||||
r1.revealChild = value;
|
|
||||||
r2.revealChild = value;
|
|
||||||
}
|
|
||||||
return r1;
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box, Window } = Widget;
|
|
||||||
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
name,
|
|
||||||
child,
|
|
||||||
showClassName = "",
|
|
||||||
hideClassName = "",
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
return Window({
|
|
||||||
name,
|
|
||||||
visible: false,
|
|
||||||
layer: 'overlay',
|
|
||||||
...props,
|
|
||||||
|
|
||||||
child: Box({
|
|
||||||
setup: (self) => {
|
|
||||||
self.keybind("Escape", () => closeEverything());
|
|
||||||
if (showClassName != "" && hideClassName !== "") {
|
|
||||||
self.hook(App, (self, currentName, visible) => {
|
|
||||||
if (currentName === name) {
|
|
||||||
self.toggleClassName(hideClassName, !visible);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (showClassName !== "" && hideClassName !== "")
|
|
||||||
self.className = `${showClassName} ${hideClassName}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
import Cairo from 'gi://cairo?version=1.0';
|
|
||||||
|
|
||||||
export const dummyRegion = new Cairo.Region();
|
|
||||||
export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
|
|
|
@ -1,57 +0,0 @@
|
||||||
const { Gdk } = imports.gi;
|
|
||||||
|
|
||||||
export function setupCursorHover(button) { // Hand pointing cursor on hover
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
button.connect('enter-notify-event', () => {
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'pointer');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
|
|
||||||
button.connect('leave-notify-event', () => {
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'default');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupCursorHoverAim(button) { // Crosshair cursor on hover
|
|
||||||
button.connect('enter-notify-event', () => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'crosshair');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
|
|
||||||
button.connect('leave-notify-event', () => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'default');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupCursorHoverGrab(button) { // Hand ready to grab on hover
|
|
||||||
button.connect('enter-notify-event', () => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'grab');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
|
|
||||||
button.connect('leave-notify-event', () => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'default');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupCursorHoverInfo(button) { // "?" mark cursor on hover
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
button.connect('enter-notify-event', () => {
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'help');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
|
|
||||||
button.connect('leave-notify-event', () => {
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'default');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
const { Gdk } = imports.gi;
|
|
||||||
|
|
||||||
const MODS = {
|
|
||||||
'Shift': Gdk.ModifierType.SHIFT_MASK,
|
|
||||||
'Ctrl': Gdk.ModifierType.CONTROL_MASK,
|
|
||||||
'Alt': Gdk.ModifierType.ALT_MASK,
|
|
||||||
'Hyper': Gdk.ModifierType.HYPER_MASK,
|
|
||||||
'Meta': Gdk.ModifierType.META_MASK
|
|
||||||
}
|
|
||||||
|
|
||||||
export const checkKeybind = (event, keybind) => {
|
|
||||||
const pressedModMask = event.get_state()[1];
|
|
||||||
const pressedKey = event.get_keyval()[1];
|
|
||||||
const keys = keybind.split('+');
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i] in MODS) {
|
|
||||||
if (!(pressedModMask & MODS[keys[i]])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (pressedKey !== Gdk[`KEY_${keys[i]}`]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
const { GLib, Gdk, Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Cairo = imports.cairo;
|
|
||||||
const Pango = imports.gi.Pango;
|
|
||||||
const PangoCairo = imports.gi.PangoCairo;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box, DrawingArea, EventBox } = Widget;
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
|
|
||||||
const dummyWs = Box({ className: 'bar-ws-focus' }); // Not shown. Only for getting size props
|
|
||||||
const dummyActiveWs = Box({ className: 'bar-ws-focus bar-ws-focus-active' }); // Not shown. Only for getting size props
|
|
||||||
const dummyOccupiedWs = Box({ className: 'bar-ws-focus bar-ws-focus-occupied' }); // Not shown. Only for getting size props
|
|
||||||
|
|
||||||
const WS_TAKEN_WIDTH_MULTIPLIER = 1.4;
|
|
||||||
const floor = Math.floor;
|
|
||||||
const ceil = Math.ceil;
|
|
||||||
|
|
||||||
// Font size = workspace id
|
|
||||||
const WorkspaceContents = (count = 10) => {
|
|
||||||
return DrawingArea({
|
|
||||||
className: 'menu-decel',
|
|
||||||
attribute: {
|
|
||||||
lastImmediateActiveWs: 0,
|
|
||||||
immediateActiveWs: 0,
|
|
||||||
initialized: false,
|
|
||||||
workspaceMask: 0,
|
|
||||||
workspaceGroup: 0,
|
|
||||||
updateMask: (self) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / count) * userOptions.workspaces.shown;
|
|
||||||
// if (self.attribute.initialized) return; // We only need this to run once
|
|
||||||
const workspaces = Hyprland.workspaces;
|
|
||||||
let workspaceMask = 0;
|
|
||||||
for (let i = 0; i < workspaces.length; i++) {
|
|
||||||
const ws = workspaces[i];
|
|
||||||
if (ws.id <= offset || ws.id > offset + count) continue; // Out of range, ignore
|
|
||||||
if (workspaces[i].windows > 0)
|
|
||||||
workspaceMask |= (1 << (ws.id - offset));
|
|
||||||
}
|
|
||||||
// console.log('Mask:', workspaceMask.toString(2));
|
|
||||||
self.attribute.workspaceMask = workspaceMask;
|
|
||||||
// self.attribute.initialized = true;
|
|
||||||
self.queue_draw();
|
|
||||||
},
|
|
||||||
toggleMask: (self, occupied, name) => {
|
|
||||||
if (occupied) self.attribute.workspaceMask |= (1 << parseInt(name));
|
|
||||||
else self.attribute.workspaceMask &= ~(1 << parseInt(name));
|
|
||||||
self.queue_draw();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (area) => area
|
|
||||||
.hook(Hyprland.active.workspace, (self) => {
|
|
||||||
const newActiveWs = (Hyprland.active.workspace.id - 1) % count + 1;
|
|
||||||
self.setCss(`font-size: ${newActiveWs}px;`);
|
|
||||||
self.attribute.lastImmediateActiveWs = self.attribute.immediateActiveWs;
|
|
||||||
self.attribute.immediateActiveWs = newActiveWs;
|
|
||||||
const previousGroup = self.attribute.workspaceGroup;
|
|
||||||
const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / count);
|
|
||||||
if (currentGroup !== previousGroup) {
|
|
||||||
self.attribute.updateMask(self);
|
|
||||||
self.attribute.workspaceGroup = currentGroup;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.hook(Hyprland, (self) => self.attribute.updateMask(self), 'notify::workspaces')
|
|
||||||
.on('draw', Lang.bind(area, (area, cr) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / count) * userOptions.workspaces.shown;
|
|
||||||
|
|
||||||
const allocation = area.get_allocation();
|
|
||||||
const { width, height } = allocation;
|
|
||||||
|
|
||||||
const workspaceStyleContext = dummyWs.get_style_context();
|
|
||||||
const workspaceDiameter = workspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
const workspaceRadius = workspaceDiameter / 2;
|
|
||||||
const wsbg = workspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const occupiedWorkspaceStyleContext = dummyOccupiedWs.get_style_context();
|
|
||||||
const occupiedbg = occupiedWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const activeWorkspaceStyleContext = dummyActiveWs.get_style_context();
|
|
||||||
const activeWorkspaceWidth = activeWorkspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
// const activeWorkspaceWidth = 100;
|
|
||||||
const activebg = activeWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const widgetStyleContext = area.get_style_context();
|
|
||||||
const activeWs = widgetStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
|
||||||
const lastImmediateActiveWs = area.attribute.lastImmediateActiveWs;
|
|
||||||
const immediateActiveWs = area.attribute.immediateActiveWs;
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
area.set_size_request(workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER * (count - 1) + activeWorkspaceWidth, -1);
|
|
||||||
for (let i = 1; i <= count; i++) {
|
|
||||||
if (i == immediateActiveWs) continue;
|
|
||||||
let colors = {};
|
|
||||||
if (area.attribute.workspaceMask & (1 << i)) colors = occupiedbg;
|
|
||||||
else colors = wsbg;
|
|
||||||
|
|
||||||
// if ((i == immediateActiveWs + 1 && immediateActiveWs < activeWs) ||
|
|
||||||
// (i == immediateActiveWs + 1 && immediateActiveWs < activeWs)) {
|
|
||||||
// const widthPercentage = (i == immediateActiveWs - 1) ?
|
|
||||||
// 1 - (immediateActiveWs - activeWs) :
|
|
||||||
// activeWs - immediateActiveWs;
|
|
||||||
// cr.setSourceRGBA(colors.red * widthPercentage + activebg.red * (1 - widthPercentage),
|
|
||||||
// colors.green * widthPercentage + activebg.green * (1 - widthPercentage),
|
|
||||||
// colors.blue * widthPercentage + activebg.blue * (1 - widthPercentage),
|
|
||||||
// colors.alpha);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
cr.setSourceRGBA(colors.red, colors.green, colors.blue, colors.alpha)
|
|
||||||
|
|
||||||
const centerX = (i <= activeWs) ?
|
|
||||||
(-workspaceRadius + (workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER * i))
|
|
||||||
: -workspaceRadius + workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER * (count - 1) + activeWorkspaceWidth - ((count - i) * workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER);
|
|
||||||
cr.arc(centerX, height / 2, workspaceRadius, 0, 2 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
// What if shrinking
|
|
||||||
if (i == floor(activeWs) && immediateActiveWs > activeWs) { // To right
|
|
||||||
const widthPercentage = 1 - (ceil(activeWs) - activeWs);
|
|
||||||
const leftX = centerX;
|
|
||||||
const wsWidth = (activeWorkspaceWidth - (workspaceDiameter * 1.5)) * (1 - widthPercentage);
|
|
||||||
cr.rectangle(leftX, height / 2 - workspaceRadius, wsWidth, workspaceDiameter);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(leftX + wsWidth, height / 2, workspaceRadius, 0, Math.PI * 2);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
else if (i == ceil(activeWs) && immediateActiveWs < activeWs) { // To left
|
|
||||||
const widthPercentage = activeWs - floor(activeWs);
|
|
||||||
const rightX = centerX;
|
|
||||||
const wsWidth = (activeWorkspaceWidth - (workspaceDiameter * 1.5)) * widthPercentage;
|
|
||||||
const leftX = rightX - wsWidth;
|
|
||||||
cr.rectangle(leftX, height / 2 - workspaceRadius, wsWidth, workspaceDiameter);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(leftX, height / 2, workspaceRadius, 0, Math.PI * 2);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let widthPercentage, leftX, rightX, activeWsWidth;
|
|
||||||
cr.setSourceRGBA(activebg.red, activebg.green, activebg.blue, activebg.alpha);
|
|
||||||
if (immediateActiveWs > activeWs) { // To right
|
|
||||||
const immediateActiveWs = ceil(activeWs);
|
|
||||||
widthPercentage = immediateActiveWs - activeWs;
|
|
||||||
rightX = -workspaceRadius + workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER * (count - 1) + activeWorkspaceWidth - ((count - immediateActiveWs) * workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER);
|
|
||||||
activeWsWidth = (activeWorkspaceWidth - (workspaceDiameter * 1.5)) * (1 - widthPercentage);
|
|
||||||
leftX = rightX - activeWsWidth;
|
|
||||||
|
|
||||||
cr.arc(leftX, height / 2, workspaceRadius, 0, Math.PI * 2); // Should be 0.5 * Math.PI, 1.5 * Math.PI in theory but it leaves a weird 1px gap
|
|
||||||
cr.fill();
|
|
||||||
cr.rectangle(leftX, height / 2 - workspaceRadius, activeWsWidth, workspaceDiameter);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(leftX + activeWsWidth, height / 2, workspaceRadius, 0, Math.PI * 2);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
else { // To left
|
|
||||||
const immediateActiveWs = floor(activeWs);
|
|
||||||
widthPercentage = 1 - (activeWs - immediateActiveWs);
|
|
||||||
leftX = -workspaceRadius + (workspaceDiameter * WS_TAKEN_WIDTH_MULTIPLIER * immediateActiveWs);
|
|
||||||
activeWsWidth = (activeWorkspaceWidth - (workspaceDiameter * 1.5)) * widthPercentage
|
|
||||||
|
|
||||||
cr.arc(leftX, height / 2, workspaceRadius, 0, Math.PI * 2); // Should be 0.5 * Math.PI, 1.5 * Math.PI in theory but it leaves a weird 1px gap
|
|
||||||
cr.fill();
|
|
||||||
cr.rectangle(leftX, height / 2 - workspaceRadius, activeWsWidth, workspaceDiameter);
|
|
||||||
cr.fill();
|
|
||||||
cr.arc(leftX + activeWsWidth, height / 2, workspaceRadius, 0, Math.PI * 2);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => EventBox({
|
|
||||||
onScrollUp: () => Hyprland.messageAsync(`dispatch workspace -1`).catch(print),
|
|
||||||
onScrollDown: () => Hyprland.messageAsync(`dispatch workspace +1`).catch(print),
|
|
||||||
onMiddleClick: () => toggleWindowOnAllMonitors('osk'),
|
|
||||||
onSecondaryClick: () => App.toggleWindow('overview'),
|
|
||||||
attribute: {
|
|
||||||
clicked: false,
|
|
||||||
ws_group: 0,
|
|
||||||
},
|
|
||||||
child: Box({
|
|
||||||
homogeneous: true,
|
|
||||||
// className: 'bar-group-margin',
|
|
||||||
children: [Box({
|
|
||||||
// className: 'bar-group bar-group-standalone bar-group-pad',
|
|
||||||
css: 'min-width: 2px;',
|
|
||||||
children: [WorkspaceContents(userOptions.workspaces.shown)],
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
setup: (self) => {
|
|
||||||
self.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
|
|
||||||
self.on('motion-notify-event', (self, event) => {
|
|
||||||
if (!self.attribute.clicked) return;
|
|
||||||
const [_, cursorX, cursorY] = event.get_coords();
|
|
||||||
const widgetWidth = self.get_allocation().width;
|
|
||||||
const wsId = Math.ceil(cursorX * userOptions.workspaces.shown / widgetWidth);
|
|
||||||
Utils.execAsync([`${App.configDir}/scripts/hyprland/workspace_action.sh`, 'workspace', `${wsId}`])
|
|
||||||
.catch(print);
|
|
||||||
})
|
|
||||||
self.on('button-press-event', (self, event) => {
|
|
||||||
if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
|
|
||||||
self.attribute.clicked = true;
|
|
||||||
const [_, cursorX, cursorY] = event.get_coords();
|
|
||||||
const widgetWidth = self.get_allocation().width;
|
|
||||||
// const wsId = Math.ceil(cursorX * NUM_OF_WORKSPACES_PER_GROUP / widgetWidth) + self.attribute.ws_group * NUM_OF_WORKSPACES_PER_GROUP;
|
|
||||||
// Hyprland.messageAsync(`dispatch workspace ${wsId}`).catch(print);
|
|
||||||
const wsId = Math.ceil(cursorX * userOptions.workspaces.shown / widgetWidth);
|
|
||||||
Utils.execAsync([`${App.configDir}/scripts/hyprland/workspace_action.sh`, 'workspace', `${wsId}`])
|
|
||||||
.catch(print);
|
|
||||||
})
|
|
||||||
self.on('button-release-event', (self) => self.attribute.clicked = false);
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,121 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
|
|
||||||
|
|
||||||
import WindowTitle from "./normal/spaceleft.js";
|
|
||||||
import Indicators from "./normal/spaceright.js";
|
|
||||||
import Music from "./normal/music.js";
|
|
||||||
import System from "./normal/system.js";
|
|
||||||
import { enableClickthrough } from "../.widgetutils/clickthrough.js";
|
|
||||||
import { RoundedCorner } from "../.commonwidgets/cairo_roundedcorner.js";
|
|
||||||
import { currentShellMode } from '../../variables.js';
|
|
||||||
|
|
||||||
const NormalOptionalWorkspaces = async () => {
|
|
||||||
try {
|
|
||||||
return (await import('./normal/workspaces_hyprland.js')).default();
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const FocusOptionalWorkspaces = async () => {
|
|
||||||
try {
|
|
||||||
return (await import('./focus/workspaces_hyprland.js')).default();
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Bar = async (monitor = 0) => {
|
|
||||||
const SideModule = (children) => Widget.Box({
|
|
||||||
className: 'bar-sidemodule',
|
|
||||||
children: children,
|
|
||||||
});
|
|
||||||
const normalBarContent = Widget.CenterBox({
|
|
||||||
className: 'bar-bg',
|
|
||||||
setup: (self) => {
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
// execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print);
|
|
||||||
},
|
|
||||||
startWidget: (await WindowTitle(monitor)),
|
|
||||||
centerWidget: Widget.Box({
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
SideModule([Music()]),
|
|
||||||
Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [await NormalOptionalWorkspaces()],
|
|
||||||
}),
|
|
||||||
SideModule([System()]),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
endWidget: Indicators(monitor),
|
|
||||||
});
|
|
||||||
const focusedBarContent = Widget.CenterBox({
|
|
||||||
className: 'bar-bg-focus',
|
|
||||||
startWidget: Widget.Box({}),
|
|
||||||
centerWidget: Widget.Box({
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
SideModule([]),
|
|
||||||
Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [await FocusOptionalWorkspaces()],
|
|
||||||
}),
|
|
||||||
SideModule([]),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
endWidget: Widget.Box({}),
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Battery, (self) => {
|
|
||||||
if (!Battery.available) return;
|
|
||||||
self.toggleClassName('bar-bg-focus-batterylow', Battery.percent <= userOptions.battery.low);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const nothingContent = Widget.Box({
|
|
||||||
className: 'bar-bg-nothing',
|
|
||||||
})
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `bar${monitor}`,
|
|
||||||
anchor: ['top', 'left', 'right'],
|
|
||||||
exclusivity: 'exclusive',
|
|
||||||
visible: true,
|
|
||||||
child: Widget.Stack({
|
|
||||||
homogeneous: false,
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
children: {
|
|
||||||
'normal': normalBarContent,
|
|
||||||
'focus': focusedBarContent,
|
|
||||||
'nothing': nothingContent,
|
|
||||||
},
|
|
||||||
setup: (self) => self.hook(currentShellMode, (self) => {
|
|
||||||
self.shown = currentShellMode.value[monitor];
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BarCornerTopleft = (monitor = 0) => Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `barcornertl${monitor}`,
|
|
||||||
layer: 'top',
|
|
||||||
anchor: ['top', 'left'],
|
|
||||||
exclusivity: 'normal',
|
|
||||||
visible: true,
|
|
||||||
child: RoundedCorner('topleft', { className: 'corner', }),
|
|
||||||
setup: enableClickthrough,
|
|
||||||
});
|
|
||||||
export const BarCornerTopright = (monitor = 0) => Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `barcornertr${monitor}`,
|
|
||||||
layer: 'top',
|
|
||||||
anchor: ['top', 'right'],
|
|
||||||
exclusivity: 'normal',
|
|
||||||
visible: true,
|
|
||||||
child: RoundedCorner('topright', { className: 'corner', }),
|
|
||||||
setup: enableClickthrough,
|
|
||||||
});
|
|
|
@ -1,230 +0,0 @@
|
||||||
const { GLib } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
|
||||||
const { Box, Button, EventBox, Label, Overlay, Revealer, Scrollable } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { AnimatedCircProg } from "../../.commonwidgets/cairo_circularprogress.js";
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import { showMusicControls } from '../../../variables.js';
|
|
||||||
|
|
||||||
const CUSTOM_MODULE_CONTENT_INTERVAL_FILE = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-interval.txt`;
|
|
||||||
const CUSTOM_MODULE_CONTENT_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-poll.sh`;
|
|
||||||
const CUSTOM_MODULE_LEFTCLICK_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-leftclick.sh`;
|
|
||||||
const CUSTOM_MODULE_RIGHTCLICK_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-rightclick.sh`;
|
|
||||||
const CUSTOM_MODULE_MIDDLECLICK_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-middleclick.sh`;
|
|
||||||
const CUSTOM_MODULE_SCROLLUP_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-scrollup.sh`;
|
|
||||||
const CUSTOM_MODULE_SCROLLDOWN_SCRIPT = `${GLib.get_user_cache_dir()}/ags/user/scripts/custom-module-scrolldown.sh`;
|
|
||||||
|
|
||||||
function trimTrackTitle(title) {
|
|
||||||
if (!title) return '';
|
|
||||||
const cleanPatterns = [
|
|
||||||
/【[^】]*】/, // Touhou n weeb stuff
|
|
||||||
" [FREE DOWNLOAD]", // F-777
|
|
||||||
];
|
|
||||||
cleanPatterns.forEach((expr) => title = title.replace(expr, ''));
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BarGroup = ({ child }) => Box({
|
|
||||||
className: 'bar-group-margin bar-sides',
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
className: 'bar-group bar-group-standalone bar-group-pad-system',
|
|
||||||
children: [child],
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const BarResource = (name, icon, command, circprogClassName = 'bar-batt-circprog', textClassName = 'txt-onSurfaceVariant', iconClassName = 'bar-batt') => {
|
|
||||||
const resourceCircProg = AnimatedCircProg({
|
|
||||||
className: `${circprogClassName}`,
|
|
||||||
vpack: 'center',
|
|
||||||
hpack: 'center',
|
|
||||||
});
|
|
||||||
const resourceProgress = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Overlay({
|
|
||||||
child: Box({
|
|
||||||
vpack: 'center',
|
|
||||||
className: `${iconClassName}`,
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
MaterialIcon(icon, 'small'),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
overlays: [resourceCircProg]
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
const resourceLabel = Label({
|
|
||||||
className: `txt-smallie ${textClassName}`,
|
|
||||||
});
|
|
||||||
const widget = Button({
|
|
||||||
onClicked: () => Utils.execAsync(['bash', '-c', `${userOptions.apps.taskManager}`]).catch(print),
|
|
||||||
child: Box({
|
|
||||||
className: `spacing-h-4 ${textClassName}`,
|
|
||||||
children: [
|
|
||||||
resourceProgress,
|
|
||||||
resourceLabel,
|
|
||||||
],
|
|
||||||
setup: (self) => self.poll(5000, () => execAsync(['bash', '-c', command])
|
|
||||||
.then((output) => {
|
|
||||||
resourceCircProg.css = `font-size: ${Number(output)}px;`;
|
|
||||||
resourceLabel.label = `${Math.round(Number(output))}%`;
|
|
||||||
widget.tooltipText = `${name}: ${Math.round(Number(output))}%`;
|
|
||||||
}).catch(print))
|
|
||||||
,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TrackProgress = () => {
|
|
||||||
const _updateProgress = (circprog) => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
if (!mpris) return;
|
|
||||||
// Set circular progress value
|
|
||||||
circprog.css = `font-size: ${Math.max(mpris.position / mpris.length * 100, 0)}px;`
|
|
||||||
}
|
|
||||||
return AnimatedCircProg({
|
|
||||||
className: 'bar-music-circprog',
|
|
||||||
vpack: 'center', hpack: 'center',
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(Mpris, _updateProgress)
|
|
||||||
.poll(3000, _updateProgress)
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const switchToRelativeWorkspace = async (self, num) => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
Hyprland.messageAsync(`dispatch workspace ${num > 0 ? '+' : ''}${num}`).catch(print);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
// TODO: use cairo to make button bounce smaller on click, if that's possible
|
|
||||||
const playingState = Box({ // Wrap a box cuz overlay can't have margins itself
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Overlay({
|
|
||||||
child: Box({
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'bar-music-playstate',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Label({
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'bar-music-playstate-txt',
|
|
||||||
justification: 'center',
|
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
|
||||||
}),
|
|
||||||
})],
|
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
if (!mpris) return;
|
|
||||||
label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
|
|
||||||
label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
overlays: [
|
|
||||||
TrackProgress(),
|
|
||||||
]
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
const trackTitle = Label({
|
|
||||||
hexpand: true,
|
|
||||||
className: 'txt-smallie bar-music-txt',
|
|
||||||
truncate: 'end',
|
|
||||||
maxWidthChars: 1, // Doesn't matter, just needs to be non negative
|
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
if (mpris)
|
|
||||||
label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`;
|
|
||||||
else
|
|
||||||
label.label = 'No media';
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
const musicStuff = Box({
|
|
||||||
className: 'spacing-h-10',
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
playingState,
|
|
||||||
trackTitle,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const SystemResourcesOrCustomModule = () => {
|
|
||||||
// Check if $XDG_CACHE_HOME/ags/user/scripts/custom-module-poll.sh exists
|
|
||||||
if (GLib.file_test(CUSTOM_MODULE_CONTENT_SCRIPT, GLib.FileTest.EXISTS)) {
|
|
||||||
const interval = Number(Utils.readFile(CUSTOM_MODULE_CONTENT_INTERVAL_FILE)) || 5000;
|
|
||||||
return BarGroup({
|
|
||||||
child: Button({
|
|
||||||
child: Label({
|
|
||||||
className: 'txt-smallie txt-onSurfaceVariant',
|
|
||||||
useMarkup: true,
|
|
||||||
setup: (self) => Utils.timeout(1, () => {
|
|
||||||
self.label = exec(CUSTOM_MODULE_CONTENT_SCRIPT);
|
|
||||||
self.poll(interval, (self) => {
|
|
||||||
const content = exec(CUSTOM_MODULE_CONTENT_SCRIPT);
|
|
||||||
self.label = content;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
onPrimaryClickRelease: () => execAsync(CUSTOM_MODULE_LEFTCLICK_SCRIPT).catch(print),
|
|
||||||
onSecondaryClickRelease: () => execAsync(CUSTOM_MODULE_RIGHTCLICK_SCRIPT).catch(print),
|
|
||||||
onMiddleClickRelease: () => execAsync(CUSTOM_MODULE_MIDDLECLICK_SCRIPT).catch(print),
|
|
||||||
onScrollUp: () => execAsync(CUSTOM_MODULE_SCROLLUP_SCRIPT).catch(print),
|
|
||||||
onScrollDown: () => execAsync(CUSTOM_MODULE_SCROLLDOWN_SCRIPT).catch(print),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else return BarGroup({
|
|
||||||
child: Box({
|
|
||||||
children: [
|
|
||||||
BarResource('RAM Usage', 'memory', `LANG=C free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`,
|
|
||||||
'bar-ram-circprog', 'bar-ram-txt', 'bar-ram-icon'),
|
|
||||||
Revealer({
|
|
||||||
revealChild: true,
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-10 margin-left-10',
|
|
||||||
children: [
|
|
||||||
BarResource('Swap Usage', 'swap_horiz', `LANG=C free | awk '/^Swap/ {if ($2 > 0) printf("%.2f\\n", ($3/$2) * 100); else print "0";}'`,
|
|
||||||
'bar-swap-circprog', 'bar-swap-txt', 'bar-swap-icon'),
|
|
||||||
BarResource('CPU Usage', 'settings_motion_mode', `LANG=C top -bn1 | grep Cpu | sed 's/\\,/\\./g' | awk '{print $2}'`,
|
|
||||||
'bar-cpu-circprog', 'bar-cpu-txt', 'bar-cpu-icon'),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
self.revealChild = (!mpris);
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
],
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return EventBox({
|
|
||||||
onScrollUp: (self) => switchToRelativeWorkspace(self, -1),
|
|
||||||
onScrollDown: (self) => switchToRelativeWorkspace(self, +1),
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
SystemResourcesOrCustomModule(),
|
|
||||||
EventBox({
|
|
||||||
child: BarGroup({ child: musicStuff }),
|
|
||||||
onPrimaryClick: () => showMusicControls.setValue(!showMusicControls.value),
|
|
||||||
onSecondaryClick: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print),
|
|
||||||
onMiddleClick: () => execAsync('playerctl play-pause').catch(print),
|
|
||||||
setup: (self) => self.on('button-press-event', (self, event) => {
|
|
||||||
if (event.get_button()[1] === 8) // Side button
|
|
||||||
execAsync('playerctl previous').catch(print)
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Brightness from '../../../services/brightness.js';
|
|
||||||
import Indicator from '../../../services/indicator.js';
|
|
||||||
|
|
||||||
const WindowTitle = async () => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
return Widget.Scrollable({
|
|
||||||
hexpand: true, vexpand: true,
|
|
||||||
hscroll: 'automatic', vscroll: 'never',
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
truncate: 'end',
|
|
||||||
maxWidthChars: 1, // Doesn't matter, just needs to be non negative
|
|
||||||
className: 'txt-smaller bar-wintitle-topdesc txt',
|
|
||||||
setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client
|
|
||||||
label.label = Hyprland.active.client.class.length === 0 ? 'Desktop' : Hyprland.active.client.class;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
truncate: 'end',
|
|
||||||
maxWidthChars: 1, // Doesn't matter, just needs to be non negative
|
|
||||||
className: 'txt-smallie bar-wintitle-txt',
|
|
||||||
setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client
|
|
||||||
label.label = Hyprland.active.client.title.length === 0 ? `Workspace ${Hyprland.active.workspace.id}` : Hyprland.active.client.title;
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default async (monitor = 0) => {
|
|
||||||
const optionalWindowTitleInstance = await WindowTitle();
|
|
||||||
return Widget.EventBox({
|
|
||||||
onScrollUp: () => {
|
|
||||||
Indicator.popup(1); // Since the brightness and speaker are both on the same window
|
|
||||||
Brightness[monitor].screen_value += 0.05;
|
|
||||||
},
|
|
||||||
onScrollDown: () => {
|
|
||||||
Indicator.popup(1); // Since the brightness and speaker are both on the same window
|
|
||||||
Brightness[monitor].screen_value -= 0.05;
|
|
||||||
},
|
|
||||||
onPrimaryClick: () => {
|
|
||||||
App.toggleWindow('sideleft');
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
homogeneous: false,
|
|
||||||
children: [
|
|
||||||
Widget.Box({ className: 'bar-corner-spacing' }),
|
|
||||||
Widget.Overlay({
|
|
||||||
overlays: [
|
|
||||||
Widget.Box({ hexpand: true }),
|
|
||||||
Widget.Box({
|
|
||||||
className: 'bar-sidemodule', hexpand: true,
|
|
||||||
children: [Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'bar-space-button',
|
|
||||||
children: [
|
|
||||||
optionalWindowTitleInstance,
|
|
||||||
]
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
|
||||||
import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
|
|
||||||
const { execAsync } = Utils;
|
|
||||||
import Indicator from '../../../services/indicator.js';
|
|
||||||
import { StatusIcons } from '../../.commonwidgets/statusicons.js';
|
|
||||||
import { Tray } from "./tray.js";
|
|
||||||
|
|
||||||
const SeparatorDot = () => Widget.Revealer({
|
|
||||||
transition: 'slide_left',
|
|
||||||
revealChild: false,
|
|
||||||
attribute: {
|
|
||||||
'count': SystemTray.items.length,
|
|
||||||
'update': (self, diff) => {
|
|
||||||
self.attribute.count += diff;
|
|
||||||
self.revealChild = (self.attribute.count > 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'separator-circle',
|
|
||||||
}),
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(SystemTray, (self) => self.attribute.update(self, 1), 'added')
|
|
||||||
.hook(SystemTray, (self) => self.attribute.update(self, -1), 'removed')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default (monitor = 0) => {
|
|
||||||
const barTray = Tray();
|
|
||||||
const barStatusIcons = StatusIcons({
|
|
||||||
className: 'bar-statusicons',
|
|
||||||
setup: (self) => self.hook(App, (self, currentName, visible) => {
|
|
||||||
if (currentName === 'sideright') {
|
|
||||||
self.toggleClassName('bar-statusicons-active', visible);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}, monitor);
|
|
||||||
const SpaceRightDefaultClicks = (child) => Widget.EventBox({
|
|
||||||
onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) },
|
|
||||||
onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) },
|
|
||||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
|
||||||
onSecondaryClick: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print),
|
|
||||||
onMiddleClick: () => execAsync('playerctl play-pause').catch(print),
|
|
||||||
setup: (self) => self.on('button-press-event', (self, event) => {
|
|
||||||
if (event.get_button()[1] === 8)
|
|
||||||
execAsync('playerctl previous').catch(print)
|
|
||||||
}),
|
|
||||||
child: child,
|
|
||||||
});
|
|
||||||
const emptyArea = SpaceRightDefaultClicks(Widget.Box({ hexpand: true, }));
|
|
||||||
const indicatorArea = SpaceRightDefaultClicks(Widget.Box({
|
|
||||||
children: [
|
|
||||||
SeparatorDot(),
|
|
||||||
barStatusIcons
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
const actualContent = Widget.Box({
|
|
||||||
hexpand: true,
|
|
||||||
className: 'spacing-h-5 bar-spaceright',
|
|
||||||
children: [
|
|
||||||
emptyArea,
|
|
||||||
barTray,
|
|
||||||
indicatorArea
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return Widget.EventBox({
|
|
||||||
onScrollUp: () => {
|
|
||||||
if (!Audio.speaker) return;
|
|
||||||
if (Audio.speaker.volume <= 0.09) Audio.speaker.volume += 0.01;
|
|
||||||
else Audio.speaker.volume += 0.03;
|
|
||||||
Indicator.popup(1);
|
|
||||||
},
|
|
||||||
onScrollDown: () => {
|
|
||||||
if (!Audio.speaker) return;
|
|
||||||
if (Audio.speaker.volume <= 0.09) Audio.speaker.volume -= 0.01;
|
|
||||||
else Audio.speaker.volume -= 0.03;
|
|
||||||
Indicator.popup(1);
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
actualContent,
|
|
||||||
SpaceRightDefaultClicks(Widget.Box({ className: 'bar-corner-spacing' })),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,236 +0,0 @@
|
||||||
// This is for the right pills of the bar.
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Label, Button, Overlay, Revealer, Scrollable, Stack, EventBox } = Widget;
|
|
||||||
const { exec, execAsync } = Utils;
|
|
||||||
const { GLib } = imports.gi;
|
|
||||||
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import { AnimatedCircProg } from "../../.commonwidgets/cairo_circularprogress.js";
|
|
||||||
import { WWO_CODE, WEATHER_SYMBOL, NIGHT_WEATHER_SYMBOL } from '../../.commondata/weather.js';
|
|
||||||
|
|
||||||
const WEATHER_CACHE_FOLDER = `${GLib.get_user_cache_dir()}/ags/weather`;
|
|
||||||
Utils.exec(`mkdir -p ${WEATHER_CACHE_FOLDER}`);
|
|
||||||
|
|
||||||
const BarBatteryProgress = () => {
|
|
||||||
const _updateProgress = (circprog) => { // Set circular progress value
|
|
||||||
circprog.css = `font-size: ${Math.abs(Battery.percent)}px;`
|
|
||||||
|
|
||||||
circprog.toggleClassName('bar-batt-circprog-low', Battery.percent <= userOptions.battery.low);
|
|
||||||
circprog.toggleClassName('bar-batt-circprog-full', Battery.charged);
|
|
||||||
}
|
|
||||||
return AnimatedCircProg({
|
|
||||||
className: 'bar-batt-circprog',
|
|
||||||
vpack: 'center', hpack: 'center',
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(Battery, _updateProgress)
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const time = Variable('', {
|
|
||||||
poll: [
|
|
||||||
userOptions.time.interval,
|
|
||||||
() => GLib.DateTime.new_now_local().format(userOptions.time.format),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const date = Variable('', {
|
|
||||||
poll: [
|
|
||||||
userOptions.time.dateInterval,
|
|
||||||
() => GLib.DateTime.new_now_local().format(userOptions.time.dateFormatLong),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarClock = () => Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'spacing-h-4 bar-clock-box',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
className: 'bar-time',
|
|
||||||
label: time.bind(),
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'txt-norm txt-onLayer1',
|
|
||||||
label: '•',
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'txt-smallie bar-date',
|
|
||||||
label: date.bind(),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const UtilButton = ({ name, icon, onClicked }) => Button({
|
|
||||||
vpack: 'center',
|
|
||||||
tooltipText: name,
|
|
||||||
onClicked: onClicked,
|
|
||||||
className: 'bar-util-btn icon-material txt-norm',
|
|
||||||
label: `${icon}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const Utilities = () => Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
UtilButton({
|
|
||||||
name: 'Screen snip', icon: 'screenshot_region', onClicked: () => {
|
|
||||||
Utils.execAsync(`${App.configDir}/scripts/grimblast.sh copy area`)
|
|
||||||
.catch(print)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
UtilButton({
|
|
||||||
name: 'Color picker', icon: 'colorize', onClicked: () => {
|
|
||||||
Utils.execAsync(['hyprpicker', '-a']).catch(print)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
UtilButton({
|
|
||||||
name: 'Toggle on-screen keyboard', icon: 'keyboard', onClicked: () => {
|
|
||||||
toggleWindowOnAllMonitors('osk');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarBattery = () => Box({
|
|
||||||
className: 'spacing-h-4 bar-batt-txt',
|
|
||||||
children: [
|
|
||||||
Revealer({
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_right',
|
|
||||||
child: MaterialIcon('bolt', 'norm', { tooltipText: "Charging" }),
|
|
||||||
setup: (self) => self.hook(Battery, revealer => {
|
|
||||||
self.revealChild = Battery.charging;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
className: 'txt-smallie',
|
|
||||||
setup: (self) => self.hook(Battery, label => {
|
|
||||||
label.label = `${Number.parseFloat(Battery.percent.toFixed(1))}%`;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Overlay({
|
|
||||||
child: Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'bar-batt',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
MaterialIcon('battery_full', 'small'),
|
|
||||||
],
|
|
||||||
setup: (self) => self.hook(Battery, box => {
|
|
||||||
box.toggleClassName('bar-batt-low', Battery.percent <= userOptions.battery.low);
|
|
||||||
box.toggleClassName('bar-batt-full', Battery.charged);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
overlays: [
|
|
||||||
BarBatteryProgress(),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const BarGroup = ({ child }) => Widget.Box({
|
|
||||||
className: 'bar-group-margin bar-sides',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'bar-group bar-group-standalone bar-group-pad-system',
|
|
||||||
children: [child],
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const BatteryModule = () => Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
children: {
|
|
||||||
'laptop': Box({
|
|
||||||
className: 'spacing-h-4', children: [
|
|
||||||
BarGroup({ child: Utilities() }),
|
|
||||||
BarGroup({ child: BarBattery() }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
'desktop': BarGroup({
|
|
||||||
child: Box({
|
|
||||||
hexpand: true,
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-4 txt-onSurfaceVariant',
|
|
||||||
children: [
|
|
||||||
MaterialIcon('device_thermostat', 'small'),
|
|
||||||
Label({
|
|
||||||
label: 'Weather',
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: (self) => self.poll(900000, async (self) => {
|
|
||||||
const WEATHER_CACHE_PATH = WEATHER_CACHE_FOLDER + '/wttr.in.txt';
|
|
||||||
const updateWeatherForCity = (city) => execAsync(`curl https://wttr.in/${city.replace(/ /g, '%20')}?format=j1`)
|
|
||||||
.then(output => {
|
|
||||||
const weather = JSON.parse(output);
|
|
||||||
Utils.writeFile(JSON.stringify(weather), WEATHER_CACHE_PATH)
|
|
||||||
.catch(print);
|
|
||||||
const weatherCode = weather.current_condition[0].weatherCode;
|
|
||||||
const weatherDesc = weather.current_condition[0].weatherDesc[0].value;
|
|
||||||
const temperature = weather.current_condition[0][`temp_${userOptions.weather.preferredUnit}`];
|
|
||||||
const feelsLike = weather.current_condition[0][`FeelsLike${userOptions.weather.preferredUnit}`];
|
|
||||||
const weatherSymbol = WEATHER_SYMBOL[WWO_CODE[weatherCode]];
|
|
||||||
self.children[0].label = weatherSymbol;
|
|
||||||
self.children[1].label = `${temperature}°${userOptions.weather.preferredUnit} • Feels like ${feelsLike}°${userOptions.weather.preferredUnit}`;
|
|
||||||
self.tooltipText = weatherDesc;
|
|
||||||
}).catch((err) => {
|
|
||||||
try { // Read from cache
|
|
||||||
const weather = JSON.parse(
|
|
||||||
Utils.readFile(WEATHER_CACHE_PATH)
|
|
||||||
);
|
|
||||||
const weatherCode = weather.current_condition[0].weatherCode;
|
|
||||||
const weatherDesc = weather.current_condition[0].weatherDesc[0].value;
|
|
||||||
const temperature = weather.current_condition[0][`temp_${userOptions.weather.preferredUnit}`];
|
|
||||||
const feelsLike = weather.current_condition[0][`FeelsLike${userOptions.weather.preferredUnit}`];
|
|
||||||
const weatherSymbol = WEATHER_SYMBOL[WWO_CODE[weatherCode]];
|
|
||||||
self.children[0].label = weatherSymbol;
|
|
||||||
self.children[1].label = `${temperature}°${userOptions.weather.preferredUnit} • Feels like ${feelsLike}°${userOptions.weather.preferredUnit}`;
|
|
||||||
self.tooltipText = weatherDesc;
|
|
||||||
} catch (err) {
|
|
||||||
print(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (userOptions.weather.city != '' && userOptions.weather.city != null) {
|
|
||||||
updateWeatherForCity(userOptions.weather.city.replace(/ /g, '%20'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Utils.execAsync('curl ipinfo.io')
|
|
||||||
.then(output => {
|
|
||||||
return JSON.parse(output)['city'].toLowerCase();
|
|
||||||
})
|
|
||||||
.then(updateWeatherForCity)
|
|
||||||
.catch(print)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
setup: (stack) => Utils.timeout(10, () => {
|
|
||||||
if (!Battery.available) stack.shown = 'desktop';
|
|
||||||
else stack.shown = 'laptop';
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const switchToRelativeWorkspace = async (self, num) => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
Hyprland.messageAsync(`dispatch workspace ${num > 0 ? '+' : ''}${num}`).catch(print);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => Widget.EventBox({
|
|
||||||
onScrollUp: (self) => switchToRelativeWorkspace(self, -1),
|
|
||||||
onScrollDown: (self) => switchToRelativeWorkspace(self, +1),
|
|
||||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
|
||||||
child: Widget.Box({
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
BarGroup({ child: BarClock() }),
|
|
||||||
BatteryModule(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,36 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
|
|
||||||
const { Box, Icon, Button, Revealer } = Widget;
|
|
||||||
const { Gravity } = imports.gi.Gdk;
|
|
||||||
|
|
||||||
const SysTrayItem = (item) => item.id !== null ? Button({
|
|
||||||
className: 'bar-systray-item',
|
|
||||||
child: Icon({ hpack: 'center' }).bind('icon', item, 'icon'),
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(item, (self) => self.tooltipMarkup = item['tooltip-markup'])
|
|
||||||
,
|
|
||||||
onPrimaryClick: (_, event) => item.activate(event),
|
|
||||||
onSecondaryClick: (btn, event) => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null),
|
|
||||||
}) : null;
|
|
||||||
|
|
||||||
export const Tray = (props = {}) => {
|
|
||||||
const trayContent = Box({
|
|
||||||
className: 'margin-right-5 spacing-h-15',
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(SystemTray, (self) => {
|
|
||||||
self.children = SystemTray.items.map(SysTrayItem);
|
|
||||||
self.show_all();
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
const trayRevealer = Widget.Revealer({
|
|
||||||
revealChild: true,
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: trayContent,
|
|
||||||
});
|
|
||||||
return Box({
|
|
||||||
...props,
|
|
||||||
children: [trayRevealer],
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
const { GLib, Gdk, Gtk } = imports.gi;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Cairo = imports.cairo;
|
|
||||||
const Pango = imports.gi.Pango;
|
|
||||||
const PangoCairo = imports.gi.PangoCairo;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box, DrawingArea, EventBox } = Widget;
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
|
|
||||||
const dummyWs = Box({ className: 'bar-ws' }); // Not shown. Only for getting size props
|
|
||||||
const dummyActiveWs = Box({ className: 'bar-ws bar-ws-active' }); // Not shown. Only for getting size props
|
|
||||||
const dummyOccupiedWs = Box({ className: 'bar-ws bar-ws-occupied' }); // Not shown. Only for getting size props
|
|
||||||
|
|
||||||
const mix = (value1, value2, perc) => {
|
|
||||||
return value1 * perc + value2 * (1 - perc);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFontWeightName = (weight) => {
|
|
||||||
switch (weight) {
|
|
||||||
case Pango.Weight.ULTRA_LIGHT:
|
|
||||||
return 'UltraLight';
|
|
||||||
case Pango.Weight.LIGHT:
|
|
||||||
return 'Light';
|
|
||||||
case Pango.Weight.NORMAL:
|
|
||||||
return 'Normal';
|
|
||||||
case Pango.Weight.BOLD:
|
|
||||||
return 'Bold';
|
|
||||||
case Pango.Weight.ULTRA_BOLD:
|
|
||||||
return 'UltraBold';
|
|
||||||
case Pango.Weight.HEAVY:
|
|
||||||
return 'Heavy';
|
|
||||||
default:
|
|
||||||
return 'Normal';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Font size = workspace id
|
|
||||||
const WorkspaceContents = (count = 10) => {
|
|
||||||
return DrawingArea({
|
|
||||||
className: 'bar-ws-container',
|
|
||||||
attribute: {
|
|
||||||
initialized: false,
|
|
||||||
workspaceMask: 0,
|
|
||||||
workspaceGroup: 0,
|
|
||||||
updateMask: (self) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / count) * userOptions.workspaces.shown;
|
|
||||||
// if (self.attribute.initialized) return; // We only need this to run once
|
|
||||||
const workspaces = Hyprland.workspaces;
|
|
||||||
let workspaceMask = 0;
|
|
||||||
for (let i = 0; i < workspaces.length; i++) {
|
|
||||||
const ws = workspaces[i];
|
|
||||||
if (ws.id <= offset || ws.id > offset + count) continue; // Out of range, ignore
|
|
||||||
if (workspaces[i].windows > 0)
|
|
||||||
workspaceMask |= (1 << (ws.id - offset));
|
|
||||||
}
|
|
||||||
// console.log('Mask:', workspaceMask.toString(2));
|
|
||||||
self.attribute.workspaceMask = workspaceMask;
|
|
||||||
// self.attribute.initialized = true;
|
|
||||||
self.queue_draw();
|
|
||||||
},
|
|
||||||
toggleMask: (self, occupied, name) => {
|
|
||||||
if (occupied) self.attribute.workspaceMask |= (1 << parseInt(name));
|
|
||||||
else self.attribute.workspaceMask &= ~(1 << parseInt(name));
|
|
||||||
self.queue_draw();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (area) => area
|
|
||||||
.hook(Hyprland.active.workspace, (self) => {
|
|
||||||
self.setCss(`font-size: ${(Hyprland.active.workspace.id - 1) % count + 1}px;`);
|
|
||||||
const previousGroup = self.attribute.workspaceGroup;
|
|
||||||
const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / count);
|
|
||||||
if (currentGroup !== previousGroup) {
|
|
||||||
self.attribute.updateMask(self);
|
|
||||||
self.attribute.workspaceGroup = currentGroup;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.hook(Hyprland, (self) => self.attribute.updateMask(self), 'notify::workspaces')
|
|
||||||
.on('draw', Lang.bind(area, (area, cr) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / count) * userOptions.workspaces.shown;
|
|
||||||
|
|
||||||
const allocation = area.get_allocation();
|
|
||||||
const { width, height } = allocation;
|
|
||||||
|
|
||||||
const workspaceStyleContext = dummyWs.get_style_context();
|
|
||||||
const workspaceDiameter = workspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
const workspaceRadius = workspaceDiameter / 2;
|
|
||||||
const workspaceFontSize = workspaceStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 4 * 3;
|
|
||||||
const workspaceFontFamily = workspaceStyleContext.get_property('font-family', Gtk.StateFlags.NORMAL);
|
|
||||||
const workspaceFontWeight = workspaceStyleContext.get_property('font-weight', Gtk.StateFlags.NORMAL);
|
|
||||||
const wsbg = workspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const wsfg = workspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const occupiedWorkspaceStyleContext = dummyOccupiedWs.get_style_context();
|
|
||||||
const occupiedbg = occupiedWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const occupiedfg = occupiedWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const activeWorkspaceStyleContext = dummyActiveWs.get_style_context();
|
|
||||||
const activebg = activeWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
|
||||||
const activefg = activeWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
|
||||||
area.set_size_request(workspaceDiameter * count, -1);
|
|
||||||
const widgetStyleContext = area.get_style_context();
|
|
||||||
const activeWs = widgetStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const activeWsCenterX = -(workspaceDiameter / 2) + (workspaceDiameter * activeWs);
|
|
||||||
const activeWsCenterY = height / 2;
|
|
||||||
|
|
||||||
// Font
|
|
||||||
const layout = PangoCairo.create_layout(cr);
|
|
||||||
const fontDesc = Pango.font_description_from_string(`${workspaceFontFamily[0]} ${getFontWeightName(workspaceFontWeight)} ${workspaceFontSize}`);
|
|
||||||
layout.set_font_description(fontDesc);
|
|
||||||
cr.setAntialias(Cairo.Antialias.BEST);
|
|
||||||
// Get kinda min radius for number indicators
|
|
||||||
layout.set_text("0".repeat(count.toString().length), -1);
|
|
||||||
const [layoutWidth, layoutHeight] = layout.get_pixel_size();
|
|
||||||
const indicatorRadius = Math.max(layoutWidth, layoutHeight) / 2 * 1.15; // smaller than sqrt(2)*radius
|
|
||||||
const indicatorGap = workspaceRadius - indicatorRadius;
|
|
||||||
|
|
||||||
for (let i = 1; i <= count; i++) {
|
|
||||||
if (area.attribute.workspaceMask & (1 << i)) {
|
|
||||||
// Draw bg highlight
|
|
||||||
cr.setSourceRGBA(occupiedbg.red, occupiedbg.green, occupiedbg.blue, occupiedbg.alpha);
|
|
||||||
const wsCenterX = -(workspaceRadius) + (workspaceDiameter * i);
|
|
||||||
const wsCenterY = height / 2;
|
|
||||||
if (!(area.attribute.workspaceMask & (1 << (i - 1)))) { // Left
|
|
||||||
cr.arc(wsCenterX, wsCenterY, workspaceRadius, 0.5 * Math.PI, 1.5 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cr.rectangle(wsCenterX - workspaceRadius, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2)
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
if (!(area.attribute.workspaceMask & (1 << (i + 1)))) { // Right
|
|
||||||
cr.arc(wsCenterX, wsCenterY, workspaceRadius, -0.5 * Math.PI, 0.5 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cr.rectangle(wsCenterX, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2)
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw active ws
|
|
||||||
cr.setSourceRGBA(activebg.red, activebg.green, activebg.blue, activebg.alpha);
|
|
||||||
cr.arc(activeWsCenterX, activeWsCenterY, indicatorRadius, 0, 2 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
|
|
||||||
// Draw workspace numbers
|
|
||||||
for (let i = 1; i <= count; i++) {
|
|
||||||
const inactivecolors = area.attribute.workspaceMask & (1 << i) ? occupiedfg : wsfg;
|
|
||||||
if (i == activeWs) {
|
|
||||||
cr.setSourceRGBA(activefg.red, activefg.green, activefg.blue, activefg.alpha);
|
|
||||||
}
|
|
||||||
// Moving to
|
|
||||||
else if ((i == Math.floor(activeWs) && Hyprland.active.workspace.id < activeWs) || (i == Math.ceil(activeWs) && Hyprland.active.workspace.id > activeWs)) {
|
|
||||||
cr.setSourceRGBA(mix(activefg.red, inactivecolors.red, 1 - Math.abs(activeWs - i)), mix(activefg.green, inactivecolors.green, 1 - Math.abs(activeWs - i)), mix(activefg.blue, inactivecolors.blue, 1 - Math.abs(activeWs - i)), activefg.alpha);
|
|
||||||
}
|
|
||||||
// Moving from
|
|
||||||
else if ((i == Math.floor(activeWs) && Hyprland.active.workspace.id > activeWs) || (i == Math.ceil(activeWs) && Hyprland.active.workspace.id < activeWs)) {
|
|
||||||
cr.setSourceRGBA(mix(activefg.red, inactivecolors.red, 1 - Math.abs(activeWs - i)), mix(activefg.green, inactivecolors.green, 1 - Math.abs(activeWs - i)), mix(activefg.blue, inactivecolors.blue, 1 - Math.abs(activeWs - i)), activefg.alpha);
|
|
||||||
}
|
|
||||||
// Inactive
|
|
||||||
else
|
|
||||||
cr.setSourceRGBA(inactivecolors.red, inactivecolors.green, inactivecolors.blue, inactivecolors.alpha);
|
|
||||||
|
|
||||||
layout.set_text(`${i + offset}`, -1);
|
|
||||||
const [layoutWidth, layoutHeight] = layout.get_pixel_size();
|
|
||||||
const x = -workspaceRadius + (workspaceDiameter * i) - (layoutWidth / 2);
|
|
||||||
const y = (height - layoutHeight) / 2;
|
|
||||||
cr.moveTo(x, y);
|
|
||||||
PangoCairo.show_layout(cr, layout);
|
|
||||||
cr.stroke();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => EventBox({
|
|
||||||
onScrollUp: () => Hyprland.messageAsync(`dispatch workspace -1`).catch(print),
|
|
||||||
onScrollDown: () => Hyprland.messageAsync(`dispatch workspace +1`).catch(print),
|
|
||||||
onMiddleClick: () => toggleWindowOnAllMonitors('osk'),
|
|
||||||
onSecondaryClick: () => App.toggleWindow('overview'),
|
|
||||||
attribute: {
|
|
||||||
clicked: false,
|
|
||||||
ws_group: 0,
|
|
||||||
},
|
|
||||||
child: Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'bar-group-margin',
|
|
||||||
children: [Box({
|
|
||||||
className: 'bar-group bar-group-standalone bar-group-pad',
|
|
||||||
css: 'min-width: 2px;',
|
|
||||||
children: [WorkspaceContents(userOptions.workspaces.shown)],
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
setup: (self) => {
|
|
||||||
self.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
|
|
||||||
self.on('motion-notify-event', (self, event) => {
|
|
||||||
if (!self.attribute.clicked) return;
|
|
||||||
const [_, cursorX, cursorY] = event.get_coords();
|
|
||||||
const widgetWidth = self.get_allocation().width;
|
|
||||||
const wsId = Math.ceil(cursorX * userOptions.workspaces.shown / widgetWidth);
|
|
||||||
Utils.execAsync([`${App.configDir}/scripts/hyprland/workspace_action.sh`, 'workspace', `${wsId}`])
|
|
||||||
.catch(print);
|
|
||||||
})
|
|
||||||
self.on('button-press-event', (self, event) => {
|
|
||||||
if (event.get_button()[1] === 1) {
|
|
||||||
self.attribute.clicked = true;
|
|
||||||
const [_, cursorX, cursorY] = event.get_coords();
|
|
||||||
const widgetWidth = self.get_allocation().width;
|
|
||||||
const wsId = Math.ceil(cursorX * userOptions.workspaces.shown / widgetWidth);
|
|
||||||
Utils.execAsync([`${App.configDir}/scripts/hyprland/workspace_action.sh`, 'workspace', `${wsId}`])
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (event.get_button()[1] === 8) {
|
|
||||||
Hyprland.messageAsync(`dispatch togglespecialworkspace`).catch(print);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.on('button-release-event', (self) => self.attribute.clicked = false);
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,122 +0,0 @@
|
||||||
export const keybindList = [[
|
|
||||||
{
|
|
||||||
"icon": "pin_drop",
|
|
||||||
"name": "Workspaces: navigation",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "+", "#"], "action": "Go to workspace #" },
|
|
||||||
{ "keys": ["", "+", "S"], "action": "Toggle special workspace" },
|
|
||||||
{ "keys": ["", "+", "(Scroll ↑↓)"], "action": "Go to workspace -1/+1" },
|
|
||||||
{ "keys": ["Ctrl", "", "+", "←"], "action": "Go to workspace on the left" },
|
|
||||||
{ "keys": ["Ctrl", "", "+", "→"], "action": "Go to workspace on the right" },
|
|
||||||
{ "keys": ["", "+", "PageUp"], "action": "Go to workspace on the left" },
|
|
||||||
{ "keys": ["", "+", "PageDown"], "action": "Go to workspace on the right" }
|
|
||||||
],
|
|
||||||
"id": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon": "overview_key",
|
|
||||||
"name": "Workspaces: management",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "Alt", "+", "#"], "action": "Move window to workspace #" },
|
|
||||||
{ "keys": ["", "Alt", "+", "S"], "action": "Move window to special workspace" },
|
|
||||||
{ "keys": ["", "Alt", "+", "PageUp"], "action": "Move window to workspace on the left" },
|
|
||||||
{ "keys": ["", "Alt", "+", "PageDown"], "action": "Move window to workspace on the right" }
|
|
||||||
],
|
|
||||||
"id": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon": "move_group",
|
|
||||||
"name": "Windows",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "+", "←↑→↓"], "action": "Focus window in direction" },
|
|
||||||
{ "keys": ["", "Shift", "+", "←↑→↓"], "action": "Swap window in direction" },
|
|
||||||
{ "keys": ["", "+", ";"], "action": "Split ratio -" },
|
|
||||||
{ "keys": ["", "+", "'"], "action": "Split ratio +" },
|
|
||||||
{ "keys": ["", "+", "Lmb"], "action": "Move window" },
|
|
||||||
{ "keys": ["", "+", "Rmb"], "action": "Resize window" },
|
|
||||||
{ "keys": ["", "Alt", "+", "Space"], "action": "Float window" },
|
|
||||||
{ "keys": ["", "+", "F"], "action": "Fullscreen" },
|
|
||||||
{ "keys": ["", "Alt", "+", "F"], "action": "Fake fullscreen" }
|
|
||||||
],
|
|
||||||
"id": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"icon": "widgets",
|
|
||||||
"name": "Widgets (AGS)",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "OR", "", "+", "Tab"], "action": "Toggle overview/launcher" },
|
|
||||||
{ "keys": ["Ctrl", "", "+", "R"], "action": "Restart AGS" },
|
|
||||||
{ "keys": ["", "+", "/"], "action": "Toggle this cheatsheet" },
|
|
||||||
{ "keys": ["", "+", "N"], "action": "Toggle system sidebar" },
|
|
||||||
{ "keys": ["", "+", "B", "OR", "", "+", "O"], "action": "Toggle utilities sidebar" },
|
|
||||||
{ "keys": ["", "+", "K"], "action": "Toggle virtual keyboard" },
|
|
||||||
{ "keys": ["Ctrl", "Alt", "+", "Del"], "action": "Power/Session menu" },
|
|
||||||
|
|
||||||
{ "keys": ["Esc"], "action": "Exit a window" },
|
|
||||||
{ "keys": ["rightCtrl"], "action": "Dismiss/close sidebar" },
|
|
||||||
|
|
||||||
{ "keys": ["Ctrl", "", "+", "T"], "action": "Change wallpaper+colorscheme" },
|
|
||||||
|
|
||||||
// { "keys": ["", "+", "B"], "action": "Toggle left sidebar" },
|
|
||||||
// { "keys": ["", "+", "N"], "action": "Toggle right sidebar" },
|
|
||||||
// { "keys": ["", "+", "G"], "action": "Toggle volume mixer" },
|
|
||||||
// { "keys": ["", "+", "M"], "action": "Toggle useless audio visualizer" },
|
|
||||||
// { "keys": ["(right)Ctrl"], "action": "Dismiss notification & close menus" }
|
|
||||||
],
|
|
||||||
"id": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon": "construction",
|
|
||||||
"name": "Utilities",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["PrtSc"], "action": "Screenshot >> clipboard" },
|
|
||||||
{ "keys": ["Ctrl", "PrtSc"], "action": "Screenshot >> file + clipboard" },
|
|
||||||
{ "keys": ["", "Shift", "+", "S"], "action": "Screen snip >> clipboard" },
|
|
||||||
{ "keys": ["", "Shift", "+", "T"], "action": "Image to text >> clipboard" },
|
|
||||||
{ "keys": ["", "Shift", "+", "C"], "action": "Color picker" },
|
|
||||||
{ "keys": ["", "Alt", "+", "R"], "action": "Record region" },
|
|
||||||
{ "keys": ["Ctrl", "Alt", "+", "R"], "action": "Record region with sound" },
|
|
||||||
{ "keys": ["", "Shift", "Alt", "+", "R"], "action": "Record screen with sound" }
|
|
||||||
],
|
|
||||||
"id": 5
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"icon": "apps",
|
|
||||||
"name": "Apps",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "+", "T"], "action": "Launch terminal: foot" },
|
|
||||||
{ "keys": ["", "+", "W"], "action": "Launch browser: Firefox" },
|
|
||||||
{ "keys": ["", "+", "C"], "action": "Launch editor: vscode" },
|
|
||||||
{ "keys": ["", "+", "X"], "action": "Launch editor: GNOME Text Editor" },
|
|
||||||
{ "keys": ["", "+", "I"], "action": "Launch settings: GNOME Control center" }
|
|
||||||
],
|
|
||||||
"id": 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon": "keyboard",
|
|
||||||
"name": "Typing",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": ["", "+", "V"], "action": "Clipboard history >> clipboard" },
|
|
||||||
{ "keys": ["", "+", "."], "action": "Emoji picker >> clipboard" },
|
|
||||||
],
|
|
||||||
"id": 7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon": "terminal",
|
|
||||||
"name": "Launcher actions",
|
|
||||||
"binds": [
|
|
||||||
{ "keys": [">raw"], "action": "Toggle mouse acceleration" },
|
|
||||||
{ "keys": [">img"], "action": "Select wallpaper and generate colorscheme" },
|
|
||||||
{ "keys": [">light"], "action": "Switch to light theme" },
|
|
||||||
{ "keys": [">dark"], "action": "Switch to dark theme" },
|
|
||||||
{ "keys": [">badapple"], "action": "Apply black n' white colorscheme" },
|
|
||||||
{ "keys": [">color"], "action": "Pick acccent color" },
|
|
||||||
{ "keys": [">todo"], "action": "Type something after that to add a To-do item" },
|
|
||||||
],
|
|
||||||
"id": 8
|
|
||||||
}
|
|
||||||
]];
|
|
|
@ -1,195 +0,0 @@
|
||||||
export const periodicTable = [
|
|
||||||
[
|
|
||||||
{ name: 'Hydrogen', symbol: 'H', number: 1, weight: 1.01, type: 'nonmetal' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: 'Helium', symbol: 'He', number: 2, weight: 4.00, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Lithium', symbol: 'Li', number: 3, weight: 6.94, type: 'metal' },
|
|
||||||
{ name: 'Beryllium', symbol: 'Be', number: 4, weight: 9.01, type: 'metal' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: 'Boron', symbol: 'B', number: 5, weight: 10.81, type: 'nonmetal' },
|
|
||||||
{ name: 'Carbon', symbol: 'C', number: 6, weight: 12.01, type: 'nonmetal' },
|
|
||||||
{ name: 'Nitrogen', symbol: 'N', number: 7, weight: 14.01, type: 'nonmetal' },
|
|
||||||
{ name: 'Oxygen', symbol: 'O', number: 8, weight: 16, type: 'nonmetal' },
|
|
||||||
{ name: 'Fluorine', symbol: 'F', number: 9, weight: 19, type: 'nonmetal' },
|
|
||||||
{ name: 'Neon', symbol: 'Ne', number: 10, weight: 20.18, type: 'noblegas' },
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Sodium', symbol: 'Na', number: 11, weight: 22.99, type: 'metal' },
|
|
||||||
{ name: 'Magnesium', symbol: 'Mg', number: 12, weight: 24.31, type: 'metal' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: 'Aluminum', symbol: 'Al', number: 13, weight: 26.98, type: 'metal' },
|
|
||||||
{ name: 'Silicon', symbol: 'Si', number: 14, weight: 28.09, type: 'nonmetal' },
|
|
||||||
{ name: 'Phosphorus', symbol: 'P', number: 15, weight: 30.97, type: 'nonmetal' },
|
|
||||||
{ name: 'Sulfur', symbol: 'S', number: 16, weight: 32.07, type: 'nonmetal' },
|
|
||||||
{ name: 'Chlorine', symbol: 'Cl', number: 17, weight: 35.45, type: 'nonmetal' },
|
|
||||||
{ name: 'Argon', symbol: 'Ar', number: 18, weight: 39.95, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Kalium', symbol: 'K', number: 19, weight: 39.098, type: 'metal' },
|
|
||||||
{ name: 'Calcium', symbol: 'Ca', number: 20, weight: 40.078, type: 'metal' },
|
|
||||||
{ name: 'Scandium', symbol: 'Sc', number: 21, weight: 44.956, type: 'metal' },
|
|
||||||
{ name: 'Titanium', symbol: 'Ti', number: 22, weight: 47.87, type: 'metal' },
|
|
||||||
{ name: 'Vanadium', symbol: 'V', number: 23, weight: 50.94, type: 'metal' },
|
|
||||||
{ name: 'Chromium', symbol: 'Cr', number: 24, weight: 52, type: 'metal', icon: 'chromium-browser' },
|
|
||||||
{ name: 'Manganese', symbol: 'Mn', number: 25, weight: 54.94, type: 'metal' },
|
|
||||||
{ name: 'Iron', symbol: 'Fe', number: 26, weight: 55.85, type: 'metal' },
|
|
||||||
{ name: 'Cobalt', symbol: 'Co', number: 27, weight: 58.93, type: 'metal' },
|
|
||||||
{ name: 'Nickel', symbol: 'Ni', number: 28, weight: 58.69, type: 'metal' },
|
|
||||||
{ name: 'Copper', symbol: 'Cu', number: 29, weight: 63.55, type: 'metal' },
|
|
||||||
{ name: 'Zinc', symbol: 'Zn', number: 30, weight: 65.38, type: 'metal' },
|
|
||||||
{ name: 'Gallium', symbol: 'Ga', number: 31, weight: 69.72, type: 'metal' },
|
|
||||||
{ name: 'Germanium', symbol: 'Ge', number: 32, weight: 72.63, type: 'metal' },
|
|
||||||
{ name: 'Arsenic', symbol: 'As', number: 33, weight: 74.92, type: 'nonmetal' },
|
|
||||||
{ name: 'Selenium', symbol: 'Se', number: 34, weight: 78.96, type: 'nonmetal' },
|
|
||||||
{ name: 'Bromine', symbol: 'Br', number: 35, weight: 79.904, type: 'nonmetal' },
|
|
||||||
{ name: 'Krypton', symbol: 'Kr', number: 36, weight: 83.8, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Rubidium', symbol: 'Rb', number: 37, weight: 85.47, type: 'metal' },
|
|
||||||
{ name: 'Strontium', symbol: 'Sr', number: 38, weight: 87.62, type: 'metal' },
|
|
||||||
{ name: 'Yttrium', symbol: 'Y', number: 39, weight: 88.91, type: 'metal' },
|
|
||||||
{ name: 'Zirconium', symbol: 'Zr', number: 40, weight: 91.22, type: 'metal' },
|
|
||||||
{ name: 'Niobium', symbol: 'Nb', number: 41, weight: 92.91, type: 'metal' },
|
|
||||||
{ name: 'Molybdenum', symbol: 'Mo', number: 42, weight: 95.94, type: 'metal' },
|
|
||||||
{ name: 'Technetium', symbol: 'Tc', number: 43, weight: 98, type: 'metal' },
|
|
||||||
{ name: 'Ruthenium', symbol: 'Ru', number: 44, weight: 101.07, type: 'metal' },
|
|
||||||
{ name: 'Rhodium', symbol: 'Rh', number: 45, weight: 102.91, type: 'metal' },
|
|
||||||
{ name: 'Palladium', symbol: 'Pd', number: 46, weight: 106.42, type: 'metal' },
|
|
||||||
{ name: 'Silver', symbol: 'Ag', number: 47, weight: 107.87, type: 'metal' },
|
|
||||||
{ name: 'Cadmium', symbol: 'Cd', number: 48, weight: 112.41, type: 'metal' },
|
|
||||||
{ name: 'Indium', symbol: 'In', number: 49, weight: 114.82, type: 'metal' },
|
|
||||||
{ name: 'Tin', symbol: 'Sn', number: 50, weight: 118.71, type: 'metal' },
|
|
||||||
{ name: 'Antimony', symbol: 'Sb', number: 51, weight: 121.76, type: 'metal' },
|
|
||||||
{ name: 'Tellurium', symbol: 'Te', number: 52, weight: 127.6, type: 'nonmetal' },
|
|
||||||
{ name: 'Iodine', symbol: 'I', number: 53, weight: 126.9, type: 'nonmetal' },
|
|
||||||
{ name: 'Xenon', symbol: 'Xe', number: 54, weight: 131.29, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Cesium', symbol: 'Cs', number: 55, weight: 132.91, type: 'metal' },
|
|
||||||
{ name: 'Barium', symbol: 'Ba', number: 56, weight: 137.33, type: 'metal' },
|
|
||||||
{ name: 'Lanthanum', symbol: 'La', number: 57, weight: 138.91, type: 'lanthanum' },
|
|
||||||
{ name: 'Hafnium', symbol: 'Hf', number: 72, weight: 178.49, type: 'metal' },
|
|
||||||
{ name: 'Tantalum', symbol: 'Ta', number: 73, weight: 180.95, type: 'metal' },
|
|
||||||
{ name: 'Tungsten', symbol: 'W', number: 74, weight: 183.84, type: 'metal' },
|
|
||||||
{ name: 'Rhenium', symbol: 'Re', number: 75, weight: 186.21, type: 'metal' },
|
|
||||||
{ name: 'Osmium', symbol: 'Os', number: 76, weight: 190.23, type: 'metal' },
|
|
||||||
{ name: 'Iridium', symbol: 'Ir', number: 77, weight: 192.22, type: 'metal' },
|
|
||||||
{ name: 'Platinum', symbol: 'Pt', number: 78, weight: 195.09, type: 'metal' },
|
|
||||||
{ name: 'Gold', symbol: 'Au', number: 79, weight: 196.97, type: 'metal' },
|
|
||||||
{ name: 'Mercury', symbol: 'Hg', number: 80, weight: 200.59, type: 'metal' },
|
|
||||||
{ name: 'Thallium', symbol: 'Tl', number: 81, weight: 204.38, type: 'metal' },
|
|
||||||
{ name: 'Lead', symbol: 'Pb', number: 82, weight: 207.2, type: 'metal' },
|
|
||||||
{ name: 'Bismuth', symbol: 'Bi', number: 83, weight: 208.98, type: 'metal' },
|
|
||||||
{ name: 'Polonium', symbol: 'Po', number: 84, weight: 209, type: 'metal' },
|
|
||||||
{ name: 'Astatine', symbol: 'At', number: 85, weight: 210, type: 'nonmetal' },
|
|
||||||
{ name: 'Radon', symbol: 'Rn', number: 86, weight: 222, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Francium', symbol: 'Fr', number: 87, weight: 223, type: 'metal' },
|
|
||||||
{ name: 'Radium', symbol: 'Ra', number: 88, weight: 226, type: 'metal' },
|
|
||||||
{ name: 'Actinium', symbol: 'Ac', number: 89, weight: 227, type: 'actinium' },
|
|
||||||
{ name: 'Rutherfordium', symbol: 'Rf', number: 104, weight: 267, type: 'metal' },
|
|
||||||
{ name: 'Dubnium', symbol: 'Db', number: 105, weight: 268, type: 'metal' },
|
|
||||||
{ name: 'Seaborgium', symbol: 'Sg', number: 106, weight: 271, type: 'metal' },
|
|
||||||
{ name: 'Bohrium', symbol: 'Bh', number: 107, weight: 272, type: 'metal' },
|
|
||||||
{ name: 'Hassium', symbol: 'Hs', number: 108, weight: 277, type: 'metal' },
|
|
||||||
{ name: 'Meitnerium', symbol: 'Mt', number: 109, weight: 278, type: 'metal' },
|
|
||||||
{ name: 'Darmstadtium', symbol: 'Ds', number: 110, weight: 281, type: 'metal' },
|
|
||||||
{ name: 'Roentgenium', symbol: 'Rg', number: 111, weight: 280, type: 'metal' },
|
|
||||||
{ name: 'Copernicium', symbol: 'Cn', number: 112, weight: 285, type: 'metal' },
|
|
||||||
{ name: 'Nihonium', symbol: 'Nh', number: 113, weight: 286, type: 'metal' },
|
|
||||||
{ name: 'Flerovium', symbol: 'Fl', number: 114, weight: 289, type: 'metal' },
|
|
||||||
{ name: 'Moscovium', symbol: 'Mc', number: 115, weight: 290, type: 'metal' },
|
|
||||||
{ name: 'Livermorium', symbol: 'Lv', number: 116, weight: 293, type: 'metal' },
|
|
||||||
{ name: 'Tennessine', symbol: 'Ts', number: 117, weight: 294, type: 'metal' },
|
|
||||||
{ name: 'Oganesson', symbol: 'Og', number: 118, weight: 294, type: 'noblegas' },
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
export const series = [
|
|
||||||
[
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: 'Cerium', symbol: 'Ce', number: 58, weight: 140.12, type: 'lanthanum' },
|
|
||||||
{ name: 'Praseodymium', symbol: 'Pr', number: 59, weight: 140.91, type: 'lanthanum' },
|
|
||||||
{ name: 'Neodymium', symbol: 'Nd', number: 60, weight: 144.24, type: 'lanthanum' },
|
|
||||||
{ name: 'Promethium', symbol: 'Pm', number: 61, weight: 145, type: 'lanthanum' },
|
|
||||||
{ name: 'Samarium', symbol: 'Sm', number: 62, weight: 150.36, type: 'lanthanum' },
|
|
||||||
{ name: 'Europium', symbol: 'Eu', number: 63, weight: 151.96, type: 'lanthanum' },
|
|
||||||
{ name: 'Gadolinium', symbol: 'Gd', number: 64, weight: 157.25, type: 'lanthanum' },
|
|
||||||
{ name: 'Terbium', symbol: 'Tb', number: 65, weight: 158.93, type: 'lanthanum' },
|
|
||||||
{ name: 'Dysprosium', symbol: 'Dy', number: 66, weight: 162.5, type: 'lanthanum' },
|
|
||||||
{ name: 'Holmium', symbol: 'Ho', number: 67, weight: 164.93, type: 'lanthanum' },
|
|
||||||
{ name: 'Erbium', symbol: 'Er', number: 68, weight: 167.26, type: 'lanthanum' },
|
|
||||||
{ name: 'Thulium', symbol: 'Tm', number: 69, weight: 168.93, type: 'lanthanum' },
|
|
||||||
{ name: 'Ytterbium', symbol: 'Yb', number: 70, weight: 173.04, type: 'lanthanum' },
|
|
||||||
{ name: 'Lutetium', symbol: 'Lu', number: 71, weight: 174.97, type: 'lanthanum' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
{ name: 'Thorium', symbol: 'Th', number: 90, weight: 232.04, type: 'actinium' },
|
|
||||||
{ name: 'Protactinium', symbol: 'Pa', number: 91, weight: 231.04, type: 'actinium' },
|
|
||||||
{ name: 'Uranium', symbol: 'U', number: 92, weight: 238.03, type: 'actinium' },
|
|
||||||
{ name: 'Neptunium', symbol: 'Np', number: 93, weight: 237, type: 'actinium' },
|
|
||||||
{ name: 'Plutonium', symbol: 'Pu', number: 94, weight: 244, type: 'actinium' },
|
|
||||||
{ name: 'Americium', symbol: 'Am', number: 95, weight: 243, type: 'actinium' },
|
|
||||||
{ name: 'Curium', symbol: 'Cm', number: 96, weight: 247, type: 'actinium' },
|
|
||||||
{ name: 'Berkelium', symbol: 'Bk', number: 97, weight: 247, type: 'actinium' },
|
|
||||||
{ name: 'Californium', symbol: 'Cf', number: 98, weight: 251, type: 'actinium' },
|
|
||||||
{ name: 'Einsteinium', symbol: 'Es', number: 99, weight: 252, type: 'actinium' },
|
|
||||||
{ name: 'Fermium', symbol: 'Fm', number: 100, weight: 257, type: 'actinium' },
|
|
||||||
{ name: 'Mendelevium', symbol: 'Md', number: 101, weight: 258, type: 'actinium' },
|
|
||||||
{ name: 'Nobelium', symbol: 'No', number: 102, weight: 259, type: 'actinium' },
|
|
||||||
{ name: 'Lawrencium', symbol: 'Lr', number: 103, weight: 262, type: 'actinium' },
|
|
||||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
export const niceTypes = {
|
|
||||||
'metal': "Metal",
|
|
||||||
'nonmetal': "Nonmetal",
|
|
||||||
'noblegas': "Noble gas",
|
|
||||||
'lanthanum': "Lanthanum",
|
|
||||||
'actinium': "Actinium"
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
const { GLib, Gtk } = imports.gi;
|
|
||||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
|
||||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
|
||||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
|
||||||
import { IconTabContainer } from "../.commonwidgets/tabcontainer.js";
|
|
||||||
const { Box, Label, Scrollable } = Widget;
|
|
||||||
|
|
||||||
const HYPRLAND_KEYBIND_CONFIG_FILE = userOptions.cheatsheet.keybinds.configPath ?
|
|
||||||
userOptions.cheatsheet.keybinds.configPath : `${GLib.get_user_config_dir()}/hypr/hyprland/keybinds.conf`;
|
|
||||||
const KEYBIND_SECTIONS_PER_PAGE = 3;
|
|
||||||
const getKeybindList = () => {
|
|
||||||
let data = Utils.exec(`${App.configDir}/scripts/hyprland/get_keybinds.py --path ${HYPRLAND_KEYBIND_CONFIG_FILE}`);
|
|
||||||
if (data == "\"error\"") {
|
|
||||||
Utils.timeout(2000, () => Utils.execAsync(['notify-send',
|
|
||||||
'Update path to keybinds',
|
|
||||||
'Keybinds hyprland config file not found. Check your user options.',
|
|
||||||
'-a', 'ags',
|
|
||||||
]).catch(print))
|
|
||||||
return { children: [] };
|
|
||||||
}
|
|
||||||
return JSON.parse(data);
|
|
||||||
};
|
|
||||||
const keybindList = getKeybindList();
|
|
||||||
|
|
||||||
const keySubstitutions = {
|
|
||||||
"Super": "",
|
|
||||||
"mouse_up": "Scroll ↓", // ikr, weird
|
|
||||||
"mouse_down": "Scroll ↑", // trust me bro
|
|
||||||
"mouse:272": "LMB",
|
|
||||||
"mouse:273": "RMB",
|
|
||||||
"mouse:275": "MouseBack",
|
|
||||||
"Slash": "/",
|
|
||||||
"Hash": "#"
|
|
||||||
}
|
|
||||||
|
|
||||||
const substituteKey = (key) => {
|
|
||||||
return keySubstitutions[key] || key;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Keybind = (keybindData, type) => { // type: either "keys" or "actions"
|
|
||||||
const Key = (key) => Label({ // Specific keys
|
|
||||||
vpack: 'center',
|
|
||||||
className: `${['OR', '+'].includes(key) ? 'cheatsheet-key-notkey' : 'cheatsheet-key'} txt-small`,
|
|
||||||
label: substituteKey(key),
|
|
||||||
});
|
|
||||||
const Action = (text) => Label({ // Binds
|
|
||||||
xalign: 0,
|
|
||||||
label: text,
|
|
||||||
className: "txt txt-small cheatsheet-action",
|
|
||||||
})
|
|
||||||
return Widget.Box({
|
|
||||||
className: "spacing-h-10 cheatsheet-bind-lineheight",
|
|
||||||
children: type == "keys" ? [
|
|
||||||
...(keybindData.mods.length > 0 ? [
|
|
||||||
...keybindData.mods.map(Key),
|
|
||||||
Key("+"),
|
|
||||||
] : []),
|
|
||||||
Key(keybindData.key),
|
|
||||||
] : [Action(keybindData.comment)],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const Section = (sectionData, scope) => {
|
|
||||||
const keys = Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: sectionData.keybinds.map((data) => Keybind(data, "keys"))
|
|
||||||
})
|
|
||||||
const actions = Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: sectionData.keybinds.map((data) => Keybind(data, "actions"))
|
|
||||||
})
|
|
||||||
const name = Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: "cheatsheet-category-title txt margin-bottom-10",
|
|
||||||
label: sectionData.name,
|
|
||||||
})
|
|
||||||
const binds = Box({
|
|
||||||
className: 'spacing-h-10',
|
|
||||||
children: [
|
|
||||||
keys,
|
|
||||||
actions,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const childrenSections = Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: sectionData.children.map((data) => Section(data, scope + 1))
|
|
||||||
})
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
...((sectionData.name && sectionData.name.length > 0) ? [name] : []),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
children: [
|
|
||||||
binds,
|
|
||||||
childrenSections,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const numOfTabs = Math.ceil(keybindList.children.length / KEYBIND_SECTIONS_PER_PAGE);
|
|
||||||
const keybindPages = Array.from({ length: numOfTabs }, (_, i) => ({
|
|
||||||
iconWidget: Label({
|
|
||||||
className: "txt txt-small",
|
|
||||||
label: `${i + 1}`,
|
|
||||||
}),
|
|
||||||
name: `${i + 1}`,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-30',
|
|
||||||
children: keybindList.children.slice(
|
|
||||||
KEYBIND_SECTIONS_PER_PAGE * i, 0 + KEYBIND_SECTIONS_PER_PAGE * (i + 1),
|
|
||||||
).map(data => Section(data, 1)),
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
return IconTabContainer({
|
|
||||||
iconWidgets: keybindPages.map((kbp) => kbp.iconWidget),
|
|
||||||
names: keybindPages.map((kbp) => kbp.name),
|
|
||||||
children: keybindPages.map((kbp) => kbp.child),
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,146 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { setupCursorHover } from "../.widgetutils/cursorhover.js";
|
|
||||||
import PopupWindow from '../.widgethacks/popupwindow.js';
|
|
||||||
import Keybinds from "./keybinds.js";
|
|
||||||
import PeriodicTable from "./periodictable.js";
|
|
||||||
import { ExpandingIconTabContainer } from '../.commonwidgets/tabcontainer.js';
|
|
||||||
import { checkKeybind } from '../.widgetutils/keybind.js';
|
|
||||||
import clickCloseRegion from '../.commonwidgets/clickcloseregion.js';
|
|
||||||
|
|
||||||
const cheatsheets = [
|
|
||||||
{
|
|
||||||
name: 'Keybinds',
|
|
||||||
materialIcon: 'keyboard',
|
|
||||||
contentWidget: Keybinds,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Periodic table',
|
|
||||||
materialIcon: 'experiment',
|
|
||||||
contentWidget: PeriodicTable,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const CheatsheetHeader = () => Widget.CenterBox({
|
|
||||||
vertical: false,
|
|
||||||
startWidget: Widget.Box({}),
|
|
||||||
centerWidget: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: "spacing-h-15",
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-5 cheatsheet-title',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
hpack: 'center',
|
|
||||||
css: 'margin-right: 0.682rem;',
|
|
||||||
className: 'txt-title',
|
|
||||||
label: 'Cheat sheet',
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
vpack: 'center',
|
|
||||||
className: "cheatsheet-key txt-small",
|
|
||||||
label: "",
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
vpack: 'center',
|
|
||||||
className: "cheatsheet-key-notkey txt-small",
|
|
||||||
label: "+",
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
vpack: 'center',
|
|
||||||
className: "cheatsheet-key txt-small",
|
|
||||||
label: "/",
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
endWidget: Widget.Button({
|
|
||||||
vpack: 'start',
|
|
||||||
hpack: 'end',
|
|
||||||
className: "cheatsheet-closebtn icon-material txt txt-hugeass",
|
|
||||||
onClicked: () => {
|
|
||||||
closeWindowOnAllMonitors('cheatsheet');
|
|
||||||
},
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'icon-material txt txt-hugeass',
|
|
||||||
label: 'close'
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const sheetContents = [];
|
|
||||||
const SheetContent = (id) => {
|
|
||||||
sheetContents[id] = ExpandingIconTabContainer({
|
|
||||||
tabsHpack: 'center',
|
|
||||||
tabSwitcherClassName: 'sidebar-icontabswitcher',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge * 1.4,
|
|
||||||
icons: cheatsheets.map((api) => api.materialIcon),
|
|
||||||
names: cheatsheets.map((api) => api.name),
|
|
||||||
children: cheatsheets.map((api) => api.contentWidget()),
|
|
||||||
onChange: (self, id) => {
|
|
||||||
self.shown = cheatsheets[id].name;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return sheetContents[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (id) => {
|
|
||||||
const sheets = SheetContent(id);
|
|
||||||
const widgetContent = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: "cheatsheet-bg spacing-v-5",
|
|
||||||
children: [
|
|
||||||
CheatsheetHeader(),
|
|
||||||
sheets,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return PopupWindow({
|
|
||||||
monitor: id,
|
|
||||||
name: `cheatsheet${id}`,
|
|
||||||
layer: 'overlay',
|
|
||||||
keymode: 'on-demand',
|
|
||||||
visible: false,
|
|
||||||
anchor: ['top', 'bottom', 'left', 'right'],
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
clickCloseRegion({ name: 'cheatsheet' }),
|
|
||||||
Widget.Box({
|
|
||||||
children: [
|
|
||||||
clickCloseRegion({ name: 'cheatsheet' }),
|
|
||||||
widgetContent,
|
|
||||||
clickCloseRegion({ name: 'cheatsheet' }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
clickCloseRegion({ name: 'cheatsheet' }),
|
|
||||||
],
|
|
||||||
setup: (self) => self.on('key-press-event', (widget, event) => { // Typing
|
|
||||||
// Whole sheet
|
|
||||||
if (checkKeybind(event, userOptions.keybinds.cheatsheet.nextTab))
|
|
||||||
sheetContents.forEach(tab => tab.nextTab())
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.cheatsheet.prevTab))
|
|
||||||
sheetContents.forEach(tab => tab.prevTab())
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.cheatsheet.cycleTab))
|
|
||||||
sheetContents.forEach(tab => tab.cycleTab())
|
|
||||||
// Keybinds
|
|
||||||
if (sheets.attribute.names[sheets.attribute.shown.value] == 'Keybinds') { // If Keybinds tab is focused
|
|
||||||
if (checkKeybind(event, userOptions.keybinds.cheatsheet.keybinds.nextTab)) {
|
|
||||||
sheetContents.forEach((sheet) => {
|
|
||||||
const toSwitchTab = sheet.attribute.children[sheet.attribute.shown.value];
|
|
||||||
toSwitchTab.nextTab();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.cheatsheet.keybinds.prevTab)) {
|
|
||||||
sheetContents.forEach((sheet) => {
|
|
||||||
const toSwitchTab = sheet.attribute.children[sheet.attribute.shown.value];
|
|
||||||
toSwitchTab.prevTab();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { niceTypes, periodicTable, series } from "./data_periodictable.js";
|
|
||||||
const { Box, Button, Icon, Label, Revealer } = Widget;
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const ElementTile = (element) => {
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
tooltipText: element.electronConfig ? `${element.electronConfig}` : null,
|
|
||||||
className: `cheatsheet-periodictable-${element.type}`,
|
|
||||||
children: element.name == '' ? null : [
|
|
||||||
Box({
|
|
||||||
className: 'padding-left-8 padding-right-8 padding-top-8',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
label: `${element.number}`,
|
|
||||||
className: "cheatsheet-periodictable-elementnum txt-tiny txt-bold",
|
|
||||||
}),
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
Label({
|
|
||||||
label: `${element.weight}`,
|
|
||||||
className: "txt-smaller",
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
element.icon ? Icon({
|
|
||||||
icon: element.icon,
|
|
||||||
className: "txt-hugerass txt-bold",
|
|
||||||
}) : Label({
|
|
||||||
label: `${element.symbol}`,
|
|
||||||
className: "cheatsheet-periodictable-elementsymbol",
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
label: `${element.name}`,
|
|
||||||
className: "txt-tiny",
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const BoardColor = (type) => Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: `cheatsheet-periodictable-legend-color-wrapper`,
|
|
||||||
children: [Box({
|
|
||||||
className: `cheatsheet-periodictable-legend-color-${type}`,
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
label: `${niceTypes[type]}`,
|
|
||||||
className: "txt txt-small",
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const mainBoard = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: "spacing-v-3",
|
|
||||||
children: periodicTable.map((row, _) => Box({ // Rows
|
|
||||||
className: "spacing-h-5",
|
|
||||||
children: row.map((element, _) => ElementTile(element))
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
const seriesBoard = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: "spacing-v-3",
|
|
||||||
children: series.map((row, _) => Box({ // Rows
|
|
||||||
className: "spacing-h-5",
|
|
||||||
children: row.map((element, _) => ElementTile(element))
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
const legend = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-20',
|
|
||||||
children: [
|
|
||||||
BoardColor('metal'),
|
|
||||||
BoardColor('nonmetal'),
|
|
||||||
BoardColor('noblegas'),
|
|
||||||
BoardColor('lanthanum'),
|
|
||||||
BoardColor('actinium'),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-20',
|
|
||||||
children: [
|
|
||||||
mainBoard,
|
|
||||||
seriesBoard,
|
|
||||||
legend
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { enableClickthrough } from "../.widgetutils/clickthrough.js";
|
|
||||||
import { RoundedCorner } from "../.commonwidgets/cairo_roundedcorner.js";
|
|
||||||
|
|
||||||
export default (monitor = 0, ) => {
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `crosshair${monitor}`,
|
|
||||||
layer: 'overlay',
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
visible: false,
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: 'crosshair-symbolic',
|
|
||||||
css: `
|
|
||||||
font-size: ${userOptions.gaming.crosshair.size}px;
|
|
||||||
color: ${userOptions.gaming.crosshair.color};
|
|
||||||
`,
|
|
||||||
}),
|
|
||||||
setup: enableClickthrough,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
export const quickLaunchItems = [
|
|
||||||
{
|
|
||||||
"name": "GitHub + Files×2",
|
|
||||||
"command": "github-desktop & nautilus --new-window & nautilus --new-window &"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Terminal×2",
|
|
||||||
"command": "foot & foot &"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Discord + Youtube + Github",
|
|
||||||
"command": "xdg-open 'https://discord.com/app' && xdg-open 'https://youtube.com/' && xdg-open 'https://github.com/' &"
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -1,24 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
|
|
||||||
import WallpaperImage from './wallpaper.js';
|
|
||||||
import TimeAndLaunchesWidget from './timeandlaunches.js'
|
|
||||||
import SystemWidget from './system.js'
|
|
||||||
|
|
||||||
export default (monitor) => Widget.Window({
|
|
||||||
name: `desktopbackground${monitor}`,
|
|
||||||
// anchor: ['top', 'bottom', 'left', 'right'],
|
|
||||||
layer: 'background',
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
visible: true,
|
|
||||||
child: Widget.Overlay({
|
|
||||||
child: WallpaperImage(monitor),
|
|
||||||
// child: Widget.Box({}),
|
|
||||||
overlays: [
|
|
||||||
TimeAndLaunchesWidget(),
|
|
||||||
SystemWidget(),
|
|
||||||
],
|
|
||||||
setup: (self) => {
|
|
||||||
self.set_overlay_pass_through(self.get_children()[1], true);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
|
@ -1,161 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
const { Box, EventBox, Label, Revealer, Overlay } = Widget;
|
|
||||||
import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
|
|
||||||
const ResourceValue = (name, icon, interval, valueUpdateCmd, displayFunc, props = {}) => Box({
|
|
||||||
...props,
|
|
||||||
className: 'bg-system-bg txt',
|
|
||||||
children: [
|
|
||||||
Revealer({
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: 'margin-right-15',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
xalign: 1,
|
|
||||||
className: 'txt-small txt',
|
|
||||||
label: `${name}`,
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
xalign: 1,
|
|
||||||
className: 'titlefont txt-norm txt-onSecondaryContainer',
|
|
||||||
setup: (self) => self
|
|
||||||
.poll(interval, (label) => displayFunc(label))
|
|
||||||
,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
Overlay({
|
|
||||||
child: AnimatedCircProg({
|
|
||||||
className: 'bg-system-circprog',
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.poll(interval, (self) => {
|
|
||||||
execAsync(['bash', '-c', `${valueUpdateCmd}`]).then((newValue) => {
|
|
||||||
self.css = `font-size: ${Math.round(newValue)}px;`
|
|
||||||
}).catch(print);
|
|
||||||
})
|
|
||||||
,
|
|
||||||
}),
|
|
||||||
overlays: [
|
|
||||||
MaterialIcon(`${icon}`, 'hugeass'),
|
|
||||||
],
|
|
||||||
setup: self => self.set_overlay_pass_through(self.get_children()[1], true),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const resources = Box({
|
|
||||||
vpack: 'fill',
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
ResourceValue('Memory', 'memory', 10000, `free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`,
|
|
||||||
(label) => {
|
|
||||||
execAsync(['bash', '-c', `free -h | awk '/^Mem/ {print $3 " / " $2}' | sed 's/Gi/Gib/g'`])
|
|
||||||
.then((output) => {
|
|
||||||
label.label = `${output}`
|
|
||||||
}).catch(print);
|
|
||||||
}, { hpack: 'end' }),
|
|
||||||
ResourceValue('Swap', 'swap_horiz', 10000, `free | awk '/^Swap/ {if ($2 > 0) printf("%.2f\\n", ($3/$2) * 100); else print "0";}'`,
|
|
||||||
(label) => {
|
|
||||||
execAsync(['bash', '-c', `free -h | awk '/^Swap/ {if ($2 != "0") print $3 " / " $2; else print "No swap"}' | sed 's/Gi/Gib/g'`])
|
|
||||||
.then((output) => {
|
|
||||||
label.label = `${output}`
|
|
||||||
}).catch(print);
|
|
||||||
}, { hpack: 'end' }),
|
|
||||||
ResourceValue('Disk space', 'hard_drive_2', 3600000, `echo $(df --output=pcent / | tr -dc '0-9')`,
|
|
||||||
(label) => {
|
|
||||||
execAsync(['bash', '-c', `df -h --output=avail / | awk 'NR==2{print $1}'`])
|
|
||||||
.then((output) => {
|
|
||||||
label.label = `${output} available`
|
|
||||||
}).catch(print);
|
|
||||||
}, { hpack: 'end' }),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const distroAndVersion = Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
hpack: 'end',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'bg-distro-txt',
|
|
||||||
xalign: 0,
|
|
||||||
label: 'Hyping on ',
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
className: 'bg-distro-name',
|
|
||||||
xalign: 0,
|
|
||||||
label: '<distro>',
|
|
||||||
setup: (label) => {
|
|
||||||
execAsync([`grep`, `-oP`, `PRETTY_NAME="\\K[^"]+`, `/etc/os-release`]).then(distro => {
|
|
||||||
label.label = distro;
|
|
||||||
}).catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
hpack: 'end',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'bg-distro-txt',
|
|
||||||
xalign: 0,
|
|
||||||
label: 'with ',
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
className: 'bg-distro-name',
|
|
||||||
xalign: 0,
|
|
||||||
label: 'An environment idk',
|
|
||||||
setup: (label) => {
|
|
||||||
// hyprctl will return unsuccessfully if Hyprland isn't running
|
|
||||||
execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(version => {
|
|
||||||
label.label = `Hyprland ${version}`;
|
|
||||||
}).catch(() => execAsync([`bash`, `-c`, `sway -v | cut -d'-' -f1 | sed 's/sway version /v/'`]).then(version => {
|
|
||||||
label.label = `Sway ${version}`;
|
|
||||||
}).catch(print));
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default () => Box({
|
|
||||||
hpack: 'end',
|
|
||||||
vpack: 'end',
|
|
||||||
children: [
|
|
||||||
EventBox({
|
|
||||||
child: Box({
|
|
||||||
hpack: 'end',
|
|
||||||
vpack: 'end',
|
|
||||||
className: 'bg-distro-box spacing-v-20',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
resources,
|
|
||||||
distroAndVersion,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onPrimaryClickRelease: () => {
|
|
||||||
const kids = resources.get_children();
|
|
||||||
for (let i = 0; i < kids.length; i++) {
|
|
||||||
const child = kids[i];
|
|
||||||
const firstChild = child.get_children()[0];
|
|
||||||
firstChild.revealChild = !firstChild.revealChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
const { GLib } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
const { Box, Label, Button, Revealer, EventBox } = Widget;
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { quickLaunchItems } from './data_quicklaunches.js'
|
|
||||||
|
|
||||||
const TimeAndDate = () => Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v--5',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'bg-time-clock',
|
|
||||||
xalign: 0,
|
|
||||||
label: GLib.DateTime.new_now_local().format(userOptions.time.format),
|
|
||||||
setup: (self) => self.poll(userOptions.time.interval, label => {
|
|
||||||
label.label = GLib.DateTime.new_now_local().format(userOptions.time.format);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
className: 'bg-time-date',
|
|
||||||
xalign: 0,
|
|
||||||
label: GLib.DateTime.new_now_local().format(userOptions.time.dateFormatLong),
|
|
||||||
setup: (self) => self.poll(userOptions.time.dateInterval, (label) => {
|
|
||||||
label.label = GLib.DateTime.new_now_local().format(userOptions.time.dateFormatLong);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const QuickLaunches = () => Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: 'bg-quicklaunch-title',
|
|
||||||
label: 'Quick Launches',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: quickLaunchItems.map((item, i) => Button({
|
|
||||||
onClicked: () => {
|
|
||||||
execAsync(['bash', '-c', `${item["command"]}`]).catch(print);
|
|
||||||
},
|
|
||||||
className: 'bg-quicklaunch-btn',
|
|
||||||
child: Label({
|
|
||||||
label: `${item["name"]}`,
|
|
||||||
}),
|
|
||||||
setup: (self) => {
|
|
||||||
setupCursorHover(self);
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default () => Box({
|
|
||||||
hpack: 'start',
|
|
||||||
vpack: 'end',
|
|
||||||
vertical: true,
|
|
||||||
className: 'bg-time-box spacing-h--10',
|
|
||||||
children: [
|
|
||||||
TimeAndDate(),
|
|
||||||
// QuickLaunches(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { exec, execAsync } = Utils;
|
|
||||||
const { Box, Button, Label, Stack } = Widget;
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
|
|
||||||
import Wallpaper from '../../services/wallpaper.js';
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { clamp } from '../.miscutils/mathfuncs.js';
|
|
||||||
import { monitors } from '../.commondata/hyprlanddata.js';
|
|
||||||
|
|
||||||
const DISABLE_AGS_WALLPAPER = true;
|
|
||||||
|
|
||||||
const SWITCHWALL_SCRIPT_PATH = `${App.configDir}/scripts/color_generation/switchwall.sh`;
|
|
||||||
const WALLPAPER_ZOOM_SCALE = 1.25; // For scrolling when we switch workspace
|
|
||||||
const MAX_WORKSPACES = 10;
|
|
||||||
|
|
||||||
export default (monitor = 0) => {
|
|
||||||
const WALLPAPER_OFFSCREEN_X = (WALLPAPER_ZOOM_SCALE - 1) * monitors[monitor].width;
|
|
||||||
const WALLPAPER_OFFSCREEN_Y = (WALLPAPER_ZOOM_SCALE - 1) * monitors[monitor].height;
|
|
||||||
const wallpaperImage = Widget.DrawingArea({
|
|
||||||
attribute: {
|
|
||||||
pixbuf: undefined,
|
|
||||||
workspace: 1,
|
|
||||||
sideleft: 0,
|
|
||||||
sideright: 0,
|
|
||||||
updatePos: (self) => {
|
|
||||||
self.setCss(`font-size: ${self.attribute.workspace - self.attribute.sideleft + self.attribute.sideright}px;`)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
className: 'bg-wallpaper-transition',
|
|
||||||
setup: (self) => {
|
|
||||||
self.set_size_request(monitors[monitor].width, monitors[monitor].height);
|
|
||||||
self
|
|
||||||
// TODO: reduced updates using timeouts to reduce lag
|
|
||||||
// .hook(Hyprland.active.workspace, (self) => {
|
|
||||||
// self.attribute.workspace = Hyprland.active.workspace.id
|
|
||||||
// self.attribute.updatePos(self);
|
|
||||||
// })
|
|
||||||
// .hook(App, (box, name, visible) => { // Update on open
|
|
||||||
// if (self.attribute[name] === undefined) return;
|
|
||||||
// self.attribute[name] = (visible ? 1 : 0);
|
|
||||||
// self.attribute.updatePos(self);
|
|
||||||
// })
|
|
||||||
.on('draw', (self, cr) => {
|
|
||||||
if (!self.attribute.pixbuf) return;
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
const workspace = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
|
||||||
// Draw
|
|
||||||
Gdk.cairo_set_source_pixbuf(cr, self.attribute.pixbuf,
|
|
||||||
-(WALLPAPER_OFFSCREEN_X / (MAX_WORKSPACES + 1) * (clamp(workspace, 0, MAX_WORKSPACES + 1))),
|
|
||||||
-WALLPAPER_OFFSCREEN_Y / 2);
|
|
||||||
cr.paint();
|
|
||||||
})
|
|
||||||
.hook(Wallpaper, (self) => {
|
|
||||||
if (DISABLE_AGS_WALLPAPER) return;
|
|
||||||
const wallPath = Wallpaper.get(monitor);
|
|
||||||
if (!wallPath || wallPath === "") return;
|
|
||||||
self.attribute.pixbuf = GdkPixbuf.Pixbuf.new_from_file(wallPath);
|
|
||||||
|
|
||||||
const scale_x = monitors[monitor].width * WALLPAPER_ZOOM_SCALE / self.attribute.pixbuf.get_width();
|
|
||||||
const scale_y = monitors[monitor].height * WALLPAPER_ZOOM_SCALE / self.attribute.pixbuf.get_height();
|
|
||||||
const scale_factor = Math.max(scale_x, scale_y);
|
|
||||||
|
|
||||||
self.attribute.pixbuf = self.attribute.pixbuf.scale_simple(
|
|
||||||
Math.round(self.attribute.pixbuf.get_width() * scale_factor),
|
|
||||||
Math.round(self.attribute.pixbuf.get_height() * scale_factor),
|
|
||||||
GdkPixbuf.InterpType.BILINEAR
|
|
||||||
);
|
|
||||||
self.queue_draw();
|
|
||||||
}, 'updated');
|
|
||||||
;
|
|
||||||
}
|
|
||||||
,
|
|
||||||
});
|
|
||||||
const wallpaperPrompt = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
hpack: 'center',
|
|
||||||
justification: 'center',
|
|
||||||
className: 'txt-large',
|
|
||||||
label: `No wallpaper loaded.\nAn image ≥ ${monitors[monitor].width * WALLPAPER_ZOOM_SCALE} × ${monitors[monitor].height * WALLPAPER_ZOOM_SCALE} is recommended.`,
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'btn-primary',
|
|
||||||
label: `Select one`,
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: (self) => Utils.execAsync([SWITCHWALL_SCRIPT_PATH]).catch(print),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const stack = Stack({
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
children: {
|
|
||||||
'disabled': Box({}),
|
|
||||||
'image': wallpaperImage,
|
|
||||||
'prompt': wallpaperPrompt,
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(Wallpaper, (self) => {
|
|
||||||
if (DISABLE_AGS_WALLPAPER) {
|
|
||||||
self.shown = 'disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const wallPath = Wallpaper.get(monitor);
|
|
||||||
self.shown = ((wallPath && wallPath != "") ? 'image' : 'prompt');
|
|
||||||
}, 'updated')
|
|
||||||
,
|
|
||||||
})
|
|
||||||
return stack;
|
|
||||||
// return wallpaperImage;
|
|
||||||
}
|
|
|
@ -1,300 +0,0 @@
|
||||||
const { Gtk, GLib } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { EventBox, Button } = Widget;
|
|
||||||
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
const { Box, Revealer } = Widget;
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { getAllFiles, searchIcons } from './icons.js'
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
import { substitute } from '../.miscutils/icons.js';
|
|
||||||
|
|
||||||
const icon_files = userOptions.icons.searchPaths.map(e => getAllFiles(e)).flat(1)
|
|
||||||
|
|
||||||
let isPinned = false
|
|
||||||
let cachePath = new Map()
|
|
||||||
|
|
||||||
let timers = []
|
|
||||||
|
|
||||||
function clearTimes() {
|
|
||||||
timers.forEach(e => GLib.source_remove(e))
|
|
||||||
timers = []
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExclusiveWindow(client) {
|
|
||||||
const fn = [
|
|
||||||
(client) => !(client !== null && client !== undefined),
|
|
||||||
// Jetbrains
|
|
||||||
(client) => client.title.includes("win"),
|
|
||||||
// Vscode
|
|
||||||
(client) => client.title === '' && client.class === ''
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const item of fn) { if (item(client)) { return true } }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const focus = ({ address }) => Utils.execAsync(`hyprctl dispatch focuswindow address:${address}`).catch(print);
|
|
||||||
|
|
||||||
const DockSeparator = (props = {}) => Box({
|
|
||||||
...props,
|
|
||||||
className: 'dock-separator',
|
|
||||||
})
|
|
||||||
|
|
||||||
const PinButton = () => Widget.Button({
|
|
||||||
className: 'dock-app-btn dock-app-btn-animate',
|
|
||||||
tooltipText: 'Pin Dock',
|
|
||||||
child: Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'dock-app-icon txt',
|
|
||||||
child: MaterialIcon('push_pin', 'hugeass')
|
|
||||||
}),
|
|
||||||
onClicked: (self) => {
|
|
||||||
isPinned = !isPinned
|
|
||||||
self.className = `${isPinned ? "pinned-dock-app-btn" : "dock-app-btn animate"} dock-app-btn-animate`
|
|
||||||
},
|
|
||||||
setup: setupCursorHover,
|
|
||||||
})
|
|
||||||
|
|
||||||
const LauncherButton = () => Widget.Button({
|
|
||||||
className: 'dock-app-btn dock-app-btn-animate',
|
|
||||||
tooltipText: 'Open launcher',
|
|
||||||
child: Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'dock-app-icon txt',
|
|
||||||
child: MaterialIcon('apps', 'hugerass')
|
|
||||||
}),
|
|
||||||
onClicked: (self) => {
|
|
||||||
App.toggleWindow('overview');
|
|
||||||
},
|
|
||||||
setup: setupCursorHover,
|
|
||||||
})
|
|
||||||
|
|
||||||
const AppButton = ({ icon, ...rest }) => Widget.Revealer({
|
|
||||||
attribute: {
|
|
||||||
'workspace': 0
|
|
||||||
},
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_right',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Button({
|
|
||||||
...rest,
|
|
||||||
className: 'dock-app-btn dock-app-btn-animate',
|
|
||||||
child: Widget.Box({
|
|
||||||
child: Widget.Overlay({
|
|
||||||
child: Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'dock-app-icon',
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: icon,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
overlays: [Widget.Box({
|
|
||||||
class_name: 'indicator',
|
|
||||||
vpack: 'end',
|
|
||||||
hpack: 'center',
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
setup: (button) => {
|
|
||||||
setupCursorHover(button);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const Taskbar = (monitor) => Widget.Box({
|
|
||||||
className: 'dock-apps',
|
|
||||||
attribute: {
|
|
||||||
monitor: monitor,
|
|
||||||
'map': new Map(),
|
|
||||||
'clientSortFunc': (a, b) => {
|
|
||||||
return a.attribute.workspace > b.attribute.workspace;
|
|
||||||
},
|
|
||||||
'update': (box, monitor) => {
|
|
||||||
for (let i = 0; i < Hyprland.clients.length; i++) {
|
|
||||||
const client = Hyprland.clients[i];
|
|
||||||
if (client["pid"] == -1) return;
|
|
||||||
const appClass = substitute(client.class);
|
|
||||||
// for (const appName of userOptions.dock.pinnedApps) {
|
|
||||||
// if (appClass.includes(appName.toLowerCase()))
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
let appClassLower = appClass.toLowerCase()
|
|
||||||
let path = ''
|
|
||||||
if (cachePath[appClassLower]) { path = cachePath[appClassLower] }
|
|
||||||
else {
|
|
||||||
path = searchIcons(appClass.toLowerCase(), icon_files)
|
|
||||||
cachePath[appClassLower] = path
|
|
||||||
}
|
|
||||||
if (path === '') { path = substitute(appClass) }
|
|
||||||
const newButton = AppButton({
|
|
||||||
icon: path,
|
|
||||||
tooltipText: `${client.title} (${appClass})`,
|
|
||||||
onClicked: () => focus(client),
|
|
||||||
});
|
|
||||||
newButton.attribute.workspace = client.workspace.id;
|
|
||||||
newButton.revealChild = true;
|
|
||||||
box.attribute.map.set(client.address, newButton);
|
|
||||||
}
|
|
||||||
box.children = Array.from(box.attribute.map.values());
|
|
||||||
},
|
|
||||||
'add': (box, address, monitor) => {
|
|
||||||
if (!address) { // First active emit is undefined
|
|
||||||
box.attribute.update(box);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newClient = Hyprland.clients.find(client => {
|
|
||||||
return client.address == address;
|
|
||||||
});
|
|
||||||
if (ExclusiveWindow(newClient)) { return }
|
|
||||||
let appClass = newClient.class
|
|
||||||
let appClassLower = appClass.toLowerCase()
|
|
||||||
let path = ''
|
|
||||||
if (cachePath[appClassLower]) { path = cachePath[appClassLower] }
|
|
||||||
else {
|
|
||||||
path = searchIcons(appClassLower, icon_files)
|
|
||||||
cachePath[appClassLower] = path
|
|
||||||
}
|
|
||||||
if (path === '') { path = substitute(appClass) }
|
|
||||||
const newButton = AppButton({
|
|
||||||
icon: path,
|
|
||||||
tooltipText: `${newClient.title} (${appClass})`,
|
|
||||||
onClicked: () => focus(newClient),
|
|
||||||
})
|
|
||||||
newButton.attribute.workspace = newClient.workspace.id;
|
|
||||||
box.attribute.map.set(address, newButton);
|
|
||||||
box.children = Array.from(box.attribute.map.values());
|
|
||||||
newButton.revealChild = true;
|
|
||||||
},
|
|
||||||
'remove': (box, address) => {
|
|
||||||
if (!address) return;
|
|
||||||
|
|
||||||
const removedButton = box.attribute.map.get(address);
|
|
||||||
if (!removedButton) return;
|
|
||||||
removedButton.revealChild = false;
|
|
||||||
|
|
||||||
Utils.timeout(userOptions.animations.durationLarge, () => {
|
|
||||||
removedButton.destroy();
|
|
||||||
box.attribute.map.delete(address);
|
|
||||||
box.children = Array.from(box.attribute.map.values());
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Hyprland, (box, address) => box.attribute.add(box, address, self.monitor), 'client-added')
|
|
||||||
.hook(Hyprland, (box, address) => box.attribute.remove(box, address, self.monitor), 'client-removed')
|
|
||||||
Utils.timeout(100, () => self.attribute.update(self));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const PinnedApps = () => Widget.Box({
|
|
||||||
class_name: 'dock-apps',
|
|
||||||
homogeneous: true,
|
|
||||||
children: userOptions.dock.pinnedApps
|
|
||||||
.map(term => ({ app: Applications.query(term)?.[0], term }))
|
|
||||||
.filter(({ app }) => app)
|
|
||||||
.map(({ app, term = true }) => {
|
|
||||||
const newButton = AppButton({
|
|
||||||
// different icon, emm...
|
|
||||||
icon: userOptions.dock.searchPinnedAppIcons ?
|
|
||||||
searchIcons(app.name, icon_files) :
|
|
||||||
app.icon_name,
|
|
||||||
onClicked: () => {
|
|
||||||
for (const client of Hyprland.clients) {
|
|
||||||
if (client.class.toLowerCase().includes(term))
|
|
||||||
return focus(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.launch();
|
|
||||||
},
|
|
||||||
onMiddleClick: () => app.launch(),
|
|
||||||
tooltipText: app.name,
|
|
||||||
setup: (self) => {
|
|
||||||
self.revealChild = true;
|
|
||||||
self.hook(Hyprland, button => {
|
|
||||||
const running = Hyprland.clients
|
|
||||||
.find(client => client.class.toLowerCase().includes(term)) || false;
|
|
||||||
|
|
||||||
button.toggleClassName('notrunning', !running);
|
|
||||||
button.toggleClassName('focused', Hyprland.active.client.address == running.address);
|
|
||||||
button.set_tooltip_text(running ? running.title : app.name);
|
|
||||||
}, 'notify::clients')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
newButton.revealChild = true;
|
|
||||||
return newButton;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default (monitor = 0) => {
|
|
||||||
const dockContent = Box({
|
|
||||||
className: 'dock-bg spacing-h-5',
|
|
||||||
children: [
|
|
||||||
PinButton(),
|
|
||||||
PinnedApps(),
|
|
||||||
DockSeparator(),
|
|
||||||
Taskbar(),
|
|
||||||
LauncherButton(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const dockRevealer = Revealer({
|
|
||||||
attribute: {
|
|
||||||
'updateShow': self => { // I only use mouse to resize. I don't care about keyboard resize if that's a thing
|
|
||||||
if (userOptions.dock.monitorExclusivity)
|
|
||||||
self.revealChild = Hyprland.active.monitor.id === monitor;
|
|
||||||
else
|
|
||||||
self.revealChild = true;
|
|
||||||
|
|
||||||
return self.revealChild
|
|
||||||
}
|
|
||||||
},
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_up',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: dockContent,
|
|
||||||
setup: (self) => {
|
|
||||||
const callback = (self, trigger) => {
|
|
||||||
if (!userOptions.dock.trigger.includes(trigger)) return
|
|
||||||
const flag = self.attribute.updateShow(self)
|
|
||||||
|
|
||||||
if (flag) clearTimes();
|
|
||||||
|
|
||||||
const hidden = userOptions.dock.autoHide.find(e => e["trigger"] === trigger)
|
|
||||||
|
|
||||||
if (hidden) {
|
|
||||||
let id = Utils.timeout(hidden.interval, () => {
|
|
||||||
if (!isPinned) { self.revealChild = false }
|
|
||||||
timers = timers.filter(e => e !== id)
|
|
||||||
})
|
|
||||||
timers.push(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
// .hook(Hyprland, (self) => self.attribute.updateShow(self))
|
|
||||||
.hook(Hyprland.active.workspace, self => callback(self, "workspace-active"))
|
|
||||||
.hook(Hyprland.active.client, self => callback(self, "client-active"))
|
|
||||||
.hook(Hyprland, self => callback(self, "client-added"), "client-added")
|
|
||||||
.hook(Hyprland, self => callback(self, "client-removed"), "client-removed")
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return EventBox({
|
|
||||||
onHover: () => {
|
|
||||||
dockRevealer.revealChild = true;
|
|
||||||
clearTimes()
|
|
||||||
},
|
|
||||||
child: Box({
|
|
||||||
homogeneous: true,
|
|
||||||
css: `min-height: ${userOptions.dock.hiddenThickness}px;`,
|
|
||||||
children: [dockRevealer],
|
|
||||||
}),
|
|
||||||
setup: self => self.on("leave-notify-event", () => {
|
|
||||||
if (!isPinned) dockRevealer.revealChild = false;
|
|
||||||
clearTimes()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
const { Gio, GLib } = imports.gi
|
|
||||||
|
|
||||||
const exists = (path) => Gio.File.new_for_path(path).query_exists(null);
|
|
||||||
|
|
||||||
export const levenshteinDistance = (a, b) => {
|
|
||||||
if (!a.length) { return b.length }
|
|
||||||
if (!b.length) { return a.length }
|
|
||||||
|
|
||||||
let f = Array.from(new Array(a.length + 1),
|
|
||||||
() => new Array(b.length + 1).fill(0))
|
|
||||||
|
|
||||||
for (let i = 0; i <= b.length; i++) { f[0][i] = i; }
|
|
||||||
for (let i = 0; i <= a.length; i++) { f[i][0] = i; }
|
|
||||||
|
|
||||||
for (let i = 1; i <= a.length; i++) {
|
|
||||||
for (let j = 1; j <= b.length; j++) {
|
|
||||||
if (a.charAt(i - 1) === b.charAt(j - 1)) {
|
|
||||||
f[i][j] = f[i-1][j-1]
|
|
||||||
} else {
|
|
||||||
f[i][j] = Math.min(f[i-1][j-1], Math.min(f[i][j-1], f[i-1][j])) + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f[a.length][b.length]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAllFiles = (dir, files = []) => {
|
|
||||||
if (!exists(dir)) { return [] }
|
|
||||||
const file = Gio.File.new_for_path(dir);
|
|
||||||
const enumerator = file.enumerate_children('standard::name,standard::type',
|
|
||||||
Gio.FileQueryInfoFlags.NONE, null);
|
|
||||||
|
|
||||||
for (const info of enumerator) {
|
|
||||||
if (info.get_file_type() === Gio.FileType.DIRECTORY) {
|
|
||||||
files.push(getAllFiles(`${dir}/${info.get_name()}`))
|
|
||||||
} else {
|
|
||||||
files.push(`${dir}/${info.get_name()}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files.flat(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const searchIcons = (appClass, files) => {
|
|
||||||
appClass = appClass.toLowerCase()
|
|
||||||
|
|
||||||
if (!files.length) { return "" }
|
|
||||||
|
|
||||||
let appro = 0x3f3f3f3f
|
|
||||||
let path = ""
|
|
||||||
|
|
||||||
for (const item of files) {
|
|
||||||
let score = levenshteinDistance(item.split("/").pop().toLowerCase().split(".")[0], appClass)
|
|
||||||
|
|
||||||
if (score < appro) {
|
|
||||||
appro = score
|
|
||||||
path = item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Dock from './dock.js';
|
|
||||||
|
|
||||||
export default (monitor = 0) => Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `dock${monitor}`,
|
|
||||||
layer: userOptions.dock.layer,
|
|
||||||
anchor: ['bottom'],
|
|
||||||
exclusivity: 'normal',
|
|
||||||
visible: true,
|
|
||||||
child: Dock(monitor),
|
|
||||||
});
|
|
|
@ -1,264 +0,0 @@
|
||||||
const { Gio, GLib } = imports.gi;
|
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { ConfigToggle, ConfigMulipleSelection } from '../.commonwidgets/configwidgets.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync } = Utils;
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { showColorScheme } from '../../variables.js';
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
import { darkMode } from '../.miscutils/system.js';
|
|
||||||
|
|
||||||
const ColorBox = ({
|
|
||||||
name = 'Color',
|
|
||||||
...rest
|
|
||||||
}) => Widget.Box({
|
|
||||||
...rest,
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
label: `${name}`,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const ColorSchemeSettingsRevealer = () => {
|
|
||||||
const headerButtonIcon = MaterialIcon('expand_more', 'norm');
|
|
||||||
const header = Widget.Button({
|
|
||||||
className: 'osd-settings-btn-arrow',
|
|
||||||
onClicked: () => {
|
|
||||||
content.revealChild = !content.revealChild;
|
|
||||||
headerButtonIcon.label = content.revealChild ? 'expand_less' : 'expand_more';
|
|
||||||
},
|
|
||||||
setup: setupCursorHover,
|
|
||||||
hpack: 'end',
|
|
||||||
child: headerButtonIcon,
|
|
||||||
});
|
|
||||||
const content = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: 200,
|
|
||||||
child: ColorSchemeSettings(),
|
|
||||||
setup: (self) => self.hook(isHoveredColorschemeSettings, (revealer) => {
|
|
||||||
if (isHoveredColorschemeSettings.value == false) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (isHoveredColorschemeSettings.value == false)
|
|
||||||
revealer.revealChild = false;
|
|
||||||
headerButtonIcon.label = 'expand_more';
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return Widget.EventBox({
|
|
||||||
onHover: (self) => {
|
|
||||||
isHoveredColorschemeSettings.setValue(true);
|
|
||||||
},
|
|
||||||
onHoverLost: (self) => {
|
|
||||||
isHoveredColorschemeSettings.setValue(false);
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
header,
|
|
||||||
content,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateSchemeInitIndex(optionsArr, searchValue = 'vibrant') {
|
|
||||||
if (searchValue == '')
|
|
||||||
searchValue = 'vibrant';
|
|
||||||
const flatArray = optionsArr.flatMap(subArray => subArray);
|
|
||||||
const result = flatArray.findIndex(element => element.value === searchValue);
|
|
||||||
const rowIndex = Math.floor(result / optionsArr[0].length);
|
|
||||||
const columnIndex = result % optionsArr[0].length;
|
|
||||||
return [rowIndex, columnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
const schemeOptionsArr = [
|
|
||||||
[
|
|
||||||
{ name: 'Tonal Spot', value: 'tonalspot' },
|
|
||||||
{ name: 'Fruit Salad', value: 'fruitsalad' },
|
|
||||||
{ name: 'Fidelity', value: 'fidelity' },
|
|
||||||
{ name: 'Rainbow', value: 'rainbow' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Neutral', value: 'neutral' },
|
|
||||||
{ name: 'Monochrome', value: 'monochrome' },
|
|
||||||
{ name: 'Expressive', value: 'expressive' },
|
|
||||||
{ name: 'Vibrant', value: 'vibrant' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ name: 'Vibrant+', value: 'morevibrant' },
|
|
||||||
],
|
|
||||||
//[
|
|
||||||
// { name: 'Content', value: 'content' },
|
|
||||||
//]
|
|
||||||
];
|
|
||||||
|
|
||||||
const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/colormode.txt`;
|
|
||||||
const initTransparency = Utils.exec(`bash -c "sed -n \'2p\' ${LIGHTDARK_FILE_LOCATION}"`);
|
|
||||||
const initTransparencyVal = (initTransparency == "transparent") ? 1 : 0;
|
|
||||||
const initScheme = Utils.exec(`bash -c "sed -n \'3p\' ${LIGHTDARK_FILE_LOCATION}"`);
|
|
||||||
const initSchemeIndex = calculateSchemeInitIndex(schemeOptionsArr, initScheme);
|
|
||||||
|
|
||||||
const ColorSchemeSettings = () => Widget.Box({
|
|
||||||
className: 'osd-colorscheme-settings spacing-v-5 margin-20',
|
|
||||||
vertical: true,
|
|
||||||
vpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-norm titlefont txt',
|
|
||||||
label: 'Options',
|
|
||||||
hpack: 'center',
|
|
||||||
}),
|
|
||||||
//////////////////
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'dark_mode',
|
|
||||||
name: 'Dark Mode',
|
|
||||||
desc: 'Ya should go to sleep!',
|
|
||||||
initValue: darkMode.value,
|
|
||||||
onChange: (_, newValue) => {
|
|
||||||
darkMode.value = !!newValue;
|
|
||||||
},
|
|
||||||
extraSetup: (self) => self.hook(darkMode, (self) => {
|
|
||||||
self.enabled.value = darkMode.value;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'border_clear',
|
|
||||||
name: 'Transparency',
|
|
||||||
desc: 'Make shell elements transparent',
|
|
||||||
initValue: initTransparencyVal,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
let transparency = newValue == 0 ? "opaque" : "transparent";
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "2s/.*/${transparency}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`])
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
|
|
||||||
.catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
tooltipText: 'Theme GTK apps using accent color\n(drawback: dark/light mode switching requires restart)',
|
|
||||||
className: 'txt spacing-h-5 configtoggle-box',
|
|
||||||
children: [
|
|
||||||
MaterialIcon('imagesearch_roller', 'norm'),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'txt txt-small',
|
|
||||||
label: 'Use Gradience',
|
|
||||||
}),
|
|
||||||
Widget.Box({ hexpand: true }),
|
|
||||||
ConfigMulipleSelection({
|
|
||||||
hpack: 'center',
|
|
||||||
vpack: 'center',
|
|
||||||
optionsArr: [
|
|
||||||
[{ name: 'Off', value: 0 }, { name: 'On', value: 1 }],
|
|
||||||
],
|
|
||||||
initIndex: [-1, -1],
|
|
||||||
onChange: (value, name) => {
|
|
||||||
const ADWAITA_BLUE = "#3584E4";
|
|
||||||
if (value) execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh - --yes-gradience`, `&`])
|
|
||||||
.catch(print);
|
|
||||||
else execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh "${ADWAITA_BLUE}" --no-gradience`, `&`])
|
|
||||||
.catch(print);
|
|
||||||
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-norm titlefont txt margin-top-5',
|
|
||||||
label: 'Scheme styles',
|
|
||||||
hpack: 'center',
|
|
||||||
}),
|
|
||||||
//////////////////
|
|
||||||
ConfigMulipleSelection({
|
|
||||||
hpack: 'center',
|
|
||||||
vpack: 'center',
|
|
||||||
optionsArr: schemeOptionsArr,
|
|
||||||
initIndex: initSchemeIndex,
|
|
||||||
onChange: (value, name) => {
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "3s/.*/${value}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`])
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
|
|
||||||
.catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const ColorschemeContent = () => Widget.Box({
|
|
||||||
className: 'osd-colorscheme spacing-v-5',
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-norm titlefont txt',
|
|
||||||
label: 'Color scheme',
|
|
||||||
hpack: 'center',
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
ColorBox({ name: 'P', className: 'osd-color osd-color-primary' }),
|
|
||||||
ColorBox({ name: 'S', className: 'osd-color osd-color-secondary' }),
|
|
||||||
ColorBox({ name: 'T', className: 'osd-color osd-color-tertiary' }),
|
|
||||||
ColorBox({ name: 'Sf', className: 'osd-color osd-color-surface' }),
|
|
||||||
ColorBox({ name: 'Sf-i', className: 'osd-color osd-color-inverseSurface' }),
|
|
||||||
ColorBox({ name: 'E', className: 'osd-color osd-color-error' }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
ColorBox({ name: 'P-c', className: 'osd-color osd-color-primaryContainer' }),
|
|
||||||
ColorBox({ name: 'S-c', className: 'osd-color osd-color-secondaryContainer' }),
|
|
||||||
ColorBox({ name: 'T-c', className: 'osd-color osd-color-tertiaryContainer' }),
|
|
||||||
ColorBox({ name: 'Sf-c', className: 'osd-color osd-color-surfaceContainer' }),
|
|
||||||
ColorBox({ name: 'Sf-v', className: 'osd-color osd-color-surfaceVariant' }),
|
|
||||||
ColorBox({ name: 'E-c', className: 'osd-color osd-color-errorContainer' }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
ColorSchemeSettingsRevealer(),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const isHoveredColorschemeSettings = Variable(false);
|
|
||||||
|
|
||||||
export default () => Widget.Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: ColorschemeContent(),
|
|
||||||
setup: (self) => {
|
|
||||||
self
|
|
||||||
.hook(showColorScheme, (revealer) => {
|
|
||||||
if (showColorScheme.value == true)
|
|
||||||
revealer.revealChild = true;
|
|
||||||
else
|
|
||||||
revealer.revealChild = isHoveredColorschemeSettings.value;
|
|
||||||
})
|
|
||||||
.hook(isHoveredColorschemeSettings, (revealer) => {
|
|
||||||
if (isHoveredColorschemeSettings.value == false) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (isHoveredColorschemeSettings.value == false)
|
|
||||||
revealer.revealChild = showColorScheme.value;
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,124 +0,0 @@
|
||||||
// This file is for brightness/volume indicators
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
|
||||||
const { Box, Label, ProgressBar } = Widget;
|
|
||||||
import { MarginRevealer } from '../.widgethacks/advancedrevealers.js';
|
|
||||||
import Brightness from '../../services/brightness.js';
|
|
||||||
import Indicator from '../../services/indicator.js';
|
|
||||||
|
|
||||||
const OsdValue = ({
|
|
||||||
name, nameSetup = undefined, labelSetup, progressSetup,
|
|
||||||
extraClassName = '', extraProgressClassName = '',
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const valueName = Label({
|
|
||||||
xalign: 0, yalign: 0, hexpand: true,
|
|
||||||
className: 'osd-label',
|
|
||||||
label: `${name}`,
|
|
||||||
setup: nameSetup,
|
|
||||||
});
|
|
||||||
const valueNumber = Label({
|
|
||||||
hexpand: false, className: 'osd-value-txt',
|
|
||||||
setup: labelSetup,
|
|
||||||
});
|
|
||||||
return Box({ // Volume
|
|
||||||
vertical: true,
|
|
||||||
hexpand: true,
|
|
||||||
className: `osd-bg osd-value ${extraClassName}`,
|
|
||||||
attribute: {
|
|
||||||
'disable': () => {
|
|
||||||
valueNumber.label = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vexpand: true,
|
|
||||||
children: [
|
|
||||||
valueName,
|
|
||||||
valueNumber,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
ProgressBar({
|
|
||||||
className: `osd-progress ${extraProgressClassName}`,
|
|
||||||
hexpand: true,
|
|
||||||
vertical: false,
|
|
||||||
setup: progressSetup,
|
|
||||||
})
|
|
||||||
],
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (monitor = 0) => {
|
|
||||||
const brightnessIndicator = OsdValue({
|
|
||||||
name: 'Brightness',
|
|
||||||
extraClassName: 'osd-brightness',
|
|
||||||
extraProgressClassName: 'osd-brightness-progress',
|
|
||||||
labelSetup: (self) => self.hook(Brightness[monitor], self => {
|
|
||||||
self.label = `${Math.round(Brightness[monitor].screen_value * 100)}`;
|
|
||||||
}, 'notify::screen-value'),
|
|
||||||
progressSetup: (self) => self.hook(Brightness[monitor], (progress) => {
|
|
||||||
const updateValue = Brightness[monitor].screen_value;
|
|
||||||
if (updateValue !== progress.value) Indicator.popup(1);
|
|
||||||
progress.value = updateValue;
|
|
||||||
}, 'notify::screen-value'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const volumeIndicator = OsdValue({
|
|
||||||
name: 'Volume',
|
|
||||||
extraClassName: 'osd-volume',
|
|
||||||
extraProgressClassName: 'osd-volume-progress',
|
|
||||||
attribute: { headphones: undefined , device: undefined},
|
|
||||||
nameSetup: (self) => Utils.timeout(1, () => {
|
|
||||||
const updateAudioDevice = (self) => {
|
|
||||||
const usingHeadphones = (Audio.speaker?.stream?.port)?.toLowerCase().includes('headphone');
|
|
||||||
if (volumeIndicator.attribute.headphones === undefined ||
|
|
||||||
volumeIndicator.attribute.headphones !== usingHeadphones) {
|
|
||||||
volumeIndicator.attribute.headphones = usingHeadphones;
|
|
||||||
self.label = usingHeadphones ? 'Headphones' : 'Speakers';
|
|
||||||
// Indicator.popup(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.hook(Audio, updateAudioDevice);
|
|
||||||
Utils.timeout(1000, updateAudioDevice);
|
|
||||||
}),
|
|
||||||
labelSetup: (self) => self.hook(Audio, (label) => {
|
|
||||||
const newDevice = (Audio.speaker?.name);
|
|
||||||
const updateValue = Math.round(Audio.speaker?.volume * 100);
|
|
||||||
if (!isNaN(updateValue)) {
|
|
||||||
if (newDevice === volumeIndicator.attribute.device && updateValue != label.label) {
|
|
||||||
Indicator.popup(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volumeIndicator.attribute.device = newDevice;
|
|
||||||
label.label = `${updateValue}`;
|
|
||||||
}),
|
|
||||||
progressSetup: (self) => self.hook(Audio, (progress) => {
|
|
||||||
const updateValue = Audio.speaker?.volume;
|
|
||||||
if (!isNaN(updateValue)) {
|
|
||||||
if (updateValue > 1) progress.value = 1;
|
|
||||||
else progress.value = updateValue;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return MarginRevealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
showClass: 'osd-show',
|
|
||||||
hideClass: 'osd-hide',
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(Indicator, (revealer, value) => {
|
|
||||||
if (value > -1) revealer.attribute.show();
|
|
||||||
else revealer.attribute.hide();
|
|
||||||
}, 'popup')
|
|
||||||
,
|
|
||||||
child: Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vertical: false,
|
|
||||||
className: 'spacing-h--10',
|
|
||||||
children: [
|
|
||||||
brightnessIndicator,
|
|
||||||
volumeIndicator,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Indicator from '../../services/indicator.js';
|
|
||||||
import IndicatorValues from './indicatorvalues.js';
|
|
||||||
import MusicControls from './musiccontrols.js';
|
|
||||||
import ColorScheme from './colorscheme.js';
|
|
||||||
import NotificationPopups from './notificationpopups.js';
|
|
||||||
|
|
||||||
export default (monitor = 0) => Widget.Window({
|
|
||||||
name: `indicator${monitor}`,
|
|
||||||
monitor,
|
|
||||||
className: 'indicator',
|
|
||||||
layer: 'overlay',
|
|
||||||
// exclusivity: 'ignore',
|
|
||||||
visible: true,
|
|
||||||
anchor: ['top'],
|
|
||||||
child: Widget.EventBox({
|
|
||||||
onHover: () => { //make the widget hide when hovering
|
|
||||||
Indicator.popup(-1);
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'osd-window',
|
|
||||||
css: 'min-height: 2px;',
|
|
||||||
children: [
|
|
||||||
IndicatorValues(monitor),
|
|
||||||
MusicControls(),
|
|
||||||
NotificationPopups(),
|
|
||||||
ColorScheme(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
});
|
|
|
@ -1,408 +0,0 @@
|
||||||
const { GLib } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
|
||||||
const { exec, execAsync } = Utils;
|
|
||||||
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
|
||||||
|
|
||||||
import { fileExists } from '../.miscutils/files.js';
|
|
||||||
import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
|
|
||||||
import { showMusicControls } from '../../variables.js';
|
|
||||||
import { darkMode, hasPlasmaIntegration } from '../.miscutils/system.js';
|
|
||||||
|
|
||||||
const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
|
|
||||||
const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/colormode.txt`;
|
|
||||||
const colorMode = Utils.exec(`bash -c "sed -n \'1p\' '${LIGHTDARK_FILE_LOCATION}'"`);
|
|
||||||
const lightDark = (colorMode == "light") ? '-l' : '';
|
|
||||||
const COVER_COLORSCHEME_SUFFIX = '_colorscheme.css';
|
|
||||||
var lastCoverPath = '';
|
|
||||||
|
|
||||||
function isRealPlayer(player) {
|
|
||||||
return (
|
|
||||||
// Remove unecessary native buses from browsers if there's plasma integration
|
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
|
||||||
// playerctld just copies other buses and we don't need duplicates
|
|
||||||
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
|
||||||
// Non-instance mpd bus
|
|
||||||
!(player.busName.endsWith('.mpd') && !player.busName.endsWith('MediaPlayer2.mpd'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getPlayer = (name = userOptions.music.preferredPlayer) => Mpris.getPlayer(name) || Mpris.players[0] || null;
|
|
||||||
function lengthStr(length) {
|
|
||||||
const min = Math.floor(length / 60);
|
|
||||||
const sec = Math.floor(length % 60);
|
|
||||||
const sec0 = sec < 10 ? '0' : '';
|
|
||||||
return `${min}:${sec0}${sec}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectMediaSource(link) {
|
|
||||||
if (link.startsWith("file://")) {
|
|
||||||
if (link.includes('firefox-mpris'))
|
|
||||||
return ' Firefox'
|
|
||||||
return " File";
|
|
||||||
}
|
|
||||||
let url = link.replace(/(^\w+:|^)\/\//, '');
|
|
||||||
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
|
|
||||||
if (domain == 'ytimg.com') return ' Youtube';
|
|
||||||
if (domain == 'discordapp.net') return ' Discord';
|
|
||||||
if (domain == 'sndcdn.com') return ' SoundCloud';
|
|
||||||
return domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif';
|
|
||||||
function getTrackfont(player) {
|
|
||||||
const title = player.trackTitle;
|
|
||||||
const artists = player.trackArtists.join(' ');
|
|
||||||
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo'))
|
|
||||||
return 'Chakra Petch'; // Rigid square replacement
|
|
||||||
if (title.includes('東方'))
|
|
||||||
return 'Crimson Text, serif'; // Serif for Touhou stuff
|
|
||||||
return DEFAULT_MUSIC_FONT;
|
|
||||||
}
|
|
||||||
function trimTrackTitle(title) {
|
|
||||||
if (!title) return '';
|
|
||||||
const cleanPatterns = [
|
|
||||||
/【[^】]*】/, // Touhou n weeb stuff
|
|
||||||
" [FREE DOWNLOAD]", // F-777
|
|
||||||
];
|
|
||||||
cleanPatterns.forEach((expr) => title = title.replace(expr, ''));
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TrackProgress = ({ player, ...rest }) => {
|
|
||||||
const _updateProgress = (circprog) => {
|
|
||||||
// const player = Mpris.getPlayer();
|
|
||||||
if (!player) return;
|
|
||||||
// Set circular progress (see definition of AnimatedCircProg for explanation)
|
|
||||||
circprog.css = `font-size: ${Math.max(player.position / player.length * 100, 0)}px;`
|
|
||||||
}
|
|
||||||
return AnimatedCircProg({
|
|
||||||
...rest,
|
|
||||||
className: 'osd-music-circprog',
|
|
||||||
vpack: 'center',
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(Mpris, _updateProgress)
|
|
||||||
.poll(3000, _updateProgress)
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const TrackTitle = ({ player, ...rest }) => Label({
|
|
||||||
...rest,
|
|
||||||
label: 'No music playing',
|
|
||||||
xalign: 0,
|
|
||||||
truncate: 'end',
|
|
||||||
// wrap: true,
|
|
||||||
className: 'osd-music-title',
|
|
||||||
setup: (self) => self.hook(player, (self) => {
|
|
||||||
// Player name
|
|
||||||
self.label = player.trackTitle.length > 0 ? trimTrackTitle(player.trackTitle) : 'No media';
|
|
||||||
// Font based on track/artist
|
|
||||||
const fontForThisTrack = getTrackfont(player);
|
|
||||||
self.css = `font-family: ${fontForThisTrack}, ${DEFAULT_MUSIC_FONT};`;
|
|
||||||
}, 'notify::track-title'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const TrackArtists = ({ player, ...rest }) => Label({
|
|
||||||
...rest,
|
|
||||||
xalign: 0,
|
|
||||||
className: 'osd-music-artists',
|
|
||||||
truncate: 'end',
|
|
||||||
setup: (self) => self.hook(player, (self) => {
|
|
||||||
self.label = player.trackArtists.length > 0 ? player.trackArtists.join(', ') : '';
|
|
||||||
}, 'notify::track-artists'),
|
|
||||||
})
|
|
||||||
|
|
||||||
const CoverArt = ({ player, ...rest }) => {
|
|
||||||
const fallbackCoverArt = Box({ // Fallback
|
|
||||||
className: 'osd-music-cover-fallback',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Label({
|
|
||||||
className: 'icon-material txt-gigantic txt-thin',
|
|
||||||
label: 'music_note',
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
// const coverArtDrawingArea = Widget.DrawingArea({ className: 'osd-music-cover-art' });
|
|
||||||
// const coverArtDrawingAreaStyleContext = coverArtDrawingArea.get_style_context();
|
|
||||||
const realCoverArt = Box({
|
|
||||||
className: 'osd-music-cover-art',
|
|
||||||
homogeneous: true,
|
|
||||||
// children: [coverArtDrawingArea],
|
|
||||||
attribute: {
|
|
||||||
'pixbuf': null,
|
|
||||||
// 'showImage': (self, imagePath) => {
|
|
||||||
// const borderRadius = coverArtDrawingAreaStyleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
// const frameHeight = coverArtDrawingAreaStyleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
|
||||||
// const frameWidth = coverArtDrawingAreaStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
// let imageHeight = frameHeight;
|
|
||||||
// let imageWidth = frameWidth;
|
|
||||||
// // Get image dimensions
|
|
||||||
// execAsync(['identify', '-format', '{"w":%w,"h":%h}', imagePath])
|
|
||||||
// .then((output) => {
|
|
||||||
// const imageDimensions = JSON.parse(output);
|
|
||||||
// const imageAspectRatio = imageDimensions.w / imageDimensions.h;
|
|
||||||
// const displayedAspectRatio = imageWidth / imageHeight;
|
|
||||||
// if (imageAspectRatio >= displayedAspectRatio) {
|
|
||||||
// imageWidth = imageHeight * imageAspectRatio;
|
|
||||||
// } else {
|
|
||||||
// imageHeight = imageWidth / imageAspectRatio;
|
|
||||||
// }
|
|
||||||
// // Real stuff
|
|
||||||
// // TODO: fix memory leak(?)
|
|
||||||
// // if (self.attribute.pixbuf) {
|
|
||||||
// // self.attribute.pixbuf.unref();
|
|
||||||
// // self.attribute.pixbuf = null;
|
|
||||||
// // }
|
|
||||||
// self.attribute.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(imagePath, imageWidth, imageHeight);
|
|
||||||
|
|
||||||
// coverArtDrawingArea.set_size_request(frameWidth, frameHeight);
|
|
||||||
// coverArtDrawingArea.connect("draw", (widget, cr) => {
|
|
||||||
// // Clip a rounded rectangle area
|
|
||||||
// cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
|
|
||||||
// cr.arc(frameWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
|
|
||||||
// cr.arc(frameWidth - borderRadius, frameHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
|
|
||||||
// cr.arc(borderRadius, frameHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
|
|
||||||
// cr.closePath();
|
|
||||||
// cr.clip();
|
|
||||||
// // Paint image as bg, centered
|
|
||||||
// Gdk.cairo_set_source_pixbuf(cr, self.attribute.pixbuf,
|
|
||||||
// frameWidth / 2 - imageWidth / 2,
|
|
||||||
// frameHeight / 2 - imageHeight / 2
|
|
||||||
// );
|
|
||||||
// cr.paint();
|
|
||||||
// });
|
|
||||||
// }).catch(print)
|
|
||||||
// },
|
|
||||||
'updateCover': (self) => {
|
|
||||||
// const player = Mpris.getPlayer(); // Maybe no need to re-get player.. can't remember why I had this
|
|
||||||
// Player closed
|
|
||||||
// Note that cover path still remains, so we're checking title
|
|
||||||
if (!player || player.trackTitle == "" || !player.coverPath) {
|
|
||||||
self.css = `background-image: none;`; // CSS image
|
|
||||||
App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const coverPath = player.coverPath;
|
|
||||||
const stylePath = `${player.coverPath}${darkMode.value ? '' : '-l'}${COVER_COLORSCHEME_SUFFIX}`;
|
|
||||||
if (player.coverPath == lastCoverPath) { // Since 'notify::cover-path' emits on cover download complete
|
|
||||||
Utils.timeout(200, () => {
|
|
||||||
// self.attribute.showImage(self, coverPath);
|
|
||||||
self.css = `background-image: url('${coverPath}');`; // CSS image
|
|
||||||
});
|
|
||||||
}
|
|
||||||
lastCoverPath = player.coverPath;
|
|
||||||
|
|
||||||
// If a colorscheme has already been generated, skip generation
|
|
||||||
if (fileExists(stylePath)) {
|
|
||||||
// self.attribute.showImage(self, coverPath)
|
|
||||||
self.css = `background-image: url('${coverPath}');`; // CSS image
|
|
||||||
App.applyCss(stylePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate colors
|
|
||||||
execAsync(['bash', '-c',
|
|
||||||
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`])
|
|
||||||
.then(() => {
|
|
||||||
exec(`wal -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
|
|
||||||
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`);
|
|
||||||
exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`);
|
|
||||||
Utils.timeout(200, () => {
|
|
||||||
// self.attribute.showImage(self, coverPath)
|
|
||||||
self.css = `background-image: url('${coverPath}');`; // CSS image
|
|
||||||
});
|
|
||||||
App.applyCss(`${stylePath}`);
|
|
||||||
})
|
|
||||||
.catch(print);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(player, (self) => {
|
|
||||||
self.attribute.updateCover(self);
|
|
||||||
}, 'notify::cover-path')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
return Box({
|
|
||||||
...rest,
|
|
||||||
className: 'osd-music-cover',
|
|
||||||
children: [
|
|
||||||
Widget.Overlay({
|
|
||||||
child: fallbackCoverArt,
|
|
||||||
overlays: [realCoverArt],
|
|
||||||
})
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const TrackControls = ({ player, ...rest }) => Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_right',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Box({
|
|
||||||
...rest,
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'osd-music-controls spacing-h-3',
|
|
||||||
children: [
|
|
||||||
Button({
|
|
||||||
className: 'osd-music-controlbtn',
|
|
||||||
onClicked: () => player.previous(),
|
|
||||||
child: Label({
|
|
||||||
className: 'icon-material osd-music-controlbtn-txt',
|
|
||||||
label: 'skip_previous',
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'osd-music-controlbtn',
|
|
||||||
onClicked: () => player.next(),
|
|
||||||
child: Label({
|
|
||||||
className: 'icon-material osd-music-controlbtn-txt',
|
|
||||||
label: 'skip_next',
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(Mpris, (self) => {
|
|
||||||
// const player = Mpris.getPlayer();
|
|
||||||
if (!player)
|
|
||||||
self.revealChild = false;
|
|
||||||
else
|
|
||||||
self.revealChild = true;
|
|
||||||
}, 'notify::play-back-status'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const TrackSource = ({ player, ...rest }) => Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Box({
|
|
||||||
...rest,
|
|
||||||
className: 'osd-music-pill spacing-h-5',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
hpack: 'fill',
|
|
||||||
justification: 'center',
|
|
||||||
className: 'icon-nerd',
|
|
||||||
setup: (self) => self.hook(player, (self) => {
|
|
||||||
self.label = detectMediaSource(player.trackCoverUrl);
|
|
||||||
}, 'notify::cover-path'),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(Mpris, (self) => {
|
|
||||||
const mpris = Mpris.getPlayer('');
|
|
||||||
if (!mpris)
|
|
||||||
self.revealChild = false;
|
|
||||||
else
|
|
||||||
self.revealChild = true;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const TrackTime = ({ player, ...rest }) => {
|
|
||||||
return Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Box({
|
|
||||||
...rest,
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'osd-music-pill spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
setup: (self) => self.poll(1000, (self) => {
|
|
||||||
// const player = Mpris.getPlayer();
|
|
||||||
if (!player) return;
|
|
||||||
self.label = lengthStr(player.position);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Label({ label: '/' }),
|
|
||||||
Label({
|
|
||||||
setup: (self) => self.hook(Mpris, (self) => {
|
|
||||||
// const player = Mpris.getPlayer();
|
|
||||||
if (!player) return;
|
|
||||||
self.label = lengthStr(player.length);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(Mpris, (self) => {
|
|
||||||
if (!player) self.revealChild = false;
|
|
||||||
else self.revealChild = true;
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlayState = ({ player }) => {
|
|
||||||
var position = 0;
|
|
||||||
const trackCircProg = TrackProgress({ player: player });
|
|
||||||
return Widget.Button({
|
|
||||||
className: 'osd-music-playstate',
|
|
||||||
child: Widget.Overlay({
|
|
||||||
child: trackCircProg,
|
|
||||||
overlays: [
|
|
||||||
Widget.Button({
|
|
||||||
className: 'osd-music-playstate-btn',
|
|
||||||
onClicked: () => player.playPause(),
|
|
||||||
child: Widget.Label({
|
|
||||||
justification: 'center',
|
|
||||||
hpack: 'fill',
|
|
||||||
vpack: 'center',
|
|
||||||
setup: (self) => self.hook(player, (label) => {
|
|
||||||
label.label = `${player.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
|
||||||
}, 'notify::play-back-status'),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
passThrough: true,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const MusicControlsWidget = (player) => Box({
|
|
||||||
className: 'osd-music spacing-h-20 test',
|
|
||||||
children: [
|
|
||||||
CoverArt({ player: player, vpack: 'center' }),
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5 osd-music-info',
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
vpack: 'center',
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
TrackTitle({ player: player }),
|
|
||||||
TrackArtists({ player: player }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Box({ vexpand: true }),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-10',
|
|
||||||
setup: (box) => {
|
|
||||||
box.pack_start(TrackControls({ player: player }), false, false, 0);
|
|
||||||
box.pack_end(PlayState({ player: player }), false, false, 0);
|
|
||||||
if(hasPlasmaIntegration || player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) box.pack_end(TrackTime({ player: player }), false, false, 0)
|
|
||||||
// box.pack_end(TrackSource({ vpack: 'center', player: player }), false, false, 0);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default () => Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: false,
|
|
||||||
child: Box({
|
|
||||||
children: Mpris.bind("players")
|
|
||||||
.as(players => players.map((player) => (isRealPlayer(player) ? MusicControlsWidget(player) : null)))
|
|
||||||
}),
|
|
||||||
setup: (self) => self.hook(showMusicControls, (revealer) => {
|
|
||||||
revealer.revealChild = showMusicControls.value;
|
|
||||||
}),
|
|
||||||
})
|
|
|
@ -1,45 +0,0 @@
|
||||||
// This file is for popup notifications
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
|
||||||
const { Box } = Widget;
|
|
||||||
import Notification from '../.commonwidgets/notification.js';
|
|
||||||
|
|
||||||
export default () => Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'osd-notifs spacing-v-5-revealer',
|
|
||||||
attribute: {
|
|
||||||
'map': new Map(),
|
|
||||||
'dismiss': (box, id, force = false) => {
|
|
||||||
if (!id || !box.attribute.map.has(id))
|
|
||||||
return;
|
|
||||||
const notifWidget = box.attribute.map.get(id);
|
|
||||||
if (notifWidget == null || notifWidget.attribute.hovered && !force)
|
|
||||||
return; // cuz already destroyed
|
|
||||||
|
|
||||||
notifWidget.revealChild = false;
|
|
||||||
notifWidget.attribute.destroyWithAnims();
|
|
||||||
box.attribute.map.delete(id);
|
|
||||||
},
|
|
||||||
'notify': (box, id) => {
|
|
||||||
if (!id || Notifications.dnd) return;
|
|
||||||
if (!Notifications.getNotification(id)) return;
|
|
||||||
|
|
||||||
box.attribute.map.delete(id);
|
|
||||||
|
|
||||||
const notif = Notifications.getNotification(id);
|
|
||||||
const newNotif = Notification({
|
|
||||||
notifObject: notif,
|
|
||||||
isPopup: true,
|
|
||||||
});
|
|
||||||
box.attribute.map.set(id, newNotif);
|
|
||||||
box.pack_end(box.attribute.map.get(id), false, false, 0);
|
|
||||||
box.show_all();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(Notifications, (box, id) => box.attribute.notify(box, id), 'notified')
|
|
||||||
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id), 'dismissed')
|
|
||||||
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed')
|
|
||||||
,
|
|
||||||
});
|
|
|
@ -1,218 +0,0 @@
|
||||||
// We're going to use ydotool
|
|
||||||
// See /usr/include/linux/input-event-codes.h for keycodes
|
|
||||||
|
|
||||||
export const DEFAULT_OSK_LAYOUT = "qwerty_full"
|
|
||||||
export const oskLayouts = {
|
|
||||||
qwerty_full: {
|
|
||||||
name: "QWERTY - Full",
|
|
||||||
name_short: "US",
|
|
||||||
comment: "Like physical keyboard",
|
|
||||||
// A key looks like this: { k: "a", ks: "A", t: "normal" } (key, key-shift, type)
|
|
||||||
// key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand
|
|
||||||
// keys: [
|
|
||||||
// [{ k: "Esc", t: "fn" }, { k: "F1", t: "fn" }, { k: "F2", t: "fn" }, { k: "F3", t: "fn" }, { k: "F4", t: "fn" }, { k: "F5", t: "fn" }, { k: "F6", t: "fn" }, { k: "F7", t: "fn" }, { k: "F8", t: "fn" }, { k: "F9", t: "fn" }, { k: "F10", t: "fn" }, { k: "F11", t: "fn" }, { k: "F12", t: "fn" }, { k: "PrtSc", t: "fn" }, { k: "Del", t: "fn" }],
|
|
||||||
// [{ k: "`", ks: "~", t: "normal" }, { k: "1", ks: "!", t: "normal" }, { k: "2", ks: "@", t: "normal" }, { k: "3", ks: "#", t: "normal" }, { k: "4", ks: "$", t: "normal" }, { k: "5", ks: "%", t: "normal" }, { k: "6", ks: "^", t: "normal" }, { k: "7", ks: "&", t: "normal" }, { k: "8", ks: "*", t: "normal" }, { k: "9", ks: "(", t: "normal" }, { k: "0", ks: ")", t: "normal" }, { k: "-", ks: "_", t: "normal" }, { k: "=", ks: "+", t: "normal" }, { k: "Backspace", t: "shift" }],
|
|
||||||
// [{ k: "Tab", t: "tab" }, { k: "q", ks: "Q", t: "normal" }, { k: "w", ks: "W", t: "normal" }, { k: "e", ks: "E", t: "normal" }, { k: "r", ks: "R", t: "normal" }, { k: "t", ks: "T", t: "normal" }, { k: "y", ks: "Y", t: "normal" }, { k: "u", ks: "U", t: "normal" }, { k: "i", ks: "I", t: "normal" }, { k: "o", ks: "O", t: "normal" }, { k: "p", ks: "P", t: "normal" }, { k: "[", ks: "{", t: "normal" }, { k: "]", ks: "}", t: "normal" }, { k: "\\", ks: "|", t: "expand" }],
|
|
||||||
// [{ k: "Caps", t: "caps" }, { k: "a", ks: "A", t: "normal" }, { k: "s", ks: "S", t: "normal" }, { k: "d", ks: "D", t: "normal" }, { k: "f", ks: "F", t: "normal" }, { k: "g", ks: "G", t: "normal" }, { k: "h", ks: "H", t: "normal" }, { k: "j", ks: "J", t: "normal" }, { k: "k", ks: "K", t: "normal" }, { k: "l", ks: "L", t: "normal" }, { k: ";", ks: ":", t: "normal" }, { k: "'", ks: '"', t: "normal" }, { k: "Enter", t: "expand" }],
|
|
||||||
// [{ k: "Shift", t: "shift" }, { k: "z", ks: "Z", t: "normal" }, { k: "x", ks: "X", t: "normal" }, { k: "c", ks: "C", t: "normal" }, { k: "v", ks: "V", t: "normal" }, { k: "b", ks: "B", t: "normal" }, { k: "n", ks: "N", t: "normal" }, { k: "m", ks: "M", t: "normal" }, { k: ",", ks: "<", t: "normal" }, { k: ".", ks: ">", t: "normal" }, { k: "/", ks: "?", t: "normal" }, { k: "Shift", t: "expand" }],
|
|
||||||
// [{ k: "Ctrl", t: "control" }, { k: "Fn", t: "normal" }, { k: "Win", t: "normal" }, { k: "Alt", t: "normal" }, { k: "Space", t: "space" }, { k: "Alt", t: "normal" }, { k: "Menu", t: "normal" }, { k: "Ctrl", t: "control" }]
|
|
||||||
// ]
|
|
||||||
// A normal key looks like this: {label: "a", labelShift: "A", shape: "normal", keycode: 30, type: "normal"}
|
|
||||||
// A modkey looks like this: {label: "Ctrl", shape: "control", keycode: 29, type: "modkey"}
|
|
||||||
// key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand
|
|
||||||
keys: [
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "Esc", shape: "fn", keycode: 1 },
|
|
||||||
{ keytype: "normal", label: "F1", shape: "fn", keycode: 59 },
|
|
||||||
{ keytype: "normal", label: "F2", shape: "fn", keycode: 60 },
|
|
||||||
{ keytype: "normal", label: "F3", shape: "fn", keycode: 61 },
|
|
||||||
{ keytype: "normal", label: "F4", shape: "fn", keycode: 62 },
|
|
||||||
{ keytype: "normal", label: "F5", shape: "fn", keycode: 63 },
|
|
||||||
{ keytype: "normal", label: "F6", shape: "fn", keycode: 64 },
|
|
||||||
{ keytype: "normal", label: "F7", shape: "fn", keycode: 65 },
|
|
||||||
{ keytype: "normal", label: "F8", shape: "fn", keycode: 66 },
|
|
||||||
{ keytype: "normal", label: "F9", shape: "fn", keycode: 67 },
|
|
||||||
{ keytype: "normal", label: "F10", shape: "fn", keycode: 68 },
|
|
||||||
{ keytype: "normal", label: "F11", shape: "fn", keycode: 87 },
|
|
||||||
{ keytype: "normal", label: "F12", shape: "fn", keycode: 88 },
|
|
||||||
{ keytype: "normal", label: "PrtSc", shape: "fn", keycode: 99 },
|
|
||||||
{ keytype: "normal", label: "Del", shape: "fn", keycode: 111 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "`", labelShift: "~", shape: "normal", keycode: 41 },
|
|
||||||
{ keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 },
|
|
||||||
{ keytype: "normal", label: "2", labelShift: "@", shape: "normal", keycode: 3 },
|
|
||||||
{ keytype: "normal", label: "3", labelShift: "#", shape: "normal", keycode: 4 },
|
|
||||||
{ keytype: "normal", label: "4", labelShift: "$", shape: "normal", keycode: 5 },
|
|
||||||
{ keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 },
|
|
||||||
{ keytype: "normal", label: "6", labelShift: "^", shape: "normal", keycode: 7 },
|
|
||||||
{ keytype: "normal", label: "7", labelShift: "&", shape: "normal", keycode: 8 },
|
|
||||||
{ keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 },
|
|
||||||
{ keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 },
|
|
||||||
{ keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 },
|
|
||||||
{ keytype: "normal", label: "-", labelShift: "_", shape: "normal", keycode: 12 },
|
|
||||||
{ keytype: "normal", label: "=", labelShift: "+", shape: "normal", keycode: 13 },
|
|
||||||
{ keytype: "normal", label: "Backspace", shape: "expand", keycode: 14 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "Tab", shape: "tab", keycode: 15 },
|
|
||||||
{ keytype: "normal", label: "q", labelShift: "Q", shape: "normal", keycode: 16 },
|
|
||||||
{ keytype: "normal", label: "w", labelShift: "W", shape: "normal", keycode: 17 },
|
|
||||||
{ keytype: "normal", label: "e", labelShift: "E", shape: "normal", keycode: 18 },
|
|
||||||
{ keytype: "normal", label: "r", labelShift: "R", shape: "normal", keycode: 19 },
|
|
||||||
{ keytype: "normal", label: "t", labelShift: "T", shape: "normal", keycode: 20 },
|
|
||||||
{ keytype: "normal", label: "y", labelShift: "Y", shape: "normal", keycode: 21 },
|
|
||||||
{ keytype: "normal", label: "u", labelShift: "U", shape: "normal", keycode: 22 },
|
|
||||||
{ keytype: "normal", label: "i", labelShift: "I", shape: "normal", keycode: 23 },
|
|
||||||
{ keytype: "normal", label: "o", labelShift: "O", shape: "normal", keycode: 24 },
|
|
||||||
{ keytype: "normal", label: "p", labelShift: "P", shape: "normal", keycode: 25 },
|
|
||||||
{ keytype: "normal", label: "[", labelShift: "{", shape: "normal", keycode: 26 },
|
|
||||||
{ keytype: "normal", label: "]", labelShift: "}", shape: "normal", keycode: 27 },
|
|
||||||
{ keytype: "normal", label: "\\", labelShift: "|", shape: "expand", keycode: 43 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
//{ keytype: "normal", label: "Caps", shape: "caps", keycode: 58 }, // not needed as double-pressing shift does that
|
|
||||||
{ keytype: "spacer", label: "", shape: "empty" },
|
|
||||||
{ keytype: "spacer", label: "", shape: "empty" },
|
|
||||||
{ keytype: "normal", label: "a", labelShift: "A", shape: "normal", keycode: 30 },
|
|
||||||
{ keytype: "normal", label: "s", labelShift: "S", shape: "normal", keycode: 31 },
|
|
||||||
{ keytype: "normal", label: "d", labelShift: "D", shape: "normal", keycode: 32 },
|
|
||||||
{ keytype: "normal", label: "f", labelShift: "F", shape: "normal", keycode: 33 },
|
|
||||||
{ keytype: "normal", label: "g", labelShift: "G", shape: "normal", keycode: 34 },
|
|
||||||
{ keytype: "normal", label: "h", labelShift: "H", shape: "normal", keycode: 35 },
|
|
||||||
{ keytype: "normal", label: "j", labelShift: "J", shape: "normal", keycode: 36 },
|
|
||||||
{ keytype: "normal", label: "k", labelShift: "K", shape: "normal", keycode: 37 },
|
|
||||||
{ keytype: "normal", label: "l", labelShift: "L", shape: "normal", keycode: 38 },
|
|
||||||
{ keytype: "normal", label: ";", labelShift: ":", shape: "normal", keycode: 39 },
|
|
||||||
{ keytype: "normal", label: "'", labelShift: '"', shape: "normal", keycode: 40 },
|
|
||||||
{ keytype: "normal", label: "Enter", shape: "expand", keycode: 28 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "shift", keycode: 42 },
|
|
||||||
{ keytype: "normal", label: "z", labelShift: "Z", shape: "normal", keycode: 44 },
|
|
||||||
{ keytype: "normal", label: "x", labelShift: "X", shape: "normal", keycode: 45 },
|
|
||||||
{ keytype: "normal", label: "c", labelShift: "C", shape: "normal", keycode: 46 },
|
|
||||||
{ keytype: "normal", label: "v", labelShift: "V", shape: "normal", keycode: 47 },
|
|
||||||
{ keytype: "normal", label: "b", labelShift: "B", shape: "normal", keycode: 48 },
|
|
||||||
{ keytype: "normal", label: "n", labelShift: "N", shape: "normal", keycode: 49 },
|
|
||||||
{ keytype: "normal", label: "m", labelShift: "M", shape: "normal", keycode: 50 },
|
|
||||||
{ keytype: "normal", label: ",", labelShift: "<", shape: "normal", keycode: 51 },
|
|
||||||
{ keytype: "normal", label: ".", labelShift: ">", shape: "normal", keycode: 52 },
|
|
||||||
{ keytype: "normal", label: "/", labelShift: "?", shape: "normal", keycode: 53 },
|
|
||||||
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 } // optional
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 },
|
|
||||||
// { label: "Super", shape: "normal", keycode: 125 }, // dangerous
|
|
||||||
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
|
|
||||||
{ keytype: "normal", label: "Space", shape: "space", keycode: 57 },
|
|
||||||
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 100 },
|
|
||||||
// { label: "Super", shape: "normal", keycode: 126 }, // dangerous
|
|
||||||
{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 },
|
|
||||||
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 }
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
qwertz_full: {
|
|
||||||
name: "QWERTZ - Full",
|
|
||||||
name_short: "DE",
|
|
||||||
comment: "Keyboard layout commonly used in German-speaking countries",
|
|
||||||
keys: [
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "Esc", shape: "fn", keycode: 1 },
|
|
||||||
{ keytype: "normal", label: "F1", shape: "fn", keycode: 59 },
|
|
||||||
{ keytype: "normal", label: "F2", shape: "fn", keycode: 60 },
|
|
||||||
{ keytype: "normal", label: "F3", shape: "fn", keycode: 61 },
|
|
||||||
{ keytype: "normal", label: "F4", shape: "fn", keycode: 62 },
|
|
||||||
{ keytype: "normal", label: "F5", shape: "fn", keycode: 63 },
|
|
||||||
{ keytype: "normal", label: "F6", shape: "fn", keycode: 64 },
|
|
||||||
{ keytype: "normal", label: "F7", shape: "fn", keycode: 65 },
|
|
||||||
{ keytype: "normal", label: "F8", shape: "fn", keycode: 66 },
|
|
||||||
{ keytype: "normal", label: "F9", shape: "fn", keycode: 67 },
|
|
||||||
{ keytype: "normal", label: "F10", shape: "fn", keycode: 68 },
|
|
||||||
{ keytype: "normal", label: "F11", shape: "fn", keycode: 87 },
|
|
||||||
{ keytype: "normal", label: "F12", shape: "fn", keycode: 88 },
|
|
||||||
{ keytype: "normal", label: "Druck", shape: "fn", keycode: 99 },
|
|
||||||
{ keytype: "normal", label: "Entf", shape: "fn", keycode: 111 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "^", labelShift: "°", labelAlt: "′", shape: "normal", keycode: 41 },
|
|
||||||
{ keytype: "normal", label: "1", labelShift: "!", labelAlt: "¹", shape: "normal", keycode: 2 },
|
|
||||||
{ keytype: "normal", label: "2", labelShift: "\"", labelAlt: "²", shape: "normal", keycode: 3 },
|
|
||||||
{ keytype: "normal", label: "3", labelShift: "§", labelAlt: "³", shape: "normal", keycode: 4 },
|
|
||||||
{ keytype: "normal", label: "4", labelShift: "$", labelAlt: "¼", shape: "normal", keycode: 5 },
|
|
||||||
{ keytype: "normal", label: "5", labelShift: "%", labelAlt: "½", shape: "normal", keycode: 6 },
|
|
||||||
{ keytype: "normal", label: "6", labelShift: "&", labelAlt: "¬", shape: "normal", keycode: 7 },
|
|
||||||
{ keytype: "normal", label: "7", labelShift: "/", labelAlt: "{", shape: "normal", keycode: 8 },
|
|
||||||
{ keytype: "normal", label: "8", labelShift: "(", labelAlt: "[", shape: "normal", keycode: 9 },
|
|
||||||
{ keytype: "normal", label: "9", labelShift: ")", labelAlt: "]", shape: "normal", keycode: 10 },
|
|
||||||
{ keytype: "normal", label: "0", labelShift: "=", labelAlt: "}", shape: "normal", keycode: 11 },
|
|
||||||
{ keytype: "normal", label: "ß", labelShift: "?", labelAlt: "\\", shape: "normal", keycode: 12 },
|
|
||||||
{ keytype: "normal", label: "´", labelShift: "`", labelAlt: "¸", shape: "normal", keycode: 13 },
|
|
||||||
{ keytype: "normal", label: "⟵", shape: "expand", keycode: 14 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "normal", label: "Tab ⇆", shape: "tab", keycode: 15 },
|
|
||||||
{ keytype: "normal", label: "q", labelShift: "Q", labelAlt: "@", shape: "normal", keycode: 16 },
|
|
||||||
{ keytype: "normal", label: "w", labelShift: "W", labelAlt: "ſ", shape: "normal", keycode: 17 },
|
|
||||||
{ keytype: "normal", label: "e", labelShift: "E", labelAlt: "€", shape: "normal", keycode: 18 },
|
|
||||||
{ keytype: "normal", label: "r", labelShift: "R", labelAlt: "¶", shape: "normal", keycode: 19 },
|
|
||||||
{ keytype: "normal", label: "t", labelShift: "T", labelAlt: "ŧ", shape: "normal", keycode: 20 },
|
|
||||||
{ keytype: "normal", label: "z", labelShift: "Z", labelAlt: "←", shape: "normal", keycode: 21 },
|
|
||||||
{ keytype: "normal", label: "u", labelShift: "U", labelAlt: "↓", shape: "normal", keycode: 22 },
|
|
||||||
{ keytype: "normal", label: "i", labelShift: "I", labelAlt: "→", shape: "normal", keycode: 23 },
|
|
||||||
{ keytype: "normal", label: "o", labelShift: "O", labelAlt: "ø", shape: "normal", keycode: 24 },
|
|
||||||
{ keytype: "normal", label: "p", labelShift: "P", labelAlt: "þ", shape: "normal", keycode: 25 },
|
|
||||||
{ keytype: "normal", label: "ü", labelShift: "Ü", labelAlt: "¨", shape: "normal", keycode: 26 },
|
|
||||||
{ keytype: "normal", label: "+", labelShift: "*", labelAlt: "~", shape: "normal", keycode: 27 },
|
|
||||||
{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
//{ keytype: "normal", label: "Umschalt ⇩", shape: "caps", keycode: 58 },
|
|
||||||
{ keytype: "spacer", label: "", shape: "empty" },
|
|
||||||
{ keytype: "spacer", label: "", shape: "empty" },
|
|
||||||
{ keytype: "normal", label: "a", labelShift: "A", labelAlt: "æ", shape: "normal", keycode: 30 },
|
|
||||||
{ keytype: "normal", label: "s", labelShift: "S", labelAlt: "ſ", shape: "normal", keycode: 31 },
|
|
||||||
{ keytype: "normal", label: "d", labelShift: "D", labelAlt: "ð", shape: "normal", keycode: 32 },
|
|
||||||
{ keytype: "normal", label: "f", labelShift: "F", labelAlt: "đ", shape: "normal", keycode: 33 },
|
|
||||||
{ keytype: "normal", label: "g", labelShift: "G", labelAlt: "ŋ", shape: "normal", keycode: 34 },
|
|
||||||
{ keytype: "normal", label: "h", labelShift: "H", labelAlt: "ħ", shape: "normal", keycode: 35 },
|
|
||||||
{ keytype: "normal", label: "j", labelShift: "J", labelAlt: "", shape: "normal", keycode: 36 },
|
|
||||||
{ keytype: "normal", label: "k", labelShift: "K", labelAlt: "ĸ", shape: "normal", keycode: 37 },
|
|
||||||
{ keytype: "normal", label: "l", labelShift: "L", labelAlt: "ł", shape: "normal", keycode: 38 },
|
|
||||||
{ keytype: "normal", label: "ö", labelShift: "Ö", labelAlt: "˝", shape: "normal", keycode: 39 },
|
|
||||||
{ keytype: "normal", label: "ä", labelShift: 'Ä', labelAlt: "^", shape: "normal", keycode: 40 },
|
|
||||||
{ keytype: "normal", label: "#", labelShift: '\'', labelAlt: "’", shape: "normal", keycode: 43 },
|
|
||||||
{ keytype: "spacer", label: "", shape: "empty" },
|
|
||||||
//{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "shift", keycode: 42 },
|
|
||||||
{ keytype: "normal", label: "<", labelShift: ">", labelAlt: "|", shape: "normal", keycode: 86 },
|
|
||||||
{ keytype: "normal", label: "y", labelShift: "Y", labelAlt: "»", shape: "normal", keycode: 44 },
|
|
||||||
{ keytype: "normal", label: "x", labelShift: "X", labelAlt: "«", shape: "normal", keycode: 45 },
|
|
||||||
{ keytype: "normal", label: "c", labelShift: "C", labelAlt: "¢", shape: "normal", keycode: 46 },
|
|
||||||
{ keytype: "normal", label: "v", labelShift: "V", labelAlt: "„", shape: "normal", keycode: 47 },
|
|
||||||
{ keytype: "normal", label: "b", labelShift: "B", labelAlt: "“", shape: "normal", keycode: 48 },
|
|
||||||
{ keytype: "normal", label: "n", labelShift: "N", labelAlt: "”", shape: "normal", keycode: 49 },
|
|
||||||
{ keytype: "normal", label: "m", labelShift: "M", labelAlt: "µ", shape: "normal", keycode: 50 },
|
|
||||||
{ keytype: "normal", label: ",", labelShift: ";", labelAlt: "·", shape: "normal", keycode: 51 },
|
|
||||||
{ keytype: "normal", label: ".", labelShift: ":", labelAlt: "…", shape: "normal", keycode: 52 },
|
|
||||||
{ keytype: "normal", label: "-", labelShift: "_", labelAlt: "–", shape: "normal", keycode: 53 },
|
|
||||||
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 }, // optional
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ keytype: "modkey", label: "Strg", shape: "control", keycode: 29 },
|
|
||||||
//{ keytype: "normal", label: "", shape: "normal", keycode: 125 }, // dangerous
|
|
||||||
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
|
|
||||||
{ keytype: "normal", label: "Leertaste", shape: "space", keycode: 57 },
|
|
||||||
{ keytype: "modkey", label: "Alt Gr", shape: "normal", keycode: 100 },
|
|
||||||
// { label: "Super", shape: "normal", keycode: 126 }, // dangerous
|
|
||||||
//{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, // doesn't work?
|
|
||||||
{ keytype: "modkey", label: "Strg", shape: "control", keycode: 97 },
|
|
||||||
{ keytype: "normal", label: "⇦", shape: "normal", keycode: 105 },
|
|
||||||
{ keytype: "normal", label: "⇨", shape: "normal", keycode: 106 },
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import PopupWindow from '../.widgethacks/popupwindow.js';
|
|
||||||
import OnScreenKeyboard from "./onscreenkeyboard.js";
|
|
||||||
|
|
||||||
export default (id) => PopupWindow({
|
|
||||||
monitor: id,
|
|
||||||
anchor: ['bottom'],
|
|
||||||
name: `osk${id}`,
|
|
||||||
showClassName: 'osk-show',
|
|
||||||
hideClassName: 'osk-hide',
|
|
||||||
child: OnScreenKeyboard({ id: id }),
|
|
||||||
});
|
|
|
@ -1,267 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
const { Box, EventBox, Button, Revealer } = Widget;
|
|
||||||
const { execAsync } = Utils;
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
import { DEFAULT_OSK_LAYOUT, oskLayouts } from './data_keyboardlayouts.js';
|
|
||||||
import { setupCursorHoverGrab } from '../.widgetutils/cursorhover.js';
|
|
||||||
|
|
||||||
const keyboardLayout = oskLayouts[userOptions.onScreenKeyboard.layout] ? userOptions.onScreenKeyboard.layout : DEFAULT_OSK_LAYOUT;
|
|
||||||
const keyboardJson = oskLayouts[keyboardLayout];
|
|
||||||
|
|
||||||
async function startYdotoolIfNeeded() {
|
|
||||||
const running = exec('pidof ydotool')
|
|
||||||
if (!running) execAsync(['ydotoold']).catch(print);
|
|
||||||
}
|
|
||||||
|
|
||||||
function releaseAllKeys() {
|
|
||||||
const keycodes = Array.from(Array(249).keys());
|
|
||||||
execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)])
|
|
||||||
.then(console.log('[OSK] Released all keys'))
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
class ShiftMode {
|
|
||||||
static Off = new ShiftMode('Off');
|
|
||||||
static Normal = new ShiftMode('Normal');
|
|
||||||
static Locked = new ShiftMode('Locked');
|
|
||||||
|
|
||||||
constructor(name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
return `ShiftMode.${this.name}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var modsPressed = false;
|
|
||||||
|
|
||||||
const TopDecor = () => Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'osk-dragline',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [EventBox({
|
|
||||||
setup: setupCursorHoverGrab,
|
|
||||||
})]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const KeyboardControlButton = (icon, text, runFunction) => Button({
|
|
||||||
className: 'osk-control-button spacing-h-10',
|
|
||||||
onClicked: () => runFunction(),
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
MaterialIcon(icon, 'norm'),
|
|
||||||
Widget.Label({
|
|
||||||
label: `${text}`,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const KeyboardControls = () => Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: [
|
|
||||||
Button({
|
|
||||||
className: 'osk-control-button txt-norm icon-material',
|
|
||||||
onClicked: () => {
|
|
||||||
releaseAllKeys();
|
|
||||||
toggleWindowOnAllMonitors('osk');
|
|
||||||
},
|
|
||||||
label: 'keyboard_hide',
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'osk-control-button txt-norm',
|
|
||||||
label: `${keyboardJson['name_short']}`,
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'osk-control-button txt-norm icon-material',
|
|
||||||
onClicked: () => { // TODO: Proper clipboard widget, since fuzzel doesn't receive mouse inputs
|
|
||||||
execAsync([`bash`, `-c`, "pkill fuzzel || cliphist list | fuzzel --no-fuzzy --dmenu | cliphist decode | wl-copy"]).catch(print);
|
|
||||||
},
|
|
||||||
label: 'assignment',
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
var shiftMode = ShiftMode.Off;
|
|
||||||
var shiftButton;
|
|
||||||
var rightShiftButton;
|
|
||||||
var allButtons = [];
|
|
||||||
const KeyboardItself = (kbJson) => {
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: kbJson.keys.map(row => Box({
|
|
||||||
vertical: false,
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: row.map(key => {
|
|
||||||
return Button({
|
|
||||||
className: `osk-key osk-key-${key.shape}`,
|
|
||||||
hexpand: ["space", "expand"].includes(key.shape),
|
|
||||||
label: key.label,
|
|
||||||
attribute:
|
|
||||||
{ key: key },
|
|
||||||
setup: (button) => {
|
|
||||||
let pressed = false;
|
|
||||||
allButtons = allButtons.concat(button);
|
|
||||||
if (key.keytype == "normal") {
|
|
||||||
button.connect('pressed', () => { // mouse down
|
|
||||||
execAsync(`ydotool key ${key.keycode}:1`).catch(print);
|
|
||||||
});
|
|
||||||
button.connect('clicked', () => { // release
|
|
||||||
execAsync(`ydotool key ${key.keycode}:0`).catch(print);
|
|
||||||
|
|
||||||
if (shiftMode == ShiftMode.Normal) {
|
|
||||||
shiftMode = ShiftMode.Off;
|
|
||||||
if (typeof shiftButton !== 'undefined') {
|
|
||||||
execAsync(`ydotool key 42:0`).catch(print);
|
|
||||||
shiftButton.toggleClassName('osk-key-active', false);
|
|
||||||
}
|
|
||||||
if (typeof rightShiftButton !== 'undefined') {
|
|
||||||
execAsync(`ydotool key 54:0`).catch(print);
|
|
||||||
rightShiftButton.toggleClassName('osk-key-active', false);
|
|
||||||
}
|
|
||||||
allButtons.forEach(button => {
|
|
||||||
if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.label;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (key.keytype == "modkey") {
|
|
||||||
button.connect('pressed', () => { // release
|
|
||||||
if (pressed) {
|
|
||||||
execAsync(`ydotool key ${key.keycode}:0`).catch(print);
|
|
||||||
button.toggleClassName('osk-key-active', false);
|
|
||||||
pressed = false;
|
|
||||||
if (key.keycode == 100) { // Alt Gr button
|
|
||||||
allButtons.forEach(button => { if (typeof button.attribute.key.labelAlt !== 'undefined') button.label = button.attribute.key.label; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
execAsync(`ydotool key ${key.keycode}:1`).catch(print);
|
|
||||||
button.toggleClassName('osk-key-active', true);
|
|
||||||
if (!(key.keycode == 42 || key.keycode == 54)) pressed = true;
|
|
||||||
else switch (shiftMode.name) { // This toggles the shift button state
|
|
||||||
case "Off": {
|
|
||||||
shiftMode = ShiftMode.Normal;
|
|
||||||
allButtons.forEach(button => { if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.labelShift; })
|
|
||||||
if (typeof shiftButton !== 'undefined') {
|
|
||||||
shiftButton.toggleClassName('osk-key-active', true);
|
|
||||||
}
|
|
||||||
if (typeof rightShiftButton !== 'undefined') {
|
|
||||||
rightShiftButton.toggleClassName('osk-key-active', true);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "Normal": {
|
|
||||||
shiftMode = ShiftMode.Locked;
|
|
||||||
if (typeof shiftButton !== 'undefined') shiftButton.label = key.labelCaps;
|
|
||||||
if (typeof rightShiftButton !== 'undefined') rightShiftButton.label = key.labelCaps;
|
|
||||||
} break;
|
|
||||||
case "Locked": {
|
|
||||||
shiftMode = ShiftMode.Off;
|
|
||||||
if (typeof shiftButton !== 'undefined') {
|
|
||||||
shiftButton.label = key.label;
|
|
||||||
shiftButton.toggleClassName('osk-key-active', false);
|
|
||||||
}
|
|
||||||
if (typeof rightShiftButton !== 'undefined') {
|
|
||||||
rightShiftButton.label = key.label;
|
|
||||||
rightShiftButton.toggleClassName('osk-key-active', false);
|
|
||||||
}
|
|
||||||
execAsync(`ydotool key ${key.keycode}:0`).catch(print);
|
|
||||||
|
|
||||||
allButtons.forEach(button => { if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.label; }
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (key.keycode == 100) { // Alt Gr button
|
|
||||||
allButtons.forEach(button => { if (typeof button.attribute.key.labelAlt !== 'undefined') button.label = button.attribute.key.labelAlt; });
|
|
||||||
}
|
|
||||||
modsPressed = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (key.keycode == 42) shiftButton = button;
|
|
||||||
else if (key.keycode == 54) rightShiftButton = button;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const KeyboardWindow = () => Box({
|
|
||||||
vexpand: true,
|
|
||||||
hexpand: true,
|
|
||||||
vertical: true,
|
|
||||||
className: 'osk-window spacing-v-5',
|
|
||||||
children: [
|
|
||||||
TopDecor(),
|
|
||||||
Box({
|
|
||||||
className: 'osk-body spacing-h-10',
|
|
||||||
children: [
|
|
||||||
KeyboardControls(),
|
|
||||||
Widget.Box({ className: 'separator-line' }),
|
|
||||||
KeyboardItself(keyboardJson),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: (self) => self.hook(App, (self, name, visible) => { // Update on open
|
|
||||||
if (!name) return;
|
|
||||||
if (name.startsWith('osk') && visible) {
|
|
||||||
self.setCss(`margin-bottom: -0px;`);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ({ id }) => {
|
|
||||||
const kbWindow = KeyboardWindow();
|
|
||||||
const gestureEvBox = EventBox({ child: kbWindow })
|
|
||||||
const gesture = Gtk.GestureDrag.new(gestureEvBox);
|
|
||||||
gesture.connect('drag-begin', async () => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
Hyprland.messageAsync('j/cursorpos').then((out) => {
|
|
||||||
gesture.startY = JSON.parse(out).y;
|
|
||||||
}).catch(print);
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
gesture.connect('drag-update', async () => {
|
|
||||||
try {
|
|
||||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
|
||||||
Hyprland.messageAsync('j/cursorpos').then((out) => {
|
|
||||||
const currentY = JSON.parse(out).y;
|
|
||||||
const offset = gesture.startY - currentY;
|
|
||||||
|
|
||||||
if (offset > 0) return;
|
|
||||||
|
|
||||||
kbWindow.setCss(`
|
|
||||||
margin-bottom: ${offset}px;
|
|
||||||
`);
|
|
||||||
}).catch(print);
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
gesture.connect('drag-end', () => {
|
|
||||||
var offset = gesture.get_offset()[2];
|
|
||||||
if (offset > 50) {
|
|
||||||
App.closeWindow(`osk${id}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
kbWindow.setCss(`
|
|
||||||
transition: margin-bottom 170ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
||||||
margin-bottom: 0px;
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return gestureEvBox;
|
|
||||||
};
|
|
|
@ -1,28 +0,0 @@
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
|
|
||||||
function moveClientToWorkspace(address, workspace) {
|
|
||||||
Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dumpToWorkspace(from, to) {
|
|
||||||
if (from == to) return;
|
|
||||||
Hyprland.clients.forEach(client => {
|
|
||||||
if (client.workspace.id == from) {
|
|
||||||
moveClientToWorkspace(client.address, to);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function swapWorkspace(workspaceA, workspaceB) {
|
|
||||||
if (workspaceA == workspaceB) return;
|
|
||||||
const clientsA = [];
|
|
||||||
const clientsB = [];
|
|
||||||
Hyprland.clients.forEach(client => {
|
|
||||||
if (client.workspace.id == workspaceA) clientsA.push(client.address);
|
|
||||||
if (client.workspace.id == workspaceB) clientsB.push(client.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB));
|
|
||||||
clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA));
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import { SearchAndWindows } from "./windowcontent.js";
|
|
||||||
import PopupWindow from '../.widgethacks/popupwindow.js';
|
|
||||||
import { clickCloseRegion } from '../.commonwidgets/clickcloseregion.js';
|
|
||||||
|
|
||||||
export default (id = '') => PopupWindow({
|
|
||||||
name: `overview${id}`,
|
|
||||||
// exclusivity: 'ignore',
|
|
||||||
keymode: 'on-demand',
|
|
||||||
visible: false,
|
|
||||||
anchor: ['top', 'bottom', 'left', 'right'],
|
|
||||||
layer: 'overlay',
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
clickCloseRegion({ name: 'overview', multimonitor: false, expand: false }),
|
|
||||||
Widget.Box({
|
|
||||||
children: [
|
|
||||||
clickCloseRegion({ name: 'overview', multimonitor: false }),
|
|
||||||
SearchAndWindows(),
|
|
||||||
clickCloseRegion({ name: 'overview', multimonitor: false }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
clickCloseRegion({ name: 'overview', multimonitor: false }),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
const { Gio, GLib } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import Todo from "../../services/todo.js";
|
|
||||||
import { darkMode } from '../.miscutils/system.js';
|
|
||||||
|
|
||||||
export function hasUnterminatedBackslash(inputString) {
|
|
||||||
// Use a regular expression to match a trailing odd number of backslashes
|
|
||||||
const regex = /\\+$/;
|
|
||||||
return regex.test(inputString);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function launchCustomCommand(command) {
|
|
||||||
const args = command.toLowerCase().split(' ');
|
|
||||||
if (args[0] == '>raw') { // Mouse raw input
|
|
||||||
Utils.execAsync('hyprctl -j getoption input:accel_profile')
|
|
||||||
.then((output) => {
|
|
||||||
const value = JSON.parse(output)["str"].trim();
|
|
||||||
if (value != "[[EMPTY]]" && value != "") {
|
|
||||||
execAsync(['bash', '-c', `hyprctl keyword input:accel_profile '[[EMPTY]]'`]).catch(print);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
execAsync(['bash', '-c', `hyprctl keyword input:accel_profile flat`]).catch(print);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else if (args[0] == '>img') { // Change wallpaper
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchwall.sh`, `&`]).catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>color') { // Generate colorscheme from color picker
|
|
||||||
if (!args[1])
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh --pick`, `&`]).catch(print);
|
|
||||||
else if (args[1][0] === '#')
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh "${args[1]}"`, `&`]).catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>light') { // Light mode
|
|
||||||
darkMode.value = false;
|
|
||||||
}
|
|
||||||
else if (args[0] == '>dark') { // Dark mode
|
|
||||||
darkMode.value = true;
|
|
||||||
}
|
|
||||||
else if (args[0] == '>badapple') { // Black and white
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "3s/.*/monochrome/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`])
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>adw' || args[0] == '>adwaita') {
|
|
||||||
const ADWAITA_BLUE = "#3584E4";
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh "${ADWAITA_BLUE}" --no-gradience`, `&`])
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>grad' || args[0] == '>gradience') {
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh - --yes-gradience`, `&`])
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>nograd' || args[0] == '>nogradience') {
|
|
||||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh - --no-gradience`, `&`])
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>material') { // Use material colors
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && echo "material" > ${GLib.get_user_state_dir()}/ags/user/colorbackend.txt`]).catch(print)
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>pywal') { // Use Pywal (ik it looks shit but I'm not removing)
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && echo "pywal" > ${GLib.get_user_state_dir()}/ags/user/colorbackend.txt`]).catch(print)
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>todo') { // Todo
|
|
||||||
Todo.add(args.slice(1).join(' '));
|
|
||||||
}
|
|
||||||
else if (args[0] == '>shutdown') { // Shut down
|
|
||||||
execAsync([`bash`, `-c`, `systemctl poweroff || loginctl poweroff`]).catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>reboot') { // Reboot
|
|
||||||
execAsync([`bash`, `-c`, `systemctl reboot || loginctl reboot`]).catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>sleep') { // Sleep
|
|
||||||
execAsync([`bash`, `-c`, `systemctl suspend || loginctl suspend`]).catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>logout') { // Log out
|
|
||||||
execAsync([`bash`, `-c`, `pkill Hyprland`]).catch(print);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function execAndClose(command, terminal) {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
if (terminal) {
|
|
||||||
execAsync([`bash`, `-c`, `${userOptions.apps.terminal} fish -C "${command}"`, `&`]).catch(print);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
execAsync(command).catch(print);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function couldBeMath(str) {
|
|
||||||
const regex = /^[0-9.+*/-]/;
|
|
||||||
return regex.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandTilde(path) {
|
|
||||||
if (path.startsWith('~')) {
|
|
||||||
return GLib.get_home_dir() + path.slice(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileIcon(fileInfo) {
|
|
||||||
let icon = fileInfo.get_icon();
|
|
||||||
if (icon) {
|
|
||||||
// Get the icon's name
|
|
||||||
return icon.get_names()[0];
|
|
||||||
} else {
|
|
||||||
// Default icon for files
|
|
||||||
return 'text-x-generic';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ls({ path = '~', silent = false }) {
|
|
||||||
let contents = [];
|
|
||||||
try {
|
|
||||||
let expandedPath = expandTilde(path);
|
|
||||||
if (expandedPath.endsWith('/'))
|
|
||||||
expandedPath = expandedPath.slice(0, -1);
|
|
||||||
let folder = Gio.File.new_for_path(expandedPath);
|
|
||||||
|
|
||||||
let enumerator = folder.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
|
||||||
let fileInfo;
|
|
||||||
while ((fileInfo = enumerator.next_file(null)) !== null) {
|
|
||||||
let fileName = fileInfo.get_display_name();
|
|
||||||
let fileType = fileInfo.get_file_type();
|
|
||||||
|
|
||||||
let item = {
|
|
||||||
parentPath: expandedPath,
|
|
||||||
name: fileName,
|
|
||||||
type: fileType === Gio.FileType.DIRECTORY ? 'folder' : 'file',
|
|
||||||
icon: getFileIcon(fileInfo),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add file extension for files
|
|
||||||
if (fileType === Gio.FileType.REGULAR) {
|
|
||||||
let fileExtension = fileName.split('.').pop();
|
|
||||||
item.type = `${fileExtension}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
contents.push(item);
|
|
||||||
contents.sort((a, b) => {
|
|
||||||
const aIsFolder = a.type.startsWith('folder');
|
|
||||||
const bIsFolder = b.type.startsWith('folder');
|
|
||||||
if (aIsFolder && !bIsFolder) {
|
|
||||||
return -1;
|
|
||||||
} else if (!aIsFolder && bIsFolder) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return a.name.localeCompare(b.name); // Sort alphabetically within folders and files
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (!silent) console.log(e);
|
|
||||||
}
|
|
||||||
return contents;
|
|
||||||
}
|
|
|
@ -1,431 +0,0 @@
|
||||||
// TODO
|
|
||||||
// - Make client destroy/create not destroy and recreate the whole thing
|
|
||||||
// - Active ws hook optimization: only update when moving to next group
|
|
||||||
//
|
|
||||||
const { Gdk, Gtk } = imports.gi;
|
|
||||||
const { Gravity } = imports.gi.Gdk;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { setupCursorHoverGrab } from '../.widgetutils/cursorhover.js';
|
|
||||||
import { dumpToWorkspace, swapWorkspace } from "./actions.js";
|
|
||||||
import { iconExists, substitute } from "../.miscutils/icons.js";
|
|
||||||
import { monitors } from '../.commondata/hyprlanddata.js';
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
|
|
||||||
const NUM_OF_WORKSPACES_SHOWN = userOptions.overview.numOfCols * userOptions.overview.numOfRows;
|
|
||||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
|
||||||
|
|
||||||
const overviewTick = Variable(false);
|
|
||||||
|
|
||||||
export default (overviewMonitor = 0) => {
|
|
||||||
const clientMap = new Map();
|
|
||||||
const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({
|
|
||||||
label: `${label}`,
|
|
||||||
setup: (menuItem) => {
|
|
||||||
let submenu = new Gtk.Menu();
|
|
||||||
submenu.className = 'menu';
|
|
||||||
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
const startWorkspace = offset + 1;
|
|
||||||
const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1;
|
|
||||||
for (let i = startWorkspace; i <= endWorkspace; i++) {
|
|
||||||
let button = new Gtk.MenuItem({
|
|
||||||
label: `Workspace ${i}`
|
|
||||||
});
|
|
||||||
button.connect("activate", () => {
|
|
||||||
// execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
|
|
||||||
actionFunc(thisWorkspace, i);
|
|
||||||
overviewTick.setValue(!overviewTick.value);
|
|
||||||
});
|
|
||||||
submenu.append(button);
|
|
||||||
}
|
|
||||||
menuItem.set_reserve_indicator(true);
|
|
||||||
menuItem.set_submenu(submenu);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, initialClass, monitor, title, xwayland }, screenCoords) => {
|
|
||||||
const revealInfoCondition = (Math.min(w, h) * userOptions.overview.scale > 70);
|
|
||||||
if (w <= 0 || h <= 0 || (c === '' && title === '')) return null;
|
|
||||||
// Non-primary monitors
|
|
||||||
if (screenCoords.x != 0) x -= screenCoords.x;
|
|
||||||
if (screenCoords.y != 0) y -= screenCoords.y;
|
|
||||||
// Other offscreen adjustments
|
|
||||||
if (x + w <= 0) x += (Math.floor(x / monitors[monitor].width) * monitors[monitor].width);
|
|
||||||
else if (x < 0) { w = x + w; x = 0; }
|
|
||||||
if (y + h <= 0) x += (Math.floor(y / monitors[monitor].height) * monitors[monitor].height);
|
|
||||||
else if (y < 0) { h = y + h; y = 0; }
|
|
||||||
// Truncate if offscreen
|
|
||||||
if (x + w > monitors[monitor].width) w = monitors[monitor].width - x;
|
|
||||||
if (y + h > monitors[monitor].height) h = monitors[monitor].height - y;
|
|
||||||
|
|
||||||
if(c.length == 0) c = initialClass;
|
|
||||||
const iconName = substitute(c);
|
|
||||||
const appIcon = iconExists(iconName) ? Widget.Icon({
|
|
||||||
icon: iconName,
|
|
||||||
size: Math.min(w, h) * userOptions.overview.scale / 2.5,
|
|
||||||
}) : MaterialIcon('terminal', 'gigantic', {
|
|
||||||
css: `font-size: ${Math.min(w, h) * userOptions.overview.scale / 2.5}px`,
|
|
||||||
});
|
|
||||||
return Widget.Button({
|
|
||||||
attribute: {
|
|
||||||
address, x, y, w, h, ws: id,
|
|
||||||
updateIconSize: (self) => {
|
|
||||||
appIcon.size = Math.min(self.attribute.w, self.attribute.h) * userOptions.overview.scale / 2.5;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
className: 'overview-tasks-window',
|
|
||||||
hpack: 'start',
|
|
||||||
vpack: 'start',
|
|
||||||
css: `
|
|
||||||
margin-left: ${Math.round(x * userOptions.overview.scale)}px;
|
|
||||||
margin-top: ${Math.round(y * userOptions.overview.scale)}px;
|
|
||||||
margin-right: -${Math.round((x + w) * userOptions.overview.scale)}px;
|
|
||||||
margin-bottom: -${Math.round((y + h) * userOptions.overview.scale)}px;
|
|
||||||
`,
|
|
||||||
onClicked: (self) => {
|
|
||||||
Hyprland.messageAsync(`dispatch focuswindow address:${address}`);
|
|
||||||
App.closeWindow('overview');
|
|
||||||
},
|
|
||||||
onMiddleClickRelease: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`),
|
|
||||||
onSecondaryClick: (button) => {
|
|
||||||
button.toggleClassName('overview-tasks-window-selected', true);
|
|
||||||
const menu = Widget.Menu({
|
|
||||||
className: 'menu',
|
|
||||||
children: [
|
|
||||||
Widget.MenuItem({
|
|
||||||
child: Widget.Label({
|
|
||||||
xalign: 0,
|
|
||||||
label: "Close (Middle-click)",
|
|
||||||
}),
|
|
||||||
onActivate: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`),
|
|
||||||
}),
|
|
||||||
ContextMenuWorkspaceArray({
|
|
||||||
label: "Dump windows to workspace",
|
|
||||||
actionFunc: dumpToWorkspace,
|
|
||||||
thisWorkspace: Number(id)
|
|
||||||
}),
|
|
||||||
ContextMenuWorkspaceArray({
|
|
||||||
label: "Swap windows with workspace",
|
|
||||||
actionFunc: swapWorkspace,
|
|
||||||
thisWorkspace: Number(id)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
menu.connect("deactivate", () => {
|
|
||||||
button.toggleClassName('overview-tasks-window-selected', false);
|
|
||||||
})
|
|
||||||
menu.connect("selection-done", () => {
|
|
||||||
button.toggleClassName('overview-tasks-window-selected', false);
|
|
||||||
})
|
|
||||||
menu.popup_at_widget(button.get_parent(), Gravity.SOUTH, Gravity.NORTH, null); // Show menu below the button
|
|
||||||
button.connect("destroy", () => menu.destroy());
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
homogeneous: true,
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
vpack: 'center',
|
|
||||||
children: [
|
|
||||||
appIcon,
|
|
||||||
// TODO: Add xwayland tag instead of just having italics
|
|
||||||
Widget.Revealer({
|
|
||||||
transition: 'slide_right',
|
|
||||||
revealChild: revealInfoCondition,
|
|
||||||
child: Widget.Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
revealChild: revealInfoCondition,
|
|
||||||
child: Widget.Label({
|
|
||||||
maxWidthChars: 1, // Doesn't matter what number
|
|
||||||
truncate: 'end',
|
|
||||||
className: `margin-top-5 ${xwayland ? 'txt txt-italic' : 'txt'}`,
|
|
||||||
css: `
|
|
||||||
font-size: ${Math.min(monitors[monitor].width, monitors[monitor].height) * userOptions.overview.scale / 14.6}px;
|
|
||||||
margin: 0px ${Math.min(monitors[monitor].width, monitors[monitor].height) * userOptions.overview.scale / 10}px;
|
|
||||||
`,
|
|
||||||
// If the title is too short, include the class
|
|
||||||
label: (title.length <= 1 ? `${c}: ${title}` : title),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
tooltipText: `${c}: ${title}`,
|
|
||||||
setup: (button) => {
|
|
||||||
setupCursorHoverGrab(button);
|
|
||||||
|
|
||||||
button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE);
|
|
||||||
button.drag_source_set_icon_name(substitute(c));
|
|
||||||
|
|
||||||
button.connect('drag-begin', (button) => { // On drag start, add the dragging class
|
|
||||||
button.toggleClassName('overview-tasks-window-dragging', true);
|
|
||||||
});
|
|
||||||
button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address
|
|
||||||
data.set_text(address, address.length);
|
|
||||||
button.toggleClassName('overview-tasks-window-dragging', false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const Workspace = (index) => {
|
|
||||||
// const fixed = Widget.Fixed({
|
|
||||||
// attribute: {
|
|
||||||
// put: (widget, x, y) => {
|
|
||||||
// fixed.put(widget, x, y);
|
|
||||||
// },
|
|
||||||
// move: (widget, x, y) => {
|
|
||||||
// fixed.move(widget, x, y);
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
const fixed = Widget.Box({
|
|
||||||
attribute: {
|
|
||||||
put: (widget, x, y) => {
|
|
||||||
if (!widget.attribute) return;
|
|
||||||
// Note: x and y are already multiplied by userOptions.overview.scale
|
|
||||||
const newCss = `
|
|
||||||
margin-left: ${Math.round(x)}px;
|
|
||||||
margin-top: ${Math.round(y)}px;
|
|
||||||
margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px;
|
|
||||||
margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px;
|
|
||||||
`;
|
|
||||||
widget.css = newCss;
|
|
||||||
fixed.pack_start(widget, false, false, 0);
|
|
||||||
},
|
|
||||||
move: (widget, x, y) => {
|
|
||||||
if (!widget) return;
|
|
||||||
if (!widget.attribute) return;
|
|
||||||
// Note: x and y are already multiplied by userOptions.overview.scale
|
|
||||||
const newCss = `
|
|
||||||
margin-left: ${Math.round(x)}px;
|
|
||||||
margin-top: ${Math.round(y)}px;
|
|
||||||
margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px;
|
|
||||||
margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px;
|
|
||||||
`;
|
|
||||||
widget.css = newCss;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const WorkspaceNumber = ({ index, ...rest }) => Widget.Label({
|
|
||||||
className: 'overview-tasks-workspace-number',
|
|
||||||
label: `${index}`,
|
|
||||||
css: `
|
|
||||||
margin: ${Math.min(monitors[overviewMonitor].width, monitors[overviewMonitor].height) * userOptions.overview.scale * userOptions.overview.wsNumMarginScale}px;
|
|
||||||
font-size: ${monitors[overviewMonitor].height * userOptions.overview.scale * userOptions.overview.wsNumScale}px;
|
|
||||||
`,
|
|
||||||
setup: (self) => self.hook(Hyprland.active.workspace, (self) => {
|
|
||||||
// Update when going to new ws group
|
|
||||||
const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN);
|
|
||||||
self.label = `${currentGroup * NUM_OF_WORKSPACES_SHOWN + index}`;
|
|
||||||
}),
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
const widget = Widget.Box({
|
|
||||||
className: 'overview-tasks-workspace',
|
|
||||||
vpack: 'center',
|
|
||||||
// Rounding and adding 1px to minimum width/height to work around scaling inaccuracy:
|
|
||||||
css: `
|
|
||||||
min-width: ${1 + Math.round(monitors[overviewMonitor].width * userOptions.overview.scale)}px;
|
|
||||||
min-height: ${1 + Math.round(monitors[overviewMonitor].height * userOptions.overview.scale)}px;
|
|
||||||
`,
|
|
||||||
children: [Widget.EventBox({
|
|
||||||
hexpand: true,
|
|
||||||
onPrimaryClick: () => {
|
|
||||||
Hyprland.messageAsync(`dispatch workspace ${index}`);
|
|
||||||
App.closeWindow('overview');
|
|
||||||
},
|
|
||||||
setup: (eventbox) => {
|
|
||||||
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
|
||||||
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
Hyprland.messageAsync(`dispatch movetoworkspacesilent ${index + offset},address:${data.get_text()}`)
|
|
||||||
overviewTick.setValue(!overviewTick.value);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Widget.Overlay({
|
|
||||||
child: Widget.Box({}),
|
|
||||||
overlays: [
|
|
||||||
WorkspaceNumber({ index: index, hpack: 'start', vpack: 'start' }),
|
|
||||||
fixed
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
})],
|
|
||||||
});
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
fixed.attribute.put(WorkspaceNumber(offset + index), 0, 0);
|
|
||||||
widget.clear = () => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
clientMap.forEach((client, address) => {
|
|
||||||
if (!client) return;
|
|
||||||
if ((client.attribute.ws <= offset || client.attribute.ws > offset + NUM_OF_WORKSPACES_SHOWN) ||
|
|
||||||
(client.attribute.ws == offset + index)) {
|
|
||||||
client.destroy();
|
|
||||||
client = null;
|
|
||||||
clientMap.delete(address);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
widget.set = (clientJson, screenCoords) => {
|
|
||||||
let c = clientMap.get(clientJson.address);
|
|
||||||
if (c) {
|
|
||||||
if (c.attribute?.ws !== clientJson.workspace.id) {
|
|
||||||
c.destroy();
|
|
||||||
c = null;
|
|
||||||
clientMap.delete(clientJson.address);
|
|
||||||
}
|
|
||||||
else if (c) {
|
|
||||||
c.attribute.w = clientJson.size[0];
|
|
||||||
c.attribute.h = clientJson.size[1];
|
|
||||||
c.attribute.updateIconSize(c);
|
|
||||||
fixed.attribute.move(c,
|
|
||||||
Math.max(0, clientJson.at[0] * userOptions.overview.scale),
|
|
||||||
Math.max(0, clientJson.at[1] * userOptions.overview.scale)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const newWindow = Window(clientJson, screenCoords);
|
|
||||||
if (newWindow === null) return;
|
|
||||||
// clientMap.set(clientJson.address, newWindow);
|
|
||||||
fixed.attribute.put(newWindow,
|
|
||||||
Math.max(0, newWindow.attribute.x * userOptions.overview.scale),
|
|
||||||
Math.max(0, newWindow.attribute.y * userOptions.overview.scale)
|
|
||||||
);
|
|
||||||
clientMap.set(clientJson.address, newWindow);
|
|
||||||
};
|
|
||||||
widget.unset = (clientAddress) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
let c = clientMap.get(clientAddress);
|
|
||||||
if (!c) return;
|
|
||||||
c.destroy();
|
|
||||||
c = null;
|
|
||||||
clientMap.delete(clientAddress);
|
|
||||||
};
|
|
||||||
widget.show = () => {
|
|
||||||
fixed.show_all();
|
|
||||||
}
|
|
||||||
return widget;
|
|
||||||
};
|
|
||||||
|
|
||||||
const arr = (s, n) => {
|
|
||||||
const array = [];
|
|
||||||
for (let i = 0; i < n; i++)
|
|
||||||
array.push(s + i);
|
|
||||||
|
|
||||||
return array;
|
|
||||||
};
|
|
||||||
|
|
||||||
const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({
|
|
||||||
children: arr(startWorkspace, workspaces).map(Workspace),
|
|
||||||
attribute: {
|
|
||||||
workspaceGroup: Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN),
|
|
||||||
monitorMap: [],
|
|
||||||
getMonitorMap: (box) => {
|
|
||||||
execAsync('hyprctl -j monitors').then(monitors => {
|
|
||||||
box.attribute.monitorMap = JSON.parse(monitors).reduce((acc, item) => {
|
|
||||||
acc[item.id] = { x: item.x, y: item.y };
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
update: (box) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
Hyprland.messageAsync('j/clients').then(clients => {
|
|
||||||
const allClients = JSON.parse(clients);
|
|
||||||
const kids = box.get_children();
|
|
||||||
kids.forEach(kid => kid.clear());
|
|
||||||
for (let i = 0; i < allClients.length; i++) {
|
|
||||||
const client = allClients[i];
|
|
||||||
const childID = client.workspace.id - (offset + startWorkspace);
|
|
||||||
if (offset + startWorkspace <= client.workspace.id &&
|
|
||||||
client.workspace.id <= offset + startWorkspace + workspaces) {
|
|
||||||
const screenCoords = box.attribute.monitorMap[client.monitor];
|
|
||||||
if (kids[childID]) {
|
|
||||||
kids[childID].set(client, screenCoords);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kids.forEach(kid => kid.show());
|
|
||||||
}).catch(print);
|
|
||||||
},
|
|
||||||
updateWorkspace: (box, id) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
if (!( // Not in range, ignore
|
|
||||||
offset + startWorkspace <= id &&
|
|
||||||
id <= offset + startWorkspace + workspaces
|
|
||||||
)) return;
|
|
||||||
// if (!App.getWindow(windowName)?.visible) return;
|
|
||||||
Hyprland.messageAsync('j/clients').then(clients => {
|
|
||||||
const allClients = JSON.parse(clients);
|
|
||||||
const kids = box.get_children();
|
|
||||||
for (let i = 0; i < allClients.length; i++) {
|
|
||||||
const client = allClients[i];
|
|
||||||
if (client.workspace.id != id) continue;
|
|
||||||
const screenCoords = box.attribute.monitorMap[client.monitor];
|
|
||||||
kids[id - (offset + startWorkspace)]?.set(client, screenCoords);
|
|
||||||
}
|
|
||||||
kids[id - (offset + startWorkspace)]?.show();
|
|
||||||
}).catch(print);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup: (box) => {
|
|
||||||
box.attribute.getMonitorMap(box);
|
|
||||||
box
|
|
||||||
.hook(overviewTick, (box) => box.attribute.update(box))
|
|
||||||
.hook(Hyprland, (box, clientAddress) => {
|
|
||||||
const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
|
|
||||||
const kids = box.get_children();
|
|
||||||
const client = Hyprland.getClient(clientAddress);
|
|
||||||
if (!client) return;
|
|
||||||
const id = client.workspace.id;
|
|
||||||
|
|
||||||
box.attribute.updateWorkspace(box, id);
|
|
||||||
kids[id - (offset + startWorkspace)]?.unset(clientAddress);
|
|
||||||
}, 'client-removed')
|
|
||||||
.hook(Hyprland, (box, clientAddress) => {
|
|
||||||
const client = Hyprland.getClient(clientAddress);
|
|
||||||
if (!client) return;
|
|
||||||
box.attribute.updateWorkspace(box, client.workspace.id);
|
|
||||||
}, 'client-added')
|
|
||||||
.hook(Hyprland.active.workspace, (box) => {
|
|
||||||
// Full update when going to new ws group
|
|
||||||
const previousGroup = box.attribute.workspaceGroup;
|
|
||||||
const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN);
|
|
||||||
if (currentGroup !== previousGroup) {
|
|
||||||
if (!App.getWindow(windowName) || !App.getWindow(windowName).visible) return;
|
|
||||||
box.attribute.update(box);
|
|
||||||
box.attribute.workspaceGroup = currentGroup;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.hook(App, (box, name, visible) => { // Update on open
|
|
||||||
if (name == 'overview' && visible) box.attribute.update(box);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return Widget.Revealer({
|
|
||||||
revealChild: true,
|
|
||||||
// hpack to prevent unneeded expansion in overview-tasks-workspace:
|
|
||||||
hpack: 'center',
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'overview-tasks',
|
|
||||||
children: Array.from({ length: userOptions.overview.numOfRows }, (_, index) =>
|
|
||||||
OverviewRow({
|
|
||||||
startWorkspace: 1 + index * userOptions.overview.numOfCols,
|
|
||||||
workspaces: userOptions.overview.numOfCols,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,176 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { searchItem } from './searchitem.js';
|
|
||||||
import { execAndClose, couldBeMath, launchCustomCommand } from './miscfunctions.js';
|
|
||||||
|
|
||||||
export const NoResultButton = () => searchItem({
|
|
||||||
materialIconName: 'Error',
|
|
||||||
name: "Search invalid",
|
|
||||||
content: "No results found!",
|
|
||||||
onActivate: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const DirectoryButton = ({ parentPath, name, type, icon }) => {
|
|
||||||
const actionText = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "crossfade",
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'overview-search-results-txt txt txt-small txt-action',
|
|
||||||
label: 'Open',
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const actionTextRevealer = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "slide_left",
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
child: actionText,
|
|
||||||
});
|
|
||||||
return Widget.Button({
|
|
||||||
className: 'overview-search-result-btn',
|
|
||||||
onClicked: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
execAsync(['bash', '-c', `xdg-open '${parentPath}/${name}'`, `&`]).catch(print);
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: false,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'overview-search-results-icon',
|
|
||||||
homogeneous: true,
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: icon,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'overview-search-results-txt txt txt-norm',
|
|
||||||
label: name,
|
|
||||||
}),
|
|
||||||
Widget.Box({ hexpand: true }),
|
|
||||||
actionTextRevealer,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (self) => self
|
|
||||||
.on('focus-in-event', (button) => {
|
|
||||||
actionText.revealChild = true;
|
|
||||||
actionTextRevealer.revealChild = true;
|
|
||||||
})
|
|
||||||
.on('focus-out-event', (button) => {
|
|
||||||
actionText.revealChild = false;
|
|
||||||
actionTextRevealer.revealChild = false;
|
|
||||||
})
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CalculationResultButton = ({ result, text }) => searchItem({
|
|
||||||
materialIconName: 'calculate',
|
|
||||||
name: `Math result`,
|
|
||||||
actionName: "Copy",
|
|
||||||
content: `${result}`,
|
|
||||||
onActivate: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
execAsync(['wl-copy', `${result}`]).catch(print);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const DesktopEntryButton = (app) => {
|
|
||||||
const actionText = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "crossfade",
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'overview-search-results-txt txt txt-small txt-action',
|
|
||||||
label: 'Launch',
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const actionTextRevealer = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "slide_left",
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
child: actionText,
|
|
||||||
});
|
|
||||||
return Widget.Button({
|
|
||||||
className: 'overview-search-result-btn',
|
|
||||||
onClicked: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
app.launch();
|
|
||||||
},
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: false,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'overview-search-results-icon',
|
|
||||||
homogeneous: true,
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: app.iconName,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'overview-search-results-txt txt txt-norm',
|
|
||||||
label: app.name,
|
|
||||||
}),
|
|
||||||
Widget.Box({ hexpand: true }),
|
|
||||||
actionTextRevealer,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (self) => self
|
|
||||||
.on('focus-in-event', (button) => {
|
|
||||||
actionText.revealChild = true;
|
|
||||||
actionTextRevealer.revealChild = true;
|
|
||||||
})
|
|
||||||
.on('focus-out-event', (button) => {
|
|
||||||
actionText.revealChild = false;
|
|
||||||
actionTextRevealer.revealChild = false;
|
|
||||||
})
|
|
||||||
,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ExecuteCommandButton = ({ command, terminal = false }) => searchItem({
|
|
||||||
materialIconName: `${terminal ? 'terminal' : 'settings_b_roll'}`,
|
|
||||||
name: `Run command`,
|
|
||||||
actionName: `Execute ${terminal ? 'in terminal' : ''}`,
|
|
||||||
content: `${command}`,
|
|
||||||
onActivate: () => execAndClose(command, terminal),
|
|
||||||
extraClassName: 'techfont',
|
|
||||||
})
|
|
||||||
|
|
||||||
export const CustomCommandButton = ({ text = '' }) => searchItem({
|
|
||||||
materialIconName: 'settings_suggest',
|
|
||||||
name: 'Action',
|
|
||||||
actionName: 'Run',
|
|
||||||
content: `${text}`,
|
|
||||||
onActivate: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
launchCustomCommand(text);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SearchButton = ({ text = '' }) => searchItem({
|
|
||||||
materialIconName: 'travel_explore',
|
|
||||||
name: 'Search the web',
|
|
||||||
actionName: 'Go',
|
|
||||||
content: `${text}`,
|
|
||||||
onActivate: () => {
|
|
||||||
App.closeWindow('overview');
|
|
||||||
let search = userOptions.search.engineBaseUrl + text;
|
|
||||||
for (let site of userOptions.search.excludedSites) {
|
|
||||||
if (site) search += ` -site:${site}`;
|
|
||||||
}
|
|
||||||
execAsync(['bash', '-c', `xdg-open '${search}' &`]).catch(print);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,65 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
|
|
||||||
export const searchItem = ({ materialIconName, name, actionName, content, onActivate, extraClassName = '', ...rest }) => {
|
|
||||||
const actionText = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "crossfade",
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'overview-search-results-txt txt txt-small txt-action',
|
|
||||||
label: `${actionName}`,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const actionTextRevealer = Widget.Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: "slide_left",
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
child: actionText,
|
|
||||||
})
|
|
||||||
return Widget.Button({
|
|
||||||
className: `overview-search-result-btn txt ${extraClassName}`,
|
|
||||||
onClicked: onActivate,
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: false,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
className: `icon-material overview-search-results-icon`,
|
|
||||||
label: `${materialIconName}`,
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'overview-search-results-txt txt-smallie txt-subtext',
|
|
||||||
label: `${name}`,
|
|
||||||
truncate: "end",
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'overview-search-results-txt txt-norm',
|
|
||||||
label: `${content}`,
|
|
||||||
truncate: "end",
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({ hexpand: true }),
|
|
||||||
actionTextRevealer,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (self) => self
|
|
||||||
.on('focus-in-event', (button) => {
|
|
||||||
actionText.revealChild = true;
|
|
||||||
actionTextRevealer.revealChild = true;
|
|
||||||
})
|
|
||||||
.on('focus-out-event', (button) => {
|
|
||||||
actionText.revealChild = false;
|
|
||||||
actionTextRevealer.revealChild = false;
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
const { Gdk, Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { execAndClose, expandTilde, hasUnterminatedBackslash, couldBeMath, launchCustomCommand, ls } from './miscfunctions.js';
|
|
||||||
import {
|
|
||||||
CalculationResultButton, CustomCommandButton, DirectoryButton,
|
|
||||||
DesktopEntryButton, ExecuteCommandButton, SearchButton,
|
|
||||||
} from './searchbuttons.js';
|
|
||||||
import { checkKeybind } from '../.widgetutils/keybind.js';
|
|
||||||
|
|
||||||
// Add math funcs
|
|
||||||
const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math;
|
|
||||||
const pi = Math.PI;
|
|
||||||
// trigonometric funcs for deg
|
|
||||||
const sind = x => sin(x * pi / 180);
|
|
||||||
const cosd = x => cos(x * pi / 180);
|
|
||||||
const tand = x => tan(x * pi / 180);
|
|
||||||
const cotd = x => cot(x * pi / 180);
|
|
||||||
const asind = x => asin(x) * 180 / pi;
|
|
||||||
const acosd = x => acos(x) * 180 / pi;
|
|
||||||
const atand = x => atan(x) * 180 / pi;
|
|
||||||
const acotd = x => acot(x) * 180 / pi;
|
|
||||||
|
|
||||||
const MAX_RESULTS = 10;
|
|
||||||
const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size
|
|
||||||
const OVERVIEW_WS_NUM_SCALE = 0.09;
|
|
||||||
const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
|
|
||||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
|
||||||
|
|
||||||
function iconExists(iconName) {
|
|
||||||
let iconTheme = Gtk.IconTheme.get_default();
|
|
||||||
return iconTheme.has_icon(iconName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const OptionalOverview = async () => {
|
|
||||||
try {
|
|
||||||
return (await import('./overview_hyprland.js')).default();
|
|
||||||
} catch {
|
|
||||||
return Widget.Box({});
|
|
||||||
// return (await import('./overview_hyprland.js')).default();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const overviewContent = await OptionalOverview();
|
|
||||||
|
|
||||||
export const SearchAndWindows = () => {
|
|
||||||
var _appSearchResults = [];
|
|
||||||
|
|
||||||
const resultsBox = Widget.Box({
|
|
||||||
className: 'overview-search-results',
|
|
||||||
vertical: true,
|
|
||||||
});
|
|
||||||
const resultsRevealer = Widget.Revealer({
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_down',
|
|
||||||
// duration: 200,
|
|
||||||
hpack: 'center',
|
|
||||||
child: resultsBox,
|
|
||||||
});
|
|
||||||
const entryPromptRevealer = Widget.Revealer({
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: true,
|
|
||||||
hpack: 'center',
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'overview-search-prompt txt-small txt',
|
|
||||||
label: 'Type to search'
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const entryIconRevealer = Widget.Revealer({
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: false,
|
|
||||||
hpack: 'end',
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'txt txt-large icon-material overview-search-icon',
|
|
||||||
label: 'search',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const entryIcon = Widget.Box({
|
|
||||||
className: 'overview-search-prompt-box',
|
|
||||||
setup: box => box.pack_start(entryIconRevealer, true, true, 0),
|
|
||||||
});
|
|
||||||
|
|
||||||
const entry = Widget.Entry({
|
|
||||||
className: 'overview-search-box txt-small txt',
|
|
||||||
hpack: 'center',
|
|
||||||
onAccept: (self) => { // This is when you hit Enter
|
|
||||||
resultsBox.children[0].onClicked();
|
|
||||||
},
|
|
||||||
onChange: (entry) => { // this is when you type
|
|
||||||
const isAction = entry.text[0] == '>';
|
|
||||||
const isDir = (['/', '~'].includes(entry.text[0]));
|
|
||||||
resultsBox.get_children().forEach(ch => ch.destroy());
|
|
||||||
|
|
||||||
// check empty if so then dont do stuff
|
|
||||||
if (entry.text == '') {
|
|
||||||
resultsRevealer.revealChild = false;
|
|
||||||
overviewContent.revealChild = true;
|
|
||||||
entryPromptRevealer.revealChild = true;
|
|
||||||
entryIconRevealer.revealChild = false;
|
|
||||||
entry.toggleClassName('overview-search-box-extended', false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const text = entry.text;
|
|
||||||
resultsRevealer.revealChild = true;
|
|
||||||
overviewContent.revealChild = false;
|
|
||||||
entryPromptRevealer.revealChild = false;
|
|
||||||
entryIconRevealer.revealChild = true;
|
|
||||||
entry.toggleClassName('overview-search-box-extended', true);
|
|
||||||
_appSearchResults = Applications.query(text);
|
|
||||||
|
|
||||||
// Calculate
|
|
||||||
if (userOptions.search.enableFeatures.mathResults && couldBeMath(text)) { // Eval on typing is dangerous; this is a small workaround.
|
|
||||||
try {
|
|
||||||
const fullResult = eval(text.replace(/\^/g, "**"));
|
|
||||||
resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
|
|
||||||
} catch (e) {
|
|
||||||
// console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (userOptions.search.enableFeatures.directorySearch && isDir) {
|
|
||||||
var contents = [];
|
|
||||||
contents = ls({ path: text, silent: true });
|
|
||||||
contents.forEach((item) => {
|
|
||||||
resultsBox.add(DirectoryButton(item));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (userOptions.search.enableFeatures.actions && isAction) { // Eval on typing is dangerous, this is a workaround.
|
|
||||||
resultsBox.add(CustomCommandButton({ text: entry.text }));
|
|
||||||
}
|
|
||||||
// Add application entries
|
|
||||||
let appsToAdd = MAX_RESULTS;
|
|
||||||
_appSearchResults.forEach(app => {
|
|
||||||
if (appsToAdd == 0) return;
|
|
||||||
resultsBox.add(DesktopEntryButton(app));
|
|
||||||
appsToAdd--;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fallbacks
|
|
||||||
// if the first word is an actual command
|
|
||||||
if (userOptions.search.enableFeatures.commands && !isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') {
|
|
||||||
resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add fallback: search
|
|
||||||
if (userOptions.search.enableFeatures.aiSearch)
|
|
||||||
resultsBox.add(AiButton({ text: entry.text }));
|
|
||||||
if (userOptions.search.enableFeatures.webSearch)
|
|
||||||
resultsBox.add(SearchButton({ text: entry.text }));
|
|
||||||
if (resultsBox.children.length == 0) resultsBox.add(NoResultButton());
|
|
||||||
resultsBox.show_all();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
entry,
|
|
||||||
Widget.Box({
|
|
||||||
className: 'overview-search-icon-box',
|
|
||||||
setup: (box) => {
|
|
||||||
box.pack_start(entryPromptRevealer, true, true, 0)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
entryIcon,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
overviewContent,
|
|
||||||
resultsRevealer,
|
|
||||||
],
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(App, (_b, name, visible) => {
|
|
||||||
if (name == 'overview' && !visible) {
|
|
||||||
resultsBox.children = [];
|
|
||||||
entry.set_text('');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('key-press-event', (widget, event) => { // Typing
|
|
||||||
const keyval = event.get_keyval()[1];
|
|
||||||
const modstate = event.get_state()[1];
|
|
||||||
if (checkKeybind(event, userOptions.keybinds.overview.altMoveLeft))
|
|
||||||
entry.set_position(Math.max(entry.get_position() - 1, 0));
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.overview.altMoveRight))
|
|
||||||
entry.set_position(Math.min(entry.get_position() + 1, entry.get_text().length));
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.overview.deleteToEnd)) {
|
|
||||||
const text = entry.get_text();
|
|
||||||
const pos = entry.get_position();
|
|
||||||
const newText = text.slice(0, pos);
|
|
||||||
entry.set_text(newText);
|
|
||||||
entry.set_position(newText.length);
|
|
||||||
}
|
|
||||||
else if (!(modstate & Gdk.ModifierType.CONTROL_MASK)) { // Ctrl not held
|
|
||||||
if (keyval >= 32 && keyval <= 126 && widget != entry) {
|
|
||||||
Utils.timeout(1, () => entry.grab_focus());
|
|
||||||
entry.set_text(entry.text + String.fromCharCode(keyval));
|
|
||||||
entry.set_position(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,38 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
import { enableClickthrough } from "../.widgetutils/clickthrough.js";
|
|
||||||
import { RoundedCorner } from "../.commonwidgets/cairo_roundedcorner.js";
|
|
||||||
|
|
||||||
if(userOptions.appearance.fakeScreenRounding === 2) Hyprland.connect('event', (service, name, data) => {
|
|
||||||
if (name == 'fullscreen') {
|
|
||||||
const monitor = Hyprland.active.monitor.id;
|
|
||||||
if (data == '1') {
|
|
||||||
for (const window of App.windows) {
|
|
||||||
if (window.name.startsWith("corner") && window.name.endsWith(monitor)) {
|
|
||||||
App.closeWindow(window.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const window of App.windows) {
|
|
||||||
if (window.name.startsWith("corner") && window.name.endsWith(monitor)) {
|
|
||||||
App.openWindow(window.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default (monitor = 0, where = 'bottom left', useOverlayLayer = true) => {
|
|
||||||
const positionString = where.replace(/\s/, ""); // remove space
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
name: `corner${positionString}${monitor}`,
|
|
||||||
layer: useOverlayLayer ? 'overlay' : 'top',
|
|
||||||
anchor: where.split(' '),
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
visible: true,
|
|
||||||
child: RoundedCorner(positionString, { className: 'corner-black', }),
|
|
||||||
setup: enableClickthrough,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import SessionScreen from "./sessionscreen.js";
|
|
||||||
import PopupWindow from '../.widgethacks/popupwindow.js';
|
|
||||||
|
|
||||||
export default (id = 0) => PopupWindow({ // On-screen keyboard
|
|
||||||
monitor: id,
|
|
||||||
name: `session${id}`,
|
|
||||||
visible: false,
|
|
||||||
keymode: 'on-demand',
|
|
||||||
layer: 'overlay',
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
anchor: ['top', 'bottom', 'left', 'right'],
|
|
||||||
child: SessionScreen({ id: id }),
|
|
||||||
})
|
|
|
@ -1,134 +0,0 @@
|
||||||
// This is for the cool memory indicator on the sidebar
|
|
||||||
// For the right pill of the bar, see system.js
|
|
||||||
const { Gdk, Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
import { monitors } from '../.commondata/hyprlanddata.js';
|
|
||||||
|
|
||||||
const { exec, execAsync } = Utils;
|
|
||||||
|
|
||||||
const SessionButton = (name, icon, command, props = {}, colorid = 0) => {
|
|
||||||
const buttonDescription = Widget.Revealer({
|
|
||||||
vpack: 'end',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
transition: 'slide_down',
|
|
||||||
revealChild: false,
|
|
||||||
child: Widget.Label({
|
|
||||||
className: 'txt-smaller session-button-desc',
|
|
||||||
label: name,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return Widget.Button({
|
|
||||||
onClicked: command,
|
|
||||||
className: `session-button session-color-${colorid}`,
|
|
||||||
child: Widget.Overlay({
|
|
||||||
className: 'session-button-box',
|
|
||||||
child: Widget.Label({
|
|
||||||
vexpand: true,
|
|
||||||
className: 'icon-material',
|
|
||||||
label: icon,
|
|
||||||
}),
|
|
||||||
overlays: [
|
|
||||||
buttonDescription,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onHover: (button) => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'pointer');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
buttonDescription.revealChild = true;
|
|
||||||
},
|
|
||||||
onHoverLost: (button) => {
|
|
||||||
const display = Gdk.Display.get_default();
|
|
||||||
const cursor = Gdk.Cursor.new_from_name(display, 'default');
|
|
||||||
button.get_window().set_cursor(cursor);
|
|
||||||
buttonDescription.revealChild = false;
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.on('focus-in-event', (self) => {
|
|
||||||
buttonDescription.revealChild = true;
|
|
||||||
self.toggleClassName('session-button-focused', true);
|
|
||||||
})
|
|
||||||
.on('focus-out-event', (self) => {
|
|
||||||
buttonDescription.revealChild = false;
|
|
||||||
self.toggleClassName('session-button-focused', false);
|
|
||||||
})
|
|
||||||
,
|
|
||||||
...props,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ({ id = 0 }) => {
|
|
||||||
// lock, logout, sleep
|
|
||||||
const lockButton = SessionButton('Lock', 'lock', () => { closeWindowOnAllMonitors('session'); execAsync(['loginctl', 'lock-session']).catch(print) }, {}, 1);
|
|
||||||
const logoutButton = SessionButton('Logout', 'logout', () => { closeWindowOnAllMonitors('session'); execAsync(['bash', '-c', 'pkill Hyprland || loginctl terminate-user $USER']).catch(print) }, {}, 2);
|
|
||||||
const sleepButton = SessionButton('Sleep', 'sleep', () => { closeWindowOnAllMonitors('session'); execAsync(['bash', '-c', 'systemctl suspend || loginctl suspend']).catch(print) }, {}, 3);
|
|
||||||
// hibernate, shutdown, reboot
|
|
||||||
const hibernateButton = SessionButton('Hibernate', 'downloading', () => { closeWindowOnAllMonitors('session'); execAsync(['bash', '-c', 'systemctl hibernate || loginctl hibernate']).catch(print) }, {}, 4);
|
|
||||||
const shutdownButton = SessionButton('Shutdown', 'power_settings_new', () => { closeWindowOnAllMonitors('session'); execAsync(['bash', '-c', 'systemctl poweroff || loginctl poweroff']).catch(print) }, {}, 5);
|
|
||||||
const rebootButton = SessionButton('Reboot', 'restart_alt', () => { closeWindowOnAllMonitors('session'); execAsync(['bash', '-c', 'systemctl reboot || loginctl reboot']).catch(print) }, {}, 6);
|
|
||||||
const cancelButton = SessionButton('Cancel', 'close', () => closeWindowOnAllMonitors('session'), { className: 'session-button-cancel' }, 7);
|
|
||||||
|
|
||||||
const sessionDescription = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
css: 'margin-bottom: 0.682rem;',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
className: 'txt-title txt',
|
|
||||||
label: 'Session',
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
className: 'txt-small txt',
|
|
||||||
label: 'Use arrow keys to navigate.\nEnter to select, Esc to cancel.'
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const SessionButtonRow = (children) => Widget.Box({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'spacing-h-15',
|
|
||||||
children: children,
|
|
||||||
});
|
|
||||||
const sessionButtonRows = [
|
|
||||||
SessionButtonRow([lockButton, logoutButton, sleepButton]),
|
|
||||||
SessionButtonRow([hibernateButton, shutdownButton, rebootButton]),
|
|
||||||
SessionButtonRow([cancelButton]),
|
|
||||||
]
|
|
||||||
return Widget.Box({
|
|
||||||
className: 'session-bg',
|
|
||||||
css: `
|
|
||||||
min-width: ${monitors[id].width}px;
|
|
||||||
min-height: ${monitors[id].height}px;
|
|
||||||
`, // idk why but height = screen height doesn't fill
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.EventBox({
|
|
||||||
onPrimaryClick: () => closeWindowOnAllMonitors('session'),
|
|
||||||
onSecondaryClick: () => closeWindowOnAllMonitors('session'),
|
|
||||||
onMiddleClick: () => closeWindowOnAllMonitors('session'),
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vexpand: true,
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
sessionDescription,
|
|
||||||
...sessionButtonRows,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(App, (_b, name, visible) => {
|
|
||||||
if (visible) lockButton.grab_focus(); // Lock is the default option
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,365 +0,0 @@
|
||||||
const { Gdk, Gio, GLib, Gtk } = imports.gi;
|
|
||||||
import GtkSource from "gi://GtkSource?version=3.0";
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Button, Label, Icon, Scrollable, Stack } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import md2pango from '../../.miscutils/md2pango.js';
|
|
||||||
import { darkMode } from "../../.miscutils/system.js";
|
|
||||||
|
|
||||||
const LATEX_DIR = `${GLib.get_user_cache_dir()}/ags/media/latex`;
|
|
||||||
const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/assets/themes/sourceviewtheme${darkMode.value ? '' : '-light'}.xml`;
|
|
||||||
const CUSTOM_SCHEME_ID = `custom${darkMode.value ? '' : '-light'}`;
|
|
||||||
const USERNAME = GLib.get_user_name();
|
|
||||||
|
|
||||||
/////////////////////// Custom source view colorscheme /////////////////////////
|
|
||||||
|
|
||||||
function loadCustomColorScheme(filePath) {
|
|
||||||
// Read the XML file content
|
|
||||||
const file = Gio.File.new_for_path(filePath);
|
|
||||||
const [success, contents] = file.load_contents(null);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
logError('Failed to load the XML file.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the XML content and set the Style Scheme
|
|
||||||
const schemeManager = GtkSource.StyleSchemeManager.get_default();
|
|
||||||
schemeManager.append_search_path(file.get_parent().get_path());
|
|
||||||
}
|
|
||||||
loadCustomColorScheme(CUSTOM_SOURCEVIEW_SCHEME_PATH);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
function substituteLang(str) {
|
|
||||||
const subs = [
|
|
||||||
{ from: 'javascript', to: 'js' },
|
|
||||||
{ from: 'bash', to: 'sh' },
|
|
||||||
];
|
|
||||||
for (const { from, to } of subs) {
|
|
||||||
if (from === str) return to;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HighlightedCode = (content, lang) => {
|
|
||||||
const buffer = new GtkSource.Buffer();
|
|
||||||
const sourceView = new GtkSource.View({
|
|
||||||
buffer: buffer,
|
|
||||||
wrap_mode: Gtk.WrapMode.NONE
|
|
||||||
});
|
|
||||||
const langManager = GtkSource.LanguageManager.get_default();
|
|
||||||
let displayLang = langManager.get_language(substituteLang(lang)); // Set your preferred language
|
|
||||||
if (displayLang) {
|
|
||||||
buffer.set_language(displayLang);
|
|
||||||
}
|
|
||||||
const schemeManager = GtkSource.StyleSchemeManager.get_default();
|
|
||||||
buffer.set_style_scheme(schemeManager.get_scheme(CUSTOM_SCHEME_ID));
|
|
||||||
buffer.set_text(content, -1);
|
|
||||||
return sourceView;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TextBlock = (content = '') => Label({
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
|
|
||||||
useMarkup: true,
|
|
||||||
xalign: 0,
|
|
||||||
wrap: true,
|
|
||||||
selectable: true,
|
|
||||||
label: content,
|
|
||||||
});
|
|
||||||
|
|
||||||
Utils.execAsync(['bash', '-c', `rm -rf ${LATEX_DIR}`])
|
|
||||||
.then(() => Utils.execAsync(['bash', '-c', `mkdir -p ${LATEX_DIR}`]))
|
|
||||||
.catch(print);
|
|
||||||
const Latex = (content = '') => {
|
|
||||||
const latexViewArea = Box({
|
|
||||||
// vscroll: 'never',
|
|
||||||
// hscroll: 'automatic',
|
|
||||||
// homogeneous: true,
|
|
||||||
attribute: {
|
|
||||||
render: async (self, text) => {
|
|
||||||
if (text.length == 0) return;
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
const fontSize = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
const timeSinceEpoch = Date.now();
|
|
||||||
const fileName = `${timeSinceEpoch}.tex`;
|
|
||||||
const outFileName = `${timeSinceEpoch}-symbolic.svg`;
|
|
||||||
const outIconName = `${timeSinceEpoch}-symbolic`;
|
|
||||||
const scriptFileName = `${timeSinceEpoch}-render.sh`;
|
|
||||||
const filePath = `${LATEX_DIR}/${fileName}`;
|
|
||||||
const outFilePath = `${LATEX_DIR}/${outFileName}`;
|
|
||||||
const scriptFilePath = `${LATEX_DIR}/${scriptFileName}`;
|
|
||||||
|
|
||||||
Utils.writeFile(text, filePath).catch(print);
|
|
||||||
// Since MicroTex doesn't support file path input properly, we gotta cat it
|
|
||||||
// And escaping such a command is a fucking pain so I decided to just generate a script
|
|
||||||
// Note: MicroTex doesn't support `&=`
|
|
||||||
// You can add this line in the middle for debugging: echo "$text" > ${filePath}.tmp
|
|
||||||
const renderScript = `#!/usr/bin/env bash
|
|
||||||
text=$(cat ${filePath} | sed 's/$/ \\\\\\\\/g' | sed 's/&=/=/g')
|
|
||||||
cd /opt/MicroTeX
|
|
||||||
./LaTeX -headless -input="$text" -output=${outFilePath} -textsize=${fontSize * 1.1} -padding=0 -maxwidth=${latexViewArea.get_allocated_width() * 0.85} > /dev/null 2>&1
|
|
||||||
sed -i 's/fill="rgb(0%, 0%, 0%)"/style="fill:#000000"/g' ${outFilePath}
|
|
||||||
sed -i 's/stroke="rgb(0%, 0%, 0%)"/stroke="${darkMode.value ? '#ffffff' : '#000000'}"/g' ${outFilePath}
|
|
||||||
`;
|
|
||||||
Utils.writeFile(renderScript, scriptFilePath).catch(print);
|
|
||||||
Utils.exec(`chmod a+x ${scriptFilePath}`)
|
|
||||||
Utils.timeout(100, () => {
|
|
||||||
Utils.exec(`bash ${scriptFilePath}`);
|
|
||||||
Gtk.IconTheme.get_default().append_search_path(LATEX_DIR);
|
|
||||||
|
|
||||||
self.child?.destroy();
|
|
||||||
self.child = Gtk.Image.new_from_icon_name(outIconName, 0);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup: (self) => self.attribute.render(self, content).catch(print),
|
|
||||||
});
|
|
||||||
const wholeThing = Box({
|
|
||||||
className: 'sidebar-chat-latex',
|
|
||||||
homogeneous: true,
|
|
||||||
attribute: {
|
|
||||||
'updateText': (text) => {
|
|
||||||
latexViewArea.attribute.render(latexViewArea, text).catch(print);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
children: [Scrollable({
|
|
||||||
vscroll: 'never',
|
|
||||||
hscroll: 'automatic',
|
|
||||||
child: latexViewArea
|
|
||||||
})]
|
|
||||||
})
|
|
||||||
return wholeThing;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CodeBlock = (content = '', lang = 'txt') => {
|
|
||||||
if (lang == 'tex' || lang == 'latex') {
|
|
||||||
return Latex(content);
|
|
||||||
}
|
|
||||||
const topBar = Box({
|
|
||||||
className: 'sidebar-chat-codeblock-topbar',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
label: lang,
|
|
||||||
className: 'sidebar-chat-codeblock-topbar-txt',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
hexpand: true,
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'sidebar-chat-codeblock-topbar-btn',
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
MaterialIcon('content_copy', 'small'),
|
|
||||||
Label({
|
|
||||||
label: 'Copy',
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onClicked: (self) => {
|
|
||||||
const buffer = sourceView.get_buffer();
|
|
||||||
const copyContent = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), false); // TODO: fix this
|
|
||||||
execAsync([`wl-copy`, `${copyContent}`]).catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
// Source view
|
|
||||||
const sourceView = HighlightedCode(content, lang);
|
|
||||||
|
|
||||||
const codeBlock = Box({
|
|
||||||
attribute: {
|
|
||||||
'updateText': (text) => {
|
|
||||||
sourceView.get_buffer().set_text(text, -1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
className: 'sidebar-chat-codeblock',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
topBar,
|
|
||||||
Box({
|
|
||||||
className: 'sidebar-chat-codeblock-code',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Scrollable({
|
|
||||||
vscroll: 'never',
|
|
||||||
hscroll: 'automatic',
|
|
||||||
child: sourceView,
|
|
||||||
})],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// const schemeIds = styleManager.get_scheme_ids();
|
|
||||||
|
|
||||||
// print("Available Style Schemes:");
|
|
||||||
// for (let i = 0; i < schemeIds.length; i++) {
|
|
||||||
// print(schemeIds[i]);
|
|
||||||
// }
|
|
||||||
return codeBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Divider = () => Box({
|
|
||||||
className: 'sidebar-chat-divider',
|
|
||||||
})
|
|
||||||
|
|
||||||
const MessageContent = (content) => {
|
|
||||||
const contentBox = Box({
|
|
||||||
vertical: true,
|
|
||||||
attribute: {
|
|
||||||
'fullUpdate': (self, content, useCursor = false) => {
|
|
||||||
// Clear and add first text widget
|
|
||||||
const children = contentBox.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i];
|
|
||||||
child.destroy();
|
|
||||||
}
|
|
||||||
contentBox.add(TextBlock())
|
|
||||||
// Loop lines. Put normal text in markdown parser
|
|
||||||
// and put code into code highlighter (TODO)
|
|
||||||
let lines = content.split('\n');
|
|
||||||
let lastProcessed = 0;
|
|
||||||
let inCode = false;
|
|
||||||
for (const [index, line] of lines.entries()) {
|
|
||||||
// Code blocks
|
|
||||||
const codeBlockRegex = /^\s*```([a-zA-Z0-9]+)?\n?/;
|
|
||||||
if (codeBlockRegex.test(line)) {
|
|
||||||
const kids = self.get_children();
|
|
||||||
const lastLabel = kids[kids.length - 1];
|
|
||||||
const blockContent = lines.slice(lastProcessed, index).join('\n');
|
|
||||||
if (!inCode) {
|
|
||||||
lastLabel.label = md2pango(blockContent);
|
|
||||||
contentBox.add(CodeBlock('', codeBlockRegex.exec(line)[1]));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lastLabel.attribute.updateText(blockContent);
|
|
||||||
contentBox.add(TextBlock());
|
|
||||||
}
|
|
||||||
|
|
||||||
lastProcessed = index + 1;
|
|
||||||
inCode = !inCode;
|
|
||||||
}
|
|
||||||
// Breaks
|
|
||||||
const dividerRegex = /^\s*---/;
|
|
||||||
if (!inCode && dividerRegex.test(line)) {
|
|
||||||
const kids = self.get_children();
|
|
||||||
const lastLabel = kids[kids.length - 1];
|
|
||||||
const blockContent = lines.slice(lastProcessed, index).join('\n');
|
|
||||||
lastLabel.label = md2pango(blockContent);
|
|
||||||
contentBox.add(Divider());
|
|
||||||
contentBox.add(TextBlock());
|
|
||||||
lastProcessed = index + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lastProcessed < lines.length) {
|
|
||||||
const kids = self.get_children();
|
|
||||||
const lastLabel = kids[kids.length - 1];
|
|
||||||
let blockContent = lines.slice(lastProcessed, lines.length).join('\n');
|
|
||||||
if (!inCode)
|
|
||||||
lastLabel.label = `${md2pango(blockContent)}${useCursor ? userOptions.ai.writingCursor : ''}`;
|
|
||||||
else
|
|
||||||
lastLabel.attribute.updateText(blockContent);
|
|
||||||
}
|
|
||||||
// Debug: plain text
|
|
||||||
// contentBox.add(Label({
|
|
||||||
// hpack: 'fill',
|
|
||||||
// className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
|
|
||||||
// useMarkup: false,
|
|
||||||
// xalign: 0,
|
|
||||||
// wrap: true,
|
|
||||||
// selectable: true,
|
|
||||||
// label: '------------------------------\n' + md2pango(content),
|
|
||||||
// }))
|
|
||||||
contentBox.show_all();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
contentBox.attribute.fullUpdate(contentBox, content, false);
|
|
||||||
return contentBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChatMessage = (message, modelName = 'Model') => {
|
|
||||||
const TextSkeleton = (extraClassName = '') => Box({
|
|
||||||
className: `sidebar-chat-message-skeletonline ${extraClassName}`,
|
|
||||||
})
|
|
||||||
const messageContentBox = MessageContent(message.content);
|
|
||||||
const messageLoadingSkeleton = Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
children: Array.from({ length: 3 }, (_, id) => TextSkeleton(`sidebar-chat-message-skeletonline-offset${id}`)),
|
|
||||||
})
|
|
||||||
const messageArea = Stack({
|
|
||||||
homogeneous: message.role !== 'user',
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
children: {
|
|
||||||
'thinking': messageLoadingSkeleton,
|
|
||||||
'message': messageContentBox,
|
|
||||||
},
|
|
||||||
shown: message.thinking ? 'thinking' : 'message',
|
|
||||||
});
|
|
||||||
const thisMessage = Box({
|
|
||||||
className: 'sidebar-chat-message',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
hpack: 'start',
|
|
||||||
xalign: 0,
|
|
||||||
className: `txt txt-bold sidebar-chat-name sidebar-chat-name-${message.role == 'user' ? 'user' : 'bot'}`,
|
|
||||||
wrap: true,
|
|
||||||
useMarkup: true,
|
|
||||||
label: (message.role == 'user' ? USERNAME : modelName),
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'sidebar-chat-messagearea',
|
|
||||||
children: [messageArea]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(message, (self, isThinking) => {
|
|
||||||
messageArea.shown = message.thinking ? 'thinking' : 'message';
|
|
||||||
}, 'notify::thinking')
|
|
||||||
.hook(message, (self) => { // Message update
|
|
||||||
messageContentBox.attribute.fullUpdate(messageContentBox, message.content, message.role != 'user');
|
|
||||||
}, 'notify::content')
|
|
||||||
.hook(message, (label, isDone) => { // Remove the cursor
|
|
||||||
messageContentBox.attribute.fullUpdate(messageContentBox, message.content, false);
|
|
||||||
}, 'notify::done')
|
|
||||||
,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return thisMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SystemMessage = (content, commandName, scrolledWindow) => {
|
|
||||||
const messageContentBox = MessageContent(content);
|
|
||||||
const thisMessage = Box({
|
|
||||||
className: 'sidebar-chat-message',
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
xalign: 0,
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'txt txt-bold sidebar-chat-name sidebar-chat-name-system',
|
|
||||||
wrap: true,
|
|
||||||
label: `System • ${commandName}`,
|
|
||||||
}),
|
|
||||||
messageContentBox,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return thisMessage;
|
|
||||||
}
|
|
|
@ -1,523 +0,0 @@
|
||||||
const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Button, EventBox, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { fileExists } from '../../.miscutils/files.js';
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
|
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
|
||||||
import BooruService from '../../../services/booru.js';
|
|
||||||
import { chatEntry } from '../apiwidgets.js';
|
|
||||||
import { ConfigToggle } from '../../.commonwidgets/configwidgets.js';
|
|
||||||
import { SystemMessage } from './ai_chatmessage.js';
|
|
||||||
|
|
||||||
const IMAGE_REVEAL_DELAY = 13; // Some wait for inits n other weird stuff
|
|
||||||
const USER_CACHE_DIR = GLib.get_user_cache_dir();
|
|
||||||
|
|
||||||
// Create cache folder and clear pics from previous session
|
|
||||||
Utils.exec(`bash -c 'mkdir -p ${USER_CACHE_DIR}/ags/media/waifus'`);
|
|
||||||
Utils.exec(`bash -c 'rm ${USER_CACHE_DIR}/ags/media/waifus/*'`);
|
|
||||||
|
|
||||||
const TagButton = (command) => Button({
|
|
||||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
|
||||||
onClicked: () => { chatEntry.buffer.text += `${command} ` },
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: command,
|
|
||||||
});
|
|
||||||
|
|
||||||
const CommandButton = (command, displayName = command) => Button({
|
|
||||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
|
||||||
onClicked: () => sendMessage(command),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: displayName,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const booruTabIcon = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
homogeneous: true,
|
|
||||||
children: [
|
|
||||||
MaterialIcon('gallery_thumbnail', 'norm'),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const BooruInfo = () => {
|
|
||||||
const booruLogo = Label({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'sidebar-chat-welcome-logo',
|
|
||||||
label: 'gallery_thumbnail',
|
|
||||||
})
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
vexpand: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
booruLogo,
|
|
||||||
Label({
|
|
||||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Anime booru',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'txt-smallie txt-subtext',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Powered by yande.re and konachan',
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'txt-subtext txt-norm icon-material',
|
|
||||||
label: 'info',
|
|
||||||
tooltipText: 'An image booru. May contain NSFW content.\nWatch your back.\n\nDisclaimer: Not affiliated with the provider\nnor responsible for any of its content.',
|
|
||||||
setup: setupCursorHoverInfo,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BooruSettings = () => MarginRevealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
revealChild: true,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'sidebar-chat-settings',
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'sidebar-chat-settings-toggles',
|
|
||||||
children: [
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'menstrual_health',
|
|
||||||
name: 'Lewds',
|
|
||||||
desc: `Shows naughty stuff when enabled.\nYa like those? Add this to user_options.js:
|
|
||||||
'sidebar': {
|
|
||||||
'image': {
|
|
||||||
'allowNsfw': true,
|
|
||||||
}
|
|
||||||
},`,
|
|
||||||
initValue: BooruService.nsfw,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
BooruService.nsfw = newValue;
|
|
||||||
},
|
|
||||||
extraSetup: (self) => self.hook(BooruService, (self) => {
|
|
||||||
self.attribute.enabled.value = BooruService.nsfw;
|
|
||||||
}, 'notify::nsfw')
|
|
||||||
}),
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'sell',
|
|
||||||
name: 'Save in folder by tags',
|
|
||||||
desc: 'Saves images in folders by their tags',
|
|
||||||
initValue: userOptions.sidebar.image.saveInFolderByTags,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
userOptions.sidebar.image.saveInFolderByTags = newValue;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const booruWelcome = Box({
|
|
||||||
vexpand: true,
|
|
||||||
homogeneous: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
BooruInfo(),
|
|
||||||
BooruSettings(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const BooruPage = (taglist, serviceName = 'Booru') => {
|
|
||||||
const PageState = (icon, name) => Box({
|
|
||||||
className: 'spacing-h-5 txt',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'sidebar-waifu-txt txt-smallie',
|
|
||||||
xalign: 0,
|
|
||||||
label: name,
|
|
||||||
}),
|
|
||||||
MaterialIcon(icon, 'norm'),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const ImageAction = ({ name, icon, action }) => Button({
|
|
||||||
className: 'sidebar-waifu-image-action txt-norm icon-material',
|
|
||||||
tooltipText: name,
|
|
||||||
label: icon,
|
|
||||||
onClicked: action,
|
|
||||||
setup: setupCursorHover,
|
|
||||||
})
|
|
||||||
const PreviewImage = (data, delay = 0) => {
|
|
||||||
const imageArea = Widget.DrawingArea({
|
|
||||||
className: 'sidebar-booru-image-drawingarea',
|
|
||||||
});
|
|
||||||
const imageBox = Box({
|
|
||||||
className: 'sidebar-booru-image',
|
|
||||||
// css: `background-image: url('${data.preview_url}');`,
|
|
||||||
attribute: {
|
|
||||||
'update': (self, data, force = false) => {
|
|
||||||
const imagePath = `${USER_CACHE_DIR}/ags/media/waifus/${data.md5}.${data.file_ext}`;
|
|
||||||
const widgetStyleContext = imageArea.get_style_context();
|
|
||||||
const widgetWidth = widgetStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
|
||||||
const widgetHeight = widgetWidth / data.aspect_ratio;
|
|
||||||
imageArea.set_size_request(widgetWidth, widgetHeight);
|
|
||||||
const showImage = () => {
|
|
||||||
// const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(imagePath, widgetWidth, widgetHeight);
|
|
||||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(imagePath, widgetWidth, widgetHeight, false);
|
|
||||||
imageArea.connect("draw", (widget, cr) => {
|
|
||||||
const borderRadius = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
// Draw a rounded rectangle
|
|
||||||
cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
|
|
||||||
cr.arc(widgetWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
|
|
||||||
cr.arc(widgetWidth - borderRadius, widgetHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
|
|
||||||
cr.arc(borderRadius, widgetHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
|
|
||||||
cr.closePath();
|
|
||||||
cr.clip();
|
|
||||||
|
|
||||||
// Paint image as bg
|
|
||||||
Gdk.cairo_set_source_pixbuf(cr, pixbuf, (widgetWidth - widgetWidth) / 2, (widgetHeight - widgetHeight) / 2);
|
|
||||||
cr.paint();
|
|
||||||
});
|
|
||||||
self.queue_draw();
|
|
||||||
imageRevealer.revealChild = true;
|
|
||||||
}
|
|
||||||
// Show
|
|
||||||
// const downloadCommand = `wget -O '${imagePath}' '${data.preview_url}'`;
|
|
||||||
const downloadCommand = `curl -L -o '${imagePath}' '${data.preview_url}'`;
|
|
||||||
if (!force && fileExists(imagePath)) showImage();
|
|
||||||
else Utils.timeout(delay, () => Utils.execAsync(['bash', '-c', downloadCommand])
|
|
||||||
.then(showImage)
|
|
||||||
.catch(print)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
child: imageArea,
|
|
||||||
setup: (self) => {
|
|
||||||
Utils.timeout(1000, () => self.attribute.update(self, data));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const imageActions = Revealer({
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
vpack: 'start',
|
|
||||||
className: 'sidebar-booru-image-actions spacing-h-3',
|
|
||||||
children: [
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Go to file url',
|
|
||||||
icon: 'file_open',
|
|
||||||
action: () => execAsync(['xdg-open', `${data.file_url}`]).catch(print),
|
|
||||||
}),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Go to source',
|
|
||||||
icon: 'open_in_new',
|
|
||||||
action: () => execAsync(['xdg-open', `${data.source}`]).catch(print),
|
|
||||||
}),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Save image',
|
|
||||||
icon: 'save',
|
|
||||||
action: (self) => {
|
|
||||||
const currentTags = BooruService.queries.at(-1).realTagList.filter(tag => !tag.includes('rating:'));
|
|
||||||
const tagDirectory = currentTags.join('+');
|
|
||||||
let fileExtension = data.file_ext || 'jpg';
|
|
||||||
const saveCommand = `mkdir -p $(xdg-user-dir PICTURES)/homework/${data.is_nsfw ? '🌶️/' : ''}${userOptions.sidebar.image.saveInFolderByTags ? tagDirectory : ''} && curl -L -o $(xdg-user-dir PICTURES)/homework/${data.is_nsfw ? '🌶️/' : ''}${userOptions.sidebar.image.saveInFolderByTags ? (tagDirectory + '/') : ''}${data.md5}.${fileExtension} '${data.file_url}'`;
|
|
||||||
execAsync(['bash', '-c', saveCommand])
|
|
||||||
.then(() => self.label = 'done')
|
|
||||||
.catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const imageOverlay = Overlay({
|
|
||||||
passThrough: true,
|
|
||||||
child: imageBox,
|
|
||||||
overlays: [imageActions]
|
|
||||||
});
|
|
||||||
const imageRevealer = Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: EventBox({
|
|
||||||
onHover: () => { imageActions.revealChild = true },
|
|
||||||
onHoverLost: () => { imageActions.revealChild = false },
|
|
||||||
child: imageOverlay,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return imageRevealer;
|
|
||||||
}
|
|
||||||
const downloadState = Stack({
|
|
||||||
homogeneous: false,
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'api': PageState('api', 'Calling API'),
|
|
||||||
'download': PageState('downloading', 'Downloading image'),
|
|
||||||
'done': PageState('done', 'Finished!'),
|
|
||||||
'error': PageState('error', 'Error'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const downloadIndicator = MarginRevealer({
|
|
||||||
vpack: 'center',
|
|
||||||
transition: 'slide_left',
|
|
||||||
revealChild: true,
|
|
||||||
child: downloadState,
|
|
||||||
});
|
|
||||||
const pageHeading = Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
hpack: 'start',
|
|
||||||
className: `sidebar-booru-provider`,
|
|
||||||
label: `${serviceName}`,
|
|
||||||
truncate: 'end',
|
|
||||||
maxWidthChars: 20,
|
|
||||||
}),
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
downloadIndicator,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
children: [
|
|
||||||
Scrollable({
|
|
||||||
hexpand: true,
|
|
||||||
vscroll: 'never',
|
|
||||||
hscroll: 'automatic',
|
|
||||||
child: Box({
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
...taglist.map((tag) => TagButton(tag)),
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const pageImages = Box({
|
|
||||||
hpack: 'start',
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'sidebar-booru-imagegrid',
|
|
||||||
})
|
|
||||||
const pageImageRevealer = Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: false,
|
|
||||||
child: pageImages,
|
|
||||||
});
|
|
||||||
const thisPage = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'sidebar-chat-message',
|
|
||||||
attribute: {
|
|
||||||
'imagePath': '',
|
|
||||||
'isNsfw': false,
|
|
||||||
'update': (data, force = false) => { // TODO: Use columns. Sort min to max h/w ratio then greedily put em in...
|
|
||||||
// Sort by .aspect_ratio
|
|
||||||
data = data.sort(
|
|
||||||
(a, b) => a.aspect_ratio - b.aspect_ratio
|
|
||||||
);
|
|
||||||
if (data.length == 0) {
|
|
||||||
downloadState.shown = 'error';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const imageColumns = userOptions.sidebar.image.columns;
|
|
||||||
const imageRows = data.length / imageColumns;
|
|
||||||
|
|
||||||
// Init cols
|
|
||||||
pageImages.children = Array.from(
|
|
||||||
{ length: imageColumns },
|
|
||||||
(_, i) => Box({
|
|
||||||
attribute: { height: 0 },
|
|
||||||
vertical: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// Greedy add O(n^2) 😭
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
// Find column with lowest length
|
|
||||||
let minHeight = Infinity;
|
|
||||||
let minIndex = -1;
|
|
||||||
for (let j = 0; j < imageColumns; j++) {
|
|
||||||
const height = pageImages.children[j].attribute.height;
|
|
||||||
if (height < minHeight) {
|
|
||||||
minHeight = height;
|
|
||||||
minIndex = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add image to it
|
|
||||||
pageImages.children[minIndex].pack_start(PreviewImage(data[i], minIndex), false, false, 0)
|
|
||||||
pageImages.children[minIndex].attribute.height += 1 / data[i].aspect_ratio; // we want height/width
|
|
||||||
}
|
|
||||||
pageImages.show_all();
|
|
||||||
|
|
||||||
// Reveal stuff
|
|
||||||
Utils.timeout(IMAGE_REVEAL_DELAY,
|
|
||||||
() => pageImageRevealer.revealChild = true
|
|
||||||
);
|
|
||||||
downloadIndicator.attribute.hide();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
pageHeading,
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [pageImageRevealer],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})],
|
|
||||||
});
|
|
||||||
return thisPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
const booruContent = Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vertical: true,
|
|
||||||
attribute: {
|
|
||||||
'map': new Map(),
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(BooruService, (box, id) => {
|
|
||||||
if (id === undefined) return;
|
|
||||||
const newPage = BooruPage(BooruService.queries[id].taglist, BooruService.queries[id].providerName);
|
|
||||||
box.add(newPage);
|
|
||||||
box.show_all();
|
|
||||||
box.attribute.map.set(id, newPage);
|
|
||||||
}, 'newResponse')
|
|
||||||
.hook(BooruService, (box, id) => {
|
|
||||||
if (id === undefined) return;
|
|
||||||
if (!BooruService.responses[id]) return;
|
|
||||||
box.attribute.map.get(id)?.attribute.update(BooruService.responses[id]);
|
|
||||||
}, 'updateResponse')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const booruView = Scrollable({
|
|
||||||
className: 'sidebar-chat-viewport',
|
|
||||||
vexpand: true,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
booruWelcome,
|
|
||||||
booruContent,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (scrolledWindow) => {
|
|
||||||
// Show scrollbar
|
|
||||||
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
const vScrollbar = scrolledWindow.get_vscrollbar();
|
|
||||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
|
||||||
// Avoid click-to-scroll-widget-to-view behavior
|
|
||||||
Utils.timeout(1, () => {
|
|
||||||
const viewport = scrolledWindow.child;
|
|
||||||
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
|
|
||||||
})
|
|
||||||
// Scroll to bottom with new content if chat entry not focused
|
|
||||||
const adjustment = scrolledWindow.get_vadjustment();
|
|
||||||
adjustment.connect("changed", () => {
|
|
||||||
if (!chatEntry.hasFocus) return;
|
|
||||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const booruTags = Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Scrollable({
|
|
||||||
vscroll: 'never',
|
|
||||||
hscroll: 'automatic',
|
|
||||||
hexpand: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
TagButton('( * )'),
|
|
||||||
TagButton('hololive'),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
Box({ className: 'separator-line' }),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const booruCommands = Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
setup: (self) => {
|
|
||||||
self.pack_end(CommandButton('/clear'), false, false, 0);
|
|
||||||
self.pack_end(CommandButton('/next'), false, false, 0);
|
|
||||||
self.pack_start(Button({
|
|
||||||
className: 'sidebar-chat-chip-toggle',
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: 'Tags →',
|
|
||||||
onClicked: () => {
|
|
||||||
booruTags.revealChild = !booruTags.revealChild;
|
|
||||||
}
|
|
||||||
}), false, false, 0);
|
|
||||||
self.pack_start(booruTags, true, true, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearChat = () => { // destroy!!
|
|
||||||
booruContent.attribute.map.forEach((value, key, map) => {
|
|
||||||
value.destroy();
|
|
||||||
value = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendMessage = (text) => {
|
|
||||||
// Commands
|
|
||||||
if (text.startsWith('+')) { // Next page
|
|
||||||
const lastQuery = BooruService.queries.at(-1);
|
|
||||||
BooruService.fetch(`${lastQuery.realTagList.join(' ')} ${lastQuery.page + 1}`)
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/')) {
|
|
||||||
if (text.startsWith('/clear')) clearChat();
|
|
||||||
else if (text.startsWith('/safe')) {
|
|
||||||
BooruService.nsfw = false;
|
|
||||||
const message = SystemMessage(`Switched to safe mode`, '/safe', booruView)
|
|
||||||
booruContent.add(message);
|
|
||||||
booruContent.show_all();
|
|
||||||
booruContent.attribute.map.set(Date.now(), message);
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/lewd')) {
|
|
||||||
BooruService.nsfw = true;
|
|
||||||
const message = SystemMessage(`Tiddies enabled`, '/lewd', booruView)
|
|
||||||
booruContent.add(message);
|
|
||||||
booruContent.show_all();
|
|
||||||
booruContent.attribute.map.set(Date.now(), message);
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/mode')) {
|
|
||||||
const mode = text.slice(text.indexOf(' ') + 1);
|
|
||||||
BooruService.mode = mode;
|
|
||||||
const message = SystemMessage(`Changed provider to ${BooruService.providerName}`, '/mode', booruView)
|
|
||||||
booruContent.add(message);
|
|
||||||
booruContent.show_all();
|
|
||||||
booruContent.attribute.map.set(Date.now(), message);
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/next')) {
|
|
||||||
sendMessage('+')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else BooruService.fetch(text);
|
|
||||||
}
|
|
|
@ -1,355 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
const { Box, Button, Icon, Label, Revealer, Scrollable } = Widget;
|
|
||||||
import GPTService from '../../../services/gpt.js';
|
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
|
||||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
|
||||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../.commonwidgets/configwidgets.js';
|
|
||||||
import { markdownTest } from '../../.miscutils/md2pango.js';
|
|
||||||
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import { chatEntry } from '../apiwidgets.js';
|
|
||||||
|
|
||||||
export const chatGPTTabIcon = Icon({
|
|
||||||
hpack: 'center',
|
|
||||||
icon: `ollama-symbolic`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ProviderSwitcher = () => {
|
|
||||||
const ProviderChoice = (id, provider) => {
|
|
||||||
const providerSelected = MaterialIcon('check', 'norm', {
|
|
||||||
setup: (self) => self.hook(GPTService, (self) => {
|
|
||||||
self.toggleClassName('invisible', GPTService.providerID !== id);
|
|
||||||
}, 'providerChanged')
|
|
||||||
});
|
|
||||||
return Button({
|
|
||||||
tooltipText: provider.description,
|
|
||||||
onClicked: () => {
|
|
||||||
GPTService.providerID = id;
|
|
||||||
providerList.revealChild = false;
|
|
||||||
indicatorChevron.label = 'expand_more';
|
|
||||||
},
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-10 txt',
|
|
||||||
children: [
|
|
||||||
Icon({
|
|
||||||
icon: provider['logo_name'],
|
|
||||||
className: 'txt-large'
|
|
||||||
}),
|
|
||||||
Label({
|
|
||||||
hexpand: true,
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-small',
|
|
||||||
label: provider.name,
|
|
||||||
}),
|
|
||||||
providerSelected
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const indicatorChevron = MaterialIcon('expand_more', 'norm');
|
|
||||||
const indicatorButton = Button({
|
|
||||||
tooltipText: 'Select ChatGPT-compatible API provider',
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-10 txt',
|
|
||||||
children: [
|
|
||||||
MaterialIcon('cloud', 'norm'),
|
|
||||||
Label({
|
|
||||||
hexpand: true,
|
|
||||||
xalign: 0,
|
|
||||||
className: 'txt-small',
|
|
||||||
label: GPTService.providerID,
|
|
||||||
setup: (self) => self.hook(GPTService, (self) => {
|
|
||||||
self.label = `${GPTService.providers[GPTService.providerID]['name']}`;
|
|
||||||
}, 'providerChanged')
|
|
||||||
}),
|
|
||||||
indicatorChevron,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onClicked: () => {
|
|
||||||
providerList.revealChild = !providerList.revealChild;
|
|
||||||
indicatorChevron.label = (providerList.revealChild ? 'expand_less' : 'expand_more');
|
|
||||||
},
|
|
||||||
setup: setupCursorHover,
|
|
||||||
});
|
|
||||||
const providerList = Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
vertical: true, className: 'spacing-v-5 sidebar-chat-providerswitcher-list',
|
|
||||||
children: [
|
|
||||||
Box({ className: 'separator-line margin-top-5 margin-bottom-5' }),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
vertical: true,
|
|
||||||
setup: (self) => self.hook(GPTService, (self) => {
|
|
||||||
self.children = Object.entries(GPTService.providers)
|
|
||||||
.map(([id, provider]) => ProviderChoice(id, provider));
|
|
||||||
}, 'initialized'),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return Box({
|
|
||||||
hpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
className: 'sidebar-chat-providerswitcher',
|
|
||||||
children: [
|
|
||||||
indicatorButton,
|
|
||||||
providerList,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const GPTInfo = () => {
|
|
||||||
const openAiLogo = Icon({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'sidebar-chat-welcome-logo',
|
|
||||||
icon: `ollama-symbolic`,
|
|
||||||
});
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
openAiLogo,
|
|
||||||
Label({
|
|
||||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Assistant',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'txt-smallie txt-subtext',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Provider shown above',
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'txt-subtext txt-norm icon-material',
|
|
||||||
label: 'info',
|
|
||||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.\n\nPrivacy: OpenAI claims they do not use your data\nwhen you use their API. Idk about others.',
|
|
||||||
setup: setupCursorHoverInfo,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const GPTSettings = () => MarginRevealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
revealChild: true,
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(GPTService, (self) => Utils.timeout(200, () => {
|
|
||||||
self.attribute.hide();
|
|
||||||
}), 'newMsg')
|
|
||||||
.hook(GPTService, (self) => Utils.timeout(200, () => {
|
|
||||||
self.attribute.show();
|
|
||||||
}), 'clear')
|
|
||||||
,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'sidebar-chat-settings',
|
|
||||||
children: [
|
|
||||||
ConfigSegmentedSelection({
|
|
||||||
hpack: 'center',
|
|
||||||
icon: 'casino',
|
|
||||||
name: 'Randomness',
|
|
||||||
desc: 'The model\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1',
|
|
||||||
options: [
|
|
||||||
{ value: 0.00, name: 'Precise', },
|
|
||||||
{ value: 0.50, name: 'Balanced', },
|
|
||||||
{ value: 1.00, name: 'Creative', },
|
|
||||||
],
|
|
||||||
initIndex: 2,
|
|
||||||
onChange: (value, name) => {
|
|
||||||
GPTService.temperature = value;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'sidebar-chat-settings-toggles',
|
|
||||||
children: [
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'model_training',
|
|
||||||
name: 'Enhancements',
|
|
||||||
desc: 'Tells the model:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
|
||||||
initValue: GPTService.assistantPrompt,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
GPTService.assistantPrompt = newValue;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const OpenaiApiKeyInstructions = () => Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(GPTService, (self, hasKey) => {
|
|
||||||
self.revealChild = (GPTService.key.length == 0);
|
|
||||||
}, 'hasKey')
|
|
||||||
,
|
|
||||||
child: Button({
|
|
||||||
child: Label({
|
|
||||||
useMarkup: true,
|
|
||||||
wrap: true,
|
|
||||||
className: 'txt sidebar-chat-welcome-txt',
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'An API key is required\nYou can grab one <u>here</u>, then enter it below'
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: () => {
|
|
||||||
Utils.execAsync(['bash', '-c', `xdg-open ${GPTService.getKeyUrl}`]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
|
|
||||||
const GPTWelcome = () => Box({
|
|
||||||
vexpand: true,
|
|
||||||
homogeneous: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
GPTInfo(),
|
|
||||||
OpenaiApiKeyInstructions(),
|
|
||||||
GPTSettings(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const chatContent = Box({
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
vertical: true,
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(GPTService, (box, id) => {
|
|
||||||
const message = GPTService.messages[id];
|
|
||||||
if (!message) return;
|
|
||||||
box.add(ChatMessage(message, `Model (${GPTService.providers[GPTService.providerID]['name']})`))
|
|
||||||
}, 'newMsg')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearChat = () => {
|
|
||||||
GPTService.clear();
|
|
||||||
const children = chatContent.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i];
|
|
||||||
child.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CommandButton = (command) => Button({
|
|
||||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
|
||||||
onClicked: () => sendMessage(command),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: command,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const chatGPTCommands = Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
CommandButton('/key'),
|
|
||||||
CommandButton('/model'),
|
|
||||||
CommandButton('/clear'),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
export const sendMessage = (text) => {
|
|
||||||
// Check if text or API key is empty
|
|
||||||
if (text.length == 0) return;
|
|
||||||
if (GPTService.key.length == 0) {
|
|
||||||
GPTService.key = text;
|
|
||||||
chatContent.add(SystemMessage(`Key saved to\n\`${GPTService.keyPath}\``, 'API Key', chatGPTView));
|
|
||||||
text = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Commands
|
|
||||||
if (text.startsWith('/')) {
|
|
||||||
if (text.startsWith('/clear')) clearChat();
|
|
||||||
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${GPTService.modelName}\``, '/model', chatGPTView))
|
|
||||||
else if (text.startsWith('/prompt')) {
|
|
||||||
const firstSpaceIndex = text.indexOf(' ');
|
|
||||||
const prompt = text.slice(firstSpaceIndex + 1);
|
|
||||||
if (firstSpaceIndex == -1 || prompt.length < 1) {
|
|
||||||
chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', chatGPTView))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GPTService.addMessage('user', prompt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/key')) {
|
|
||||||
const parts = text.split(' ');
|
|
||||||
if (parts.length == 1) chatContent.add(SystemMessage(
|
|
||||||
`Key stored in:\n\`${GPTService.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
|
|
||||||
'/key',
|
|
||||||
chatGPTView));
|
|
||||||
else {
|
|
||||||
GPTService.key = parts[1];
|
|
||||||
chatContent.add(SystemMessage(`Updated API Key at\n\`${GPTService.keyPath}\``, '/key', chatGPTView));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/test'))
|
|
||||||
chatContent.add(SystemMessage(markdownTest, `Markdown test`, chatGPTView));
|
|
||||||
else
|
|
||||||
chatContent.add(SystemMessage(`Invalid command.`, 'Error', chatGPTView))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GPTService.send(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const chatGPTView = Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
ProviderSwitcher(),
|
|
||||||
Scrollable({
|
|
||||||
className: 'sidebar-chat-viewport',
|
|
||||||
vexpand: true,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
GPTWelcome(),
|
|
||||||
chatContent,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (scrolledWindow) => {
|
|
||||||
// Show scrollbar
|
|
||||||
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
const vScrollbar = scrolledWindow.get_vscrollbar();
|
|
||||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
|
||||||
// Avoid click-to-scroll-widget-to-view behavior
|
|
||||||
Utils.timeout(1, () => {
|
|
||||||
const viewport = scrolledWindow.child;
|
|
||||||
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
|
|
||||||
})
|
|
||||||
// Always scroll to bottom with new content
|
|
||||||
const adjustment = scrolledWindow.get_vadjustment();
|
|
||||||
adjustment.connect("changed", () => {
|
|
||||||
if(!chatEntry.hasFocus) return;
|
|
||||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
|
@ -1,288 +0,0 @@
|
||||||
const { Gtk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
|
|
||||||
const { Box, Button, Icon, Label, Revealer, Scrollable } = Widget;
|
|
||||||
import GeminiService from '../../../services/gemini.js';
|
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
|
||||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
|
||||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../.commonwidgets/configwidgets.js';
|
|
||||||
import { markdownTest } from '../../.miscutils/md2pango.js';
|
|
||||||
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
|
|
||||||
import { chatEntry } from '../apiwidgets.js';
|
|
||||||
|
|
||||||
const MODEL_NAME = `Gemini`;
|
|
||||||
|
|
||||||
export const geminiTabIcon = Icon({
|
|
||||||
hpack: 'center',
|
|
||||||
icon: `google-gemini-symbolic`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const GeminiInfo = () => {
|
|
||||||
const geminiLogo = Icon({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'sidebar-chat-welcome-logo',
|
|
||||||
icon: `google-gemini-symbolic`,
|
|
||||||
});
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
geminiLogo,
|
|
||||||
Label({
|
|
||||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Assistant (Gemini)',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'txt-smallie txt-subtext',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Powered by Google',
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'txt-subtext txt-norm icon-material',
|
|
||||||
label: 'info',
|
|
||||||
tooltipText: 'Uses gemini-pro.\nNot affiliated, endorsed, or sponsored by Google.\n\nPrivacy: Chat messages aren\'t linked to your account,\n but will be read by human reviewers to improve the model.',
|
|
||||||
setup: setupCursorHoverInfo,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GeminiSettings = () => MarginRevealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
revealChild: true,
|
|
||||||
extraSetup: (self) => self
|
|
||||||
.hook(GeminiService, (self) => Utils.timeout(200, () => {
|
|
||||||
self.attribute.hide();
|
|
||||||
}), 'newMsg')
|
|
||||||
.hook(GeminiService, (self) => Utils.timeout(200, () => {
|
|
||||||
self.attribute.show();
|
|
||||||
}), 'clear')
|
|
||||||
,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'sidebar-chat-settings',
|
|
||||||
children: [
|
|
||||||
ConfigSegmentedSelection({
|
|
||||||
hpack: 'center',
|
|
||||||
icon: 'casino',
|
|
||||||
name: 'Randomness',
|
|
||||||
desc: 'Gemini\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1',
|
|
||||||
options: [
|
|
||||||
{ value: 0.00, name: 'Precise', },
|
|
||||||
{ value: 0.50, name: 'Balanced', },
|
|
||||||
{ value: 1.00, name: 'Creative', },
|
|
||||||
],
|
|
||||||
initIndex: 2,
|
|
||||||
onChange: (value, name) => {
|
|
||||||
GeminiService.temperature = value;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'sidebar-chat-settings-toggles',
|
|
||||||
children: [
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'model_training',
|
|
||||||
name: 'Enhancements',
|
|
||||||
desc: 'Tells Gemini:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
|
||||||
initValue: GeminiService.assistantPrompt,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
GeminiService.assistantPrompt = newValue;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'shield',
|
|
||||||
name: 'Safety',
|
|
||||||
desc: 'When turned off, tells the API (not the model) \nto not block harmful/explicit content',
|
|
||||||
initValue: GeminiService.safe,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
GeminiService.safe = newValue;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ConfigToggle({
|
|
||||||
icon: 'history',
|
|
||||||
name: 'History',
|
|
||||||
desc: 'Saves chat history\nMessages in previous chats won\'t show automatically, but they are there',
|
|
||||||
initValue: GeminiService.useHistory,
|
|
||||||
onChange: (self, newValue) => {
|
|
||||||
GeminiService.useHistory = newValue;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GoogleAiInstructions = () => Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(GeminiService, (self, hasKey) => {
|
|
||||||
self.revealChild = (GeminiService.key.length == 0);
|
|
||||||
}, 'hasKey')
|
|
||||||
,
|
|
||||||
child: Button({
|
|
||||||
child: Label({
|
|
||||||
useMarkup: true,
|
|
||||||
wrap: true,
|
|
||||||
className: 'txt sidebar-chat-welcome-txt',
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'A Google AI API key is required\nYou can grab one <u>here</u>, then enter it below',
|
|
||||||
// setup: self => self.set_markup("This is a <a href=\"https://www.github.com\">test link</a>")
|
|
||||||
}),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: () => {
|
|
||||||
Utils.execAsync(['bash', '-c', `xdg-open https://makersuite.google.com/app/apikey &`]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
|
|
||||||
const geminiWelcome = Box({
|
|
||||||
vexpand: true,
|
|
||||||
homogeneous: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
GeminiInfo(),
|
|
||||||
GoogleAiInstructions(),
|
|
||||||
GeminiSettings(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const chatContent = Box({
|
|
||||||
className: 'spacing-v-5',
|
|
||||||
vertical: true,
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(GeminiService, (box, id) => {
|
|
||||||
const message = GeminiService.messages[id];
|
|
||||||
if (!message) return;
|
|
||||||
box.add(ChatMessage(message, MODEL_NAME))
|
|
||||||
}, 'newMsg')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearChat = () => {
|
|
||||||
GeminiService.clear();
|
|
||||||
const children = chatContent.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i];
|
|
||||||
child.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CommandButton = (command) => Button({
|
|
||||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
|
||||||
onClicked: () => sendMessage(command),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: command,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const geminiCommands = Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
CommandButton('/key'),
|
|
||||||
CommandButton('/model'),
|
|
||||||
CommandButton('/clear'),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
export const sendMessage = (text) => {
|
|
||||||
// Check if text or API key is empty
|
|
||||||
if (text.length == 0) return;
|
|
||||||
if (GeminiService.key.length == 0) {
|
|
||||||
GeminiService.key = text;
|
|
||||||
chatContent.add(SystemMessage(`Key saved to\n\`${GeminiService.keyPath}\``, 'API Key', geminiView));
|
|
||||||
text = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Commands
|
|
||||||
if (text.startsWith('/')) {
|
|
||||||
if (text.startsWith('/clear')) clearChat();
|
|
||||||
else if (text.startsWith('/load')) {
|
|
||||||
clearChat();
|
|
||||||
GeminiService.loadHistory();
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${GeminiService.modelName}\``, '/model', geminiView))
|
|
||||||
else if (text.startsWith('/prompt')) {
|
|
||||||
const firstSpaceIndex = text.indexOf(' ');
|
|
||||||
const prompt = text.slice(firstSpaceIndex + 1);
|
|
||||||
if (firstSpaceIndex == -1 || prompt.length < 1) {
|
|
||||||
chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', geminiView))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GeminiService.addMessage('user', prompt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/key')) {
|
|
||||||
const parts = text.split(' ');
|
|
||||||
if (parts.length == 1) chatContent.add(SystemMessage(
|
|
||||||
`Key stored in:\n\`${GeminiService.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
|
|
||||||
'/key',
|
|
||||||
geminiView));
|
|
||||||
else {
|
|
||||||
GeminiService.key = parts[1];
|
|
||||||
chatContent.add(SystemMessage(`Updated API Key at\n\`${GeminiService.keyPath}\``, '/key', geminiView));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (text.startsWith('/test'))
|
|
||||||
chatContent.add(SystemMessage(markdownTest, `Markdown test`, geminiView));
|
|
||||||
else
|
|
||||||
chatContent.add(SystemMessage(`Invalid command.`, 'Error', geminiView))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GeminiService.send(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const geminiView = Box({
|
|
||||||
homogeneous: true,
|
|
||||||
children: [Scrollable({
|
|
||||||
className: 'sidebar-chat-viewport',
|
|
||||||
vexpand: true,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
geminiWelcome,
|
|
||||||
chatContent,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (scrolledWindow) => {
|
|
||||||
// Show scrollbar
|
|
||||||
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
const vScrollbar = scrolledWindow.get_vscrollbar();
|
|
||||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
|
||||||
// Avoid click-to-scroll-widget-to-view behavior
|
|
||||||
Utils.timeout(1, () => {
|
|
||||||
const viewport = scrolledWindow.child;
|
|
||||||
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
|
|
||||||
})
|
|
||||||
// Always scroll to bottom with new content
|
|
||||||
const adjustment = scrolledWindow.get_vadjustment();
|
|
||||||
adjustment.connect("changed", () => Utils.timeout(1, () => {
|
|
||||||
if(!chatEntry.hasFocus) return;
|
|
||||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})]
|
|
||||||
});
|
|
|
@ -1,419 +0,0 @@
|
||||||
// TODO: execAsync(['identify', '-format', '{"w":%w,"h":%h}', imagePath])
|
|
||||||
// to detect img dimensions
|
|
||||||
|
|
||||||
const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Button, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { fileExists } from '../../.miscutils/files.js';
|
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
||||||
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
|
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
|
||||||
import WaifuService from '../../../services/waifus.js';
|
|
||||||
import { darkMode } from '../../.miscutils/system.js';
|
|
||||||
import { chatEntry } from '../apiwidgets.js';
|
|
||||||
|
|
||||||
async function getImageViewerApp(preferredApp) {
|
|
||||||
Utils.execAsync(['bash', '-c', `command -v ${preferredApp}`])
|
|
||||||
.then((output) => {
|
|
||||||
if (output != '') return preferredApp;
|
|
||||||
else return 'xdg-open';
|
|
||||||
})
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
|
|
||||||
const IMAGE_REVEAL_DELAY = 13; // Some wait for inits n other weird stuff
|
|
||||||
const IMAGE_VIEWER_APP = getImageViewerApp(userOptions.apps.imageViewer); // Gnome's image viewer cuz very comfortable zooming
|
|
||||||
const USER_CACHE_DIR = GLib.get_user_cache_dir();
|
|
||||||
|
|
||||||
// Create cache folder and clear pics from previous session
|
|
||||||
Utils.exec(`bash -c 'mkdir -p ${USER_CACHE_DIR}/ags/media/waifus'`);
|
|
||||||
Utils.exec(`bash -c 'rm ${USER_CACHE_DIR}/ags/media/waifus/*'`);
|
|
||||||
|
|
||||||
const CommandButton = (command) => Button({
|
|
||||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
|
||||||
onClicked: () => sendMessage(command),
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: command,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const waifuTabIcon = Box({
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
MaterialIcon('photo', 'norm'),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const WaifuInfo = () => {
|
|
||||||
const waifuLogo = Label({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'sidebar-chat-welcome-logo',
|
|
||||||
label: 'photo',
|
|
||||||
})
|
|
||||||
return Box({
|
|
||||||
vertical: true,
|
|
||||||
vexpand: true,
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
children: [
|
|
||||||
waifuLogo,
|
|
||||||
Label({
|
|
||||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Waifus',
|
|
||||||
}),
|
|
||||||
Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Label({
|
|
||||||
className: 'txt-smallie txt-subtext',
|
|
||||||
wrap: true,
|
|
||||||
justify: Gtk.Justification.CENTER,
|
|
||||||
label: 'Powered by waifu.im + other APIs',
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
className: 'txt-subtext txt-norm icon-material',
|
|
||||||
label: 'info',
|
|
||||||
tooltipText: 'Type tags for a random pic.\nNSFW content will not be returned unless\nyou explicitly request such a tag.\n\nDisclaimer: Not affiliated with the providers\nnor responsible for any of their content.',
|
|
||||||
setup: setupCursorHoverInfo,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const waifuWelcome = Box({
|
|
||||||
vexpand: true,
|
|
||||||
homogeneous: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
WaifuInfo(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const WaifuImage = (taglist) => {
|
|
||||||
const ImageState = (icon, name) => Box({
|
|
||||||
className: 'spacing-h-5 txt',
|
|
||||||
children: [
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
Label({
|
|
||||||
className: 'sidebar-waifu-txt txt-smallie',
|
|
||||||
xalign: 0,
|
|
||||||
label: name,
|
|
||||||
}),
|
|
||||||
MaterialIcon(icon, 'norm'),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const ImageAction = ({ name, icon, action }) => Button({
|
|
||||||
className: 'sidebar-waifu-image-action txt-norm icon-material',
|
|
||||||
tooltipText: name,
|
|
||||||
label: icon,
|
|
||||||
onClicked: action,
|
|
||||||
setup: setupCursorHover,
|
|
||||||
})
|
|
||||||
const downloadState = Stack({
|
|
||||||
homogeneous: false,
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationSmall,
|
|
||||||
children: {
|
|
||||||
'api': ImageState('api', 'Calling API'),
|
|
||||||
'download': ImageState('downloading', 'Downloading image'),
|
|
||||||
'done': ImageState('done', 'Finished!'),
|
|
||||||
'error': ImageState('error', 'Error'),
|
|
||||||
'notfound': ImageState('error', 'Not found!'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const downloadIndicator = MarginRevealer({
|
|
||||||
vpack: 'center',
|
|
||||||
transition: 'slide_left',
|
|
||||||
revealChild: true,
|
|
||||||
child: downloadState,
|
|
||||||
});
|
|
||||||
const blockHeading = Box({
|
|
||||||
hpack: 'fill',
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
...taglist.map((tag) => CommandButton(tag)),
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
downloadIndicator,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const blockImageActions = Revealer({
|
|
||||||
transition: 'crossfade',
|
|
||||||
revealChild: false,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
className: 'sidebar-waifu-image-actions spacing-h-3',
|
|
||||||
children: [
|
|
||||||
Box({ hexpand: true }),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Go to source',
|
|
||||||
icon: 'link',
|
|
||||||
action: () => execAsync(['xdg-open', `${thisBlock.attribute.imageData.source}`]).catch(print),
|
|
||||||
}),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Hoard',
|
|
||||||
icon: 'save',
|
|
||||||
action: (self) => {
|
|
||||||
execAsync(['bash', '-c', `mkdir -p $(xdg-user-dir PICTURES)/homework${thisBlock.attribute.isNsfw ? '/🌶️' : ''} && cp ${thisBlock.attribute.imagePath} $(xdg-user-dir PICTURES)/homework${thisBlock.attribute.isNsfw ? '/🌶️/' : ''}`])
|
|
||||||
.then(() => self.label = 'done')
|
|
||||||
.catch(print);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ImageAction({
|
|
||||||
name: 'Open externally',
|
|
||||||
icon: 'open_in_new',
|
|
||||||
action: () => execAsync([IMAGE_VIEWER_APP, `${thisBlock.attribute.imagePath}`]).catch(print),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const blockImage = Widget.DrawingArea({
|
|
||||||
className: 'sidebar-waifu-image',
|
|
||||||
});
|
|
||||||
const blockImageRevealer = Revealer({
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
revealChild: false,
|
|
||||||
child: Box({
|
|
||||||
className: 'margin-top-5',
|
|
||||||
children: [Overlay({
|
|
||||||
child: Box({
|
|
||||||
homogeneous: true,
|
|
||||||
className: 'sidebar-waifu-image',
|
|
||||||
children: [blockImage],
|
|
||||||
}),
|
|
||||||
overlays: [blockImageActions],
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const thisBlock = Box({
|
|
||||||
className: 'sidebar-chat-message',
|
|
||||||
attribute: {
|
|
||||||
'imagePath': '',
|
|
||||||
'isNsfw': false,
|
|
||||||
'imageData': '',
|
|
||||||
'update': (imageData, force = false) => {
|
|
||||||
thisBlock.attribute.imageData = imageData;
|
|
||||||
const { status, signature, url, extension, source, dominant_color, is_nsfw, width, height, tags } = thisBlock.attribute.imageData;
|
|
||||||
thisBlock.attribute.isNsfw = is_nsfw;
|
|
||||||
if (status == 404) {
|
|
||||||
downloadState.shown = 'notfound';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (status != 200) {
|
|
||||||
downloadState.shown = 'error';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
thisBlock.attribute.imagePath = `${USER_CACHE_DIR}/ags/media/waifus/${signature}${extension}`;
|
|
||||||
downloadState.shown = 'download';
|
|
||||||
// Width/height
|
|
||||||
const widgetWidth = Math.min(Math.floor(waifuContent.get_allocated_width() * 0.85), width);
|
|
||||||
const widgetHeight = Math.ceil(widgetWidth * height / width);
|
|
||||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
|
||||||
const showImage = () => {
|
|
||||||
downloadState.shown = 'done';
|
|
||||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(thisBlock.attribute.imagePath, widgetWidth, widgetHeight);
|
|
||||||
// const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false);
|
|
||||||
|
|
||||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
|
||||||
blockImage.connect("draw", (widget, cr) => {
|
|
||||||
const borderRadius = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
|
||||||
|
|
||||||
// Draw a rounded rectangle
|
|
||||||
cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
|
|
||||||
cr.arc(widgetWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
|
|
||||||
cr.arc(widgetWidth - borderRadius, widgetHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
|
|
||||||
cr.arc(borderRadius, widgetHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
|
|
||||||
cr.closePath();
|
|
||||||
cr.clip();
|
|
||||||
|
|
||||||
// Paint image as bg
|
|
||||||
Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
|
|
||||||
cr.paint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reveal stuff
|
|
||||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => {
|
|
||||||
blockImageRevealer.revealChild = true;
|
|
||||||
})
|
|
||||||
Utils.timeout(IMAGE_REVEAL_DELAY + blockImageRevealer.transitionDuration,
|
|
||||||
() => blockImageActions.revealChild = true
|
|
||||||
);
|
|
||||||
downloadIndicator.attribute.hide();
|
|
||||||
}
|
|
||||||
// Show
|
|
||||||
if (!force && fileExists(thisBlock.attribute.imagePath)) showImage();
|
|
||||||
else Utils.execAsync(['bash', '-c', `wget -O '${thisBlock.attribute.imagePath}' '${url}'`])
|
|
||||||
.then(showImage)
|
|
||||||
.catch(print);
|
|
||||||
thisBlock.css = `background-color: mix(${darkMode.value ? 'black' : 'white'}, ${dominant_color}, 0.97);`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
blockHeading,
|
|
||||||
Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'start',
|
|
||||||
children: [blockImageRevealer],
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return thisBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
const waifuContent = Box({
|
|
||||||
className: 'spacing-v-15',
|
|
||||||
vertical: true,
|
|
||||||
attribute: {
|
|
||||||
'map': new Map(),
|
|
||||||
},
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(WaifuService, (box, id) => {
|
|
||||||
if (id === undefined) return;
|
|
||||||
const newImageBlock = WaifuImage(WaifuService.queries[id]);
|
|
||||||
box.add(newImageBlock);
|
|
||||||
box.show_all();
|
|
||||||
box.attribute.map.set(id, newImageBlock);
|
|
||||||
}, 'newResponse')
|
|
||||||
.hook(WaifuService, (box, id) => {
|
|
||||||
if (id === undefined) return;
|
|
||||||
const data = WaifuService.responses[id];
|
|
||||||
if (!data) return;
|
|
||||||
const imageBlock = box.attribute.map.get(id);
|
|
||||||
imageBlock?.attribute.update(data);
|
|
||||||
}, 'updateResponse')
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const waifuView = Scrollable({
|
|
||||||
className: 'sidebar-chat-viewport',
|
|
||||||
vexpand: true,
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
waifuWelcome,
|
|
||||||
waifuContent,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
setup: (scrolledWindow) => {
|
|
||||||
// Show scrollbar
|
|
||||||
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
const vScrollbar = scrolledWindow.get_vscrollbar();
|
|
||||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
|
||||||
// Avoid click-to-scroll-widget-to-view behavior
|
|
||||||
Utils.timeout(1, () => {
|
|
||||||
const viewport = scrolledWindow.child;
|
|
||||||
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
|
|
||||||
})
|
|
||||||
// Always scroll to bottom with new content
|
|
||||||
const adjustment = scrolledWindow.get_vadjustment();
|
|
||||||
adjustment.connect("changed", () => {
|
|
||||||
if (!chatEntry.hasFocus) return;
|
|
||||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const waifuTags = Revealer({
|
|
||||||
revealChild: false,
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
Scrollable({
|
|
||||||
vscroll: 'never',
|
|
||||||
hscroll: 'automatic',
|
|
||||||
hexpand: true,
|
|
||||||
child: Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
children: [
|
|
||||||
CommandButton('waifu'),
|
|
||||||
CommandButton('maid'),
|
|
||||||
CommandButton('uniform'),
|
|
||||||
CommandButton('oppai'),
|
|
||||||
CommandButton('selfies'),
|
|
||||||
CommandButton('marin-kitagawa'),
|
|
||||||
CommandButton('raiden-shogun'),
|
|
||||||
CommandButton('mori-calliope'),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
Box({ className: 'separator-line' }),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const waifuCommands = Box({
|
|
||||||
className: 'spacing-h-5',
|
|
||||||
setup: (self) => {
|
|
||||||
self.pack_end(CommandButton('/clear'), false, false, 0);
|
|
||||||
self.pack_start(Button({
|
|
||||||
className: 'sidebar-chat-chip-toggle',
|
|
||||||
setup: setupCursorHover,
|
|
||||||
label: 'Tags →',
|
|
||||||
onClicked: () => {
|
|
||||||
waifuTags.revealChild = !waifuTags.revealChild;
|
|
||||||
}
|
|
||||||
}), false, false, 0);
|
|
||||||
self.pack_start(waifuTags, true, true, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearChat = () => { // destroy!!
|
|
||||||
waifuContent.attribute.map.forEach((value, key, map) => {
|
|
||||||
value.destroy();
|
|
||||||
value = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function newSimpleImageCall(name, url, width, height, dominantColor = '#9392A6') {
|
|
||||||
const timeSinceEpoch = Date.now();
|
|
||||||
const newImage = WaifuImage([`/${name}`]);
|
|
||||||
waifuContent.add(newImage);
|
|
||||||
waifuContent.attribute.map.set(timeSinceEpoch, newImage);
|
|
||||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage?.attribute.update({
|
|
||||||
status: 200,
|
|
||||||
url: url,
|
|
||||||
extension: '',
|
|
||||||
signature: timeSinceEpoch,
|
|
||||||
source: url,
|
|
||||||
dominant_color: dominantColor,
|
|
||||||
is_nsfw: false,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
tags: [`/${name}`],
|
|
||||||
}, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendMessage = (text) => {
|
|
||||||
// Commands
|
|
||||||
if (text.startsWith('/')) {
|
|
||||||
if (text.startsWith('/clear')) clearChat();
|
|
||||||
else if (text.startsWith('/test'))
|
|
||||||
newSimpleImageCall('test', 'https://picsum.photos/600/400', 300, 200);
|
|
||||||
else if (text.startsWith('/chino'))
|
|
||||||
newSimpleImageCall('chino', 'https://chino.pages.dev/chino', 300, 400, '#B2AEF3');
|
|
||||||
else if (text.startsWith('/place'))
|
|
||||||
newSimpleImageCall('place', 'https://placewaifu.com/image/400/600', 400, 600, '#F0A235');
|
|
||||||
|
|
||||||
}
|
|
||||||
else WaifuService.fetch(text);
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
const { Gtk, Gdk } = imports.gi;
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from '../.widgetutils/cursorhover.js';
|
|
||||||
// APIs
|
|
||||||
import GPTService from '../../services/gpt.js';
|
|
||||||
import Gemini from '../../services/gemini.js';
|
|
||||||
import { geminiView, geminiCommands, sendMessage as geminiSendMessage, geminiTabIcon } from './apis/gemini.js';
|
|
||||||
import { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
|
|
||||||
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
|
|
||||||
import { booruView, booruCommands, sendMessage as booruSendMessage, booruTabIcon } from './apis/booru.js';
|
|
||||||
import { enableClickthrough } from "../.widgetutils/clickthrough.js";
|
|
||||||
import { checkKeybind } from '../.widgetutils/keybind.js';
|
|
||||||
const TextView = Widget.subclass(Gtk.TextView, "AgsTextView");
|
|
||||||
|
|
||||||
import { widgetContent } from './sideleft.js';
|
|
||||||
import { IconTabContainer } from '../.commonwidgets/tabcontainer.js';
|
|
||||||
|
|
||||||
const EXPAND_INPUT_THRESHOLD = 30;
|
|
||||||
const APILIST = {
|
|
||||||
'gpt': {
|
|
||||||
name: 'Assistant (GPTs)',
|
|
||||||
sendCommand: chatGPTSendMessage,
|
|
||||||
contentWidget: chatGPTView,
|
|
||||||
commandBar: chatGPTCommands,
|
|
||||||
tabIcon: chatGPTTabIcon,
|
|
||||||
placeholderText: 'Message the model...',
|
|
||||||
},
|
|
||||||
'gemini': {
|
|
||||||
name: 'Assistant (Gemini Pro)',
|
|
||||||
sendCommand: geminiSendMessage,
|
|
||||||
contentWidget: geminiView,
|
|
||||||
commandBar: geminiCommands,
|
|
||||||
tabIcon: geminiTabIcon,
|
|
||||||
placeholderText: 'Message Gemini...',
|
|
||||||
},
|
|
||||||
'waifu': {
|
|
||||||
name: 'Waifus',
|
|
||||||
sendCommand: waifuSendMessage,
|
|
||||||
contentWidget: waifuView,
|
|
||||||
commandBar: waifuCommands,
|
|
||||||
tabIcon: waifuTabIcon,
|
|
||||||
placeholderText: 'Enter tags',
|
|
||||||
},
|
|
||||||
'booru': {
|
|
||||||
name: 'Booru',
|
|
||||||
sendCommand: booruSendMessage,
|
|
||||||
contentWidget: booruView,
|
|
||||||
commandBar: booruCommands,
|
|
||||||
tabIcon: booruTabIcon,
|
|
||||||
placeholderText: 'Enter tags',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const APIS = userOptions.sidebar.pages.apis.order.map((apiName) => APILIST[apiName]);
|
|
||||||
let currentApiId = 0;
|
|
||||||
|
|
||||||
function apiSendMessage(textView) {
|
|
||||||
// Get text
|
|
||||||
const buffer = textView.get_buffer();
|
|
||||||
const [start, end] = buffer.get_bounds();
|
|
||||||
const text = buffer.get_text(start, end, true).trimStart();
|
|
||||||
if (!text || text.length == 0) return;
|
|
||||||
// Send
|
|
||||||
APIS[currentApiId].sendCommand(text)
|
|
||||||
// Reset
|
|
||||||
buffer.set_text("", -1);
|
|
||||||
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
|
|
||||||
chatEntry.set_valign(Gtk.Align.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const chatEntry = TextView({
|
|
||||||
hexpand: true,
|
|
||||||
wrapMode: Gtk.WrapMode.WORD_CHAR,
|
|
||||||
acceptsTab: false,
|
|
||||||
className: 'sidebar-chat-entry txt txt-smallie',
|
|
||||||
setup: (self) => self
|
|
||||||
.hook(App, (self, currentName, visible) => {
|
|
||||||
if (visible && currentName === 'sideleft') {
|
|
||||||
self.grab_focus();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.hook(GPTService, (self) => {
|
|
||||||
if (APIS[currentApiId].name != 'Assistant (GPTs)') return;
|
|
||||||
self.placeholderText = (GPTService.key.length > 0 ? 'Message the model...' : 'Enter API Key...');
|
|
||||||
}, 'hasKey')
|
|
||||||
.hook(Gemini, (self) => {
|
|
||||||
if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return;
|
|
||||||
self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...');
|
|
||||||
}, 'hasKey')
|
|
||||||
.on("key-press-event", (widget, event) => {
|
|
||||||
// Don't send when Shift+Enter
|
|
||||||
if (event.get_keyval()[1] === Gdk.KEY_Return) {
|
|
||||||
if (event.get_state()[1] !== 17) {// SHIFT_MASK doesn't work but 17 should be shift
|
|
||||||
apiSendMessage(widget);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Keybinds
|
|
||||||
if (checkKeybind(event, userOptions.keybinds.sidebar.cycleTab))
|
|
||||||
widgetContent.cycleTab();
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.nextTab))
|
|
||||||
widgetContent.nextTab();
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.prevTab))
|
|
||||||
widgetContent.prevTab();
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.apis.nextTab)) {
|
|
||||||
apiWidgets.attribute.nextTab();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.apis.prevTab)) {
|
|
||||||
apiWidgets.attribute.prevTab();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
||||||
|
|
||||||
chatEntry.get_buffer().connect("changed", (buffer) => {
|
|
||||||
const bufferText = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), true);
|
|
||||||
chatSendButton.toggleClassName('sidebar-chat-send-available', bufferText.length > 0);
|
|
||||||
chatPlaceholderRevealer.revealChild = (bufferText.length == 0);
|
|
||||||
if (buffer.get_line_count() > 1 || bufferText.length > EXPAND_INPUT_THRESHOLD) {
|
|
||||||
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', true);
|
|
||||||
chatEntry.set_valign(Gtk.Align.FILL);
|
|
||||||
chatPlaceholder.set_valign(Gtk.Align.FILL);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
|
|
||||||
chatEntry.set_valign(Gtk.Align.CENTER);
|
|
||||||
chatPlaceholder.set_valign(Gtk.Align.CENTER);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatEntryWrapper = Scrollable({
|
|
||||||
className: 'sidebar-chat-wrapper',
|
|
||||||
hscroll: 'never',
|
|
||||||
vscroll: 'always',
|
|
||||||
child: chatEntry,
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatSendButton = Button({
|
|
||||||
className: 'txt-norm icon-material sidebar-chat-send',
|
|
||||||
vpack: 'end',
|
|
||||||
label: 'arrow_upward',
|
|
||||||
setup: setupCursorHover,
|
|
||||||
onClicked: (self) => {
|
|
||||||
APIS[currentApiId].sendCommand(chatEntry.get_buffer().text);
|
|
||||||
chatEntry.get_buffer().set_text("", -1);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatPlaceholder = Label({
|
|
||||||
className: 'txt-subtext txt-smallie margin-left-5',
|
|
||||||
hpack: 'start',
|
|
||||||
vpack: 'center',
|
|
||||||
label: APIS[currentApiId].placeholderText,
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatPlaceholderRevealer = Revealer({
|
|
||||||
revealChild: true,
|
|
||||||
transition: 'crossfade',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
child: chatPlaceholder,
|
|
||||||
setup: enableClickthrough,
|
|
||||||
});
|
|
||||||
|
|
||||||
const textboxArea = Box({ // Entry area
|
|
||||||
className: 'sidebar-chat-textarea',
|
|
||||||
children: [
|
|
||||||
Overlay({
|
|
||||||
passThrough: true,
|
|
||||||
child: chatEntryWrapper,
|
|
||||||
overlays: [chatPlaceholderRevealer],
|
|
||||||
}),
|
|
||||||
Box({ className: 'width-10' }),
|
|
||||||
chatSendButton,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const apiCommandStack = Stack({
|
|
||||||
transition: 'slide_up_down',
|
|
||||||
transitionDuration: userOptions.animations.durationLarge,
|
|
||||||
children: APIS.reduce((acc, api) => {
|
|
||||||
acc[api.name] = api.commandBar;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const apiContentStack = IconTabContainer({
|
|
||||||
tabSwitcherClassName: 'sidebar-icontabswitcher',
|
|
||||||
className: 'margin-top-5',
|
|
||||||
iconWidgets: APIS.map((api) => api.tabIcon),
|
|
||||||
names: APIS.map((api) => api.name),
|
|
||||||
children: APIS.map((api) => api.contentWidget),
|
|
||||||
onChange: (self, id) => {
|
|
||||||
apiCommandStack.shown = APIS[id].name;
|
|
||||||
chatPlaceholder.label = APIS[id].placeholderText;
|
|
||||||
currentApiId = id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function switchToTab(id) {
|
|
||||||
apiContentStack.shown.value = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiWidgets = Widget.Box({
|
|
||||||
attribute: {
|
|
||||||
'nextTab': () => switchToTab(Math.min(currentApiId + 1, APIS.length - 1)),
|
|
||||||
'prevTab': () => switchToTab(Math.max(0, currentApiId - 1)),
|
|
||||||
},
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
homogeneous: false,
|
|
||||||
children: [
|
|
||||||
apiContentStack,
|
|
||||||
apiCommandStack,
|
|
||||||
textboxArea,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export default apiWidgets;
|
|
|
@ -1,18 +0,0 @@
|
||||||
import PopupWindow from '../.widgethacks/popupwindow.js';
|
|
||||||
import SidebarLeft from "./sideleft.js";
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box } = Widget;
|
|
||||||
import clickCloseRegion from '../.commonwidgets/clickcloseregion.js';
|
|
||||||
|
|
||||||
export default () => PopupWindow({
|
|
||||||
keymode: 'on-demand',
|
|
||||||
anchor: ['left', 'top', 'bottom'],
|
|
||||||
name: 'sideleft',
|
|
||||||
layer: 'overlay',
|
|
||||||
child: Box({
|
|
||||||
children: [
|
|
||||||
SidebarLeft(),
|
|
||||||
clickCloseRegion({ name: 'sideleft', multimonitor: false, fillMonitor: 'horizontal' }),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,121 +0,0 @@
|
||||||
const { Gdk } = imports.gi;
|
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { Box, Button, EventBox, Label, Revealer, Scrollable, Stack } = Widget;
|
|
||||||
const { execAsync, exec } = Utils;
|
|
||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
|
||||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
|
||||||
import toolBox from './toolbox.js';
|
|
||||||
import apiWidgets from './apiwidgets.js';
|
|
||||||
import { chatEntry } from './apiwidgets.js';
|
|
||||||
import { TabContainer } from '../.commonwidgets/tabcontainer.js';
|
|
||||||
import { checkKeybind } from '../.widgetutils/keybind.js';
|
|
||||||
|
|
||||||
const SIDEBARTABS = {
|
|
||||||
'apis': {
|
|
||||||
name: 'apis',
|
|
||||||
content: apiWidgets,
|
|
||||||
materialIcon: 'api',
|
|
||||||
friendlyName: 'APIs',
|
|
||||||
},
|
|
||||||
'tools': {
|
|
||||||
name: 'tools',
|
|
||||||
content: toolBox,
|
|
||||||
materialIcon: 'home_repair_service',
|
|
||||||
friendlyName: 'Tools',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const CONTENTS = userOptions.sidebar.pages.order.map((tabName) => SIDEBARTABS[tabName])
|
|
||||||
|
|
||||||
const pinButton = Button({
|
|
||||||
attribute: {
|
|
||||||
'enabled': false,
|
|
||||||
'toggle': (self) => {
|
|
||||||
self.attribute.enabled = !self.attribute.enabled;
|
|
||||||
self.toggleClassName('sidebar-pin-enabled', self.attribute.enabled);
|
|
||||||
|
|
||||||
const sideleftWindow = App.getWindow('sideleft');
|
|
||||||
const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1];
|
|
||||||
|
|
||||||
sideleftContent.toggleClassName('sidebar-pinned', self.attribute.enabled);
|
|
||||||
|
|
||||||
if (self.attribute.enabled) {
|
|
||||||
sideleftWindow.exclusivity = 'on-demad';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sideleftWindow.exclusivity = 'normal';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vpack: 'start',
|
|
||||||
className: 'sidebar-pin',
|
|
||||||
child: MaterialIcon('push_pin', 'larger'),
|
|
||||||
tooltipText: 'Pin sidebar (Ctrl+P)',
|
|
||||||
onClicked: (self) => self.attribute.toggle(self),
|
|
||||||
setup: (self) => {
|
|
||||||
setupCursorHover(self);
|
|
||||||
self.hook(App, (self, currentName, visible) => {
|
|
||||||
if (currentName === 'sideleft' && visible) self.grab_focus();
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const widgetContent = TabContainer({
|
|
||||||
icons: CONTENTS.map((item) => item.materialIcon),
|
|
||||||
names: CONTENTS.map((item) => item.friendlyName),
|
|
||||||
children: CONTENTS.map((item) => item.content),
|
|
||||||
className: 'sidebar-left spacing-v-10',
|
|
||||||
setup: (self) => self.hook(App, (self, currentName, visible) => {
|
|
||||||
if (currentName === 'sideleft')
|
|
||||||
self.toggleClassName('sidebar-pinned', pinButton.attribute.enabled && visible);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => Box({
|
|
||||||
// vertical: true,
|
|
||||||
vexpand: true,
|
|
||||||
css: 'min-width: 2px;',
|
|
||||||
children: [
|
|
||||||
widgetContent,
|
|
||||||
],
|
|
||||||
setup: (self) => self
|
|
||||||
.on('key-press-event', (widget, event) => { // Handle keybinds
|
|
||||||
if (checkKeybind(event, userOptions.keybinds.sidebar.pin))
|
|
||||||
pinButton.attribute.toggle(pinButton);
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.cycleTab))
|
|
||||||
widgetContent.cycleTab();
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.nextTab))
|
|
||||||
widgetContent.nextTab();
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.prevTab))
|
|
||||||
widgetContent.prevTab();
|
|
||||||
|
|
||||||
if (widgetContent.attribute.names[widgetContent.attribute.shown.value] == 'APIs') { // If api tab is focused
|
|
||||||
// Focus entry when typing
|
|
||||||
if ((
|
|
||||||
!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
|
||||||
event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 &&
|
|
||||||
widget != chatEntry && event.get_keyval()[1] != Gdk.KEY_space)
|
|
||||||
||
|
|
||||||
((event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
|
||||||
event.get_keyval()[1] === Gdk.KEY_v)
|
|
||||||
) {
|
|
||||||
chatEntry.grab_focus();
|
|
||||||
const buffer = chatEntry.get_buffer();
|
|
||||||
buffer.set_text(buffer.text + String.fromCharCode(event.get_keyval()[1]), -1);
|
|
||||||
buffer.place_cursor(buffer.get_iter_at_offset(-1));
|
|
||||||
}
|
|
||||||
// Switch API type
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.apis.nextTab)) {
|
|
||||||
const toSwitchTab = widgetContent.attribute.children[widgetContent.attribute.shown.value];
|
|
||||||
toSwitchTab.nextTab();
|
|
||||||
}
|
|
||||||
else if (checkKeybind(event, userOptions.keybinds.sidebar.apis.prevTab)) {
|
|
||||||
const toSwitchTab = widgetContent.attribute.children[widgetContent.attribute.shown.value];
|
|
||||||
toSwitchTab.prevTab();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
,
|
|
||||||
});
|
|
|
@ -1,20 +0,0 @@
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
||||||
const { Box, Label, Scrollable } = Widget;
|
|
||||||
import QuickScripts from './tools/quickscripts.js';
|
|
||||||
import ColorPicker from './tools/colorpicker.js';
|
|
||||||
import Name from './tools/name.js';
|
|
||||||
|
|
||||||
export default Scrollable({
|
|
||||||
hscroll: "never",
|
|
||||||
vscroll: "automatic",
|
|
||||||
child: Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'spacing-v-10',
|
|
||||||
children: [
|
|
||||||
QuickScripts(),
|
|
||||||
ColorPicker(),
|
|
||||||
Box({ vexpand: true }),
|
|
||||||
Name(),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,99 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Function to get the current resolution
|
|
||||||
get_current_resolution() {
|
|
||||||
local output
|
|
||||||
output=$(hyprctl monitors -j)
|
|
||||||
local width height refreshRate
|
|
||||||
width=$(echo "$output" | jq -r '.[0].width')
|
|
||||||
height=$(echo "$output" | jq -r '.[0].height')
|
|
||||||
refreshRate=$(echo "$output" | jq -r '.[0].refreshRate')
|
|
||||||
echo "$width $height $refreshRate"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to update the Hyprland configuration with the new resolution
|
|
||||||
update_resolution_config() {
|
|
||||||
local newWidth="$1"
|
|
||||||
local newHeight="$2"
|
|
||||||
local newRefreshRate="$3"
|
|
||||||
local currentRes
|
|
||||||
currentRes=$(get_current_resolution)
|
|
||||||
local width height refreshRate
|
|
||||||
width=${newWidth:-$(echo "$currentRes" | awk '{print $1}')}
|
|
||||||
height=${newHeight:-$(echo "$currentRes" | awk '{print $2}')}
|
|
||||||
refreshRate=${newRefreshRate:-$(echo "$currentRes" | awk '{print $3}')}
|
|
||||||
|
|
||||||
local modelineOutput
|
|
||||||
modelineOutput=$(gtf "$width" "$height" "$refreshRate")
|
|
||||||
local modeline
|
|
||||||
modeline=$(echo "$modelineOutput" | grep -oP 'Modeline "\K[^"]+')
|
|
||||||
|
|
||||||
if [ -z "$modeline" ]; then
|
|
||||||
echo "Failed to generate modeline"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract the resolution and refresh rate from the modeline
|
|
||||||
local resolution
|
|
||||||
resolution=$(echo "$modeline" | grep -oP '^[0-9]+x[0-9]+')
|
|
||||||
local rate
|
|
||||||
rate=$(echo "$modeline" | grep -oP '[0-9]+.[0-9]+$')
|
|
||||||
|
|
||||||
if [ -z "$resolution" ] || [ -z "$rate" ]; then
|
|
||||||
echo "Failed to extract resolution or refresh rate from modeline"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local configPath="${HOME}/.config/hypr/hyprland/general.conf"
|
|
||||||
local newConfigContent
|
|
||||||
newConfigContent=$(sed "s/^monitor=.*$/monitor=eDP-1, $resolution@$rate, auto, 1/" "$configPath")
|
|
||||||
|
|
||||||
echo "$newConfigContent" > "$configPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main script
|
|
||||||
echo "Welcome to the Resolution Configurator"
|
|
||||||
echo ""
|
|
||||||
echo " +---------------------------+"
|
|
||||||
echo " | _____ |"
|
|
||||||
echo " | | | |"
|
|
||||||
echo " | | | |"
|
|
||||||
echo " | |_____| |"
|
|
||||||
echo " | |"
|
|
||||||
echo " +---------------------------+"
|
|
||||||
echo ""
|
|
||||||
echo "Current resolution and refresh rate:"
|
|
||||||
currentRes=$(get_current_resolution)
|
|
||||||
width=$(echo "$currentRes" | awk '{print $1}')
|
|
||||||
height=$(echo "$currentRes" | awk '{print $2}')
|
|
||||||
refreshRate=$(echo "$currentRes" | awk '{print $3}')
|
|
||||||
|
|
||||||
echo "Width: $width px"
|
|
||||||
echo "Height: $height px"
|
|
||||||
echo "Refresh Rate: $refreshRate Hz"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
read -p "Enter new width (or press Enter to keep current width): " newWidth
|
|
||||||
read -p "Enter new height (or press Enter to keep current height): " newHeight
|
|
||||||
read -p "Enter new refresh rate (or press Enter to keep current refresh rate): " newRefreshRate
|
|
||||||
|
|
||||||
# Validate inputs (if provided)
|
|
||||||
if [[ ! "$newWidth" =~ ^[0-9]+$ && -n "$newWidth" ]]; then
|
|
||||||
echo "Invalid width value."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! "$newHeight" =~ ^[0-9]+$ && -n "$newHeight" ]]; then
|
|
||||||
echo "Invalid height value."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! "$newRefreshRate" =~ ^[0-9]+$ && -n "$newRefreshRate" ]]; then
|
|
||||||
echo "Invalid refresh rate value."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
update_resolution_config "$newWidth" "$newHeight" "$newRefreshRate"
|
|
||||||
|
|
||||||
echo "Resolution updated successfully."
|
|
|
@ -1,198 +0,0 @@
|
||||||
// It's weird, I know
|
|
||||||
const { Gio, GLib } = imports.gi;
|
|
||||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
||||||
const { exec, execAsync } = Utils;
|
|
||||||
import { clamp } from '../../.miscutils/mathfuncs.js';
|
|
||||||
|
|
||||||
export class ColorPickerSelection extends Service {
|
|
||||||
static {
|
|
||||||
Service.register(this, {
|
|
||||||
'picked': [],
|
|
||||||
'assigned': ['int'],
|
|
||||||
'hue': [],
|
|
||||||
'sl': [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_hue = 198;
|
|
||||||
_xAxis = 94;
|
|
||||||
_yAxis = 80;
|
|
||||||
|
|
||||||
get hue() { return this._hue; }
|
|
||||||
set hue(value) {
|
|
||||||
this._hue = clamp(value, 0, 360);
|
|
||||||
this.emit('hue');
|
|
||||||
this.emit('picked');
|
|
||||||
this.emit('changed');
|
|
||||||
}
|
|
||||||
get xAxis() { return this._xAxis; }
|
|
||||||
set xAxis(value) {
|
|
||||||
this._xAxis = clamp(value, 0, 100);
|
|
||||||
this.emit('sl');
|
|
||||||
this.emit('picked');
|
|
||||||
this.emit('changed');
|
|
||||||
}
|
|
||||||
get yAxis() { return this._yAxis; }
|
|
||||||
set yAxis(value) {
|
|
||||||
this._yAxis = clamp(value, 0, 100);
|
|
||||||
this.emit('sl');
|
|
||||||
this.emit('picked');
|
|
||||||
this.emit('changed');
|
|
||||||
}
|
|
||||||
setColorFromHex(hexString, id) {
|
|
||||||
const hsl = hexToHSL(hexString);
|
|
||||||
this._hue = hsl.hue;
|
|
||||||
this._xAxis = hsl.saturation;
|
|
||||||
// this._yAxis = hsl.lightness;
|
|
||||||
this._yAxis = (100 - hsl.saturation / 2) / 100 * hsl.lightness;
|
|
||||||
// console.log(this._hue, this._xAxis, this._yAxis)
|
|
||||||
this.emit('assigned', id);
|
|
||||||
this.emit('changed');
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.emit('changed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function hslToRgbValues(h, s, l) {
|
|
||||||
h /= 360;
|
|
||||||
s /= 100;
|
|
||||||
l /= 100;
|
|
||||||
let r, g, b;
|
|
||||||
if (s === 0) {
|
|
||||||
r = g = b = l; // achromatic
|
|
||||||
} else {
|
|
||||||
const hue2rgb = (p, q, t) => {
|
|
||||||
if (t < 0) t += 1;
|
|
||||||
if (t > 1) t -= 1;
|
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
||||||
if (t < 1 / 2) return q;
|
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
const p = 2 * l - q;
|
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
|
||||||
g = hue2rgb(p, q, h);
|
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
|
||||||
}
|
|
||||||
const to255 = x => Math.round(x * 255);
|
|
||||||
r = to255(r);
|
|
||||||
g = to255(g);
|
|
||||||
b = to255(b);
|
|
||||||
return `${Math.round(r)},${Math.round(g)},${Math.round(b)}`;
|
|
||||||
// return `rgb(${r},${g},${b})`;
|
|
||||||
}
|
|
||||||
export function hslToHex(h, s, l) {
|
|
||||||
h /= 360;
|
|
||||||
s /= 100;
|
|
||||||
l /= 100;
|
|
||||||
let r, g, b;
|
|
||||||
if (s === 0) {
|
|
||||||
r = g = b = l; // achromatic
|
|
||||||
} else {
|
|
||||||
const hue2rgb = (p, q, t) => {
|
|
||||||
if (t < 0) t += 1;
|
|
||||||
if (t > 1) t -= 1;
|
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
||||||
if (t < 1 / 2) return q;
|
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
const p = 2 * l - q;
|
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
|
||||||
g = hue2rgb(p, q, h);
|
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
|
||||||
}
|
|
||||||
const toHex = x => {
|
|
||||||
const hex = Math.round(x * 255).toString(16);
|
|
||||||
return hex.length === 1 ? "0" + hex : hex;
|
|
||||||
};
|
|
||||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// export function hexToHSL(hex) {
|
|
||||||
// // Remove the '#' if present
|
|
||||||
// hex = hex.replace(/^#/, '');
|
|
||||||
// // Parse the hex value into RGB components
|
|
||||||
// const bigint = parseInt(hex, 16);
|
|
||||||
// const r = (bigint >> 16) & 255;
|
|
||||||
// const g = (bigint >> 8) & 255;
|
|
||||||
// const b = bigint & 255;
|
|
||||||
// // Normalize RGB values to range [0, 1]
|
|
||||||
// const normalizedR = r / 255;
|
|
||||||
// const normalizedG = g / 255;
|
|
||||||
// const normalizedB = b / 255;
|
|
||||||
// // Find the maximum and minimum values
|
|
||||||
// const max = Math.max(normalizedR, normalizedG, normalizedB);
|
|
||||||
// const min = Math.min(normalizedR, normalizedG, normalizedB);
|
|
||||||
// // Calculate the lightness
|
|
||||||
// const lightness = (max + min) / 2;
|
|
||||||
// // If the color is grayscale, set saturation to 0
|
|
||||||
// if (max === min) {
|
|
||||||
// return {
|
|
||||||
// hue: 0,
|
|
||||||
// saturation: 0,
|
|
||||||
// lightness: lightness * 100 // Convert to percentage
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// // Calculate the saturation
|
|
||||||
// const d = max - min;
|
|
||||||
// const saturation = lightness > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
||||||
// // Calculate the hue
|
|
||||||
// let hue;
|
|
||||||
// if (max === normalizedR) {
|
|
||||||
// hue = ((normalizedG - normalizedB) / d + (normalizedG < normalizedB ? 6 : 0)) * 60;
|
|
||||||
// } else if (max === normalizedG) {
|
|
||||||
// hue = ((normalizedB - normalizedR) / d + 2) * 60;
|
|
||||||
// } else {
|
|
||||||
// hue = ((normalizedR - normalizedG) / d + 4) * 60;
|
|
||||||
// }
|
|
||||||
// return {
|
|
||||||
// hue: Math.round(hue),
|
|
||||||
// saturation: Math.round(saturation * 100), // Convert to percentage
|
|
||||||
// lightness: Math.round(lightness * 100) // Convert to percentage
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function hexToHSL(hex) {
|
|
||||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
||||||
|
|
||||||
var r = parseInt(result[1], 16);
|
|
||||||
var g = parseInt(result[2], 16);
|
|
||||||
var b = parseInt(result[3], 16);
|
|
||||||
|
|
||||||
r /= 255, g /= 255, b /= 255;
|
|
||||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
||||||
var h, s, l = (max + min) / 2;
|
|
||||||
|
|
||||||
if (max == min) {
|
|
||||||
h = s = 0; // achromatic
|
|
||||||
} else {
|
|
||||||
var d = max - min;
|
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
||||||
switch (max) {
|
|
||||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|
||||||
case g: h = (b - r) / d + 2; break;
|
|
||||||
case b: h = (r - g) / d + 4; break;
|
|
||||||
}
|
|
||||||
h /= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s * 100;
|
|
||||||
s = Math.round(s);
|
|
||||||
l = l * 100;
|
|
||||||
l = Math.round(l);
|
|
||||||
h = Math.round(360 * h);
|
|
||||||
|
|
||||||
return {
|
|
||||||
hue: h,
|
|
||||||
saturation: s,
|
|
||||||
lightness: l
|
|
||||||
};
|
|
||||||
}
|
|