diff --git a/modules/ags/astal/greeter.ts b/modules/ags/astal/greeter.ts
new file mode 100644
index 00000000..be1e95b1
--- /dev/null
+++ b/modules/ags/astal/greeter.ts
@@ -0,0 +1,7 @@
+import Greeter from './ts/greetd/main';
+
+App.config({
+    windows: () => [
+        Greeter(),
+    ],
+});
diff --git a/modules/ags/astal/js/utils.js b/modules/ags/astal/js/utils.js
index f7cce39e..3bb911b6 100644
--- a/modules/ags/astal/js/utils.js
+++ b/modules/ags/astal/js/utils.js
@@ -29,19 +29,19 @@ export const transpileTypeScript = async(host) => {
         // Create the dir if it doesn't exist
         `mkdir -p /tmp/astal-${host}; ` +
 
+        // Let bun see tsconfig.json
+        `cd ${App.configDir};` +
+
         `bun build ${App.configDir}/${host}.ts ` +
         '--external resource:///* ' +
         '--external gi://* ' +
         '--external cairo ' +
-        '--external */fzf.es.js ' +
 
-        // Since bun wants to right in cwd, we just redirect stdin instead
+        // Since bun wants to write in cwd, we just redirect stdin instead
         `> ${outPath}`,
     ]).catch(print);
 
-    if (host !== 'greeter') {
-        watchAndCompileSass(host);
-    }
+    watchAndCompileSass(host);
 
     return await import(`file://${outPath}`);
 };
