aboutsummaryrefslogtreecommitdiff
path: root/.config/ags/widget/bar/buttons
diff options
context:
space:
mode:
Diffstat (limited to '.config/ags/widget/bar/buttons')
-rw-r--r--.config/ags/widget/bar/buttons/BatteryBar.ts94
-rw-r--r--.config/ags/widget/bar/buttons/ColorPicker.ts37
-rw-r--r--.config/ags/widget/bar/buttons/Date.ts15
-rw-r--r--.config/ags/widget/bar/buttons/Launcher.ts49
-rw-r--r--.config/ags/widget/bar/buttons/Media.ts92
-rw-r--r--.config/ags/widget/bar/buttons/Messages.ts16
-rw-r--r--.config/ags/widget/bar/buttons/PowerMenu.ts15
-rw-r--r--.config/ags/widget/bar/buttons/ScreenRecord.ts21
-rw-r--r--.config/ags/widget/bar/buttons/SysTray.ts39
-rw-r--r--.config/ags/widget/bar/buttons/SystemIndicators.ts107
-rw-r--r--.config/ags/widget/bar/buttons/Taskbar.ts90
-rw-r--r--.config/ags/widget/bar/buttons/Workspaces.ts66
12 files changed, 641 insertions, 0 deletions
diff --git a/.config/ags/widget/bar/buttons/BatteryBar.ts b/.config/ags/widget/bar/buttons/BatteryBar.ts
new file mode 100644
index 0000000..18de329
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/BatteryBar.ts
@@ -0,0 +1,94 @@
+import icons from "lib/icons"
+import options from "options"
+import PanelButton from "../PanelButton"
+
+const battery = await Service.import("battery")
+const { bar, percentage, blocks, width, low } = options.bar.battery
+
+const Indicator = () => Widget.Icon({
+ setup: self => self.hook(battery, () => {
+ self.icon = battery.charging || battery.charged
+ ? icons.battery.charging
+ : battery.icon_name
+ }),
+})
+
+const PercentLabel = () => Widget.Revealer({
+ transition: "slide_right",
+ click_through: true,
+ reveal_child: percentage.bind(),
+ child: Widget.Label({
+ label: battery.bind("percent").as(p => `${p}%`),
+ }),
+})
+
+const LevelBar = () => {
+ const level = Widget.LevelBar({
+ bar_mode: "discrete",
+ max_value: blocks.bind(),
+ visible: bar.bind().as(b => b !== "hidden"),
+ value: battery.bind("percent").as(p => (p / 100) * blocks.value),
+ })
+ const update = () => {
+ level.value = (battery.percent / 100) * blocks.value
+ level.css = `block { min-width: ${width.value / blocks.value}pt; }`
+ }
+ return level
+ .hook(width, update)
+ .hook(blocks, update)
+ .hook(bar, () => {
+ level.vpack = bar.value === "whole" ? "fill" : "center"
+ level.hpack = bar.value === "whole" ? "fill" : "center"
+ })
+}
+
+const WholeButton = () => Widget.Overlay({
+ vexpand: true,
+ child: LevelBar(),
+ class_name: "whole",
+ pass_through: true,
+ overlay: Widget.Box({
+ hpack: "center",
+ children: [
+ Widget.Icon({
+ icon: icons.battery.charging,
+ visible: Utils.merge([
+ battery.bind("charging"),
+ battery.bind("charged"),
+ ], (ing, ed) => ing || ed),
+ }),
+ Widget.Box({
+ hpack: "center",
+ vpack: "center",
+ child: PercentLabel(),
+ }),
+ ],
+ }),
+})
+
+const Regular = () => Widget.Box({
+ class_name: "regular",
+ children: [
+ Indicator(),
+ PercentLabel(),
+ LevelBar(),
+ ],
+})
+
+export default () => PanelButton({
+ class_name: "battery-bar",
+ hexpand: false,
+ on_clicked: () => { percentage.value = !percentage.value },
+ visible: battery.bind("available"),
+ child: Widget.Box({
+ expand: true,
+ visible: battery.bind("available"),
+ child: bar.bind().as(b => b === "whole" ? WholeButton() : Regular()),
+ }),
+ setup: self => self
+ .hook(bar, w => w.toggleClassName("bar-hidden", bar.value === "hidden"))
+ .hook(battery, w => {
+ w.toggleClassName("charging", battery.charging || battery.charged)
+ w.toggleClassName("low", battery.percent < low.value)
+ }),
+})
diff --git a/.config/ags/widget/bar/buttons/ColorPicker.ts b/.config/ags/widget/bar/buttons/ColorPicker.ts
new file mode 100644
index 0000000..5b1f3f6
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/ColorPicker.ts
@@ -0,0 +1,37 @@
+import PanelButton from "../PanelButton"
+import colorpicker from "service/colorpicker"
+import Gdk from "gi://Gdk"
+
+const css = (color: string) => `
+* {
+ background-color: ${color};
+ color: transparent;
+}
+*:hover {
+ color: white;
+ text-shadow: 2px 2px 3px rgba(0,0,0,.8);
+}`
+
+export default () => {
+ const menu = Widget.Menu({
+ class_name: "colorpicker",
+ children: colorpicker.bind("colors").as(c => c.map(color => Widget.MenuItem({
+ child: Widget.Label(color),
+ css: css(color),
+ on_activate: () => colorpicker.wlCopy(color),
+ }))),
+ })
+
+ return PanelButton({
+ class_name: "color-picker",
+ child: Widget.Icon("color-select-symbolic"),
+ tooltip_text: colorpicker.bind("colors").as(v => `${v.length} colors`),
+ on_clicked: colorpicker.pick,
+ on_secondary_click: self => {
+ if (colorpicker.colors.length === 0)
+ return
+
+ menu.popup_at_widget(self, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null)
+ },
+ })
+}
diff --git a/.config/ags/widget/bar/buttons/Date.ts b/.config/ags/widget/bar/buttons/Date.ts
new file mode 100644
index 0000000..44c2540
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Date.ts
@@ -0,0 +1,15 @@
+import { clock } from "lib/variables"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const { format, action } = options.bar.date
+const time = Utils.derive([clock, format], (c, f) => c.format(f) || "")
+
+export default () => PanelButton({
+ window: "datemenu",
+ on_clicked: action.bind(),
+ child: Widget.Label({
+ justification: "center",
+ label: time.bind(),
+ }),
+})
diff --git a/.config/ags/widget/bar/buttons/Launcher.ts b/.config/ags/widget/bar/buttons/Launcher.ts
new file mode 100644
index 0000000..f3fee6b
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Launcher.ts
@@ -0,0 +1,49 @@
+import PanelButton from "../PanelButton"
+import options from "options"
+import nix from "service/nix"
+
+const { icon, label, action } = options.bar.launcher
+
+function Spinner() {
+ const child = Widget.Icon({
+ icon: icon.icon.bind(),
+ class_name: Utils.merge([
+ icon.colored.bind(),
+ nix.bind("ready"),
+ ], (c, r) => `${c ? "colored" : ""} ${r ? "" : "spinning"}`),
+ css: `
+ @keyframes spin {
+ to { -gtk-icon-transform: rotate(1turn); }
+ }
+
+ image.spinning {
+ animation-name: spin;
+ animation-duration: 1s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ }
+ `,
+ })
+
+ return Widget.Revealer({
+ transition: "slide_left",
+ child,
+ reveal_child: Utils.merge([
+ icon.icon.bind(),
+ nix.bind("ready"),
+ ], (i, r) => Boolean(i || r)),
+ })
+}
+
+export default () => PanelButton({
+ window: "launcher",
+ on_clicked: action.bind(),
+ child: Widget.Box([
+ Spinner(),
+ Widget.Label({
+ class_name: label.colored.bind().as(c => c ? "colored" : ""),
+ visible: label.label.bind().as(v => !!v),
+ label: label.label.bind(),
+ }),
+ ]),
+})
diff --git a/.config/ags/widget/bar/buttons/Media.ts b/.config/ags/widget/bar/buttons/Media.ts
new file mode 100644
index 0000000..b3aab61
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Media.ts
@@ -0,0 +1,92 @@
+import { type MprisPlayer } from "types/service/mpris"
+import PanelButton from "../PanelButton"
+import options from "options"
+import icons from "lib/icons"
+import { icon } from "lib/utils"
+
+const mpris = await Service.import("mpris")
+const { length, direction, preferred, monochrome, format } = options.bar.media
+
+const getPlayer = (name = preferred.value) =>
+ mpris.getPlayer(name) || mpris.players[0] || null
+
+const Content = (player: MprisPlayer) => {
+ const revealer = Widget.Revealer({
+ click_through: true,
+ visible: length.bind().as(l => l > 0),
+ transition: direction.bind().as(d => `slide_${d}` as const),
+ setup: self => {
+ let current = ""
+ self.hook(player, () => {
+ if (current === player.track_title)
+ return
+
+ current = player.track_title
+ self.reveal_child = true
+ Utils.timeout(3000, () => {
+ !self.is_destroyed && (self.reveal_child = false)
+ })
+ })
+ },
+ child: Widget.Label({
+ truncate: "end",
+ max_width_chars: length.bind().as(n => n > 0 ? n : -1),
+ label: Utils.merge([
+ player.bind("track_title"),
+ player.bind("track_artists"),
+ format.bind(),
+ ], () => `${format}`
+ .replace("{title}", player.track_title)
+ .replace("{artists}", player.track_artists.join(", "))
+ .replace("{artist}", player.track_artists[0] || "")
+ .replace("{album}", player.track_album)
+ .replace("{name}", player.name)
+ .replace("{identity}", player.identity),
+ ),
+ }),
+ })
+
+ const playericon = Widget.Icon({
+ icon: Utils.merge([player.bind("entry"), monochrome.bind()], (entry => {
+ const name = `${entry}${monochrome.value ? "-symbolic" : ""}`
+ return icon(name, icons.fallback.audio)
+ })),
+ })
+
+ return Widget.Box({
+ attribute: { revealer },
+ children: direction.bind().as(d => d === "right"
+ ? [playericon, revealer] : [revealer, playericon]),
+ })
+}
+
+export default () => {
+ let player = getPlayer()
+
+ const btn = PanelButton({
+ class_name: "media",
+ child: Widget.Icon(icons.fallback.audio),
+ })
+
+ const update = () => {
+ player = getPlayer()
+ btn.visible = !!player
+
+ if (!player)
+ return
+
+ const content = Content(player)
+ const { revealer } = content.attribute
+ btn.child = content
+ btn.on_primary_click = () => { player.playPause() }
+ btn.on_secondary_click = () => { player.playPause() }
+ btn.on_scroll_up = () => { player.next() }
+ btn.on_scroll_down = () => { player.previous() }
+ btn.on_hover = () => { revealer.reveal_child = true }
+ btn.on_hover_lost = () => { revealer.reveal_child = false }
+ }
+
+ return btn
+ .hook(preferred, update)
+ .hook(mpris, update, "notify::players")
+}
diff --git a/.config/ags/widget/bar/buttons/Messages.ts b/.config/ags/widget/bar/buttons/Messages.ts
new file mode 100644
index 0000000..a8971e9
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Messages.ts
@@ -0,0 +1,16 @@
+import icons from "lib/icons"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const n = await Service.import("notifications")
+const notifs = n.bind("notifications")
+const action = options.bar.messages.action.bind()
+
+export default () => PanelButton({
+ class_name: "messages",
+ on_clicked: action,
+ visible: notifs.as(n => n.length > 0),
+ child: Widget.Box([
+ Widget.Icon(icons.notifications.message),
+ ]),
+})
diff --git a/.config/ags/widget/bar/buttons/PowerMenu.ts b/.config/ags/widget/bar/buttons/PowerMenu.ts
new file mode 100644
index 0000000..4432ade
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/PowerMenu.ts
@@ -0,0 +1,15 @@
+import icons from "lib/icons"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const { monochrome, action } = options.bar.powermenu
+
+export default () => PanelButton({
+ window: "powermenu",
+ on_clicked: action.bind(),
+ child: Widget.Icon(icons.powermenu.shutdown),
+ setup: self => self.hook(monochrome, () => {
+ self.toggleClassName("colored", !monochrome.value)
+ self.toggleClassName("box")
+ }),
+})
diff --git a/.config/ags/widget/bar/buttons/ScreenRecord.ts b/.config/ags/widget/bar/buttons/ScreenRecord.ts
new file mode 100644
index 0000000..1d6eb36
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/ScreenRecord.ts
@@ -0,0 +1,21 @@
+import PanelButton from "../PanelButton"
+import screenrecord from "service/screenrecord"
+import icons from "lib/icons"
+
+export default () => PanelButton({
+ class_name: "recorder",
+ on_clicked: () => screenrecord.stop(),
+ visible: screenrecord.bind("recording"),
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.recorder.recording),
+ Widget.Label({
+ label: screenrecord.bind("timer").as(time => {
+ const sec = time % 60
+ const min = Math.floor(time / 60)
+ return `${min}:${sec < 10 ? "0" + sec : sec}`
+ }),
+ }),
+ ],
+ }),
+})
diff --git a/.config/ags/widget/bar/buttons/SysTray.ts b/.config/ags/widget/bar/buttons/SysTray.ts
new file mode 100644
index 0000000..9f569d1
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/SysTray.ts
@@ -0,0 +1,39 @@
+import { type TrayItem } from "types/service/systemtray"
+import PanelButton from "../PanelButton"
+import Gdk from "gi://Gdk"
+import options from "options"
+
+const systemtray = await Service.import("systemtray")
+const { ignore } = options.bar.systray
+
+const SysTrayItem = (item: TrayItem) => PanelButton({
+ class_name: "tray-item",
+ child: Widget.Icon({ icon: item.bind("icon") }),
+ tooltip_markup: item.bind("tooltip_markup"),
+ setup: self => {
+ const { menu } = item
+ if (!menu)
+ return
+
+ const id = menu.connect("popped-up", () => {
+ self.toggleClassName("active")
+ menu.connect("notify::visible", () => {
+ self.toggleClassName("active", menu.visible)
+ })
+ menu.disconnect(id!)
+ })
+
+ self.connect("destroy", () => menu.disconnect(id))
+ },
+
+ on_primary_click: btn => item.menu?.popup_at_widget(
+ btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null),
+
+ on_secondary_click: btn => item.menu?.popup_at_widget(
+ btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null),
+})
+
+export default () => Widget.Box()
+ .bind("children", systemtray, "items", i => i
+ .filter(({ id }) => !ignore.value.includes(id))
+ .map(SysTrayItem))
diff --git a/.config/ags/widget/bar/buttons/SystemIndicators.ts b/.config/ags/widget/bar/buttons/SystemIndicators.ts
new file mode 100644
index 0000000..cc98548
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/SystemIndicators.ts
@@ -0,0 +1,107 @@
+import PanelButton from '../PanelButton';
+import icons from 'lib/icons';
+import asusctl from 'service/asusctl';
+
+const notifications = await Service.import('notifications');
+const bluetooth = await Service.import('bluetooth');
+const audio = await Service.import('audio');
+const network = await Service.import('network');
+const powerprof = await Service.import('powerprofiles');
+
+const ProfileIndicator = () => {
+ const visible = asusctl.available ? asusctl.bind('profile').as(p => p !== 'Balanced') : powerprof.bind('active_profile').as(p => p !== 'balanced');
+
+ const icon = asusctl.available ? asusctl.bind('profile').as(p => icons.asusctl.profile[p]) : powerprof.bind('active_profile').as(p => icons.powerprofile[p]);
+
+ return Widget.Icon({ visible, icon });
+};
+
+const ModeIndicator = () => {
+ if (!asusctl.available) {
+ return Widget.Icon({
+ setup(self) {
+ Utils.idle(() => (self.visible = false));
+ },
+ });
+ }
+
+ return Widget.Icon({
+ visible: asusctl.bind('mode').as(m => m !== 'Hybrid'),
+ icon: asusctl.bind('mode').as(m => icons.asusctl.mode[m]),
+ });
+};
+
+const MicrophoneIndicator = () =>
+ Widget.Icon()
+ .hook(audio, self => (self.visible = audio.recorders.length > 0 || audio.microphone.is_muted || false))
+ .hook(audio.microphone, self => {
+ const vol = audio.microphone.is_muted ? 0 : audio.microphone.volume;
+ const { muted, low, medium, high } = icons.audio.mic;
+ const cons = [
+ [67, high],
+ [34, medium],
+ [1, low],
+ [0, muted],
+ ] as const;
+ self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || '';
+ });
+
+const DNDIndicator = () =>
+ Widget.Icon({
+ visible: notifications.bind('dnd'),
+ icon: icons.notifications.silent,
+ });
+
+const BluetoothIndicator = () =>
+ Widget.Overlay({
+ class_name: 'bluetooth',
+ passThrough: true,
+ child: Widget.Icon({
+ icon: icons.bluetooth.enabled,
+ visible: bluetooth.bind('enabled'),
+ }),
+ overlay: Widget.Label({
+ hpack: 'end',
+ vpack: 'start',
+ label: bluetooth.bind('connected_devices').as(c => `${c.length}`),
+ visible: bluetooth.bind('connected_devices').as(c => c.length > 0),
+ }),
+ });
+
+const NetworkIndicator = () =>
+ Widget.Icon().hook(network, self => {
+ const icon = network[network.primary || 'wifi']?.icon_name;
+ self.icon = icon || '';
+ self.visible = !!icon;
+ });
+
+const AudioIndicator = () =>
+ Widget.Icon().hook(audio.speaker, self => {
+ const vol = audio.speaker.is_muted ? 0 : audio.speaker.volume;
+ const { muted, low, medium, high, overamplified } = icons.audio.volume;
+ const cons = [
+ [101, overamplified],
+ [67, high],
+ [34, medium],
+ [1, low],
+ [0, muted],
+ ] as const;
+ self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || '';
+ });
+
+export default () =>
+ PanelButton({
+ window: 'quicksettings',
+ on_clicked: () => App.toggleWindow('quicksettings'),
+ on_scroll_up: () => (audio.speaker.volume += 0.02),
+ on_scroll_down: () => (audio.speaker.volume -= 0.02),
+ child: Widget.Box([
+ //ProfileIndicator(),
+ ModeIndicator(),
+ DNDIndicator(),
+ BluetoothIndicator(),
+ MicrophoneIndicator(),
+ AudioIndicator(),
+ NetworkIndicator(),
+ ]),
+ });
diff --git a/.config/ags/widget/bar/buttons/Taskbar.ts b/.config/ags/widget/bar/buttons/Taskbar.ts
new file mode 100644
index 0000000..b9c65fa
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Taskbar.ts
@@ -0,0 +1,90 @@
+import { launchApp, icon } from "lib/utils"
+import icons from "lib/icons"
+import options from "options"
+import PanelButton from "../PanelButton"
+
+const hyprland = await Service.import("hyprland")
+const apps = await Service.import("applications")
+const { monochrome, exclusive, iconSize } = options.bar.taskbar
+const { position } = options.bar
+
+const focus = (address: string) => hyprland.messageAsync(
+ `dispatch focuswindow address:${address}`)
+
+const DummyItem = (address: string) => Widget.Box({
+ attribute: { address },
+ visible: false,
+})
+
+const AppItem = (address: string) => {
+ const client = hyprland.getClient(address)
+ if (!client || client.class === "")
+ return DummyItem(address)
+
+ const app = apps.list.find(app => app.match(client.class))
+
+ const btn = PanelButton({
+ class_name: "panel-button",
+ tooltip_text: Utils.watch(client.title, hyprland, () =>
+ hyprland.getClient(address)?.title || "",
+ ),
+ on_primary_click: () => focus(address),
+ on_middle_click: () => app && launchApp(app),
+ child: Widget.Icon({
+ size: iconSize.bind(),
+ icon: monochrome.bind().as(m => icon(
+ (app?.icon_name || client.class) + (m ? "-symbolic" : ""),
+ icons.fallback.executable + (m ? "-symbolic" : ""),
+ )),
+ }),
+ })
+
+ return Widget.Box(
+ {
+ attribute: { address },
+ visible: Utils.watch(true, [exclusive, hyprland], () => {
+ return exclusive.value
+ ? hyprland.active.workspace.id === client.workspace.id
+ : true
+ }),
+ },
+ Widget.Overlay({
+ child: btn,
+ pass_through: true,
+ overlay: Widget.Box({
+ className: "indicator",
+ hpack: "center",
+ vpack: position.bind().as(p => p === "top" ? "start" : "end"),
+ setup: w => w.hook(hyprland, () => {
+ w.toggleClassName("active", hyprland.active.client.address === address)
+ }),
+ }),
+ }),
+ )
+}
+
+function sortItems<T extends { attribute: { address: string } }>(arr: T[]) {
+ return arr.sort(({ attribute: a }, { attribute: b }) => {
+ const aclient = hyprland.getClient(a.address)!
+ const bclient = hyprland.getClient(b.address)!
+ return aclient.workspace.id - bclient.workspace.id
+ })
+}
+
+export default () => Widget.Box({
+ class_name: "taskbar",
+ children: sortItems(hyprland.clients.map(c => AppItem(c.address))),
+ setup: w => w
+ .hook(hyprland, (w, address?: string) => {
+ if (typeof address === "string")
+ w.children = w.children.filter(ch => ch.attribute.address !== address)
+ }, "client-removed")
+ .hook(hyprland, (w, address?: string) => {
+ if (typeof address === "string")
+ w.children = sortItems([...w.children, AppItem(address)])
+ }, "client-added")
+ .hook(hyprland, (w, event?: string) => {
+ if (event === "movewindow")
+ w.children = sortItems(w.children)
+ }, "event"),
+})
diff --git a/.config/ags/widget/bar/buttons/Workspaces.ts b/.config/ags/widget/bar/buttons/Workspaces.ts
new file mode 100644
index 0000000..a59f61b
--- /dev/null
+++ b/.config/ags/widget/bar/buttons/Workspaces.ts
@@ -0,0 +1,66 @@
+import PanelButton from '../PanelButton';
+import options from 'options';
+import { sh, range } from 'lib/utils';
+
+const hyprland = await Service.import('hyprland');
+const { workspaces } = options.bar.workspaces;
+
+const dispatch = arg => {
+ sh(`hyprctl dispatch workspace ${arg}`);
+};
+
+const Workspaces = ws =>
+ Widget.Box({
+ children: range(ws || 20).map(i =>
+ Widget.Label({
+ attribute: i,
+ vpack: 'center',
+ label: `${i}`,
+ setup: self => {
+ const updateState = () => {
+ const monitorData = JSON.parse(hyprland.message('j/monitors'));
+ const activeWorkspaceId = monitorData[0]?.activeWorkspace?.id;
+ const workspaceData = hyprland.getWorkspace(i);
+
+ if (activeWorkspaceId !== undefined) {
+ self.toggleClassName('active', activeWorkspaceId === i);
+ }
+ self.toggleClassName('occupied', (workspaceData?.windows || 0) > 0);
+ };
+
+ // Hook to Hyprland for updates
+ self.hook(hyprland, updateState);
+
+ // Initial update
+ updateState();
+ },
+ }),
+ ),
+ setup: box => {
+ box.hook(hyprland, () => {
+ const monitorData = JSON.parse(hyprland.message('j/monitors'));
+ const activeWorkspaceId = monitorData[0]?.activeWorkspace?.id;
+
+ if (activeWorkspaceId !== undefined) {
+ for (const btn of box.children) {
+ const workspaceId = btn.attribute;
+ btn.toggleClassName('active', workspaceId === activeWorkspaceId);
+
+ if (ws === 0) {
+ btn.visible = hyprland.workspaces.some(workspace => workspace.id === workspaceId);
+ }
+ }
+ }
+ });
+ },
+ });
+
+export default () =>
+ PanelButton({
+ window: 'overview',
+ class_name: 'workspaces',
+ on_scroll_up: () => dispatch('m+1'),
+ on_scroll_down: () => dispatch('m-1'),
+ on_clicked: () => App.toggleWindow('overview'),
+ child: workspaces.bind().as(Workspaces),
+ });