mirror of
https://github.com/tuxdotrs/tpanel.git
synced 2026-09-20 02:19:03 +05:30
Compare commits
21 Commits
0708fc688e
...
ags
| Author | SHA1 | Date | |
|---|---|---|---|
|
c946acacf3
|
|||
|
14d3510746
|
|||
|
9b8c4df596
|
|||
|
00e8ee6d8b
|
|||
|
69b93ca32f
|
|||
|
18eb55eb05
|
|||
|
bd05b722f5
|
|||
|
38b6cfb89a
|
|||
|
b41e5706e1
|
|||
|
f46e52d427
|
|||
|
b0d22ef4bc
|
|||
|
ed56ef463e
|
|||
|
292a7ad786
|
|||
|
4b7ae63bc8
|
|||
| 0c7610355f | |||
|
|
0c1ac689cf | ||
|
98203af3e4
|
|||
|
416f2004af
|
|||
|
d185a7e694
|
|||
| 410ee29aa1 | |||
|
c9cb1a411e
|
@@ -8,7 +8,7 @@
|
|||||||
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/tuxdotrs/tpanel">
|
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/tuxdotrs/tpanel">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -34,3 +34,5 @@ environment.systemPackages = [ inputs.tpanel.packages.${system}.default ];
|
|||||||
# Add this in your HomeManager config
|
# Add this in your HomeManager config
|
||||||
home.packages = [ inputs.tpanel.packages.${system}.default ];
|
home.packages = [ inputs.tpanel.packages.${system}.default ];
|
||||||
```
|
```
|
||||||
|
## Outdated Screenshot
|
||||||
|

