commit 82da1c1ecb77a2c989c63189a03debf688afb553 Author: Franek Date: Thu Feb 6 12:05:55 2025 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..298eb4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +@girs/ diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..10669ca --- /dev/null +++ b/app.ts @@ -0,0 +1,52 @@ +import style from "./styles/style.scss" +import { App, Gtk } from "astal/gtk4" +import { GLib } from "astal"; + +import Hyprland from "gi://AstalHyprland"; + +import QuickSettings from "widget/quick_settings/quick_settings"; +import BluetoothWindow from "./widget/quick_settings/bluetooth"; + +import Launcher from "widget/launcher/launcher"; +import Bar from "widget/bar/bar"; + +const hypr = Hyprland.get_default(); +const windows = new Map(); + +const setupBars = async (monitor_id: number) => { + const components = [Bar, Launcher, QuickSettings, BluetoothWindow]; + + const windows = await Promise.all( + components.map(item => Promise.resolve(item(monitor_id)) as Promise) + ); + + return windows; +}; + +App.start({ + css: style, + async main() { + App.add_icons(`${GLib.get_user_data_dir()}/icons/Astal`); + + const monitors = App.get_monitors(); + for (const monitor of monitors) { + const index = monitors.indexOf(monitor); + windows.set(index, await setupBars(index)); + + hypr.connect("monitor-added", async (_, monitor: Hyprland.Monitor) => { + if (!windows.has(monitor.id)) windows.set(monitor.id, await setupBars(monitor.id)) + }); + + hypr.connect("monitor-removed", (_, monitor_id: number) => { + const monitorWindows = windows.get(monitor_id) + if (monitorWindows) { + for (const monitorWindow of monitorWindows) { + monitorWindow.destroy(); + }; + + windows.delete(monitor_id); + }; + }); + } + }, +}) diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 0000000..467c0a4 --- /dev/null +++ b/env.d.ts @@ -0,0 +1,21 @@ +declare const SRC: string + +declare module "inline:*" { + const content: string + export default content +} + +declare module "*.scss" { + const content: string + export default content +} + +declare module "*.blp" { + const content: string + export default content +} + +declare module "*.css" { + const content: string + export default content +} diff --git a/lib/icons.tsx b/lib/icons.tsx new file mode 100644 index 0000000..6366fbe --- /dev/null +++ b/lib/icons.tsx @@ -0,0 +1,6 @@ +export default { + battery: { + charging: "battery-symbolic", + discharging: "battery-caution-symbolic" + } +}; diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..2196b22 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,16 @@ +import { Gtk, Gdk } from "astal/gtk4"; + +export type If = Condition extends true ? Then : Else; +export type Belongs = { + [K in keyof T]: T[K] extends U ? K : never; +}[keyof T]; + +export const hideWindow = (self: Gtk.Window, keyval: number) => { + if (keyval === Gdk.KEY_Escape) self.hide(); +} + +export const openOnButton = (event: Gdk.ButtonEvent, keyval: number) => (action: () => void) => { + if (event.get_button() !== keyval) return; + + action(); +} \ No newline at end of file diff --git a/lib/widgets/datetime.tsx b/lib/widgets/datetime.tsx new file mode 100644 index 0000000..8ae205e --- /dev/null +++ b/lib/widgets/datetime.tsx @@ -0,0 +1,24 @@ +import { Variable, GLib } from "astal"; +import { Widget } from "astal/gtk4"; + +type Props = { + format: string, + interval?: number +} & Widget.LabelProps; + +export default function DateTime({ format, interval, ...props }: Props) { + const shouldPoll = typeof interval === "number" && interval >= 1; + + const currentTime = () => { + const dateTime = GLib.DateTime.new_now_local(); + return dateTime.format(format)!; + } + + if (shouldPoll) { + const pollTime = new Variable(currentTime()).poll(interval || 1000, currentTime); + return