diff --git a/modules/ags/astal/scss/greeter.scss b/modules/ags/astal/scss/greeter.scss
new file mode 100644
index 00000000..34cf7798
--- /dev/null
+++ b/modules/ags/astal/scss/greeter.scss
@@ -0,0 +1,16 @@
+window {
+  all: unset;
+  background-color: transparent;
+}
+
+
+.base {
+  background-color: #{"@window_bg_color"};
+  border: 1.3px solid #{"@accent_bg_color"};
+  border-radius: 12px;
+  padding: 5px;
+}
+
+dropdown popover.menu {
+  padding-top: 0;
+}
diff --git a/modules/ags/astal/scss/wim.scss b/modules/ags/astal/scss/wim.scss
deleted file mode 100644
index 1a14d467..00000000
--- a/modules/ags/astal/scss/wim.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-window {
-  all: unset;
-}
-
-
-.base {
-  background-color: #{"@window_bg_color"};
-  border: 1px solid #{"@accent_bg_color"};
-  padding: 5px;
-}
diff --git a/modules/ags/astal/ts/greetd/main.ts b/modules/ags/astal/ts/greetd/main.ts
new file mode 100644
index 00000000..ca1cefa6
--- /dev/null
+++ b/modules/ags/astal/ts/greetd/main.ts
@@ -0,0 +1,115 @@
+const { Box, Entry, Label, Window } = Widget;
+const { execAsync, idle, readFileAsync } = Utils;
+
+const greetd = await Service.import('greetd');
+
+const { Gtk } = imports.gi;
+
+const DEFAULT_NAME = 'matt';
+
+// Types
+import { StringObject } from 'types/@girs/gtk-4.0/gtk-4.0.cjs';
+
+
+// Run Wallpaper daemon here to not cause issues at startup
+execAsync(['bash', '-c',
+    `swww init --no-cache && swww img -t none ${App.configDir}/.wallpaper`]).catch(print);
+
+const parsePasswd = (fileContent: string) => {
+    const splitUsers = fileContent.split('\n');
+    const parsedUsers = splitUsers.map((u) => {
+        const user = u.split(':');
+
+        return {
+            name: user[0],
+            uid: Number(user[2]),
+            gid: Number(user[3]),
+            desc: user[4],
+            home: user[5],
+            shell: user[6],
+        };
+    });
+
+    // Filter out system users, nixbld users and nobody
+    return parsedUsers.filter((u) => {
+        return u.uid >= 1000 &&
+            !u.name.includes('nixbld') &&
+            u.name !== 'nobody';
+    });
+};
+const users = parsePasswd(await readFileAsync('/etc/passwd'));
+
+const dropdown = Gtk.DropDown.new_from_strings(users.map((u) => u.name));
+
+const password = Entry({
+    placeholderText: 'Password',
+    visibility: false,
+
+    setup: (self) => idle(() => {
+        self.grab_focus();
+    }),
+
+    on_accept: () => {
+        greetd.login(
+            (dropdown.selectedItem as StringObject)['string'] || '',
+            password.text || '',
+            'Hyprland',
+
+        ).catch((error) => {
+            response.label = JSON.stringify(error);
+        });
+    },
+
+});
+
+const response = Label();
+
+export default () => Window({
+    name: 'greeter',
+    keymode: 'on-demand',
+
+    child: Box({
+        vertical: true,
+        hpack: 'center',
+        vpack: 'center',
+        hexpand: true,
+        vexpand: true,
+        cssClasses: ['base'],
+
+        children: [
+            Box({
+                vertical: true,
+                hpack: 'center',
+                vpack: 'center',
+                hexpand: true,
+                vexpand: true,
+
+                setup: (self) => {
+                    self.add_css_class('linked');
+
+                    idle(() => {
+                        const usernames = [] as string[];
+
+                        for (let i = 0; i < dropdown.model.get_n_items(); ++i) {
+                            const name = (dropdown.model.get_item(i) as StringObject)['string'];
+
+                            if (name) {
+                                usernames.push(name);
+                            }
+                        }
+
+                        if (usernames.includes(DEFAULT_NAME)) {
+                            dropdown.set_selected(usernames.indexOf(DEFAULT_NAME));
+                        }
+                    });
+                },
+
+                children: [
+                    dropdown,
+                    password,
+                ],
+            }),
+            response,
+        ],
+    }),
+});
diff --git a/modules/ags/astal/wim.ts b/modules/ags/astal/wim.ts
deleted file mode 100644
index e6c546a9..00000000
--- a/modules/ags/astal/wim.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import Adw from 'gi://Adw';
-
-
-App.config({
-    windows: () => [
-        Widget.Window({
-            name: 'test',
-
-            child: Widget.Box({
-                setup: (self) => {
-                    self.toggleCssClass('base');
-                },
-
-                child: new Adw.SplitButton({
-                    label: 'test',
-                }),
-            }),
-        }),
-    ],
-});
diff --git a/modules/ags/config/greeter.ts b/modules/ags/config/greeter.ts
deleted file mode 100644
index 11cf01c2..00000000
--- a/modules/ags/config/greeter.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-/* eslint no-magic-numbers: 0 */
-
-const { Box, Button, Entry, Label, Menu, MenuItem, Window } = Widget;
-const { execAsync, idle, readFileAsync } = Utils;
-
-const greetd = await Service.import('greetd');
-
-const { Gdk } = imports.gi;
-
-const DEFAULT_NAME = 'matt';
-
-// Types
-type User = {
-    name: string;
-    uid: number;
-    gid: number;
-    desc: string;
-    home: string;
-    shell: string;
-};
-
-
-// Run Wallpaper daemon here to not cause issues at startup
-execAsync(['swww', 'init', '--no-cache']).then(() => {
-    execAsync([
-        'swww', 'img', '-t', 'none',
-        `${App.configDir}/.wallpaper`,
-    ]).catch(print);
-}).catch(print);
-
-// Put ref of Label here to change it easily later
-const name = Label(DEFAULT_NAME);
-
-// Initiate menu here to not have garbage collection take it away
-// TODO: figure out type
-let menu;
-
-const parsePasswd = (fileContent: string) => {
-    const splitUsers = fileContent.split('\n');
-    const parsedUsers = splitUsers.map((u) => {
-        const user = u.split(':');
-
-        return {
-            name: user[0],
-            uid: Number(user[2]),
-            gid: Number(user[3]),
-            desc: user[4],
-            home: user[5],
-            shell: user[6],
-        };
-    });
-
-    // Filter out system users, nixbld users and nobody
-    return parsedUsers.filter((u) => {
-        return u.uid >= 1000 &&
-              !u.name.includes('nixbld') &&
-               u.name !== 'nobody';
-    });
-};
-
-// FIXME: make menu scrollable
-const DropdownMenu = (users: User[]) => Menu({
-    attach_widget: dropdown,
-    children: users.map((u) => MenuItem({
-        on_activate: () => {
-            name.label = u.name;
-        },
-
-        child: Label({
-            label: u.name,
-            justification: 'center',
-            css: `min-width: ${dropdown.get_allocated_width() / 2}px;`,
-        }),
-    })),
-});
-
-const dropdown = Button({
-    child: name,
-
-    setup: () => {
-        idle(() => {
-            readFileAsync('/etc/passwd').then((out) => {
-                const users = parsePasswd(out);
-
-                menu = DropdownMenu(users);
-            }).catch(print);
-        });
-    },
-
-    on_primary_click_release: (self, event) => {
-        menu.popup_at_widget(
-            self,
-            Gdk.Gravity.SOUTH,
-            Gdk.Gravity.NORTH,
-            event,
-        );
-    },
-});
-
-const password = Entry({
-    placeholder_text: 'Password',
-    visibility: false,
-
-    on_accept: () => {
-        greetd.login(
-            name.label || '',
-            password.text || '',
-            'Hyprland',
-
-        ).catch((error) => {
-            response.label = JSON.stringify(error);
-        });
-    },
-
-}).on('realize', (entry) => entry.grab_focus());
-
-const response = Label();
-
-const win = Window({
-    name: 'greeter',
-    css: 'background-color: transparent;',
-    anchor: ['top', 'left', 'right', 'bottom'],
-    keymode: 'on-demand',
-
-    child: Box({
-        vertical: true,
-        hpack: 'center',
-        vpack: 'center',
-        hexpand: true,
-        vexpand: true,
-
-        children: [
-            dropdown,
-            password,
-            response,
-        ],
-    }),
-});
-
-export default { windows: [win] };
diff --git a/modules/ags/config/js/utils.js b/modules/ags/config/js/utils.js
index ed295934..7ed4398b 100644
--- a/modules/ags/config/js/utils.js
+++ b/modules/ags/config/js/utils.js
@@ -41,9 +41,7 @@ export const transpileTypeScript = async(host) => {
         `> ${outPath}`,
     ]).catch(print);
 
-    if (host !== 'greeter') {
-        watchAndCompileSass(host);
-    }
+    watchAndCompileSass(host);
 
     return await import(`file://${outPath}`);
 };
diff --git a/modules/ags/config/test-greeter.js b/modules/ags/config/test-greeter.js
deleted file mode 100644
index 057bfb14..00000000
--- a/modules/ags/config/test-greeter.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Delete /tmp/ags-greeter before and after using this
-import { transpileTypeScript } from './js/utils.js';
-
-export default (await transpileTypeScript('greeter')).default;
diff --git a/modules/greetd/ags.nix b/modules/greetd/astal.nix
similarity index 76%
rename from modules/greetd/ags.nix
rename to modules/greetd/astal.nix
index a23ae866..6c1db7b4 100644
--- a/modules/greetd/ags.nix
+++ b/modules/greetd/astal.nix
@@ -1,5 +1,5 @@
 {
-  ags,
+  astal,
   config,
   pkgs,
   ...
@@ -15,12 +15,12 @@ in {
 
   home-manager.users.greeter = {
     imports = [
-      ags.homeManagerModules.default
+      astal.homeManagerModules.default
       ../../common/vars
       ../../home/theme
     ];
 
-    programs.ags.enable = true;
+    programs.astal.enable = true;
 
     home = {
       packages = [
@@ -33,14 +33,14 @@ in {
       ];
 
       file = {
-        ".config/ags/.wallpaper".source = "${pkgs.dracula-theme}/wallpapers/waves.png";
+        ".config/astal/.wallpaper".source = "${pkgs.dracula-theme}/wallpapers/waves.png";
 
-        ".config/ags" = {
-          source = ../ags/config;
+        ".config/astal" = {
+          source = ../ags/astal;
           recursive = true;
         };
 
-        ".config/ags/config.js".text =
+        ".config/astal/config.js".text =
           /*
           javascript
           */
diff --git a/modules/greetd/default.nix b/modules/greetd/default.nix
index 007ca764..4dcefc07 100644
--- a/modules/greetd/default.nix
+++ b/modules/greetd/default.nix
@@ -11,7 +11,7 @@
   isTouchscreen = config.hardware.sensor.iio.enable;
   hyprland = config.home-manager.users.${mainUser}.wayland.windowManager.hyprland.finalPackage;
 in {
-  imports = [./ags.nix];
+  imports = [./astal.nix];
 
   services = {
     xserver = {
diff --git a/modules/greetd/hyprland.nix b/modules/greetd/hyprland.nix
index 7ee40666..a4f09099 100644
--- a/modules/greetd/hyprland.nix
+++ b/modules/greetd/hyprland.nix
@@ -93,7 +93,7 @@ in {
       }
 
       exec-once = ${setupMonitors}
-      exec-once = ags -b greeter &> /tmp/ags.log; hyprctl dispatch exit
+      exec-once = astal -b greeter &> /tmp/astal.log; hyprctl dispatch exit
     ''
   );
 }