summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-04-02 15:06:27 +1100
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-04-02 15:06:27 +1100
commite9eba2e2914e6c9aa01dd3d6267c969bcba3bb2f (patch)
tree9ce2fad274d4978d9531b5c57c2ceb4d67c219b5 /src/modules
parentsidebar: add date time to time pane (diff)
downloadcaelestia-shell-e9eba2e2914e6c9aa01dd3d6267c969bcba3bb2f.tar.gz
caelestia-shell-e9eba2e2914e6c9aa01dd3d6267c969bcba3bb2f.tar.bz2
caelestia-shell-e9eba2e2914e6c9aa01dd3d6267c969bcba3bb2f.zip
cleanup: remove popdown stuff
Popdowns were replaced with the sidebar
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/popdowns/bluetoothdevices.tsx98
-rw-r--r--src/modules/popdowns/index.tsx19
-rw-r--r--src/modules/popdowns/media.tsx152
-rw-r--r--src/modules/popdowns/networks.tsx101
-rw-r--r--src/modules/popdowns/notifications.tsx65
-rw-r--r--src/modules/popdowns/sideleft.tsx205
-rw-r--r--src/modules/popdowns/sideright.tsx146
-rw-r--r--src/modules/popdowns/updates.tsx145
8 files changed, 0 insertions, 931 deletions
diff --git a/src/modules/popdowns/bluetoothdevices.tsx b/src/modules/popdowns/bluetoothdevices.tsx
deleted file mode 100644
index c71d981..0000000
--- a/src/modules/popdowns/bluetoothdevices.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { bind, Variable } from "astal";
-import { Astal, Gtk } from "astal/gtk3";
-import AstalBluetooth from "gi://AstalBluetooth";
-import PopdownWindow from "../../widgets/popdownwindow";
-
-const BluetoothDevice = (device: AstalBluetooth.Device) => (
- <box className={bind(device, "connected").as(c => `device ${c ? "active" : ""}`)}>
- <icon
- className="icon"
- icon={bind(device, "icon").as(i =>
- Astal.Icon.lookup_icon(`${i}-symbolic`) ? `${i}-symbolic` : "bluetooth-symbolic"
- )}
- />
- <label
- truncate
- xalign={0}
- setup={self => {
- const update = () =>
- (self.label = `${device.alias}${
- device.connected || device.paired ? ` (${device.connected ? "Connected" : "Paired"})` : ""
- }`);
- self.hook(device, "notify::alias", update);
- self.hook(device, "notify::connected", update);
- self.hook(device, "notify::paired", update);
- update();
- }}
- />
- <box hexpand />
- <button
- cursor="pointer"
- onClicked={self => {
- if (device.connected)
- device.disconnect_device((_, res) => {
- self.sensitive = true;
- device.disconnect_device_finish(res);
- });
- else
- device.connect_device((_, res) => {
- self.sensitive = true;
- device.connect_device_finish(res);
- });
- self.sensitive = false;
- }}
- label={bind(device, "connected").as(c => (c ? "Disconnect" : "Connect"))}
- />
- <button
- cursor="pointer"
- onClicked={() => AstalBluetooth.get_default().adapter.remove_device(device)}
- label="Remove"
- />
- </box>
-);
-
-const List = () => (
- <box vertical valign={Gtk.Align.START} className="list">
- {bind(AstalBluetooth.get_default(), "devices").as(d => d.map(BluetoothDevice))}
- </box>
-);
-
-export default () => {
- const bluetooth = AstalBluetooth.get_default();
- const label = Variable("");
-
- const update = () => {
- const devices = bluetooth.get_devices();
- const connected = devices.filter(d => d.connected).length;
- label.set(`${connected} connected device${connected === 1 ? "" : "s"} (${devices.length} available)`);
- };
- bluetooth.get_devices().forEach(d => d.connect("notify::connected", update));
- bluetooth.connect("device-added", (_, device) => device.connect("notify::connected", update));
- bluetooth.connect("notify::devices", update);
- update();
-
- return (
- <PopdownWindow
- name="bluetooth-devices"
- count={bind(bluetooth, "devices").as(n => n.length)}
- countLabel={bind(label)}
- headerButtons={[
- {
- label: bind(bluetooth, "isPowered").as(p => (p ? "Disable" : "Enable")),
- onClicked: () => bluetooth.toggle(),
- },
- {
- label: "Discovery",
- onClicked: () => {
- if (bluetooth.adapter.discovering) bluetooth.adapter.start_discovery();
- else bluetooth.adapter.stop_discovery();
- },
- enabled: bind(bluetooth.adapter, "discovering"),
- },
- ]}
- emptyIcon="bluetooth_disabled"
- emptyLabel="No Bluetooth devices"
- list={<List />}
- />
- );
-};
diff --git a/src/modules/popdowns/index.tsx b/src/modules/popdowns/index.tsx
deleted file mode 100644
index fb9abf7..0000000
--- a/src/modules/popdowns/index.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import BluetoothDevices from "./bluetoothdevices";
-import Media from "./media";
-import Networks from "./networks";
-import Notifications from "./notifications";
-import SideLeft from "./sideleft";
-import SideRight from "./sideright";
-import Updates from "./updates";
-
-export default () => {
- <Notifications />;
- <Updates />;
- <BluetoothDevices />;
- <Networks />;
- <Media />;
- <SideRight />;
- <SideLeft />;
-
- return null;
-};
diff --git a/src/modules/popdowns/media.tsx b/src/modules/popdowns/media.tsx
deleted file mode 100644
index 8757cd2..0000000
--- a/src/modules/popdowns/media.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import Players from "@/services/players";
-import { isRealPlayer } from "@/utils/mpris";
-import PopupWindow from "@/widgets/popupwindow";
-import { bind, Variable } from "astal";
-import { Astal, Gtk } from "astal/gtk3";
-import AstalMpris from "gi://AstalMpris";
-
-const shuffleToIcon = (s: AstalMpris.Shuffle) =>
- `caelestia-${s === AstalMpris.Shuffle.ON ? "shuffle" : "consecutive"}-symbolic`;
-
-const playbackToIcon = (s: AstalMpris.PlaybackStatus) =>
- `caelestia-${s === AstalMpris.PlaybackStatus.PLAYING ? "pause" : "play"}-symbolic`;
-
-const loopToIcon = (s: AstalMpris.Loop) => {
- if (s === AstalMpris.Loop.PLAYLIST) return "caelestia-repeat-symbolic";
- if (s === AstalMpris.Loop.TRACK) return "caelestia-repeat-one-symbolic";
- return "caelestia-no-repeat-symbolic";
-};
-
-const lengthStr = (length: number) =>
- `${Math.floor(length / 60)}:${Math.floor(length % 60)
- .toString()
- .padStart(2, "0")}`;
-
-const Player = ({ player }: { player: AstalMpris.Player }) => {
- const background = (
- <box className="background" css={bind(player, "coverArt").as(u => `background-image: url("${u}");`)} />
- );
- return (
- <overlay
- overlays={[
- <box className="overlay" />,
- <box
- vertical
- className="player"
- setup={self =>
- self.connect("size-allocate", () =>
- background.set_size_request(self.get_preferred_width()[1], self.get_preferred_height()[1])
- )
- }
- >
- <label className="title" label={bind(player, "title").as(t => t ?? "")} />
- <label className="artist" label={bind(player, "artist").as(a => a ?? "")} />
- <label className="album" label={bind(player, "album").as(a => a ?? "")} />
- <box className="controls" halign={Gtk.Align.CENTER}>
- <button
- cursor="pointer"
- onClicked={() => player.shuffle()}
- sensitive={bind(player, "canControl")}
- >
- <icon icon={bind(player, "shuffleStatus").as(shuffleToIcon)} />
- </button>
- <button
- cursor="pointer"
- onClicked={() => player.previous()}
- sensitive={bind(player, "canGoPrevious")}
- >
- <icon icon="caelestia-skip-previous-symbolic" />
- </button>
- <button
- cursor="pointer"
- onClicked={() => player.play_pause()}
- sensitive={bind(player, "canControl")}
- >
- <icon icon={bind(player, "playbackStatus").as(playbackToIcon)} />
- </button>
- <button cursor="pointer" onClicked={() => player.next()} sensitive={bind(player, "canGoNext")}>
- <icon icon="caelestia-skip-next-symbolic" />
- </button>
- <button cursor="pointer" onClicked={() => player.loop()} sensitive={bind(player, "canControl")}>
- <icon icon={bind(player, "loopStatus").as(loopToIcon)} />
- </button>
- </box>
- <slider
- hexpand
- onDragged={self => player.set_position(self.value * player.length)}
- setup={self => {
- const update = () => {
- self.set_tooltip_text(`${lengthStr(player.position)}/${lengthStr(player.length)}`);
- self.set_value(player.position / player.length);
- };
- self.hook(player, "notify::position", update);
- self.hook(player, "notify::length", update);
- update();
- }}
- />
- </box>,
- ]}
- >
- {background}
- </overlay>
- );
-};
-
-export default () => {
- const shown = Variable("none");
- return (
- <PopupWindow name="media">
- <box className="media">
- <eventbox
- onClick={(_, event) => {
- if (event.button === Astal.MouseButton.SECONDARY) {
- const current = Players.get_default().list.find(p => p.busName === shown.get());
- if (current) Players.get_default().makeCurrent(current);
- }
- }}
- onScroll={(_, event) => {
- const players = AstalMpris.get_default().players.filter(isRealPlayer);
- const idx = players.findIndex(p => p.busName === shown.get());
- if (idx === -1) return;
- if (event.delta_y < 0) {
- if (idx > 0) shown.set(players[idx - 1].busName);
- } else if (idx < players.length - 1) shown.set(players[idx + 1].busName);
- }}
- >
- <stack
- expand
- transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
- transitionDuration={150}
- shown={bind(shown)}
- setup={self => {
- const players = new Map<string, JSX.Element>();
-
- const addPlayer = (player: AstalMpris.Player) => {
- if (!isRealPlayer(player) || players.has(player.busName)) return;
- const widget = <Player player={player} />;
- self.add_named(widget, player.busName);
- if (players.size === 0) shown.set(player.busName);
- players.set(player.busName, widget);
- };
-
- for (const player of Players.get_default().list) addPlayer(player);
-
- self.hook(AstalMpris.get_default(), "player-added", (_, player) => addPlayer(player));
- self.hook(AstalMpris.get_default(), "player-closed", (_, player) => {
- players.get(player.busName)?.destroy();
- players.delete(player.busName);
- if (shown.get() === player.busName)
- shown.set(AstalMpris.get_default().players.find(isRealPlayer)?.busName ?? "none");
- });
- }}
- >
- <box vertical valign={Gtk.Align.CENTER} name="none">
- <label className="icon" label="music_note" />
- <label label="No media playing" />
- </box>
- </stack>
- </eventbox>
- </box>
- </PopupWindow>
- );
-};
diff --git a/src/modules/popdowns/networks.tsx b/src/modules/popdowns/networks.tsx
deleted file mode 100644
index 726f1c9..0000000
--- a/src/modules/popdowns/networks.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import { bind, execAsync, Variable } from "astal";
-import { Gtk } from "astal/gtk3";
-import AstalNetwork from "gi://AstalNetwork";
-import PopdownWindow from "../../widgets/popdownwindow";
-
-const Network = (accessPoint: AstalNetwork.AccessPoint) => (
- <box
- className={bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(
- a => `network ${a === accessPoint ? "active" : ""}`
- )}
- >
- <icon className="icon" icon={bind(accessPoint, "iconName")} />
- <label
- truncate
- xalign={0}
- setup={self => {
- const update = () =>
- (self.label = `${accessPoint.ssid}${` (${accessPoint.frequency > 5000 ? 5 : 2.4}GHz | ${
- accessPoint.strength
- }/100)`}`);
- self.hook(accessPoint, "notify::ssid", update);
- self.hook(accessPoint, "notify::frequency", update);
- self.hook(accessPoint, "notify::strength", update);
- update();
- }}
- />
- <box hexpand />
- <button
- cursor="pointer"
- onClicked={self => {
- const cmd =
- AstalNetwork.get_default().wifi.activeAccessPoint === accessPoint ? "c down id" : "d wifi connect";
- execAsync(`nmcli ${cmd} '${accessPoint.ssid}'`)
- .then(() => (self.sensitive = true))
- .catch(console.error);
- self.sensitive = false;
- }}
- label={bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(a =>
- a === accessPoint ? "Disconnect" : "Connect"
- )}
- />
- <button
- cursor="pointer"
- onClicked={() => execAsync(`nmcli c delete id '${accessPoint.ssid}'`).catch(() => {})}
- label="Forget"
- />
- </box>
-);
-
-const List = () => {
- const { wifi } = AstalNetwork.get_default();
- const children = Variable.derive([bind(wifi, "accessPoints"), bind(wifi, "activeAccessPoint")], (aps, ac) =>
- aps
- .filter(a => a.ssid)
- .sort((a, b) => (a === ac ? -1 : b.strength - a.strength))
- .map(Network)
- );
-
- return (
- <box vertical valign={Gtk.Align.START} className="list" onDestroy={() => children.drop()}>
- {bind(children)}
- </box>
- );
-};
-
-export default () => {
- const network = AstalNetwork.get_default();
- const label = Variable("");
-
- const update = () => {
- if (network.primary === AstalNetwork.Primary.WIFI) label.set(network.wifi.ssid ?? "Disconnected");
- else if (network.primary === AstalNetwork.Primary.WIRED) label.set(`Ethernet (${network.wired.speed})`);
- else label.set("No Wifi");
- };
- network.connect("notify::primary", update);
- network.get_wifi()?.connect("notify::ssid", update);
- network.get_wired()?.connect("notify::speed", update);
- update();
-
- return (
- <PopdownWindow
- name="networks"
- count={bind(network.wifi, "accessPoints").as(a => a.length)}
- countLabel={bind(label)}
- headerButtons={[
- {
- label: bind(network.wifi, "enabled").as(e => (e ? "Disable" : "Enable")),
- onClicked: () => (network.wifi.enabled = !network.wifi.enabled),
- },
- {
- label: bind(network.wifi, "scanning").as(s => (s ? "Scanning" : "Scan")),
- onClicked: () => network.wifi.scan(),
- enabled: bind(network.wifi, "scanning"),
- },
- ]}
- emptyIcon="wifi_off"
- emptyLabel={bind(network.wifi, "enabled").as(p => (p ? "No available networks" : "Wifi is off"))}
- list={<List />}
- />
- );
-};
diff --git a/src/modules/popdowns/notifications.tsx b/src/modules/popdowns/notifications.tsx
deleted file mode 100644
index 4ab0095..0000000
--- a/src/modules/popdowns/notifications.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { bind } from "astal";
-import { Astal, Gtk } from "astal/gtk3";
-import AstalNotifd from "gi://AstalNotifd";
-import Notification from "../../widgets/notification";
-import PopdownWindow from "../../widgets/popdownwindow";
-
-const List = () => (
- <box
- vertical
- valign={Gtk.Align.START}
- className="list"
- setup={self => {
- const notifd = AstalNotifd.get_default();
- const map = new Map<number, Notification>();
-
- const addNotification = (notification: AstalNotifd.Notification) => {
- const notif = (<Notification notification={notification} />) as Notification;
- notif.connect("destroy", () => map.get(notification.id) === notif && map.delete(notification.id));
- map.get(notification.id)?.destroyWithAnims();
- map.set(notification.id, notif);
-
- self.pack_end(
- <eventbox
- // Dismiss on middle click
- onClick={(_, event) => event.button === Astal.MouseButton.MIDDLE && notification.dismiss()}
- >
- {notif}
- </eventbox>,
- false,
- false,
- 0
- );
- };
-
- notifd
- .get_notifications()
- .sort((a, b) => a.time - b.time)
- .forEach(addNotification);
-
- self.hook(notifd, "notified", (_, id) => addNotification(notifd.get_notification(id)));
- self.hook(notifd, "resolved", (_, id) => map.get(id)?.destroyWithAnims());
- }}
- />
-);
-
-export default () => (
- <PopdownWindow
- name="notifications"
- count={bind(AstalNotifd.get_default(), "notifications").as(n => n.length)}
- headerButtons={[
- {
- label: "Silence",
- onClicked: () => (AstalNotifd.get_default().dontDisturb = !AstalNotifd.get_default().dontDisturb),
- enabled: bind(AstalNotifd.get_default(), "dontDisturb"),
- },
- {
- label: "Clear",
- onClicked: () => AstalNotifd.get_default().notifications.forEach(n => n.dismiss()),
- },
- ]}
- emptyIcon="notifications_active"
- emptyLabel="All caught up!"
- list={<List />}
- />
-);
diff --git a/src/modules/popdowns/sideleft.tsx b/src/modules/popdowns/sideleft.tsx
deleted file mode 100644
index 753026e..0000000
--- a/src/modules/popdowns/sideleft.tsx
+++ /dev/null
@@ -1,205 +0,0 @@
-import Cpu from "@/services/cpu";
-import Gpu from "@/services/gpu";
-import Memory from "@/services/memory";
-import Storage from "@/services/storage";
-import { osId } from "@/utils/system";
-import PopupWindow from "@/widgets/popupwindow";
-import { bind, execAsync, GLib, type Binding } from "astal";
-import { App, Gtk, type Widget } from "astal/gtk3";
-import type cairo from "cairo";
-import { sideleft } from "config";
-
-const fmt = (bytes: number, pow: number) => +(bytes / 1024 ** pow).toFixed(2);
-const format = ({ total, used }: { total: number; used: number }) => {
- if (total >= 1024 ** 4) return `${fmt(used, 4)}/${fmt(total, 4)} TiB`;
- if (total >= 1024 ** 3) return `${fmt(used, 3)}/${fmt(total, 3)} GiB`;
- if (total >= 1024 ** 2) return `${fmt(used, 2)}/${fmt(total, 2)} MiB`;
- if (total >= 1024) return `${fmt(used, 1)}/${fmt(total, 1)} KiB`;
- return `${used}/${total} B`;
-};
-
-const User = () => (
- <box className="user">
- <box
- valign={Gtk.Align.CENTER}
- className="face"
- css={`
- background-image: url("${HOME}/.face");
- `}
- >
- {!GLib.file_test(HOME + "/.face", GLib.FileTest.EXISTS) && (
- <label
- setup={self => {
- const name = GLib.get_real_name();
- if (name !== "Unknown")
- self.label = name
- .split(" ")
- .map(s => s[0].toUpperCase())
- .join("");
- else self.label = "";
- }}
- />
- )}
- </box>
- <box vertical hexpand valign={Gtk.Align.CENTER} className="details">
- <label xalign={0} className="name" label={GLib.get_user_name()} />
- <label xalign={0} label={(GLib.getenv("XDG_CURRENT_DESKTOP") ?? osId).toUpperCase()} />
- </box>
- <button
- valign={Gtk.Align.CENTER}
- className="power"
- cursor="pointer"
- onClicked={() => App.toggle_window("session")}
- label="power_settings_new"
- />
- </box>
-);
-
-const QuickLaunch = () => (
- <box className="quick-launch">
- <box vertical>
- <box>{/* <button */}</box> // TODO
- </box>
- </box>
-);
-
-const Location = ({ label, num }: { label: Binding<string>; num: number }) => (
- <button
- className={"loc" + num}
- cursor="pointer"
- onClicked={self => {
- self.get_toplevel().hide();
- const dir = label.get().split(" ").at(-1);
- execAsync(`xdg-open ${HOME}/${dir?.toLowerCase() === "home" ? "" : `${dir}/`}`).catch(console.error);
- }}
- >
- <label xalign={0} label={label} />
- </button>
-);
-
-const Locations = () => (
- <box className="locations">
- <box vertical>
- <Location label={bind(sideleft.directories.left.top)} num={1} />
- <Location label={bind(sideleft.directories.left.middle)} num={2} />
- <Location label={bind(sideleft.directories.left.bottom)} num={3} />
- </box>
- <box vertical>
- <Location label={bind(sideleft.directories.right.top)} num={4} />
- <Location label={bind(sideleft.directories.right.middle)} num={5} />
- <Location label={bind(sideleft.directories.right.bottom)} num={6} />
- </box>
- </box>
-);
-
-const Slider = ({ value }: { value: Binding<number> }) => (
- <drawingarea
- hexpand
- valign={Gtk.Align.CENTER}
- className="slider"
- css={bind(value).as(v => `font-size: ${v}px;`)}
- setup={self => {
- const halfPi = Math.PI / 2;
-
- const styleContext = self.get_style_context();
- self.set_size_request(-1, styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number);
-
- self.connect("draw", (_, cr: cairo.Context) => {
- const styleContext = self.get_style_context();
-
- const width = self.get_allocated_width();
- const height = styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number;
- self.set_size_request(-1, height);
-
- const progressValue = styleContext.get_property("font-size", Gtk.StateFlags.NORMAL) as number;
- let radius = styleContext.get_property("border-radius", Gtk.StateFlags.NORMAL) as number;
-
- const bg = styleContext.get_background_color(Gtk.StateFlags.NORMAL);
- cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
-
- // Background
- cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
- cr.arc(width - radius, radius, radius, -halfPi, 0); // Top right
- cr.arc(width - radius, height - radius, radius, 0, halfPi); // Bottom right
- cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
- cr.fill();
-
- // Flatten when near 0
- radius = Math.min(radius, Math.min(width * progressValue, height) / 2);
-
- const progressPosition = width * progressValue - radius;
- const fg = styleContext.get_color(Gtk.StateFlags.NORMAL);
- cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
-
- // Foreground
- cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
- cr.arc(progressPosition, radius, radius, -halfPi, 0); // Top right
- cr.arc(progressPosition, height - radius, radius, 0, halfPi); // Bottom right
- cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
- cr.fill();
- });
- }}
- />
-);
-
-const Resource = ({
- icon,
- name,
- value,
- labelSetup,
-}: {
- icon: string;
- name: string;
- value: Binding<number>;
- labelSetup?: (self: Widget.Label) => void;
-}) => (
- <box vertical className={`resource ${name}`}>
- <box className="inner">
- <label label={icon} />
- <Slider value={value.as(v => v / 100)} />
- </box>
- <label halign={Gtk.Align.END} label={labelSetup ? "" : value.as(v => `${+v.toFixed(2)}%`)} setup={labelSetup} />
- </box>
-);
-
-const HwResources = () => (
- <box vertical className="hw-resources">
- {Gpu.get_default().available && <Resource icon="󰢮" name="gpu" value={bind(Gpu.get_default(), "usage")} />}
- <Resource icon="" name="cpu" value={bind(Cpu.get_default(), "usage")} />
- <Resource
- icon=""
- name="memory"
- value={bind(Memory.get_default(), "usage")}
- labelSetup={self => {
- const mem = Memory.get_default();
- const update = () => (self.label = format(mem));
- self.hook(mem, "notify::used", update);
- self.hook(mem, "notify::total", update);
- update();
- }}
- />
- <Resource
- icon="󰋊"
- name="storage"
- value={bind(Storage.get_default(), "usage")}
- labelSetup={self => {
- const storage = Storage.get_default();
- const update = () => (self.label = format(storage));
- self.hook(storage, "notify::used", update);
- self.hook(storage, "notify::total", update);
- update();
- }}
- />
- </box>
-);
-
-export default () => (
- <PopupWindow name="sideleft">
- <box vertical className="sideleft">
- <User />
- {/* <QuickLaunch /> */}
- <Locations />
- <HwResources />
- </box>
- </PopupWindow>
-);
diff --git a/src/modules/popdowns/sideright.tsx b/src/modules/popdowns/sideright.tsx
deleted file mode 100644
index 5e873b3..0000000
--- a/src/modules/popdowns/sideright.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-import SWeather, { type WeatherData } from "@/services/weather";
-import { ellipsize } from "@/utils/strings";
-import { bindCurrentTime } from "@/utils/system";
-import { setupCustomTooltip, Calendar as WCal } from "@/utils/widgets";
-import PopupWindow from "@/widgets/popupwindow";
-import { bind, timeout } from "astal";
-import { Astal, Gtk, type Gdk } from "astal/gtk3";
-
-const getHoursFromUpdate = (data: WeatherData, hours: number) => {
- const updateTime = data.location.localtime_epoch;
- const updateHourStart = updateTime - (updateTime % 3600);
-
- let nextHour = new Date((updateHourStart + hours * 3600) * 1000).getHours();
- if (nextHour >= 24) nextHour -= 24;
-
- return nextHour;
-};
-
-const Time = () => (
- <box className="time">
- <box hexpand halign={Gtk.Align.CENTER}>
- <label label={bindCurrentTime("%I:%M:%S")} />
- <label className="ampm" label={bindCurrentTime("%p", c => (c.get_hour() < 12 ? "AM" : "PM"))} />
- </box>
- </box>
-);
-
-const Calendar = () => (
- <box className="calendar">
- <eventbox
- setup={self => {
- self.connect("button-press-event", (_, event: Gdk.Event) => {
- if (event.get_button()[1] === Astal.MouseButton.MIDDLE) {
- const now = new Date();
- const child = self.get_child() as WCal | null;
- if (!child) return;
- child.select_month(now.getMonth(), now.getFullYear());
- }
- });
- }}
- >
- <WCal
- hexpand
- showDetails={false}
- setup={self => {
- const update = () =>
- timeout(0.1, () => {
- const now = new Date();
- if (self.month === now.getMonth() && self.year === now.getFullYear())
- self.day = now.getDate();
- else self.day = 0;
- });
- self.connect("month-changed", update);
- self.connect("next-month", update);
- self.connect("prev-month", update);
- self.connect("next-year", update);
- self.connect("prev-year", update);
- update();
- }}
- />
- </eventbox>
- </box>
-);
-
-const Weather = () => {
- const weather = SWeather.get_default();
-
- return (
- <box vertical className="weather">
- <centerbox className="current">
- <label
- halign={Gtk.Align.START}
- valign={Gtk.Align.CENTER}
- className="status-icon"
- label={bind(weather, "icon")}
- />
- <box vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} className="status">
- <box halign={Gtk.Align.CENTER} className="temperature">
- <label label={bind(weather, "temperature")} />
- <label
- className={bind(weather, "tempColour").as(c => `temp-icon ${c}`)}
- label={bind(weather, "tempIcon")}
- />
- </box>
- <label label={bind(weather, "condition").as(c => ellipsize(c, 16))} />
- </box>
- <box vertical halign={Gtk.Align.END} valign={Gtk.Align.CENTER} className="other-data">
- <label
- xalign={0}
- label={bind(weather, "wind").as(w => ` ${w}`)}
- setup={self =>
- setupCustomTooltip(
- self,
- bind(weather, "wind").as(w => `${w} wind speed`)
- )
- }
- />
- <label
- xalign={0}
- label={bind(weather, "rainChance").as(r => ` ${r}`)}
- setup={self =>
- setupCustomTooltip(
- self,
- bind(weather, "rainChance").as(r => `${r} chance of rain`)
- )
- }
- />
- </box>
- </centerbox>
- <box className="separator" />
- <box className="forecast">
- {Array.from({ length: 4 }).map((_, i) => (
- <box vertical hexpand className="hour">
- <label
- label={bind(weather, "raw").as(r => {
- const hour = getHoursFromUpdate(r, i + 1);
- return `${hour % 12 || 12}${hour < 12 ? "AM" : "PM"}`;
- })}
- />
- <label
- className="icon"
- label={bind(weather, "raw").as(r =>
- weather.getIcon(weather.forecast[getHoursFromUpdate(r, i + 1)].condition.text)
- )}
- />
- <label
- label={bind(weather, "raw").as(r =>
- weather.getTemp(weather.forecast[getHoursFromUpdate(r, i + 1)])
- )}
- />
- </box>
- ))}
- </box>
- </box>
- );
-};
-
-export default () => (
- <PopupWindow name="sideright">
- <box vertical className="sideright">
- <Time />
- <Calendar />
- <Weather />
- </box>
- </PopupWindow>
-);
diff --git a/src/modules/popdowns/updates.tsx b/src/modules/popdowns/updates.tsx
deleted file mode 100644
index 4cb95e7..0000000
--- a/src/modules/popdowns/updates.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import Palette from "@/services/palette";
-import Updates, { Repo as IRepo, Update as IUpdate } from "@/services/updates";
-import { MenuItem } from "@/utils/widgets";
-import PopdownWindow from "@/widgets/popdownwindow";
-import { bind, execAsync, GLib, Variable } from "astal";
-import { App, Astal, Gtk } from "astal/gtk3";
-
-const constructItem = (label: string, exec: string, quiet = true) =>
- new MenuItem({
- label,
- onActivate() {
- App.get_window("updates")?.hide();
- execAsync(exec).catch(e => !quiet && console.error(e));
- },
- });
-
-const Update = (update: IUpdate) => {
- const menu = new Gtk.Menu();
- menu.append(constructItem("Open info in browser", `xdg-open '${update.url}'`, false));
- menu.append(constructItem("Open info in terminal", `uwsm app -- foot -H pacman -Qi ${update.name}`));
- menu.append(new Gtk.SeparatorMenuItem({ visible: true }));
- menu.append(constructItem("Reinstall", `uwsm app -T -- yay -S ${update.name}`));
- menu.append(constructItem("Remove with dependencies", `uwsm app -T -- yay -Rns ${update.name}`));
-
- return (
- <button
- onClick={(_, event) => event.button === Astal.MouseButton.SECONDARY && menu.popup_at_pointer(null)}
- onDestroy={() => menu.destroy()}
- >
- <label
- truncate
- useMarkup
- xalign={0}
- label={bind(Palette.get_default(), "colours").as(
- c =>
- `${update.name} <span foreground="${c.teal}">(${update.version.old} -> ${
- update.version.new
- })</span>\n <span foreground="${c.subtext0}">${GLib.markup_escape_text(
- update.description,
- update.description.length
- )}</span>`
- )}
- />
- </button>
- );
-};
-
-const Repo = ({ repo, first }: { repo: IRepo; first?: boolean }) => {
- const expanded = Variable(first);
-
- return (
- <box vertical className="repo">
- <button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}>
- <box className="header">
- <label className="icon" label={repo.icon} />
- <label label={`${repo.name} (${repo.updates.length})`} />
- <box hexpand />
- <label className="icon" label={bind(expanded).as(e => (e ? "expand_less" : "expand_more"))} />
- </box>
- </button>
- <revealer
- revealChild={bind(expanded)}
- transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
- transitionDuration={200}
- >
- <box vertical className="list">
- {repo.updates.map(Update)}
- </box>
- </revealer>
- </box>
- );
-};
-
-const News = ({ news }: { news: string }) => {
- const expanded = Variable(true);
-
- news = news
- .replace(/^([0-9]{4}-[0-9]{2}-[0-9]{2} .+)$/gm, "<b>$1</b>") // Make titles bold
- .replaceAll("\n\x1b[0m\n<b>", "\n\n<b>") // Remove unopened \x1b[0m after each piece of news
- .slice(0, -5) // Remove last unopened \x1b[0m
- .replaceAll("\x1b[0m", "</span>"); // Replace reset code with end span
-
- return (
- <box vertical className="repo">
- <button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}>
- <box className="header">
- <label className="icon" label="newspaper" />
- <label label="News" />
- <box hexpand />
- <label className="icon" label={bind(expanded).as(e => (e ? "expand_less" : "expand_more"))} />
- </box>
- </button>
- <revealer
- revealChild={bind(expanded)}
- transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
- transitionDuration={200}
- >
- <label
- wrap
- useMarkup
- xalign={0}
- className="news"
- label={bind(Palette.get_default(), "teal").as(
- c => news.replaceAll("\x1b[36m", `<span foreground="${c}">`) // Replace color codes with html spans
- )}
- />
- </revealer>
- </box>
- );
-};
-
-const List = () => (
- <box vertical valign={Gtk.Align.START} className="repos">
- {bind(Updates.get_default(), "updateData").as(d =>
- d.news
- ? [<News news={d.news} />, ...d.repos.map(r => <Repo repo={r} />)]
- : d.repos.map((r, i) => <Repo repo={r} first={i === 0} />)
- )}
- </box>
-);
-
-export default () => (
- <PopdownWindow
- name="updates"
- count={bind(Updates.get_default(), "numUpdates")}
- headerButtons={[
- {
- label: "Update all",
- onClicked: () =>
- execAsync("uwsm app -T -- yay")
- .then(() => Updates.get_default().getUpdates())
- // Ignore errors
- .catch(() => {}),
- },
- {
- label: bind(Updates.get_default(), "loading").as(l => (l ? "Loading" : "Reload")),
- onClicked: () => Updates.get_default().getUpdates(),
- enabled: bind(Updates.get_default(), "loading"),
- },
- ]}
- emptyIcon="deployed_code_history"
- emptyLabel="All packages up to date!"
- list={<List />}
- />
-);