From 82da1c1ecb77a2c989c63189a03debf688afb553 Mon Sep 17 00:00:00 2001 From: Franek Date: Thu, 6 Feb 2025 12:05:55 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + app.ts | 52 +++++++ env.d.ts | 21 +++ lib/icons.tsx | 6 + lib/utils.ts | 16 +++ lib/widgets/datetime.tsx | 24 ++++ package.json | 6 + settings.json | 5 + styles/_variables.scss | 6 + styles/classes.scss | 6 + styles/components/_bar.scss | 30 ++++ styles/components/_index.scss | 3 + styles/components/_launcher.scss | 53 +++++++ styles/components/_quick_settings.scss | 66 +++++++++ styles/mixins.scss | 8 ++ styles/style.scss | 12 ++ tsconfig.json | 19 +++ widget/bar/bar.tsx | 81 +++++++++++ widget/bar/network/button.tsx | 43 ++++++ widget/bar/tray.tsx | 30 ++++ widget/bar/workspace.tsx | 18 +++ widget/launcher/launcher.tsx | 82 +++++++++++ widget/quick_settings/bluetooth.tsx | 73 ++++++++++ widget/quick_settings/quick_settings.tsx | 167 +++++++++++++++++++++++ 24 files changed, 829 insertions(+) create mode 100644 .gitignore create mode 100644 app.ts create mode 100644 env.d.ts create mode 100644 lib/icons.tsx create mode 100644 lib/utils.ts create mode 100644 lib/widgets/datetime.tsx create mode 100644 package.json create mode 100644 settings.json create mode 100644 styles/_variables.scss create mode 100644 styles/classes.scss create mode 100644 styles/components/_bar.scss create mode 100644 styles/components/_index.scss create mode 100644 styles/components/_launcher.scss create mode 100644 styles/components/_quick_settings.scss create mode 100644 styles/mixins.scss create mode 100644 styles/style.scss create mode 100644 tsconfig.json create mode 100644 widget/bar/bar.tsx create mode 100644 widget/bar/network/button.tsx create mode 100644 widget/bar/tray.tsx create mode 100644 widget/bar/workspace.tsx create mode 100644 widget/launcher/launcher.tsx create mode 100644 widget/quick_settings/bluetooth.tsx create mode 100644 widget/quick_settings/quick_settings.tsx 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