diff --git a/directoryMonitorService.js b/directoryMonitorService.js deleted file mode 100644 index 1447a8f..0000000 --- a/directoryMonitorService.js +++ /dev/null @@ -1,41 +0,0 @@ -import Service from "resource:///com/github/Aylur/ags/service.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; -import { monitorFile } from "resource:///com/github/Aylur/ags/utils.js"; -import Gio from "gi://Gio"; - -class DirectoryMonitorService extends Service { - static { - Service.register(this, {}, {}); - } - - constructor() { - super(); - this.recursiveDirectoryMonitor(`${App.configDir}/scss`); - } - - recursiveDirectoryMonitor(directoryPath) { - monitorFile(directoryPath, (_, eventType) => { - if (eventType === Gio.FileMonitorEvent.CHANGES_DONE_HINT) { - this.emit("changed"); - } - }); - - const directory = Gio.File.new_for_path(directoryPath); - const enumerator = directory.enumerate_children( - "standard::*", - Gio.FileQueryInfoFlags.NONE, - null, - ); - - let fileInfo; - while ((fileInfo = enumerator.next_file(null)) !== null) { - const childPath = directoryPath + "/" + fileInfo.get_name(); - if (fileInfo.get_file_type() === Gio.FileType.DIRECTORY) { - this.recursiveDirectoryMonitor(childPath); - } - } - } -} - -const service = new DirectoryMonitorService(); -export default service; diff --git a/directoryMonitorService.ts b/directoryMonitorService.ts new file mode 100644 index 0000000..911dc69 --- /dev/null +++ b/directoryMonitorService.ts @@ -0,0 +1,42 @@ +import Service from "resource:///com/github/Aylur/ags/service.js"; +import App from "resource:///com/github/Aylur/ags/app.js"; +import { monitorFile } from "resource:///com/github/Aylur/ags/utils.js"; +import Gio from "gi://Gio"; +import { FileInfo } from "types/@girs/gio-2.0/gio-2.0.cjs"; + +class DirectoryMonitorService extends Service { + static { + Service.register(this, {}, {}); + } + + constructor() { + super(); + this.recursiveDirectoryMonitor(`${App.configDir}/scss`); + } + + recursiveDirectoryMonitor(directoryPath: string) { + monitorFile(directoryPath, (_, eventType) => { + if (eventType === Gio.FileMonitorEvent.CHANGES_DONE_HINT) { + this.emit("changed"); + } + }); + + const directory = Gio.File.new_for_path(directoryPath); + const enumerator = directory.enumerate_children( + "standard::*", + Gio.FileQueryInfoFlags.NONE, + null, + ); + + let fileInfo: FileInfo; + while ((fileInfo = enumerator.next_file(null) as FileInfo) !== null) { + const childPath = directoryPath + "/" + fileInfo.get_name(); + if (fileInfo.get_file_type() === Gio.FileType.DIRECTORY) { + this.recursiveDirectoryMonitor(childPath); + } + } + } +} + +const service = new DirectoryMonitorService(); +export default service; diff --git a/lib/types/gpustat.d.ts b/lib/types/gpustat.d.ts new file mode 100644 index 0000000..ede3378 --- /dev/null +++ b/lib/types/gpustat.d.ts @@ -0,0 +1,25 @@ +export type GPU_Stat_Process = { + username: string; + command: string; + full_command: string[]; + gpu_memory_usage: number; + cpu_percent: number; + cpu_memory_usage: number; + pid: number; +}; + +export type GPU_Stat = { + index: number; + uuid: string; + name: string; + "temperature.gpu": number; + "fan.speed": number; + "utilization.gpu": number; + "utilization.enc": number; + "utilization.dec": number; + "power.draw": number; + "enforced.power.limit": number; + "memory.used": number; + "memory.total": number; + processes: Process[]; +}; diff --git a/lib/types/network.d.ts b/lib/types/network.d.ts new file mode 100644 index 0000000..63c8804 --- /dev/null +++ b/lib/types/network.d.ts @@ -0,0 +1,10 @@ +export type AccessPoint = { + bssid: string | null; + address: string | null; + lastSeen: number; + ssid: string | null; + active: boolean; + strength: number; + frequency: number; + iconName: string | undefined; +} diff --git a/lib/types/options.d.ts b/lib/types/options.d.ts index e89b98c..3f340bf 100644 --- a/lib/types/options.d.ts +++ b/lib/types/options.d.ts @@ -1 +1,3 @@ export type Unit = "imperial" | "metric"; +export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown"; +export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left"; diff --git a/lib/types/power.d.ts b/lib/types/power.d.ts new file mode 100644 index 0000000..5a44633 --- /dev/null +++ b/lib/types/power.d.ts @@ -0,0 +1 @@ +export type Action = "sleep" | "reboot" | "logout" | "shutdown"; diff --git a/lib/types/widget.d.ts b/lib/types/widget.d.ts new file mode 100644 index 0000000..7fcda61 --- /dev/null +++ b/lib/types/widget.d.ts @@ -0,0 +1,3 @@ +export type Exclusivity = 'normal' | 'ignore' | 'exclusive'; +export type Anchor = "left" | "right" | "top" | "down"; +export type Transition = "none" | "crossfade" | "slide_right" | "slide_left" | "slide_up" | "slide_down"; diff --git a/modules/menus/DropdownMenu.js b/modules/menus/DropdownMenu.ts similarity index 96% rename from modules/menus/DropdownMenu.js rename to modules/menus/DropdownMenu.ts index c8b5364..5c3cfc4 100644 --- a/modules/menus/DropdownMenu.js +++ b/modules/menus/DropdownMenu.ts @@ -1,7 +1,8 @@ const hyprland = await Service.import("hyprland"); import { globalMousePos } from "globals"; +import { Exclusivity } from "lib/types/widget"; -export const Padding = (name) => +export const Padding = (name: string) => Widget.EventBox({ hexpand: true, vexpand: true, @@ -10,7 +11,7 @@ export const Padding = (name) => setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)), }); -const moveBoxToCursor = (self, fixed) => { +const moveBoxToCursor = (self: any, fixed: boolean) => { if (fixed) { return; } @@ -77,7 +78,7 @@ export default ({ child, layout = "center", transition, - exclusivity = "ignore", + exclusivity = "ignore" as Exclusivity, fixed = false, ...props }) => diff --git a/modules/menus/PopupWindow.js b/modules/menus/PopupWindow.js deleted file mode 100644 index dbe89c9..0000000 --- a/modules/menus/PopupWindow.js +++ /dev/null @@ -1,165 +0,0 @@ -export const Padding = (name, opts = {}) => - Widget.EventBox({ - class_name: opts?.className || "", - hexpand: true, - vexpand: typeof opts?.vexpand === "boolean" ? opts.vexpand : true, - can_focus: false, - child: Widget.Box(), - setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)), - }); - -const PopupRevealer = (name, child, transition = "slide_down") => - Widget.Box( - { css: "padding: 1px;" }, - Widget.Revealer({ - transition, - child: Widget.Box({ - class_name: `window-content ${name}-window`, - child, - }), - transitionDuration: 200, - setup: (self) => - self.hook(App, (_, wname, visible) => { - if (wname === name) self.reveal_child = visible; - }), - }), - ); - -const Layout = (name, child, transition) => ({ - center: () => - Widget.CenterBox( - {}, - Padding(name), - Widget.CenterBox( - { vertical: true }, - Padding(name), - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - top: () => - Widget.CenterBox( - {}, - Padding(name), - Widget.Box( - { vertical: true }, - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "top-right": () => - Widget.Box( - {}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name, { - vexpand: false, - className: "event-top-padding", - }), - PopupRevealer(name, child, transition), - Padding(name), - ), - ), - "top-center": () => - Widget.Box( - {}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name, { - vexpand: false, - className: "event-top-padding", - }), - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "top-left": () => - Widget.Box( - {}, - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name, { - vexpand: false, - className: "event-top-padding", - }), - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "bottom-left": () => - Widget.Box( - {}, - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - Padding(name), - ), - "bottom-center": () => - Widget.Box( - {}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - Padding(name), - ), - "bottom-right": () => - Widget.Box( - {}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - ), -}); - -export default ({ - name, - child, - layout = "center", - transition, - exclusivity = "ignore", - ...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", "bottom", "right", "left"], - child: Layout(name, child, transition)[layout](), - ...props, - }); diff --git a/modules/menus/PopupWindow.ts b/modules/menus/PopupWindow.ts new file mode 100644 index 0000000..af26df4 --- /dev/null +++ b/modules/menus/PopupWindow.ts @@ -0,0 +1,172 @@ +import { Exclusivity, Transition } from "lib/types/widget"; + +type Opts = { + className: string + vexpand: boolean +} + +export const Padding = (name: string, opts: Opts) => + Widget.EventBox({ + class_name: opts?.className || "", + hexpand: true, + vexpand: typeof opts?.vexpand === "boolean" ? opts.vexpand : true, + can_focus: false, + child: Widget.Box(), + setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)), + }); + +const PopupRevealer = (name: string, child: any, transition = "slide_down" as Transition) => + Widget.Box( + { css: "padding: 1px;" }, + Widget.Revealer({ + transition, + child: Widget.Box({ + class_name: `window-content ${name}-window`, + child, + }), + transitionDuration: 200, + setup: (self) => + self.hook(App, (_, wname, visible) => { + if (wname === name) self.reveal_child = visible; + }), + }), + ); + +const Layout = (name: string, child: any, transition: Transition) => ({ + center: () => + Widget.CenterBox( + {}, + Padding(name, {} as Opts), + Widget.CenterBox( + { vertical: true }, + Padding(name, {} as Opts), + PopupRevealer(name, child, transition), + Padding(name, {} as Opts), + ), + Padding(name, {} as Opts), + ), + top: () => + Widget.CenterBox( + {}, + Padding(name, {} as Opts), + Widget.Box( + { vertical: true }, + PopupRevealer(name, child, transition), + Padding(name, {} as Opts), + ), + Padding(name, {} as Opts), + ), + "top-right": () => + Widget.Box( + {}, + Padding(name, {} as Opts), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, { + vexpand: false, + className: "event-top-padding", + }), + PopupRevealer(name, child, transition), + Padding(name, {} as Opts), + ), + ), + "top-center": () => + Widget.Box( + {}, + Padding(name, {} as Opts), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, { + vexpand: false, + className: "event-top-padding", + }), + PopupRevealer(name, child, transition), + Padding(name, {} as Opts), + ), + Padding(name, {} as Opts), + ), + "top-left": () => + Widget.Box( + {}, + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, { + vexpand: false, + className: "event-top-padding", + }), + PopupRevealer(name, child, transition), + Padding(name, {} as Opts), + ), + Padding(name, {} as Opts), + ), + "bottom-left": () => + Widget.Box( + {}, + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, {} as Opts), + PopupRevealer(name, child, transition), + ), + Padding(name, {} as Opts), + ), + "bottom-center": () => + Widget.Box( + {}, + Padding(name, {} as Opts), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, {} as Opts), + PopupRevealer(name, child, transition), + ), + Padding(name, {} as Opts), + ), + "bottom-right": () => + Widget.Box( + {}, + Padding(name, {} as Opts), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name, {} as Opts), + PopupRevealer(name, child, transition), + ), + ), +}); + +export default ({ + name, + child, + layout = "center", + transition, + exclusivity = "ignore" as Exclusivity, + ...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", "bottom", "right", "left"], + child: Layout(name, child, transition)[layout](), + ...props, + }); diff --git a/modules/menus/dashboard/controls/index.js b/modules/menus/dashboard/controls/index.ts similarity index 100% rename from modules/menus/dashboard/controls/index.js rename to modules/menus/dashboard/controls/index.ts diff --git a/modules/menus/dashboard/directories/index.js b/modules/menus/dashboard/directories/index.js deleted file mode 100644 index 7bd24a4..0000000 --- a/modules/menus/dashboard/directories/index.js +++ /dev/null @@ -1,133 +0,0 @@ -import GLib from "gi://GLib"; -import options from "options"; - -const { left, right } = options.menus.dashboard.directories; - -const Directories = () => { - return Widget.Box({ - class_name: "dashboard-card directories-container", - vpack: "fill", - hpack: "fill", - expand: true, - children: [ - Widget.Box({ - vertical: true, - expand: true, - class_name: "section right", - children: [ - Widget.Button({ - hpack: "start", - expand: true, - class_name: "directory-link left top", - on_primary_click: left.directory1.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: left.directory1.label.bind("value"), - }), - }), - Widget.Button({ - expand: true, - hpack: "start", - class_name: "directory-link left middle", - on_primary_click: left.directory2.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: left.directory2.label.bind("value"), - }), - }), - Widget.Button({ - expand: true, - hpack: "start", - class_name: "directory-link left bottom", - on_primary_click: left.directory3.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: left.directory3.label.bind("value"), - }), - }), - ], - }), - Widget.Box({ - vertical: true, - expand: true, - class_name: "section left", - children: [ - Widget.Button({ - hpack: "start", - expand: true, - class_name: "directory-link right top", - on_primary_click: right.directory1.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: right.directory1.label.bind("value"), - }), - }), - Widget.Button({ - expand: true, - hpack: "start", - class_name: "directory-link right middle", - on_primary_click: right.directory2.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: right.directory2.label.bind("value"), - }), - }), - Widget.Button({ - expand: true, - hpack: "start", - class_name: "directory-link right bottom", - on_primary_click: right.directory3.command - .bind("value") - .as((cmd) => { - return () => { - App.closeWindow("dashboardmenu"); - Utils.execAsync(cmd); - }; - }), - child: Widget.Label({ - hpack: "start", - label: right.directory3.label.bind("value"), - }), - }), - ], - }), - ], - }); -}; - -export { Directories }; diff --git a/modules/menus/dashboard/directories/index.ts b/modules/menus/dashboard/directories/index.ts new file mode 100644 index 0000000..6841858 --- /dev/null +++ b/modules/menus/dashboard/directories/index.ts @@ -0,0 +1,132 @@ +import options from "options"; + +const { left, right } = options.menus.dashboard.directories; + +const Directories = () => { + return Widget.Box({ + class_name: "dashboard-card directories-container", + vpack: "fill", + hpack: "fill", + expand: true, + children: [ + Widget.Box({ + vertical: true, + expand: true, + class_name: "section right", + children: [ + Widget.Button({ + hpack: "start", + expand: true, + class_name: "directory-link left top", + on_primary_click: left.directory1.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: left.directory1.label.bind("value"), + }), + }), + Widget.Button({ + expand: true, + hpack: "start", + class_name: "directory-link left middle", + on_primary_click: left.directory2.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: left.directory2.label.bind("value"), + }), + }), + Widget.Button({ + expand: true, + hpack: "start", + class_name: "directory-link left bottom", + on_primary_click: left.directory3.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: left.directory3.label.bind("value"), + }), + }), + ], + }), + Widget.Box({ + vertical: true, + expand: true, + class_name: "section left", + children: [ + Widget.Button({ + hpack: "start", + expand: true, + class_name: "directory-link right top", + on_primary_click: right.directory1.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: right.directory1.label.bind("value"), + }), + }), + Widget.Button({ + expand: true, + hpack: "start", + class_name: "directory-link right middle", + on_primary_click: right.directory2.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: right.directory2.label.bind("value"), + }), + }), + Widget.Button({ + expand: true, + hpack: "start", + class_name: "directory-link right bottom", + on_primary_click: right.directory3.command + .bind("value") + .as((cmd) => { + return () => { + App.closeWindow("dashboardmenu"); + Utils.execAsync(cmd); + }; + }), + child: Widget.Label({ + hpack: "start", + label: right.directory3.label.bind("value"), + }), + }), + ], + }), + ], + }); +}; + +export { Directories }; diff --git a/modules/menus/dashboard/index.js b/modules/menus/dashboard/index.ts similarity index 100% rename from modules/menus/dashboard/index.js rename to modules/menus/dashboard/index.ts diff --git a/modules/menus/dashboard/profile/index.js b/modules/menus/dashboard/profile/index.js deleted file mode 100644 index 81630a4..0000000 --- a/modules/menus/dashboard/profile/index.js +++ /dev/null @@ -1,79 +0,0 @@ -import icons from "../../../icons/index.js"; -import powermenu from "../../power/helpers/actions.js"; - -import options from "options"; -const { image, name } = options.menus.dashboard.powermenu.avatar; - -const Profile = () => { - const handleClick = (action) => { - App.closeWindow("dashboardmenu"); - return powermenu.action(action); - }; - - return Widget.Box({ - class_name: "profiles-container", - hpack: "fill", - hexpand: true, - children: [ - Widget.Box({ - class_name: "profile-picture-container dashboard-card", - hexpand: true, - vertical: true, - children: [ - Widget.Icon({ - hpack: "center", - class_name: "profile-picture", - icon: image.bind("value"), - }), - Widget.Label({ - hpack: "center", - class_name: "profile-name", - label: name.bind("value").as((v) => { - if (v === "system") { - return Utils.exec("bash -c whoami"); - } - return v; - }), - }), - ], - }), - Widget.Box({ - class_name: "power-menu-container dashboard-card", - vertical: true, - vexpand: true, - children: [ - Widget.Button({ - class_name: "dashboard-button shutdown", - on_clicked: () => handleClick("shutdown"), - tooltip_text: "Shut Down", - vexpand: true, - child: Widget.Icon(icons.powermenu.shutdown), - }), - Widget.Button({ - class_name: "dashboard-button restart", - on_clicked: () => handleClick("reboot"), - tooltip_text: "Restart", - vexpand: true, - child: Widget.Icon(icons.powermenu.reboot), - }), - Widget.Button({ - class_name: "dashboard-button lock", - on_clicked: () => handleClick("logout"), - tooltip_text: "Log Out", - vexpand: true, - child: Widget.Icon(icons.powermenu.logout), - }), - Widget.Button({ - class_name: "dashboard-button sleep", - on_clicked: () => handleClick("sleep"), - tooltip_text: "Sleep", - vexpand: true, - child: Widget.Icon(icons.powermenu.sleep), - }), - ], - }), - ], - }); -}; - -export { Profile }; diff --git a/modules/menus/dashboard/profile/index.ts b/modules/menus/dashboard/profile/index.ts new file mode 100644 index 0000000..e17e925 --- /dev/null +++ b/modules/menus/dashboard/profile/index.ts @@ -0,0 +1,80 @@ +import icons from "../../../icons/index.js"; +import powermenu from "../../power/helpers/actions.js"; +import { PowerOptions } from "lib/types/options.js"; + +import options from "options"; +const { image, name } = options.menus.dashboard.powermenu.avatar; + +const Profile = () => { + const handleClick = (action: PowerOptions) => { + App.closeWindow("dashboardmenu"); + return powermenu.action(action); + }; + + return Widget.Box({ + class_name: "profiles-container", + hpack: "fill", + hexpand: true, + children: [ + Widget.Box({ + class_name: "profile-picture-container dashboard-card", + hexpand: true, + vertical: true, + children: [ + Widget.Icon({ + hpack: "center", + class_name: "profile-picture", + icon: image.bind("value"), + }), + Widget.Label({ + hpack: "center", + class_name: "profile-name", + label: name.bind("value").as((v) => { + if (v === "system") { + return Utils.exec("bash -c whoami"); + } + return v; + }), + }), + ], + }), + Widget.Box({ + class_name: "power-menu-container dashboard-card", + vertical: true, + vexpand: true, + children: [ + Widget.Button({ + class_name: "dashboard-button shutdown", + on_clicked: () => handleClick("shutdown"), + tooltip_text: "Shut Down", + vexpand: true, + child: Widget.Icon(icons.powermenu.shutdown), + }), + Widget.Button({ + class_name: "dashboard-button restart", + on_clicked: () => handleClick("reboot"), + tooltip_text: "Restart", + vexpand: true, + child: Widget.Icon(icons.powermenu.reboot), + }), + Widget.Button({ + class_name: "dashboard-button lock", + on_clicked: () => handleClick("logout"), + tooltip_text: "Log Out", + vexpand: true, + child: Widget.Icon(icons.powermenu.logout), + }), + Widget.Button({ + class_name: "dashboard-button sleep", + on_clicked: () => handleClick("sleep"), + tooltip_text: "Sleep", + vexpand: true, + child: Widget.Icon(icons.powermenu.sleep), + }), + ], + }), + ], + }); +}; + +export { Profile }; diff --git a/modules/menus/dashboard/shortcuts/index.js b/modules/menus/dashboard/shortcuts/index.ts similarity index 98% rename from modules/menus/dashboard/shortcuts/index.js rename to modules/menus/dashboard/shortcuts/index.ts index 5648839..7c744c3 100644 --- a/modules/menus/dashboard/shortcuts/index.js +++ b/modules/menus/dashboard/shortcuts/index.ts @@ -16,16 +16,12 @@ const Shortcuts = () => { }, ], }); - const handleClick = (action, resolver, tOut = 250) => { + const handleClick = (action: any, tOut: number = 250) => { App.closeWindow("dashboardmenu"); setTimeout(() => { Utils.execAsync(action) .then((res) => { - if (typeof resolver === "function") { - return resolver(res); - } - return res; }) .catch((err) => err); diff --git a/modules/menus/dashboard/stats/index.js b/modules/menus/dashboard/stats/index.ts similarity index 97% rename from modules/menus/dashboard/stats/index.js rename to modules/menus/dashboard/stats/index.ts index 381f5b3..ca8c54c 100644 --- a/modules/menus/dashboard/stats/index.js +++ b/modules/menus/dashboard/stats/index.ts @@ -1,11 +1,12 @@ import options from "options"; +import { GPU_Stat } from "lib/types/gpustat"; const { terminal } = options; const Stats = () => { const divide = ([total, free]) => free / total; - const formatSizeInGB = (sizeInKB) => + const formatSizeInGB = (sizeInKB: number) => Number((sizeInKB / 1024 ** 2).toFixed(2)); const cpu = Variable(0, { @@ -73,8 +74,10 @@ const Stats = () => { const totalGpu = 100; const usedGpu = - data.gpus.reduce((acc, gpu) => acc + gpu["utilization.gpu"], 0) / - data.gpus.length; + data.gpus.reduce((acc: number, gpu: GPU_Stat) => { + + return acc + gpu["utilization.gpu"] + }, 0) / data.gpus.length; return divide([totalGpu, usedGpu]); } catch (e) { diff --git a/modules/menus/energy/brightness/index.js b/modules/menus/energy/brightness/index.js deleted file mode 100644 index a9b6c84..0000000 --- a/modules/menus/energy/brightness/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import brightness from "../../../../services/Brightness.js"; -import icons from "../../../icons/index.js"; - -const Brightness = () => { - return Widget.Box({ - class_name: "menu-section-container brightness", - vertical: true, - children: [ - Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", - child: Widget.Label({ - class_name: "menu-label", - hexpand: true, - hpack: "start", - label: "Brightness", - }), - }), - Widget.Box({ - class_name: "menu-items-section", - vpack: "fill", - vexpand: true, - vertical: true, - child: Widget.Box({ - class_name: "brightness-container", - children: [ - Widget.Icon({ - vexpand: true, - vpack: "center", - class_name: "brightness-slider-icon", - icon: icons.brightness.screen, - }), - Widget.Slider({ - vpack: "center", - vexpand: true, - value: brightness.bind("screen_value"), - class_name: "menu-active-slider menu-slider brightness", - draw_value: false, - hexpand: true, - min: 0, - max: 1, - onChange: ({ value }) => (brightness.screen_value = value), - }), - Widget.Label({ - vpack: "center", - vexpand: true, - class_name: "brightness-slider-label", - label: brightness - .bind("screen_value") - .as((b) => `${Math.floor(b * 100)}%`), - }), - ], - }), - }), - ], - }); -}; - -export { Brightness }; diff --git a/modules/menus/energy/brightness/index.ts b/modules/menus/energy/brightness/index.ts new file mode 100644 index 0000000..c375375 --- /dev/null +++ b/modules/menus/energy/brightness/index.ts @@ -0,0 +1,59 @@ +import brightness from "../../../../services/Brightness.js"; +import icons from "../../../icons/index.js"; + +const Brightness = () => { + return Widget.Box({ + class_name: "menu-section-container brightness", + vertical: true, + children: [ + Widget.Box({ + class_name: "menu-label-container", + hpack: "fill", + child: Widget.Label({ + class_name: "menu-label", + hexpand: true, + hpack: "start", + label: "Brightness", + }), + }), + Widget.Box({ + class_name: "menu-items-section", + vpack: "fill", + vexpand: true, + vertical: true, + child: Widget.Box({ + class_name: "brightness-container", + children: [ + Widget.Icon({ + vexpand: true, + vpack: "center", + class_name: "brightness-slider-icon", + icon: icons.brightness.screen, + }), + Widget.Slider({ + vpack: "center", + vexpand: true, + value: brightness.bind("screen_value"), + class_name: "menu-active-slider menu-slider brightness", + draw_value: false, + hexpand: true, + min: 0, + max: 1, + onChange: ({ value }) => (brightness.screen_value = value), + }), + Widget.Label({ + vpack: "center", + vexpand: true, + class_name: "brightness-slider-label", + label: brightness + .bind("screen_value") + .as((b) => `${Math.floor(b * 100)}%`), + }), + ], + }), + }), + ], + }); +}; + +export { Brightness }; diff --git a/modules/menus/energy/index.js b/modules/menus/energy/index.js deleted file mode 100644 index dab0347..0000000 --- a/modules/menus/energy/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import DropdownMenu from "../DropdownMenu.js"; -import { EnergyProfiles } from "./profiles/index.js"; -import { Brightness } from "./brightness/index.js"; - -export default () => { - return DropdownMenu({ - name: "energymenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "menu-items energy", - hpack: "fill", - hexpand: true, - child: Widget.Box({ - vertical: true, - hpack: "fill", - hexpand: true, - class_name: "menu-items-container energy", - children: [ - Brightness(), - EnergyProfiles(), - ], - }), - }), - }); -}; diff --git a/modules/menus/energy/index.ts b/modules/menus/energy/index.ts new file mode 100644 index 0000000..514f8a1 --- /dev/null +++ b/modules/menus/energy/index.ts @@ -0,0 +1,25 @@ +import DropdownMenu from "../DropdownMenu.js"; +import { EnergyProfiles } from "./profiles/index.js"; +import { Brightness } from "./brightness/index.js"; + +export default () => { + return DropdownMenu({ + name: "energymenu", + transition: "crossfade", + child: Widget.Box({ + class_name: "menu-items energy", + hpack: "fill", + hexpand: true, + child: Widget.Box({ + vertical: true, + hpack: "fill", + hexpand: true, + class_name: "menu-items-container energy", + children: [ + Brightness(), + EnergyProfiles(), + ], + }), + }), + }); +}; diff --git a/modules/menus/energy/profiles/index.js b/modules/menus/energy/profiles/index.js deleted file mode 100644 index 077076b..0000000 --- a/modules/menus/energy/profiles/index.js +++ /dev/null @@ -1,58 +0,0 @@ -const powerProfiles = await Service.import("powerprofiles"); -import icons from "../../../icons/index.js"; - -const EnergyProfiles = () => { - return Widget.Box({ - class_name: "menu-section-container energy", - vertical: true, - children: [ - Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", - child: Widget.Label({ - class_name: "menu-label", - hexpand: true, - hpack: "start", - label: "Power Profile", - }), - }), - Widget.Box({ - class_name: "menu-items-section", - vpack: "fill", - vexpand: true, - vertical: true, - children: powerProfiles.bind("profiles").as((profiles) => { - return profiles.map((prof) => { - const ProfileLabels = { - "power-saver": "Power Saver", - balanced: "Balanced", - performance: "Performance", - }; - return Widget.Button({ - on_primary_click: () => { - powerProfiles.active_profile = prof.Profile; - }, - class_name: powerProfiles.bind("active_profile").as((active) => { - return `power-profile-item ${active === prof.Profile ? "active" : ""}`; - }), - child: Widget.Box({ - children: [ - Widget.Icon({ - class_name: "power-profile-icon", - icon: icons.powerprofile[prof.Profile], - }), - Widget.Label({ - class_name: "power-profile-label", - label: ProfileLabels[prof.Profile], - }), - ], - }), - }); - }); - }), - }), - ], - }); -}; - -export { EnergyProfiles }; diff --git a/modules/menus/energy/profiles/index.ts b/modules/menus/energy/profiles/index.ts new file mode 100644 index 0000000..80ba93f --- /dev/null +++ b/modules/menus/energy/profiles/index.ts @@ -0,0 +1,58 @@ +const powerProfiles = await Service.import("powerprofiles"); +import icons from "../../../icons/index.js"; + +const EnergyProfiles = () => { + return Widget.Box({ + class_name: "menu-section-container energy", + vertical: true, + children: [ + Widget.Box({ + class_name: "menu-label-container", + hpack: "fill", + child: Widget.Label({ + class_name: "menu-label", + hexpand: true, + hpack: "start", + label: "Power Profile", + }), + }), + Widget.Box({ + class_name: "menu-items-section", + vpack: "fill", + vexpand: true, + vertical: true, + children: powerProfiles.bind("profiles").as((profiles) => { + return profiles.map((prof) => { + const ProfileLabels = { + "power-saver": "Power Saver", + balanced: "Balanced", + performance: "Performance", + }; + return Widget.Button({ + on_primary_click: () => { + powerProfiles.active_profile = prof.Profile; + }, + class_name: powerProfiles.bind("active_profile").as((active) => { + return `power-profile-item ${active === prof.Profile ? "active" : ""}`; + }), + child: Widget.Box({ + children: [ + Widget.Icon({ + class_name: "power-profile-icon", + icon: icons.powerprofile[prof.Profile], + }), + Widget.Label({ + class_name: "power-profile-label", + label: ProfileLabels[prof.Profile], + }), + ], + }), + }); + }); + }), + }), + ], + }); +}; + +export { EnergyProfiles }; diff --git a/modules/menus/main.js b/modules/menus/main.ts similarity index 71% rename from modules/menus/main.js rename to modules/menus/main.ts index dd09519..8a2de30 100644 --- a/modules/menus/main.js +++ b/modules/menus/main.ts @@ -10,14 +10,14 @@ import EnergyMenu from "./energy/index.js"; import DashboardMenu from "./dashboard/index.js"; export default [ - PowerMenu(), - Verification(), - AudioMenu(), - NetworkMenu(), - BluetoothMenu(), - MediaMenu(), - NotificationsMenu(), - CalendarMenu(), - EnergyMenu(), - DashboardMenu(), + PowerMenu(), + Verification(), + AudioMenu(), + NetworkMenu(), + BluetoothMenu(), + MediaMenu(), + NotificationsMenu(), + CalendarMenu(), + EnergyMenu(), + DashboardMenu(), ]; diff --git a/modules/menus/media/components/bar.js b/modules/menus/media/components/bar.js deleted file mode 100644 index 122dd68..0000000 --- a/modules/menus/media/components/bar.js +++ /dev/null @@ -1,67 +0,0 @@ -const media = await Service.import("mpris"); - -const Bar = (getPlayerInfo) => { - return Widget.Box({ - class_name: "media-indicator-current-progress-bar", - hexpand: true, - children: [ - Widget.Box({ - hexpand: true, - child: Widget.Slider({ - hexpand: true, - tooltip_text: "--", - class_name: "menu-slider media progress", - draw_value: false, - on_change: ({ value }) => { - const foundPlayer = getPlayerInfo(media); - if (foundPlayer === undefined) { - return; - } - return (foundPlayer.position = value * foundPlayer.length); - }, - setup: (self) => { - const update = () => { - const foundPlayer = getPlayerInfo(media); - if (foundPlayer !== undefined) { - const value = foundPlayer.position / foundPlayer.length; - self.value = value > 0 ? value : 0; - } else { - self.value = 0; - } - }; - self.hook(media, update); - self.poll(1000, update); - - function updateTooltip() { - const foundPlayer = getPlayerInfo(media); - if (foundPlayer === undefined) { - return self.tooltip_text = '00:00' - } - const curHour = Math.floor(foundPlayer.position / 3600); - const curMin = Math.floor((foundPlayer.position % 3600) / 60); - const curSec = Math.floor(foundPlayer.position % 60); - - if ( - typeof foundPlayer.position === "number" && - foundPlayer.position >= 0 - ) { - // WARN: These nested ternaries are absolutely disgusting lol - self.tooltip_text = `${ - curHour > 0 - ? (curHour < 10 ? "0" + curHour : curHour) + ":" - : "" - }${curMin < 10 ? "0" + curMin : curMin}:${curSec < 10 ? "0" + curSec : curSec}`; - } else { - self.tooltip_text = `00:00`; - } - } - self.poll(1000, updateTooltip); - self.hook(media, updateTooltip); - }, - }), - }), - ], - }); -}; - -export { Bar }; diff --git a/modules/menus/media/components/bar.ts b/modules/menus/media/components/bar.ts new file mode 100644 index 0000000..75914ad --- /dev/null +++ b/modules/menus/media/components/bar.ts @@ -0,0 +1,66 @@ +const media = await Service.import("mpris"); + +const Bar = (getPlayerInfo: Function) => { + return Widget.Box({ + class_name: "media-indicator-current-progress-bar", + hexpand: true, + children: [ + Widget.Box({ + hexpand: true, + child: Widget.Slider({ + hexpand: true, + tooltip_text: "--", + class_name: "menu-slider media progress", + draw_value: false, + on_change: ({ value }) => { + const foundPlayer = getPlayerInfo(media); + if (foundPlayer === undefined) { + return; + } + return (foundPlayer.position = value * foundPlayer.length); + }, + setup: (self) => { + const update = () => { + const foundPlayer = getPlayerInfo(media); + if (foundPlayer !== undefined) { + const value = foundPlayer.position / foundPlayer.length; + self.value = value > 0 ? value : 0; + } else { + self.value = 0; + } + }; + self.hook(media, update); + self.poll(1000, update); + + function updateTooltip() { + const foundPlayer = getPlayerInfo(media); + if (foundPlayer === undefined) { + return self.tooltip_text = '00:00' + } + const curHour = Math.floor(foundPlayer.position / 3600); + const curMin = Math.floor((foundPlayer.position % 3600) / 60); + const curSec = Math.floor(foundPlayer.position % 60); + + if ( + typeof foundPlayer.position === "number" && + foundPlayer.position >= 0 + ) { + // WARN: These nested ternaries are absolutely disgusting lol + self.tooltip_text = `${curHour > 0 + ? (curHour < 10 ? "0" + curHour : curHour) + ":" + : "" + }${curMin < 10 ? "0" + curMin : curMin}:${curSec < 10 ? "0" + curSec : curSec}`; + } else { + self.tooltip_text = `00:00`; + } + } + self.poll(1000, updateTooltip); + self.hook(media, updateTooltip); + }, + }), + }), + ], + }); +}; + +export { Bar }; diff --git a/modules/menus/media/components/controls.js b/modules/menus/media/components/controls.js deleted file mode 100644 index c8f51e1..0000000 --- a/modules/menus/media/components/controls.js +++ /dev/null @@ -1,187 +0,0 @@ -import icons from "../../../icons/index.js"; -const media = await Service.import("mpris"); - -const Controls = (getPlayerInfo) => { - const isLoopActive = (player) => { - return player["loop-status"] !== null && - ["track", "playlist"].includes(player["loop-status"].toLowerCase()) - ? "active" - : ""; - }; - - const isShuffleActive = (player) => { - return player["shuffle-status"] !== null && player["shuffle-status"] - ? "active" - : ""; - }; - - return Widget.Box({ - class_name: "media-indicator-current-player-controls", - vertical: true, - children: [ - Widget.Box({ - class_name: "media-indicator-current-controls", - hpack: "center", - children: [ - Widget.Box({ - class_name: "media-indicator-control shuffle", - children: [ - Widget.Button({ - hpack: "center", - hasTooltip: true, - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.tooltip_text = "Unavailable"; - self.class_name = - "media-indicator-control-button shuffle disabled"; - return; - } - - self.tooltip_text = - foundPlayer.shuffle_status !== null - ? foundPlayer.shuffle_status - ? "Shuffling" - : "Not Shuffling" - : null; - self.on_primary_click = () => foundPlayer.shuffle(); - self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? "enabled" : "disabled"}`; - }); - }, - child: Widget.Icon(icons.mpris.shuffle["enabled"]), - }), - ], - }), - Widget.Box({ - children: [ - Widget.Button({ - hpack: "center", - child: Widget.Icon(icons.mpris.prev), - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button prev disabled"; - return; - } - - self.on_primary_click = () => foundPlayer.previous(); - self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? "enabled" : "disabled"}`; - }); - }, - }), - ], - }), - Widget.Box({ - children: [ - Widget.Button({ - hpack: "center", - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button play disabled"; - return; - } - - self.on_primary_click = () => foundPlayer.playPause(); - self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? "enabled" : "disabled"}`; - }); - }, - child: Widget.Icon({ - icon: Utils.watch( - icons.mpris.paused, - media, - "changed", - () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - return icons.mpris["paused"]; - } - return icons.mpris[ - foundPlayer.play_back_status.toLowerCase() - ]; - }, - ), - }), - }), - ], - }), - Widget.Box({ - class_name: `media-indicator-control next`, - children: [ - Widget.Button({ - hpack: "center", - child: Widget.Icon(icons.mpris.next), - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.class_name = - "media-indicator-control-button next disabled"; - return; - } - - self.on_primary_click = () => foundPlayer.next(); - self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? "enabled" : "disabled"}`; - }); - }, - }), - ], - }), - Widget.Box({ - class_name: "media-indicator-control loop", - children: [ - Widget.Button({ - hpack: "center", - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.tooltip_text = "Unavailable"; - self.class_name = - "media-indicator-control-button shuffle disabled"; - return; - } - - self.tooltip_text = - foundPlayer.loop_status !== null - ? foundPlayer.loop_status - ? "Shuffling" - : "Not Shuffling" - : null; - self.on_primary_click = () => foundPlayer.loop(); - self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? "enabled" : "disabled"}`; - }); - }, - child: Widget.Icon({ - setup: (self) => { - self.hook(media, () => { - const foundPlayer = getPlayerInfo(); - if (foundPlayer === undefined) { - self.icon = icons.mpris.loop["none"]; - return; - } - - self.icon = - foundPlayer.loop_status === null - ? icons.mpris.loop["none"] - : icons.mpris.loop[ - foundPlayer.loop_status?.toLowerCase() - ]; - }); - }, - }), - }), - ], - }), - ], - }), - ], - }); -}; - -export { Controls }; diff --git a/modules/menus/media/components/controls.ts b/modules/menus/media/components/controls.ts new file mode 100644 index 0000000..54471f5 --- /dev/null +++ b/modules/menus/media/components/controls.ts @@ -0,0 +1,188 @@ +import { MprisPlayer } from "types/service/mpris.js"; +import icons from "../../../icons/index.js"; +const media = await Service.import("mpris"); + +const Controls = (getPlayerInfo: Function) => { + const isLoopActive = (player: MprisPlayer) => { + return player["loop-status"] !== null && + ["track", "playlist"].includes(player["loop-status"].toLowerCase()) + ? "active" + : ""; + }; + + const isShuffleActive = (player: MprisPlayer) => { + return player["shuffle-status"] !== null && player["shuffle-status"] + ? "active" + : ""; + }; + + return Widget.Box({ + class_name: "media-indicator-current-player-controls", + vertical: true, + children: [ + Widget.Box({ + class_name: "media-indicator-current-controls", + hpack: "center", + children: [ + Widget.Box({ + class_name: "media-indicator-control shuffle", + children: [ + Widget.Button({ + hpack: "center", + hasTooltip: true, + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.tooltip_text = "Unavailable"; + self.class_name = + "media-indicator-control-button shuffle disabled"; + return; + } + + self.tooltip_text = + foundPlayer.shuffle_status !== null + ? foundPlayer.shuffle_status + ? "Shuffling" + : "Not Shuffling" + : null; + self.on_primary_click = () => foundPlayer.shuffle(); + self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? "enabled" : "disabled"}`; + }); + }, + child: Widget.Icon(icons.mpris.shuffle["enabled"]), + }), + ], + }), + Widget.Box({ + children: [ + Widget.Button({ + hpack: "center", + child: Widget.Icon(icons.mpris.prev), + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.class_name = + "media-indicator-control-button prev disabled"; + return; + } + + self.on_primary_click = () => foundPlayer.previous(); + self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? "enabled" : "disabled"}`; + }); + }, + }), + ], + }), + Widget.Box({ + children: [ + Widget.Button({ + hpack: "center", + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.class_name = + "media-indicator-control-button play disabled"; + return; + } + + self.on_primary_click = () => foundPlayer.playPause(); + self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? "enabled" : "disabled"}`; + }); + }, + child: Widget.Icon({ + icon: Utils.watch( + icons.mpris.paused, + media, + "changed", + () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + return icons.mpris["paused"]; + } + return icons.mpris[ + foundPlayer.play_back_status.toLowerCase() + ]; + }, + ), + }), + }), + ], + }), + Widget.Box({ + class_name: `media-indicator-control next`, + children: [ + Widget.Button({ + hpack: "center", + child: Widget.Icon(icons.mpris.next), + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.class_name = + "media-indicator-control-button next disabled"; + return; + } + + self.on_primary_click = () => foundPlayer.next(); + self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? "enabled" : "disabled"}`; + }); + }, + }), + ], + }), + Widget.Box({ + class_name: "media-indicator-control loop", + children: [ + Widget.Button({ + hpack: "center", + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.tooltip_text = "Unavailable"; + self.class_name = + "media-indicator-control-button shuffle disabled"; + return; + } + + self.tooltip_text = + foundPlayer.loop_status !== null + ? foundPlayer.loop_status + ? "Shuffling" + : "Not Shuffling" + : null; + self.on_primary_click = () => foundPlayer.loop(); + self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? "enabled" : "disabled"}`; + }); + }, + child: Widget.Icon({ + setup: (self) => { + self.hook(media, () => { + const foundPlayer = getPlayerInfo(); + if (foundPlayer === undefined) { + self.icon = icons.mpris.loop["none"]; + return; + } + + self.icon = + foundPlayer.loop_status === null + ? icons.mpris.loop["none"] + : icons.mpris.loop[ + foundPlayer.loop_status?.toLowerCase() + ]; + }); + }, + }), + }), + ], + }), + ], + }), + ], + }); +}; + +export { Controls }; diff --git a/modules/menus/media/components/mediainfo.js b/modules/menus/media/components/mediainfo.js deleted file mode 100644 index 8237b8e..0000000 --- a/modules/menus/media/components/mediainfo.js +++ /dev/null @@ -1,85 +0,0 @@ -const media = await Service.import("mpris"); - -const MediaInfo = (getPlayerInfo) => { - return Widget.Box({ - class_name: "media-indicator-current-media-info", - hpack: "center", - hexpand: true, - vertical: true, - children: [ - Widget.Box({ - class_name: "media-indicator-current-song-name", - hpack: "center", - children: [ - Widget.Label({ - truncate: "end", - max_width_chars: 31, - wrap: true, - class_name: "media-indicator-current-song-name-label", - setup: (self) => { - self.hook(media, () => { - const curPlayer = getPlayerInfo(); - return (self.label = - curPlayer !== undefined && curPlayer["track-title"].length - ? curPlayer["track-title"] - : "No Media Currently Playing"); - }); - }, - }), - ], - }), - Widget.Box({ - class_name: "media-indicator-current-song-author", - hpack: "center", - children: [ - Widget.Label({ - truncate: "end", - wrap: true, - max_width_chars: 35, - class_name: "media-indicator-current-song-author-label", - setup: (self) => { - self.hook(media, () => { - const curPlayer = getPlayerInfo(); - - const makeArtistList = (trackArtists) => { - if (trackArtists.length === 1 && !trackArtists[0].length) { - return "-----"; - } - - return trackArtists.join(", "); - }; - return (self.label = - curPlayer !== undefined && curPlayer["track-artists"].length - ? makeArtistList(curPlayer["track-artists"]) - : "-----"); - }); - }, - }), - ], - }), - Widget.Box({ - class_name: "media-indicator-current-song-album", - hpack: "center", - children: [ - Widget.Label({ - truncate: "end", - wrap: true, - max_width_chars: 40, - class_name: "media-indicator-current-song-album-label", - setup: (self) => { - self.hook(media, () => { - const curPlayer = getPlayerInfo(); - return (self.label = - curPlayer !== undefined && curPlayer["track-album"].length - ? curPlayer["track-album"] - : "---"); - }); - }, - }), - ], - }), - ], - }); -}; - -export { MediaInfo }; diff --git a/modules/menus/media/components/mediainfo.ts b/modules/menus/media/components/mediainfo.ts new file mode 100644 index 0000000..b8a64aa --- /dev/null +++ b/modules/menus/media/components/mediainfo.ts @@ -0,0 +1,85 @@ +const media = await Service.import("mpris"); + +const MediaInfo = (getPlayerInfo: Function) => { + return Widget.Box({ + class_name: "media-indicator-current-media-info", + hpack: "center", + hexpand: true, + vertical: true, + children: [ + Widget.Box({ + class_name: "media-indicator-current-song-name", + hpack: "center", + children: [ + Widget.Label({ + truncate: "end", + max_width_chars: 31, + wrap: true, + class_name: "media-indicator-current-song-name-label", + setup: (self) => { + self.hook(media, () => { + const curPlayer = getPlayerInfo(); + return (self.label = + curPlayer !== undefined && curPlayer["track-title"].length + ? curPlayer["track-title"] + : "No Media Currently Playing"); + }); + }, + }), + ], + }), + Widget.Box({ + class_name: "media-indicator-current-song-author", + hpack: "center", + children: [ + Widget.Label({ + truncate: "end", + wrap: true, + max_width_chars: 35, + class_name: "media-indicator-current-song-author-label", + setup: (self) => { + self.hook(media, () => { + const curPlayer = getPlayerInfo(); + + const makeArtistList = (trackArtists: string[]) => { + if (trackArtists.length === 1 && !trackArtists[0].length) { + return "-----"; + } + + return trackArtists.join(", "); + }; + return (self.label = + curPlayer !== undefined && curPlayer["track-artists"].length + ? makeArtistList(curPlayer["track-artists"]) + : "-----"); + }); + }, + }), + ], + }), + Widget.Box({ + class_name: "media-indicator-current-song-album", + hpack: "center", + children: [ + Widget.Label({ + truncate: "end", + wrap: true, + max_width_chars: 40, + class_name: "media-indicator-current-song-album-label", + setup: (self) => { + self.hook(media, () => { + const curPlayer = getPlayerInfo(); + return (self.label = + curPlayer !== undefined && curPlayer["track-album"].length + ? curPlayer["track-album"] + : "---"); + }); + }, + }), + ], + }), + ], + }); +}; + +export { MediaInfo }; diff --git a/modules/menus/media/index.js b/modules/menus/media/index.ts similarity index 100% rename from modules/menus/media/index.js rename to modules/menus/media/index.ts diff --git a/modules/menus/media/media.js b/modules/menus/media/media.js deleted file mode 100644 index 8ec5b62..0000000 --- a/modules/menus/media/media.js +++ /dev/null @@ -1,75 +0,0 @@ -const media = await Service.import("mpris"); -import { MediaInfo } from "./components/mediainfo.js"; -import { Controls } from "./components/controls.js"; -import { Bar } from "./components/bar.js"; - -const Media = () => { - const curPlayer = Variable(""); - - media.connect("changed", () => { - const statusOrder = { - Playing: 1, - Paused: 2, - Stopped: 3, - }; - - const isPlaying = media.players.find( - (p) => p["play-back-status"] === "Playing", - ); - - if (isPlaying) { - curPlayer.value = media.players.sort( - (a, b) => - statusOrder[a["play-back-status"]] - - statusOrder[b["play-back-status"]], - )[0].name; - } - }); - - const getPlayerInfo = () => { - return media.players.find((p) => p.name === curPlayer.value); - }; - - return Widget.Box({ - class_name: "menu-section-container", - children: [ - Widget.Box({ - class_name: "menu-items-section", - vertical: false, - child: Widget.Box({ - class_name: "menu-content", - children: [ - Widget.Box({ - class_name: "media-content", - child: Widget.Box({ - class_name: "media-indicator-right-section", - hpack: "fill", - hexpand: true, - vertical: true, - children: [ - MediaInfo(getPlayerInfo), - Controls(getPlayerInfo), - Bar(getPlayerInfo), - ], - }), - }), - ], - setup: (self) => { - self.hook(media, () => { - const curPlayer = getPlayerInfo(); - if (curPlayer !== undefined) { - self.css = `background-image: linear-gradient( - rgba(30, 30, 46, 0.85), - rgba(30, 30, 46, 0.9), - #1e1e2e 40em), url("${curPlayer.track_cover_url}"); - `; - } - }); - }, - }), - }), - ], - }); -}; - -export { Media }; diff --git a/modules/menus/media/media.ts b/modules/menus/media/media.ts new file mode 100644 index 0000000..959157d --- /dev/null +++ b/modules/menus/media/media.ts @@ -0,0 +1,76 @@ +const media = await Service.import("mpris"); +import { MediaInfo } from "./components/mediainfo.js"; +import { Controls } from "./components/controls.js"; +import { Bar } from "./components/bar.js"; +import { MprisPlayer } from "types/service/mpris.js"; + +const Media = () => { + const curPlayer = Variable(""); + + media.connect("changed", () => { + const statusOrder = { + Playing: 1, + Paused: 2, + Stopped: 3, + }; + + const isPlaying = media.players.find( + (p) => p["play-back-status"] === "Playing", + ); + + if (isPlaying) { + curPlayer.value = media.players.sort( + (a, b) => + statusOrder[a["play-back-status"]] - + statusOrder[b["play-back-status"]], + )[0].name; + } + }); + + const getPlayerInfo = (): MprisPlayer => { + return media.players.find((p) => p.name === curPlayer.value) || media.players[0]; + }; + + return Widget.Box({ + class_name: "menu-section-container", + children: [ + Widget.Box({ + class_name: "menu-items-section", + vertical: false, + child: Widget.Box({ + class_name: "menu-content", + children: [ + Widget.Box({ + class_name: "media-content", + child: Widget.Box({ + class_name: "media-indicator-right-section", + hpack: "fill", + hexpand: true, + vertical: true, + children: [ + MediaInfo(getPlayerInfo), + Controls(getPlayerInfo), + Bar(getPlayerInfo), + ], + }), + }), + ], + setup: (self) => { + self.hook(media, () => { + const curPlayer = getPlayerInfo(); + if (curPlayer !== undefined) { + self.css = `background-image: linear-gradient( + rgba(30, 30, 46, 0.85), + rgba(30, 30, 46, 0.9), + #1e1e2e 40em), url("${curPlayer.track_cover_url}"); + `; + } + }); + }, + }), + }), + ], + }); +}; + +export { Media }; diff --git a/modules/menus/network/ethernet/index.js b/modules/menus/network/ethernet/index.ts similarity index 100% rename from modules/menus/network/ethernet/index.js rename to modules/menus/network/ethernet/index.ts diff --git a/modules/menus/network/index.js b/modules/menus/network/index.ts similarity index 100% rename from modules/menus/network/index.js rename to modules/menus/network/index.ts diff --git a/modules/menus/network/utils.js b/modules/menus/network/utils.js deleted file mode 100644 index c105166..0000000 --- a/modules/menus/network/utils.js +++ /dev/null @@ -1,23 +0,0 @@ -const getWifiIcon = (iconName) => { - const deviceIconMap = [ - ["network-wireless-acquiring", "󰤩"], - ["network-wireless-connected", "󰤨"], - ["network-wireless-encrypted", "󰤪"], - ["network-wireless-hotspot", "󰤨"], - ["network-wireless-no-route", "󰤩"], - ["network-wireless-offline", "󰤮"], - ["network-wireless-signal-excellent", "󰤨"], - ["network-wireless-signal-good", "󰤥"], - ["network-wireless-signal-ok", "󰤢"], - ["network-wireless-signal-weak", "󰤟"], - ["network-wireless-signal-none", "󰤯"], - ]; - - const foundMatch = deviceIconMap.find((icon) => - RegExp(icon[0]).test(iconName.toLowerCase()), - ); - - return foundMatch ? foundMatch[1] : "󰤨"; -}; - -export { getWifiIcon }; diff --git a/modules/menus/network/utils.ts b/modules/menus/network/utils.ts new file mode 100644 index 0000000..a46e85b --- /dev/null +++ b/modules/menus/network/utils.ts @@ -0,0 +1,23 @@ +const getWifiIcon = (iconName: string) => { + const deviceIconMap = [ + ["network-wireless-acquiring", "󰤩"], + ["network-wireless-connected", "󰤨"], + ["network-wireless-encrypted", "󰤪"], + ["network-wireless-hotspot", "󰤨"], + ["network-wireless-no-route", "󰤩"], + ["network-wireless-offline", "󰤮"], + ["network-wireless-signal-excellent", "󰤨"], + ["network-wireless-signal-good", "󰤥"], + ["network-wireless-signal-ok", "󰤢"], + ["network-wireless-signal-weak", "󰤟"], + ["network-wireless-signal-none", "󰤯"], + ]; + + const foundMatch = deviceIconMap.find((icon) => + RegExp(icon[0]).test(iconName.toLowerCase()), + ); + + return foundMatch ? foundMatch[1] : "󰤨"; +}; + +export { getWifiIcon }; diff --git a/modules/menus/network/wifi/APStaging.js b/modules/menus/network/wifi/APStaging.js deleted file mode 100644 index f8bf186..0000000 --- a/modules/menus/network/wifi/APStaging.js +++ /dev/null @@ -1,97 +0,0 @@ -const renderWapStaging = (self, network, staging, connecting) => { - Utils.merge([network.bind("wifi"), staging.bind("value")], () => { - if (!Object.keys(staging.value).length) { - return (self.child = Widget.Box()); - } - - return (self.child = Widget.Box({ - class_name: "network-element-item staging", - vertical: true, - children: [ - Widget.Box({ - hpack: "fill", - hexpand: true, - children: [ - Widget.Icon({ - class_name: `network-icon wifi`, - icon: `${staging.value.iconName}`, - }), - Widget.Box({ - class_name: "connection-container", - hexpand: true, - vertical: true, - children: [ - Widget.Label({ - class_name: "active-connection", - hpack: "start", - truncate: "end", - wrap: true, - label: staging.value.ssid, - }), - ], - }), - Widget.Revealer({ - hpack: "end", - reveal_child: connecting - .bind("value") - .as((c) => staging.value.bssid === c), - child: Widget.Spinner({ - class_name: "spinner wap", - }), - }), - ], - }), - Widget.Box({ - class_name: "network-password-input-container", - hpack: "fill", - hexpand: true, - children: [ - Widget.Entry({ - hpack: "start", - hexpand: true, - visibility: false, - class_name: "network-password-input", - placeholder_text: "enter password", - onAccept: (selfInp) => { - connecting.value = staging.value.bssid; - Utils.execAsync( - `nmcli dev wifi connect ${staging.value.bssid} password ${selfInp.text}`, - ) - .catch((err) => { - connecting.value = ""; - console.error( - `Failed to connect to wifi: ${staging.value.ssid}... ${err}`, - ); - Utils.notify({ - summary: "Network", - body: err, - timeout: 5000, - }); - }) - .then(() => { - connecting.value = ""; - staging.value = {}; - }); - selfInp.text = ""; - }, - }), - Widget.Button({ - hpack: "end", - class_name: "close-network-password-input-button", - on_primary_click: () => { - connecting.value = ""; - staging.value = {}; - }, - child: Widget.Icon({ - class_name: "close-network-password-input-icon", - icon: "window-close-symbolic", - }), - }), - ], - }), - ], - })); - }); -}; - -export { renderWapStaging }; diff --git a/modules/menus/network/wifi/APStaging.ts b/modules/menus/network/wifi/APStaging.ts new file mode 100644 index 0000000..40bc5d0 --- /dev/null +++ b/modules/menus/network/wifi/APStaging.ts @@ -0,0 +1,100 @@ +import { Network } from "types/service/network"; +import { Variable } from "types/variable"; +import { AccessPoint } from "lib/types/network"; +const renderWapStaging = (self: any, network: Network, staging: Variable, connecting: Variable) => { + Utils.merge([network.bind("wifi"), staging.bind("value")], () => { + if (!Object.keys(staging.value).length) { + return (self.child = Widget.Box()); + } + + return (self.child = Widget.Box({ + class_name: "network-element-item staging", + vertical: true, + children: [ + Widget.Box({ + hpack: "fill", + hexpand: true, + children: [ + Widget.Icon({ + class_name: `network-icon wifi`, + icon: `${staging.value.iconName}`, + }), + Widget.Box({ + class_name: "connection-container", + hexpand: true, + vertical: true, + children: [ + Widget.Label({ + class_name: "active-connection", + hpack: "start", + truncate: "end", + wrap: true, + label: staging.value.ssid, + }), + ], + }), + Widget.Revealer({ + hpack: "end", + reveal_child: connecting + .bind("value") + .as((c) => staging.value.bssid === c), + child: Widget.Spinner({ + class_name: "spinner wap", + }), + }), + ], + }), + Widget.Box({ + class_name: "network-password-input-container", + hpack: "fill", + hexpand: true, + children: [ + Widget.Entry({ + hpack: "start", + hexpand: true, + visibility: false, + class_name: "network-password-input", + placeholder_text: "enter password", + onAccept: (selfInp) => { + connecting.value = staging.value.bssid || ""; + Utils.execAsync( + `nmcli dev wifi connect ${staging.value.bssid} password ${selfInp.text}`, + ) + .catch((err) => { + connecting.value = ""; + console.error( + `Failed to connect to wifi: ${staging.value.ssid}... ${err}`, + ); + Utils.notify({ + summary: "Network", + body: err, + timeout: 5000, + }); + }) + .then(() => { + connecting.value = ""; + staging.value = {} as AccessPoint; + }); + selfInp.text = ""; + }, + }), + Widget.Button({ + hpack: "end", + class_name: "close-network-password-input-button", + on_primary_click: () => { + connecting.value = ""; + staging.value = {} as AccessPoint; + }, + child: Widget.Icon({ + class_name: "close-network-password-input-icon", + icon: "window-close-symbolic", + }), + }), + ], + }), + ], + })); + }); +}; + +export { renderWapStaging }; diff --git a/modules/menus/network/wifi/WirelessAPs.js b/modules/menus/network/wifi/WirelessAPs.js deleted file mode 100644 index fb00c6f..0000000 --- a/modules/menus/network/wifi/WirelessAPs.js +++ /dev/null @@ -1,211 +0,0 @@ -import { getWifiIcon } from "../utils.js"; -const renderWAPs = (self, network, staging, connecting) => { - const getIdBySsid = (ssid, nmcliOutput) => { - const lines = nmcliOutput.trim().split("\n"); - for (const line of lines) { - const columns = line.trim().split(/\s{2,}/); - if (columns[0].includes(ssid)) { - return columns[1]; - } - } - return null; - }; - - const WifiStatusMap = { - unknown: "Status Unknown", - unmanaged: "Unmanaged", - unavailable: "Unavailable", - disconnected: "Disconnected", - prepare: "Preparing Connecting", - config: "Connecting", - need_auth: "Needs Authentication", - ip_config: "Requesting IP", - ip_check: "Checking Access", - secondaries: "Waiting on Secondaries", - activated: "Connected", - deactivating: "Disconnecting", - failed: "Connection Failed", - }; - self.hook(network, () => { - Utils.merge([staging.bind("value"), connecting.bind("value")], () => { - // Sometimes the network service will yield a "this._device is undefined" when - // trying to access the "access_points" property. So we must validate that - // it's not 'undefined' - let WAPs = - network.wifi._device !== undefined ? network.wifi["access-points"] : []; - - const dedupeWAPs = () => { - const dedupMap = {}; - WAPs.forEach((item) => { - if (!Object.hasOwnProperty.call(dedupMap, item.ssid)) { - dedupMap[item.ssid] = item; - } - }); - - return Object.keys(dedupMap).map((itm) => dedupMap[itm]); - }; - - WAPs = dedupeWAPs(); - - const isInStaging = (wap) => { - if (Object.keys(staging.value).length === 0) { - return false; - } - - return wap.bssid === staging.value.bssid; - }; - - const isDisconnecting = (wap) => { - if (wap.ssid === network.wifi.ssid) { - return network.wifi.state.toLowerCase() === "deactivating"; - } - return false; - }; - - const filteredWAPs = WAPs.filter((ap) => { - return ap.ssid !== "Unknown" && !isInStaging(ap); - }).sort((a, b) => { - if (network.wifi.ssid === a.ssid) { - return -1; - } - - if (network.wifi.ssid === b.ssid) { - return 1; - } - - return b.strength - a.strength; - }); - - if (filteredWAPs.length <= 0 && Object.keys(staging.value).length === 0) { - return (self.child = Widget.Label({ - class_name: "waps-not-found dim", - expand: true, - hpack: "center", - vpack: "center", - label: "No Wi-Fi Networks Found", - })); - } - return (self.children = filteredWAPs.map((ap) => { - return Widget.Box({ - children: [ - Widget.Button({ - on_primary_click: () => { - if (ap.bssid === connecting.value || ap.active) { - return; - } - - connecting.value = ap.bssid; - Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`) - .then(() => { - connecting.value = ""; - staging.value = {}; - }) - .catch((err) => { - if ( - err - .toLowerCase() - .includes("secrets were required, but not provided") - ) { - staging.value = ap; - } else { - Utils.notify({ - summary: "Network", - body: err, - timeout: 5000, - }); - } - connecting.value = ""; - }); - }, - class_name: "network-element-item", - child: Widget.Box({ - hexpand: true, - children: [ - Widget.Box({ - hpack: "start", - hexpand: true, - children: [ - Widget.Label({ - vpack: "start", - class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? "active" : ""}`, - label: getWifiIcon(`${ap["iconName"]}`), - }), - Widget.Box({ - class_name: "connection-container", - vpack: "center", - vertical: true, - children: [ - Widget.Label({ - vpack: "center", - class_name: "active-connection", - hpack: "start", - truncate: "end", - wrap: true, - label: ap.ssid, - }), - Widget.Revealer({ - revealChild: ap.ssid === network.wifi.ssid, - child: Widget.Label({ - hpack: "start", - class_name: "connection-status dim", - label: - WifiStatusMap[network.wifi.state.toLowerCase()], - }), - }), - ], - }), - ], - }), - Widget.Revealer({ - hpack: "end", - vpack: "start", - reveal_child: - ap.bssid === connecting.value || isDisconnecting(ap), - child: Widget.Spinner({ - vpack: "start", - class_name: "spinner wap", - }), - }), - ], - }), - }), - Widget.Revealer({ - vpack: "start", - reveal_child: ap.bssid !== connecting.value && ap.active, - child: Widget.Button({ - tooltip_text: "Delete/Forget Network", - class_name: "menu-icon-button network disconnect", - on_primary_click: () => { - connecting.value = ap.bssid; - Utils.execAsync("nmcli connection show --active").then(() => { - Utils.execAsync("nmcli connection show --active").then( - (res) => { - const connectionId = getIdBySsid(ap.ssid, res); - - Utils.execAsync( - `nmcli connection delete ${connectionId} "${ap.ssid}"`, - ) - .then(() => (connecting.value = "")) - .catch((err) => { - connecting.value = ""; - console.error( - `Error while forgetting "${ap.ssid}": ${err}`, - ); - }); - }, - ); - }); - }, - child: Widget.Label({ - label: "󰚃", - }), - }), - }), - ], - }); - })); - }); - }); -}; - -export { renderWAPs }; diff --git a/modules/menus/network/wifi/WirelessAPs.ts b/modules/menus/network/wifi/WirelessAPs.ts new file mode 100644 index 0000000..3b4506b --- /dev/null +++ b/modules/menus/network/wifi/WirelessAPs.ts @@ -0,0 +1,216 @@ +import { Network } from "types/service/network.js"; +import { AccessPoint } from "lib/types/network.js"; +import { Variable } from "types/variable.js"; +import { getWifiIcon } from "../utils.js"; +const renderWAPs = (self: any, network: Network, staging: Variable, connecting: Variable) => { + const getIdBySsid = (ssid: string, nmcliOutput: string) => { + const lines = nmcliOutput.trim().split("\n"); + for (const line of lines) { + const columns = line.trim().split(/\s{2,}/); + if (columns[0].includes(ssid)) { + return columns[1]; + } + } + return null; + }; + + const WifiStatusMap = { + unknown: "Status Unknown", + unmanaged: "Unmanaged", + unavailable: "Unavailable", + disconnected: "Disconnected", + prepare: "Preparing Connecting", + config: "Connecting", + need_auth: "Needs Authentication", + ip_config: "Requesting IP", + ip_check: "Checking Access", + secondaries: "Waiting on Secondaries", + activated: "Connected", + deactivating: "Disconnecting", + failed: "Connection Failed", + }; + self.hook(network, () => { + Utils.merge([staging.bind("value"), connecting.bind("value")], () => { + // Sometimes the network service will yield a "this._device is undefined" when + // trying to access the "access_points" property. So we must validate that + // it's not 'undefined' + // + // Also this is an AGS bug that needs to be fixed + let WAPs = + network.wifi._device !== undefined ? network.wifi["access-points"] : []; + + const dedupeWAPs = () => { + const dedupMap = {}; + WAPs.forEach((item: AccessPoint) => { + if (item.ssid !== null && !Object.hasOwnProperty.call(dedupMap, item.ssid)) { + dedupMap[item.ssid] = item; + } + }); + + return Object.keys(dedupMap).map((itm) => dedupMap[itm]); + }; + + WAPs = dedupeWAPs(); + + const isInStaging = (wap: AccessPoint) => { + if (Object.keys(staging.value).length === 0) { + return false; + } + + return wap.bssid === staging.value.bssid; + }; + + const isDisconnecting = (wap: AccessPoint) => { + if (wap.ssid === network.wifi.ssid) { + return network.wifi.state.toLowerCase() === "deactivating"; + } + return false; + }; + + const filteredWAPs = WAPs.filter((ap: AccessPoint) => { + return ap.ssid !== "Unknown" && !isInStaging(ap); + }).sort((a: AccessPoint, b: AccessPoint) => { + if (network.wifi.ssid === a.ssid) { + return -1; + } + + if (network.wifi.ssid === b.ssid) { + return 1; + } + + return b.strength - a.strength; + }); + + if (filteredWAPs.length <= 0 && Object.keys(staging.value).length === 0) { + return (self.child = Widget.Label({ + class_name: "waps-not-found dim", + expand: true, + hpack: "center", + vpack: "center", + label: "No Wi-Fi Networks Found", + })); + } + return (self.children = filteredWAPs.map((ap: AccessPoint) => { + return Widget.Box({ + children: [ + Widget.Button({ + on_primary_click: () => { + if (ap.bssid === connecting.value || ap.active) { + return; + } + + connecting.value = ap.bssid || ""; + Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`) + .then(() => { + connecting.value = ""; + staging.value = {} as AccessPoint; + }) + .catch((err) => { + if ( + err + .toLowerCase() + .includes("secrets were required, but not provided") + ) { + staging.value = ap; + } else { + Utils.notify({ + summary: "Network", + body: err, + timeout: 5000, + }); + } + connecting.value = ""; + }); + }, + class_name: "network-element-item", + child: Widget.Box({ + hexpand: true, + children: [ + Widget.Box({ + hpack: "start", + hexpand: true, + children: [ + Widget.Label({ + vpack: "start", + class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? "active" : ""}`, + label: getWifiIcon(`${ap["iconName"]}`), + }), + Widget.Box({ + class_name: "connection-container", + vpack: "center", + vertical: true, + children: [ + Widget.Label({ + vpack: "center", + class_name: "active-connection", + hpack: "start", + truncate: "end", + wrap: true, + label: ap.ssid, + }), + Widget.Revealer({ + revealChild: ap.ssid === network.wifi.ssid, + child: Widget.Label({ + hpack: "start", + class_name: "connection-status dim", + label: + WifiStatusMap[network.wifi.state.toLowerCase()], + }), + }), + ], + }), + ], + }), + Widget.Revealer({ + hpack: "end", + vpack: "start", + reveal_child: + ap.bssid === connecting.value || isDisconnecting(ap), + child: Widget.Spinner({ + vpack: "start", + class_name: "spinner wap", + }), + }), + ], + }), + }), + Widget.Revealer({ + vpack: "start", + reveal_child: ap.bssid !== connecting.value && ap.active, + child: Widget.Button({ + tooltip_text: "Delete/Forget Network", + class_name: "menu-icon-button network disconnect", + on_primary_click: () => { + connecting.value = ap.bssid || ""; + Utils.execAsync("nmcli connection show --active").then(() => { + Utils.execAsync("nmcli connection show --active").then( + (res) => { + const connectionId = getIdBySsid(ap.ssid || "", res); + + Utils.execAsync( + `nmcli connection delete ${connectionId} "${ap.ssid}"`, + ) + .then(() => (connecting.value = "")) + .catch((err) => { + connecting.value = ""; + console.error( + `Error while forgetting "${ap.ssid}": ${err}`, + ); + }); + }, + ); + }); + }, + child: Widget.Label({ + label: "󰚃", + }), + }), + }), + ], + }); + })); + }); + }); +}; + +export { renderWAPs }; diff --git a/modules/menus/network/wifi/index.js b/modules/menus/network/wifi/index.js deleted file mode 100644 index 1554c2f..0000000 --- a/modules/menus/network/wifi/index.js +++ /dev/null @@ -1,72 +0,0 @@ -const network = await Service.import("network"); -import { renderWAPs } from "./WirelessAPs.js"; -import { renderWapStaging } from "./APStaging.js"; - -const Staging = Variable({}); -const Connecting = Variable(""); - -const searchInProgress = Variable(false); - -const startRotation = () => { - searchInProgress.value = true; - setTimeout(() => { - searchInProgress.value = false; - }, 5 * 1000); -}; - -const Wifi = () => { - return Widget.Box({ - class_name: "menu-section-container wifi", - vertical: true, - children: [ - Widget.Box({ - class_name: "menu-label-container", - hpack: "fill", - children: [ - Widget.Label({ - class_name: "menu-label", - hexpand: true, - hpack: "start", - label: "Wi-Fi", - }), - Widget.Button({ - vpack: "center", - hpack: "end", - class_name: "menu-icon-button search network", - on_primary_click: () => { - startRotation(); - network.wifi.scan(); - }, - child: Widget.Icon({ - class_name: searchInProgress - .bind("value") - .as((v) => (v ? "spinning" : "")), - icon: "view-refresh-symbolic", - }), - }), - ], - }), - Widget.Box({ - class_name: "menu-items-section", - vertical: true, - children: [ - Widget.Box({ - class_name: "wap-staging", - setup: (self) => { - renderWapStaging(self, network, Staging, Connecting); - }, - }), - Widget.Box({ - class_name: "available-waps", - vertical: true, - setup: (self) => { - renderWAPs(self, network, Staging, Connecting); - }, - }), - ], - }), - ], - }); -}; - -export { Wifi }; diff --git a/modules/menus/network/wifi/index.ts b/modules/menus/network/wifi/index.ts new file mode 100644 index 0000000..8fedae3 --- /dev/null +++ b/modules/menus/network/wifi/index.ts @@ -0,0 +1,73 @@ +const network = await Service.import("network"); +import { renderWAPs } from "./WirelessAPs.js"; +import { renderWapStaging } from "./APStaging.js"; +import { AccessPoint } from "lib/types/network.js"; + +const Staging = Variable({} as AccessPoint); +const Connecting = Variable(""); + +const searchInProgress = Variable(false); + +const startRotation = () => { + searchInProgress.value = true; + setTimeout(() => { + searchInProgress.value = false; + }, 5 * 1000); +}; + +const Wifi = () => { + return Widget.Box({ + class_name: "menu-section-container wifi", + vertical: true, + children: [ + Widget.Box({ + class_name: "menu-label-container", + hpack: "fill", + children: [ + Widget.Label({ + class_name: "menu-label", + hexpand: true, + hpack: "start", + label: "Wi-Fi", + }), + Widget.Button({ + vpack: "center", + hpack: "end", + class_name: "menu-icon-button search network", + on_primary_click: () => { + startRotation(); + network.wifi.scan(); + }, + child: Widget.Icon({ + class_name: searchInProgress + .bind("value") + .as((v) => (v ? "spinning" : "")), + icon: "view-refresh-symbolic", + }), + }), + ], + }), + Widget.Box({ + class_name: "menu-items-section", + vertical: true, + children: [ + Widget.Box({ + class_name: "wap-staging", + setup: (self) => { + renderWapStaging(self, network, Staging, Connecting); + }, + }), + Widget.Box({ + class_name: "available-waps", + vertical: true, + setup: (self) => { + renderWAPs(self, network, Staging, Connecting); + }, + }), + ], + }), + ], + }); +}; + +export { Wifi }; diff --git a/modules/menus/notifications/controls/index.js b/modules/menus/notifications/controls/index.js deleted file mode 100644 index 4019db1..0000000 --- a/modules/menus/notifications/controls/index.js +++ /dev/null @@ -1,56 +0,0 @@ -const Controls = (notifs) => { - return Widget.Box({ - class_name: "notification-menu-controls", - expand: false, - vertical: false, - children: [ - Widget.Box({ - class_name: "menu-label-container notifications", - hpack: "start", - vpack: "center", - expand: true, - children: [ - Widget.Label({ - class_name: "menu-label notifications", - label: "Notifications", - }), - ], - }), - Widget.Box({ - hpack: "end", - vpack: "center", - expand: false, - children: [ - Widget.Switch({ - class_name: "menu-switch notifications", - active: notifs.bind("dnd").as((dnd) => !dnd), - on_activate: ({ active }) => { - notifs.dnd = !active; - }, - }), - Widget.Box({ - children: [ - Widget.Separator({ - hpack: "center", - vexpand: true, - vertical: true, - class_name: "menu-separator notification-controls", - }), - Widget.Button({ - class_name: "clear-notifications-button", - tooltip_text: "Clear Notifications", - on_primary_click: () => notifs.clear(), - child: Widget.Label({ - class_name: "clear-notifications-label", - label: "", - }), - }), - ], - }), - ], - }), - ], - }); -}; - -export { Controls }; diff --git a/modules/menus/notifications/controls/index.ts b/modules/menus/notifications/controls/index.ts new file mode 100644 index 0000000..0bbbce1 --- /dev/null +++ b/modules/menus/notifications/controls/index.ts @@ -0,0 +1,56 @@ +const Controls = (notifs) => { + return Widget.Box({ + class_name: "notification-menu-controls", + expand: false, + vertical: false, + children: [ + Widget.Box({ + class_name: "menu-label-container notifications", + hpack: "start", + vpack: "center", + expand: true, + children: [ + Widget.Label({ + class_name: "menu-label notifications", + label: "Notifications", + }), + ], + }), + Widget.Box({ + hpack: "end", + vpack: "center", + expand: false, + children: [ + Widget.Switch({ + class_name: "menu-switch notifications", + active: notifs.bind("dnd").as((dnd: boolean) => !dnd), + on_activate: ({ active }) => { + notifs.dnd = !active; + }, + }), + Widget.Box({ + children: [ + Widget.Separator({ + hpack: "center", + vexpand: true, + vertical: true, + class_name: "menu-separator notification-controls", + }), + Widget.Button({ + class_name: "clear-notifications-button", + tooltip_text: "Clear Notifications", + on_primary_click: () => notifs.clear(), + child: Widget.Label({ + class_name: "clear-notifications-label", + label: "", + }), + }), + ], + }), + ], + }), + ], + }); +}; + +export { Controls }; diff --git a/modules/menus/notifications/index.js b/modules/menus/notifications/index.ts similarity index 100% rename from modules/menus/notifications/index.js rename to modules/menus/notifications/index.ts diff --git a/modules/menus/notifications/notification/actions/index.js b/modules/menus/notifications/notification/actions/index.js deleted file mode 100644 index 4cfe857..0000000 --- a/modules/menus/notifications/notification/actions/index.js +++ /dev/null @@ -1,47 +0,0 @@ -const Actions = (notif, notifs) => { - if (notif.actions !== undefined && notif.actions.length > 0) { - return Widget.Box({ - class_name: "notification-card-actions menu", - hexpand: true, - vpack: "end", - children: notif.actions.map((action) => { - return Widget.Button({ - hexpand: true, - class_name: "notification-action-buttons menu", - on_primary_click: () => { - if (action.id.includes("scriptAction:-")) { - App.closeWindow("notificationsmenu"); - Utils.execAsync( - `${action.id.replace("scriptAction:-", "")}`, - ).catch((err) => console.error(err)); - notifs.CloseNotification(notif.id); - } else { - App.closeWindow("notificationsmenu"); - notif.invoke(action.id); - } - }, - child: Widget.Box({ - hpack: "center", - hexpand: true, - children: [ - Widget.Label({ - class_name: "notification-action-buttons-label menu", - hexpand: true, - max_width_chars: 15, - truncate: "end", - wrap: true, - label: action.label, - }), - ], - }), - }); - }), - }); - } - - return Widget.Box({ - class_name: "spacer", - }); -}; - -export { Actions }; diff --git a/modules/menus/notifications/notification/actions/index.ts b/modules/menus/notifications/notification/actions/index.ts new file mode 100644 index 0000000..3d9f2c9 --- /dev/null +++ b/modules/menus/notifications/notification/actions/index.ts @@ -0,0 +1,48 @@ +import { Notification, Notifications } from "types/service/notifications"; +const Actions = (notif: Notification, notifs: Notifications) => { + if (notif.actions !== undefined && notif.actions.length > 0) { + return Widget.Box({ + class_name: "notification-card-actions menu", + hexpand: true, + vpack: "end", + children: notif.actions.map((action) => { + return Widget.Button({ + hexpand: true, + class_name: "notification-action-buttons menu", + on_primary_click: () => { + if (action.id.includes("scriptAction:-")) { + App.closeWindow("notificationsmenu"); + Utils.execAsync( + `${action.id.replace("scriptAction:-", "")}`, + ).catch((err) => console.error(err)); + notifs.CloseNotification(notif.id); + } else { + App.closeWindow("notificationsmenu"); + notif.invoke(action.id); + } + }, + child: Widget.Box({ + hpack: "center", + hexpand: true, + children: [ + Widget.Label({ + class_name: "notification-action-buttons-label menu", + hexpand: true, + max_width_chars: 15, + truncate: "end", + wrap: true, + label: action.label, + }), + ], + }), + }); + }), + }); + } + + return Widget.Box({ + class_name: "spacer", + }); +}; + +export { Actions }; diff --git a/modules/menus/notifications/notification/body/index.js b/modules/menus/notifications/notification/body/index.js deleted file mode 100644 index 99e864c..0000000 --- a/modules/menus/notifications/notification/body/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { notifHasImg } from "../../utils.js"; - -export const Body = (notif) => { - return Widget.Box({ - vpack: "start", - hexpand: true, - class_name: "notification-card-body menu", - children: [ - Widget.Label({ - hexpand: true, - use_markup: true, - xalign: 0, - justification: "left", - truncate: "end", - lines: 2, - max_width_chars: !notifHasImg(notif) ? 35 : 28, - wrap: true, - class_name: "notification-card-body-label menu", - label: notif["body"], - }), - ], - }); -}; diff --git a/modules/menus/notifications/notification/body/index.ts b/modules/menus/notifications/notification/body/index.ts new file mode 100644 index 0000000..2372de2 --- /dev/null +++ b/modules/menus/notifications/notification/body/index.ts @@ -0,0 +1,24 @@ +import { notifHasImg } from "../../utils.js"; +import { Notification } from "types/service/notifications"; + +export const Body = (notif: Notification) => { + return Widget.Box({ + vpack: "start", + hexpand: true, + class_name: "notification-card-body menu", + children: [ + Widget.Label({ + hexpand: true, + use_markup: true, + xalign: 0, + justification: "left", + truncate: "end", + lines: 2, + max_width_chars: !notifHasImg(notif) ? 35 : 28, + wrap: true, + class_name: "notification-card-body-label menu", + label: notif["body"], + }), + ], + }); +}; diff --git a/modules/menus/notifications/notification/close/index.js b/modules/menus/notifications/notification/close/index.js deleted file mode 100644 index 0267964..0000000 --- a/modules/menus/notifications/notification/close/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export const CloseButton = (notif, notifs) => { - return Widget.Button({ - class_name: "close-notification-button menu", - on_primary_click: () => { - notifs.CloseNotification(notif.id); - }, - child: Widget.Label({ - label: "󰅜", - hpack: "center", - }), - }); -}; diff --git a/modules/menus/notifications/notification/close/index.ts b/modules/menus/notifications/notification/close/index.ts new file mode 100644 index 0000000..4ef3dff --- /dev/null +++ b/modules/menus/notifications/notification/close/index.ts @@ -0,0 +1,13 @@ +import { Notification, Notifications } from "types/service/notifications"; +export const CloseButton = (notif: Notification, notifs: Notifications) => { + return Widget.Button({ + class_name: "close-notification-button menu", + on_primary_click: () => { + notifs.CloseNotification(notif.id); + }, + child: Widget.Label({ + label: "󰅜", + hpack: "center", + }), + }); +}; diff --git a/modules/menus/notifications/notification/header/icon.js b/modules/menus/notifications/notification/header/icon.ts similarity index 100% rename from modules/menus/notifications/notification/header/icon.js rename to modules/menus/notifications/notification/header/icon.ts diff --git a/modules/menus/notifications/notification/header/index.js b/modules/menus/notifications/notification/header/index.js deleted file mode 100644 index 4653743..0000000 --- a/modules/menus/notifications/notification/header/index.js +++ /dev/null @@ -1,48 +0,0 @@ -import GLib from "gi://GLib"; -import { NotificationIcon } from "./icon.js"; -import { notifHasImg } from "../../utils.js"; - -const time = (time, format = "%I:%M %p") => - GLib.DateTime.new_from_unix_local(time).format(format); - -export const Header = (notif) => { - return Widget.Box({ - vertical: false, - hexpand: true, - children: [ - Widget.Box({ - class_name: "notification-card-header menu", - hpack: "start", - children: [NotificationIcon(notif)], - }), - Widget.Box({ - class_name: "notification-card-header menu", - hexpand: true, - vpack: "start", - children: [ - Widget.Label({ - class_name: "notification-card-header-label menu", - hpack: "start", - hexpand: true, - vexpand: true, - max_width_chars: !notifHasImg(notif) ? 34 : 22, - truncate: "end", - wrap: true, - label: notif["summary"], - }), - ], - }), - Widget.Box({ - class_name: "notification-card-header menu", - hpack: "end", - vpack: "start", - hexpand: true, - child: Widget.Label({ - vexpand: true, - class_name: "notification-time", - label: time(notif.time), - }), - }), - ], - }); -}; diff --git a/modules/menus/notifications/notification/header/index.ts b/modules/menus/notifications/notification/header/index.ts new file mode 100644 index 0000000..0a3de1d --- /dev/null +++ b/modules/menus/notifications/notification/header/index.ts @@ -0,0 +1,49 @@ +import GLib from "gi://GLib"; +import { Notification } from "types/service/notifications"; +import { NotificationIcon } from "./icon.js"; +import { notifHasImg } from "../../utils.js"; + +const time = (time: number, format = "%I:%M %p") => + GLib.DateTime.new_from_unix_local(time).format(format); + +export const Header = (notif: Notification) => { + return Widget.Box({ + vertical: false, + hexpand: true, + children: [ + Widget.Box({ + class_name: "notification-card-header menu", + hpack: "start", + children: [NotificationIcon(notif)], + }), + Widget.Box({ + class_name: "notification-card-header menu", + hexpand: true, + vpack: "start", + children: [ + Widget.Label({ + class_name: "notification-card-header-label menu", + hpack: "start", + hexpand: true, + vexpand: true, + max_width_chars: !notifHasImg(notif) ? 34 : 22, + truncate: "end", + wrap: true, + label: notif["summary"], + }), + ], + }), + Widget.Box({ + class_name: "notification-card-header menu", + hpack: "end", + vpack: "start", + hexpand: true, + child: Widget.Label({ + vexpand: true, + class_name: "notification-time", + label: time(notif.time), + }), + }), + ], + }); +}; diff --git a/modules/menus/notifications/notification/image/index.js b/modules/menus/notifications/notification/image/index.js deleted file mode 100644 index 8eb159f..0000000 --- a/modules/menus/notifications/notification/image/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import { notifHasImg } from "../../utils.js"; - -const Image = (notif) => { - if (notifHasImg(notif)) { - return Widget.Box({ - class_name: "notification-card-image-container menu", - hpack: "center", - vpack: "center", - vexpand: false, - child: Widget.Box({ - hpack: "center", - vexpand: false, - class_name: "notification-card-image menu", - css: `background-image: url("${notif.image}")`, - }), - }); - } - - return Widget.Box(); -}; - -export { Image }; diff --git a/modules/menus/notifications/notification/image/index.ts b/modules/menus/notifications/notification/image/index.ts new file mode 100644 index 0000000..c132385 --- /dev/null +++ b/modules/menus/notifications/notification/image/index.ts @@ -0,0 +1,23 @@ +import { Notification } from "types/service/notifications"; +import { notifHasImg } from "../../utils.js"; + +const Image = (notif: Notification) => { + if (notifHasImg(notif)) { + return Widget.Box({ + class_name: "notification-card-image-container menu", + hpack: "center", + vpack: "center", + vexpand: false, + child: Widget.Box({ + hpack: "center", + vexpand: false, + class_name: "notification-card-image menu", + css: `background-image: url("${notif.image}")`, + }), + }); + } + + return Widget.Box(); +}; + +export { Image }; diff --git a/modules/menus/notifications/notification/index.js b/modules/menus/notifications/notification/index.js deleted file mode 100644 index 9743782..0000000 --- a/modules/menus/notifications/notification/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { notifHasImg } from "../utils.js"; -import { Header } from "./header/index.js"; -import { Actions } from "./actions/index.js"; -import { Image } from "./image/index.js"; -import { Placeholder } from "./placeholder/index.js"; -import { Body } from "./body/index.js"; -import { CloseButton } from "./close/index.js"; - -const NotificationCard = (notifs) => { - return Widget.Box({ - class_name: "menu-content-container notifications", - hpack: "center", - vexpand: true, - spacing: 0, - vertical: true, - setup: (self) => { - self.hook(notifs, () => { - const sortedNotifications = notifs.notifications.sort( - (a, b) => b.time - a.time, - ); - - if (notifs.notifications.length <= 0) { - return (self.children = [Placeholder(notifs)]); - } - - return (self.children = sortedNotifications.map((notif) => { - return Widget.Box({ - class_name: "notification-card-content-container", - children: [ - Widget.Box({ - class_name: "notification-card menu", - vpack: "start", - hexpand: true, - vexpand: false, - children: [ - Image(notif), - Widget.Box({ - vpack: "center", - vertical: true, - hexpand: true, - class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : " menu"}`, - children: [ - Header(notif), - Body(notif), - Actions(notif, notifs), - ], - }), - ], - }), - CloseButton(notif, notifs), - ], - }); - })); - }); - }, - }); -}; - -export { NotificationCard }; diff --git a/modules/menus/notifications/notification/index.ts b/modules/menus/notifications/notification/index.ts new file mode 100644 index 0000000..d975c28 --- /dev/null +++ b/modules/menus/notifications/notification/index.ts @@ -0,0 +1,60 @@ +import { Notifications, Notification } from "types/service/notifications"; +import { notifHasImg } from "../utils.js"; +import { Header } from "./header/index.js"; +import { Actions } from "./actions/index.js"; +import { Image } from "./image/index.js"; +import { Placeholder } from "./placeholder/index.js"; +import { Body } from "./body/index.js"; +import { CloseButton } from "./close/index.js"; + +const NotificationCard = (notifs: Notifications) => { + return Widget.Box({ + class_name: "menu-content-container notifications", + hpack: "center", + vexpand: true, + spacing: 0, + vertical: true, + setup: (self) => { + self.hook(notifs, () => { + const sortedNotifications = notifs.notifications.sort( + (a, b) => b.time - a.time, + ); + + if (notifs.notifications.length <= 0) { + return (self.children = [Placeholder(notifs)]); + } + + return (self.children = sortedNotifications.map((notif: Notification) => { + return Widget.Box({ + class_name: "notification-card-content-container", + children: [ + Widget.Box({ + class_name: "notification-card menu", + vpack: "start", + hexpand: true, + vexpand: false, + children: [ + Image(notif), + Widget.Box({ + vpack: "center", + vertical: true, + hexpand: true, + class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : " menu"}`, + children: [ + Header(notif), + Body(notif), + Actions(notif, notifs), + ], + }), + ], + }), + CloseButton(notif, notifs), + ], + }); + })); + }); + }, + }); +}; + +export { NotificationCard }; diff --git a/modules/menus/notifications/notification/placeholder/index.js b/modules/menus/notifications/notification/placeholder/index.js deleted file mode 100644 index 61cbb2b..0000000 --- a/modules/menus/notifications/notification/placeholder/index.js +++ /dev/null @@ -1,27 +0,0 @@ -const Placeholder = (notifs) => { - return Widget.Box({ - class_name: "notification-label-container", - vpack: "fill", - hpack: "center", - expand: true, - child: Widget.Box({ - vpack: "center", - vertical: true, - expand: true, - children: [ - Widget.Label({ - vpack: "center", - class_name: "placeholder-label dim bell", - label: notifs.bind("dnd").as((dnd) => (dnd ? "󰂛" : "󰂚")), - }), - Widget.Label({ - vpack: "start", - class_name: "placehold-label dim message", - label: "You're all caught up :)", - }), - ], - }), - }); -}; - -export { Placeholder }; diff --git a/modules/menus/notifications/notification/placeholder/index.ts b/modules/menus/notifications/notification/placeholder/index.ts new file mode 100644 index 0000000..32b4473 --- /dev/null +++ b/modules/menus/notifications/notification/placeholder/index.ts @@ -0,0 +1,29 @@ +import { Notifications } from "types/service/notifications"; + +const Placeholder = (notifs: Notifications) => { + return Widget.Box({ + class_name: "notification-label-container", + vpack: "fill", + hpack: "center", + expand: true, + child: Widget.Box({ + vpack: "center", + vertical: true, + expand: true, + children: [ + Widget.Label({ + vpack: "center", + class_name: "placeholder-label dim bell", + label: notifs.bind("dnd").as((dnd) => (dnd ? "󰂛" : "󰂚")), + }), + Widget.Label({ + vpack: "start", + class_name: "placehold-label dim message", + label: "You're all caught up :)", + }), + ], + }), + }); +}; + +export { Placeholder }; diff --git a/modules/menus/notifications/utils.js b/modules/menus/notifications/utils.js deleted file mode 100644 index 033d622..0000000 --- a/modules/menus/notifications/utils.js +++ /dev/null @@ -1,5 +0,0 @@ -const notifHasImg = (notif) => { - return notif.image !== undefined && notif.image.length; -}; - -export { notifHasImg }; diff --git a/modules/menus/notifications/utils.ts b/modules/menus/notifications/utils.ts new file mode 100644 index 0000000..f050165 --- /dev/null +++ b/modules/menus/notifications/utils.ts @@ -0,0 +1,7 @@ +import { Notification } from "types/service/notifications"; + +const notifHasImg = (notif: Notification) => { + return notif.image !== undefined && notif.image.length; +}; + +export { notifHasImg }; diff --git a/modules/menus/power/helpers/actions.js b/modules/menus/power/helpers/actions.js deleted file mode 100644 index efb8543..0000000 --- a/modules/menus/power/helpers/actions.js +++ /dev/null @@ -1,50 +0,0 @@ -import options from "options"; -const { sleep, reboot, logout, shutdown } = options.menus.dashboard.powermenu; - -class PowerMenu extends Service { - static { - Service.register( - this, - {}, - { - title: ["string"], - cmd: ["string"], - }, - ); - } - - #title = ""; - #cmd = ""; - - get title() { - return this.#title; - } - - action(action) { - [this.#cmd, this.#title] = { - sleep: [sleep.value, "Sleep"], - reboot: [reboot.value, "Reboot"], - logout: [logout.value, "Log Out"], - shutdown: [shutdown.value, "Shutdown"], - }[action]; - - this.notify("cmd"); - this.notify("title"); - this.emit("changed"); - App.closeWindow("powermenu"); - App.openWindow("verification"); - } - - shutdown = () => { - this.action("shutdown"); - }; - - exec = () => { - App.closeWindow("verification"); - Utils.exec(this.#cmd); - }; -} - -const powermenu = new PowerMenu(); -Object.assign(globalThis, { powermenu }); -export default powermenu; diff --git a/modules/menus/power/helpers/actions.ts b/modules/menus/power/helpers/actions.ts new file mode 100644 index 0000000..fa8b935 --- /dev/null +++ b/modules/menus/power/helpers/actions.ts @@ -0,0 +1,51 @@ +import { Action } from "lib/types/power"; +import options from "options"; +const { sleep, reboot, logout, shutdown } = options.menus.dashboard.powermenu; + +class PowerMenu extends Service { + static { + Service.register( + this, + {}, + { + title: ["string"], + cmd: ["string"], + }, + ); + } + + #title = ""; + #cmd = ""; + + get title() { + return this.#title; + } + + action(action: Action) { + [this.#cmd, this.#title] = { + sleep: [sleep.value, "Sleep"], + reboot: [reboot.value, "Reboot"], + logout: [logout.value, "Log Out"], + shutdown: [shutdown.value, "Shutdown"], + }[action]; + + this.notify("cmd"); + this.notify("title"); + this.emit("changed"); + App.closeWindow("powermenu"); + App.openWindow("verification"); + } + + shutdown = () => { + this.action("shutdown"); + }; + + exec = () => { + App.closeWindow("verification"); + Utils.exec(this.#cmd); + }; +} + +const powermenu = new PowerMenu(); +Object.assign(globalThis, { powermenu }); +export default powermenu; diff --git a/modules/menus/power/index.js b/modules/menus/power/index.js deleted file mode 100644 index bfd7bd0..0000000 --- a/modules/menus/power/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import PopupWindow from "../PopupWindow.js"; -import powermenu from "./helpers/actions.js"; -import icons from "../../icons/index.js"; - -const SysButton = (action, label) => - Widget.Button({ - class_name: `widget-button powermenu-button-${action}`, - on_clicked: () => powermenu.action(action), - child: Widget.Box({ - vertical: true, - class_name: "system-button widget-box", - children: [ - Widget.Icon({ - class_name: `system-button_icon ${action}`, - icon: icons.powermenu[action], - }), - Widget.Label({ - class_name: `system-button_label ${action}`, - label, - }), - ], - }), - }); -export default () => - PopupWindow({ - name: "powermenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "powermenu horizontal", - children: [ - SysButton("shutdown", "SHUTDOWN"), - SysButton("logout", "LOG OUT"), - SysButton("reboot", "REBOOT"), - SysButton("sleep", "SLEEP"), - ], - }), - }); diff --git a/modules/menus/power/index.ts b/modules/menus/power/index.ts new file mode 100644 index 0000000..aa9065e --- /dev/null +++ b/modules/menus/power/index.ts @@ -0,0 +1,38 @@ +import { Action } from "lib/types/power.js"; +import PopupWindow from "../PopupWindow.js"; +import powermenu from "./helpers/actions.js"; +import icons from "../../icons/index.js"; + +const SysButton = (action: Action, label: string) => + Widget.Button({ + class_name: `widget-button powermenu-button-${action}`, + on_clicked: () => powermenu.action(action), + child: Widget.Box({ + vertical: true, + class_name: "system-button widget-box", + children: [ + Widget.Icon({ + class_name: `system-button_icon ${action}`, + icon: icons.powermenu[action], + }), + Widget.Label({ + class_name: `system-button_label ${action}`, + label, + }), + ], + }), + }); +export default () => + PopupWindow({ + name: "powermenu", + transition: "crossfade", + child: Widget.Box({ + class_name: "powermenu horizontal", + children: [ + SysButton("shutdown", "SHUTDOWN"), + SysButton("logout", "LOG OUT"), + SysButton("reboot", "REBOOT"), + SysButton("sleep", "SLEEP"), + ], + }), + }); diff --git a/modules/menus/power/verification.js b/modules/menus/power/verification.ts similarity index 100% rename from modules/menus/power/verification.js rename to modules/menus/power/verification.ts diff --git a/modules/notifications/actions/index.js b/modules/notifications/actions/index.js deleted file mode 100644 index eb61f55..0000000 --- a/modules/notifications/actions/index.js +++ /dev/null @@ -1,43 +0,0 @@ -const Action = (notif, notifs) => { - if (notif.actions !== undefined && notif.actions.length > 0) { - return Widget.Box({ - class_name: "notification-card-actions", - hexpand: true, - vpack: "end", - children: notif.actions.map((action) => { - return Widget.Button({ - hexpand: true, - class_name: "notification-action-buttons", - on_primary_click: () => { - if (action.id.includes("scriptAction:-")) { - Utils.execAsync( - `${action.id.replace("scriptAction:-", "")}`, - ).catch((err) => console.error(err)); - notifs.CloseNotification(notif.id); - } else { - notif.invoke(action.id); - } - }, - child: Widget.Box({ - hpack: "center", - hexpand: true, - children: [ - Widget.Label({ - class_name: "notification-action-buttons-label", - hexpand: true, - label: action.label, - max_width_chars: 15, - truncate: "end", - wrap: true, - }), - ], - }), - }); - }), - }); - } - - return Widget.Box(); -}; - -export { Action }; diff --git a/modules/notifications/actions/index.ts b/modules/notifications/actions/index.ts new file mode 100644 index 0000000..85e3eb9 --- /dev/null +++ b/modules/notifications/actions/index.ts @@ -0,0 +1,45 @@ +import { Notification, Notifications } from "types/service/notifications"; + +const Action = (notif: Notification, notifs: Notifications) => { + if (notif.actions !== undefined && notif.actions.length > 0) { + return Widget.Box({ + class_name: "notification-card-actions", + hexpand: true, + vpack: "end", + children: notif.actions.map((action) => { + return Widget.Button({ + hexpand: true, + class_name: "notification-action-buttons", + on_primary_click: () => { + if (action.id.includes("scriptAction:-")) { + Utils.execAsync( + `${action.id.replace("scriptAction:-", "")}`, + ).catch((err) => console.error(err)); + notifs.CloseNotification(notif.id); + } else { + notif.invoke(action.id); + } + }, + child: Widget.Box({ + hpack: "center", + hexpand: true, + children: [ + Widget.Label({ + class_name: "notification-action-buttons-label", + hexpand: true, + label: action.label, + max_width_chars: 15, + truncate: "end", + wrap: true, + }), + ], + }), + }); + }), + }); + } + + return Widget.Box(); +}; + +export { Action }; diff --git a/modules/notifications/body/index.js b/modules/notifications/body/index.js deleted file mode 100644 index 0c4be9f..0000000 --- a/modules/notifications/body/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { notifHasImg } from "../../menus/notifications/utils.js"; - -export const Body = (notif) => { - return Widget.Box({ - vpack: "start", - hexpand: true, - class_name: "notification-card-body", - children: [ - Widget.Label({ - hexpand: true, - use_markup: true, - xalign: 0, - justification: "left", - truncate: "end", - lines: 2, - max_width_chars: !notifHasImg(notif) ? 35 : 28, - wrap: true, - class_name: "notification-card-body-label", - label: notif["body"], - }), - ], - }); -}; diff --git a/modules/notifications/body/index.ts b/modules/notifications/body/index.ts new file mode 100644 index 0000000..0369615 --- /dev/null +++ b/modules/notifications/body/index.ts @@ -0,0 +1,24 @@ +import { Notification } from "types/service/notifications"; +import { notifHasImg } from "../../menus/notifications/utils.js"; + +export const Body = (notif: Notification) => { + return Widget.Box({ + vpack: "start", + hexpand: true, + class_name: "notification-card-body", + children: [ + Widget.Label({ + hexpand: true, + use_markup: true, + xalign: 0, + justification: "left", + truncate: "end", + lines: 2, + max_width_chars: !notifHasImg(notif) ? 35 : 28, + wrap: true, + class_name: "notification-card-body-label", + label: notif["body"], + }), + ], + }); +}; diff --git a/modules/notifications/close/index.js b/modules/notifications/close/index.js deleted file mode 100644 index cbe3981..0000000 --- a/modules/notifications/close/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export const CloseButton = (notif, notifs) => { - return Widget.Button({ - class_name: "close-notification-button", - on_primary_click: () => { - notifs.CloseNotification(notif.id); - }, - child: Widget.Label({ - label: "󰅜", - hpack: "center", - }), - }); -}; diff --git a/modules/notifications/close/index.ts b/modules/notifications/close/index.ts new file mode 100644 index 0000000..2da8b7e --- /dev/null +++ b/modules/notifications/close/index.ts @@ -0,0 +1,14 @@ +import { Notification, Notifications } from "types/service/notifications"; + +export const CloseButton = (notif: Notification, notifs: Notifications) => { + return Widget.Button({ + class_name: "close-notification-button", + on_primary_click: () => { + notifs.CloseNotification(notif.id); + }, + child: Widget.Label({ + label: "󰅜", + hpack: "center", + }), + }); +}; diff --git a/modules/notifications/header/icon.js b/modules/notifications/header/icon.ts similarity index 100% rename from modules/notifications/header/icon.js rename to modules/notifications/header/icon.ts diff --git a/modules/notifications/header/index.js b/modules/notifications/header/index.js deleted file mode 100644 index b3ed5ea..0000000 --- a/modules/notifications/header/index.js +++ /dev/null @@ -1,49 +0,0 @@ -import GLib from "gi://GLib"; -import { notifHasImg } from "../../menus/notifications/utils.js"; -import { NotificationIcon } from "./icon.js"; - -const time = (time, format = "%I:%M %p") => - GLib.DateTime.new_from_unix_local(time).format(format); - -export const Header = (notif) => { - return Widget.Box({ - vertical: false, - hexpand: true, - children: [ - Widget.Box({ - class_name: "notification-card-header", - hpack: "start", - children: [NotificationIcon(notif)], - }), - Widget.Box({ - class_name: "notification-card-header", - hexpand: true, - hpack: "start", - vpack: "start", - children: [ - Widget.Label({ - class_name: "notification-card-header-label", - hpack: "start", - hexpand: true, - vexpand: true, - max_width_chars: !notifHasImg(notif) ? 30 : 19, - truncate: "end", - wrap: true, - label: notif["summary"], - }), - ], - }), - Widget.Box({ - class_name: "notification-card-header menu", - hpack: "end", - vpack: "start", - hexpand: true, - child: Widget.Label({ - vexpand: true, - class_name: "notification-time", - label: time(notif.time), - }), - }), - ], - }); -}; diff --git a/modules/notifications/header/index.ts b/modules/notifications/header/index.ts new file mode 100644 index 0000000..72e2476 --- /dev/null +++ b/modules/notifications/header/index.ts @@ -0,0 +1,50 @@ +import GLib from "gi://GLib"; +import { notifHasImg } from "../../menus/notifications/utils.js"; +import { NotificationIcon } from "./icon.js"; +import { Notification } from "types/service/notifications"; + +const time = (time: number, format = "%I:%M %p") => + GLib.DateTime.new_from_unix_local(time).format(format); + +export const Header = (notif: Notification) => { + return Widget.Box({ + vertical: false, + hexpand: true, + children: [ + Widget.Box({ + class_name: "notification-card-header", + hpack: "start", + children: [NotificationIcon(notif)], + }), + Widget.Box({ + class_name: "notification-card-header", + hexpand: true, + hpack: "start", + vpack: "start", + children: [ + Widget.Label({ + class_name: "notification-card-header-label", + hpack: "start", + hexpand: true, + vexpand: true, + max_width_chars: !notifHasImg(notif) ? 30 : 19, + truncate: "end", + wrap: true, + label: notif["summary"], + }), + ], + }), + Widget.Box({ + class_name: "notification-card-header menu", + hpack: "end", + vpack: "start", + hexpand: true, + child: Widget.Label({ + vexpand: true, + class_name: "notification-time", + label: time(notif.time), + }), + }), + ], + }); +}; diff --git a/modules/notifications/image/index.js b/modules/notifications/image/index.js deleted file mode 100644 index 9542bb3..0000000 --- a/modules/notifications/image/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import { notifHasImg } from "../../menus/notifications/utils.js"; - -const Image = (notif) => { - if (notifHasImg(notif)) { - return Widget.Box({ - class_name: "notification-card-image-container", - hpack: "center", - vpack: "center", - vexpand: false, - child: Widget.Box({ - hpack: "center", - vexpand: false, - class_name: "notification-card-image", - css: `background-image: url("${notif.image}")`, - }), - }); - } - - return Widget.Box(); -}; - -export { Image }; diff --git a/modules/notifications/image/index.ts b/modules/notifications/image/index.ts new file mode 100644 index 0000000..eb068a9 --- /dev/null +++ b/modules/notifications/image/index.ts @@ -0,0 +1,23 @@ +import { Notification } from "types/service/notifications"; +import { notifHasImg } from "../../menus/notifications/utils.js"; + +const Image = (notif: Notification) => { + if (notifHasImg(notif)) { + return Widget.Box({ + class_name: "notification-card-image-container", + hpack: "center", + vpack: "center", + vexpand: false, + child: Widget.Box({ + hpack: "center", + vexpand: false, + class_name: "notification-card-image", + css: `background-image: url("${notif.image}")`, + }), + }); + } + + return Widget.Box(); +}; + +export { Image }; diff --git a/modules/notifications/index.js b/modules/notifications/index.js deleted file mode 100644 index 0ceeb30..0000000 --- a/modules/notifications/index.js +++ /dev/null @@ -1,53 +0,0 @@ -const notifs = await Service.import("notifications"); -import options from "options"; -import { notifHasImg } from "../menus/notifications/utils.js"; -import { Image } from "./image/index.js"; -import { Action } from "./actions/index.js"; -import { Header } from "./header/index.js"; -import { Body } from "./body/index.js"; -import { CloseButton } from "./close/index.js"; - -const { position } = options.notifications; - -export default () => { - notifs.popupTimeout = 7000; - const getPosition = (pos) => { - return pos.split(" "); - } - - return Widget.Window({ - name: "notifications-window", - class_name: "notifications-window", - monitor: 2, - layer: "top", - anchor: position.bind("value").as(v => getPosition(v)), - exclusivity: "ignore", - child: Widget.Box({ - class_name: "notification-card-container", - vertical: true, - hexpand: true, - setup: (self) => { - self.hook(notifs, () => { - return (self.children = notifs.popups.map((notif) => { - return Widget.Box({ - class_name: "notification-card", - vpack: "start", - hexpand: true, - children: [ - Image(notif), - Widget.Box({ - vpack: "start", - vertical: true, - hexpand: true, - class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : ""}`, - children: [Header(notif), Body(notif), Action(notif, notifs)], - }), - CloseButton(notif, notifs), - ], - }); - })); - }); - }, - }), - }); -}; diff --git a/modules/notifications/index.ts b/modules/notifications/index.ts new file mode 100644 index 0000000..d6114c7 --- /dev/null +++ b/modules/notifications/index.ts @@ -0,0 +1,63 @@ +const notifs = await Service.import("notifications"); +import options from "options"; +import { notifHasImg } from "../menus/notifications/utils.js"; +import { Image } from "./image/index.js"; +import { Action } from "./actions/index.js"; +import { Header } from "./header/index.js"; +import { Body } from "./body/index.js"; +import { CloseButton } from "./close/index.js"; +import { NotificationAnchor } from "lib/types/options"; + +const { position } = options.notifications; + +export default () => { + notifs.popupTimeout = 7000; + const getPosition = (pos: NotificationAnchor): ("top" | "bottom" | "left" | "right")[] => { + const positionMap: { [key: string]: ("top" | "bottom" | "left" | "right")[] } = { + "top": ["top"], + "top right": ["top", "right"], + "top left": ["top", "left"], + "bottom": ["bottom"], + "bottom right": ["bottom", "right"], + "bottom left": ["bottom", "left"] + }; + + return positionMap[pos] || ["top"]; + } + + return Widget.Window({ + name: "notifications-window", + class_name: "notifications-window", + monitor: 2, + layer: "top", + anchor: position.bind("value").as(v => getPosition(v)), + exclusivity: "ignore", + child: Widget.Box({ + class_name: "notification-card-container", + vertical: true, + hexpand: true, + setup: (self) => { + self.hook(notifs, () => { + return (self.children = notifs.popups.map((notif) => { + return Widget.Box({ + class_name: "notification-card", + vpack: "start", + hexpand: true, + children: [ + Image(notif), + Widget.Box({ + vpack: "start", + vertical: true, + hexpand: true, + class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : ""}`, + children: [Header(notif), Body(notif), Action(notif, notifs)], + }), + CloseButton(notif, notifs), + ], + }); + })); + }); + }, + }), + }); +}; diff --git a/modules/shared/barItemBox.js b/modules/shared/barItemBox.ts similarity index 100% rename from modules/shared/barItemBox.js rename to modules/shared/barItemBox.ts diff --git a/services/Brightness.js b/services/Brightness.ts similarity index 97% rename from services/Brightness.js rename to services/Brightness.ts index 86d8788..895f6f9 100644 --- a/services/Brightness.js +++ b/services/Brightness.ts @@ -72,7 +72,7 @@ class BrightnessService extends Service { // overwriting the connect method, let's you // change the default event that widgets connect to - connect(event = 'screen-changed', callback) { + connect(event: string = 'screen-changed', callback: any) { return super.connect(event, callback); } }