|
||||||
|
|||||||
6
app.ts
6
app.ts
@@ -1,14 +1,12 @@
|
|||||||
import app from "ags/gtk4/app";
|
import app from "ags/gtk4/app";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
import style from "./style/main.scss";
|
import style from "./style/main.scss";
|
||||||
import windows from "./windows";
|
import { Main } from "./main";
|
||||||
|
|
||||||
const icons = `${GLib.get_user_config_dir()}/tpanel/assets/icons`;
|
const icons = `${GLib.get_user_config_dir()}/tpanel/assets/icons`;
|
||||||
|
|
||||||
app.start({
|
app.start({
|
||||||
css: style,
|
css: style,
|
||||||
icons: icons,
|
icons: icons,
|
||||||
main() {
|
main: Main,
|
||||||
windows.map((win) => app.get_monitors().map(win));
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
assets/gallery/latest.png
Normal file
BIN
assets/gallery/latest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M8.35179 20.2418C9.19288 21.311 10.5142 22 12 22C13.4858 22 14.8071 21.311 15.6482 20.2418C13.2264 20.57 10.7736 20.57 8.35179 20.2418Z" fill="#000"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M18.7491 9V9.7041C18.7491 10.5491 18.9903 11.3752 19.4422 12.0782L20.5496 13.8012C21.5612 15.3749 20.789 17.5139 19.0296 18.0116C14.4273 19.3134 9.57274 19.3134 4.97036 18.0116C3.21105 17.5139 2.43882 15.3749 3.45036 13.8012L4.5578 12.0782C5.00972 11.3752 5.25087 10.5491 5.25087 9.7041V9C5.25087 5.13401 8.27256 2 12 2C15.7274 2 18.7491 5.13401 18.7491 9ZM10.0717 9.75C9.67233 9.75 9.34857 9.41421 9.34857 9C9.34857 8.58578 9.67233 8.25 10.0717 8.25H13.9283C14.2208 8.25 14.4845 8.43273 14.5964 8.71299C14.7083 8.99324 14.6465 9.31583 14.4397 9.53033L11.8175 12.25H13.9283C14.3277 12.25 14.6515 12.5858 14.6515 13C14.6515 13.4142 14.3277 13.75 13.9283 13.75H10.0717C9.77922 13.75 9.51554 13.5673 9.40362 13.287C9.29169 13.0068 9.35356 12.6842 9.56037 12.4697L12.1826 9.75H10.0717Z" fill="#000"></path> </g></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M8.35179 20.2418C9.19288 21.311 10.5142 22 12 22C13.4858 22 14.8071 21.311 15.6482 20.2418C13.2264 20.57 10.7736 20.57 8.35179 20.2418Z" fill="#000"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M18.7491 9.7041V9C18.7491 5.13401 15.7274 2 12 2C8.27256 2 5.25087 5.13401 5.25087 9V9.7041C5.25087 10.5491 5.00972 11.3752 4.5578 12.0782L3.45036 13.8012C2.43882 15.3749 3.21105 17.5139 4.97036 18.0116C9.57274 19.3134 14.4273 19.3134 19.0296 18.0116C20.789 17.5139 21.5612 15.3749 20.5496 13.8012L19.4422 12.0782C18.9903 11.3752 18.7491 10.5491 18.7491 9.7041ZM12 5.25C12.4142 5.25 12.75 5.58579 12.75 6V10C12.75 10.4142 12.4142 10.75 12 10.75C11.5858 10.75 11.25 10.4142 11.25 10V6C11.25 5.58579 11.5858 5.25 12 5.25Z" fill="#000"></path> </g></svg>
|
||||||
|
After Width: | Height: | Size: 996 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M18.2211 19.6431C18.6981 18.7396 19.1627 17.7065 19.4613 16.6623C19.8722 15.2247 20.0207 13.8751 20.0629 12.8451L18.5105 11.2926L12.7073 5.48944L11.1549 3.93701C10.1248 3.97917 8.77531 4.12767 7.33767 4.53865C6.29348 4.83716 5.26037 5.30183 4.35693 5.77885C2.10098 6.96998 1.42721 9.71071 2.49716 11.8068L2.51021 11.8324L3.20923 12.9815C5.15002 16.1718 7.82804 18.8499 11.0184 20.7907L12.1675 21.4898L12.1931 21.5028C14.2892 22.5728 17.0299 21.899 18.2211 19.6431Z" fill="#000"></path> <path d="M21.7747 3.31343C22.0751 3.01296 22.0751 2.52581 21.7747 2.22535C21.4742 1.92488 20.987 1.92488 20.6866 2.22535L19.0118 3.90018C17.3118 2.66569 14.9941 2.66575 13.2942 3.9002L14.4027 5.00866L18.9915 9.59749L20.0999 10.7059C21.3344 9.00597 21.3343 6.68821 20.0998 4.98826L21.7747 3.31343Z" fill="#000"></path> </g></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
18
flake.lock
generated
18
flake.lock
generated
@@ -8,11 +8,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758577685,
|
"lastModified": 1764289441,
|
||||||
"narHash": "sha256-iHT0kvsQJG+Z89quGi7rNCXEg2e3DBGfuuCMu/UwiIo=",
|
"narHash": "sha256-ak+lgFiYE5PHByN1/BRkO5JP498hno6Ix24C1Qf/vec=",
|
||||||
"owner": "aylur",
|
"owner": "aylur",
|
||||||
"repo": "ags",
|
"repo": "ags",
|
||||||
"rev": "aa7a8a2dd6e54aaeb4e13a73ed3bc2283995090b",
|
"rev": "e169694390548dfd38ff40f1ef2163d6c3ffe3ea",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756474652,
|
"lastModified": 1764173295,
|
||||||
"narHash": "sha256-iiBU6itpEqE0spXeNJ3uJTfioSyKYjt5bNepykpDXTE=",
|
"narHash": "sha256-Jh4VtPcK2Ov+RTcV9FtyQRsxiJmXFQGfqX6jjM7/mgc=",
|
||||||
"owner": "aylur",
|
"owner": "aylur",
|
||||||
"repo": "astal",
|
"repo": "astal",
|
||||||
"rev": "20bd8318e4136fbd3d4eb2d64dbabc3acbc915dd",
|
"rev": "7d1fac8a4b2a14954843a978d2ddde86168c75ef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -44,11 +44,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758427187,
|
"lastModified": 1765779637,
|
||||||
"narHash": "sha256-pHpxZ/IyCwoTQPtFIAG2QaxuSm8jWzrzBGjwQZIttJc=",
|
"narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "554be6495561ff07b6c724047bdd7e0716aa7b46",
|
"rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
src = ./.;
|
src = ./.;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
wrapGAppsHook
|
wrapGAppsHook3
|
||||||
gobject-introspection
|
gobject-introspection
|
||||||
ags.packages.${system}.default
|
ags.packages.${system}.default
|
||||||
];
|
];
|
||||||
@@ -63,6 +63,11 @@
|
|||||||
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Remove any broken symlinks
|
||||||
|
postFixup = ''
|
||||||
|
find $out -type l ! -exec test -e {} \; -delete 2>/dev/null || true
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.runCommand "tpanel" {
|
pkgs.runCommand "tpanel" {
|
||||||
|
|||||||
21
main.tsx
Normal file
21
main.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { For, This, createBinding } from "ags";
|
||||||
|
import app from "ags/gtk4/app";
|
||||||
|
import { Bar } from "./widgets/bar";
|
||||||
|
import { Notifications } from "./widgets/notifications";
|
||||||
|
import { ControlCenter } from "./widgets/control-center";
|
||||||
|
|
||||||
|
export const Main = () => {
|
||||||
|
const monitors = createBinding(app, "monitors");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<For each={monitors}>
|
||||||
|
{(monitor) => (
|
||||||
|
<This this={app}>
|
||||||
|
<Bar gdkmonitor={monitor} />
|
||||||
|
<Notifications gdkmonitor={monitor} />
|
||||||
|
<ControlCenter gdkmonitor={monitor} />
|
||||||
|
</This>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -15,7 +15,7 @@ window.Bar {
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
image {
|
image {
|
||||||
-gtk-icon-size: 1.5rem;
|
-gtk-icon-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -73,13 +73,13 @@ window.Bar {
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
image {
|
image {
|
||||||
-gtk-icon-size: 1.5rem;
|
-gtk-icon-size: 1.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tray {
|
.tray {
|
||||||
image {
|
image {
|
||||||
-gtk-icon-size: 1.3rem;
|
-gtk-icon-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
menubutton {
|
menubutton {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ window.control-center {
|
|||||||
color: $fg-color;
|
color: $fg-color;
|
||||||
background-color: $inactive-color;
|
background-color: $inactive-color;
|
||||||
border-radius: calc($rounded / 1.5);
|
border-radius: calc($rounded / 1.5);
|
||||||
padding: 20px;
|
padding: 15px;
|
||||||
|
|
||||||
image {
|
image {
|
||||||
-gtk-icon-size: 2rem;
|
-gtk-icon-size: 2rem;
|
||||||
@@ -32,19 +32,19 @@ window.control-center {
|
|||||||
.header {
|
.header {
|
||||||
.name {
|
.name {
|
||||||
color: rgba($fg-color, 0.8);
|
color: rgba($fg-color, 0.8);
|
||||||
font-size: 1.8rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
color: rgba($fg-color, 0.5);
|
color: rgba($fg-color, 0.5);
|
||||||
font-size: 1.2rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
button {
|
button {
|
||||||
background-color: $inactive-color;
|
background-color: $inactive-color;
|
||||||
border-radius: calc($rounded / 1.5);
|
border-radius: calc($rounded / 1.5);
|
||||||
padding: 10px;
|
padding: 6px;
|
||||||
transition: color 100ms;
|
transition: color 100ms;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -56,7 +56,28 @@ window.control-center {
|
|||||||
}
|
}
|
||||||
|
|
||||||
image {
|
image {
|
||||||
-gtk-icon-size: 2rem;
|
-gtk-icon-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-list {
|
||||||
|
.focus {
|
||||||
|
background: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: $inactive-color;
|
||||||
|
border-radius: calc($rounded / 1.5);
|
||||||
|
padding: 10px;
|
||||||
|
transition: color 100ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($accent, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
image {
|
||||||
|
-gtk-icon-size: 1.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
@use "./_variable.scss" as *;
|
|
||||||
|
|
||||||
window.launcher {
|
|
||||||
background: $bg-color;
|
|
||||||
border-radius: $rounded;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
|
|
||||||
.search {
|
|
||||||
min-width: 300px;
|
|
||||||
padding: 10px;
|
|
||||||
color: rgba($fg-color, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
separator {
|
|
||||||
border: 1px solid rgba($inactive-color, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
background: $bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
border-radius: $rounded;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background: $inactive-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
image {
|
|
||||||
-gtk-icon-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
color: $fg-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,4 +3,4 @@ $fg-color: #fff;
|
|||||||
$bg-color: #101213;
|
$bg-color: #101213;
|
||||||
$inactive-color: #1b1d1e;
|
$inactive-color: #1b1d1e;
|
||||||
$rounded: 14px;
|
$rounded: 14px;
|
||||||
$font-size: 1rem;
|
$font-size: 0.8rem;
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
// bar
|
// bar
|
||||||
@use "./_bar.scss";
|
@use "./_bar.scss";
|
||||||
|
|
||||||
// launcher
|
|
||||||
@use "./_launcher.scss";
|
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
@use "./_notification.scss";
|
@use "./_notification.scss";
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import { Tray } from "./tray";
|
|||||||
import { WorkspaceButton } from "./workspace";
|
import { WorkspaceButton } from "./workspace";
|
||||||
import { Bluetooth } from "./bluetooth";
|
import { Bluetooth } from "./bluetooth";
|
||||||
import { Cava } from "./cava";
|
import { Cava } from "./cava";
|
||||||
|
import { onCleanup } from "gnim";
|
||||||
|
import { execAsync } from "ags/process";
|
||||||
|
|
||||||
export const WINDOW_NAME = "bar";
|
export const WINDOW_NAME = "bar";
|
||||||
|
|
||||||
export const Bar = (gdkmonitor: Gdk.Monitor) => {
|
export const Bar = ({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) => {
|
||||||
const { TOP } = Astal.WindowAnchor;
|
const { TOP } = Astal.WindowAnchor;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -24,10 +26,14 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
|
|||||||
anchor={TOP}
|
anchor={TOP}
|
||||||
widthRequest={1200}
|
widthRequest={1200}
|
||||||
application={app}
|
application={app}
|
||||||
|
$={(self) => onCleanup(() => self.destroy())}
|
||||||
>
|
>
|
||||||
<centerbox>
|
<centerbox>
|
||||||
<box spacing={10} $type="start">
|
<box spacing={10} $type="start">
|
||||||
<Launcher windowName="launcher" icon="nix-symbolic" />
|
<Launcher
|
||||||
|
icon="nix-symbolic"
|
||||||
|
onClicked={() => execAsync("vicinae toggle")}
|
||||||
|
/>
|
||||||
<Network />
|
<Network />
|
||||||
<Battery />
|
<Battery />
|
||||||
</box>
|
</box>
|
||||||
@@ -42,7 +48,10 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
|
|||||||
<Bluetooth />
|
<Bluetooth />
|
||||||
<Tray />
|
<Tray />
|
||||||
<Time />
|
<Time />
|
||||||
<Launcher windowName="control-center" icon="fa-ghost-symbolic" />
|
<Launcher
|
||||||
|
icon="fa-ghost-symbolic"
|
||||||
|
onClicked={() => app.toggle_window("control-center")}
|
||||||
|
/>
|
||||||
</box>
|
</box>
|
||||||
</centerbox>
|
</centerbox>
|
||||||
</window>
|
</window>
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
import { Gdk } from "ags/gtk4";
|
import { Gdk, Gtk } from "ags/gtk4";
|
||||||
import app from "ags/gtk4/app";
|
|
||||||
import { WINDOW_NAME as APP_LAUNCHER_WINDOW_NAME } from "../launcher";
|
|
||||||
import { WINDOW_NAME as CONTROL_CENTER_WINDOW_NAME } from "../control-center";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
icon: string;
|
icon: string;
|
||||||
windowName:
|
onClicked?: ((source: Gtk.Button) => void) | undefined;
|
||||||
| typeof APP_LAUNCHER_WINDOW_NAME
|
|
||||||
| typeof CONTROL_CENTER_WINDOW_NAME;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Launcher = ({ icon, windowName }: Props) => {
|
export const Launcher = ({ icon, onClicked }: Props) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
cssClasses={["launcher"]}
|
cssClasses={["launcher"]}
|
||||||
onClicked={() => app.toggle_window(windowName)}
|
onClicked={onClicked}
|
||||||
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
||||||
>
|
>
|
||||||
<image iconName={icon} />
|
<image iconName={icon} />
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ export const Network = () => {
|
|||||||
const wifi = net.wifi;
|
const wifi = net.wifi;
|
||||||
switch (wifi.internet) {
|
switch (wifi.internet) {
|
||||||
case 0:
|
case 0:
|
||||||
const ssid = wifi.ssid;
|
let ssid = wifi.ssid ? wifi.ssid : "";
|
||||||
ssid ? ssid : "";
|
|
||||||
let text = ssid.length > 10 ? ssid.substring(0, 10) + "..." : ssid;
|
let text = ssid.length > 10 ? ssid.substring(0, 10) + "..." : ssid;
|
||||||
return text;
|
return text;
|
||||||
case 1:
|
case 1:
|
||||||
@@ -39,7 +38,7 @@ export const Network = () => {
|
|||||||
cssClasses={["pill"]}
|
cssClasses={["pill"]}
|
||||||
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
||||||
onClicked={() =>
|
onClicked={() =>
|
||||||
execAsync(`wezterm start --class wezterm-floating nmtui`)
|
execAsync(`wezterm start --class wezterm-floating impala`)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<box spacing={5}>
|
<box spacing={5}>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const Workspace = ({ ws }: WsButtonProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const WorkspaceButton = () => {
|
export const WorkspaceButton = () => {
|
||||||
const range = Array.from({ length: 4 + 1 }, (_, i) => i);
|
const range = Array.from({ length: 6 + 1 }, (_, i) => i);
|
||||||
return (
|
return (
|
||||||
<box cssClasses={["workspace-container"]} spacing={4}>
|
<box cssClasses={["workspace-container"]} spacing={4}>
|
||||||
{range.map((i) => (
|
{range.map((i) => (
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { Header } from "./header";
|
|||||||
import { NotificationList } from "./notification-list";
|
import { NotificationList } from "./notification-list";
|
||||||
import { SlidingControls } from "./sliding-controls";
|
import { SlidingControls } from "./sliding-controls";
|
||||||
import { SystemInfo } from "./system-info";
|
import { SystemInfo } from "./system-info";
|
||||||
|
import { onCleanup } from "ags";
|
||||||
|
|
||||||
export const WINDOW_NAME = "control-center";
|
export const WINDOW_NAME = "control-center";
|
||||||
|
|
||||||
export const ControlCenter = (gdkmonitor: Gdk.Monitor) => {
|
export const ControlCenter = ({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) => {
|
||||||
const { TOP, BOTTOM, RIGHT } = Astal.WindowAnchor;
|
const { TOP, BOTTOM, RIGHT } = Astal.WindowAnchor;
|
||||||
const { VERTICAL } = Gtk.Orientation;
|
const { VERTICAL } = Gtk.Orientation;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ export const ControlCenter = (gdkmonitor: Gdk.Monitor) => {
|
|||||||
application={app}
|
application={app}
|
||||||
keymode={Astal.Keymode.ON_DEMAND}
|
keymode={Astal.Keymode.ON_DEMAND}
|
||||||
anchor={TOP | BOTTOM | RIGHT}
|
anchor={TOP | BOTTOM | RIGHT}
|
||||||
|
$={(self) => onCleanup(() => self.destroy())}
|
||||||
>
|
>
|
||||||
<Gtk.EventControllerKey onKeyPressed={onKey} />
|
<Gtk.EventControllerKey onKeyPressed={onKey} />
|
||||||
<box vexpand orientation={VERTICAL} spacing={20}>
|
<box vexpand orientation={VERTICAL} spacing={20}>
|
||||||
|
|||||||
@@ -1,11 +1,53 @@
|
|||||||
import { Gtk } from "ags/gtk4";
|
import { Gdk, Gtk } from "ags/gtk4";
|
||||||
|
import Notifd from "gi://AstalNotifd";
|
||||||
|
import { createComputed, createBinding } from "ags";
|
||||||
|
|
||||||
export const NotificationList = () => {
|
export const NotificationList = () => {
|
||||||
const { VERTICAL } = Gtk.Orientation;
|
const { VERTICAL } = Gtk.Orientation;
|
||||||
|
|
||||||
|
const notifd = Notifd.get_default();
|
||||||
|
const isDndMode = createBinding(notifd, "dont-disturb");
|
||||||
|
|
||||||
|
const toggleDndMode = () => {
|
||||||
|
const currentDnd = notifd.get_dont_disturb();
|
||||||
|
notifd.set_dont_disturb(!currentDnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const notificationIcon = createComputed([isDndMode], (dndMode) => {
|
||||||
|
if (dndMode) {
|
||||||
|
return "fa-bell-off-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "fa-bell-symbolic";
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box vexpand orientation={VERTICAL} spacing={20}>
|
<box
|
||||||
<label label="Notifications" />
|
vexpand
|
||||||
|
hexpand
|
||||||
|
orientation={VERTICAL}
|
||||||
|
spacing={20}
|
||||||
|
cssClasses={["notification-list"]}
|
||||||
|
>
|
||||||
|
<box hexpand>
|
||||||
|
<label label="Notifications" />
|
||||||
|
<box hexpand halign={Gtk.Align.END} spacing={10}>
|
||||||
|
<button
|
||||||
|
onClicked={toggleDndMode}
|
||||||
|
tooltipText={isDndMode((dnd) =>
|
||||||
|
dnd ? "Disable Do Not Disturb" : "Enable Do Not Disturb",
|
||||||
|
)}
|
||||||
|
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
||||||
|
class={isDndMode((dnd) => (dnd ? "focus" : ""))}
|
||||||
|
>
|
||||||
|
<image iconName={notificationIcon} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button cursor={Gdk.Cursor.new_from_name("pointer", null)}>
|
||||||
|
<image iconName="fa-broom-symbolic" />
|
||||||
|
</button>
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
<box vexpand cssClasses={["pill"]}></box>
|
<box vexpand cssClasses={["pill"]}></box>
|
||||||
</box>
|
</box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { createPoll } from "ags/time";
|
|||||||
export const SystemInfo = () => {
|
export const SystemInfo = () => {
|
||||||
const cpu = PollCMD("cat /sys/class/thermal/thermal_zone*/temp");
|
const cpu = PollCMD("cat /sys/class/thermal/thermal_zone*/temp");
|
||||||
const gpu = PollCMD("supergfxctl -g");
|
const gpu = PollCMD("supergfxctl -g");
|
||||||
const profile = PollCMD("asusctl profile -p");
|
const profile = PollCMD("asusctl profile get");
|
||||||
const tailscale = PollCMD("tailscale ping homelab");
|
const home = PollCMD("ping 100.64.0.3 -c 1");
|
||||||
const cpuUsage = PollCMD(
|
const cpuUsage = PollCMD(
|
||||||
`awk '{u=$2+$4; t=$2+$3+$4+$5+$6+$7+$8; if(NR==1){u1=u; t1=t}else{printf "%.1f%%",(u-u1)*100/(t-t1)}}' <(grep '^cpu ' /proc/stat; sleep 1; grep '^cpu ' /proc/stat)`,
|
`awk '{u=$2+$4; t=$2+$3+$4+$5+$6+$7+$8; if(NR==1){u1=u; t1=t}else{printf "%.1f%%",(u-u1)*100/(t-t1)}}' <(grep '^cpu ' /proc/stat; sleep 1; grep '^cpu ' /proc/stat)`,
|
||||||
2000,
|
2000,
|
||||||
@@ -66,8 +66,9 @@ export const SystemInfo = () => {
|
|||||||
<image iconName="fa-speed-symbolic" />
|
<image iconName="fa-speed-symbolic" />
|
||||||
<label
|
<label
|
||||||
label={profile((val) => {
|
label={profile((val) => {
|
||||||
const data = val.split(" ");
|
const match = val.match(/^Active profile:\s*(.+)$/m);
|
||||||
return data[data.length - 1];
|
const activeProfile = match?.[1]?.trim() ?? "NA";
|
||||||
|
return activeProfile;
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</box>
|
</box>
|
||||||
@@ -80,9 +81,9 @@ export const SystemInfo = () => {
|
|||||||
<image iconName="fa-home-symbolic" />
|
<image iconName="fa-home-symbolic" />
|
||||||
|
|
||||||
<label
|
<label
|
||||||
label={tailscale((val) => {
|
label={home((val) => {
|
||||||
const data = val.split(" ");
|
const time = `${parseInt(val.match(/time=([\d.]+)\s*ms/)?.[1] ?? "0", 10)}ms`;
|
||||||
return data[data.length - 1];
|
return time;
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</box>
|
</box>
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
import { createState, For } from "ags";
|
|
||||||
import { Astal, Gdk, Gtk } from "ags/gtk4";
|
|
||||||
import app from "ags/gtk4/app";
|
|
||||||
import AstalApps from "gi://AstalApps";
|
|
||||||
|
|
||||||
export const WINDOW_NAME = "launcher";
|
|
||||||
|
|
||||||
let searchEntry: Gtk.Entry;
|
|
||||||
const apps = new AstalApps.Apps();
|
|
||||||
const [searchText, setSearchText] = createState("");
|
|
||||||
|
|
||||||
const hide = () => {
|
|
||||||
app.get_window(WINDOW_NAME)?.set_visible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppButton = ({ app }: { app: AstalApps.Application }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
cssClasses={["button"]}
|
|
||||||
onClicked={() => {
|
|
||||||
hide();
|
|
||||||
app.launch();
|
|
||||||
}}
|
|
||||||
cursor={Gdk.Cursor.new_from_name("pointer", null)}
|
|
||||||
>
|
|
||||||
<box spacing={6} halign={Gtk.Align.START} valign={Gtk.Align.CENTER}>
|
|
||||||
<image iconName={app.iconName} iconSize={Gtk.IconSize.LARGE} />
|
|
||||||
<label cssClasses={["name"]} xalign={0} label={app.name} />
|
|
||||||
</box>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppList = () => {
|
|
||||||
const appList = searchText((text) => apps.fuzzy_query(text));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Gtk.ScrolledWindow vexpand heightRequest={500} widthRequest={350}>
|
|
||||||
<box
|
|
||||||
spacing={5}
|
|
||||||
orientation={Gtk.Orientation.VERTICAL}
|
|
||||||
cssClasses={["list"]}
|
|
||||||
>
|
|
||||||
<For each={appList}>{(app) => <AppButton app={app} />}</For>
|
|
||||||
</box>
|
|
||||||
</Gtk.ScrolledWindow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppSearch = () => {
|
|
||||||
return (
|
|
||||||
<entry
|
|
||||||
$={(ref) => (searchEntry = ref)}
|
|
||||||
cssClasses={["search"]}
|
|
||||||
text={searchText}
|
|
||||||
placeholderText="Search..."
|
|
||||||
onNotifyText={({ text }) => setSearchText(text)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Launcher = (gdkmonitor: Gdk.Monitor) => {
|
|
||||||
return (
|
|
||||||
<window
|
|
||||||
name={WINDOW_NAME}
|
|
||||||
cssClasses={["launcher"]}
|
|
||||||
gdkmonitor={gdkmonitor}
|
|
||||||
application={app}
|
|
||||||
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
|
||||||
keymode={Astal.Keymode.ON_DEMAND}
|
|
||||||
onNotifyVisible={({ visible }) => {
|
|
||||||
if (visible) searchEntry.grab_focus();
|
|
||||||
else searchEntry.set_text("");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Gtk.EventControllerKey onKeyPressed={onKey} />
|
|
||||||
|
|
||||||
<box orientation={Gtk.Orientation.VERTICAL} spacing={5}>
|
|
||||||
<AppSearch />
|
|
||||||
<Gtk.Separator orientation={Gtk.Orientation.HORIZONTAL} />
|
|
||||||
<AppList />
|
|
||||||
</box>
|
|
||||||
</window>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKey = (
|
|
||||||
_e: Gtk.EventControllerKey,
|
|
||||||
keyval: number,
|
|
||||||
_: number,
|
|
||||||
mod: number,
|
|
||||||
) => {
|
|
||||||
if (keyval === Gdk.KEY_Escape) {
|
|
||||||
app.toggle_window(WINDOW_NAME);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
import { For, createState, onCleanup } from "ags";
|
import {
|
||||||
|
For,
|
||||||
|
createBinding,
|
||||||
|
createComputed,
|
||||||
|
createState,
|
||||||
|
onCleanup,
|
||||||
|
} from "ags";
|
||||||
import { Astal, Gdk, Gtk } from "ags/gtk4";
|
import { Astal, Gdk, Gtk } from "ags/gtk4";
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import { Notification } from "./notification";
|
import { Notification } from "./notification";
|
||||||
|
|
||||||
export const WINDOW_NAME = "notifications";
|
export const WINDOW_NAME = "notifications";
|
||||||
|
|
||||||
export const Notifications = (gdkmonitor: Gdk.Monitor) => {
|
export const Notifications = ({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) => {
|
||||||
const notifd = AstalNotifd.get_default();
|
const notifd = AstalNotifd.get_default();
|
||||||
|
const isDndMode = createBinding(notifd, "dont-disturb");
|
||||||
|
|
||||||
const { TOP, RIGHT } = Astal.WindowAnchor;
|
const { TOP, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
@@ -33,13 +40,20 @@ export const Notifications = (gdkmonitor: Gdk.Monitor) => {
|
|||||||
notifd.disconnect(resolvedHandler);
|
notifd.disconnect(resolvedHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shouldShowWindow = createComputed(
|
||||||
|
[notifications, isDndMode],
|
||||||
|
(notifs, dndEnabled) => {
|
||||||
|
return !dndEnabled && notifs.length > 0;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<window
|
<window
|
||||||
$={(self) => onCleanup(() => self.destroy())}
|
$={(self) => onCleanup(() => self.destroy())}
|
||||||
name={WINDOW_NAME}
|
name={WINDOW_NAME}
|
||||||
cssClasses={["notifications"]}
|
cssClasses={["notifications"]}
|
||||||
gdkmonitor={gdkmonitor}
|
gdkmonitor={gdkmonitor}
|
||||||
visible={notifications((ns) => ns.length > 0)}
|
visible={shouldShowWindow}
|
||||||
anchor={TOP | RIGHT}
|
anchor={TOP | RIGHT}
|
||||||
>
|
>
|
||||||
<box orientation={Gtk.Orientation.VERTICAL} spacing={10}>
|
<box orientation={Gtk.Orientation.VERTICAL} spacing={10}>
|
||||||
|
|||||||
50
widgets/notifications/manager.ts
Normal file
50
widgets/notifications/manager.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import GLib from "gi://GLib";
|
||||||
|
|
||||||
|
type TimeoutManager = {
|
||||||
|
setupTimeout: () => void;
|
||||||
|
clearTimeout: () => void;
|
||||||
|
handleHover: () => void;
|
||||||
|
handleHoverLost: () => void;
|
||||||
|
cleanup: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTimeoutManager = (
|
||||||
|
dismissCallback: () => void,
|
||||||
|
timeoutDelay: number,
|
||||||
|
): TimeoutManager => {
|
||||||
|
let isHovered = false;
|
||||||
|
let timeoutId: number | null = null;
|
||||||
|
|
||||||
|
const clearTimeout = () => {
|
||||||
|
if (timeoutId !== null) {
|
||||||
|
GLib.source_remove(timeoutId);
|
||||||
|
timeoutId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupTimeout = () => {
|
||||||
|
clearTimeout();
|
||||||
|
|
||||||
|
if (!isHovered) {
|
||||||
|
timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeoutDelay, () => {
|
||||||
|
clearTimeout();
|
||||||
|
dismissCallback();
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
setupTimeout,
|
||||||
|
clearTimeout,
|
||||||
|
handleHover: () => {
|
||||||
|
isHovered = true;
|
||||||
|
clearTimeout();
|
||||||
|
},
|
||||||
|
handleHoverLost: () => {
|
||||||
|
isHovered = false;
|
||||||
|
setupTimeout();
|
||||||
|
},
|
||||||
|
cleanup: clearTimeout,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -3,6 +3,8 @@ import Adw from "gi://Adw";
|
|||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
import Pango from "gi://Pango";
|
import Pango from "gi://Pango";
|
||||||
|
import { createTimeoutManager } from "./manager";
|
||||||
|
import { onCleanup, onMount } from "ags";
|
||||||
|
|
||||||
const isIcon = (icon?: string | null) => {
|
const isIcon = (icon?: string | null) => {
|
||||||
const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!);
|
const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!);
|
||||||
@@ -33,7 +35,20 @@ const urgency = (n: AstalNotifd.Notification) => {
|
|||||||
const { VERTICAL } = Gtk.Orientation;
|
const { VERTICAL } = Gtk.Orientation;
|
||||||
const { START, END, CENTER } = Gtk.Align;
|
const { START, END, CENTER } = Gtk.Align;
|
||||||
|
|
||||||
|
// Keep track of notification validity
|
||||||
|
const TIMEOUT_DELAY = 3000;
|
||||||
|
|
||||||
export const Notification = ({ n }: { n: AstalNotifd.Notification }) => {
|
export const Notification = ({ n }: { n: AstalNotifd.Notification }) => {
|
||||||
|
const timeoutManager = createTimeoutManager(() => n.dismiss(), TIMEOUT_DELAY);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
timeoutManager.setupTimeout();
|
||||||
|
});
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
timeoutManager.cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Adw.Clamp maximumSize={400}>
|
<Adw.Clamp maximumSize={400}>
|
||||||
<box
|
<box
|
||||||
@@ -42,6 +57,11 @@ export const Notification = ({ n }: { n: AstalNotifd.Notification }) => {
|
|||||||
orientation={VERTICAL}
|
orientation={VERTICAL}
|
||||||
spacing={20}
|
spacing={20}
|
||||||
>
|
>
|
||||||
|
<Gtk.EventControllerMotion
|
||||||
|
onEnter={() => timeoutManager.handleHover()}
|
||||||
|
onLeave={() => timeoutManager.handleHoverLost()}
|
||||||
|
/>
|
||||||
|
|
||||||
<box class="header" spacing={10}>
|
<box class="header" spacing={10}>
|
||||||
{(n.appIcon || isIcon(n.desktopEntry)) && (
|
{(n.appIcon || isIcon(n.desktopEntry)) && (
|
||||||
<image
|
<image
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Bar } from "./widgets/bar";
|
|
||||||
import { Launcher } from "./widgets/launcher";
|
|
||||||
import { Notifications } from "./widgets/notifications";
|
|
||||||
import { ControlCenter } from "./widgets/control-center";
|
|
||||||
|
|
||||||
export default [Bar, Launcher, Notifications, ControlCenter];
|
|
||||||
Reference in New Issue
Block a user