diff --git a/lib/pkgs.nix b/lib/pkgs.nix
index c347bcf2..8b16451b 100644
--- a/lib/pkgs.nix
+++ b/lib/pkgs.nix
@@ -32,7 +32,7 @@
packages,
pname,
}: let
- inherit (pkgs.lib) concatMapStrings elemAt filter hasAttr length map optionalString splitString toLower;
+ inherit (pkgs.lib) concatMapStrings elemAt length map optionalString splitString toLower;
withGirNames =
map (package: {
@@ -40,9 +40,13 @@
girName =
if package.pname == "astal-wireplumber"
then "AstalWp-0.1"
+ else if package.name == "astal-0.1.0"
+ then "AstalIO-0.1"
+ else if package.name == "astal-3.0.0"
+ then "Astal-3.0"
else (concatMapStrings capitalise (splitString "-" package.pname)) + "-0.1";
})
- (filter (hasAttr "pname") packages);
+ packages;
in {
"${configPath}${optionalString (length packages == 1) "/${toLower (elemAt withGirNames 0).girName}"}".source =
pkgs.callPackage
diff --git a/nixosModules/ags/v2/app.ts b/nixosModules/ags/v2/app.ts
index 2765fae1..f35a47d9 100644
--- a/nixosModules/ags/v2/app.ts
+++ b/nixosModules/ags/v2/app.ts
@@ -1,10 +1,11 @@
-import { App } from 'astal';
+import { App } from 'astal/gtk3';
import style from './style.scss';
import Bar from './widgets/bar/wim';
import BgFade from './widgets/bg-fade/main';
import Corners from './widgets/corners/main';
+import { NotifPopups } from './widgets/notifs/main';
App.start({
@@ -14,5 +15,6 @@ App.start({
Bar();
BgFade();
Corners();
+ NotifPopups();
},
});
diff --git a/nixosModules/ags/v2/default.nix b/nixosModules/ags/v2/default.nix
index 5e2b7671..b5902532 100644
--- a/nixosModules/ags/v2/default.nix
+++ b/nixosModules/ags/v2/default.nix
@@ -9,7 +9,7 @@ self: {
inherit (self.inputs) agsV2;
agsV2Packages = agsV2.packages.${pkgs.system};
- astalLibs = attrValues (removeAttrs agsV2.inputs.astal.packages.${pkgs.system} ["docs"]);
+ astalLibs = attrValues (removeAttrs agsV2.inputs.astal.packages.${pkgs.system} ["docs" "gjs"]);
configDir = "/home/matt/.nix/nixosModules/ags/v2";
in {
home = {
@@ -42,21 +42,19 @@ self: {
"${configDir}/tsconfig.json".source = pkgs.writers.writeJSON "tsconfig.json" {
"$schema" = "https://json.schemastore.org/tsconfig";
"compilerOptions" = {
+ "experimentalDecorators" = true;
+ "strict" = true;
"target" = "ES2023";
+ "moduleResolution" = "Bundler";
+ "jsx" = "react-jsx";
+ "jsxImportSource" = "${agsV2Packages.gjs}/share/astal/gjs/gtk3";
+ "paths" = {
+ "astal" = ["${agsV2Packages.gjs}/share/astal/gjs"];
+ "astal/*" = ["${agsV2Packages.gjs}/share/astal/gjs/*"];
+ };
+ "skipLibCheck" = true;
"module" = "ES2022";
"lib" = ["ES2023"];
- "strict" = true;
- "moduleResolution" = "Bundler";
- "skipLibCheck" = true;
- "checkJs" = true;
- "allowJs" = true;
- "experimentalDecorators" = true;
- "jsx" = "react-jsx";
- "jsxImportSource" = "${agsV2Packages.astal3}/share/astal/gjs/src/jsx";
- "paths" = {
- "astal" = ["${agsV2Packages.astal3}/share/astal/gjs"];
- "astal/*" = ["${agsV2Packages.astal3}/share/astal/gjs/src/*"];
- };
};
};
}
diff --git a/nixosModules/ags/v2/lib.ts b/nixosModules/ags/v2/lib.ts
index 120765b2..b7e4cd94 100644
--- a/nixosModules/ags/v2/lib.ts
+++ b/nixosModules/ags/v2/lib.ts
@@ -1,4 +1,4 @@
-import { Gdk } from 'astal';
+import { Gdk } from 'astal/gtk3';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const Hyprland = AstalHyprland.get_default();
diff --git a/nixosModules/ags/v2/style.scss b/nixosModules/ags/v2/style.scss
index 0c65f809..429f802f 100644
--- a/nixosModules/ags/v2/style.scss
+++ b/nixosModules/ags/v2/style.scss
@@ -45,3 +45,4 @@ window {
}
@import 'widgets/bar/style.scss';
+@import 'widgets/notifs/style.scss';
diff --git a/nixosModules/ags/v2/widgets/bar/fullscreen.tsx b/nixosModules/ags/v2/widgets/bar/fullscreen.tsx
index 17e9dc10..41a3b5b4 100644
--- a/nixosModules/ags/v2/widgets/bar/fullscreen.tsx
+++ b/nixosModules/ags/v2/widgets/bar/fullscreen.tsx
@@ -1,4 +1,5 @@
-import { Astal, bind, Gdk, Gtk, Variable, Widget } from 'astal';
+import { Astal, Gdk, Gtk, Widget } from 'astal/gtk3';
+import { bind, Variable } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const Hyprland = AstalHyprland.get_default();
diff --git a/nixosModules/ags/v2/widgets/bar/items/tray.tsx b/nixosModules/ags/v2/widgets/bar/items/tray.tsx
index 67f98c30..cb641532 100644
--- a/nixosModules/ags/v2/widgets/bar/items/tray.tsx
+++ b/nixosModules/ags/v2/widgets/bar/items/tray.tsx
@@ -1,4 +1,5 @@
-import { App, bind, Gdk, Gtk, idle, Widget } from 'astal';
+import { App, Gdk, Gtk, Widget } from 'astal/gtk3';
+import { bind, idle } from 'astal';
import AstalTray from 'gi://AstalTray';
const Tray = AstalTray.get_default();
diff --git a/nixosModules/ags/v2/widgets/bar/items/workspaces.tsx b/nixosModules/ags/v2/widgets/bar/items/workspaces.tsx
index 8dbafef5..23ba8cfd 100644
--- a/nixosModules/ags/v2/widgets/bar/items/workspaces.tsx
+++ b/nixosModules/ags/v2/widgets/bar/items/workspaces.tsx
@@ -1,4 +1,5 @@
-import { Gtk, timeout, Widget } from 'astal';
+import { Gtk, Widget } from 'astal/gtk3';
+import { timeout } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const Hyprland = AstalHyprland.get_default();
diff --git a/nixosModules/ags/v2/widgets/bar/wim.tsx b/nixosModules/ags/v2/widgets/bar/wim.tsx
index 84092642..8f4af7a7 100644
--- a/nixosModules/ags/v2/widgets/bar/wim.tsx
+++ b/nixosModules/ags/v2/widgets/bar/wim.tsx
@@ -1,4 +1,4 @@
-import { Astal, Gtk } from 'astal';
+import { Astal, Gtk } from 'astal/gtk3';
import Battery from './items/battery';
import Clock from './items/clock';
diff --git a/nixosModules/ags/v2/widgets/bg-fade/main.tsx b/nixosModules/ags/v2/widgets/bg-fade/main.tsx
index c30dc90f..2f53a00f 100644
--- a/nixosModules/ags/v2/widgets/bg-fade/main.tsx
+++ b/nixosModules/ags/v2/widgets/bg-fade/main.tsx
@@ -1,4 +1,4 @@
-import { Astal } from 'astal';
+import { Astal } from 'astal/gtk3';
export default () => {
diff --git a/nixosModules/ags/v2/widgets/corners/main.tsx b/nixosModules/ags/v2/widgets/corners/main.tsx
index 6ebdb19b..a8c34d28 100644
--- a/nixosModules/ags/v2/widgets/corners/main.tsx
+++ b/nixosModules/ags/v2/widgets/corners/main.tsx
@@ -1,4 +1,4 @@
-import { Astal } from 'astal';
+import { Astal } from 'astal/gtk3';
import RoundedCorner from './screen-corners';
diff --git a/nixosModules/ags/v2/widgets/corners/screen-corners.tsx b/nixosModules/ags/v2/widgets/corners/screen-corners.tsx
index 0a786c41..69dc74a8 100644
--- a/nixosModules/ags/v2/widgets/corners/screen-corners.tsx
+++ b/nixosModules/ags/v2/widgets/corners/screen-corners.tsx
@@ -1,4 +1,4 @@
-import { Gtk } from 'astal';
+import { Gtk } from 'astal/gtk3';
import Cairo from 'cairo';
diff --git a/nixosModules/ags/v2/widgets/misc/popup-window.tsx b/nixosModules/ags/v2/widgets/misc/popup-window.tsx
index 9814d7d2..66a922a9 100644
--- a/nixosModules/ags/v2/widgets/misc/popup-window.tsx
+++ b/nixosModules/ags/v2/widgets/misc/popup-window.tsx
@@ -1,5 +1,6 @@
-import { Astal, Binding, idle, Widget } from 'astal';
+import { Astal, Widget } from 'astal/gtk3';
import { register, property } from 'astal/gobject';
+import { Binding, idle } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const Hyprland = AstalHyprland.get_default();
diff --git a/nixosModules/ags/v2/widgets/misc/separator.tsx b/nixosModules/ags/v2/widgets/misc/separator.tsx
index b22a95ab..aaf0aeca 100644
--- a/nixosModules/ags/v2/widgets/misc/separator.tsx
+++ b/nixosModules/ags/v2/widgets/misc/separator.tsx
@@ -1,4 +1,4 @@
-import { Widget } from 'astal';
+import { Widget } from 'astal/gtk3';
export default ({
diff --git a/nixosModules/ags/v2/widgets/notifs/center.tsx b/nixosModules/ags/v2/widgets/notifs/center.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/nixosModules/ags/v2/widgets/notifs/gesture.tsx b/nixosModules/ags/v2/widgets/notifs/gesture.tsx
new file mode 100644
index 00000000..6ae0bb9a
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/notifs/gesture.tsx
@@ -0,0 +1,249 @@
+import { Gdk, Gtk, Widget } from 'astal/gtk3';
+import { register, property } from 'astal/gobject';
+import { idle } from 'astal';
+
+import AstalNotifd from 'gi://AstalNotifd?version=0.1';
+const Notifications = AstalNotifd.get_default();
+
+import { HasNotifs } from './notification';
+
+
+const display = Gdk.Display.get_default();
+
+const MAX_OFFSET = 200;
+const OFFSCREEN = 300;
+const ANIM_DURATION = 500;
+const SLIDE_MIN_THRESHOLD = 10;
+const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;';
+const MAX_LEFT = `
+ margin-left: -${Number(MAX_OFFSET + OFFSCREEN)}px;
+ margin-right: ${Number(MAX_OFFSET + OFFSCREEN)}px;
+`;
+const MAX_RIGHT = `
+ margin-left: ${Number(MAX_OFFSET + OFFSCREEN)}px;
+ margin-right: -${Number(MAX_OFFSET + OFFSCREEN)}px;
+`;
+
+const slideLeft = `${TRANSITION} ${MAX_LEFT} opacity: 0;`;
+
+const slideRight = `${TRANSITION} ${MAX_RIGHT} opacity: 0;`;
+
+const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`;
+
+type NotifGestureWrapperProps = Widget.BoxProps & {
+ id: number
+ slide_in_from?: 'Left' | 'Right'
+};
+
+@register()
+class NotifGestureWrapper extends Widget.EventBox {
+ readonly id: number;
+
+ readonly slide_in_from: 'Left' | 'Right';
+
+ readonly slideAway: (side: 'Left' | 'Right') => void;
+
+ @property(Boolean)
+ declare hovered: boolean;
+
+ @property(Boolean)
+ declare dragging: boolean;
+
+ constructor({
+ id,
+ slide_in_from = 'Left',
+ ...rest
+ }: NotifGestureWrapperProps) {
+ super();
+ this.id = id;
+ this.dragging = false;
+ this.hovered = false;
+ this.slide_in_from = slide_in_from;
+
+ this.slideAway = (side: 'Left' | 'Right', force = false) => {
+ ((this.get_child() as Widget.Revealer).get_child() as Widget.Box)
+ .css = side === 'Left' ? slideLeft : slideRight;
+
+ // Make it uninteractable
+ this.sensitive = false;
+
+ setTimeout(() => {
+ (this.get_child() as Widget.Revealer).revealChild = false;
+
+ setTimeout(() => {
+ // Kill notif and update HasNotifs after anim is done
+ if (force) {
+ Notifications.get_notification(this.id)?.dismiss();
+ }
+ HasNotifs.set(Notifications.get_notifications().length > 0);
+ this.destroy();
+ }, ANIM_DURATION);
+ }, ANIM_DURATION - 100);
+ };
+
+ this.connect('notify::dragging', () => {
+ if (!this.hovered && this.dragging) {
+ this.hovered = true;
+ }
+ });
+
+ // OnClick
+ this.connect('button-press-event', () => {
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grabbing',
+ ));
+ if (!this.hovered) {
+ this.hovered = true;
+ }
+ });
+
+ // OnRelease
+ this.connect('button-release-event', () => {
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grab',
+ ));
+ if (!this.hovered) {
+ this.hovered = true;
+ }
+ });
+
+ // OnHover
+ this.connect('enter-notify-event', () => {
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grab',
+ ));
+ if (!this.hovered) {
+ this.hovered = true;
+ }
+ });
+
+ // OnHoverLost
+ this.connect('leave-notify-event', () => {
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grab',
+ ));
+
+ if (this.hovered) {
+ this.hovered = false;
+ }
+ });
+
+ const gesture = Gtk.GestureDrag.new(this);
+
+ this.add(
+
+ {
+ self
+ // When dragging
+ .hook(gesture, 'drag-update', () => {
+ let offset = gesture.get_offset()[1];
+
+ if (!offset || offset === 0) {
+ return;
+ }
+
+ // Slide right
+ if (offset > 0) {
+ self.css = `
+ opacity: 1; transition: none;
+ margin-left: ${offset}px;
+ margin-right: -${offset}px;
+ `;
+ }
+
+ // Slide left
+ else {
+ offset = Math.abs(offset);
+ self.css = `
+ opacity: 1; transition: none;
+ margin-right: ${offset}px;
+ margin-left: -${offset}px;
+ `;
+ }
+
+ // Put a threshold on if a click is actually dragging
+ this.dragging = Math.abs(offset) > SLIDE_MIN_THRESHOLD;
+
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grabbing',
+ ));
+ })
+
+ // On drag end
+ .hook(gesture, 'drag-end', () => {
+ const offset = gesture.get_offset()[1];
+
+ if (!offset) {
+ return;
+ }
+
+ // If crosses threshold after letting go, slide away
+ if (Math.abs(offset) > MAX_OFFSET) {
+ if (offset > 0) {
+ this.slideAway('Right');
+ }
+ else {
+ this.slideAway('Left');
+ }
+ }
+ else {
+ self.css = defaultStyle;
+ this.dragging = false;
+
+ if (!display) {
+ return;
+ }
+ this.window.set_cursor(Gdk.Cursor.new_from_name(
+ display,
+ 'grab',
+ ));
+ }
+ });
+
+ // Reverse of slideAway, so it started at squeeze, then we go to slide
+ self.css = this.slide_in_from === 'Left' ?
+ slideLeft :
+ slideRight;
+
+ idle(() => {
+ (self.get_parent() as Widget.Revealer).revealChild = true;
+
+ setTimeout(() => {
+ // Then we go to center
+ self.css = defaultStyle;
+ }, ANIM_DURATION);
+ });
+ }}
+ />
+ ,
+ );
+ }
+}
+
+export default (props: NotifGestureWrapperProps) => new NotifGestureWrapper(props);
diff --git a/nixosModules/ags/v2/widgets/notifs/main.tsx b/nixosModules/ags/v2/widgets/notifs/main.tsx
new file mode 100644
index 00000000..27eddb6c
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/notifs/main.tsx
@@ -0,0 +1,14 @@
+import { Astal } from 'astal/gtk3';
+
+import Popups from './popups';
+
+
+export const NotifPopups = () => (
+
+
+
+);
diff --git a/nixosModules/ags/v2/widgets/notifs/notification.tsx b/nixosModules/ags/v2/widgets/notifs/notification.tsx
new file mode 100644
index 00000000..2f81c601
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/notifs/notification.tsx
@@ -0,0 +1,160 @@
+import { App, Gtk } from 'astal/gtk3';
+import { Variable } from 'astal';
+
+import GLib from 'gi://GLib?version=2.0';
+
+import AstalApps from 'gi://AstalApps?version=0.1';
+const Applications = AstalApps.Apps.new();
+
+import AstalNotifd from 'gi://AstalNotifd?version=0.1';
+const Notifications = AstalNotifd.get_default();
+
+import NotifGestureWrapper from './gesture';
+
+
+// Make a variable to connect to for Widgets
+// to know when there are notifs or not
+export const HasNotifs = Variable(false);
+
+const setTime = (time: number): string => {
+ return GLib.DateTime
+ .new_from_unix_local(time)
+ .format('%H:%M') ?? '';
+};
+
+const NotifIcon = ({ notifObj }: { notifObj: AstalNotifd.Notification }) => {
+ let icon: string;
+
+ if (notifObj.get_image() !== '') {
+ icon = notifObj.get_image();
+ App.add_icons(icon);
+ }
+ else if (notifObj.get_app_icon() !== '') {
+ icon = notifObj.get_app_icon();
+ }
+ else {
+ icon = Applications.query(
+ notifObj.get_app_name(),
+ false,
+ )[0].get_icon_name();
+ }
+
+ return (
+
+
+
+ );
+};
+
+const BlockedApps = [
+ 'Spotify',
+];
+
+export const Notification = ({
+ id = 0,
+}): ReturnType | undefined => {
+ const notifObj = Notifications.get_notification(id);
+
+ if (!notifObj) {
+ return;
+ }
+
+ if (BlockedApps.find((app) => app === notifObj.app_name)) {
+ notifObj.dismiss();
+
+ return;
+ }
+
+ HasNotifs.set(Notifications.get_notifications().length > 0);
+
+ return (
+
+
+ {/* Content */}
+
+
+
+ {/* Top of Content */}
+
+
+
+ {/* Title */}
+
+
+ {/* Time */}
+
+
+ {/* Close button */}
+
+
+
+
+ {/* Description */}
+
+
+
+
+ {/* Actions */}
+
+ {notifObj.get_actions().map((action) => (
+
+ ))}
+
+
+
+ ) as ReturnType;
+};
diff --git a/nixosModules/ags/v2/widgets/notifs/popups.tsx b/nixosModules/ags/v2/widgets/notifs/popups.tsx
new file mode 100644
index 00000000..01129d1f
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/notifs/popups.tsx
@@ -0,0 +1,48 @@
+import AstalNotifd from 'gi://AstalNotifd?version=0.1';
+const Notifications = AstalNotifd.get_default();
+
+import { Notification } from './notification';
+
+
+export default () => (
+ {
+ const NotifsMap = new Map>();
+
+ const addPopup = (id: number) => {
+ if (!id) {
+ return;
+ }
+
+ const NewNotif = Notification({ id });
+
+ if (NewNotif) {
+ // Use this instead of add to put it at the top
+ self.pack_end(NewNotif, false, false, 0);
+ self.show_all();
+
+ NotifsMap.set(id, NewNotif);
+ }
+ };
+
+ const handleResolved = (id: number) => {
+ const notif = NotifsMap.get(id);
+
+ if (!notif) {
+ return;
+ }
+
+ notif.slideAway('Left');
+ NotifsMap.delete(id);
+ };
+
+ self
+ .hook(Notifications, 'notified', (_, id) => addPopup(id))
+ .hook(Notifications, 'resolved', (_, id) => handleResolved(id));
+ }}
+ />
+);
diff --git a/nixosModules/ags/v2/widgets/notifs/style.scss b/nixosModules/ags/v2/widgets/notifs/style.scss
new file mode 100644
index 00000000..c8d595be
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/notifs/style.scss
@@ -0,0 +1,32 @@
+.notification.widget {
+ // urgencies
+ // &.urgency ...
+
+ .icon {
+ margin-right: 10px;
+ }
+
+ .time {
+ margin: 3px;
+ }
+
+ .close-button {
+ margin: 3px;
+ }
+
+ .title {
+ font-weight: 800;
+ font-size: 20px;
+ margin-bottom: 5px;
+ }
+
+ .description {
+ margin-bottom: 5px;
+ }
+
+ .actions {
+ margin: 3px;
+
+ .action-button {}
+ }
+}
diff --git a/nixosModules/desktop/environment/config/kdeglobals b/nixosModules/desktop/environment/config/kdeglobals
index 719dadad..17898700 100644
--- a/nixosModules/desktop/environment/config/kdeglobals
+++ b/nixosModules/desktop/environment/config/kdeglobals
@@ -14,10 +14,10 @@ Show Inline Previews=true
Show Preview=false
Show Speedbar=true
Show hidden files=true
-Sort by=Name
+Sort by=Date
Sort directories first=true
Sort hidden files last=false
-Sort reversed=false
+Sort reversed=true
Speedbar Width=189
View Style=DetailTree