This project is under active development and not yet stable. APIs may change without notice.
A greetd greeter library for AGS / GJS.
Build your own Linux login screen with GTK4 and TypeScript (JSX). tadaima handles greetd authentication, session discovery, and state caching -- you bring the UI.
npx create-tadaima my-greeter
createGreeter() -- Factory for session discovery, cache, and login handlercreateLoginHandler() -- Concurrency-guarded login with greetd IPC.desktop files// src/app.tsx
import app from 'ags/gtk4/app';
import Greeter from './components/Greeter';
app.start({
instanceName: 'greeter',
requestHandler(_, response) {
response('not implemented');
},
main() {
Greeter();
},
});
// src/components/Greeter.tsx
import app from 'ags/gtk4/app';
import { Gtk } from 'ags/gtk4';
import { createGreeter } from '@myxogastria0808/tadaima';
const Greeter = (): void => {
const { sessions, sessionNames, cache, createLoginHandler } = createGreeter({
sessionDirs: ['/usr/share/wayland-sessions', '/usr/share/xsessions'],
cachePath: '/var/cache/tadaima/state.json',
});
let usernameEntry!: Gtk.Entry;
let passwordEntry!: Gtk.PasswordEntry;
let sessionDropdown!: Gtk.DropDown;
let errorLabel!: Gtk.Label;
let loginButton!: Gtk.Button;
const handleLogin = createLoginHandler({
username: () => usernameEntry.text,
password: () => passwordEntry.text,
selectedSession: () => sessions[sessionDropdown.selected],
onLoggingIn: () => {
errorLabel.visible = false;
loginButton.sensitive = false;
loginButton.label = 'Logging in...';
},
onError: (message) => {
errorLabel.label = message;
errorLabel.visible = true;
passwordEntry.text = '';
passwordEntry.grab_focus();
loginButton.sensitive = true;
loginButton.label = 'Login';
},
});
const win = (
<Gtk.ApplicationWindow application={app} name="greeter">
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} valign={Gtk.Align.CENTER} halign={Gtk.Align.CENTER}>
<Gtk.Label label="Login" />
<Gtk.Entry
text={cache.username}
placeholderText="Username"
onActivate={() => passwordEntry.grab_focus()}
$={(self) => (usernameEntry = self)}
/>
<Gtk.PasswordEntry
placeholderText="Password"
showPeekIcon={true}
onActivate={handleLogin}
$={(self) => (passwordEntry = self)}
/>
<Gtk.DropDown
$constructor={() => Gtk.DropDown.new_from_strings(sessionNames)}
selected={cache.sessionIndex}
$={(self) => (sessionDropdown = self)}
/>
<Gtk.Label label="" visible={false} $={(self) => (errorLabel = self)} />
<Gtk.Button label="Login" onClicked={handleLogin} $={(self) => (loginButton = self)} />
</Gtk.Box>
</Gtk.ApplicationWindow>
) as Gtk.ApplicationWindow;
win.present();
passwordEntry.grab_focus();
};
export default Greeter;