added stuff
This commit is contained in:
parent
e8d9044d2b
commit
9d0ebdfbd0
907 changed files with 70990 additions and 0 deletions
|
@ -0,0 +1,71 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
const { Window, Box, CenterBox } = Widget;
|
||||
|
||||
// Widgets
|
||||
import { LauncherIcon } from "./modules/launcher.js";
|
||||
import { Workspaces } from "./modules/workspaces.js";
|
||||
import { Tray } from "./modules/tray.js";
|
||||
import { BatteryWidget } from "./modules/battery.js";
|
||||
import { Clock } from "./modules/clock.js";
|
||||
import { PowerMenu } from "./modules/power.js";
|
||||
import { Swallow } from "./modules/swallow.js";
|
||||
import { BluetoothWidget } from "./modules/bluetooth.js";
|
||||
import { AudioWidget } from "./modules/audio.js";
|
||||
import { NetworkWidget } from "./modules/network.js";
|
||||
import { SystemUsage } from "./modules/system.js";
|
||||
import { Weather } from "./modules/weather.js";
|
||||
|
||||
const Top = () =>
|
||||
Box({
|
||||
className: "barTop",
|
||||
vertical: true,
|
||||
vpack: "start",
|
||||
children: [LauncherIcon(), SystemUsage(), Weather()],
|
||||
});
|
||||
|
||||
const Center = () =>
|
||||
Box({
|
||||
className: "barCenter",
|
||||
vertical: true,
|
||||
children: [Workspaces()],
|
||||
});
|
||||
|
||||
const Bottom = () =>
|
||||
Box({
|
||||
className: "barBottom",
|
||||
vertical: true,
|
||||
vpack: "end",
|
||||
children: [
|
||||
Tray(),
|
||||
Box({
|
||||
className: "utilsBox",
|
||||
vertical: true,
|
||||
children: [
|
||||
BluetoothWidget(),
|
||||
AudioWidget(),
|
||||
Swallow(),
|
||||
BatteryWidget(),
|
||||
NetworkWidget(),
|
||||
],
|
||||
}),
|
||||
Clock(),
|
||||
PowerMenu(),
|
||||
],
|
||||
});
|
||||
|
||||
export const Bar = ({ monitor } = {}) =>
|
||||
Window({
|
||||
name: "bar",
|
||||
anchor: ["top", "bottom", "left"],
|
||||
exclusivity: "exclusive",
|
||||
layer: "top",
|
||||
margins: [8, 0, 8, 8],
|
||||
monitor,
|
||||
child: CenterBox({
|
||||
className: "bar",
|
||||
vertical: true,
|
||||
startWidget: Top(),
|
||||
centerWidget: Center(),
|
||||
endWidget: Bottom(),
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import { Audio, Widget } from "../../../imports.js";
|
||||
import { getAudioIcon } from "../../../utils/audio.js";
|
||||
import { launchApp } from "../../../utils/global.js";
|
||||
|
||||
const { Button, Icon } = Widget;
|
||||
|
||||
const AudioIcon = () =>
|
||||
Icon({
|
||||
setup: (self) => {
|
||||
self.hook(Audio, getAudioIcon, "speaker-changed");
|
||||
},
|
||||
});
|
||||
|
||||
export const AudioWidget = () => {
|
||||
return Button({
|
||||
className: "audio",
|
||||
cursor: "pointer",
|
||||
visible: true,
|
||||
child: AudioIcon(),
|
||||
onClicked: () => launchApp("pavucontrol"),
|
||||
});
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
import { Widget, Battery } from "../../../imports.js";
|
||||
import {
|
||||
getBatteryPercentage,
|
||||
getBatteryTooltip,
|
||||
getBatteryIcon,
|
||||
} from "../../../utils/battery.js";
|
||||
const { Button, Box, Label, Revealer } = Widget;
|
||||
|
||||
const BatIcon = () =>
|
||||
Label({ className: "batIcon" })
|
||||
// NOTE: label needs to be used instead of icon here
|
||||
.bind("label", Battery, "percent", getBatteryIcon)
|
||||
.bind("tooltip-text", Battery, "percent", getBatteryTooltip);
|
||||
|
||||
const BatStatus = () =>
|
||||
Revealer({
|
||||
transition: "slide_down",
|
||||
transition_duration: 200,
|
||||
child: Label().bind("label", Battery, "percent", getBatteryPercentage),
|
||||
});
|
||||
|
||||
export const BatteryWidget = () =>
|
||||
Button({
|
||||
onPrimaryClick: (self) => {
|
||||
self.child.children[1].revealChild =
|
||||
!self.child.children[1].revealChild;
|
||||
},
|
||||
child: Box({
|
||||
className: "battery",
|
||||
cursor: "pointer",
|
||||
vertical: true,
|
||||
children: [BatIcon(), BatStatus()],
|
||||
visible: Battery.bind("available"),
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import { Bluetooth, Widget, Utils } from "../../../imports.js";
|
||||
import {
|
||||
getBluetoothIcon,
|
||||
getBluetoothLabel,
|
||||
getBluetoothClass,
|
||||
getBluetoothTooltip,
|
||||
} from "../../../utils/bluetooth.js";
|
||||
const { Button, Label } = Widget;
|
||||
|
||||
const BluetoothModule = () =>
|
||||
Label({ className: "bluetoothIcon" })
|
||||
.bind("label", Bluetooth, "connected-devices", getBluetoothIcon)
|
||||
.bind("class", Bluetooth, "connected-devices", getBluetoothClass)
|
||||
.bind("label", Bluetooth, "connected-devices", getBluetoothLabel)
|
||||
.bind(
|
||||
"tooltip-text",
|
||||
Bluetooth,
|
||||
"connected-devices",
|
||||
getBluetoothTooltip,
|
||||
);
|
||||
|
||||
export const BluetoothWidget = () =>
|
||||
Button({
|
||||
className: "bluetooth",
|
||||
cursor: "pointer",
|
||||
child: BluetoothModule(),
|
||||
visible: Bluetooth.connectedDevices.length > 0,
|
||||
onClicked: () => Utils.exec("blueman-applet"),
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
import { Widget, Utils } from "../../../imports.js";
|
||||
const { exec, execAsync } = Utils;
|
||||
const { Label, Box } = Widget;
|
||||
|
||||
const Time = () =>
|
||||
Label({
|
||||
className: "timeLabel",
|
||||
setup: (self) => {
|
||||
// the current quote syntax is the only one that works
|
||||
// eslint-disable-next-line quotes
|
||||
self.poll(1000, (self) => (self.label = exec('date "+%H%n%M"')));
|
||||
self.poll(1000, (self) =>
|
||||
execAsync(["date", "+%H%n%M"])
|
||||
.then((time) => (self.label = time))
|
||||
// eslint-disable-next-line no-undef
|
||||
.catch(print.error),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const Clock = () =>
|
||||
Box({
|
||||
className: "clock",
|
||||
vertical: true,
|
||||
children: [Time()],
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { Widget, App } from "../../../imports.js";
|
||||
import { getLauncherIcon } from "../../../utils/launcher.js";
|
||||
const { Button, Label } = Widget;
|
||||
|
||||
export const LauncherIcon = () =>
|
||||
Button({
|
||||
vexpand: false,
|
||||
className: "launcherIcon",
|
||||
cursor: "pointer",
|
||||
child: Label(""),
|
||||
onClicked: () => App.toggleWindow("launcher"),
|
||||
setup: (self) => {
|
||||
self.hook(App, getLauncherIcon, "window-toggled");
|
||||
},
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { Widget, Utils } from "../../../imports.js";
|
||||
const { Button, Label } = Widget;
|
||||
|
||||
export const Lock = () =>
|
||||
Button({
|
||||
className: "lock",
|
||||
cursor: "pointer",
|
||||
child: Label(""),
|
||||
onClicked: () => Utils.exec("swaylock"),
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import { Network, Widget, Utils } from "../../../imports.js";
|
||||
import {
|
||||
getWifiIcon,
|
||||
getWifiTooltip,
|
||||
getWiredIcon,
|
||||
getWiredTooltip,
|
||||
} from "../../../utils/network.js";
|
||||
const { Stack, Button, Label } = Widget;
|
||||
|
||||
const WifiIndicator = () =>
|
||||
Label({ has_tooltip: true })
|
||||
.bind("label", Network.wifi, "strength", getWifiIcon)
|
||||
.bind("tooltip-text", Network.wifi, "strength", getWifiTooltip);
|
||||
|
||||
const WiredIndicator = () =>
|
||||
Label({ cursor: "pointer" })
|
||||
.bind("label", Network.wired, "internet", getWiredIcon)
|
||||
.bind("tooltip-text", Network.wired, "internet", getWiredTooltip);
|
||||
|
||||
export const NetworkWidget = () =>
|
||||
Button({
|
||||
className: "network",
|
||||
cursor: "pointer",
|
||||
onClicked: () => Utils.exec("nm-connection-editor"),
|
||||
child: Stack({
|
||||
shown: Network.bind("primary").as(
|
||||
(/** @type {any} */ p) => p || "wifi",
|
||||
),
|
||||
children: {
|
||||
wifi: WifiIndicator(),
|
||||
wired: WiredIndicator(),
|
||||
},
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { Widget } from "../../../imports.js";
|
||||
const { Button, Label } = Widget;
|
||||
|
||||
export const PowerMenu = () =>
|
||||
Button({
|
||||
vexpand: false,
|
||||
className: "power",
|
||||
cursor: "pointer",
|
||||
child: Label(""),
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { Widget } from "../../../imports.js";
|
||||
const { Label, Button } = Widget;
|
||||
|
||||
import { toggleSwallowStatus, status } from "../../../utils/swallow.js";
|
||||
|
||||
export const Swallow = () =>
|
||||
Button({
|
||||
className: "swallow",
|
||||
cursor: "pointer",
|
||||
tooltipText: `Swallow: ${status.value}`,
|
||||
onPrimaryClick: toggleSwallowStatus,
|
||||
child: Label({
|
||||
label: "",
|
||||
}),
|
||||
}).hook(status, (self) => (self.tooltipText = `${status.value}`));
|
|
@ -0,0 +1,134 @@
|
|||
import { Variable, Widget } from "../../../imports.js";
|
||||
const { Button, Revealer, Box, Label, CircularProgress } = Widget;
|
||||
|
||||
const getMemClass = (v) => {
|
||||
const val = v * 100;
|
||||
const className = [
|
||||
[100, "memCritical"],
|
||||
[75, "memHigh"],
|
||||
[35, "memMod"],
|
||||
[5, "memLow"],
|
||||
[0, "memIdle"],
|
||||
[-1, "memRevealer"],
|
||||
].find(([threshold]) => threshold <= val)[1];
|
||||
|
||||
return className;
|
||||
};
|
||||
|
||||
const getCpuClass = (v) => {
|
||||
const val = v * 100;
|
||||
|
||||
const className = [
|
||||
[100, "cpuCritical"],
|
||||
[75, "cpuHigh"],
|
||||
[35, "cpuMod"],
|
||||
[5, "cpuLow"],
|
||||
[0, "cpuIdle"],
|
||||
[-1, "cpuRevealer"],
|
||||
].find(([threshold]) => threshold <= val)[1];
|
||||
|
||||
return className;
|
||||
};
|
||||
|
||||
const divide = ([total, free]) => free / total;
|
||||
|
||||
const cpu = Variable(0, {
|
||||
poll: [
|
||||
2000,
|
||||
"top -b -n 1",
|
||||
(out) =>
|
||||
divide([
|
||||
100,
|
||||
out
|
||||
.split("\n")
|
||||
.find((line) => line.includes("Cpu(s)"))
|
||||
.split(/\s+/)[1]
|
||||
.replace(",", "."),
|
||||
]),
|
||||
],
|
||||
});
|
||||
|
||||
const mem = Variable(0, {
|
||||
poll: [
|
||||
2000,
|
||||
"free",
|
||||
(out) =>
|
||||
divide(
|
||||
out
|
||||
.split("\n")
|
||||
.find((line) => line.includes("Mem:"))
|
||||
.split(/\s+/)
|
||||
.splice(1, 2),
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {typeof cpu | typeof ram} process
|
||||
* @param {Array<any>} extraChildren
|
||||
* @param {() => void} onPrimary
|
||||
*/
|
||||
const systemWidget = (name, process, extraChildren = [], onPrimary) =>
|
||||
Button({
|
||||
className: name + "Button",
|
||||
onPrimaryClick: onPrimary,
|
||||
child: Box({
|
||||
className: name,
|
||||
vertical: true,
|
||||
children: [
|
||||
CircularProgress({
|
||||
className: name + "Progress",
|
||||
// binds: [["value", process]],
|
||||
rounded: true,
|
||||
inverted: false,
|
||||
startAt: 0.27,
|
||||
}).bind("value", process),
|
||||
...extraChildren,
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const CPU = systemWidget(
|
||||
"cpu",
|
||||
cpu,
|
||||
[
|
||||
Revealer({
|
||||
transition: "slide_down",
|
||||
child: Label()
|
||||
.bind("label", cpu, "value", (v) => `${Math.floor(v * 100)}%`)
|
||||
.bind("className", cpu, "value", getCpuClass),
|
||||
transition_duration: 250,
|
||||
}),
|
||||
],
|
||||
(self) => {
|
||||
self.child.children[1].revealChild =
|
||||
!self.child.children[1].revealChild;
|
||||
},
|
||||
);
|
||||
|
||||
const MEM = systemWidget(
|
||||
"mem",
|
||||
mem,
|
||||
[
|
||||
Revealer({
|
||||
transition: "slide_down",
|
||||
child: Label()
|
||||
.bind("label", mem, "value", (v) => `${Math.floor(v * 100)}%`)
|
||||
.bind("className", cpu, "value", getMemClass),
|
||||
transition_duration: 250,
|
||||
}),
|
||||
],
|
||||
(self) => {
|
||||
self.child.children[1].revealChild =
|
||||
!self.child.children[1].revealChild;
|
||||
},
|
||||
);
|
||||
|
||||
export const SystemUsage = () =>
|
||||
Box({
|
||||
className: "systemUsage",
|
||||
vertical: true,
|
||||
cursor: "pointer",
|
||||
children: [CPU, MEM],
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import { Widget, SystemTray } from "../../../imports.js";
|
||||
import { getTrayItems } from "../../../utils/tray.js";
|
||||
const { Box, EventBox, Label, Revealer } = Widget;
|
||||
|
||||
const RevIcon = () =>
|
||||
Label({
|
||||
className: "trayChevron",
|
||||
label: "",
|
||||
});
|
||||
|
||||
const TrayItems = () =>
|
||||
Box({
|
||||
className: "trayIcons",
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
self.hook(SystemTray, getTrayItems);
|
||||
},
|
||||
});
|
||||
|
||||
export const Tray = () =>
|
||||
EventBox({
|
||||
onPrimaryClick: (self) => {
|
||||
self.child.children[0].label = self.child.children[1].revealChild
|
||||
? ""
|
||||
: "";
|
||||
self.child.children[1].revealChild =
|
||||
!self.child.children[1].revealChild;
|
||||
},
|
||||
child: Box({
|
||||
className: "tray",
|
||||
vertical: true,
|
||||
children: [
|
||||
RevIcon(),
|
||||
Revealer({
|
||||
transition: "slide_up",
|
||||
child: TrayItems(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { Widget } from "../../../imports.js";
|
||||
import {
|
||||
WeatherValue,
|
||||
getWeatherIcon,
|
||||
getWeatherTooltip,
|
||||
} from "../../../utils/weather.js";
|
||||
const { Label } = Widget;
|
||||
|
||||
const weatherWidget = () =>
|
||||
Label({
|
||||
hexpand: false,
|
||||
vexpand: false,
|
||||
class_name: "weather",
|
||||
setup: (self) => {
|
||||
self.bind("label", WeatherValue, "value", getWeatherIcon);
|
||||
self.bind("tooltip-text", WeatherValue, "value", getWeatherTooltip);
|
||||
},
|
||||
});
|
||||
|
||||
export const Weather = () =>
|
||||
Widget.CenterBox({
|
||||
vertical: true,
|
||||
centerWidget: weatherWidget(),
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
import { Widget, Hyprland } from "../../../imports.js";
|
||||
import { getFocusedWorkspace } from "../../../utils/hyprland.js";
|
||||
const { Box, Button } = Widget;
|
||||
const { messageAsync } = Hyprland;
|
||||
|
||||
export const Workspaces = () =>
|
||||
Box({
|
||||
className: "workspaces",
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: Array.from({ length: 10 }, (_, i) => i + 1).map((i) =>
|
||||
Button({
|
||||
cursor: "pointer",
|
||||
attribute: { index: i },
|
||||
onClicked: () => messageAsync(`dispatch workspace ${i}`),
|
||||
onSecondaryClick: () =>
|
||||
messageAsync(`dispatch movetoworkspacesilent ${i}`),
|
||||
}),
|
||||
),
|
||||
|
||||
setup: (self) => {
|
||||
self.hook(Hyprland, getFocusedWorkspace);
|
||||
},
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
const { Box } = Widget;
|
||||
|
||||
export const DesktopIcons = () =>
|
||||
Box({
|
||||
className: "desktopIcons",
|
||||
vertical: true,
|
||||
hpack: "start",
|
||||
vpack: "start",
|
||||
children: [],
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
import { Widget, Utils } from "../../imports.js";
|
||||
const { Box, EventBox, Label, MenuItem, Menu } = Widget;
|
||||
const { exec, execAsync } = Utils;
|
||||
|
||||
/**
|
||||
* Creates a menu item with an icon.
|
||||
* @param {string} icon - The icon to display for the menu item.
|
||||
* @param {string} itemLabel - The label for the menu item.
|
||||
* @param {Function} onClick - The function to be executed when the menu item is activated.
|
||||
* @returns {Object} A menu item object with the specified icon, label, and click action.
|
||||
*/
|
||||
function ItemWithIcon(icon, itemLabel, onClick) {
|
||||
return MenuItem({
|
||||
className: "desktopMenuItem",
|
||||
child: Box({
|
||||
children: [
|
||||
Label({
|
||||
className: "desktopMenuItemIcon",
|
||||
label: icon,
|
||||
}),
|
||||
Label(itemLabel),
|
||||
],
|
||||
}),
|
||||
onActivate: onClick,
|
||||
});
|
||||
}
|
||||
|
||||
const Separator = () =>
|
||||
MenuItem({
|
||||
child: Box({
|
||||
className: "separator",
|
||||
css: `
|
||||
min-height: 1px;
|
||||
margin: 3px 6px;
|
||||
`,
|
||||
}),
|
||||
});
|
||||
|
||||
const rioMenu = () => {
|
||||
return [
|
||||
ItemWithIcon("", "Terminal", () =>
|
||||
exec(
|
||||
'sh -c "$HOME/.config/ags/bin/open_window `slurp -d -c 999999 -w 2` foot"',
|
||||
),
|
||||
),
|
||||
ItemWithIcon("", "Resize", () =>
|
||||
exec(
|
||||
'sh -c "$HOME/.config/ags/bin/move_window `slurp -d -c 999999 -w 2`"',
|
||||
),
|
||||
),
|
||||
ItemWithIcon("", "Move", () => exec("hyprctl dispatch submap move")),
|
||||
ItemWithIcon("", "Delete", () => exec("hyprctl kill")),
|
||||
Separator(),
|
||||
];
|
||||
};
|
||||
|
||||
const Powermenu = () => {
|
||||
return MenuItem({
|
||||
className: "desktopMenuItem",
|
||||
child: Box({
|
||||
children: [
|
||||
Label({
|
||||
className: "desktopMenuItemIcon",
|
||||
label: "",
|
||||
}),
|
||||
Label("Powermenu"),
|
||||
],
|
||||
}),
|
||||
submenu: Menu({
|
||||
className: "desktopMenu",
|
||||
children: [
|
||||
ItemWithIcon("", "Lock", () => Utils.exec("gtklock")),
|
||||
ItemWithIcon("", "Log Out", () =>
|
||||
exec("hyprctl dispatch exit"),
|
||||
),
|
||||
ItemWithIcon("", "Suspend", () => exec("systemctl suspend")),
|
||||
ItemWithIcon("", "Reboot", () => exec("systemctl reboot")),
|
||||
ItemWithIcon("", "Shutdown", () => exec("systemctl poweroff")),
|
||||
],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export const DesktopMenu = () =>
|
||||
EventBox({
|
||||
onSecondaryClick: (_, event) =>
|
||||
Menu({
|
||||
className: "desktopMenu",
|
||||
children: [
|
||||
...rioMenu(),
|
||||
ItemWithIcon("", "Colorpicker", () =>
|
||||
execAsync(["hyprpicker", "-a", "wl-copy"]),
|
||||
),
|
||||
Separator(),
|
||||
Powermenu(),
|
||||
],
|
||||
}).popup_at_pointer(event),
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
const { Window } = Widget;
|
||||
|
||||
import { DesktopMenu } from "./desktopMenu.js";
|
||||
import { DesktopIcons } from "./desktopIcons.js";
|
||||
|
||||
export const Desktop = ({ monitor } = {}) =>
|
||||
Window({
|
||||
name: "desktop",
|
||||
anchor: ["top", "bottom", "left", "right"],
|
||||
layer: "bottom",
|
||||
monitor,
|
||||
child: Widget.Overlay({
|
||||
child: DesktopMenu(),
|
||||
overlays: [DesktopIcons()],
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
import { Widget, App, Applications, Utils, Hyprland } from "../../imports.js";
|
||||
import PopupWindow from "../../utils/popupWindow.js";
|
||||
const { Box, Button, Icon, Label, Scrollable, Entry } = Widget;
|
||||
|
||||
const WINDOW_NAME = "launcher";
|
||||
|
||||
const truncateString = (str, maxLength) =>
|
||||
str.length > maxLength ? `${str.slice(0, maxLength)}...` : str;
|
||||
|
||||
const AppItem = (app) =>
|
||||
Button({
|
||||
className: "launcherApp",
|
||||
onClicked: () => {
|
||||
App.closeWindow(WINDOW_NAME);
|
||||
Hyprland.messageAsync(`dispatch exec gtk-launch ${app.desktop}`);
|
||||
++app.frequency;
|
||||
},
|
||||
setup: (self) => (self.app = app),
|
||||
child: Box({
|
||||
children: [
|
||||
Icon({
|
||||
className: "launcherItemIcon",
|
||||
icon: app.iconName || "",
|
||||
size: 24,
|
||||
}),
|
||||
Box({
|
||||
className: "launcherItem",
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
children: [
|
||||
Label({
|
||||
className: "launcherItemTitle",
|
||||
label: app.name,
|
||||
xalign: 0,
|
||||
vpack: "center",
|
||||
truncate: "end",
|
||||
}),
|
||||
!!app.description &&
|
||||
Widget.Label({
|
||||
className: "launcherItemDescription",
|
||||
label:
|
||||
truncateString(app.description, 75) || "",
|
||||
wrap: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
vpack: "center",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const Launcher = () => {
|
||||
const list = Box({ vertical: true });
|
||||
|
||||
const entry = Entry({
|
||||
className: "launcherEntry",
|
||||
hexpand: true,
|
||||
text: "-",
|
||||
onAccept: ({ text }) => {
|
||||
const isCommand = text.startsWith(">");
|
||||
const appList = Applications.query(text || "");
|
||||
if (isCommand === true) {
|
||||
App.toggleWindow(WINDOW_NAME);
|
||||
Utils.execAsync(text.slice(1));
|
||||
} else if (appList[0]) {
|
||||
App.toggleWindow(WINDOW_NAME);
|
||||
appList[0].launch();
|
||||
}
|
||||
},
|
||||
onChange: ({ text }) =>
|
||||
list.children.map((item) => {
|
||||
item.visible = item.app.match(text);
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
className: "launcher",
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
self.hook(App, (_, name, visible) => {
|
||||
if (name !== WINDOW_NAME) return;
|
||||
|
||||
list.children = Applications.list.map(AppItem);
|
||||
|
||||
entry.text = "";
|
||||
if (visible) entry.grab_focus();
|
||||
});
|
||||
},
|
||||
children: [
|
||||
entry,
|
||||
Scrollable({
|
||||
hscroll: "never",
|
||||
css: "min-width: 250px; min-height: 360px;",
|
||||
child: list,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export const AppLauncher = () =>
|
||||
PopupWindow({
|
||||
name: WINDOW_NAME,
|
||||
anchor: ["top", "bottom", "right"],
|
||||
margins: [13, 13, 0, 13],
|
||||
layer: "overlay",
|
||||
transition: "slide_down",
|
||||
transitionDuration: 150,
|
||||
popup: true,
|
||||
keymode: "on-demand",
|
||||
child: Launcher(),
|
||||
});
|
29
nyx/homes/notashelf/services/wayland/ags/js/windows/music/controls.js
vendored
Normal file
29
nyx/homes/notashelf/services/wayland/ags/js/windows/music/controls.js
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { Icons, Widget } from "../../imports.js";
|
||||
import { mprisStateIcon } from "../../utils/mpris.js";
|
||||
|
||||
export default (player) =>
|
||||
Widget.CenterBox({
|
||||
className: "controls",
|
||||
hpack: "center",
|
||||
|
||||
startWidget: Widget.Button({
|
||||
onClicked: () => player.previous(),
|
||||
child: Widget.Icon(Icons.media.previous),
|
||||
}),
|
||||
|
||||
centerWidget: Widget.Button({
|
||||
onClicked: () => player.playPause(),
|
||||
|
||||
child: Widget.Icon().bind(
|
||||
"icon",
|
||||
player,
|
||||
"play-back-status",
|
||||
mprisStateIcon,
|
||||
),
|
||||
}),
|
||||
|
||||
endWidget: Widget.Button({
|
||||
onClicked: () => player.next(),
|
||||
child: Widget.Icon(Icons.media.next),
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
|
||||
export default (player) =>
|
||||
Widget.Box({ className: "cover" }).bind(
|
||||
"css",
|
||||
player,
|
||||
"cover-path",
|
||||
(cover) => `background-image: url('${cover ?? ""}')`,
|
||||
);
|
|
@ -0,0 +1,45 @@
|
|||
import { Mpris, Widget } from "../../imports.js";
|
||||
import { findPlayer, generateBackground } from "../../utils/mpris.js";
|
||||
import PopupWindow from "./popup_window.js";
|
||||
|
||||
import Cover from "./cover.js";
|
||||
import { Artists, Title } from "./title_artists.js";
|
||||
import TimeInfo from "./time_info.js";
|
||||
import Controls from "./controls.js";
|
||||
import PlayerInfo from "./player_info.js";
|
||||
|
||||
const Info = (player) =>
|
||||
Widget.Box({
|
||||
className: "info",
|
||||
vertical: true,
|
||||
vexpand: false,
|
||||
hexpand: false,
|
||||
homogeneous: true,
|
||||
|
||||
children: [
|
||||
PlayerInfo(player),
|
||||
Title(player),
|
||||
Artists(player),
|
||||
Controls(player),
|
||||
TimeInfo(player),
|
||||
],
|
||||
});
|
||||
|
||||
const MusicBox = (player) =>
|
||||
Widget.Box({
|
||||
className: "music window",
|
||||
children: [Cover(player), Info(player)],
|
||||
}).bind("css", player, "cover-path", generateBackground);
|
||||
|
||||
export const Media = () =>
|
||||
PopupWindow({
|
||||
monitor: 0,
|
||||
anchor: ["top"],
|
||||
layer: "top",
|
||||
margins: [8, 0, 0, 0],
|
||||
name: "music",
|
||||
child: Widget.Box(),
|
||||
}).bind("child", Mpris, "players", (players) => {
|
||||
if (players.length == 0) return Widget.Box();
|
||||
return MusicBox(findPlayer(players));
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import { Icons, Utils, Widget } from "../../imports.js";
|
||||
|
||||
export default (player) =>
|
||||
Widget.Box({
|
||||
className: "player-info",
|
||||
vexpand: true,
|
||||
vpack: "start",
|
||||
|
||||
children: [
|
||||
Widget.Icon({
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
className: "player-icon",
|
||||
tooltipText: player.identity ?? "",
|
||||
}).bind("icon", player, "entry", (entry) => {
|
||||
// the Spotify icon is called spotify-client
|
||||
if (entry == "spotify") entry = "spotify-client";
|
||||
return Utils.lookUpIcon(entry ?? "")
|
||||
? entry
|
||||
: Icons.media.player;
|
||||
}),
|
||||
],
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import { Widget } from "../../imports.js";
|
||||
const { Box, Revealer, Window } = Widget;
|
||||
|
||||
export default ({
|
||||
name,
|
||||
child,
|
||||
revealerSetup = null,
|
||||
transition = "crossfade",
|
||||
transitionDuration = 200,
|
||||
...props
|
||||
}) => {
|
||||
const window = Window({
|
||||
name,
|
||||
popup: false,
|
||||
focusable: false,
|
||||
visible: false,
|
||||
...props,
|
||||
|
||||
setup: (self) => (self.getChild = () => child),
|
||||
|
||||
child: Box({
|
||||
css: `
|
||||
min-height: 1px;
|
||||
min-width: 1px;
|
||||
padding: 1px;
|
||||
`,
|
||||
child: Revealer({
|
||||
transition,
|
||||
transitionDuration,
|
||||
child: child,
|
||||
|
||||
setup:
|
||||
revealerSetup ??
|
||||
((self) =>
|
||||
self.hook(App, (self, currentName, visible) => {
|
||||
if (currentName === name) {
|
||||
self.reveal_child = visible;
|
||||
}
|
||||
})),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
return window;
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
import { lengthStr } from "../../utils/mpris.js";
|
||||
|
||||
export const PositionLabel = (player) =>
|
||||
Widget.Label({
|
||||
className: "position",
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
|
||||
setup: (self) => {
|
||||
const update = (_, time) => {
|
||||
player.length > 0
|
||||
? (self.label = lengthStr(time || player.position))
|
||||
: (self.visible = !!player);
|
||||
};
|
||||
|
||||
self.hook(player, update, "position").poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
export const LengthLabel = (player) =>
|
||||
Widget.Label({
|
||||
className: "length",
|
||||
hexpand: true,
|
||||
xalign: 1,
|
||||
})
|
||||
.bind("visible", player, "length", (length) => length > 0)
|
||||
.bind("label", player, "length", (length) => lengthStr(length));
|
||||
|
||||
export const Position = (player) =>
|
||||
Widget.Slider({
|
||||
className: "position",
|
||||
draw_value: false,
|
||||
|
||||
onChange: ({ value }) => (player.position = player.length * value),
|
||||
|
||||
setup: (self) => {
|
||||
const update = () => {
|
||||
if (self.dragging) return;
|
||||
|
||||
self.visible = player.length > 0;
|
||||
|
||||
if (player.length > 0) {
|
||||
self.value = player.position / player.length;
|
||||
}
|
||||
};
|
||||
|
||||
self.hook(player, update)
|
||||
.hook(player, update, "position")
|
||||
.poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
export default (player) =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
vpack: "end",
|
||||
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
children: [PositionLabel(player), LengthLabel(player)],
|
||||
}),
|
||||
Position(player),
|
||||
],
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
|
||||
export const Title = (player) =>
|
||||
Widget.Scrollable({
|
||||
className: "title",
|
||||
vscroll: "never",
|
||||
hscroll: "automatic",
|
||||
|
||||
child: Widget.Label({
|
||||
className: "title",
|
||||
label: "Nothing playing",
|
||||
}).bind(
|
||||
"label",
|
||||
player,
|
||||
"track-title",
|
||||
(title) => title ?? "Nothing playing",
|
||||
),
|
||||
});
|
||||
|
||||
export const Artists = (player) =>
|
||||
Widget.Scrollable({
|
||||
className: "artists",
|
||||
vscroll: "never",
|
||||
hscroll: "automatic",
|
||||
|
||||
child: Widget.Label({ className: "artists" }).bind(
|
||||
"label",
|
||||
player,
|
||||
"track-artists",
|
||||
(artists) => artists.join(", ") ?? "",
|
||||
),
|
||||
});
|
|
@ -0,0 +1,134 @@
|
|||
import { Hyprland, Notifications, Utils, Widget } from "../../imports.js";
|
||||
const { Box, Icon, Label, Button, EventBox, Window } = Widget;
|
||||
const { lookUpIcon } = Utils;
|
||||
|
||||
const closeAll = () => {
|
||||
Notifications.popups.map((n) => n.dismiss());
|
||||
};
|
||||
|
||||
const NotificationIcon = ({ app_entry, app_icon, image }) => {
|
||||
if (image) {
|
||||
return Box({
|
||||
css: `
|
||||
background-image: url("${image}");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
if (lookUpIcon(app_icon)) {
|
||||
return Icon(app_icon);
|
||||
}
|
||||
|
||||
if (app_entry && lookUpIcon(app_entry)) {
|
||||
return Icon(app_entry);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const Notification = (notif) => {
|
||||
const icon = Box({
|
||||
vpack: "start",
|
||||
class_name: "icon",
|
||||
// @ts-ignore
|
||||
setup: (/** @type {{ child: any; }} */ self) => {
|
||||
const icon = NotificationIcon(notif);
|
||||
if (icon !== null) self.child = icon;
|
||||
},
|
||||
});
|
||||
|
||||
const title = Label({
|
||||
class_name: "title",
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
hexpand: true,
|
||||
max_width_chars: 24,
|
||||
truncate: "end",
|
||||
wrap: true,
|
||||
label: notif.summary,
|
||||
use_markup: true,
|
||||
});
|
||||
|
||||
const body = Label({
|
||||
class_name: "body",
|
||||
hexpand: true,
|
||||
use_markup: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
max_width_chars: 100,
|
||||
wrap: true,
|
||||
label: notif.body,
|
||||
});
|
||||
|
||||
const actions = Box({
|
||||
class_name: "actions",
|
||||
children: notif.actions
|
||||
.filter(({ id }) => id != "default")
|
||||
.map(({ id, label }) =>
|
||||
Button({
|
||||
class_name: "action-button",
|
||||
on_clicked: () => notif.invoke(id),
|
||||
hexpand: true,
|
||||
child: Label(label),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
return EventBox({
|
||||
on_primary_click: () => {
|
||||
if (notif.actions.length > 0) notif.invoke(notif.actions[0].id);
|
||||
},
|
||||
on_middle_click: closeAll,
|
||||
on_secondary_click: () => notif.dismiss(),
|
||||
child: Box({
|
||||
class_name: `notification ${notif.urgency}`,
|
||||
vertical: true,
|
||||
|
||||
children: [
|
||||
Box({
|
||||
class_name: "info",
|
||||
children: [
|
||||
icon,
|
||||
Box({
|
||||
vertical: true,
|
||||
class_name: "text",
|
||||
vpack: "center",
|
||||
|
||||
setup: (self) => {
|
||||
if (notif.body.length > 0)
|
||||
self.children = [title, body];
|
||||
else self.children = [title];
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
actions,
|
||||
],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
let lastMonitor;
|
||||
export const Notifs = () =>
|
||||
Window({
|
||||
name: "notifications",
|
||||
anchor: ["top", "right"],
|
||||
margins: [8, 8, 8, 0],
|
||||
child: Box({
|
||||
css: "padding: 1px;",
|
||||
class_name: "notifications",
|
||||
vertical: true,
|
||||
// @ts-ignore
|
||||
children: Notifications.bind("popups").transform((popups) => {
|
||||
return popups.map(Notification);
|
||||
}),
|
||||
}),
|
||||
}).hook(Hyprland.active, (self) => {
|
||||
// prevent useless resets
|
||||
if (lastMonitor === Hyprland.active.monitor) return;
|
||||
|
||||
self.monitor = Hyprland.active.monitor.id;
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
import { Widget, Utils } from "../../imports.js";
|
||||
import Brightness from "../../services/brightness.js";
|
||||
const { Box, Slider, Label, Revealer } = Widget;
|
||||
|
||||
const BrightnessIcon = () =>
|
||||
Label({
|
||||
className: "brtPopupIcon",
|
||||
setup: (self) => {
|
||||
self.hook(Brightness, (self) => {
|
||||
const icons = ["", "", "", "", "", "", "", "", ""];
|
||||
|
||||
let index = Math.floor((Brightness.screen * 100) / 11);
|
||||
index = Math.max(0, Math.min(index, icons.length - 1));
|
||||
|
||||
if (index >= 0 && index < icons.length) {
|
||||
self.label = icons[index].toString();
|
||||
} else {
|
||||
log("Index out of bounds:", index);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const PercentBar = () =>
|
||||
Slider({
|
||||
className: "brtPopupBar",
|
||||
drawValue: false,
|
||||
onChange: ({ value }) => (Brightness.screen = value),
|
||||
setup: (self) => {
|
||||
self.hook(Brightness, (self) => (self.value = Brightness.screen));
|
||||
},
|
||||
});
|
||||
|
||||
export const BrightnessPopup = () =>
|
||||
Box({
|
||||
css: `min-height: 1px;
|
||||
min-width: 1px;`,
|
||||
child: Revealer({
|
||||
transition: "slide_up",
|
||||
child: Box({
|
||||
className: "brightnessPopup",
|
||||
children: [BrightnessIcon(), PercentBar()],
|
||||
}),
|
||||
attribute: { count: 0 },
|
||||
setup: (self) => {
|
||||
self.hook(Brightness, (self) => {
|
||||
self.revealChild = true;
|
||||
self.attribute.count++;
|
||||
Utils.timeout(1500, () => {
|
||||
self.attribute.count--;
|
||||
|
||||
if (self.attribute.count === 0)
|
||||
self.revealChild = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { Widget } from "../../imports.js";
|
||||
|
||||
// Widgets
|
||||
import { BrightnessPopup } from "./brightnessPopup.js";
|
||||
import { VolumePopup } from "./volumePopup.js";
|
||||
|
||||
export const Popups = () =>
|
||||
Widget.Window({
|
||||
name: "popups",
|
||||
className: "popups",
|
||||
anchor: ["bottom", "right"],
|
||||
layer: "overlay",
|
||||
margins: [0, 12, 8, 0],
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [BrightnessPopup(), VolumePopup()],
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { Widget, Utils, Audio } from "../../imports.js";
|
||||
import { getSliderIcon, volumePercentBar } from "../../utils/audio.js";
|
||||
const { Box, Revealer } = Widget;
|
||||
const { speaker } = Audio;
|
||||
const { timeout } = Utils;
|
||||
|
||||
export const VolumePopup = () =>
|
||||
Box({
|
||||
css: `
|
||||
min-height: 2px;
|
||||
min-width: 2px;
|
||||
`,
|
||||
child: Revealer({
|
||||
transition: "slide_up",
|
||||
child: Box({
|
||||
className: "volumePopup",
|
||||
children: [getSliderIcon(), volumePercentBar()],
|
||||
}),
|
||||
attribute: { count: 0 },
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
speaker,
|
||||
() => {
|
||||
self.reveal_child = true;
|
||||
self.attribute.count++;
|
||||
timeout(1500, () => {
|
||||
self.attribute.count--;
|
||||
|
||||
if (self.attribute.count === 0)
|
||||
self.reveal_child = false;
|
||||
});
|
||||
},
|
||||
"notify::volume",
|
||||
),
|
||||
}),
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue