diff --git a/modules/ags/config/eslint.config.js b/modules/ags/config/eslint.config.js index b9bf3c6a..fb088f0e 100644 --- a/modules/ags/config/eslint.config.js +++ b/modules/ags/config/eslint.config.js @@ -16,6 +16,8 @@ export default tseslint.config( eslint.configs.recommended, jsdoc.configs['flat/recommended-typescript'], + { ignores: ['js'] }, + { plugins: { jsdoc, diff --git a/modules/ags/config/js/utils.js b/modules/ags/config/js/utils.js index 7ed4398b..c776f621 100644 --- a/modules/ags/config/js/utils.js +++ b/modules/ags/config/js/utils.js @@ -32,7 +32,7 @@ export const transpileTypeScript = async(host) => { // Let bun see tsconfig.json `cd ${App.configDir};` + - `bun build ${App.configDir}/${host}.ts ` + + `bun build ${App.configDir}/${host === 'lockscreen' ? 'ts/lockscreen/main' : host}.ts ` + '--external resource:///* ' + '--external gi://* ' + '--external cairo ' + diff --git a/modules/ags/config/lockscreen.ts b/modules/ags/config/lockscreen.ts deleted file mode 100644 index 25d76e51..00000000 --- a/modules/ags/config/lockscreen.ts +++ /dev/null @@ -1,8 +0,0 @@ -import LaunchLockscreen from './ts/lockscreen/main.ts'; - - -LaunchLockscreen(); - -App.config({ - icons: './icons', -}); diff --git a/modules/ags/config/ts/lockscreen/lock.ts b/modules/ags/config/ts/lockscreen/lock.ts new file mode 100644 index 00000000..5c57b50e --- /dev/null +++ b/modules/ags/config/ts/lockscreen/lock.ts @@ -0,0 +1,211 @@ +const { Box, Entry, Label, Window } = Widget; + +import Gdk from 'gi://Gdk?version=3.0'; +import Gtk from 'gi://Gtk?version=3.0'; +import Lock from 'gi://GtkSessionLock?version=0.1'; + +// This file is generated by Nix +import Vars from './vars.ts'; + +import Separator from '../misc/separator.ts'; +import { get_hyprland_monitor_desc } from '../lib.ts'; + +/* Types */ +import { Box as AgsBox } from 'types/widgets/box'; + + +const lock = Lock.prepare_lock(); +const windows: Map = new Map(); +const blurBGs: AgsBox[] = []; + +const transition_duration = 1000; +const WINDOW_MARGINS = -2; +const ENTRY_SPACING = 20; +const CLOCK_SPACING = 60; + +const bgCSS = ({ w = 1, h = 1 } = {}) => ` + border: 2px solid rgba(189, 147, 249, 0.8); + background: rgba(0, 0, 0, 0.2); + min-height: ${h}px; + min-width: ${w}px; + transition: min-height ${transition_duration / 2}ms, + min-width ${transition_duration / 2}ms; +`; + +const unlock = () => { + blurBGs.forEach((b) => { + b.css = bgCSS({ + w: b.attribute.geometry.w, + h: 1, + }); + + Utils.timeout(transition_duration / 2, () => { + b.css = bgCSS({ + w: 1, + h: 1, + }); + }); + }); + Utils.timeout(transition_duration, () => { + lock.unlock_and_destroy(); + Gdk.Display.get_default()?.sync(); + App.quit(); + }); +}; + +const Clock = () => Label({ class_name: 'clock' }) + .poll(1000, (self) => { + self.label = (new Date().toLocaleString([], { + hour: 'numeric', + minute: 'numeric', + hour12: true, + }) ?? '') + .replace('a.m.', 'AM') + .replace('p.m.', 'PM'); + }); + +const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => { + const rev = Box({ + css: bgCSS(), + attribute: { + geometry: {} as { w: number, h: number }, + }, + + setup: (self) => Utils.idle(() => { + self.attribute.geometry = { + w: monitor.geometry.width, + h: monitor.geometry.height, + }; + + self.css = bgCSS({ + w: self.attribute.geometry.w, + h: 1, + }); + + Utils.timeout(transition_duration / 2, () => { + self.css = bgCSS({ + w: self.attribute.geometry.w, + h: self.attribute.geometry.h, + }); + }); + }), + }); + + blurBGs.push(rev); + + Window({ + name: `blur-bg-${monitor.get_model()}`, + gdkmonitor: monitor, + layer: 'overlay', + anchor: ['top', 'bottom', 'right', 'left'], + margins: [WINDOW_MARGINS], + exclusivity: 'ignore', + + child: Box({ + hexpand: false, + vexpand: false, + hpack: 'center', + vpack: 'center', + child: rev, + }), + }); + + const label = Label('Enter password:'); + + return new Gtk.Window({ + child: visible ? + Box({ + vertical: true, + vpack: 'center', + hpack: 'center', + spacing: 16, + + children: [ + Clock(), + + Separator(CLOCK_SPACING, { vertical: true }), + + Box({ + hpack: 'center', + class_name: 'avatar', + }), + + Box({ + class_name: 'entry-box', + vertical: true, + children: [ + label, + + Separator(ENTRY_SPACING, { vertical: true }), + + Entry({ + hpack: 'center', + xalign: 0.5, + visibility: false, + placeholder_text: 'password', + + on_accept: (self) => { + self.sensitive = false; + + Utils.authenticate(self.text ?? '') + .then(() => unlock()) + .catch((e) => { + self.text = ''; + label.label = e.message; + self.sensitive = true; + }); + }, + }).on('realize', (entry) => entry.grab_focus()), + ], + }), + ], + }) : + Box(), + }); +}; + +const createWindow = (monitor: Gdk.Monitor) => { + const hyprDesc = get_hyprland_monitor_desc(monitor); + const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen; + const win = PasswordPrompt(monitor, entryVisible); + + windows.set(monitor, win); +}; + +const lock_screen = () => { + const display = Gdk.Display.get_default(); + + for (let m = 0; m < (display?.get_n_monitors() ?? 0); m++) { + const monitor = display?.get_monitor(m); + + if (monitor) { + createWindow(monitor); + } + } + display?.connect('monitor-added', (_, monitor) => { + createWindow(monitor); + }); + lock.lock_lock(); + windows.forEach((win, monitor) => { + lock.new_surface(win, monitor); + win.show(); + }); +}; + +const on_finished = () => { + lock.destroy(); + Gdk.Display.get_default()?.sync(); + App.quit(); +}; + +lock.connect('finished', on_finished); + +if (Vars.hasFprintd) { + globalThis.authFinger = () => Utils.authenticate('') + .then(() => unlock()) + .catch(logError); + globalThis.authFinger(); +} + + +export default () => lock_screen(); diff --git a/modules/ags/config/ts/lockscreen/main.ts b/modules/ags/config/ts/lockscreen/main.ts index 5c57b50e..629fe150 100644 --- a/modules/ags/config/ts/lockscreen/main.ts +++ b/modules/ags/config/ts/lockscreen/main.ts @@ -1,211 +1,4 @@ -const { Box, Entry, Label, Window } = Widget; - -import Gdk from 'gi://Gdk?version=3.0'; -import Gtk from 'gi://Gtk?version=3.0'; -import Lock from 'gi://GtkSessionLock?version=0.1'; - -// This file is generated by Nix -import Vars from './vars.ts'; - -import Separator from '../misc/separator.ts'; -import { get_hyprland_monitor_desc } from '../lib.ts'; - -/* Types */ -import { Box as AgsBox } from 'types/widgets/box'; +import LaunchLockscreen from './lock.ts'; -const lock = Lock.prepare_lock(); -const windows: Map = new Map(); -const blurBGs: AgsBox[] = []; - -const transition_duration = 1000; -const WINDOW_MARGINS = -2; -const ENTRY_SPACING = 20; -const CLOCK_SPACING = 60; - -const bgCSS = ({ w = 1, h = 1 } = {}) => ` - border: 2px solid rgba(189, 147, 249, 0.8); - background: rgba(0, 0, 0, 0.2); - min-height: ${h}px; - min-width: ${w}px; - transition: min-height ${transition_duration / 2}ms, - min-width ${transition_duration / 2}ms; -`; - -const unlock = () => { - blurBGs.forEach((b) => { - b.css = bgCSS({ - w: b.attribute.geometry.w, - h: 1, - }); - - Utils.timeout(transition_duration / 2, () => { - b.css = bgCSS({ - w: 1, - h: 1, - }); - }); - }); - Utils.timeout(transition_duration, () => { - lock.unlock_and_destroy(); - Gdk.Display.get_default()?.sync(); - App.quit(); - }); -}; - -const Clock = () => Label({ class_name: 'clock' }) - .poll(1000, (self) => { - self.label = (new Date().toLocaleString([], { - hour: 'numeric', - minute: 'numeric', - hour12: true, - }) ?? '') - .replace('a.m.', 'AM') - .replace('p.m.', 'PM'); - }); - -const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => { - const rev = Box({ - css: bgCSS(), - attribute: { - geometry: {} as { w: number, h: number }, - }, - - setup: (self) => Utils.idle(() => { - self.attribute.geometry = { - w: monitor.geometry.width, - h: monitor.geometry.height, - }; - - self.css = bgCSS({ - w: self.attribute.geometry.w, - h: 1, - }); - - Utils.timeout(transition_duration / 2, () => { - self.css = bgCSS({ - w: self.attribute.geometry.w, - h: self.attribute.geometry.h, - }); - }); - }), - }); - - blurBGs.push(rev); - - Window({ - name: `blur-bg-${monitor.get_model()}`, - gdkmonitor: monitor, - layer: 'overlay', - anchor: ['top', 'bottom', 'right', 'left'], - margins: [WINDOW_MARGINS], - exclusivity: 'ignore', - - child: Box({ - hexpand: false, - vexpand: false, - hpack: 'center', - vpack: 'center', - child: rev, - }), - }); - - const label = Label('Enter password:'); - - return new Gtk.Window({ - child: visible ? - Box({ - vertical: true, - vpack: 'center', - hpack: 'center', - spacing: 16, - - children: [ - Clock(), - - Separator(CLOCK_SPACING, { vertical: true }), - - Box({ - hpack: 'center', - class_name: 'avatar', - }), - - Box({ - class_name: 'entry-box', - vertical: true, - children: [ - label, - - Separator(ENTRY_SPACING, { vertical: true }), - - Entry({ - hpack: 'center', - xalign: 0.5, - visibility: false, - placeholder_text: 'password', - - on_accept: (self) => { - self.sensitive = false; - - Utils.authenticate(self.text ?? '') - .then(() => unlock()) - .catch((e) => { - self.text = ''; - label.label = e.message; - self.sensitive = true; - }); - }, - }).on('realize', (entry) => entry.grab_focus()), - ], - }), - ], - }) : - Box(), - }); -}; - -const createWindow = (monitor: Gdk.Monitor) => { - const hyprDesc = get_hyprland_monitor_desc(monitor); - const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen; - const win = PasswordPrompt(monitor, entryVisible); - - windows.set(monitor, win); -}; - -const lock_screen = () => { - const display = Gdk.Display.get_default(); - - for (let m = 0; m < (display?.get_n_monitors() ?? 0); m++) { - const monitor = display?.get_monitor(m); - - if (monitor) { - createWindow(monitor); - } - } - display?.connect('monitor-added', (_, monitor) => { - createWindow(monitor); - }); - lock.lock_lock(); - windows.forEach((win, monitor) => { - lock.new_surface(win, monitor); - win.show(); - }); -}; - -const on_finished = () => { - lock.destroy(); - Gdk.Display.get_default()?.sync(); - App.quit(); -}; - -lock.connect('finished', on_finished); - -if (Vars.hasFprintd) { - globalThis.authFinger = () => Utils.authenticate('') - .then(() => unlock()) - .catch(logError); - globalThis.authFinger(); -} - - -export default () => lock_screen(); +LaunchLockscreen();