Compare commits
2 commits
ed206ad7e5
...
7da7b506f9
Author | SHA1 | Date | |
---|---|---|---|
7da7b506f9 | |||
2c9ff9db2f |
8 changed files with 207 additions and 142 deletions
44
flake.nix
44
flake.nix
|
@ -31,56 +31,38 @@
|
||||||
src = ./.;
|
src = ./.;
|
||||||
name = "my-shell";
|
name = "my-shell";
|
||||||
entry = "app.ts";
|
entry = "app.ts";
|
||||||
|
|
||||||
# additional libraries and executables to add to gjs' runtime
|
|
||||||
extraPackages = [
|
|
||||||
# ags.packages.${system}.battery
|
|
||||||
# pkgs.fzf
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.${system} = {
|
devShells.${system} = {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
nodejs_23
|
|
||||||
gobject-introspection
|
|
||||||
json-glib
|
|
||||||
gjs
|
|
||||||
# includes astal3 astal4 astal-io by default
|
# includes astal3 astal4 astal-io by default
|
||||||
(ags.packages.${system}.default.override {
|
(ags.packages.${system}.default.override {
|
||||||
extraPackages = builtins.attrValues {
|
extraPackages = builtins.attrValues {
|
||||||
inherit
|
inherit
|
||||||
(astal.packages.${system})
|
(astal.packages.${system})
|
||||||
hyprland
|
io
|
||||||
tray
|
astal4
|
||||||
network
|
|
||||||
battery
|
battery
|
||||||
|
wireplumber
|
||||||
|
network
|
||||||
|
mpris
|
||||||
|
powerprofiles
|
||||||
|
tray
|
||||||
|
bluetooth
|
||||||
|
hyprland
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapGAppsHook
|
nodejs_23
|
||||||
gobject-introspection
|
|
||||||
meson
|
meson
|
||||||
pkg-config
|
|
||||||
ninja
|
ninja
|
||||||
vala
|
pkg-config
|
||||||
gtk3
|
gobject-introspection
|
||||||
gtk4
|
wrapGAppsHook4
|
||||||
gtk-layer-shell
|
|
||||||
gtk4-layer-shell
|
|
||||||
json-glib
|
|
||||||
pam
|
|
||||||
gvfs
|
|
||||||
networkmanager
|
|
||||||
gdk-pixbuf
|
|
||||||
wireplumber
|
|
||||||
libdbusmenu-gtk3
|
|
||||||
wayland
|
|
||||||
blueprint-compiler
|
blueprint-compiler
|
||||||
libadwaita
|
|
||||||
wayland-scanner
|
|
||||||
dart-sass
|
dart-sass
|
||||||
esbuild
|
esbuild
|
||||||
gjs
|
gjs
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"watch": ["app.ts", "widget/"],
|
"watch": ["app.ts", "style.scss", "widget/"],
|
||||||
"ext": "ts, tsx",
|
"ext": "ts, tsx, scss, css",
|
||||||
"exec": "ags run --gtk4 ."
|
"exec": "ags run --gtk4 ."
|
||||||
}
|
}
|
||||||
|
|
106
style.scss
106
style.scss
|
@ -1,43 +1,97 @@
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
$fg-color: #{"@theme_fg_color"};
|
// Colors:
|
||||||
|
|
||||||
|
$fg-color: white;
|
||||||
|
$active-color: #bdae93;
|
||||||
|
$hover-color: #b8bb26;
|
||||||
|
|
||||||
$bg-color: #{"@theme_bg_color"};
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
$corner-size: 7;
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
window.Bar {
|
window.Bar {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
/* background: #fbf1c7; */
|
||||||
|
padding: 2px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
|
||||||
color: $fg-color;
|
color: $fg-color;
|
||||||
font-weight: bold;
|
|
||||||
font-size: 25px;
|
|
||||||
|
|
||||||
>centerbox {
|
>centerbox {
|
||||||
background: $bg-color;
|
background: $bg-color;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.AudioSlider {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.Systray {
|
||||||
|
color: $fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Workspaces {
|
||||||
|
$workspace-button-size: 8px;
|
||||||
|
$bar-button-border-radius: 25px;
|
||||||
|
|
||||||
|
margin: 0 60px 4px 60px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 40px;
|
||||||
|
transition: all 1 linear;
|
||||||
|
transition-delay: 1s;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
border-radius: 8px;
|
all: unset;
|
||||||
margin: 2px;
|
color: $fg-color;
|
||||||
}
|
transition: min-width 0.25s ease-out;
|
||||||
|
margin: 1em;
|
||||||
|
padding: $workspace-button-size;
|
||||||
|
margin: $workspace-button-size;
|
||||||
|
background-color: black;
|
||||||
|
border-radius: calc(nth($bar-button-border-radius, 1) * 1.5);
|
||||||
|
/* Hide non-occupied workspaces */
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0;
|
||||||
|
padding: 0;
|
||||||
|
transition: 0;
|
||||||
|
|
||||||
.Workspaces {
|
&:hover {
|
||||||
button {
|
color: $fg-color;
|
||||||
all: unset;
|
background-color: $fg-color;
|
||||||
background-color: transparent;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
|
|
||||||
&:hover label {
|
|
||||||
background-color: red;
|
|
||||||
border-color: $bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active label {
|
|
||||||
background-color: $fg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.focused {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&.occupied {
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
margin: 1rem;
|
||||||
|
padding: $workspace-button-size;
|
||||||
|
margin: $workspace-button-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
min-width: 2.5rem !important;
|
||||||
|
min-height: 0.95rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.occupied {
|
||||||
|
min-height: 0.65em;
|
||||||
|
min-width: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,85 +1,10 @@
|
||||||
import { App, Astal, Gtk, Gdk, Widget, astalify } from "astal/gtk4"
|
import { App, Astal, Gtk, Gdk, } from "astal/gtk4"
|
||||||
import { GLib, Variable } from "astal"
|
import Workspaces from "./workspaces"
|
||||||
import Hyprland from "gi://AstalHyprland"
|
import Systray from "./systray"
|
||||||
import Tray from "gi://AstalTray"
|
import Battery from "./battery"
|
||||||
import Battery from "gi://AstalBattery"
|
import Time from "./time"
|
||||||
import Network from "gi://AstalNetwork"
|
|
||||||
import AstalTray from "gi://AstalTray?version=0.1"
|
|
||||||
|
|
||||||
|
|
||||||
type Workspace = {
|
|
||||||
id: number
|
|
||||||
focus: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hyprland = {
|
|
||||||
workspaces: Workspace[]
|
|
||||||
focusedWorkspace: Workspace | null
|
|
||||||
}
|
|
||||||
|
|
||||||
function Bat() {
|
|
||||||
const bat = Battery.get_default()
|
|
||||||
|
|
||||||
return <box cssClasses={["Battery"]}>
|
|
||||||
Battery: {bat.get_percentage() * 100}%
|
|
||||||
</box>
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function Net() {
|
|
||||||
const network = Network.get_default()
|
|
||||||
|
|
||||||
return <box cssClasses={["Network"]} >
|
|
||||||
{network.wifi.enabled}
|
|
||||||
</box>
|
|
||||||
}
|
|
||||||
|
|
||||||
function Workspaces() {
|
|
||||||
const hypr = Hyprland.get_default()
|
|
||||||
|
|
||||||
return <box cssClasses={["Workspaces"]} >
|
|
||||||
{
|
|
||||||
hypr.workspaces.filter(ws => !(ws.id >= -99 && ws.id < -2))
|
|
||||||
.sort((a, b) => a.id - b.id)
|
|
||||||
.map(ws => (
|
|
||||||
<button
|
|
||||||
cssClasses={
|
|
||||||
ws === hypr.focusedWorkspace ? ["focused"] : []}
|
|
||||||
onClicked={() => ws.focus()}>
|
|
||||||
{ws.id}
|
|
||||||
</button>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</box >
|
|
||||||
}
|
|
||||||
|
|
||||||
function SysTray() {
|
|
||||||
const tray = AstalTray.get_default()
|
|
||||||
|
|
||||||
print(tray.get_items.length)
|
|
||||||
|
|
||||||
return <box
|
|
||||||
cssClasses={["SysTray"]}
|
|
||||||
>
|
|
||||||
{tray.get_items().map(item => (
|
|
||||||
<menubutton
|
|
||||||
tooltipMarkup={item.tooltipMarkup}
|
|
||||||
menuModel={item.menuModel}
|
|
||||||
>
|
|
||||||
<image gicon={item.get_gicon()} />
|
|
||||||
</menubutton>
|
|
||||||
))}
|
|
||||||
</box>
|
|
||||||
}
|
|
||||||
|
|
||||||
function Time({ }) {
|
|
||||||
const time = Variable<string>("").poll(1000, () => GLib.DateTime.new_now_local().format("%H:%M")!)
|
|
||||||
|
|
||||||
return <label
|
|
||||||
cssName="Time"
|
|
||||||
onDestroy={() => time.drop()}
|
|
||||||
label={time()} />
|
|
||||||
}
|
|
||||||
export default function Bar(gdkmonitor: Gdk.Monitor) {
|
export default function Bar(gdkmonitor: Gdk.Monitor) {
|
||||||
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
|
||||||
|
|
||||||
|
@ -92,15 +17,14 @@ export default function Bar(gdkmonitor: Gdk.Monitor) {
|
||||||
application={App}>
|
application={App}>
|
||||||
<centerbox>
|
<centerbox>
|
||||||
<box hexpand halign={Gtk.Align.START}>
|
<box hexpand halign={Gtk.Align.START}>
|
||||||
<Bat />
|
<Battery />
|
||||||
<SysTray />
|
|
||||||
</box>
|
</box>
|
||||||
<box hexpand halign={Gtk.Align.CENTER}>
|
<box hexpand halign={Gtk.Align.CENTER}>
|
||||||
<Workspaces />
|
<Workspaces />
|
||||||
</box>
|
</box>
|
||||||
<box hexpand halign={Gtk.Align.END}>
|
<box hexpand halign={Gtk.Align.END}>
|
||||||
<Net />
|
|
||||||
<Time />
|
<Time />
|
||||||
|
<Systray />
|
||||||
</box>
|
</box>
|
||||||
|
|
||||||
</centerbox>
|
</centerbox>
|
||||||
|
|
14
widget/battery.tsx
Normal file
14
widget/battery.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { bind } from "astal"
|
||||||
|
import Battery from "gi://AstalBattery"
|
||||||
|
|
||||||
|
export default function BatteryLevel() {
|
||||||
|
const bat = Battery.get_default()
|
||||||
|
|
||||||
|
return <box
|
||||||
|
cssClasses={["Battery"]}
|
||||||
|
visible={bind(bat, "isPresent")}>
|
||||||
|
<image iconName={bind(bat, "batteryIconName")} />
|
||||||
|
<label label={bind(bat, "percentage").as(p => `${Math.floor(p * 100)}%`)} />
|
||||||
|
</box>
|
||||||
|
|
||||||
|
}
|
22
widget/systray.tsx
Normal file
22
widget/systray.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { bind } from "astal"
|
||||||
|
import AstalTray from "gi://AstalTray?version=0.1"
|
||||||
|
|
||||||
|
export default function Systray() {
|
||||||
|
const tray = AstalTray.get_default()
|
||||||
|
|
||||||
|
return <box cssClasses={["Systray"]} >
|
||||||
|
{
|
||||||
|
bind(tray, "items").as(items => items.map(item => (
|
||||||
|
<menubutton
|
||||||
|
tooltipMarkup={bind(item, "tooltipMarkup")}
|
||||||
|
menuModel={bind(item, "menuModel")}
|
||||||
|
setup={(self) => {
|
||||||
|
self.insert_action_group("dbusmenu", item.actionGroup)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<image gicon={bind(item, "gicon")} />
|
||||||
|
</menubutton>
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
</box >
|
||||||
|
}
|
15
widget/time.tsx
Normal file
15
widget/time.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { GLib, Variable, } from "astal"
|
||||||
|
import { App, Gtk, Gdk } from "astal/gtk4";
|
||||||
|
|
||||||
|
export default function Time({ }) {
|
||||||
|
const time = Variable<string>("").poll(1000, () => GLib.DateTime.new_now_local().format("%H:%M")!)
|
||||||
|
|
||||||
|
|
||||||
|
return <label
|
||||||
|
cssName="Time"
|
||||||
|
onDestroy={() => time.drop()}
|
||||||
|
label={time()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
54
widget/workspaces.tsx
Normal file
54
widget/workspaces.tsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import Hyprland from "gi://AstalHyprland"
|
||||||
|
import { bind, Variable } from "astal"
|
||||||
|
import { Gtk } from "astal/gtk4"
|
||||||
|
|
||||||
|
function WorkspaceButton(ws: Hyprland.Workspace) {
|
||||||
|
const hypr = Hyprland.get_default()
|
||||||
|
|
||||||
|
const classNames = Variable.derive(
|
||||||
|
[bind(hypr, "focusedWorkspace"), bind(hypr, "clients")],
|
||||||
|
(fws, _) => {
|
||||||
|
const classes = []
|
||||||
|
|
||||||
|
const active = fws.id == ws.id
|
||||||
|
|
||||||
|
active && classes.push("active")
|
||||||
|
|
||||||
|
const occupied = hypr.get_workspace(ws.id)?.get_clients()?.length > 0;
|
||||||
|
occupied && classes.push("occupied");
|
||||||
|
return classes;
|
||||||
|
})
|
||||||
|
|
||||||
|
const classes = classNames()
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<button
|
||||||
|
cssClasses={classes}
|
||||||
|
onDestroy={() => classNames.drop()}
|
||||||
|
// visible={classes.as(cn => cn.includes("occupied") || cn.includes("active"))}
|
||||||
|
valign={Gtk.Align.CENTER}
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
onClicked={() => ws.focus()}>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Workspaces() {
|
||||||
|
const hypr = Hyprland.get_default()
|
||||||
|
|
||||||
|
return <box cssClasses={["Workspaces"]}>
|
||||||
|
{bind(hypr, "workspaces").as((workspaces) =>
|
||||||
|
workspaces
|
||||||
|
.filter(ws => !(ws.id >= -99 && ws.id < -2))
|
||||||
|
.sort((a, b) => a.id - b.id)
|
||||||
|
.map((ws) => (
|
||||||
|
WorkspaceButton(ws)
|
||||||
|
)),
|
||||||
|
)}
|
||||||
|
</box >
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue