From fcd23e73c3f751bc1c68832bdf59572674547fef Mon Sep 17 00:00:00 2001 From: Anthony Rodriguez Date: Thu, 3 Oct 2024 11:18:45 +0200 Subject: [PATCH] home/programs/ags: add example bar --- home/programs/ags/default.nix | 1 + home/programs/ags/src/README.md | 15 ++ home/programs/ags/src/config.js | 214 ++++++++++++++++++++++++++++ home/programs/ags/src/style.css | 40 ++++++ home/programs/ags/src/tsconfig.json | 18 +++ home/programs/ags/src/types | 1 + 6 files changed, 289 insertions(+) create mode 100644 home/programs/ags/src/README.md create mode 100644 home/programs/ags/src/config.js create mode 100644 home/programs/ags/src/style.css create mode 100644 home/programs/ags/src/tsconfig.json create mode 120000 home/programs/ags/src/types diff --git a/home/programs/ags/default.nix b/home/programs/ags/default.nix index b8705b4..bf4f2dd 100644 --- a/home/programs/ags/default.nix +++ b/home/programs/ags/default.nix @@ -4,6 +4,7 @@ ... }: { imports = [inputs.ags.homeManagerModules.default]; + programs.ags = { enable = true; diff --git a/home/programs/ags/src/README.md b/home/programs/ags/src/README.md new file mode 100644 index 0000000..be94d6b --- /dev/null +++ b/home/programs/ags/src/README.md @@ -0,0 +1,15 @@ + +# Starter Config + +if suggestions don't work, first make sure +you have TypeScript LSP working in your editor + +if you do not want typechecking only suggestions + +```json +// tsconfig.json +"checkJs": false +``` + +types are symlinked to: +/home/nezia/.local/share/com.github.Aylur.ags/types diff --git a/home/programs/ags/src/config.js b/home/programs/ags/src/config.js new file mode 100644 index 0000000..246fbef --- /dev/null +++ b/home/programs/ags/src/config.js @@ -0,0 +1,214 @@ +// const hyprland = await Service.import("hyprland"); +const notifications = await Service.import("notifications"); +const mpris = await Service.import("mpris"); +const audio = await Service.import("audio"); +const battery = await Service.import("battery"); +const systemtray = await Service.import("systemtray"); + +const date = Variable("", { + poll: [1000, 'date "+%H:%M %b %e."'], +}); + +// widgets can be only assigned as a child in one container +// so to make a reuseable widget, make it a function +// then you can simply instantiate one by calling it + +//function Workspaces() { +// const activeId = 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: activeId.as((i) => `${i === id ? "focused" : ""}`), +// }), +// ), +// ); +// +// return Widget.Box({ +// class_name: "workspaces", +// children: workspaces, +// }); +//} + +function ClientTitle() { + return Widget.Label({ + class_name: "client-title", + label: "Label!", // hyprland.active.client.bind("title"), + }); +} + +function Clock() { + return Widget.Label({ + class_name: "clock", + label: date.bind(), + }); +} + +// we don't need dunst or any other notification daemon +// because the Notifications module is a notification daemon itself +function Notification() { + const popups = notifications.bind("popups"); + return Widget.Box({ + class_name: "notification", + visible: popups.as((p) => p.length > 0), + children: [ + Widget.Icon({ + icon: "preferences-system-notifications-symbolic", + }), + Widget.Label({ + label: popups.as((p) => p[0]?.summary || ""), + }), + ], + }); +} + +function Media() { + const label = Utils.watch("", mpris, "player-changed", () => { + if (mpris.players[0]) { + const { track_artists, track_title } = mpris.players[0]; + return `${track_artists.join(", ")} - ${track_title}`; + } else { + return "Nothing is playing"; + } + }); + + return Widget.Button({ + class_name: "media", + on_primary_click: () => mpris.getPlayer("")?.playPause(), + on_scroll_up: () => mpris.getPlayer("")?.next(), + on_scroll_down: () => mpris.getPlayer("")?.previous(), + child: 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 = Widget.Icon({ + icon: Utils.watch(getIcon(), audio.speaker, getIcon), + }); + + const slider = Widget.Slider({ + hexpand: true, + draw_value: false, + on_change: ({ value }) => (audio.speaker.volume = value), + setup: (self) => + self.hook(audio.speaker, () => { + self.value = audio.speaker.volume || 0; + }), + }); + + return Widget.Box({ + class_name: "volume", + css: "min-width: 180px", + children: [icon, slider], + }); +} + +function BatteryLabel() { + const value = battery.bind("percent").as((p) => (p > 0 ? p / 100 : 0)); + const icon = battery + .bind("percent") + .as((p) => `battery-level-${Math.floor(p / 10) * 10}-symbolic`); + + return Widget.Box({ + class_name: "battery", + visible: battery.bind("available"), + children: [ + Widget.Icon({ icon }), + Widget.LevelBar({ + widthRequest: 140, + vpack: "center", + value, + }), + ], + }); +} + +function SysTray() { + const items = systemtray.bind("items").as((items) => + items.map((item) => + Widget.Button({ + child: Widget.Icon({ icon: item.bind("icon") }), + on_primary_click: (_, event) => item.activate(event), + on_secondary_click: (_, event) => item.openMenu(event), + tooltip_markup: item.bind("tooltip_markup"), + }), + ), + ); + + return Widget.Box({ + children: items, + }); +} + +// layout of the bar +function Left() { + return Widget.Box({ + spacing: 8, + children: [ + // Workspaces(), + ClientTitle(), + ], + }); +} + +function Center() { + return Widget.Box({ + spacing: 8, + children: [Media(), Notification()], + }); +} + +function Right() { + return Widget.Box({ + hpack: "end", + spacing: 8, + children: [Volume(), BatteryLabel(), Clock(), SysTray()], + }); +} + +function Bar(monitor = 0) { + return Widget.Window({ + name: `bar-${monitor}`, // name has to be unique + class_name: "bar", + monitor, + anchor: ["top", "left", "right"], + exclusivity: "exclusive", + child: Widget.CenterBox({ + start_widget: Left(), + center_widget: Center(), + end_widget: Right(), + }), + }); +} + +App.config({ + style: "./style.css", + windows: [ + Bar(), + + // you can call it, for each monitor + // Bar(0), + // Bar(1) + ], +}); + +export {}; diff --git a/home/programs/ags/src/style.css b/home/programs/ags/src/style.css new file mode 100644 index 0000000..9ac7355 --- /dev/null +++ b/home/programs/ags/src/style.css @@ -0,0 +1,40 @@ +window.bar { + background-color: @theme_bg_color; + color: @theme_fg_color; +} + +button { + min-width: 0; + padding-top: 0; + padding-bottom: 0; + background-color: transparent; +} + +button:active { + background-color: @theme_selected_bg_color; +} + +button:hover { + border-bottom: 3px solid @theme_fg_color; +} + +label { + font-weight: bold; +} + +.workspaces button.focused { + border-bottom: 3px solid @theme_selected_bg_color; +} + +.client-title { + color: @theme_selected_bg_color; +} + +.notification { + color: yellow; +} + +levelbar block, +highlight { + min-height: 10px; +} diff --git a/home/programs/ags/src/tsconfig.json b/home/programs/ags/src/tsconfig.json new file mode 100644 index 0000000..f03f2d1 --- /dev/null +++ b/home/programs/ags/src/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": [ + "ES2022" + ], + "allowJs": true, + "checkJs": true, + "strict": true, + "noImplicitAny": false, + "baseUrl": ".", + "typeRoots": [ + "./types" + ], + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/home/programs/ags/src/types b/home/programs/ags/src/types new file mode 120000 index 0000000..bef2e43 --- /dev/null +++ b/home/programs/ags/src/types @@ -0,0 +1 @@ +/home/nezia/.local/share/com.github.Aylur.ags/types \ No newline at end of file