aboutsummaryrefslogtreecommitdiff
path: root/ags/modules
diff options
context:
space:
mode:
authoreric.marin <maarin.eric@gmail.com>2024-12-18 21:06:41 +0100
committereric.marin <maarin.eric@gmail.com>2024-12-18 21:06:41 +0100
commit000a7c2a4c4bda36f655f6489b5bac3211515a5d (patch)
tree8a5fe85f0632fcca678f0204a4288e6b3423f772 /ags/modules
parentb0cfdab1e93f660fda8f9398e30c9c996a1760f3 (diff)
downloaddotfiles-000a7c2a4c4bda36f655f6489b5bac3211515a5d.tar.gz
dotfiles-000a7c2a4c4bda36f655f6489b5bac3211515a5d.zip
niri and fuzzel
Diffstat (limited to 'ags/modules')
-rw-r--r--ags/modules/applauncher.js107
-rw-r--r--ags/modules/bar.js131
2 files changed, 238 insertions, 0 deletions
diff --git a/ags/modules/applauncher.js b/ags/modules/applauncher.js
new file mode 100644
index 0000000..f380185
--- /dev/null
+++ b/ags/modules/applauncher.js
@@ -0,0 +1,107 @@
+const { query } = await Service.import("applications")
+const WINDOW_NAME = "applauncher"
+
+/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */
+const AppItem = app => Widget.Button({
+ on_clicked: () => {
+ App.closeWindow(WINDOW_NAME)
+ app.launch()
+ },
+ attribute: { app },
+ child: Widget.Box({
+ children: [
+ Widget.Icon({
+ icon: app.icon_name || "",
+ size: 42,
+ }),
+ Widget.Label({
+ class_name: "title",
+ label: app.name,
+ xalign: 0,
+ vpack: "center",
+ truncate: "end",
+ }),
+ ],
+ }),
+})
+
+const Applauncher = ({ width = 500, height = 500, spacing = 12 }) => {
+ // list of application buttons
+ let applications = query("").map(AppItem)
+
+ // container holding the buttons
+ const list = Widget.Box({
+ vertical: true,
+ children: applications,
+ spacing,
+ })
+
+ // repopulate the box, so the most frequent apps are on top of the list
+ function repopulate() {
+ applications = query("").map(AppItem)
+ list.children = applications
+ }
+
+ // search entry
+ const entry = Widget.Entry({
+ hexpand: true,
+ css: `margin-bottom: ${spacing}px;`,
+
+ // to launch the first item on Enter
+ on_accept: () => {
+ // make sure we only consider visible (searched for) applications
+ const results = applications.filter((item) => item.visible);
+ if (results[0]) {
+ App.toggleWindow(WINDOW_NAME)
+ results[0].attribute.app.launch()
+ }
+ },
+
+ // filter out the list
+ on_change: ({ text }) => applications.forEach(item => {
+ item.visible = item.attribute.app.match(text ?? "")
+ }),
+ })
+
+ return Widget.Box({
+ vertical: true,
+ css: `margin: ${spacing * 2}px;`,
+ children: [
+ entry,
+
+ // wrap the list in a scrollable
+ Widget.Scrollable({
+ hscroll: "never",
+ css: `min-width: ${width}px;`
+ + `min-height: ${height}px;`,
+ child: list,
+ }),
+ ],
+ setup: self => self.hook(App, (_, windowName, visible) => {
+ if (windowName !== WINDOW_NAME)
+ return
+
+ // when the applauncher shows up
+ if (visible) {
+ repopulate()
+ entry.text = ""
+ entry.grab_focus()
+ }
+ }),
+ })
+}
+
+// there needs to be only one instance
+export const applauncher = Widget.Window({
+ name: WINDOW_NAME,
+ setup: self => self.keybind("Escape", () => {
+ App.closeWindow(WINDOW_NAME)
+ }),
+ visible: false,
+ keymode: "exclusive",
+ child: Applauncher({
+ width: 500,
+ height: 500,
+ spacing: 12,
+ }),
+})
diff --git a/ags/modules/bar.js b/ags/modules/bar.js
new file mode 100644
index 0000000..28fdaee
--- /dev/null
+++ b/ags/modules/bar.js
@@ -0,0 +1,131 @@
+const hyprland = await Service.import("hyprland")
+const audio = await Service.import("audio")
+const battery = await Service.import("battery")
+const network = await Service.import("network")
+
+const hour = Variable("", {
+ poll: [60000, 'date "+%H:%M"']
+})
+
+function IconLabel(icon, label) {
+ return [
+ Widget.Icon({ icon }),
+ Widget.Label({ label }),
+ ]
+}
+
+function Workspaces() {
+ const active = hyprland.active.workspace.bind("id")
+ const workspaces = hyprland.bind("workspaces")
+ .as(ws => ws.map(({ id }) => Widget.Button({
+ on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`),
+ child: Widget.Label(`${id}`),
+ class_name: active.as(i => `${i === id ? "focused" : ""}`)
+ })))
+ return Widget.Box({
+ class_name: "workspaces",
+ children: workspaces,
+ })
+}
+
+//function SysTray() {
+//
+//}
+
+function Clock() {
+ return Widget.Label({
+ class_name: "clock",
+ label: hour.bind(),
+ })
+}
+
+function Network() {
+ const label = network.wifi.bind("strength").as(s => ` ${s}`)
+ return Widget.Box({
+ class_name: "network",
+ children: [
+ Widget.Label({ label })
+ ]
+ })
+}
+
+function Volume() {
+ const icons = {
+ 101: "overamplified",
+ 67: "high",
+ 34: "medium",
+ 1: "low",
+ 0: "muted",
+ }
+ function getIcon() {
+ const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find(
+ threshold => threshold <= audio.speaker.volume * 100)
+
+ return `audio-volume-${icons[icon]}-symbolic`
+ }
+
+ const icon = Utils.watch(getIcon(), audio.speaker, getIcon)
+ const label = audio.speaker.bind("volume").as(v => ` ${Math.floor(v * 100 + 0.01)}%`)
+
+ return Widget.Box({
+ class_name: "volume",
+ children: IconLabel(icon, label)
+ })
+}
+
+function Battery() {
+ const label = battery.bind("percent").as(p => ` ${p}%`)
+ const icon = battery.bind("percent").as(p =>
+ `battery-level-${Math.floor(p / 10) * 10}-symbolic`
+ )
+
+ return Widget.Box({
+ class_name: "battery",
+ children: IconLabel(icon, label)
+ })
+}
+
+function Left() {
+ return Widget.Box({
+ spacing: 10,
+ children: [
+ Workspaces(),
+ //SysTray(),
+ ]
+ })
+}
+function Center() {
+ return Widget.Box({
+ spacing: 10,
+ children: [
+ Clock(),
+ ]
+ })
+}
+function Right() {
+ return Widget.Box({
+ hpack: "end",
+ spacing: 10,
+ children: [
+ Network(),
+ Volume(),
+ Battery(),
+ ]
+ })
+}
+
+export function Bar(monitor = 0) {
+ return Widget.Window({
+ name: `bar-${monitor}`,
+ class_name: "bar",
+ monitor,
+ anchor: ["top", "left", "right"],
+ exclusivity: "exclusive",
+ child: Widget.CenterBox({
+ start_widget: Left(),
+ center_widget: Center(),
+ end_widget: Right(),
+ }),
+ })
+}
+