mirror of
https://github.com/zoriya/flake.git
synced 2025-12-06 06:36:19 +00:00
Compare commits
20 Commits
76dfcefc9a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d9a4f392e6 | |||
| 54e9e1b567 | |||
| df08da25f3 | |||
| 75ab0bfd12 | |||
| cfa2a6e37e | |||
| 6a43765a38 | |||
| 4dfec90cbc | |||
| 78542a3c83 | |||
| 34faa25d3a | |||
| 9aec0768ae | |||
| 0e83fa95e0 | |||
| da06a331c4 | |||
| a90656fb9e | |||
| b0e4919f1a | |||
| a389ad81a5 | |||
| d75ac8bb2e | |||
| bbf687eae8 | |||
| 747374133e | |||
| c3f1877067 | |||
| e637e48a66 |
@@ -7,8 +7,11 @@ trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = tab
|
||||
max_line_length = 120
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
[{*.yaml,*.yml,*.nix}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.qml]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
@@ -30,5 +30,5 @@ Format disk with:
|
||||
nix-shell -p git go-task
|
||||
git clone https://github.com/zoriya/flake
|
||||
cd flake
|
||||
sudo task install
|
||||
sudo task install:host
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
user = "greeter";
|
||||
};
|
||||
initial_session = {
|
||||
command = ./niri-session.sh; # "${pkgs.niri}/bin/niri-session";
|
||||
command = ./niri-session.sh;
|
||||
user = user;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -178,7 +178,7 @@ binds {
|
||||
Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
|
||||
}
|
||||
|
||||
workspace "emtpy" {
|
||||
workspace "music" {
|
||||
open-on-output "Dell Inc. DELL S2722QC 2HHZH24"
|
||||
}
|
||||
workspace "chat" {
|
||||
@@ -197,10 +197,15 @@ window-rule {
|
||||
open-focused false
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="com.github.th_ch.youtube_music"
|
||||
open-on-workspace "music"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="discord"
|
||||
match app-id="vesktop"
|
||||
match app-id="com.github.th_ch.youtube_music"
|
||||
match app-id="slack"
|
||||
open-on-workspace "chat"
|
||||
}
|
||||
|
||||
|
||||
230
flake.lock
generated
230
flake.lock
generated
@@ -3,43 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"lastModified": 1761588595,
|
||||
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -56,11 +24,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760948891,
|
||||
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=",
|
||||
"lastModified": 1763759067,
|
||||
"narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04",
|
||||
"rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -69,76 +37,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"neovim-nightly",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760663237,
|
||||
"narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"neovim-nightly",
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hercules-ci-effects": {
|
||||
"inputs": {
|
||||
"flake-parts": [
|
||||
"neovim-nightly",
|
||||
"flake-parts"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"neovim-nightly",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758022363,
|
||||
"narHash": "sha256-ENUhCRWgSX4ni751HieNuQoq06dJvApV/Nm89kh+/A0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "hercules-ci-effects",
|
||||
"rev": "1a3667d33e247ad35ca250698d63f49a5453d824",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "hercules-ci-effects",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -146,11 +44,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761005073,
|
||||
"narHash": "sha256-r6qbieh8iC1q1eCaWv15f4UIp8SeGffwswhNSA1Qk3s=",
|
||||
"lastModified": 1764544324,
|
||||
"narHash": "sha256-GVBGjO7UsmzLrlOJV8NlKSxukHaHencrJqWkCA6FkqI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "84e1adb0cdd13f5f29886091c7234365e12b1e7f",
|
||||
"rev": "e4e25a8c310fa45f2a8339c7972dc43d2845a612",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -176,22 +74,18 @@
|
||||
},
|
||||
"neovim-nightly": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks": "git-hooks",
|
||||
"hercules-ci-effects": "hercules-ci-effects",
|
||||
"neovim-src": "neovim-src",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761005134,
|
||||
"narHash": "sha256-9bSlfRleXFl50M6AnurWr1oKDTk3uF5DaTVHxeds0CY=",
|
||||
"lastModified": 1764547590,
|
||||
"narHash": "sha256-PDVKI5QCPfzVCPY/ZYAQTHGwC9ksT8ISNVaVZDVzb54=",
|
||||
"owner": "nix-community",
|
||||
"repo": "neovim-nightly-overlay",
|
||||
"rev": "3a6201e41d13f1a73b2e2c734dbd36b4c42584b0",
|
||||
"rev": "71ba04d8a26327031fd58284af4a4891d7b3c842",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -203,11 +97,11 @@
|
||||
"neovim-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1761000337,
|
||||
"narHash": "sha256-fBz9U/k/YWoS4QgcoQ54NKDEopTdL2zI0gzLlWv/xR8=",
|
||||
"lastModified": 1764519732,
|
||||
"narHash": "sha256-R0UspjBwPi5St0Dxq9Ej9ejJ34K8ivF5WeCS9dMvvVQ=",
|
||||
"owner": "neovim",
|
||||
"repo": "neovim",
|
||||
"rev": "b67eff38fe19876ab228007897224ec04b58aa40",
|
||||
"rev": "d62bbe24cbe5311ce595d73a0c40dc87af989666",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -223,11 +117,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760721282,
|
||||
"narHash": "sha256-aAHphQbU9t/b2RRy2Eb8oMv+I08isXv2KUGFAFn7nCo=",
|
||||
"lastModified": 1764161084,
|
||||
"narHash": "sha256-HN84sByg9FhJnojkGGDSrcjcbeioFWoNXfuyYfJ1kBE=",
|
||||
"owner": "LnL7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "c3211fcd0c56c11ff110d346d4487b18f7365168",
|
||||
"rev": "e95de00a471d07435e0527ff4db092c84998698e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -243,11 +137,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760846226,
|
||||
"narHash": "sha256-xmU8kAsRprJiTGBTaGrwmjBP3AMA9ltlrxHKFuy5JWc=",
|
||||
"lastModified": 1764475780,
|
||||
"narHash": "sha256-77jL5H5x51ksLiOUDjY0ZK8e2T4ZXLhj3ap8ETvknWI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-index-database",
|
||||
"rev": "5024e1901239a76b7bf94a4cd27f3507e639d49e",
|
||||
"rev": "5a3ff8c1a09003f399f43d5742d893c0b1ab8af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -261,11 +155,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758048506,
|
||||
"narHash": "sha256-I7cLckLwnppaqoUFvTrgGKDevNnIn3qV/3ELxetm6jk=",
|
||||
"lastModified": 1763322257,
|
||||
"narHash": "sha256-eiKNbZXvkB2p/YhM5ltK1CO1znm5Nn2aHLW3Awcqq9g=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixos-avf",
|
||||
"rev": "1b7bf91cef5e3aeada4bc81977eb12b71585b45c",
|
||||
"rev": "3fae0a3692b993bc0c40c61138a76fc1455d0b6e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -276,11 +170,11 @@
|
||||
},
|
||||
"nixos-hardware": {
|
||||
"locked": {
|
||||
"lastModified": 1760958188,
|
||||
"narHash": "sha256-2m1S4jl+GEDtlt2QqeHil8Ny456dcGSKJAM7q3j/BFU=",
|
||||
"lastModified": 1764440730,
|
||||
"narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "d6645c340ef7d821602fd2cd199e8d1eed10afbc",
|
||||
"rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -292,17 +186,17 @@
|
||||
},
|
||||
"nixos-wsl": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_3",
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760536587,
|
||||
"narHash": "sha256-wfWqt+igns/VazjPLkyb4Z/wpn4v+XIjUeI3xY/1ENg=",
|
||||
"lastModified": 1764072830,
|
||||
"narHash": "sha256-ezkjlUCohD9o9c47Ey0/I4CamSS0QEORTqGvyGqMud0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "NixOS-WSL",
|
||||
"rev": "f98ee1de1fa36eca63c67b600f5d617e184e82ea",
|
||||
"rev": "c7832dd786175e20f2697179e0e03efadffe4201",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -329,11 +223,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1760878510,
|
||||
"narHash": "sha256-K5Osef2qexezUfs0alLvZ7nQFTGS9DL2oTVsIXsqLgs=",
|
||||
"lastModified": 1764517877,
|
||||
"narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5e2a59a5b1a82f89f2c7e598302a9cacebb72a67",
|
||||
"rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -342,6 +236,26 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"noctalia": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764582162,
|
||||
"narHash": "sha256-bqnw//qOLtDh6apLcIpgrcUK0dNRiDThLc2+kHeF98w=",
|
||||
"owner": "zoriya",
|
||||
"repo": "noctalia-shell",
|
||||
"rev": "93dd3ee2a362834ed4f3bdb5525d4b8304350af7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "zoriya",
|
||||
"repo": "noctalia-shell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
@@ -353,6 +267,7 @@
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixos-wsl": "nixos-wsl",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"noctalia": "noctalia",
|
||||
"tmux": "tmux",
|
||||
"zen-browser": "zen-browser"
|
||||
}
|
||||
@@ -360,11 +275,11 @@
|
||||
"tmux": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1760950867,
|
||||
"narHash": "sha256-81CIlZt+eG4m4HqVQgSbEjCHexF+1+QvaK86HOms1LQ=",
|
||||
"lastModified": 1764577332,
|
||||
"narHash": "sha256-v7GmXzUZpXh88+GptfUZ8JBm6SH2MYvcmURjJduAvyw=",
|
||||
"owner": "tmux",
|
||||
"repo": "tmux",
|
||||
"rev": "35ad72e56ffb2e8e09a2e2ac59bee0912fe45c6c",
|
||||
"rev": "b2d6ebaa1067ddc7aa7b760318faa24822c77f8c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -373,27 +288,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"neovim-nightly",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760945191,
|
||||
"narHash": "sha256-ZRVs8UqikBa4Ki3X4KCnMBtBW0ux1DaT35tgsnB1jM4=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "f56b1934f5f8fcab8deb5d38d42fd692632b47c2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zen-browser": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -401,11 +295,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759982773,
|
||||
"narHash": "sha256-HlTQoXRytul3jjek7vRV0Qk7voDB3Fy8RSIzDSvHIAQ=",
|
||||
"lastModified": 1764476124,
|
||||
"narHash": "sha256-QHvDAJdc7Sx8rIUpEjZYtn9n334F0Rg/8C/NcUWv7M4=",
|
||||
"owner": "youwen5",
|
||||
"repo": "zen-browser-flake",
|
||||
"rev": "f2f8aff94529e763665b807bad23396aed9d1fe8",
|
||||
"rev": "0f07eb42107d2b82d8a2e94d3c01aa3d7f500733",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
30
flake.nix
30
flake.nix
@@ -28,6 +28,10 @@
|
||||
url = "github:youwen5/zen-browser-flake";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
noctalia = {
|
||||
url = "github:zoriya/noctalia-shell";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
# use tmux's master for mode 2031
|
||||
tmux = {
|
||||
url = "github:tmux/tmux";
|
||||
@@ -62,6 +66,14 @@
|
||||
env = "niri";
|
||||
custom = [
|
||||
nixos-hardware.nixosModules.tuxedo-infinitybook-pro14-gen7
|
||||
{
|
||||
services.sshd.enable = true;
|
||||
}
|
||||
];
|
||||
customHome = [
|
||||
({pkgs, ...}: {
|
||||
home.packages = with pkgs; [slack];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
@@ -69,20 +81,6 @@
|
||||
env = "server";
|
||||
};
|
||||
|
||||
nixosConfigurations.kadan = mkSystem "kadan" {
|
||||
env = "server";
|
||||
custom = [
|
||||
({pkgs, ...}: {
|
||||
environment.systemPackages = with pkgs; [
|
||||
python3Packages.guessit
|
||||
mediainfo
|
||||
yt-dlp
|
||||
mkvtoolnix-cli
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
nixosConfigurations.virtual = mkSystem "virtual" {
|
||||
env = "niri";
|
||||
};
|
||||
@@ -160,8 +158,8 @@
|
||||
};
|
||||
in rec {
|
||||
default = pkgs.mkShell {
|
||||
inputsFrom = [nvim-lua];
|
||||
packages = with pkgs; [go-task];
|
||||
inputsFrom = [nvim-lua];
|
||||
packages = with pkgs; [go-task];
|
||||
};
|
||||
nvim-lua = pkgs.mkShell {
|
||||
name = "nvim-lua";
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod"];
|
||||
boot.kernelModules = ["kvm-intel" "coretemp" "nct6775"];
|
||||
boot.extraModulePackages = [config.boot.kernelPackages.nvidia_x11];
|
||||
boot.blacklistedKernelModules = ["nouveau"];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = ["size=2G" "mode=755"];
|
||||
};
|
||||
|
||||
fileSystems."/tmp" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = ["size=8G" "mode=755"];
|
||||
};
|
||||
|
||||
fileSystems."/nix" = {
|
||||
device = "/dev/disk/by-label/kadan";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-label/boot";
|
||||
fsType = "vfat";
|
||||
};
|
||||
|
||||
fileSystems."/mnt/a" = {
|
||||
device = "/dev/disk/by-label/a";
|
||||
fsType = "ext4";
|
||||
};
|
||||
fileSystems."/mnt/b" = {
|
||||
device = "/dev/disk/by-label/b";
|
||||
fsType = "ext4";
|
||||
};
|
||||
fileSystems."/mnt/c" = {
|
||||
device = "/dev/disk/by-label/c";
|
||||
fsType = "ext4";
|
||||
};
|
||||
fileSystems."/mnt/parity" = {
|
||||
device = "/dev/disk/by-label/parity";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/nix/persist/var/cache/swapfile";
|
||||
size = 4 * 1024;
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
environment.systemPackages = with pkgs; [mergerfs];
|
||||
fileSystems."/mnt/kyoo" = {
|
||||
device = "/mnt/a:/mnt/b:/mnt/c";
|
||||
depends = ["/mnt/a" "/mnt/b" "/mnt/c"];
|
||||
fsType = "fuse.mergerfs";
|
||||
options = [
|
||||
"func.getattr=newest" # For kyoo's scanner
|
||||
"cache.files=partial" # To enable mmap (used by rtorrent)
|
||||
"dropcacheonclose=true"
|
||||
"category.create=mfs"
|
||||
];
|
||||
};
|
||||
|
||||
services.snapraid = {
|
||||
enable = true;
|
||||
exclude = [
|
||||
"*.unrecoverable"
|
||||
"/tmp/"
|
||||
"/lost+found/"
|
||||
];
|
||||
dataDisks = {
|
||||
a = "/mnt/a/";
|
||||
b = "/mnt/b/";
|
||||
c = "/mnt/c/";
|
||||
};
|
||||
contentFiles = [
|
||||
"/var/snapraid.content"
|
||||
"/mnt/a/snapraid.content"
|
||||
"/mnt/b/snapraid.content"
|
||||
"/mnt/c/snapraid.content"
|
||||
];
|
||||
parityFiles = [
|
||||
"/mnt/parity/snapraid.parity"
|
||||
];
|
||||
};
|
||||
|
||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||
# still possible to use this option, but it's recommended to use it in conjunction
|
||||
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
#networking.interfaces.eno1.useDHCP = lib.mkDefault true;
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
enable32Bit = true;
|
||||
extraPackages = with pkgs; [vaapiVdpau];
|
||||
};
|
||||
|
||||
# Load nvidia driver for Xorg and Wayland
|
||||
services.xserver.videoDrivers = ["nvidia"];
|
||||
hardware.nvidia-container-toolkit.enable = true;
|
||||
|
||||
hardware.nvidia = {
|
||||
# Modesetting is required.
|
||||
modesetting.enable = true;
|
||||
|
||||
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
|
||||
powerManagement.enable = true;
|
||||
# Fine-grained power management. Turns off GPU when not in use.
|
||||
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
|
||||
powerManagement.finegrained = false;
|
||||
|
||||
# Use the NVidia open source kernel module (not to be confused with the
|
||||
# independent third-party "nouveau" open source driver).
|
||||
# Support is limited to the Turing and later architectures. Full list of
|
||||
# supported GPUs is at:
|
||||
# https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus
|
||||
# Only available from driver 515.43.04+
|
||||
# Do not disable this unless your GPU is unsupported or if you have a good reason to.
|
||||
open = false;
|
||||
|
||||
# Enable the Nvidia settings menu,
|
||||
# accessible via `nvidia-settings`.
|
||||
nvidiaSettings = true;
|
||||
|
||||
# Optionally, you may need to select the appropriate driver version for your specific GPU.
|
||||
package = config.boot.kernelPackages.nvidiaPackages.stable;
|
||||
};
|
||||
|
||||
system.stateVersion = "23.05";
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
{ lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
@@ -26,7 +23,7 @@
|
||||
};
|
||||
|
||||
fileSystems."/nix" =
|
||||
{ device = "/dev/disk/by-label/virtual";
|
||||
{ device = "/dev/disk/by-label/nix";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
|
||||
@@ -48,10 +48,10 @@ in
|
||||
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGcLP/ZEjnSgkzQMBeLLOWn5uejSr9Gg1h9PJZECVTLm+VDQ7KyI3ORZt+qbfEnsnGL73iwcAqB5Upy9Cdj0182mnrTk2ZViNMeFT7kLBF0yXpiajQTtMjENYj0nbNWpQ5+sJrtJKKYK/tBghW8PyTrJPpVQcrLcf4D66U5DkkJNRDeu4v9SjHKaASUeyia4gRSVV59Ugtrl0lz8sl4yBSL4957zwzdkNR0pVmftaKmUP4KfBvpNcFOOpHcdvzDtEPQs8j0g2l65YOQNNFSMsYQfxt1X4zmEi4unRIlECglaPz12CyoTiM2xmCWa/mS5nm0dR1VbEHFMRtGbbgm9MwedXoxYAfycbu08fqi1AAvg7MQxDNLfWWBIHe7+imGLKrVkqk8B89I409iI4YiOytnUkxKZkxynqVYtEE0bx5J15mniq2vJTw9JD89qSVkvGjZNGuJgh4leIlxPGj4iP8KY3N3Ifaf72PsmmwW4rB5JPDW93RL1DZV8lk3NgyF8M= zoriya@fuhen" # laptop
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCpQ8Td98YIS0EtVQ7xabYVe9A9/+ZECrHBpKi01NKQ0Mleg9Z4fnTsdGFX1uhbG6Pu7niBVzYReVTC1CbyVWKmm/4DbbRpaqY94eOzQEe0p4wMSURQ9weuB5737k+5MuLDLUbhc1ytDa84Ubj/A/rQUueKdq2K1o+YSN7b7HKe7kXoXACEpbrSCC43mteBgCtvgsLY0New9xXnvGFJPSe7PcjYkOhSJB1xA0Gu4DoDdOyErvV62QQH4sSQMu5cFICJGfdXQzBdshA8MgWKXFv3Hq7K5/GGDNyCsMxeoPQET3vbmgUsE+KGtcdqizdFM3bAfCBGXOBx6h7BoNuQzkp8hgmrq62CmMwF0krX05Sb3qR/wVjRKDo9pYuSk6/awnnBp5kY6sNgEruI93ZXNQWMkxXQNbmpCi+uEvzMveP16O/uP3NxklD4wtmfpSZsxBi+jRGFqcjdy3Qlc13Tiz98EBaXkir9YwUAh8SNs3gRaJGI0Fn2HzUCPH0zNh42EY8= zoriya@kadan" # server
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJ/c2rQ9xUI6XpDR/+dmCK5IcxkOIezvNtbC2EVTrfh73H5juotME6JrQSxgQjtgsaUAzZzrac9kI/7Do8/lisbofdKRcneXi2UEeERKrKEwC/EGcQgqnoPLL1+mnqwvQ923d3105DV4hFksoDbblCinFuUr5s55EMm991IL/T70cy820AOgAf+hgleM1Its47EBkZBzpa4KwxYepJG0+kBa7K1Loi9QgBvTGpxs7rWMDxllfL6ivrWJxAKRZdWlJ/MKBVQIYhv0W+vaQ7OZA1qUY4bq/9wY/i88nixbVSPJmikj0+QNeLksU78bOIxLpTTeLdH4HQ6+qKOBT3JhEpBtUHdBxOT5tYJTr4qwjevlFqceLw3x1V9URxPS2XBDjlxnzYzdnD40LK5BehXdmElGio9dy98/qJINbDW/7AH+BpP1GWNKVhiYXPj7A/2fkFD2K7DgIgGlsrZthS+LxDTEcQ8Yx0iD/+nI8LcnvU42S0muSvmP7LE4xBl8AoaI0= zoriya@nixos" # lucca windows
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/TujCSbUueF4p3wbzImPkEvgJjshDfh2sb/bwGdaRN" # bitwarden
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBzfKjYeQ80s/M+qEKCxBhseJjLa2OwBk9ZrHeku90Vg zoriya@kujima" # android
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq7uaYigZWAhk/EIn9SHd6mhWJHtYPRFCUlpYwGSG6SDW2Ef0epsYJL2hBuMhhmhOvmpzzKiP83hZjNXchq8u7xcAhLsCPdPXhrQ4kOScJaeLuoxyY/7wiOqTSvSwz/N2s9tdNNcOLLUAset1Kvyp1OPBkEuXbIFfRGGqwAwcS2YYimlHf9mpcg/tZujBYQHIetHQkMaO+P0h+vMjVHBBcFLcsao+1QsVjoQnZOE96QTf2Oc66SxDBXnyS+1y1OnlWAEslSDL9AXVl6wF4O1JBWcsKNk+X4ShaaJMFRiPJEDQKSqRMKXWflkdFe1vNQ0bSiA6TLeH1lPeT8PXye2sUhu/DhpEswB7sV9YPpiP216QGsVM/2MepjCRq2sdr4EZ/17tubYdNcO8PkhVlQWOhHWXvIwIrvAzOVG1l9laAL1mxAY2iQLLN/gHGFPuiLcZCBem2LrDLQ7Ny+rDYQe4jZMEX4NYzNgNbHPk1ecwj/fqeLM4Qn+TkQgUL/kSM3ZU= zroux@zroux-mac" # lucca mac
|
||||
];
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{pkgs, ...}: {
|
||||
{pkgs, lib, ...}: {
|
||||
imports = [
|
||||
./nix/nix.nix
|
||||
];
|
||||
@@ -40,4 +40,24 @@
|
||||
StandardErrorPath = "/tmp/caffeinate.err";
|
||||
};
|
||||
};
|
||||
|
||||
launchd.user.agents.ssh-tunnel = let
|
||||
ssh-tunnel = pkgs.writeShellScriptBin "ssh-tunnel" ''
|
||||
while true; do
|
||||
dns-sd -m -Q fuhen.local
|
||||
echo "Host found, starting tunnel" | tee /dev/stderr
|
||||
ssh -NR "2222:localhost:22" zoriya@fuhen.local
|
||||
echo "Connetion closed" | tee /dev/stderr
|
||||
sleep 5
|
||||
done
|
||||
'';
|
||||
in {
|
||||
command = lib.getExe ssh-tunnel;
|
||||
serviceConfig = {
|
||||
KeepAlive = true;
|
||||
RunAtLoad = true;
|
||||
StandardOutPath = "/tmp/ssh-tunnel.log";
|
||||
StandardErrorPath = "/tmp/ssh-tunnel.err";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,13 @@
|
||||
tmux
|
||||
jq
|
||||
mosh
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/issues/463295 (telepresence needs a system wide iptables)
|
||||
iptables
|
||||
telepresence2
|
||||
];
|
||||
# also needed for telepresence
|
||||
programs.fuse.userAllowOther = true;
|
||||
|
||||
programs.zsh.enable = true;
|
||||
environment.shells = with pkgs; [zsh];
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
".zen"
|
||||
".config/google-chrome"
|
||||
".config/discord"
|
||||
".config/Slack"
|
||||
".config/vesktop"
|
||||
".config/YouTube\ Music"
|
||||
".config/gh"
|
||||
|
||||
@@ -21,8 +21,14 @@ fi
|
||||
current_session=$(tmux display-message -p "#S")
|
||||
|
||||
if ! tmux has-session "-t=$selected_name" 2> /dev/null; then
|
||||
tmux new-session -ds "$selected_name" -c "$selected" -e "CMD=$EDITOR ."
|
||||
tmux new-window -dt "$selected_name:1" -c "$selected" -e "CMD="
|
||||
if [[ "$selected" == "$HOME/work/new" ]]; then
|
||||
selected_name="work"
|
||||
ssh_tunnel="ssh zroux@localhost -p 2222 -D 6666"
|
||||
tmux new-session -ds "$selected_name" -c "$selected" -e "CMD=$ssh_tunnel"
|
||||
else
|
||||
tmux new-session -ds "$selected_name" -c "$selected" -e "CMD=$EDITOR ."
|
||||
tmux new-window -dt "$selected_name:1" -c "$selected" -e "CMD="
|
||||
fi
|
||||
fi
|
||||
|
||||
tmux switch-client -t "$selected_name"
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
''
|
||||
set -g status off
|
||||
set -s set-clipboard on
|
||||
# request clipboard from parent terminal instead of using tmux's internal buffer
|
||||
set -s get-clipboard request
|
||||
set -g extended-keys on
|
||||
|
||||
# from tmux-sensible
|
||||
@@ -54,8 +56,9 @@
|
||||
|
||||
# suspend inner tmux (to allow nested sessions)
|
||||
bind @ { set prefix None; set key-table off }
|
||||
# NOTE: C-@ doesn't work since v3.5a
|
||||
bind -T off C-@ { set -u prefix; set -u key-table }
|
||||
# NOTE: C-@ doesn't work since v3.5a (since tmux doesn't support kitty keyboard protocol)
|
||||
# Instead, we bind f11 and use kitty to remap C-@ to f11.
|
||||
bind -T off f11 { set -u prefix; set -u key-table }
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
kns = "kubens";
|
||||
knsc = "kubectl config set-context --current --namespace ''";
|
||||
kg = "k get";
|
||||
kga = "kg $(k api-resources --verbs=list --namespaced -o name | paste -sd ,)";
|
||||
kga = "kg $(k api-resources --verbs=list --namespaced -o name | grep -Ev 'event|^endpoints$' | paste -sd ,)";
|
||||
# use custom function to decode secrets data instead of a simple alias for kgy
|
||||
# kgy = "k get -o yaml";
|
||||
kgw = "k get -w";
|
||||
@@ -89,9 +89,10 @@
|
||||
dr = "direnv reload";
|
||||
nixos-option = "nixos-option --flake ~/projects/flake";
|
||||
# i will never remember those flags
|
||||
ss = "ss -tlpun";
|
||||
# habits
|
||||
copyfile = "clipcopy";
|
||||
ss =
|
||||
if pkgs.stdenv.isLinux
|
||||
then "ss -tlpun"
|
||||
else "netstat -anvp tcp | awk 'NR<3 || /LISTEN/'";
|
||||
|
||||
# viu doesn't work with tmux, icat does. using that while waiting
|
||||
viu = "kitty +kitten icat";
|
||||
@@ -125,16 +126,6 @@
|
||||
src = pkgs.oh-my-zsh;
|
||||
file = "share/oh-my-zsh/plugins/git/git.plugin.zsh";
|
||||
}
|
||||
{
|
||||
name = "clipcopy"; # dependency of copypath & copyfile
|
||||
src = pkgs.oh-my-zsh;
|
||||
file = "share/oh-my-zsh/lib/clipboard.zsh";
|
||||
}
|
||||
{
|
||||
name = "copypath";
|
||||
src = pkgs.oh-my-zsh;
|
||||
file = "share/oh-my-zsh/plugins/copypath/copypath.plugin.zsh";
|
||||
}
|
||||
];
|
||||
completionInit =
|
||||
#bash
|
||||
@@ -359,6 +350,7 @@
|
||||
usql
|
||||
rsync
|
||||
moreutils
|
||||
osc
|
||||
# bitwarden-cli
|
||||
]
|
||||
++ lib.optionals pkgs.stdenv.isLinux [
|
||||
|
||||
@@ -108,3 +108,10 @@ _kgy() {
|
||||
_kubectl
|
||||
}
|
||||
compdef _kgy kgy
|
||||
|
||||
alias copyfile="osc copy"
|
||||
copypath() {
|
||||
local file="${1:-.}"
|
||||
[[ $file = /* ]] || file="$PWD/$file"
|
||||
print -n "${file:a}" | osc copy || return 1
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ zmodload zsh/stat
|
||||
timer_preexec() {
|
||||
timer=$EPOCHREALTIME
|
||||
}
|
||||
add-zsh-hook preexec timer_precmd
|
||||
add-zsh-hook preexec timer_preexec
|
||||
timer_precmd() {
|
||||
if [ -z $timer ]; then
|
||||
EXEC_TIME=""
|
||||
@@ -39,7 +39,7 @@ kube_precmd() {
|
||||
add-zsh-hook precmd kube_precmd
|
||||
|
||||
|
||||
FILL="%F{#808080}${(l.$COLUMNS..·.)}%f"
|
||||
FILL='%F{#808080}${(l.$COLUMNS..·.)}%f'
|
||||
NEWLINE=$'\n'
|
||||
|
||||
WORKDIR='%B%F{blue}%~%b%f'
|
||||
@@ -50,8 +50,8 @@ KUBE='%F{cyan}$KCTX${KNS:+/$KNS}%f'
|
||||
PROMPT_SHLVL='%(?.%F{green}.%F{red})$(printf "❯%.0s" {1..$SHLVL})%f'
|
||||
|
||||
EXEC_TIME=""
|
||||
EXIT_CODE=' %(?..%F{red}x${(j[|])pipestatus}%f)'
|
||||
JOBS=' %F{cyan}%(1j.&%j.)%f'
|
||||
EXIT_CODE='%(?.. %F{red}x${(j[|])pipestatus}%f)'
|
||||
JOBS='%F{cyan}%(1j. &%j.)%f'
|
||||
|
||||
export PROMPT="${FILL}${NEWLINE}${WORKDIR}$RO$GIT $KUBE $PROMPT_SHLVL "
|
||||
export RPROMPT="\${EXEC_TIME}${EXIT_CODE}${JOBS}"
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
qt = {
|
||||
enable = true;
|
||||
platformTheme.name = "gtk";
|
||||
};
|
||||
qt.enable = true;
|
||||
|
||||
home.pointerCursor = {
|
||||
gtk.enable = true;
|
||||
|
||||
@@ -20,7 +20,7 @@ in {
|
||||
home.packages = with pkgs;
|
||||
[
|
||||
firefox
|
||||
zen-browser.packages.${pkgs.system}.default
|
||||
zen-browser.packages.${pkgs.stdenv.hostPlatform.system}.default
|
||||
vesktop
|
||||
freecad
|
||||
kicad
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
map ctrl+minus change_font_size current -1.0
|
||||
map ctrl+0 change_font_size current 0
|
||||
map ctrl+backspace change_font_size current 0
|
||||
|
||||
# this is used to map
|
||||
map ctrl+@ send_key f11
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
4
modules/wm/ags/.gitignore
vendored
4
modules/wm/ags/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
weather_key
|
||||
settings.json
|
||||
@@ -1,48 +0,0 @@
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Gdk from "gi://Gdk";
|
||||
|
||||
import { Bar } from "./layouts/bar.js";
|
||||
import { Notifications } from "./layouts/notifications.js";
|
||||
import { OSD } from "./layouts/osd.js";
|
||||
import { Quicksettings } from "./layouts/quicksettings.js";
|
||||
|
||||
/**
|
||||
* @param {Array<(monitor: number) => Gtk.Window>} widgets
|
||||
*/
|
||||
export function forMonitors(widgets) {
|
||||
const display = Gdk.Display.get_default();
|
||||
|
||||
display?.connect("monitor-added", (disp, gdkmonitor) => {
|
||||
let monitor = 0;
|
||||
for (let i = 0; i < display.get_n_monitors(); i++) {
|
||||
if (gdkmonitor === display.get_monitor(i)) {
|
||||
monitor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
widgets.forEach((win) => App.addWindow(win(monitor)));
|
||||
});
|
||||
|
||||
display?.connect("monitor-removed", (disp, monitor) => {
|
||||
App.windows.forEach((win) => {
|
||||
// @ts-ignore
|
||||
if (win.gdkmonitor === monitor) App.removeWindow(win);
|
||||
});
|
||||
});
|
||||
|
||||
const n = display?.get_n_monitors() || 1;
|
||||
return Array.from({ length: n }, (_, i) => i).flatMap((mon) =>
|
||||
widgets.map((x) => x(mon)),
|
||||
);
|
||||
}
|
||||
|
||||
App.config({
|
||||
closeWindowDelay: {
|
||||
quicksettings: 300,
|
||||
notifications: 200,
|
||||
osd: 300,
|
||||
},
|
||||
style: `${App.configDir}/style.css`,
|
||||
windows: [...forMonitors([Bar]), Quicksettings(), Notifications(), OSD()],
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
from material_color_utilities_python import *
|
||||
from PIL import Image
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
|
||||
CACHE_DIR = os.path.expanduser("~/.cache/ags/tempcolors")
|
||||
|
||||
def cache_and_get_colors(file_path):
|
||||
file_hash = hashlib.sha256(file_path.encode('utf-8')).hexdigest()
|
||||
cache_path = os.path.join(CACHE_DIR, file_hash[:2], file_hash[2:])
|
||||
|
||||
os.makedirs(cache_path, exist_ok=True)
|
||||
|
||||
colors_path = os.path.join(cache_path, "colors.json")
|
||||
if os.path.exists(colors_path):
|
||||
with open(colors_path, "r") as f:
|
||||
parsed_colors = json.load(f)
|
||||
elif not os.path.exists(file_path):
|
||||
return None
|
||||
img = Image.open(file_path)
|
||||
basewidth = 8
|
||||
wpercent = (basewidth/float(img.size[0]))
|
||||
hsize = int((float(img.size[1])*float(wpercent)))
|
||||
img = img.resize((basewidth, hsize), Image.Resampling.LANCZOS)
|
||||
theme = themeFromImage(img)
|
||||
|
||||
scheme = theme.get("schemes")
|
||||
|
||||
parsed_colors = {
|
||||
"primary": hexFromArgb(scheme.get("dark").primary),
|
||||
"onPrimary": hexFromArgb(scheme.get("dark").onPrimary),
|
||||
"background": hexFromArgb(scheme.get("dark").background),
|
||||
"onBackground": hexFromArgb(scheme.get("dark").onBackground),
|
||||
}
|
||||
|
||||
with open(colors_path, "w") as f:
|
||||
json.dump(parsed_colors, f)
|
||||
|
||||
return parsed_colors
|
||||
|
||||
fn = sys.argv[1]
|
||||
|
||||
parsed_colors = cache_and_get_colors(fn)
|
||||
|
||||
print(json.dumps(parsed_colors, indent=4))
|
||||
@@ -1,48 +0,0 @@
|
||||
{pkgs, ...}: let
|
||||
covercolors = pkgs.stdenv.mkDerivation {
|
||||
name = "covercolors";
|
||||
dontUnpack = true;
|
||||
propagatedBuildInputs = [
|
||||
(pkgs.python3.withPackages (pyPkgs:
|
||||
with pyPkgs; [
|
||||
material-color-utilities
|
||||
pillow
|
||||
]))
|
||||
];
|
||||
installPhase = "install -Dm755 ${./covercolors.py} $out/bin/covercolors";
|
||||
};
|
||||
# systemdTarget = "graphical-session.target";
|
||||
ags = pkgs.ags_1.overrideAttrs (_: prev: {
|
||||
buildInputs =
|
||||
prev.buildInputs
|
||||
++ [
|
||||
pkgs.libdbusmenu-gtk3
|
||||
];
|
||||
});
|
||||
in {
|
||||
home.packages = with pkgs; [
|
||||
ags
|
||||
# TODO: Find a way to add this for ags only
|
||||
covercolors
|
||||
brightnessctl
|
||||
blueberry
|
||||
];
|
||||
# systemd.user.services.ags = {
|
||||
# Unit = {
|
||||
# Description = " A customizable and extensible shell ";
|
||||
# PartOf = systemdTarget;
|
||||
# Requires = systemdTarget;
|
||||
# After = systemdTarget;
|
||||
# };
|
||||
#
|
||||
# Service = {
|
||||
# Type = "simple";
|
||||
# ExecStart = "${ags}/bin/ags";
|
||||
# Restart = "always";
|
||||
# };
|
||||
#
|
||||
# Install = {WantedBy = [systemdTarget];};
|
||||
# };
|
||||
|
||||
xdg.configFile."ags".source = ./.;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import { Clock } from "../modules/clock.js";
|
||||
import * as wm from "../modules/wm.js";
|
||||
import * as audio from "../modules/audio.js";
|
||||
import * as network from "../modules/network.js";
|
||||
import * as bluetooth from "../modules/bluetooth.js";
|
||||
import * as battery from "../modules/battery.js";
|
||||
import * as notifications from "../modules/notifications.js";
|
||||
import * as mpris from "../modules/mpris.js";
|
||||
|
||||
/**
|
||||
*@param {number} monitor
|
||||
*/
|
||||
export const Bar = (monitor) =>
|
||||
Widget.Window({
|
||||
monitor,
|
||||
name: `bar${monitor}`,
|
||||
className: "transparent",
|
||||
exclusivity: "exclusive",
|
||||
anchor: ["top", "left", "right"],
|
||||
layer: "bottom",
|
||||
child: Widget.CenterBox({
|
||||
// startWidget: Widget.Box({
|
||||
// children: [
|
||||
// wm.Tags({
|
||||
// monitor,
|
||||
// labels: ["一", "二", "三", "四", "五", "六", "七", "八", "九"],
|
||||
// }),
|
||||
// wm.Layout({ monitor }),
|
||||
// wm.ClientLabel({ monitor }),
|
||||
// ],
|
||||
// }),
|
||||
centerWidget: Widget.Box({
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Button({
|
||||
css: "min-width: 200px;",
|
||||
onClicked: () => App.toggleWindow("notifications"),
|
||||
child: notifications.Indicator({
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
endWidget: Widget.Box({
|
||||
hpack: "end",
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: "module",
|
||||
css: "margin-right: 48px",
|
||||
visible: mpris.activePlayer.bind().as((x) => !!x),
|
||||
children: mpris.activePlayer
|
||||
.bind()
|
||||
.as((player) => (player ? [mpris.LinePlayer({ player })] : [])),
|
||||
}),
|
||||
Widget.Button({
|
||||
onClicked: () => App.toggleWindow("quicksettings"),
|
||||
className: "module quicksettings",
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
audio.MicrophoneIndicator({
|
||||
className: "qs-item",
|
||||
}),
|
||||
notifications.DNDIndicator({
|
||||
className: "qs-item",
|
||||
}),
|
||||
network.Indicator({ className: "qs-item" }),
|
||||
audio.VolumeIndicator({ className: "qs-item" }),
|
||||
bluetooth.Indicator({
|
||||
hideIfDisabled: true,
|
||||
className: "qs-item",
|
||||
}),
|
||||
battery.Indicator({ className: "qs-item" }),
|
||||
],
|
||||
}),
|
||||
}).hook(App, (self, win, visible) => {
|
||||
self.toggleClassName("active", win === "quicksettings" && visible);
|
||||
}),
|
||||
Clock({ format: "%a %d %b", className: "module bold" }),
|
||||
Clock({
|
||||
format: "%H:%M",
|
||||
className: "module accent bold",
|
||||
css: "margin-right: 0px",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import PopupWindow from "../misc/popup.js";
|
||||
import * as notifications from "../modules/notifications.js";
|
||||
|
||||
const notificationsService = await Service.import("notifications");
|
||||
|
||||
const Header = () =>
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
css: "margin-bottom: 40px",
|
||||
children: [
|
||||
notifications.DNDToggle({
|
||||
className: "surface p10 round",
|
||||
css: `
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
`,
|
||||
hpack: "start",
|
||||
hexpand: true,
|
||||
}),
|
||||
notifications.ClearButton({
|
||||
hpack: "end",
|
||||
hexpand: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const Notifications = () =>
|
||||
PopupWindow({
|
||||
name: "notifications",
|
||||
exclusivity: "exclusive",
|
||||
transition: "slide_down",
|
||||
layout: "top-center",
|
||||
duration: 300,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
className: "bgcont",
|
||||
css: `
|
||||
min-width: 600px;
|
||||
margin: 10px;
|
||||
padding: 12px;
|
||||
border-radius: 20px;
|
||||
`,
|
||||
children:
|
||||
/** @type {any} */
|
||||
(
|
||||
notificationsService
|
||||
.bind("notifications")
|
||||
.as((x) =>
|
||||
x.length > 0
|
||||
? [Header(), notifications.List({})]
|
||||
: [Header(), notifications.Placeholder({})],
|
||||
)
|
||||
),
|
||||
}),
|
||||
});
|
||||
@@ -1,85 +0,0 @@
|
||||
import { getIcon } from "../modules/audio.js";
|
||||
import brightness from "../services/brightness.js";
|
||||
|
||||
const audio = await Service.import("audio");
|
||||
|
||||
const DELAY = 1000;
|
||||
|
||||
function OnScreenProgress() {
|
||||
const indicator = Widget.Icon({
|
||||
vpack: "start",
|
||||
hpack: "center",
|
||||
size: 30,
|
||||
css: "padding-right: 12px;",
|
||||
});
|
||||
const progress = Widget.Slider({
|
||||
drawValue: false,
|
||||
hexpand: true,
|
||||
});
|
||||
const revealer = Widget.Revealer({
|
||||
transition: "crossfade",
|
||||
css: "opacity: 0",
|
||||
revealChild: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
child: Widget.Box({
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
className: "osd bgcount",
|
||||
css: "padding: 20px;",
|
||||
children: [indicator, progress],
|
||||
}),
|
||||
});
|
||||
// Prevent OSD to be shown when starting ags.
|
||||
Utils.timeout(DELAY * 2, () => {
|
||||
revealer.css = "opacity: 1";
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {string} icon
|
||||
*/
|
||||
function show(value, icon) {
|
||||
revealer.reveal_child = true;
|
||||
indicator.icon = icon;
|
||||
progress.value = value;
|
||||
count++;
|
||||
Utils.timeout(DELAY, () => {
|
||||
count--;
|
||||
if (count === 0) revealer.reveal_child = false;
|
||||
});
|
||||
}
|
||||
return revealer
|
||||
.hook(
|
||||
brightness,
|
||||
() => show(brightness.screen, "display-brightness-symbolic"),
|
||||
"notify::screen",
|
||||
)
|
||||
.hook(
|
||||
audio.speaker,
|
||||
() => show(audio.speaker.volume, getIcon(audio.speaker.volume * 100)),
|
||||
"notify::volume",
|
||||
)
|
||||
.hook(
|
||||
audio.speaker,
|
||||
() =>
|
||||
show(
|
||||
audio.speaker.is_muted ? 0 : audio.speaker.volume,
|
||||
audio.speaker.is_muted
|
||||
? "audio-volume-muted-symbolic"
|
||||
: getIcon(audio.speaker.volume * 100),
|
||||
),
|
||||
"notify::is-muted",
|
||||
);
|
||||
}
|
||||
|
||||
export const OSD = () =>
|
||||
Widget.Window({
|
||||
name: "osd",
|
||||
className: "indicator",
|
||||
layer: "overlay",
|
||||
clickThrough: true,
|
||||
anchor: ["bottom"],
|
||||
child: OnScreenProgress(),
|
||||
});
|
||||
@@ -1,160 +0,0 @@
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import * as audio from "../modules/audio.js";
|
||||
import * as brightness from "../modules/brightness.js";
|
||||
import * as network from "../modules/network.js";
|
||||
import * as bluetooth from "../modules/bluetooth.js";
|
||||
import * as darkmode from "../modules/darkmode.js";
|
||||
import * as powerprofile from "../modules/powerprofile.js";
|
||||
// import * as nightmode from "../modules/nightmode.js";
|
||||
import * as mpris from "../modules/mpris.js";
|
||||
import * as systray from "../modules/systray.js";
|
||||
import PopupWindow from "../misc/popup.js";
|
||||
import { opened, Menu } from "../misc/menu.js";
|
||||
|
||||
const mprisService = await Service.import("mpris");
|
||||
|
||||
/**
|
||||
* @param {Array<Gtk.Widget>} toggles
|
||||
* @param {Array<Gtk.Widget>} menus
|
||||
*/
|
||||
const Row = (toggles = [], menus = []) =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
homogeneous: true,
|
||||
children: toggles,
|
||||
}),
|
||||
...menus,
|
||||
],
|
||||
});
|
||||
|
||||
const Header = () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
css: "margin-bottom: 12px",
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: "avatar",
|
||||
css: `background-image: url("/home/${Utils.USER}/.face");`,
|
||||
}),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Button({
|
||||
child: Widget.Icon("emblem-system-symbolic"),
|
||||
onClicked: () => {
|
||||
Utils.execAsync("gnome-control-center");
|
||||
App.closeWindow("quicksettings");
|
||||
},
|
||||
vpack: "center",
|
||||
className: "surface sys-button",
|
||||
}),
|
||||
Widget.Button({
|
||||
child: Widget.Icon("system-log-out-symbolic"),
|
||||
onClicked: () => {
|
||||
opened.value = opened.value === "sleep" ? "" : "sleep";
|
||||
},
|
||||
vpack: "center",
|
||||
css: "margin: 12px",
|
||||
className: "surface sys-button",
|
||||
}),
|
||||
Widget.Button({
|
||||
child: Widget.Icon("system-shutdown-symbolic"),
|
||||
onClicked: () => {
|
||||
opened.value = opened.value === "shutdown" ? "" : "shutdown";
|
||||
},
|
||||
vpack: "center",
|
||||
className: "surface sys-button",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
VerificationMenu({
|
||||
name: "sleep",
|
||||
icon: "system-log-out-symbolic",
|
||||
title: "Hybernate?",
|
||||
command: "systemctl suspend --now",
|
||||
}),
|
||||
VerificationMenu({
|
||||
name: "shutdown",
|
||||
icon: "system-shutdown-symbolic",
|
||||
title: "Shutdown?",
|
||||
command: "shutdown now",
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {{
|
||||
* name: string,
|
||||
* icon: string,
|
||||
* title: string,
|
||||
* command: string,
|
||||
* }} props */
|
||||
const VerificationMenu = ({ name, icon, title, command }) =>
|
||||
Menu({
|
||||
name,
|
||||
icon: Widget.Icon(icon),
|
||||
title: title,
|
||||
content: [
|
||||
Widget.Button({
|
||||
onClicked: () => {
|
||||
opened.value = "";
|
||||
App.closeWindow("quicksettings");
|
||||
Utils.execAsync(command);
|
||||
},
|
||||
child: Widget.Label({
|
||||
label: "Yes",
|
||||
hpack: "start",
|
||||
css: "margin-left: 12px",
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
onClicked: () => {
|
||||
opened.value = "";
|
||||
},
|
||||
child: Widget.Label({
|
||||
label: "No",
|
||||
hpack: "start",
|
||||
css: "margin-left: 12px",
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const Quicksettings = () =>
|
||||
PopupWindow({
|
||||
name: "quicksettings",
|
||||
exclusivity: "exclusive",
|
||||
transition: "slide_down",
|
||||
layout: "top-right",
|
||||
duration: 300,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
className: "bgcont qs-container",
|
||||
children: [
|
||||
Header(),
|
||||
Row(
|
||||
[audio.Volume({ type: "speaker" })],
|
||||
[audio.SinkSelector({}), audio.AppMixer({})],
|
||||
),
|
||||
brightness.Brightness({}),
|
||||
Row(
|
||||
[network.Toggle({}), bluetooth.Toggle({})],
|
||||
[network.Selection({}), bluetooth.Selection({})],
|
||||
),
|
||||
Widget.Box({
|
||||
homogeneous: true,
|
||||
children: [darkmode.Toggle(), audio.MuteToggle({})],
|
||||
}),
|
||||
Row(
|
||||
[systray.Toggle({}), powerprofile.Toggle({})],
|
||||
[systray.Selection({}), powerprofile.Selection({})],
|
||||
),
|
||||
Widget.Box({
|
||||
children: mpris.activePlayer
|
||||
.bind()
|
||||
.as((player) => player ? [mpris.MprisPlayer({ player })] : []),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -1,166 +0,0 @@
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
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,186 +0,0 @@
|
||||
import GObject from "gi://GObject?version=2.0";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
|
||||
export const opened = Variable("");
|
||||
App.connect("window-toggled", (_, name, visible) => {
|
||||
if (name === "quicksettings" && !visible)
|
||||
Utils.timeout(500, () => {
|
||||
opened.value = "";
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* name: string,
|
||||
* activate?: false | (() => void),
|
||||
* } & import("../types/widgets/button").ButtonProps} props
|
||||
*/
|
||||
export const Arrow = ({ name, activate, ...props }) => {
|
||||
let deg = 0;
|
||||
let iconOpened = false;
|
||||
const icon = Widget.Icon("pan-end-symbolic").hook(opened, () => {
|
||||
if (
|
||||
(opened.value === name && !iconOpened) ||
|
||||
(opened.value !== name && iconOpened)
|
||||
) {
|
||||
const step = opened.value === name ? 10 : -10;
|
||||
iconOpened = !iconOpened;
|
||||
for (let i = 0; i < 9; ++i) {
|
||||
Utils.timeout(15 * i, () => {
|
||||
deg += step;
|
||||
icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return Widget.Button({
|
||||
child: icon,
|
||||
className: "qs-icon",
|
||||
onClicked: () => {
|
||||
opened.value = opened.value === name ? "" : name;
|
||||
if (typeof activate === "function") activate();
|
||||
},
|
||||
...props,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string,
|
||||
* icon: Gtk.Widget,
|
||||
* label: Gtk.Widget,
|
||||
* activate: () => void
|
||||
* deactivate: () => void
|
||||
* activateOnArrow?: boolean
|
||||
* connection: [GObject.Object, () => boolean]
|
||||
* } & import("../types/widgets/box").BoxProps} ArrowToggleButtonProps
|
||||
* @param {ArrowToggleButtonProps} props
|
||||
*/
|
||||
export const ArrowToggleButton = ({
|
||||
name,
|
||||
icon,
|
||||
label,
|
||||
activate,
|
||||
deactivate,
|
||||
activateOnArrow = true,
|
||||
connection: [service, condition],
|
||||
}) =>
|
||||
Widget.Box({
|
||||
className: "qs-button surface",
|
||||
setup: (self) =>
|
||||
self.hook(service, () => {
|
||||
self.toggleClassName("accent", condition());
|
||||
}),
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [icon, label],
|
||||
}),
|
||||
onClicked: () => {
|
||||
if (condition()) {
|
||||
deactivate();
|
||||
if (opened.value === name) opened.value = "";
|
||||
} else {
|
||||
activate();
|
||||
}
|
||||
},
|
||||
}),
|
||||
Arrow({ name, activate: activateOnArrow && activate }),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* icon: Gtk.Widget,
|
||||
* label: Gtk.Widget,
|
||||
* activate: () => void
|
||||
* deactivate: () => void
|
||||
* connection: [GObject.Object, () => boolean]
|
||||
* } & import("../types/widgets/box").BoxProps} SimpleToggleButtonProps
|
||||
* @param {SimpleToggleButtonProps} props
|
||||
*/
|
||||
export const SimpleToggleButton = ({
|
||||
icon,
|
||||
label,
|
||||
activate,
|
||||
deactivate,
|
||||
connection: [service, condition],
|
||||
}) =>
|
||||
Widget.Box({
|
||||
className: "qs-button surface",
|
||||
setup: (self) =>
|
||||
self.hook(service, () => {
|
||||
self.toggleClassName("accent", condition());
|
||||
}),
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [icon, label],
|
||||
}),
|
||||
onClicked: () => {
|
||||
if (condition()) {
|
||||
deactivate();
|
||||
} else {
|
||||
activate();
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string,
|
||||
* icon: Gtk.Widget,
|
||||
* title: string,
|
||||
* content: Gtk.Widget[],
|
||||
* } & import("../types/widgets/revealer").RevealerProps} MenuProps
|
||||
* @param {MenuProps} props
|
||||
*/
|
||||
export const Menu = ({ name, icon, title, content, ...props }) =>
|
||||
Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
reveal_child: opened.bind().as((v) => v === name),
|
||||
child: Widget.Box({
|
||||
className: "qs-submenu surface",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: "qs-sub-title accent",
|
||||
children: [
|
||||
icon,
|
||||
Widget.Label({
|
||||
className: "bold f16",
|
||||
truncate: "end",
|
||||
label: title,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
className: "qs-sub-content",
|
||||
children: content,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{type?: string, command?: string} & import("../types/widgets/button").ButtonProps} props */
|
||||
export const SettingsButton = ({ type, command, ...props }) =>
|
||||
Widget.Button({
|
||||
onClicked: () => {
|
||||
Utils.execAsync(command ?? `gnome-control-center ${type}`);
|
||||
App.closeWindow("quicksettings");
|
||||
},
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon("emblem-system-symbolic"),
|
||||
Widget.Label("Settings"),
|
||||
],
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
@@ -1,198 +0,0 @@
|
||||
// Stollen from https://github.com/Aylur/dotfiles/blob/main/ags/widget/PopupWindow.ts#
|
||||
|
||||
/** @typedef {import('../types/widgets/window').WindowProps} WindowProps */
|
||||
/** @typedef {import('../types/widgets/revealer').RevealerProps} RevealerProps */
|
||||
/** @typedef {import('../types/widgets/eventbox').EventBoxProps} EventBoxProps */
|
||||
/** @typedef {import('gi://Gtk?version=3.0')} Gtk */
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {EventBoxProps}
|
||||
* @returns {any}
|
||||
*/
|
||||
export const Padding = (
|
||||
name,
|
||||
{ css = "", hexpand = true, vexpand = true } = {},
|
||||
) =>
|
||||
// Widget.Box({});
|
||||
Widget.EventBox({
|
||||
hexpand,
|
||||
vexpand,
|
||||
can_focus: false,
|
||||
child: Widget.Box({ css }),
|
||||
setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Child} child
|
||||
* @param {Transition} [transition="slide_down"]
|
||||
* @param {number} duration
|
||||
*/
|
||||
const PopupRevealer = (
|
||||
name,
|
||||
child,
|
||||
transition = "slide_down",
|
||||
duration = 500,
|
||||
) =>
|
||||
Widget.Box(
|
||||
{ css: "padding: 1px;" },
|
||||
Widget.Revealer({
|
||||
transition,
|
||||
child: Widget.Box({
|
||||
class_name: "window-content",
|
||||
child,
|
||||
}),
|
||||
transitionDuration: duration,
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, wname, visible) => {
|
||||
if (wname === name) self.reveal_child = visible;
|
||||
}),
|
||||
}),
|
||||
);
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Child} child
|
||||
* @param {Transition} [transition]
|
||||
* @param {number} [duration]
|
||||
* @returns {{ center: () => any; top: () => any; "top-right": () => any; "top-center": () => any; "top-left": () => any; "bottom-left": () => any; "bottom-center": () => any; "bottom-right": () => any; }}
|
||||
*/
|
||||
const Layout = (name, child, transition, duration) => ({
|
||||
center: () =>
|
||||
Widget.CenterBox(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.CenterBox(
|
||||
{ vertical: true },
|
||||
Padding(name),
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
Padding(name),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
top: () =>
|
||||
Widget.CenterBox(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.Box(
|
||||
{ vertical: true },
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
Padding(name),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
"top-right": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
Padding(name),
|
||||
),
|
||||
),
|
||||
"top-center": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
Padding(name),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
"top-left": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
Padding(name),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
"bottom-left": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
Padding(name),
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
"bottom-center": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
Padding(name),
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
),
|
||||
Padding(name),
|
||||
),
|
||||
"bottom-right": () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name),
|
||||
Widget.Box(
|
||||
{
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
},
|
||||
Padding(name),
|
||||
PopupRevealer(name, child, transition, duration),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
/** @typedef {RevealerProps["transition"]} Transition */
|
||||
/** @typedef {WindowProps["child"]} Child */
|
||||
/**
|
||||
* @typedef {Omit<WindowProps, "name"> & {
|
||||
* name: string
|
||||
* layout?: keyof ReturnType<typeof Layout>
|
||||
* transition?: Transition,
|
||||
* duration?: number
|
||||
* }} PopupWindowProps
|
||||
* @param {PopupWindowProps} props
|
||||
*/
|
||||
export default ({
|
||||
name,
|
||||
child,
|
||||
layout = "center",
|
||||
transition,
|
||||
exclusivity = "ignore",
|
||||
duration,
|
||||
...props
|
||||
}) =>
|
||||
Widget.Window({
|
||||
name,
|
||||
class_names: [name, "popup-window"],
|
||||
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
||||
visible: false,
|
||||
keymode: "on-demand",
|
||||
exclusivity,
|
||||
layer: "top",
|
||||
// anchor: ["top", "right"],
|
||||
anchor: ["top", "bottom", "right", "left"],
|
||||
child: Layout(name, child, transition, duration)[layout](),
|
||||
...props,
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
|
||||
/**
|
||||
* @param {string | null | undefined} name
|
||||
* @param {string | null | undefined} [fallback]
|
||||
*/
|
||||
export function icon(name, fallback) {
|
||||
if (!name) return fallback || "";
|
||||
if (typeof name !== "string") return name;
|
||||
|
||||
const sub = substitutes[name];
|
||||
if (sub && Utils.lookUpIcon(sub)) return sub;
|
||||
if (Utils.lookUpIcon(name)) return name;
|
||||
|
||||
if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name;
|
||||
|
||||
return fallback || "";
|
||||
}
|
||||
|
||||
export const substitutes = {
|
||||
"transmission-gtk": "transmission",
|
||||
"blueberry.py": "blueberry",
|
||||
Caprine: "facebook-messenger",
|
||||
"com.raggesilver.BlackBox-symbolic": "terminal-symbolic",
|
||||
"org.wezfurlong.wezterm-symbolic": "terminal-symbolic",
|
||||
"audio-headset-bluetooth": "audio-headphones-symbolic",
|
||||
"audio-card-analog-usb": "audio-speakers-symbolic",
|
||||
"audio-card-analog-pci": "audio-card-symbolic",
|
||||
"preferences-system": "emblem-system-symbolic",
|
||||
"com.github.Aylur.ags-symbolic": "controls-symbolic",
|
||||
"com.github.Aylur.ags": "controls-symbolic",
|
||||
};
|
||||
@@ -1,228 +0,0 @@
|
||||
import { icon } from "../misc/utils.js";
|
||||
import {
|
||||
Arrow,
|
||||
Menu,
|
||||
SettingsButton,
|
||||
SimpleToggleButton,
|
||||
} from "../misc/menu.js";
|
||||
|
||||
const audio = await Service.import("audio");
|
||||
|
||||
const volumeIcons = /** @type {const} */ ([
|
||||
[101, "overamplified"],
|
||||
[67, "high"],
|
||||
[34, "medium"],
|
||||
[1, "low"],
|
||||
[0, "muted"],
|
||||
]);
|
||||
|
||||
/** @param {number} volume */
|
||||
export const getIcon = (volume) => {
|
||||
const icon = volumeIcons.find(([threshold]) => threshold <= volume)?.[1];
|
||||
return `audio-volume-${icon}-symbolic`;
|
||||
};
|
||||
|
||||
/** @param {{type?: "speaker" | "microphone"} & import("../types/widgets/icon.js").IconProps} props */
|
||||
export const VolumeIndicator = ({ type = "speaker", ...props }) =>
|
||||
Widget.Icon(props).hook(audio, (self) => {
|
||||
if (audio[type].is_muted) {
|
||||
self.icon = "audio-volume-muted-symbolic";
|
||||
self.tooltip_text = "Muted";
|
||||
return;
|
||||
}
|
||||
const vol = audio[type].volume * 100;
|
||||
self.icon = getIcon(vol);
|
||||
self.tooltip_text = `Volume: ${Math.floor(vol)}%`;
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/icon.js").IconProps} props */
|
||||
export const MicrophoneIndicator = (props) =>
|
||||
Widget.Icon(props).hook(audio, (self) => {
|
||||
self.visible = audio.microphone.is_muted || audio.recorders.length > 0;
|
||||
if (audio.microphone.is_muted) self.icon = "microphone-disabled-symbolic";
|
||||
else if (audio.recorders.length > 0)
|
||||
self.icon = "microphone-sensitivity-high-symbolic";
|
||||
});
|
||||
|
||||
/** @param {{type?: "speaker" | "microphone"} & import("../types/widgets/slider.js").SliderProps} props */
|
||||
const VolumeSlider = ({ type = "speaker", ...props }) =>
|
||||
Widget.Slider({
|
||||
hexpand: true,
|
||||
drawValue: false,
|
||||
onChange: ({ value, dragging }) => {
|
||||
if (dragging) {
|
||||
audio[type].volume = value;
|
||||
audio[type].is_muted = false;
|
||||
}
|
||||
},
|
||||
value: audio[type].bind("volume"),
|
||||
css: audio[type].bind("is_muted").as((x) => `opacity: ${x ? 0.7 : 1}`),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{type?: "speaker" | "microphone"} & import("../types/widgets/box.js").BoxProps} props */
|
||||
export const Volume = ({ type = "speaker", ...props }) =>
|
||||
Widget.Box({
|
||||
className: "qs-slider",
|
||||
children: [
|
||||
Widget.Button({
|
||||
onClicked: () => {
|
||||
audio[type].is_muted = !audio[type].is_muted;
|
||||
},
|
||||
child: VolumeIndicator({ type }),
|
||||
}),
|
||||
VolumeSlider({ type }),
|
||||
Widget.Label({
|
||||
label: audio[type].bind("volume").as(
|
||||
(vol) =>
|
||||
`${Math.floor(vol * 100)
|
||||
.toString()
|
||||
.padStart(3)}%`,
|
||||
),
|
||||
}),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
child: Arrow({ name: "sink-selector" }),
|
||||
}),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
child: Arrow({ name: "app-mixer" }),
|
||||
visible: audio.bind("apps").as((a) => a.length > 0),
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").SimpleToggleButtonProps>} props */
|
||||
export const MuteToggle = ({ ...props } = {}) =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon({
|
||||
className: "qs-icon",
|
||||
icon: audio.microphone
|
||||
.bind("is_muted")
|
||||
.as((x) =>
|
||||
x
|
||||
? "microphone-disabled-symbolic"
|
||||
: "microphone-sensitivity-high-symbolic",
|
||||
),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
label: audio.microphone
|
||||
.bind("is_muted")
|
||||
.as((x) => (x ? "Unmute" : "Mute")),
|
||||
}),
|
||||
activate: () => {
|
||||
audio.microphone.is_muted = true;
|
||||
},
|
||||
deactivate: () => {
|
||||
audio.microphone.is_muted = false;
|
||||
},
|
||||
connection: [audio.microphone, () => audio.microphone.is_muted || false],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const SinkSelector = (props) =>
|
||||
Menu({
|
||||
name: "sink-selector",
|
||||
icon: Widget.Icon("audio-headphones-symbolic"),
|
||||
title: "Sink Selector",
|
||||
content: [
|
||||
Widget.Box({
|
||||
className: "qs-sub-sub-content",
|
||||
vertical: true,
|
||||
children: audio.bind("speakers").as((a) => a.map(SinkItem)),
|
||||
}),
|
||||
Widget.Separator({ className: "accent" }),
|
||||
SettingsButton({ type: "sound" }),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/service/audio.js").Stream} stream */
|
||||
const SinkItem = (stream) =>
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
onClicked: () => {
|
||||
audio.speaker = stream;
|
||||
},
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: icon(stream.icon_name, "audio-x-generic-symbolic"),
|
||||
tooltip_text: stream.icon_name || "",
|
||||
}),
|
||||
Widget.Label({
|
||||
label: (stream.description || "").split(" ").slice(0, 4).join(" "),
|
||||
}),
|
||||
Widget.Icon({
|
||||
icon: "object-select-symbolic",
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
visible: audio.speaker.bind("stream").as((s) => s === stream.stream),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const AppMixer = (props) =>
|
||||
Menu({
|
||||
name: "app-mixer",
|
||||
icon: Widget.Icon("audio-volume-high-symbolic"),
|
||||
title: "App Mixer",
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
className: "qs-sub-sub-content",
|
||||
children: audio.bind("apps").as((a) => a.map(MixerItem)),
|
||||
}),
|
||||
Widget.Separator({ className: "accent" }),
|
||||
SettingsButton({ type: "sound" }),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/service/audio.js").Stream} stream */
|
||||
const MixerItem = (stream) =>
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Icon({
|
||||
tooltipText: stream.bind("name").as((n) => n || ""),
|
||||
icon: stream
|
||||
.bind("name")
|
||||
.as((n) =>
|
||||
n && Utils.lookUpIcon(n) ? n : "audio-x-generic-symbolic",
|
||||
),
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
truncate: "end",
|
||||
max_width_chars: 28,
|
||||
label: stream.bind("description").as((d) => d || ""),
|
||||
}),
|
||||
Widget.Slider({
|
||||
hexpand: true,
|
||||
draw_value: false,
|
||||
value: stream.bind("volume"),
|
||||
onChange: ({ value }) => {
|
||||
stream.volume = value;
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
css: "padding: 12px",
|
||||
label: stream.bind("volume").as(
|
||||
(x) =>
|
||||
`${Math.floor(x * 100)
|
||||
.toString()
|
||||
.padStart(3)}%`,
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
const battery = await Service.import("battery");
|
||||
|
||||
/** @param {import("../types/widgets/box").BoxProps} props */
|
||||
export const Indicator = ({ ...props }) =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: battery.bind("icon_name"),
|
||||
className: Utils.merge(
|
||||
[
|
||||
battery.bind("charging"),
|
||||
battery.bind("charged"),
|
||||
battery.bind("percent"),
|
||||
],
|
||||
(charging, charged, percent) => {
|
||||
if (charging || charged) return "green";
|
||||
if (percent < 30) return "red";
|
||||
return "";
|
||||
},
|
||||
),
|
||||
}),
|
||||
Widget.Label({
|
||||
label: battery.bind("percent").as((x) => `${x}%`),
|
||||
}),
|
||||
],
|
||||
visible: battery.bind("available"),
|
||||
...props,
|
||||
});
|
||||
@@ -1,94 +0,0 @@
|
||||
import { ArrowToggleButton, Menu, SettingsButton } from "../misc/menu.js";
|
||||
|
||||
const bluetooth = await Service.import("bluetooth");
|
||||
|
||||
const connected = Utils.merge(
|
||||
[bluetooth.bind("enabled"), bluetooth.bind("connected_devices")],
|
||||
(enabled, devices) => enabled && devices.length > 0,
|
||||
);
|
||||
|
||||
/** @param {{hideIfDisabled?: boolean} & import("../types/widgets/icon.js").IconProps} props */
|
||||
export const Indicator = ({ hideIfDisabled = false, ...props } = {}) =>
|
||||
Widget.Icon({
|
||||
icon: connected.as(
|
||||
(x) => `bluetooth-${x ? "active" : "disabled"}-symbolic`,
|
||||
),
|
||||
visible: connected.as((x) => x || !hideIfDisabled),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/label.js").LabelProps} props */
|
||||
export const ConnectedLabel = (props) =>
|
||||
Widget.Label(props).hook(bluetooth, (self) => {
|
||||
if (!bluetooth.enabled) self.label = "Disabled";
|
||||
|
||||
if (bluetooth.connected_devices.length === 0) self.label = "Disconnected";
|
||||
else if (bluetooth.connected_devices.length === 1)
|
||||
self.label = bluetooth.connected_devices[0].alias;
|
||||
else self.label = `${bluetooth.connected_devices.length} Connected`;
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").ArrowToggleButtonProps>} props */
|
||||
export const Toggle = (props) =>
|
||||
ArrowToggleButton({
|
||||
name: "bluetooth",
|
||||
icon: Indicator({ className: "qs-icon" }),
|
||||
label: ConnectedLabel({
|
||||
max_width_chars: 20,
|
||||
}),
|
||||
activate: () => {
|
||||
bluetooth.enabled = true;
|
||||
},
|
||||
deactivate: () => {
|
||||
bluetooth.enabled = false;
|
||||
},
|
||||
connection: [bluetooth, () => bluetooth.enabled],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const Selection = (props) =>
|
||||
Menu({
|
||||
name: "bluetooth",
|
||||
icon: Indicator({}),
|
||||
title: "Bluetooth",
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
className: "qs-sub-sub-content",
|
||||
children: bluetooth.bind("devices").as((x) => x.map(DeviceItem)),
|
||||
}),
|
||||
Widget.Separator({ className: "accent" }),
|
||||
SettingsButton({ command: "blueberry" }),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/service/bluetooth.js").BluetoothDevice} device */
|
||||
const DeviceItem = (device) =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(`${device.icon_name}-symbolic`),
|
||||
Widget.Label(device.name),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Label({
|
||||
label: `${device.battery_percentage}%`,
|
||||
css: "padding-right: 24px;",
|
||||
visible: device.bind("battery_percentage").as((x) => x > 0),
|
||||
}),
|
||||
Widget.Spinner({
|
||||
active: device.bind("connecting"),
|
||||
visible: device.bind("connecting"),
|
||||
}),
|
||||
Widget.Switch({
|
||||
active: device.bind("connected"),
|
||||
visible: device.bind("connecting").as((p) => !p),
|
||||
setup: (self) =>
|
||||
// TODO: If connecting to the device failed, reset back the switch to `active: false`.
|
||||
self.connect("state_set", () => {
|
||||
device.setConnection(self.active);
|
||||
return true;
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import brightness from "../services/brightness.js";
|
||||
|
||||
/** @param {import("../types/widgets/slider.js").SliderProps} props */
|
||||
const BrightnessSlider = (props) =>
|
||||
Widget.Slider({
|
||||
drawValue: false,
|
||||
hexpand: true,
|
||||
value: brightness.bind("screen"),
|
||||
onChange: ({ value }) => {
|
||||
brightness.screen = value;
|
||||
},
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/box.js").BoxProps} props */
|
||||
export const Brightness = (props) =>
|
||||
Widget.Box({
|
||||
className: "qs-slider",
|
||||
children: [
|
||||
Widget.Icon({
|
||||
vpack: "center",
|
||||
icon: "display-brightness-symbolic",
|
||||
tooltipText: brightness
|
||||
.bind("screen")
|
||||
.as((x) => `Screen Brightness: ${Math.floor(x * 100)}%`),
|
||||
}),
|
||||
BrightnessSlider({}),
|
||||
Widget.Label({
|
||||
label: brightness.bind("screen").as(
|
||||
(x) =>
|
||||
`${Math.floor(x * 100)
|
||||
.toString()
|
||||
.padStart(3)}%`,
|
||||
),
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import GLib from "gi://GLib";
|
||||
|
||||
export const clock = Variable(GLib.DateTime.new_now_local(), {
|
||||
poll: [1000, () => GLib.DateTime.new_now_local()],
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {{format?: string} & import("../types/widgets/label").LabelProps} props
|
||||
*/
|
||||
export const Clock = ({ format = "%a %d %b %H:%M ", ...props } = {}) =>
|
||||
Widget.Label({
|
||||
...props,
|
||||
label: Utils.derive([clock], (c) => c.format(format) || "").bind(),
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
import Gio from "gi://Gio";
|
||||
import { SimpleToggleButton } from "../misc/menu.js";
|
||||
|
||||
const interfaceXml = `
|
||||
<node name="/nl/whynothugo/darkman">
|
||||
<interface name="nl.whynothugo.darkman">
|
||||
<signal name="ModeChanged">
|
||||
<arg name="NewMode" type="s" />
|
||||
</signal>
|
||||
<property name="Mode" type="s" access="readwrite">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true" />
|
||||
</property>
|
||||
</interface>
|
||||
</node>
|
||||
`;
|
||||
const Darkman = Gio.DBusProxy.makeProxyWrapper(interfaceXml);
|
||||
|
||||
const theme = Variable(/** @type {"light" | "dark"} */ ("light"));
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").SimpleToggleButtonProps>} props */
|
||||
export const Toggle = ({ ...props } = {}) =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon({
|
||||
className: "qs-icon",
|
||||
icon: theme
|
||||
.bind()
|
||||
.as((x) =>
|
||||
x === "light"
|
||||
? "weather-clear-symbolic"
|
||||
: "weather-clear-night-symbolic",
|
||||
),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
label: theme.bind().as((x) => (x === "light" ? "Light" : "Dark")),
|
||||
}),
|
||||
activate: () => theme.setValue("dark"),
|
||||
deactivate: () => theme.setValue("light"),
|
||||
connection: [theme, () => theme.value === "dark"],
|
||||
...props,
|
||||
});
|
||||
|
||||
function init() {
|
||||
const darkman = Darkman(
|
||||
Gio.DBus.session,
|
||||
"nl.whynothugo.darkman",
|
||||
"/nl/whynothugo/darkman",
|
||||
);
|
||||
|
||||
theme.value = darkman.Mode;
|
||||
theme.connect("changed", () => {
|
||||
darkman.Mode = theme.value;
|
||||
});
|
||||
darkman.connectSignal("ModeChanged", (_proxy, _senderName, nTheme) => {
|
||||
theme.value = nTheme[0];
|
||||
});
|
||||
}
|
||||
init();
|
||||
@@ -1,290 +0,0 @@
|
||||
import { AnimatedCircProg } from "../misc/circular-progress.js";
|
||||
|
||||
const mpris = await Service.import("mpris");
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/icon").IconProps} props */
|
||||
const PlayerIcon = ({ player, ...props }) =>
|
||||
Widget.Icon({
|
||||
size: 24,
|
||||
hpack: "start",
|
||||
vpack: "start",
|
||||
tooltipText: player.identity || "",
|
||||
icon: player.bind("entry").transform((entry) => {
|
||||
const name = `${entry}-symbolic`;
|
||||
return Utils.lookUpIcon(name)
|
||||
? name
|
||||
: Utils.lookUpIcon(entry)
|
||||
? entry
|
||||
: "audio-x-generic-symbolic";
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/label").LabelProps} props */
|
||||
const TitleLabel = ({ player, ...props }) =>
|
||||
Widget.Label({
|
||||
wrap: true,
|
||||
truncate: "end",
|
||||
hpack: "start",
|
||||
label: player.bind("track_title"),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/label").LabelProps} props */
|
||||
const ArtistLabel = ({ player, ...props }) =>
|
||||
Widget.Label({
|
||||
wrap: true,
|
||||
truncate: "end",
|
||||
hpack: "start",
|
||||
label: player.bind("track_artists").transform((x) => x.map(y => y.replace(/- Topic$/, "")).join(", ")),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{
|
||||
* player: import("types/service/mpris").MprisPlayer,
|
||||
* iconProps?: import("types/widgets/icon").IconProps,
|
||||
* } & import("types/widgets/button").ButtonProps} props */
|
||||
export const PlayPause = ({ player, iconProps = {}, ...props }) =>
|
||||
Widget.Button({
|
||||
child: Widget.Icon({
|
||||
icon: player.bind("play_back_status").as(
|
||||
(x) =>
|
||||
({
|
||||
Playing: "media-playback-pause-symbolic",
|
||||
Paused: "media-playback-start-symbolic",
|
||||
Stopped: "media-playback-start-symbolic",
|
||||
})[x],
|
||||
),
|
||||
...iconProps,
|
||||
}),
|
||||
onClicked: () => player.playPause(),
|
||||
visible: player.bind("can_play"),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/button").ButtonProps} props */
|
||||
const PreviousButton = ({ player, ...props }) =>
|
||||
Widget.Button({
|
||||
child: Widget.Icon({ icon: "media-skip-backward-symbolic" }),
|
||||
onClicked: () => player.previous(),
|
||||
visible: player.bind("can_go_prev"),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/button").ButtonProps} props */
|
||||
const NextButton = ({ player, ...props }) =>
|
||||
Widget.Button({
|
||||
child: Widget.Icon({ icon: "media-skip-forward-symbolic" }),
|
||||
onClicked: () => player.next(),
|
||||
visible: player.bind("can_go_next"),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/slider").SliderProps} props */
|
||||
const PositionSlider = ({ player, ...props }) =>
|
||||
Widget.Slider({
|
||||
className: "mpris-position-slider",
|
||||
drawValue: false,
|
||||
onChange: ({ value }) => {
|
||||
player.position = value * player.length;
|
||||
},
|
||||
visible: player.bind("length").as((l) => l > 0),
|
||||
setup: (self) => {
|
||||
function update() {
|
||||
const value = player.position / player.length;
|
||||
self.value = value > 0 ? value : 0;
|
||||
}
|
||||
self.hook(player, update);
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/box").BoxProps} props */
|
||||
const PositionCircle = ({ player, child, ...props }) =>
|
||||
Widget.Box({
|
||||
child: Widget.Overlay({
|
||||
child: AnimatedCircProg({
|
||||
css: `
|
||||
min-width: 0.136rem;
|
||||
min-height: 1.636rem;
|
||||
padding: 0rem;
|
||||
`,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
className: "accent-rev",
|
||||
extraSetup: (self) => {
|
||||
function update() {
|
||||
const value = player.position / player.length;
|
||||
self.css = `
|
||||
font-size: ${Math.max(value * 100, 0)}px;
|
||||
min-width: 0.136rem;
|
||||
min-height: 1.636rem;
|
||||
padding: 0rem;
|
||||
`;
|
||||
}
|
||||
self.hook(player, update);
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
}),
|
||||
overlays: [child],
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {{player: import("types/service/mpris").MprisPlayer} & import("../types/widgets/box").BoxProps} props */
|
||||
export const LinePlayer = ({ player, ...props }) =>
|
||||
Widget.Box({
|
||||
visible: player.bind("play_back_status").as((x) => x !== "Stopped"),
|
||||
children: [
|
||||
PositionCircle({
|
||||
player,
|
||||
child: PlayPause({ player, iconProps: { size: 10 } }),
|
||||
css: "margin-right: 12px;",
|
||||
}),
|
||||
Widget.Label({
|
||||
label: Utils.merge(
|
||||
[player.bind("track_title"), player.bind("track_artists")],
|
||||
(title, artists) => `${title} - ${artists.map(x => x.replace(/- Topic$/, "")).join(", ")}`,
|
||||
),
|
||||
maxWidthChars: 30,
|
||||
wrap: true,
|
||||
truncate: "end",
|
||||
hpack: "start",
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
export const activePlayer = Variable(mpris.players[0]);
|
||||
mpris.connect("player-added", (_, bus) => {
|
||||
mpris.getPlayer(bus)?.connect("changed", (player) => {
|
||||
if (player?.play_back_status !== "Stopped") {
|
||||
activePlayer.value = player || mpris.players[0];
|
||||
} else {
|
||||
activePlayer.value = mpris.players[0];
|
||||
}
|
||||
});
|
||||
});
|
||||
mpris.connect("player-closed", (_, bus) => {
|
||||
if (activePlayer.value.bus_name === bus)
|
||||
activePlayer.value = mpris.players[0];
|
||||
});
|
||||
|
||||
/** @param {{player?: import("types/service/mpris").MprisPlayer | null} & import("../types/widgets/box").BoxProps} props */
|
||||
export const MprisPlayer = ({ player, ...props }) => {
|
||||
if (!player) return Widget.Box({ visible: false });
|
||||
const colors = getMaterialColors(player);
|
||||
|
||||
return Widget.Box({
|
||||
visible: player.bind("play_back_status").as((x) => x !== "Stopped"),
|
||||
className: `mpris-cover-art ${props.className}`,
|
||||
css: Utils.merge(
|
||||
[colors.bind(), player.bind("cover_path")],
|
||||
(colors, cover) => `
|
||||
background-image: radial-gradient(circle, rgba(0, 0, 0, 0.4) 30%, ${colors.primary}), url("${cover}"); \
|
||||
color: ${colors.onBackground};
|
||||
`,
|
||||
),
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.CenterBox({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
startWidget: Widget.Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
css: colors.bind().as((x) => `color: ${x.onBackground}`),
|
||||
child: PlayerIcon({ player }),
|
||||
}),
|
||||
centerWidget: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
css: colors.bind().as(
|
||||
(x) => `
|
||||
color: ${x.onBackground};
|
||||
margin-right: 12px;
|
||||
`,
|
||||
),
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
hexpand: true,
|
||||
children: [
|
||||
TitleLabel({
|
||||
player,
|
||||
css: "font-weight: 600; font-size: 19px;",
|
||||
maxWidthChars: 33,
|
||||
}),
|
||||
ArtistLabel({
|
||||
player,
|
||||
css: "font-weight: 400; font-size: 17px;",
|
||||
maxWidthChars: 40,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
children: [
|
||||
PlayPause({
|
||||
player,
|
||||
hpack: "end",
|
||||
className: "mpris-play",
|
||||
css: colors.bind().as(
|
||||
(x) => `
|
||||
background-color: ${x.primary};
|
||||
color: ${x.onPrimary};
|
||||
`,
|
||||
),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
endWidget: Widget.Box({
|
||||
css: colors.bind().as((x) => `color: ${x.onBackground}`),
|
||||
vpack: "end",
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
PreviousButton({ player, css: "margin-left: 16px;" }),
|
||||
PositionSlider({ player, hexpand: true }),
|
||||
NextButton({ player, css: "margin-right: 16px;" }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Move ret inside getMaterialColors to support multiple players.
|
||||
const ret = Variable({
|
||||
primary: "#222222",
|
||||
onPrimary: "#ffffff",
|
||||
background: "#222222",
|
||||
onBackground: "#ffffff",
|
||||
});
|
||||
/** @param {import("types/service/mpris").MprisPlayer} player */
|
||||
export const getMaterialColors = (player) => {
|
||||
// TODO: Move that to a hook to allow graceful disconnections
|
||||
player.connect("changed", (player) => {
|
||||
const cover = player.cover_path;
|
||||
// TODO: Wait for the cover to be downloaded, currently we hope that it's ready in <100ms
|
||||
Utils.timeout(100, () => {
|
||||
Utils.execAsync(["covercolors", cover])
|
||||
.then((colors) => {
|
||||
const col = JSON.parse(colors);
|
||||
if (!col) return;
|
||||
ret.setValue(col);
|
||||
})
|
||||
.catch(print);
|
||||
});
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
@@ -1,114 +0,0 @@
|
||||
import { ArrowToggleButton, Menu, SettingsButton } from "../misc/menu.js";
|
||||
|
||||
const network = await Service.import("network");
|
||||
|
||||
/** @param {import("../types/widgets/icon.js").IconProps} props*/
|
||||
export const Indicator = (props) =>
|
||||
Widget.Icon(props).hook(network, (self) => {
|
||||
self.icon =
|
||||
network[network.primary || "wifi"].icon_name ??
|
||||
"network-wireless-offline-symbolic";
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/label.js").LabelProps} props */
|
||||
export const SSIDLabel = (props) =>
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
...props,
|
||||
}).hook(network, (self) => {
|
||||
if (network.primary === "wifi")
|
||||
self.label = network.wifi.ssid || "Not Connected";
|
||||
else
|
||||
self.label =
|
||||
network.wired.internet !== "disconnected" ? "Wired" : "Not Connected";
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").ArrowToggleButtonProps>} props */
|
||||
export const Toggle = (props) =>
|
||||
ArrowToggleButton({
|
||||
name: "network",
|
||||
icon: Indicator({ className: "qs-icon" }),
|
||||
label: SSIDLabel({
|
||||
max_width_chars: 20,
|
||||
}),
|
||||
activate: () => {
|
||||
network.wifi.enabled = true;
|
||||
network.wifi.scan();
|
||||
},
|
||||
deactivate: () => {
|
||||
network.wifi.enabled = false;
|
||||
},
|
||||
connection: [network.wifi, () => network.wifi.enabled],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const Selection = (props) =>
|
||||
Menu({
|
||||
name: "network",
|
||||
icon: Indicator({}),
|
||||
title: "Network Selection",
|
||||
content: [
|
||||
Wired(),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
className: "qs-sub-sub-content",
|
||||
children: network.wifi.bind("access_points").as((x) =>
|
||||
x
|
||||
.sort((a, b) => b.strength - a.strength)
|
||||
.reduce((acc, x) => {
|
||||
if (!acc.find((y) => y.ssid === x.ssid)) acc.push(x);
|
||||
return acc;
|
||||
}, [])
|
||||
.slice(0, 10)
|
||||
.map(WifiItem),
|
||||
),
|
||||
}),
|
||||
Widget.Separator({ className: "accent" }),
|
||||
SettingsButton({ type: "wifi" }),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
const Wired = () =>
|
||||
Widget.Button({
|
||||
// onClicked:
|
||||
// visible: network.wired.bind("state").as(x => (console.log(x), true)),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({ icon: network.wired.bind("icon_name") }),
|
||||
Widget.Label("Wired"),
|
||||
Widget.Icon({
|
||||
icon: "object-select-symbolic",
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
visible: network.bind("primary").as((x) => x === "wired"),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {import("../types/service/network.js").Wifi["access_points"][0]} wifi */
|
||||
const WifiItem = (wifi) =>
|
||||
Widget.Button({
|
||||
onClicked: () => Utils.execAsync(`nmcli device wifi connect ${wifi.bssid}`),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(wifi.iconName),
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
max_width_chars: 28,
|
||||
label: wifi.ssid || "",
|
||||
}),
|
||||
Widget.Icon({
|
||||
icon: "object-select-symbolic",
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
setup: (self) =>
|
||||
Utils.idle(() => {
|
||||
if (!self.is_destroyed) self.visible = wifi.active;
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -1,270 +0,0 @@
|
||||
const notifications = await Service.import("notifications");
|
||||
notifications.popupTimeout = 2000; //in seconds
|
||||
notifications.forceTimeout = true; //force all notifications to timeout
|
||||
|
||||
/** @param {import("../types/widgets/icon").IconProps} props */
|
||||
export const DNDIndicator = (props) =>
|
||||
Widget.Icon({
|
||||
visible: notifications.bind("dnd"),
|
||||
icon: "notifications-disabled-symbolic",
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/button").ButtonProps} props */
|
||||
export const DNDToggle = (props) =>
|
||||
Widget.Button({
|
||||
onClicked: () => {
|
||||
notifications.dnd = !notifications.dnd;
|
||||
},
|
||||
child: Widget.Icon({
|
||||
icon: notifications
|
||||
.bind("dnd")
|
||||
.as((x) =>
|
||||
x
|
||||
? "preferences-system-notifications-symbolic"
|
||||
: "notifications-disabled-symbolic",
|
||||
),
|
||||
}),
|
||||
className: notifications.bind("dnd").as((x) => (x ? "on" : "")),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/box").BoxProps} props */
|
||||
export const Indicator = ({ ...props }) =>
|
||||
Widget.Box({
|
||||
visible: Utils.merge(
|
||||
[
|
||||
notifications.bind("notifications").as((x) => x.length > 0),
|
||||
notifications.bind("dnd"),
|
||||
],
|
||||
(hasNotif, dnd) => hasNotif && !dnd,
|
||||
),
|
||||
children: [
|
||||
Widget.Icon({ icon: "preferences-system-notifications-symbolic" }),
|
||||
Widget.Revealer({
|
||||
transition: "slide_right",
|
||||
revealChild: notifications.bind("popups").as((x) => x.length > 0),
|
||||
child: Widget.Label({
|
||||
use_markup: true,
|
||||
truncate: "end",
|
||||
wrap: false,
|
||||
label: notifications.bind("popups").as((x) => {
|
||||
const notif = x[x.length - 1];
|
||||
// Keep the text of the old notif for the fade out animation.
|
||||
if (!notif) return old_notif;
|
||||
const summary = notif.summary.substring(0, 18).trim();
|
||||
const body = notif.body.substring(0, 45).trim();
|
||||
old_notif = `${summary}: ${body}`.replaceAll("\n", " ");
|
||||
return old_notif;
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
let old_notif = "";
|
||||
|
||||
/** @param {import("../types/service/notifications").Notification} param */
|
||||
const NotificationIcon = ({ app_entry, app_icon, image }) => {
|
||||
if (image) {
|
||||
return Widget.Box({
|
||||
vpack: "start",
|
||||
hexpand: false,
|
||||
className: "r20",
|
||||
css: `
|
||||
background-image: url("${image}");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
min-width: 78px;
|
||||
min-height: 78px;
|
||||
margin-right: 12px;
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
let icon = "dialog-information-symbolic";
|
||||
if (Utils.lookUpIcon(app_icon)) icon = app_icon;
|
||||
if (Utils.lookUpIcon(app_entry || "")) icon = app_entry || "";
|
||||
|
||||
return Widget.Box({
|
||||
vpack: "start",
|
||||
hexpand: false,
|
||||
className: "r20",
|
||||
css: `
|
||||
min-width: 78px;
|
||||
min-height: 78px;
|
||||
margin-right: 12px;
|
||||
`,
|
||||
child: Widget.Icon({
|
||||
icon,
|
||||
size: 58,
|
||||
hpack: "center",
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
vexpand: true,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
import GLib from "gi://GLib";
|
||||
|
||||
/** @param {number} time */
|
||||
const time = (time, format = "%H:%M") =>
|
||||
GLib.DateTime.new_from_unix_local(time).format(format);
|
||||
|
||||
/** @param {import("../types/service/notifications").Notification} notification */
|
||||
export const Notification = (notification) => {
|
||||
const content = Widget.Box({
|
||||
children: [
|
||||
NotificationIcon(notification),
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Label({
|
||||
css: `
|
||||
font-size: 1.1em;
|
||||
margin-right: 12pt;
|
||||
`,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
hexpand: true,
|
||||
max_width_chars: 24,
|
||||
truncate: "end",
|
||||
wrap: true,
|
||||
label: notification.summary.trim(),
|
||||
use_markup: true,
|
||||
}),
|
||||
Widget.Label({
|
||||
vpack: "start",
|
||||
label: time(notification.time),
|
||||
}),
|
||||
Widget.Button({
|
||||
css: `
|
||||
margin-left: 6pt;
|
||||
min-width: 1.2em;
|
||||
min-height: 1.2em;
|
||||
`,
|
||||
vpack: "start",
|
||||
child: Widget.Icon("window-close-symbolic"),
|
||||
on_clicked: notification.close,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
css: "font-size: .9em;",
|
||||
hexpand: true,
|
||||
use_markup: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
label: notification.body.trim(),
|
||||
max_width_chars: 24,
|
||||
wrap: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const actionsbox =
|
||||
notification.actions.length > 0
|
||||
? Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
child: Widget.EventBox({
|
||||
child: Widget.Box({
|
||||
class_name: "actions horizontal",
|
||||
children: notification.actions.map((action) =>
|
||||
Widget.Button({
|
||||
className: "button",
|
||||
on_clicked: () => notification.invoke(action.id),
|
||||
hexpand: true,
|
||||
child: Widget.Label(action.label),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
: null;
|
||||
|
||||
const eventbox = Widget.EventBox({
|
||||
vexpand: false,
|
||||
on_primary_click: notification.dismiss,
|
||||
on_hover() {
|
||||
if (actionsbox) actionsbox.reveal_child = true;
|
||||
},
|
||||
on_hover_lost() {
|
||||
if (actionsbox) actionsbox.reveal_child = true;
|
||||
|
||||
notification.dismiss();
|
||||
},
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: actionsbox ? [content, actionsbox] : [content],
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
className: "surface r20 p10",
|
||||
css: "margin: 8px 0;",
|
||||
child: eventbox,
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {import("../types/widgets/scrollable").ScrollableProps} props */
|
||||
export const List = (props) =>
|
||||
Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
css: "min-height: 500px;",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: notifications
|
||||
.bind("notifications")
|
||||
.as((x) => x.map(Notification)),
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/box").BoxProps} props */
|
||||
export const Placeholder = (props) =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: "notifications-disabled-symbolic",
|
||||
size: 75,
|
||||
css: "margin: 50px 0",
|
||||
}),
|
||||
Widget.Label({
|
||||
label: "Your inbox is empty",
|
||||
css: "margin-bottom: 150px;",
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import("../types/widgets/button").ButtonProps} props */
|
||||
export const ClearButton = (props) =>
|
||||
Widget.Button({
|
||||
className: "surface r20 p10",
|
||||
onClicked: () => notifications.clear(),
|
||||
sensitive: notifications.bind("notifications").as((x) => x.length > 0),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label({ label: "Clear " }),
|
||||
Widget.Icon({
|
||||
icon: notifications
|
||||
.bind("notifications")
|
||||
.as((x) =>
|
||||
x.length > 0 ? "user-trash-full-symbolic" : "user-trash-symbolic",
|
||||
),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
...props,
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
import { ArrowToggleButton, Menu } from "../misc/menu.js";
|
||||
|
||||
const powerProfiles = await Service.import("powerprofiles");
|
||||
|
||||
/** @param {string} x */
|
||||
const capitalize = (x) => x.charAt(0).toUpperCase() + x.slice(1);
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").ArrowToggleButtonProps>} props */
|
||||
export const Toggle = (props) =>
|
||||
ArrowToggleButton({
|
||||
name: "powerprofile",
|
||||
icon: Widget.Icon({
|
||||
icon: powerProfiles.bind("icon_name"),
|
||||
className: "qs-icon",
|
||||
}),
|
||||
label: Widget.Label({
|
||||
label: powerProfiles.bind("active_profile").as(capitalize),
|
||||
truncate: "end",
|
||||
max_width_chars: 20,
|
||||
}),
|
||||
activate: () => {},
|
||||
deactivate: () => {},
|
||||
connection: [powerProfiles, () => false],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const Selection = (props) =>
|
||||
Menu({
|
||||
name: "powerprofile",
|
||||
icon: Widget.Icon({ icon: powerProfiles.bind("icon_name") }),
|
||||
title: "Power profile",
|
||||
// Hard coding the list of profiles since ags does not give them.
|
||||
content: ["power-saver", "balanced", "performance"].map(ProfileItem),
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {string} profile */
|
||||
const ProfileItem = (profile) =>
|
||||
Widget.Button({
|
||||
onClicked: () => (powerProfiles.active_profile = profile),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: `power-profile-${profile}-symbolic`,
|
||||
}),
|
||||
Widget.Label({
|
||||
label: capitalize(profile),
|
||||
truncate: "end",
|
||||
maxWidthChars: 28,
|
||||
}),
|
||||
Widget.Icon({
|
||||
icon: "object-select-symbolic",
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
visible: powerProfiles
|
||||
.bind("active_profile")
|
||||
.as((x) => x === profile),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import { icon } from "../misc/utils.js";
|
||||
import { ArrowToggleButton, Menu } from "../misc/menu.js";
|
||||
|
||||
const systemtray = await Service.import("systemtray");
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").ArrowToggleButtonProps>} props */
|
||||
export const Toggle = (props) =>
|
||||
ArrowToggleButton({
|
||||
name: "systray",
|
||||
icon: Widget.Icon({ icon: "open-menu-symbolic", className: "qs-icon" }),
|
||||
label: Widget.Label("Systray"),
|
||||
activate: () => {},
|
||||
deactivate: () => {},
|
||||
connection: [systemtray, () => systemtray.items.length > 0],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {Partial<import("../misc/menu.js").MenuProps>} props */
|
||||
export const Selection = (props) =>
|
||||
Menu({
|
||||
name: "systray",
|
||||
icon: Widget.Icon("open-menu-symbolic"),
|
||||
title: "Systray",
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
className: "qs-sub-sub-content",
|
||||
children: systemtray.bind("items").as((i) => i.map(SysTrayItem)),
|
||||
}),
|
||||
],
|
||||
...props,
|
||||
});
|
||||
|
||||
/** @param {import('../types/service/systemtray.js').TrayItem} item */
|
||||
const SysTrayItem = (item) =>
|
||||
Widget.Button({
|
||||
css: "margin: 12px;",
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({ icon: item.bind("icon").as(icon) }),
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
maxWidthChars: 28,
|
||||
}).hook(item, (self) => {
|
||||
self.label = item.title || item.tooltip_markup;
|
||||
}),
|
||||
],
|
||||
}),
|
||||
tooltipMarkup: item.bind("tooltip_markup"),
|
||||
onPrimaryClick: (_, event) => item.activate(event),
|
||||
onSecondaryClick: (_, event) => item.openMenu(event),
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
import Gdk from "gi://Gdk?version=3.0";
|
||||
|
||||
// const hyprland = await Service.import("hyprland");
|
||||
//
|
||||
// const display = Gdk.Display.get_default();
|
||||
// /** @param {number} monitor */
|
||||
// const getMonitorName = (monitor) => {
|
||||
// return display.get_default_screen().get_monitor_plug_name(monitor);
|
||||
// };
|
||||
//
|
||||
// /** @type {Record<number, boolean>} */
|
||||
// const urgents = {}
|
||||
//
|
||||
// /** @param {{monitor: number, labels: string[]} & import("types/widgets/box").BoxProps} props */
|
||||
// export const Tags = ({ monitor, labels, ...props }) => {
|
||||
// const monName = getMonitorName(monitor);
|
||||
// // @ts-ignore
|
||||
// return Widget.Box({
|
||||
// ...props,
|
||||
// children: Array.from({ length: 9 }, (_, i) => i).map((i) =>
|
||||
// Widget.EventBox({
|
||||
// child: Widget.Label({ label: labels[i], className: "tags" }),
|
||||
// attribute: i + 1,
|
||||
// onPrimaryClickRelease: () => {
|
||||
// hyprland.message(`dispatch workspace ${i + 1}`);
|
||||
// },
|
||||
// }),
|
||||
// ),
|
||||
// setup: (self) => {
|
||||
// self.hook(hyprland, (_, address) => {
|
||||
// const client = hyprland.getClient(address);
|
||||
// if (!client) return;
|
||||
// const ws = client.workspace.id;
|
||||
// urgents[ws] = true;
|
||||
// }, "urgent-window");
|
||||
//
|
||||
// self.hook(hyprland, () =>
|
||||
// self.children.forEach((btn) => {
|
||||
// const mon = hyprland.monitors.find((x) => x.name === monName);
|
||||
// const ws = hyprland.workspaces.find((x) => x.id === btn.attribute);
|
||||
//
|
||||
// const occupied = (ws?.windows ?? 0) > 0;
|
||||
// const selected = mon?.activeWorkspace?.id === btn.attribute;
|
||||
// if (selected) urgents[btn.attribute] = false;
|
||||
// const urgent = urgents[btn.attribute];
|
||||
//
|
||||
// btn.visible = occupied || selected;
|
||||
// btn.class_names = [
|
||||
// selected ? "accent" : "",
|
||||
// urgent ? "secondary" : "",
|
||||
// ];
|
||||
// }),
|
||||
// );
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// /** @param {{monitor: number } & import("types/widgets/label").LabelProps} props */
|
||||
// export const Layout = ({ monitor, ...props }) => {
|
||||
// const monName = getMonitorName(monitor);
|
||||
// return Widget.Label({
|
||||
// className: "module",
|
||||
// ...props,
|
||||
// }).hook(
|
||||
// hyprland,
|
||||
// (self) => {
|
||||
// const mon = hyprland.monitors.find((x) => x.name === monName);
|
||||
// const ws = hyprland.workspaces.find(
|
||||
// (x) => x.id === mon?.activeWorkspace?.id,
|
||||
// );
|
||||
// self.label = ws?.windows ? `[${ws?.windows}]` : "";
|
||||
// },
|
||||
// "changed",
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// /** @param {{monitor: number, fallback?: string} & import("types/widgets/label").LabelProps} props */
|
||||
// export const ClientLabel = ({ monitor, fallback = "", ...props }) => {
|
||||
// const monName = getMonitorName(monitor);
|
||||
// return Widget.Label({
|
||||
// truncate: "end",
|
||||
// maxWidthChars: 25,
|
||||
// className: "module",
|
||||
// ...props,
|
||||
// }).hook(
|
||||
// hyprland,
|
||||
// (self) => {
|
||||
// const mon = hyprland.monitors.find((x) => x.name === monName);
|
||||
// const ws = hyprland.workspaces.find(
|
||||
// (x) => x.id === mon?.activeWorkspace?.id,
|
||||
// );
|
||||
// self.label = ws?.lastwindowtitle || fallback;
|
||||
// },
|
||||
// "changed",
|
||||
// );
|
||||
// };
|
||||
@@ -1,44 +0,0 @@
|
||||
const screen = await Utils.execAsync(
|
||||
"/bin/sh -c 'ls -w1 /sys/class/backlight | head -n 1'",
|
||||
);
|
||||
const max = Number(await Utils.execAsync("brightnessctl m"));
|
||||
|
||||
class Brightness extends Service {
|
||||
static {
|
||||
Service.register(this, {}, { screen: ["float", "rw"] });
|
||||
}
|
||||
|
||||
#screen = 0;
|
||||
|
||||
get screen() {
|
||||
return this.#screen;
|
||||
}
|
||||
|
||||
set screen(percent) {
|
||||
if (percent < 0) percent = 0;
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
Utils.execAsync(`brightnessctl s ${percent * 100}% -q`)
|
||||
.then(() => {
|
||||
this.#screen = percent;
|
||||
|
||||
this.emit("changed");
|
||||
this.notify("screen");
|
||||
})
|
||||
.catch(print);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#screen = Number(Utils.exec("brightnessctl g")) / max;
|
||||
|
||||
const screenPath = `/sys/class/backlight/${screen}/brightness`;
|
||||
|
||||
Utils.monitorFile(screenPath, async (f) => {
|
||||
const v = await Utils.readFileAsync(f);
|
||||
this.#screen = Number(v) / max;
|
||||
this.changed("screen");
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Brightness();
|
||||
@@ -1,232 +0,0 @@
|
||||
* {
|
||||
font-family: monospace;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
.transparent {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.bgcont {
|
||||
background-color: #1E1E2E;
|
||||
border: 1px solid #11111B;
|
||||
}
|
||||
.surface {
|
||||
background-color: #11111B;
|
||||
color: #CDD6F4;
|
||||
}
|
||||
.accent {
|
||||
background-color: #94e2d5;
|
||||
color: #1e1e2e;
|
||||
}
|
||||
.accent-rev {
|
||||
color: #94e2d5;
|
||||
background-color: #1e1e2e;
|
||||
}
|
||||
.secondary {
|
||||
background-color: #cba6f7;
|
||||
color: #1e1e2e;
|
||||
}
|
||||
.red {
|
||||
color: #F38BA8;
|
||||
}
|
||||
.green {
|
||||
color: #A6E3A1;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.f16 {
|
||||
font-size: 16px;
|
||||
}
|
||||
.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.r20 {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.r100 {
|
||||
border-radius: 100px;
|
||||
}
|
||||
.p10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
separator {
|
||||
border-radius: 10px;
|
||||
min-height: 1px;
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
/* Slider */
|
||||
slider {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
transition: 200ms;
|
||||
min-height: 1rem;
|
||||
border-radius: 100px;
|
||||
}
|
||||
trough {
|
||||
transition: 200ms;
|
||||
border: 1px;
|
||||
background-color: #000;
|
||||
border-radius: 100px;
|
||||
}
|
||||
highlight, fill {
|
||||
background-color: #94e2d5;
|
||||
border-radius: 100px;
|
||||
transition: 200ms;
|
||||
}
|
||||
mark {
|
||||
background-color: #F38BA8;
|
||||
min-width: 1px;
|
||||
min-height: 3px;
|
||||
}
|
||||
|
||||
switch {
|
||||
background-color: #1E1E2E;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
switch slider {
|
||||
all: unset;
|
||||
transition: 200ms;
|
||||
background-color: #94e2d5;
|
||||
border-radius: 100px;
|
||||
min-width: 1rem;
|
||||
min-height: 1rem;
|
||||
}
|
||||
|
||||
switch image {
|
||||
all: unset;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
/* Bar */
|
||||
.module {
|
||||
padding: 1px 10px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
.tags {
|
||||
min-width: 2rem;
|
||||
}
|
||||
.qs-item {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
/* On Screen Display */
|
||||
.osd {
|
||||
border-radius: 20px;
|
||||
margin-bottom: 150px;
|
||||
min-width: 175px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
/* Quick settings */
|
||||
.qs-container {
|
||||
min-width: 400px;
|
||||
padding: 25px;
|
||||
margin: 10px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.qs-button {
|
||||
padding: 0px;
|
||||
margin: 5px;
|
||||
border-radius: 20px;
|
||||
min-width: 225px;
|
||||
min-height: 75px;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
transition: color 0.3s ease-in-out;
|
||||
}
|
||||
.qs-icon {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.qs-slider > * {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.qs-submenu {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 12px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.qs-sub-title {
|
||||
padding: 14px 20px;
|
||||
border-radius: 100px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.qs-sub-title > *:first-child {
|
||||
margin-right: 14px;
|
||||
}
|
||||
.qs-sub-content image {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.qs-sub-content > * {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.qs-sub-sub-content > * {
|
||||
margin: 6px 0px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
min-width: 70px;
|
||||
min-height: 70px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.sys-button {
|
||||
min-width: 40px;
|
||||
min-height: 40px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.mpris-cover-art {
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
margin: 10px 0;
|
||||
min-height: 205px;
|
||||
border-radius: 20px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.mpris-cover-art > * {
|
||||
padding: 20px;
|
||||
}
|
||||
.mpris-play {
|
||||
min-width: 60px;
|
||||
min-height: 60px;
|
||||
border-radius: 20px;
|
||||
padding: 0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.mpris-position-slider {
|
||||
margin: 0 10px;
|
||||
}
|
||||
.mpris-position-slider trough {
|
||||
border-radius: 100px;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
min-height: 4px;
|
||||
}
|
||||
.mpris-position-slider highlight, .mpris-position-slider progress {
|
||||
background-color: #fff;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.mpris-position-slider slider {
|
||||
border-radius: 100px;
|
||||
min-height: 4px;
|
||||
min-width: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"baseUrl": ".",
|
||||
"typeRoots": [
|
||||
"./types"
|
||||
],
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/nix/store/g5s50dy9za9inrw5zps05s7kh4ypnbpa-ags-1.8.2/share/com.github.Aylur.ags/types
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
noctalia,
|
||||
...
|
||||
}: let
|
||||
wallpaper = pkgs.writeShellScriptBin "wallpaper" ''
|
||||
@@ -39,9 +40,9 @@
|
||||
in {
|
||||
imports = [
|
||||
./rofi
|
||||
./ags
|
||||
./fcitx5.nix
|
||||
./hyprlock.nix
|
||||
noctalia.homeModules.default
|
||||
];
|
||||
home.packages = [
|
||||
wallpaper
|
||||
@@ -89,4 +90,124 @@ in {
|
||||
button-layout = "";
|
||||
};
|
||||
};
|
||||
|
||||
programs.noctalia-shell = {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
settings = {
|
||||
bar = {
|
||||
capsuleOpacity = 0.5;
|
||||
showCapsule = false;
|
||||
outerCorners = false;
|
||||
widgets = {
|
||||
left = [
|
||||
{
|
||||
id = "TaskbarGrouped";
|
||||
labelMode = "none";
|
||||
}
|
||||
];
|
||||
center = [
|
||||
{
|
||||
id = "NotificationHistory";
|
||||
}
|
||||
];
|
||||
right = [
|
||||
{
|
||||
id = "MediaMini";
|
||||
maxWidth = 250;
|
||||
showArtistFirst = false;
|
||||
}
|
||||
{
|
||||
id = "Spacer";
|
||||
}
|
||||
{
|
||||
id = "Tray";
|
||||
}
|
||||
{
|
||||
id = "Battery";
|
||||
displayMode = "alwaysShow";
|
||||
warningThreshold = 30;
|
||||
}
|
||||
{
|
||||
id = "Volume";
|
||||
displayMode = "alwaysHide";
|
||||
}
|
||||
{
|
||||
id = "Bluetooth";
|
||||
displayMode = "alwaysHide";
|
||||
}
|
||||
{
|
||||
id = "WiFi";
|
||||
displayMode = "alwaysHide";
|
||||
}
|
||||
{
|
||||
id = "Spacer";
|
||||
}
|
||||
{
|
||||
id = "Clock";
|
||||
formatHorizontal = "HH:mm\\nyyyy-MM-dd";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
controlCenter = {
|
||||
position = "top_center";
|
||||
};
|
||||
audio.visualizerType = "none";
|
||||
notifications = {
|
||||
enabled = true;
|
||||
location = "bar";
|
||||
lowUrgencyDuration = 3;
|
||||
normalUrgencyDuration = 3;
|
||||
criticalUrgencyDuration = 3;
|
||||
};
|
||||
osd = {
|
||||
enabled = true;
|
||||
location = "bottom";
|
||||
};
|
||||
sessionMenu = {
|
||||
enableCountdown = false;
|
||||
powerOptions = [
|
||||
{
|
||||
action = "lock";
|
||||
enabled = true;
|
||||
}
|
||||
{
|
||||
action = "suspend";
|
||||
enabled = true;
|
||||
}
|
||||
{
|
||||
action = "hibernate";
|
||||
enabled = false;
|
||||
}
|
||||
{
|
||||
action = "reboot";
|
||||
enabled = true;
|
||||
}
|
||||
{
|
||||
action = "logout";
|
||||
enabled = false;
|
||||
}
|
||||
{
|
||||
action = "shutdown";
|
||||
enabled = true;
|
||||
}
|
||||
];
|
||||
showHeader = true;
|
||||
};
|
||||
screenRecorder.directory = "~/stuff";
|
||||
settingsVersion = 23;
|
||||
setupCompleted = true;
|
||||
general = {
|
||||
lockOnSuspend = false;
|
||||
showScreenCorners = false;
|
||||
};
|
||||
colorSchemes = {
|
||||
predefinedScheme = "Catppuccin";
|
||||
};
|
||||
wallpaper.enabled = false;
|
||||
dock.enabled = false;
|
||||
nightLight.enabled = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
vim.api.nvim_create_autocmd('LspAttach', {
|
||||
callback = function(args)
|
||||
local client_id = args.data.client_id
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
if client.name == 'biome' then
|
||||
vim.lsp.on_type_formatting.enable(false, { client_id = client_id })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return {
|
||||
-- Disable lunching from node_modules (no nix binary)
|
||||
cmd = { "biome", "lsp-proxy" },
|
||||
-- Inline package.json (remove check for biome installed)
|
||||
root_dir = function(_, on_dir)
|
||||
-- To support monorepos, biome recommends starting the search for the root from cwd
|
||||
-- https://biomejs.dev/guides/big-projects/#use-multiple-configuration-files
|
||||
local cwd = vim.fn.getcwd()
|
||||
local root_files = { 'biome.json', 'biome.jsonc', 'package.json', 'package.json5' }
|
||||
local root_dir = vim.fs.dirname(vim.fs.find(root_files, { path = cwd, upward = true })[1])
|
||||
on_dir(root_dir)
|
||||
end,
|
||||
-- for json files
|
||||
workspace_required = false,
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ in
|
||||
bash-language-server
|
||||
sqls
|
||||
biome
|
||||
kdePackages.qtdeclarative # qmlls
|
||||
|
||||
# gopls also needs go /shame
|
||||
gopls
|
||||
|
||||
@@ -18,8 +18,9 @@ return {
|
||||
sql = { "pg_format" },
|
||||
cs = { "csharpier" },
|
||||
nix = { "alejandra" },
|
||||
["*"] = { "injected" },
|
||||
["_"] = { "injected", lsp_format = "last" },
|
||||
-- ["*"] = { "injected" },
|
||||
-- ["_"] = { "injected", lsp_format = "last" },
|
||||
["_"] = { lsp_format = "last" },
|
||||
},
|
||||
formatters = {
|
||||
csharpier = function()
|
||||
|
||||
@@ -25,7 +25,6 @@ return {
|
||||
strategies = {
|
||||
chat = {
|
||||
adapter = "copilot",
|
||||
model = "claude-3-7-sonnet",
|
||||
start_in_insert_mode = true,
|
||||
keymaps = {
|
||||
send = {
|
||||
@@ -35,10 +34,16 @@ return {
|
||||
modes = { n = "q", i = "<C-d>" },
|
||||
},
|
||||
},
|
||||
tools = {
|
||||
opts = {
|
||||
auto_submit_success = true,
|
||||
auto_submit_errors = true,
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
inline = {
|
||||
adapter = "copilot",
|
||||
model = "claude-3-7-sonnet",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ vim.lsp.enable({
|
||||
"jsonls",
|
||||
"biome",
|
||||
"sqls",
|
||||
"qmlls"
|
||||
-- "roslyn_ls", we use roslyn.nvim plugin instead.
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
local function git_show(ref)
|
||||
local git_root = Snacks.git.get_root()
|
||||
local function finder(opts, ctx)
|
||||
return require("snacks.picker.source.proc").proc({
|
||||
opts,
|
||||
{
|
||||
---@type snacks.picker.finder
|
||||
local function finder(_opts, ctx)
|
||||
return require("snacks.picker.source.proc").proc(
|
||||
ctx:opts({
|
||||
cmd = "git",
|
||||
args = { "show", "--name-status", "--pretty=tformat:", ref },
|
||||
cwd = Snacks.git.get_root(),
|
||||
@@ -12,8 +12,9 @@ local function git_show(ref)
|
||||
item.file = string.sub(item.text, 3)
|
||||
item.commit = ref
|
||||
end,
|
||||
},
|
||||
}, ctx)
|
||||
}),
|
||||
ctx
|
||||
)
|
||||
end
|
||||
|
||||
Snacks.picker.pick({
|
||||
@@ -263,19 +264,19 @@ return {
|
||||
end, { desc = "Grep" })
|
||||
|
||||
vim.keymap.set("n", "<leader>gl", function()
|
||||
Snacks.picker.git_log({cwd = Snacks.git.get_root() })
|
||||
Snacks.picker.git_log({ cwd = Snacks.git.get_root() })
|
||||
end, { desc = "Git log" })
|
||||
|
||||
vim.keymap.set("n", "<leader>gh", function()
|
||||
Snacks.picker.git_log_file({cwd = Snacks.git.get_root() })
|
||||
Snacks.picker.git_log_file({ cwd = Snacks.git.get_root() })
|
||||
end, { desc = "Git logs buffer" })
|
||||
|
||||
vim.keymap.set("n", "<leader>gB", function()
|
||||
Snacks.picker.git_branches({cwd = Snacks.git.get_root() })
|
||||
Snacks.picker.git_branches({ cwd = Snacks.git.get_root() })
|
||||
end, { desc = "Git branches" })
|
||||
|
||||
vim.keymap.set("n", "<leader>gs", function()
|
||||
Snacks.picker.git_status({cwd = Snacks.git.get_root() })
|
||||
Snacks.picker.git_status({ cwd = Snacks.git.get_root() })
|
||||
end, { desc = "Git status" })
|
||||
end,
|
||||
},
|
||||
|
||||
@@ -73,6 +73,7 @@ vim.keymap.set("v", "<", "<gv")
|
||||
vim.keymap.set("v", ">", ">gv")
|
||||
|
||||
-- Copy to/from system clipboard
|
||||
vim.g.clipboard = 'osc52'
|
||||
vim.keymap.set({ "n", "x" }, "<leader>y", '"+y', { desc = "Yank to system clipboard" })
|
||||
vim.keymap.set({ "n", "x" }, "<leader>Y", '"+y$', { desc = "Yank line to system clipboard" })
|
||||
vim.keymap.set({ "n", "x" }, "<leader>p", '"+p', { desc = "Past from system clipboard" })
|
||||
|
||||
@@ -18,18 +18,29 @@
|
||||
in {
|
||||
tmux = super.tmux.overrideAttrs {
|
||||
src = tmux;
|
||||
patches = [
|
||||
./tmux-get_clipboard.diff
|
||||
];
|
||||
};
|
||||
|
||||
# it doesn't start without this, no clue why.
|
||||
freecad = wrapProgram super.freecad ["freecad" "FreeCAD" "freecadcmd" "FreeCADCmd"] ''
|
||||
--set QT_QPA_PLATFORM 'wayland;xcb' \
|
||||
--set QT_QPA_PLATFORMTHEME qt5ct
|
||||
# they try to use passthrough if they detect tmux. we don't want that.
|
||||
osc = wrapProgram super.osc ["osc"] ''
|
||||
--set TMUX ""
|
||||
'';
|
||||
|
||||
# Gnome-control-center can only be launched if XDG_CURRENT_DESKTOP is GNOME.
|
||||
gnome-control-center = wrapProgram super.gnome-control-center ["gnome-control-center"] "--set XDG_CURRENT_DESKTOP GNOME";
|
||||
|
||||
slack = enableWayland super.slack ["slack"];
|
||||
# i can't get this to work /shrug
|
||||
# slack = super.symlinkJoin {
|
||||
# name = super.slack.name;
|
||||
# paths = [super.slack];
|
||||
# buildInputs = [super.makeWrapper];
|
||||
# postBuild = ''
|
||||
# wrapProgram $out/bin/slack --add-flags "--disable-smooth-scrolling"
|
||||
# substituteInPlace ${super.slack}/share/applications/slack.desktop --replace ${super.slack} $out
|
||||
# '';
|
||||
# };
|
||||
discord = enableWayland super.discord ["discord" "Discord"];
|
||||
vesktop = enableWayland super.vesktop ["vesktop"];
|
||||
youtube-music = enableWayland super.youtube-music ["youtube-music"];
|
||||
|
||||
332
overlays/tmux-get_clipboard.diff
Normal file
332
overlays/tmux-get_clipboard.diff
Normal file
@@ -0,0 +1,332 @@
|
||||
# source: https://github.com/tmux/tmux/issues/4275
|
||||
diff --git a/input.c b/input.c
|
||||
index 3a61ec26..58a1b091 100644
|
||||
--- a/input.c
|
||||
+++ b/input.c
|
||||
@@ -3069,18 +3069,41 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Handle OSC 52 reply. */
|
||||
+static void
|
||||
+input_osc_52_reply(struct input_ctx *ictx)
|
||||
+{
|
||||
+ struct paste_buffer *pb;
|
||||
+ int state;
|
||||
+ const char *buf;
|
||||
+ size_t len;
|
||||
+
|
||||
+ state = options_get_number(global_options, "get-clipboard");
|
||||
+ if (state == 0)
|
||||
+ return;
|
||||
+ if (state == 1) {
|
||||
+ if ((pb = paste_get_top(NULL)) == NULL)
|
||||
+ return;
|
||||
+ buf = paste_buffer_data(pb, &len);
|
||||
+ if (ictx->input_end == INPUT_END_BEL)
|
||||
+ input_reply_clipboard(ictx->event, buf, len, "\007");
|
||||
+ else
|
||||
+ input_reply_clipboard(ictx->event, buf, len, "\033\\");
|
||||
+ return;
|
||||
+ }
|
||||
+ input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end);
|
||||
+}
|
||||
+
|
||||
/* Handle the OSC 52 sequence for setting the clipboard. */
|
||||
static void
|
||||
input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
+ size_t len;
|
||||
char *end;
|
||||
- const char *buf = NULL;
|
||||
- size_t len = 0;
|
||||
u_char *out;
|
||||
int outlen, state;
|
||||
struct screen_write_ctx ctx;
|
||||
- struct paste_buffer *pb;
|
||||
const char* allow = "cpqs01234567";
|
||||
char flags[sizeof "cpqs01234567"] = "";
|
||||
u_int i, j = 0;
|
||||
@@ -3105,12 +3128,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
|
||||
|
||||
if (strcmp(end, "?") == 0) {
|
||||
- if ((pb = paste_get_top(NULL)) != NULL)
|
||||
- buf = paste_buffer_data(pb, &len);
|
||||
- if (ictx->input_end == INPUT_END_BEL)
|
||||
- input_reply_clipboard(ictx->event, buf, len, "\007");
|
||||
- else
|
||||
- input_reply_clipboard(ictx->event, buf, len, "\033\\");
|
||||
+ input_osc_52_reply(ictx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3169,6 +3187,7 @@ input_osc_104(struct input_ctx *ictx, const char *p)
|
||||
free(copy);
|
||||
}
|
||||
|
||||
+/* Send a clipboard reply. */
|
||||
void
|
||||
input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
|
||||
const char *end)
|
||||
@@ -3305,6 +3324,9 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
|
||||
xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx);
|
||||
tty_puts(&c->tty, s);
|
||||
break;
|
||||
+ case INPUT_REQUEST_CLIPBOARD:
|
||||
+ tty_putcode_ss(&c->tty, TTYC_MS, "", "?");
|
||||
+ break;
|
||||
case INPUT_REQUEST_QUEUE:
|
||||
break;
|
||||
}
|
||||
@@ -3312,6 +3334,39 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
|
||||
return (0);
|
||||
}
|
||||
|
||||
+/* Handle a palette reply. */
|
||||
+static void
|
||||
+input_request_palette_reply(struct input_request *ir, void *data)
|
||||
+{
|
||||
+ struct input_request_palette_data *pd = data;
|
||||
+
|
||||
+ input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
|
||||
+}
|
||||
+
|
||||
+/* Handle a clipboard reply. */
|
||||
+static void
|
||||
+input_request_clipboard_reply(struct input_request *ir, void *data)
|
||||
+{
|
||||
+ struct input_ctx *ictx = ir->ictx;
|
||||
+ struct input_request_clipboard_data *cd = data;
|
||||
+ int state;
|
||||
+ char *copy;
|
||||
+
|
||||
+ state = options_get_number(global_options, "get-clipboard");
|
||||
+ if (state == 0 || state == 1)
|
||||
+ return;
|
||||
+ if (state == 3) {
|
||||
+ copy = xmalloc(cd->len);
|
||||
+ memcpy(copy, cd->buf, cd->len);
|
||||
+ paste_add(NULL, copy, cd->len);
|
||||
+ }
|
||||
+
|
||||
+ if (ir->idx == INPUT_END_BEL)
|
||||
+ input_reply_clipboard(ictx->event, cd->buf, cd->len, "\007");
|
||||
+ else
|
||||
+ input_reply_clipboard(ictx->event, cd->buf, cd->len, "\033\\");
|
||||
+}
|
||||
+
|
||||
/* Handle a reply to a request. */
|
||||
void
|
||||
input_request_reply(struct client *c, enum input_request_type type, void *data)
|
||||
@@ -3321,11 +3376,18 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
|
||||
int complete = 0;
|
||||
|
||||
TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) {
|
||||
- if (ir->type == type && pd->idx == ir->idx) {
|
||||
+ if (ir->type != type) {
|
||||
+ input_free_request(ir);
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (type == INPUT_REQUEST_PALETTE && pd->idx == ir->idx) {
|
||||
+ found = ir;
|
||||
+ break;
|
||||
+ }
|
||||
+ if (type == INPUT_REQUEST_CLIPBOARD) {
|
||||
found = ir;
|
||||
break;
|
||||
}
|
||||
- input_free_request(ir);
|
||||
}
|
||||
if (found == NULL)
|
||||
return;
|
||||
@@ -3335,8 +3397,11 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
|
||||
break;
|
||||
if (ir->type == INPUT_REQUEST_QUEUE)
|
||||
input_send_reply(ir->ictx, ir->data);
|
||||
- else if (ir == found && ir->type == INPUT_REQUEST_PALETTE) {
|
||||
- input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
|
||||
+ else if (ir == found) {
|
||||
+ if (ir->type == INPUT_REQUEST_PALETTE)
|
||||
+ input_request_palette_reply(ir, data);
|
||||
+ else if (ir->type == INPUT_REQUEST_CLIPBOARD)
|
||||
+ input_request_clipboard_reply(ir, data);
|
||||
complete = 1;
|
||||
}
|
||||
input_free_request(ir);
|
||||
diff --git a/options-table.c b/options-table.c
|
||||
index 6946a085..b3ba39cd 100644
|
||||
--- a/options-table.c
|
||||
+++ b/options-table.c
|
||||
@@ -84,6 +84,9 @@ static const char *options_table_popup_border_lines_list[] = {
|
||||
static const char *options_table_set_clipboard_list[] = {
|
||||
"off", "external", "on", NULL
|
||||
};
|
||||
+static const char *options_table_get_clipboard_list[] = {
|
||||
+ "off", "buffer", "request", "both", NULL
|
||||
+};
|
||||
static const char *options_table_window_size_list[] = {
|
||||
"largest", "smallest", "manual", "latest", NULL
|
||||
};
|
||||
@@ -405,6 +408,18 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Whether to send focus events to applications."
|
||||
},
|
||||
|
||||
+ { .name = "get-clipboard",
|
||||
+ .type = OPTIONS_TABLE_CHOICE,
|
||||
+ .scope = OPTIONS_TABLE_SERVER,
|
||||
+ .choices = options_table_get_clipboard_list,
|
||||
+ .default_num = 1,
|
||||
+ .text = "When an application requests the clipboard, whether to "
|
||||
+ "ignore the request ('off'); respond with the newest buffer "
|
||||
+ "('buffer'); request the clipboard from the most recently "
|
||||
+ "used terminal ('request'); or to request the clipboard, "
|
||||
+ "create a buffer, and send it to the application ('both')."
|
||||
+ },
|
||||
+
|
||||
{ .name = "history-file",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
diff --git a/tmux.1 b/tmux.1
|
||||
index a25da1fb..2ae1b724 100644
|
||||
--- a/tmux.1
|
||||
+++ b/tmux.1
|
||||
@@ -4241,6 +4241,32 @@ passed through to applications running in
|
||||
.Nm .
|
||||
Attached clients should be detached and attached again after changing this
|
||||
option.
|
||||
+.It Xo Ic get-clipboard
|
||||
+.Op Ic both | request | buffer | off
|
||||
+.Xc
|
||||
+Controls the behaviour when an application requests the clipboard from
|
||||
+.Nm .
|
||||
+.Pp
|
||||
+If
|
||||
+.Ic off ,
|
||||
+the request is ignored;
|
||||
+if
|
||||
+.Ic buffer ,
|
||||
+.Nm
|
||||
+responds with the newest paste buffer;
|
||||
+.Ic request
|
||||
+causes
|
||||
+.Nm
|
||||
+to request the clipboard from the most recently used client (if possible) and
|
||||
+send the reply (if any) back to the application;
|
||||
+.Ic buffer
|
||||
+is the same as
|
||||
+.Ic request
|
||||
+but also creates a paste buffer.
|
||||
+.Pp
|
||||
+See also the
|
||||
+.Ic set-clipboard
|
||||
+option.
|
||||
.It Ic history-file Ar path
|
||||
If not empty, a file to which
|
||||
.Nm
|
||||
diff --git a/tmux.h b/tmux.h
|
||||
index c3959d2a..3dece217 100644
|
||||
--- a/tmux.h
|
||||
+++ b/tmux.h
|
||||
@@ -1129,6 +1129,7 @@ struct window_mode_entry {
|
||||
/* Type of request to client. */
|
||||
enum input_request_type {
|
||||
INPUT_REQUEST_PALETTE,
|
||||
+ INPUT_REQUEST_CLIPBOARD,
|
||||
INPUT_REQUEST_QUEUE
|
||||
};
|
||||
|
||||
@@ -1138,6 +1139,12 @@ struct input_request_palette_data {
|
||||
int c;
|
||||
};
|
||||
|
||||
+/* Clipboard request reply data. */
|
||||
+struct input_request_clipboard_data {
|
||||
+ char *buf;
|
||||
+ size_t len;
|
||||
+};
|
||||
+
|
||||
/* Request sent to client on behalf of pane. */
|
||||
TAILQ_HEAD(input_requests, input_request);
|
||||
|
||||
diff --git a/tty-keys.c b/tty-keys.c
|
||||
index 77254591..7acee80f 100644
|
||||
--- a/tty-keys.c
|
||||
+++ b/tty-keys.c
|
||||
@@ -1301,12 +1301,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||
static int
|
||||
tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||
{
|
||||
- struct client *c = tty->client;
|
||||
- struct window_pane *wp;
|
||||
- size_t end, terminator = 0, needed;
|
||||
- char *copy, *out;
|
||||
- int outlen;
|
||||
- u_int i;
|
||||
+ struct client *c = tty->client;
|
||||
+ struct window_pane *wp;
|
||||
+ size_t end, terminator = 0, needed;
|
||||
+ char *copy, *out;
|
||||
+ int outlen;
|
||||
+ u_int i;
|
||||
+ struct input_request_clipboard_data cd;
|
||||
|
||||
*size = 0;
|
||||
|
||||
@@ -1364,12 +1365,6 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||
buf++;
|
||||
end--;
|
||||
|
||||
- /* If we did not request this, ignore it. */
|
||||
- if (~tty->flags & TTY_OSC52QUERY)
|
||||
- return (0);
|
||||
- tty->flags &= ~TTY_OSC52QUERY;
|
||||
- evtimer_del(&tty->clipboard_timer);
|
||||
-
|
||||
/* It has to be a string so copy it. */
|
||||
copy = xmalloc(end + 1);
|
||||
memcpy(copy, buf, end);
|
||||
@@ -1384,18 +1379,37 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||
return (0);
|
||||
}
|
||||
free(copy);
|
||||
-
|
||||
- /* Create a new paste buffer and forward to panes. */
|
||||
log_debug("%s: %.*s", __func__, outlen, out);
|
||||
- if (c->flags & CLIENT_CLIPBOARDBUFFER) {
|
||||
- paste_add(NULL, out, outlen);
|
||||
- c->flags &= ~CLIENT_CLIPBOARDBUFFER;
|
||||
+
|
||||
+ /* Set reply if any. */
|
||||
+ if (c->clipboard_npanes == 0) {
|
||||
+ cd.buf = out;
|
||||
+ cd.len = outlen;
|
||||
+ input_request_reply(c, INPUT_REQUEST_CLIPBOARD, &cd);
|
||||
+ free(out);
|
||||
+ return (0);
|
||||
}
|
||||
+
|
||||
+ /* If we did not request this, ignore it. */
|
||||
+ if (~tty->flags & TTY_OSC52QUERY) {
|
||||
+ free(out);
|
||||
+ return (0);
|
||||
+ }
|
||||
+ tty->flags &= ~TTY_OSC52QUERY;
|
||||
+ evtimer_del(&tty->clipboard_timer);
|
||||
+
|
||||
+ /* Create a new paste buffer and forward to panes. */
|
||||
for (i = 0; i < c->clipboard_npanes; i++) {
|
||||
wp = window_pane_find_by_id(c->clipboard_panes[i]);
|
||||
if (wp != NULL)
|
||||
input_reply_clipboard(wp->event, out, outlen, "\033\\");
|
||||
}
|
||||
+ if (~c->flags & CLIENT_CLIPBOARDBUFFER)
|
||||
+ free (out);
|
||||
+ else {
|
||||
+ paste_add(NULL, out, outlen);
|
||||
+ c->flags &= ~CLIENT_CLIPBOARDBUFFER;
|
||||
+ }
|
||||
free(c->clipboard_panes);
|
||||
c->clipboard_panes = NULL;
|
||||
c->clipboard_npanes = 0;
|
||||
@@ -19,15 +19,20 @@ tasks:
|
||||
|
||||
# Install stuff
|
||||
|
||||
install:
|
||||
install:*:
|
||||
desc: install to disks labeled `nix` and `boot`
|
||||
deps: [password:root, password:zoriya]
|
||||
vars:
|
||||
HOST: "{{index .MATCH 0}}"
|
||||
cmds:
|
||||
- task: password:root
|
||||
- task: password:zoriya
|
||||
- mkdir -p /mnt/{boot,nix}
|
||||
- mount /dev/disk/by-label/boot /mnt/boot
|
||||
- mount /dev/disk/by-label/nix /mnt/nix
|
||||
- task: swap
|
||||
- defer: { task: swap-off }
|
||||
- nixos-install --no-channel-copy --no-root-password
|
||||
- mkdir -p /mnt/nix/persist/tmp
|
||||
- TMPDIR=/mnt/nix/persist/tmp nixos-install --no-channel-copy --no-root-password --flake path:.#{{.HOST}}
|
||||
|
||||
password:*:
|
||||
desc: setup passwords
|
||||
|
||||
Reference in New Issue
Block a user