diff --git a/devices/wim/config/ags/.eslintrc.json b/devices/wim/config/ags/.eslintrc.json index 0521fd2f..e6663314 100644 --- a/devices/wim/config/ags/.eslintrc.json +++ b/devices/wim/config/ags/.eslintrc.json @@ -3,12 +3,13 @@ "es2021": true }, "extends": "eslint:recommended", + "parser": "@typescript-eslint/parser", "overrides": [], "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, - "plugins": ["@stylistic"], + "plugins": ["@stylistic", "@typescript-eslint"], "rules": { "array-callback-return": ["error", { "allowImplicit": true, @@ -76,6 +77,8 @@ "prefer-regex-literals": ["error"], "prefer-template": ["warn"], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "warn", "@stylistic/array-bracket-newline": ["warn", "consistent"], "@stylistic/array-bracket-spacing": ["warn", "never"], diff --git a/devices/wim/config/ags/config.js b/devices/wim/config/ags/config.js index 0ef29d4c..686e7dfe 100644 --- a/devices/wim/config/ags/config.js +++ b/devices/wim/config/ags/config.js @@ -1,25 +1,57 @@ -import { watchAndCompileSass } from './js/utils.js'; -import windows from './js/main.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import { execAsync, monitorFile } from 'resource:///com/github/Aylur/ags/utils.js'; + + +const watchAndCompileSass = () => { + const reloadCss = () => { + const scss = `${App.configDir}/scss/main.scss`; + const css = `${App.configDir}/style.css`; + + execAsync(`sassc ${scss} ${css}`).then(() => { + App.resetCss(); + App.applyCss(css); + }); + }; + + monitorFile( + `${App.configDir}/scss`, + reloadCss, + 'directory', + ); + reloadCss(); +}; + +const transpileTypeScript = async() => { + const dir = '/tmp/ags'; + const promises = []; + const files = (await execAsync([ + 'find', `${App.configDir}/`, + '-wholename', '*services/*.ts', + '-o', + '-wholename', '*/ts/*.ts', + ])).split('\n'); + + /** @param {string} p */ + const getDirectoryPath = (p) => p.substring(0, p.lastIndexOf('/')); + + files.forEach((file) => { + const outDir = getDirectoryPath(dir + file + .replace(`${App.configDir}/ts`, '/js') + .replace(`${App.configDir}/services`, '/services')); + + promises.push( + execAsync([ + 'bun', 'build', file, + '--outdir', outDir, + '--external', '*', + ]).catch(print), + ); + }); + + await Promise.all(promises); + + return await import(`file://${dir}/js/main.js`); +}; watchAndCompileSass(); - -const closeWinDelay = 800; - - -export default { - notificationPopupTimeout: 5000, - cacheNotificationActions: true, - closeWindowDelay: { - 'applauncher': closeWinDelay, - 'calendar': closeWinDelay, - 'notification-center': closeWinDelay, - 'osd': 300, - 'osk': closeWinDelay, - 'overview': closeWinDelay, - 'powermenu': closeWinDelay, - 'quick-settings': closeWinDelay, - }, - windows: [ - ...windows, - ], -}; +export default (await transpileTypeScript()).default; diff --git a/devices/wim/config/ags/js/main.js b/devices/wim/config/ags/js/main.js deleted file mode 100644 index 4bdac902..00000000 --- a/devices/wim/config/ags/js/main.js +++ /dev/null @@ -1,33 +0,0 @@ -import Setup from './setup.js'; -import AppLauncher from './applauncher/main.js'; -import Bar from './bar/main.js'; -import BgFade from './misc/background-fade.js'; -import Calendar from './date.js'; -import Corners from './corners/main.js'; -import { NotifPopups, NotifCenter } from './notifications/main.js'; -import OSD from './osd/main.js'; -import OSK from './on-screen-keyboard/main.js'; -import Overview from './overview/main.js'; -import Powermenu from './powermenu.js'; -import QSettings from './quick-settings/main.js'; - -Setup(); - -export default [ - // Put the corners first so they - // don't block the cursor on the bar - ...Corners(), - - AppLauncher(), - Calendar(), - NotifCenter(), - OSD(), - OSK(), - Overview(), - Powermenu(), - QSettings(), - - Bar(), - BgFade(), - NotifPopups(), -]; diff --git a/devices/wim/config/ags/js/media-player/player.js b/devices/wim/config/ags/js/media-player/player.js deleted file mode 100644 index 71c1e0f7..00000000 --- a/devices/wim/config/ags/js/media-player/player.js +++ /dev/null @@ -1,230 +0,0 @@ -import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; -import Variable from 'resource:///com/github/Aylur/ags/variable.js'; - -import { Box, CenterBox } from 'resource:///com/github/Aylur/ags/widget.js'; - -import * as mpris from './mpris.js'; -import PlayerGesture from './gesture.js'; -import Separator from '../misc/separator.js'; - -const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; -const SPACING = 8; - -/** - * @typedef {import('types/service/mpris').MprisPlayer} Player - * @typedef {import('types/widgets/overlay').default} Overlay - * @typedef {import('types/variable').Variable} Variable - */ - - -/** - * @param {Player} player - * @param {Overlay} overlay - */ -const Top = (player, overlay) => Box({ - class_name: 'top', - hpack: 'start', - vpack: 'start', - - children: [ - mpris.PlayerIcon(player, overlay), - ], -}); - -/** - * @param {Player} player - * @param {Variable} colors - */ -const Center = (player, colors) => Box({ - class_name: 'center', - - children: [ - CenterBox({ - // @ts-expect-error - vertical: true, - - children: [ - Box({ - class_name: 'metadata', - vertical: true, - hpack: 'start', - vpack: 'center', - hexpand: true, - - children: [ - mpris.TitleLabel(player), - mpris.ArtistLabel(player), - ], - }), - null, - null, - ], - }), - - CenterBox({ - // @ts-expect-error - vertical: true, - - children: [ - null, - mpris.PlayPauseButton(player, colors), - null, - ], - }), - - ], -}); - -/** - * @param {Player} player - * @param {Variable} colors - */ -const Bottom = (player, colors) => Box({ - class_name: 'bottom', - - children: [ - mpris.PreviousButton(player, colors), - Separator(SPACING), - - mpris.PositionSlider(player, colors), - Separator(SPACING), - - mpris.NextButton(player, colors), - Separator(SPACING), - - mpris.ShuffleButton(player, colors), - Separator(SPACING), - - mpris.LoopButton(player, colors), - ], -}); - -/** - * @param {Player} player - * @param {Variable} colors - * @param {Overlay} overlay - */ -const PlayerBox = (player, colors, overlay) => { - const widget = mpris.CoverArt(player, colors, { - class_name: `player ${player.name}`, - hexpand: true, - - start_widget: Top(player, overlay), - center_widget: Center(player, colors), - end_widget: Bottom(player, colors), - }); - - widget.visible = false; - - return widget; -}; - -export default () => { - const content = PlayerGesture({ - attribute: { - players: new Map(), - setup: false, - }, - - setup: /** @param {any} self */ (self) => { - self - .hook(Mpris, - /** - * @param {Overlay} overlay - * @param {string} bus_name - */ - (overlay, bus_name) => { - const players = overlay.attribute.players; - - if (players.has(bus_name)) { - return; - } - - // Sometimes the signal doesn't give the bus_name - if (!bus_name) { - const player = Mpris.players.find((p) => { - return !players.has(p.bus_name); - }); - - if (player) { - bus_name = player.bus_name; - } - else { - return; - } - } - - // Get the one on top so we can move it up later - const previousFirst = overlay.attribute.list().at(-1); - - // Make the new player - const player = Mpris.getPlayer(bus_name); - const Colors = Variable(null); - - if (!player) { - return; - } - - players.set( - bus_name, - // @ts-expect-error - PlayerBox(player, Colors, content.child), - ); - overlay.overlays = Array.from(players.values()) - .map((widget) => widget); - - const includes = overlay.attribute - .includesWidget(previousFirst); - - // Select favorite player at startup - const attrs = overlay.attribute; - - if (!attrs.setup && players.has(FAVE_PLAYER)) { - attrs.moveToTop(players.get(FAVE_PLAYER)); - attrs.setup = true; - } - - // Move previousFirst on top again - else if (includes) { - attrs.moveToTop(previousFirst); - } - }, - 'player-added') - - .hook(Mpris, - /** - * @param {Overlay} overlay - * @param {string} bus_name - */ - (overlay, bus_name) => { - const players = overlay.attribute.players; - - if (!bus_name || !players.has(bus_name)) { - return; - } - - // Get the one on top so we can move it up later - const previousFirst = overlay.attribute.list().at(-1); - - // Remake overlays without deleted one - players.delete(bus_name); - overlay.overlays = Array.from(players.values()) - .map((widget) => widget); - - // Move previousFirst on top again - const includes = overlay.attribute - .includesWidget(previousFirst); - - if (includes) { - overlay.attribute.moveToTop(previousFirst); - } - }, - 'player-closed'); - }, - }); - - return Box({ - class_name: 'media', - child: content, - }); -}; diff --git a/devices/wim/config/ags/js/misc/revealer-window.js b/devices/wim/config/ags/js/misc/revealer-window.js deleted file mode 100644 index 273bccac..00000000 --- a/devices/wim/config/ags/js/misc/revealer-window.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * This is the old version of my popup windows. - * I don't use it anymore but I will keep it in - * case my new one breaks or simply as an example. - */ - -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; - -import { Revealer, Box, Window } from 'resource:///com/github/Aylur/ags/widget.js'; -import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; - -/** - * @typedef {import('types/widgets/revealer').RevealerProps} RevProps - * @typedef {import('types/widgets/window').WindowProps} WinProps - * @typedef {import('gi://Gtk').Gtk.Widget} Widget - */ - - -/** - * @param {WinProps & { - * transition?: RevProps['transition'] - * transition_duration?: RevProps['transition_duration'] - * on_open?: function - * on_close?: function - * blur?: boolean - * }} o - */ -export default ({ - transition = 'slide_down', - transition_duration = 800, - on_open = () => {/**/}, - on_close = () => {/**/}, - - // Window props - name, - child = Box(), - visible = false, - layer = 'overlay', - blur = false, - ...props -}) => { - const window = Window({ - name, - layer, - visible: false, - ...props, - - attribute: { - // @ts-expect-error - get_child: () => window.child.children[0].child, - - /** @param {Widget} new_child */ - set_child: (new_child) => { - // @ts-expect-error - window.child.children[0].child = new_child; - // @ts-expect-error - window.child.children[0].show_all(); - }, - }, - - setup: () => { - // Add way to make window open on startup - const id = App.connect('config-parsed', () => { - if (visible) { - App.openWindow(String(name)); - } - App.disconnect(id); - }); - - if (blur) { - Hyprland.sendMessage('[[BATCH]] ' + - `keyword layerrule ignorealpha[0.97],${name}; ` + - `keyword layerrule blur,${name}`); - } - }, - - // Wrapping the revealer inside a box is needed - // to allocate some space for it even when not revealed - child: Box({ - css: ` - min-height:1px; - min-width:1px; - padding: 1px; - `, - child: Revealer({ - transition, - transition_duration, - child, - - setup: (self) => { - self.hook(App, (_, currentName, isOpen) => { - if (currentName === name) { - self.reveal_child = isOpen; - - if (isOpen) { - on_open(window); - } - else { - timeout(Number(transition_duration), () => { - on_close(window); - }); - } - } - }); - }, - }), - }), - }); - - return window; -}; diff --git a/devices/wim/config/ags/js/quick-settings/toggle-button.js b/devices/wim/config/ags/js/quick-settings/toggle-button.js deleted file mode 100644 index 991b2051..00000000 --- a/devices/wim/config/ags/js/quick-settings/toggle-button.js +++ /dev/null @@ -1,58 +0,0 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; - -import { CenterBox, Icon, ToggleButton } from 'resource:///com/github/Aylur/ags/widget.js'; - -const { Gdk } = imports.gi; -const display = Gdk.Display.get_default(); - - -/** @param {import('types/widgets/revealer').default} rev */ -export default (rev) => CenterBox({ - center_widget: ToggleButton({ - setup: (self) => { - // Open at startup if there are players - const id = Mpris.connect('changed', () => { - self.set_active(Mpris.players.length > 0); - Mpris.disconnect(id); - }); - - self - .on('toggled', () => { - if (self.get_active()) { - self.child - // @ts-expect-error - ?.setCss('-gtk-icon-transform: rotate(0deg);'); - rev.reveal_child = true; - } - else { - self.child - // @ts-expect-error - ?.setCss('-gtk-icon-transform: rotate(180deg);'); - rev.reveal_child = false; - } - }) - - // OnHover - .on('enter-notify-event', () => { - self.window.set_cursor(Gdk.Cursor.new_from_name( - display, - 'pointer', - )); - self.toggleClassName('hover', true); - }) - - // OnHoverLost - .on('leave-notify-event', () => { - self.window.set_cursor(null); - self.toggleClassName('hover', false); - }); - }, - - child: Icon({ - icon: `${App.configDir}/icons/down-large.svg`, - class_name: 'arrow', - css: '-gtk-icon-transform: rotate(180deg);', - }), - }), -}); diff --git a/devices/wim/config/ags/js/utils.js b/devices/wim/config/ags/js/utils.js deleted file mode 100644 index 5df98741..00000000 --- a/devices/wim/config/ags/js/utils.js +++ /dev/null @@ -1,36 +0,0 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; -import { monitorFile, exec } from 'resource:///com/github/Aylur/ags/utils.js'; - - -export const watchAndCompileSass = () => { - const reloadCss = () => { - // Main scss file - const scss = `${App.configDir}/scss/main.scss`; - - // Target css file - const css = `${App.configDir}/style.css`; - - // Compile, reset, apply - exec(`sassc ${scss} ${css}`); - App.resetCss(); - App.applyCss(css); - }; - - monitorFile( - // Directory that contains the scss files - `${App.configDir}/scss`, - - reloadCss, - - // Specify that its a directory - 'directory', - ); - reloadCss(); -}; - -export const compileTypescript = () => { - const ts = `${App.configDir}/ts/main.ts`; - const js = `${App.configDir}/compiled.js`; - - exec(`bash -c 'cd ${App.configDir} && nix develop && bun install && tsc ${ts} --outfile ${js}'`); -}; diff --git a/devices/wim/config/ags/js/webkit.js b/devices/wim/config/ags/js/webkit.js deleted file mode 100644 index 78de0b27..00000000 --- a/devices/wim/config/ags/js/webkit.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Widget, Box } from 'resource:///com/github/Aylur/ags/widget.js'; -import WebKit2 from 'gi://WebKit2'; -import PopupWindow from './misc/popup.js'; - -const WebView = Widget.subclass(WebKit2.WebView); - - -export default () => { - const view = WebView({ - hexpand: true, - }); - - view.load_uri('https://search.nixos.org'); - - return PopupWindow({ - name: 'browser', - visible: true, - focusable: true, - layer: 'top', - child: Box({ - css: 'min-height: 600px; min-width: 800px;', - children: [view], - }), - }); -}; diff --git a/devices/wim/config/ags/services/brightness.js b/devices/wim/config/ags/services/brightness.ts similarity index 100% rename from devices/wim/config/ags/services/brightness.js rename to devices/wim/config/ags/services/brightness.ts diff --git a/devices/wim/config/ags/services/pointers.js b/devices/wim/config/ags/services/pointers.ts similarity index 52% rename from devices/wim/config/ags/services/pointers.js rename to devices/wim/config/ags/services/pointers.ts index 7750a74d..0c2bf39b 100644 --- a/devices/wim/config/ags/services/pointers.js +++ b/devices/wim/config/ags/services/pointers.ts @@ -4,15 +4,6 @@ import Service from 'resource:///com/github/Aylur/ags/service.js'; import { subprocess } from 'resource:///com/github/Aylur/ags/utils.js'; -const { GUdev } = imports.gi; - -const UDEV_POINTERS = [ - 'ID_INPUT_MOUSE', - 'ID_INPUT_POINTINGSTICK', - 'ID_INPUT_TOUCHPAD', - 'ID_INPUT_TOUCHSCREEN', - 'ID_INPUT_TABLET', -]; const ON_RELEASE_TRIGGERS = [ 'released', 'TOUCH_UP', @@ -23,6 +14,31 @@ const ON_CLICK_TRIGGERS = [ 'TOUCH_DOWN', ]; +// Types +import AgsWindow from 'types/widgets/window'; +type Subprocess = typeof imports.gi.Gio.Subprocess; +type Layer = { + address: string; + x: number; + y: number; + w: number; + h: number; + namespace: string; +}; +type Levels = { + 0?: Array | null; + 1?: Array | null; + 2?: Array | null; + 3?: Array | null; +}; +type Layers = { + levels: Levels; +}; +type CursorPos = { + x: number; + y: number; +}; + class Pointers extends Service { static { @@ -36,12 +52,9 @@ class Pointers extends Service { }); } - /** @type typeof imports.gi.Gio.Subprocess */ - #process; + #process: Subprocess; #lastLine = ''; - /** @type Array */ - #pointers = []; - #udevClient = GUdev.Client.new(['input']); + #pointers = [] as Array; get process() { return this.#process; @@ -57,67 +70,16 @@ class Pointers extends Service { constructor() { super(); - this.#initUdevConnection(); this.#initAppConnection(); } - // FIXME: logitech mouse screws everything up on disconnect - #initUdevConnection() { - this.#getDevices(); - this.#udevClient.connect('uevent', - /** - * @param {typeof imports.gi.GUdev.Client} _ - * @param {string} action - */ - (_, action) => { - if (action === 'add' || action === 'remove') { - this.#getDevices(); - if (this.#process) { - this.killProc(); - this.startProc(); - } - } - }); - } - - #getDevices() { - this.#pointers = []; - this.#udevClient.query_by_subsystem('input').forEach( - /** @param {typeof imports.gi.GUdev.Device} dev */ - (dev) => { - const isPointer = UDEV_POINTERS.some( - (p) => dev.has_property(p), - ); - - if (isPointer) { - const hasEventFile = dev.has_property('DEVNAME') && - dev.get_property('DEVNAME') - .includes('event'); - - if (hasEventFile) { - this.#pointers.push(dev.get_property('DEVNAME')); - } - } - }, - ); - - this.emit('device-fetched', true); - } - startProc() { if (this.#process) { return; } - const args = []; - - this.#pointers.forEach((dev) => { - args.push('--device'); - args.push(dev); - }); - this.#process = subprocess( - ['libinput', 'debug-events', ...args], + ['libinput', 'debug-events'], (output) => { if (output.includes('cancelled')) { return; @@ -151,16 +113,15 @@ class Pointers extends Service { #initAppConnection() { App.connect('window-toggled', () => { - const anyVisibleAndClosable = Array.from(App.windows).some((w) => { - // @ts-expect-error - const closable = w[1].attribute?.close_on_unfocus && - // @ts-expect-error + const anyVisibleAndClosable = + (Array.from(App.windows) as Array<[string, AgsWindow]>) + .some((w) => { + const closable = w[1].attribute?.close_on_unfocus && !(w[1].attribute?.close_on_unfocus === 'none' || - // @ts-expect-error w[1].attribute?.close_on_unfocus === 'stay'); - return w[1].visible && closable; - }); + return w[1].visible && closable; + }); if (anyVisibleAndClosable) { this.startProc(); @@ -172,42 +133,38 @@ class Pointers extends Service { }); } - /** @param {string} clickStage */ - static detectClickedOutside(clickStage) { - const toClose = Array.from(App.windows).some((w) => { - // @ts-expect-error - const closable = (w[1].attribute?.close_on_unfocus && - // @ts-expect-error + static detectClickedOutside(clickStage: string) { + const toClose = (Array.from(App.windows) as Array<[string, AgsWindow]>) + .some((w) => { + const closable = (w[1].attribute?.close_on_unfocus && w[1].attribute?.close_on_unfocus === clickStage); - return w[1].visible && closable; - }); + return w[1].visible && closable; + }); if (!toClose) { return; } Hyprland.sendMessage('j/layers').then((response) => { - // /** @type import('types/service/hyprland').Layer */ - const layers = JSON.parse(response); + const layers = JSON.parse(response) as { Layers: Layers }; Hyprland.sendMessage('j/cursorpos').then((res) => { - const pos = JSON.parse(res); + const pos = JSON.parse(res) as CursorPos; Object.values(layers).forEach((key) => { - const bar = key.levels['3'].find( - /** @param {{ namespace: string }} n */ + const bar = key.levels['3']?.find( (n) => n.namespace === 'bar', - ); + ) || + // Return an empty Layer if bar doesn't exist + { address: '', x: 0, y: 0, w: 0, h: 0, namespace: '' }; - const widgets = key.levels['3'].filter( - /** @param {{ namespace: string }} n */ + const widgets = key.levels['3']?.filter( (n) => { - const window = App.getWindow(n.namespace); + const window = + (App.getWindow(n.namespace) as AgsWindow); - // @ts-expect-error return window?.attribute?.close_on_unfocus && - // @ts-expect-error window?.attribute ?.close_on_unfocus === clickStage; }, @@ -220,15 +177,7 @@ class Pointers extends Service { // TODO: make this configurable } else { - widgets.forEach( - /** @param {{ - * namespace: string - * x: number - * y: number - * h: number - * w: number - * }} w - */ + widgets?.forEach( (w) => { if (!(pos.x > w.x && pos.x < w.x + w.w && pos.y > w.y && pos.y < w.y + w.h)) { diff --git a/devices/wim/config/ags/services/tablet.js b/devices/wim/config/ags/services/tablet.ts similarity index 94% rename from devices/wim/config/ags/services/tablet.js rename to devices/wim/config/ags/services/tablet.ts index 5b26daa3..095f7dc4 100644 --- a/devices/wim/config/ags/services/tablet.js +++ b/devices/wim/config/ags/services/tablet.ts @@ -15,6 +15,9 @@ const DEVICES = [ 'wacom-hid-52eb-pen', ]; +// Types +type Subprocess = typeof imports.gi.Gio.Subprocess; + class Tablet extends Service { static { @@ -33,10 +36,8 @@ class Tablet extends Service { #tabletMode = false; #oskState = false; - /** @type typeof imports.gi.Gio.Subprocess */ - #autorotate; - /** @type typeof imports.gi.Gio.Subprocess */ - #blockedInputs; + #autorotate: Subprocess; + #blockedInputs: Subprocess; get tabletMode() { return this.#tabletMode; @@ -124,7 +125,13 @@ class Tablet extends Service { ['monitor-sensor'], (output) => { if (output.includes('orientation changed')) { - const orientation = ROTATION_MAP[output.split(' ').at(-1)]; + const index = output.split(' ').at(-1); + + if (!index) { + return; + } + + const orientation = ROTATION_MAP[index]; Hyprland.sendMessage( `keyword monitor ${SCREEN},transform,${orientation}`, diff --git a/devices/wim/config/ags/services/touch-gestures.js b/devices/wim/config/ags/services/touch-gestures.ts similarity index 97% rename from devices/wim/config/ags/services/touch-gestures.js rename to devices/wim/config/ags/services/touch-gestures.ts index cb029b3e..205ecd95 100644 --- a/devices/wim/config/ags/services/touch-gestures.js +++ b/devices/wim/config/ags/services/touch-gestures.ts @@ -32,6 +32,9 @@ const DISTANCE_VERIF = [ 'L', // Large ]; +// Types +type Subprocess = typeof imports.gi.Gio.Subprocess; + // TODO: add actmode param // TODO: support multiple daemons for different thresholds @@ -44,8 +47,7 @@ class TouchGestures extends Service { } #gestures = new Map(); - /** @type typeof imports.gi.Gio.Subprocess */ - #gestureDaemon; + #gestureDaemon: Subprocess; get gestures() { return this.#gestures; diff --git a/devices/wim/config/ags/js/applauncher/app-item.js b/devices/wim/config/ags/ts/applauncher/app-item.ts similarity index 93% rename from devices/wim/config/ags/js/applauncher/app-item.js rename to devices/wim/config/ags/ts/applauncher/app-item.ts index 8fb8f37d..365bdb32 100644 --- a/devices/wim/config/ags/js/applauncher/app-item.js +++ b/devices/wim/config/ags/ts/applauncher/app-item.ts @@ -5,9 +5,11 @@ import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js'; import CursorBox from '../misc/cursorbox.js'; +// Types +import { Application } from 'types/service/applications.js'; -/** @param {import('types/service/applications.js').Application} app */ -export default (app) => { + +export default (app: Application) => { const icon = Icon({ size: 42 }); const iconString = app.app.get_string('Icon'); diff --git a/devices/wim/config/ags/js/applauncher/main.js b/devices/wim/config/ags/ts/applauncher/main.ts similarity index 80% rename from devices/wim/config/ags/js/applauncher/main.js rename to devices/wim/config/ags/ts/applauncher/main.ts index 5bb94aec..b252c7a4 100644 --- a/devices/wim/config/ags/js/applauncher/main.js +++ b/devices/wim/config/ags/ts/applauncher/main.ts @@ -1,46 +1,36 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; -// TODO: find cleaner way to import this -import { Fzf } from '../../node_modules/fzf/dist/fzf.es.js'; +// FIXME: find cleaner way to import this +// @ts-expect-error +import { Fzf } from 'file:///home/matt/.config/ags/node_modules/fzf/dist/fzf.es.js'; import { Box, Entry, Icon, Label, ListBox, Revealer, Scrollable } from 'resource:///com/github/Aylur/ags/widget.js'; import PopupWindow from '../misc/popup.js'; import AppItem from './app-item.js'; -/** - * @typedef {import('types/service/applications.js').Application} App - * @typedef {typeof imports.gi.Gtk.ListBoxRow} ListBoxRow - */ +// Types +import { Application } from 'types/service/applications.js'; +type ListBoxRow = typeof imports.gi.Gtk.ListBoxRow; const Applauncher = (window_name = 'applauncher') => { - /** @type Array */ - let fzfResults; + let fzfResults: Array; const list = ListBox({}); - /** @param {String} text */ - const setSort = (text) => { + const setSort = (text: string) => { const fzf = new Fzf(Applications.list, { - selector: /** @param {App} app */ (app) => { + selector: (app: Application) => { return app.name + app.executable; }, tiebreakers: [ - /** - * @param {App} a - * @param {App} b - */ - (a, b) => b.frequency - a.frequency, + (a: Application, b: Application) => b.frequency - a.frequency, ], }); fzfResults = fzf.find(text); list.set_sort_func( - /** - * @param {ListBoxRow} a - * @param {ListBoxRow} b - */ - (a, b) => { + (a: ListBoxRow, b: ListBoxRow) => { const row1 = a.get_children()[0]?.attribute.app.name; const row2 = b.get_children()[0]?.attribute.app.name; @@ -55,8 +45,7 @@ const Applauncher = (window_name = 'applauncher') => { }; const makeNewChildren = () => { - /** @type Array */ - const rows = list.get_children(); + const rows = list.get_children() as Array; rows.forEach((ch) => { ch.destroy(); @@ -101,8 +90,7 @@ const Applauncher = (window_name = 'applauncher') => { setSort(text); let visibleApps = 0; - /** @type Array */ - const rows = list.get_children(); + const rows = list.get_children() as Array; rows.forEach((row) => { row.changed(); diff --git a/devices/wim/config/ags/js/bar/fullscreen.js b/devices/wim/config/ags/ts/bar/fullscreen.ts similarity index 83% rename from devices/wim/config/ags/js/bar/fullscreen.js rename to devices/wim/config/ags/ts/bar/fullscreen.ts index 1114fb1a..84f3e56f 100644 --- a/devices/wim/config/ags/js/bar/fullscreen.js +++ b/devices/wim/config/ags/ts/bar/fullscreen.ts @@ -3,9 +3,12 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { Box, EventBox, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js'; +// Types +import { Variable as Var } from 'types/variable'; +import AgsBox from 'types/widgets/box'; +import { RevealerProps } from 'types/widgets/revealer'; -/** @param {import('types/variable.js').Variable} variable */ -const BarCloser = (variable) => Window({ +const BarCloser = (variable: Var) => Window({ name: 'bar-closer', visible: false, anchor: ['top', 'bottom', 'left', 'right'], @@ -27,8 +30,7 @@ const BarCloser = (variable) => Window({ }), }); -/** @param {import('types/widgets/revealer').RevealerProps} props */ -export default (props) => { +export default (props: RevealerProps) => { const Revealed = Variable(true); const barCloser = BarCloser(Revealed); @@ -48,11 +50,7 @@ export default (props) => { } }; - /** - * @param {import('types/widgets/box').default} _ - * @param {boolean} fullscreen - */ - const checkGlobalFsState = (_, fullscreen) => { + const checkGlobalFsState = (_: AgsBox, fullscreen: boolean) => { Revealed.value = !fullscreen; }; diff --git a/devices/wim/config/ags/js/bar/hovers/audio.js b/devices/wim/config/ags/ts/bar/hovers/audio.ts similarity index 100% rename from devices/wim/config/ags/js/bar/hovers/audio.js rename to devices/wim/config/ags/ts/bar/hovers/audio.ts diff --git a/devices/wim/config/ags/js/bar/hovers/bluetooth.js b/devices/wim/config/ags/ts/bar/hovers/bluetooth.ts similarity index 100% rename from devices/wim/config/ags/js/bar/hovers/bluetooth.js rename to devices/wim/config/ags/ts/bar/hovers/bluetooth.ts diff --git a/devices/wim/config/ags/js/bar/hovers/brightness.js b/devices/wim/config/ags/ts/bar/hovers/brightness.ts similarity index 100% rename from devices/wim/config/ags/js/bar/hovers/brightness.js rename to devices/wim/config/ags/ts/bar/hovers/brightness.ts diff --git a/devices/wim/config/ags/js/bar/hovers/hover-revealer.js b/devices/wim/config/ags/ts/bar/hovers/hover-revealer.ts similarity index 100% rename from devices/wim/config/ags/js/bar/hovers/hover-revealer.js rename to devices/wim/config/ags/ts/bar/hovers/hover-revealer.ts diff --git a/devices/wim/config/ags/js/bar/hovers/keyboard-layout.js b/devices/wim/config/ags/ts/bar/hovers/keyboard-layout.ts similarity index 52% rename from devices/wim/config/ags/js/bar/hovers/keyboard-layout.js rename to devices/wim/config/ags/ts/bar/hovers/keyboard-layout.ts index 4694de27..03335712 100644 --- a/devices/wim/config/ags/js/bar/hovers/keyboard-layout.js +++ b/devices/wim/config/ags/ts/bar/hovers/keyboard-layout.ts @@ -6,13 +6,22 @@ import HoverRevealer from './hover-revealer.js'; const DEFAULT_KB = 'at-translated-set-2-keyboard'; +import AgsLabel from 'types/widgets/label.js'; +type Keyboard = { + address: string; + name: string; + rules: string; + model: string; + layout: string; + variant: string; + options: string; + active_keymap: string; + main: boolean; +}; -/** - * @param {import('types/widgets/label').default} self - * @param {string} layout - * @param {string} _ - */ -const getKbdLayout = (self, _, layout) => { + + +const getKbdLayout = (self: AgsLabel, _: string, layout: string) => { if (layout) { if (layout === 'error') { return; @@ -25,15 +34,21 @@ const getKbdLayout = (self, _, layout) => { else { // At launch, kb layout is undefined Hyprland.sendMessage('j/devices').then((obj) => { - const kb = Array.from(JSON.parse(obj).keyboards) - .find((v) => v.name === DEFAULT_KB); + const keyboards = Array.from(JSON.parse(obj) + .keyboards) as Array; + const kb = keyboards.find((v) => v.name === DEFAULT_KB); - layout = kb['active_keymap']; + if (kb) { + layout = kb.active_keymap; - const shortName = layout - .match(/\(([A-Za-z]+)\)/); + const shortName = layout + .match(/\(([A-Za-z]+)\)/); - self.label = shortName ? shortName[1] : layout; + self.label = shortName ? shortName[1] : layout; + } + else { + self.label = 'None'; + } }).catch(print); } }; diff --git a/devices/wim/config/ags/js/bar/hovers/network.js b/devices/wim/config/ags/ts/bar/hovers/network.ts similarity index 100% rename from devices/wim/config/ags/js/bar/hovers/network.js rename to devices/wim/config/ags/ts/bar/hovers/network.ts diff --git a/devices/wim/config/ags/js/bar/items/battery.js b/devices/wim/config/ags/ts/bar/items/battery.ts similarity index 100% rename from devices/wim/config/ags/js/bar/items/battery.js rename to devices/wim/config/ags/ts/bar/items/battery.ts diff --git a/devices/wim/config/ags/js/bar/items/clock.js b/devices/wim/config/ags/ts/bar/items/clock.ts similarity index 100% rename from devices/wim/config/ags/js/bar/items/clock.js rename to devices/wim/config/ags/ts/bar/items/clock.ts diff --git a/devices/wim/config/ags/js/bar/items/current-window.js b/devices/wim/config/ags/ts/bar/items/current-window.ts similarity index 100% rename from devices/wim/config/ags/js/bar/items/current-window.js rename to devices/wim/config/ags/ts/bar/items/current-window.ts diff --git a/devices/wim/config/ags/js/bar/items/heart.js b/devices/wim/config/ags/ts/bar/items/heart.ts similarity index 100% rename from devices/wim/config/ags/js/bar/items/heart.js rename to devices/wim/config/ags/ts/bar/items/heart.ts diff --git a/devices/wim/config/ags/js/bar/items/notif-button.js b/devices/wim/config/ags/ts/bar/items/notif-button.ts similarity index 87% rename from devices/wim/config/ags/js/bar/items/notif-button.js rename to devices/wim/config/ags/ts/bar/items/notif-button.ts index dc5d9ebc..e4e63a6b 100644 --- a/devices/wim/config/ags/js/bar/items/notif-button.js +++ b/devices/wim/config/ags/ts/bar/items/notif-button.ts @@ -8,16 +8,19 @@ import Separator from '../../misc/separator.js'; const SPACING = 4; +// Types +import AgsWindow from 'types/widgets/window.js'; + export default () => CursorBox({ class_name: 'toggle-off', on_primary_click_release: (self) => { - // @ts-expect-error - App.getWindow('notification-center')?.attribute.set_x_pos( - self.get_allocation(), - 'right', - ); + (App.getWindow('notification-center') as AgsWindow) + ?.attribute.set_x_pos( + self.get_allocation(), + 'right', + ); App.toggleWindow('notification-center'); }, diff --git a/devices/wim/config/ags/js/bar/items/osk-toggle.js b/devices/wim/config/ags/ts/bar/items/osk-toggle.ts similarity index 99% rename from devices/wim/config/ags/js/bar/items/osk-toggle.js rename to devices/wim/config/ags/ts/bar/items/osk-toggle.ts index 5182ddff..dd75ab20 100644 --- a/devices/wim/config/ags/js/bar/items/osk-toggle.js +++ b/devices/wim/config/ags/ts/bar/items/osk-toggle.ts @@ -20,5 +20,4 @@ export default () => CursorBox({ xalign: 0.6, label: '󰌌 ', }), - }); diff --git a/devices/wim/config/ags/js/bar/items/quick-settings.js b/devices/wim/config/ags/ts/bar/items/quick-settings.ts similarity index 74% rename from devices/wim/config/ags/js/bar/items/quick-settings.js rename to devices/wim/config/ags/ts/bar/items/quick-settings.ts index 32e375b1..3f7bc4e5 100644 --- a/devices/wim/config/ags/js/bar/items/quick-settings.js +++ b/devices/wim/config/ags/ts/bar/items/quick-settings.ts @@ -13,6 +13,11 @@ import Separator from '../../misc/separator.js'; const SPACING = 4; +// Types +import AgsRevealer from 'types/widgets/revealer.js'; +import AgsBox from 'types/widgets/box.js'; +import AgsWindow from 'types/widgets/window.js'; + export default () => { const hoverRevealers = [ @@ -31,11 +36,11 @@ export default () => { class_name: 'toggle-off', on_primary_click_release: (self) => { - // @ts-expect-error - App.getWindow('quick-settings').attribute.set_x_pos( - self.get_allocation(), - 'right', - ); + (App.getWindow('quick-settings') as AgsWindow) + ?.attribute.set_x_pos( + self.get_allocation(), + 'right', + ); App.toggleWindow('quick-settings'); }, @@ -49,15 +54,15 @@ export default () => { }, attribute: { - hoverRevealers: hoverRevealers.map( - // @ts-expect-error - (rev) => rev.child.children[1], - ), + hoverRevealers: hoverRevealers.map((rev) => { + const box = rev.child as AgsBox; + + return box.children[1]; + }), }, on_hover_lost: (self) => { self.attribute.hoverRevealers.forEach( - /** @param {import('types/widgets/revealer').default} rev */ - (rev) => { + (rev: AgsRevealer) => { rev.reveal_child = false; }, ); diff --git a/devices/wim/config/ags/js/bar/items/systray.js b/devices/wim/config/ags/ts/bar/items/systray.ts similarity index 83% rename from devices/wim/config/ags/js/bar/items/systray.js rename to devices/wim/config/ags/ts/bar/items/systray.ts index 73fe3d2d..7dbd5730 100644 --- a/devices/wim/config/ags/js/bar/items/systray.js +++ b/devices/wim/config/ags/ts/bar/items/systray.ts @@ -8,35 +8,32 @@ import Separator from '../../misc/separator.js'; const REVEAL_DURATION = 500; const SPACING = 12; +// Types +import { TrayItem } from 'types/service/systemtray.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +type Menu = typeof imports.gi.Gtk.Menu; -/** @param {import('types/service/systemtray').TrayItem} item */ -const SysTrayItem = (item) => { + +const SysTrayItem = (item: TrayItem) => { if (item.id === 'spotify-client') { return; } return MenuItem({ - // @ts-expect-error - submenu: item.menu, + submenu: item.menu, tooltip_markup: item.bind('tooltip_markup'), child: Revealer({ transition: 'slide_right', transition_duration: REVEAL_DURATION, - child: Icon({ - size: 24, - // @ts-expect-error - icon: item.bind('icon'), - }), + child: Icon({ size: 24 }).bind('icon', item, 'icon'), }), }); }; const SysTray = () => MenuBar({ - attribute: { - items: new Map(), - }, + attribute: { items: new Map() }, setup: (self) => { self @@ -57,8 +54,8 @@ const SysTray = () => MenuBar({ self.attribute.items.set(id, w); self.child = w; self.show_all(); - // @ts-expect-error - w.child.reveal_child = true; + + ( w.child).reveal_child = true; }, 'added') .hook(SystemTray, (_, id) => { diff --git a/devices/wim/config/ags/js/bar/items/tablet-toggle.js b/devices/wim/config/ags/ts/bar/items/tablet-toggle.ts similarity index 100% rename from devices/wim/config/ags/js/bar/items/tablet-toggle.js rename to devices/wim/config/ags/ts/bar/items/tablet-toggle.ts diff --git a/devices/wim/config/ags/js/bar/items/workspaces.js b/devices/wim/config/ags/ts/bar/items/workspaces.ts similarity index 76% rename from devices/wim/config/ags/js/bar/items/workspaces.js rename to devices/wim/config/ags/ts/bar/items/workspaces.ts index e9851c99..5997f721 100644 --- a/devices/wim/config/ags/js/bar/items/workspaces.js +++ b/devices/wim/config/ags/ts/bar/items/workspaces.ts @@ -7,11 +7,14 @@ import CursorBox from '../../misc/cursorbox.js'; const URGENT_DURATION = 1000; -/** @typedef {import('types/widgets/revealer.js').default} Revealer */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import AgsOverlay from 'types/widgets/overlay.js'; +import AgsEventBox from 'types/widgets/eventbox.js'; -/** @property {number} id */ -const Workspace = ({ id }) => { +const Workspace = ({ id }: { id: number }) => { return Revealer({ transition: 'slide_right', attribute: { id }, @@ -28,11 +31,7 @@ const Workspace = ({ id }) => { class_name: 'button', setup: (self) => { - /** - * @param {import('types/widgets/box').default} _ - * @param {string|undefined} addr - */ - const update = (_, addr) => { + const update = (_: AgsBox, addr: string | undefined) => { const workspace = Hyprland.getWorkspace(id); const occupied = workspace && workspace.windows > 0; @@ -80,13 +79,13 @@ export default () => { const L_PADDING = 16; const WS_WIDTH = 30; - /** @param {import('types/widgets/box').default} self */ - const updateHighlight = (self) => { + const updateHighlight = (self: AgsBox) => { const currentId = Hyprland.active.workspace.id; - /** @type Array */ - // @ts-expect-error - const indicators = self?.get_parent()?.child.child.children; + const indicators = (((self.get_parent() as AgsOverlay) + .child as AgsEventBox) + .child as AgsBox) + .children as Array; const currentIndex = indicators .findIndex((w) => w.attribute.id === currentId); @@ -112,18 +111,14 @@ export default () => { child: Box({ class_name: 'workspaces', - attribute: { - /** @type Array */ - workspaces: [], - }, + attribute: { workspaces: [] }, setup: (self) => { - /** @type function(void):Array */ - const workspaces = () => self.attribute.workspaces; + const workspaces = (): Array => + self.attribute.workspaces; const refresh = () => { - self.children.forEach((rev) => { - // @ts-expect-error they are in fact revealers + (self.children as Array).forEach((rev) => { rev.reveal_child = false; }); @@ -134,10 +129,8 @@ export default () => { const updateWorkspaces = () => { Hyprland.workspaces.forEach((ws) => { - const currentWs = self.children.find((ch) => { - // @ts-expect-error - return ch.attribute.id === ws.id; - }); + const currentWs = (self.children as Array) + .find((ch) => ch.attribute.id === ws.id); if (!currentWs && ws.id > 0) { self.add(Workspace({ id: ws.id })); @@ -147,8 +140,7 @@ export default () => { // Make sure the order is correct workspaces().forEach((workspace, i) => { - // @ts-expect-error - workspace?.get_parent()?.reorder_child( + ( workspace.get_parent()).reorder_child( workspace, i, ); @@ -157,12 +149,10 @@ export default () => { self.hook(Hyprland, () => { self.attribute.workspaces = - self.children.filter((ch) => { + (self.children as Array).filter((ch) => { return Hyprland.workspaces.find((ws) => { - // @ts-expect-error return ws.id === ch.attribute.id; }); - // @ts-expect-error }).sort((a, b) => a.attribute.id - b.attribute.id); updateWorkspaces(); diff --git a/devices/wim/config/ags/js/bar/main.js b/devices/wim/config/ags/ts/bar/main.ts similarity index 100% rename from devices/wim/config/ags/js/bar/main.js rename to devices/wim/config/ags/ts/bar/main.ts diff --git a/devices/wim/config/ags/js/corners/main.js b/devices/wim/config/ags/ts/corners/main.ts similarity index 100% rename from devices/wim/config/ags/js/corners/main.js rename to devices/wim/config/ags/ts/corners/main.ts diff --git a/devices/wim/config/ags/js/corners/screen-corners.js b/devices/wim/config/ags/ts/corners/screen-corners.ts similarity index 92% rename from devices/wim/config/ags/js/corners/screen-corners.js rename to devices/wim/config/ags/ts/corners/screen-corners.ts index fa184fed..831a37da 100644 --- a/devices/wim/config/ags/js/corners/screen-corners.js +++ b/devices/wim/config/ags/ts/corners/screen-corners.ts @@ -1,7 +1,6 @@ import { Box, DrawingArea } from 'resource:///com/github/Aylur/ags/widget.js'; import Gtk from 'gi://Gtk'; -const Lang = imports.lang; export default ( place = 'top left', @@ -27,8 +26,7 @@ export default ( .get_property('border-radius', Gtk.StateFlags.NORMAL); widget.set_size_request(r, r); - // @ts-expect-error - widget.connect('draw', Lang.bind(widget, (_, cr) => { + widget.connect('draw', (_, cr) => { const c = widget.get_style_context() .get_property('background-color', Gtk.StateFlags.NORMAL); @@ -38,7 +36,7 @@ export default ( const borderColor = widget.get_style_context() .get_property('color', Gtk.StateFlags.NORMAL); - // Ur going to write border-width: something anyway + // You're going to write border-width: something anyway const borderWidth = widget.get_style_context() .get_border(Gtk.StateFlags.NORMAL).left; @@ -75,7 +73,7 @@ export default ( borderColor.blue, borderColor.alpha); cr.stroke(); - })); + }); }, }), }); diff --git a/devices/wim/config/ags/js/date.js b/devices/wim/config/ags/ts/date.ts similarity index 100% rename from devices/wim/config/ags/js/date.js rename to devices/wim/config/ags/ts/date.ts diff --git a/devices/wim/config/ags/ts/main.ts b/devices/wim/config/ags/ts/main.ts new file mode 100644 index 00000000..21d4e386 --- /dev/null +++ b/devices/wim/config/ags/ts/main.ts @@ -0,0 +1,50 @@ +import Setup from './setup.js'; +import AppLauncher from './applauncher/main.js'; +import Bar from './bar/main.js'; +import BgFade from './misc/background-fade.js'; +import Calendar from './date.js'; +import Corners from './corners/main.js'; +import { NotifPopups, NotifCenter } from './notifications/main.js'; +import OSD from './osd/main.js'; +import OSK from './on-screen-keyboard/main.js'; +import Overview from './overview/main.js'; +import Powermenu from './powermenu.js'; +import QSettings from './quick-settings/main.js'; + +Setup(); + +const closeWinDelay = 800; + + +export default { + notificationPopupTimeout: 5000, + cacheNotificationActions: true, + closeWindowDelay: { + 'applauncher': closeWinDelay, + 'calendar': closeWinDelay, + 'notification-center': closeWinDelay, + 'osd': 300, + 'osk': closeWinDelay, + 'overview': closeWinDelay, + 'powermenu': closeWinDelay, + 'quick-settings': closeWinDelay, + }, + windows: [ + // Put the corners first so they + // don't block the cursor on the bar + ...Corners(), + + AppLauncher(), + Calendar(), + NotifCenter(), + OSD(), + OSK(), + Overview(), + Powermenu(), + QSettings(), + + Bar(), + BgFade(), + NotifPopups(), + ], +}; diff --git a/devices/wim/config/ags/js/media-player/gesture.js b/devices/wim/config/ags/ts/media-player/gesture.ts similarity index 77% rename from devices/wim/config/ags/js/media-player/gesture.js rename to devices/wim/config/ags/ts/media-player/gesture.ts index 1fbf1107..269fc4e7 100644 --- a/devices/wim/config/ags/js/media-player/gesture.js +++ b/devices/wim/config/ags/ts/media-player/gesture.ts @@ -9,21 +9,24 @@ const ANIM_DURATION = 500; const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, opacity ${ANIM_DURATION}ms ease;`; -/** - * @typedef {import('types/widgets/overlay').OverlayProps} OverlayProps - * @typedef {import('types/widgets/overlay').default} Overlay - */ +// Types +import AgsOverlay from 'types/widgets/overlay'; +import OverlayProps from 'types/widgets/overlay'; +import AgsBox from 'types/widgets/box'; +import AgsCenterBox from 'types/widgets/centerbox'; +import { Connectable } from 'types/widgets/widget'; +type Gesture = { + attribute?: Object + setup?(self: Connectable & AgsOverlay): void + props?: OverlayProps +}; -/** @param {OverlayProps & { - * setup?: function(Overlay):void - * }} o - */ export default ({ attribute = {}, setup = () => {/**/}, ...props -}) => { +}: Gesture) => { const widget = EventBox(); const gesture = Gtk.GestureDrag.new(widget); @@ -39,23 +42,15 @@ export default ({ ...attribute, dragging: false, - list: () => content.get_children() - // @ts-expect-error - .filter((ch) => !ch.attribute?.empty), - - /** @param {Overlay} playerW */ - includesWidget: (playerW) => { - return Array.from(content.attribute.list()) - .find((w) => w === playerW); + includesWidget: (playerW: AgsOverlay) => { + return content.overlays.find((w) => w === playerW); }, - showTopOnly: () => Array.from(content.attribute.list()) - .forEach((over) => { - over.visible = over === content.attribute.list().at(-1); - }), + showTopOnly: () => content.overlays.forEach((over) => { + over.visible = over === content.overlays.at(-1); + }), - /** @param {import('types/widgets/centerbox').default} player */ - moveToTop: (player) => { + moveToTop: (player: AgsCenterBox) => { player.visible = true; content.reorder_overlay(player, -1); timeout(ANIM_DURATION, () => { @@ -72,23 +67,22 @@ export default ({ self .hook(gesture, (_, realGesture) => { if (realGesture) { - Array.from(self.attribute.list()) - .forEach((over) => { - over.visible = true; - }); + self.overlays.forEach((over) => { + over.visible = true; + }); } else { self.attribute.showTopOnly(); } // Don't allow gesture when only one player - if (self.attribute.list().length <= 1) { + if (self.overlays.length <= 1) { return; } self.attribute.dragging = true; let offset = gesture.get_offset()[1]; - const playerBox = self.attribute.list().at(-1); + const playerBox = self.overlays.at(-1) as AgsBox; if (!offset) { return; @@ -117,14 +111,14 @@ export default ({ .hook(gesture, () => { // Don't allow gesture when only one player - if (self.attribute.list().length <= 1) { + if (self.overlays.length <= 1) { return; } self.attribute.dragging = false; const offset = gesture.get_offset()[1]; - const playerBox = self.attribute.list().at(-1); + const playerBox = self.overlays.at(-1) as AgsBox; // If crosses threshold after letting go, slide away if (offset && Math.abs(offset) > MAX_OFFSET) { diff --git a/devices/wim/config/ags/js/media-player/mpris.js b/devices/wim/config/ags/ts/media-player/mpris.ts similarity index 83% rename from devices/wim/config/ags/js/media-player/mpris.js rename to devices/wim/config/ags/ts/media-player/mpris.ts index 447c307e..b0abbf20 100644 --- a/devices/wim/config/ags/js/media-player/mpris.js +++ b/devices/wim/config/ags/ts/media-player/mpris.ts @@ -6,12 +6,6 @@ import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Ayl import Separator from '../misc/separator.js'; import CursorBox from '../misc/cursorbox.js'; -/** - * @typedef {import('types/service/mpris').MprisPlayer} Player - * @typedef {import('types/variable').Variable} Variable - * @typedef {import('types/widgets/overlay').default} Overlay - */ - const ICON_SIZE = 32; const icons = { @@ -34,13 +28,21 @@ const icons = { }, }; +// Types +import { MprisPlayer } from 'types/service/mpris.js'; +import { Variable as Var } from 'types/variable'; +import AgsOverlay from 'types/widgets/overlay.js'; +import AgsCenterBox, { CenterBoxProps } from 'types/widgets/centerbox.js'; +import AgsLabel from 'types/widgets/label.js'; +import AgsIcon from 'types/widgets/icon.js'; +import AgsStack from 'types/widgets/stack.js'; -/** - * @param {Player} player - * @param {Variable} colors - * @param {import('types/widgets/centerbox').CenterBoxProps=} props - */ -export const CoverArt = (player, colors, props) => CenterBox({ + +export const CoverArt = ( + player: MprisPlayer, + colors: Var, + props: CenterBoxProps, +) => CenterBox({ ...props, // @ts-expect-error vertical: true, @@ -92,8 +94,8 @@ export const CoverArt = (player, colors, props) => CenterBox({ background-position: center; `; - // @ts-expect-error - if (!self?.get_parent()?.attribute.dragging) { + if (!(self.get_parent() as AgsCenterBox) + .attribute.dragging) { self.setCss(self.attribute.bgStyle); } }).catch((err) => { @@ -105,8 +107,7 @@ export const CoverArt = (player, colors, props) => CenterBox({ }, }); -/** @param {Player} player */ -export const TitleLabel = (player) => Label({ +export const TitleLabel = (player: MprisPlayer) => Label({ xalign: 0, max_width_chars: 40, truncate: 'end', @@ -115,8 +116,7 @@ export const TitleLabel = (player) => Label({ label: player.bind('track_title'), }); -/** @param {Player} player */ -export const ArtistLabel = (player) => Label({ +export const ArtistLabel = (player: MprisPlayer) => Label({ xalign: 0, max_width_chars: 40, truncate: 'end', @@ -127,17 +127,12 @@ export const ArtistLabel = (player) => Label({ }); -/** - * @param {Player} player - * @param {Overlay} overlay - */ -export const PlayerIcon = (player, overlay) => { - /** - * @param {Player} p - * @param {Overlay=} widget - * @param {Overlay=} over - */ - const playerIcon = (p, widget, over) => CursorBox({ +export const PlayerIcon = (player: MprisPlayer, overlay: AgsOverlay) => { + const playerIcon = ( + p: MprisPlayer, + widget?: AgsOverlay, + over?: AgsOverlay, + ) => CursorBox({ tooltip_text: p.identity || '', on_primary_click_release: () => { @@ -159,10 +154,16 @@ export const PlayerIcon = (player, overlay) => { }); return Box().hook(Mpris, (self) => { - const thisIndex = overlay.attribute.list() - .indexOf(self?.get_parent()?.get_parent()); + const grandPa = self.get_parent()?.get_parent(); - self.children = Array.from(overlay.attribute.list()) + if (!grandPa) { + return; + } + + const thisIndex = overlay.overlays + .indexOf(grandPa); + + self.children = (overlay.overlays as Array) .map((over, i) => { self.children.push(Separator(2)); @@ -177,11 +178,10 @@ export const PlayerIcon = (player, overlay) => { const { Gdk } = imports.gi; const display = Gdk.Display.get_default(); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const PositionSlider = (player, colors) => Slider({ +export const PositionSlider = ( + player: MprisPlayer, + colors: Var, +) => Slider({ class_name: 'position-slider', vpack: 'center', hexpand: true, @@ -250,13 +250,20 @@ export const PositionSlider = (player, colors) => Slider({ }, }); +type PlayerButtonType = { + player: MprisPlayer + colors: Var + items: Array<[name: string, widget: AgsLabel | AgsIcon]> + onClick: string + prop: string +}; const PlayerButton = ({ player, colors, items, onClick, prop, -}) => CursorBox({ +}: PlayerButtonType) => CursorBox({ child: Button({ attribute: { hovered: false }, child: Stack({ items }), @@ -269,7 +276,7 @@ const PlayerButton = ({ if (prop === 'playBackStatus' && colors.value) { const c = colors.value; - Array.from(items).forEach((item) => { + items.forEach((item) => { item[1].setCss(` background-color: ${c.hoverAccent}; color: ${c.buttonText}; @@ -287,7 +294,7 @@ const PlayerButton = ({ if (prop === 'playBackStatus' && colors.value) { const c = colors.value; - Array.from(items).forEach((item) => { + items.forEach((item) => { item[1].setCss(` background-color: ${c.buttonAccent}; color: ${c.buttonText}; @@ -301,8 +308,7 @@ const PlayerButton = ({ setup: (self) => { self .hook(player, () => { - // @ts-expect-error - self.child.shown = `${player[prop]}`; + (self.child as AgsStack).shown = `${player[prop]}`; }) .hook(colors, () => { if (!Mpris.players.find((p) => player === p)) { @@ -326,7 +332,7 @@ const PlayerButton = ({ }); } else { - Array.from(items).forEach((item) => { + items.forEach((item) => { item[1].setCss(` background-color: ${c.buttonAccent}; color: ${c.buttonText}; @@ -348,11 +354,10 @@ const PlayerButton = ({ }), }); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const ShuffleButton = (player, colors) => PlayerButton({ +export const ShuffleButton = ( + player: MprisPlayer, + colors: Var, +) => PlayerButton({ player, colors, items: [ @@ -369,11 +374,10 @@ export const ShuffleButton = (player, colors) => PlayerButton({ prop: 'shuffleStatus', }); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const LoopButton = (player, colors) => PlayerButton({ +export const LoopButton = ( + player: MprisPlayer, + colors: Var, +) => PlayerButton({ player, colors, items: [ @@ -394,11 +398,10 @@ export const LoopButton = (player, colors) => PlayerButton({ prop: 'loopStatus', }); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const PlayPauseButton = (player, colors) => PlayerButton({ +export const PlayPauseButton = ( + player: MprisPlayer, + colors: Var, +) => PlayerButton({ player, colors, items: [ @@ -419,11 +422,10 @@ export const PlayPauseButton = (player, colors) => PlayerButton({ prop: 'playBackStatus', }); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const PreviousButton = (player, colors) => PlayerButton({ +export const PreviousButton = ( + player: MprisPlayer, + colors: Var, +) => PlayerButton({ player, colors, items: [ @@ -440,11 +442,10 @@ export const PreviousButton = (player, colors) => PlayerButton({ prop: 'canGoPrev', }); -/** - * @param {Player} player - * @param {Variable} colors - */ -export const NextButton = (player, colors) => PlayerButton({ +export const NextButton = ( + player: MprisPlayer, + colors: Var, +) => PlayerButton({ player, colors, items: [ diff --git a/devices/wim/config/ags/ts/media-player/player.ts b/devices/wim/config/ags/ts/media-player/player.ts new file mode 100644 index 00000000..6449118d --- /dev/null +++ b/devices/wim/config/ags/ts/media-player/player.ts @@ -0,0 +1,217 @@ +import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; + +import { Box, CenterBox } from 'resource:///com/github/Aylur/ags/widget.js'; + +import * as mpris from './mpris.js'; +import PlayerGesture from './gesture.js'; +import Separator from '../misc/separator.js'; + +const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; +const SPACING = 8; + +// Types +import { MprisPlayer } from 'types/service/mpris.js'; +import AgsOverlay from 'types/widgets/overlay.js'; +import { Variable as Var } from 'types/variable'; +import AgsBox from 'types/widgets/box.js'; + + +const Top = ( + player: MprisPlayer, + overlay: AgsOverlay, +) => Box({ + class_name: 'top', + hpack: 'start', + vpack: 'start', + + children: [ + mpris.PlayerIcon(player, overlay), + ], +}); + +const Center = ( + player: MprisPlayer, + colors: Var, +) => Box({ + class_name: 'center', + + children: [ + CenterBox({ + // @ts-expect-error + vertical: true, + + children: [ + Box({ + class_name: 'metadata', + vertical: true, + hpack: 'start', + vpack: 'center', + hexpand: true, + + children: [ + mpris.TitleLabel(player), + mpris.ArtistLabel(player), + ], + }), + null, + null, + ], + }), + + CenterBox({ + // @ts-expect-error + vertical: true, + + children: [ + null, + mpris.PlayPauseButton(player, colors), + null, + ], + }), + + ], +}); + +const Bottom = ( + player: MprisPlayer, + colors: Var, +) => Box({ + class_name: 'bottom', + + children: [ + mpris.PreviousButton(player, colors), + Separator(SPACING), + + mpris.PositionSlider(player, colors), + Separator(SPACING), + + mpris.NextButton(player, colors), + Separator(SPACING), + + mpris.ShuffleButton(player, colors), + Separator(SPACING), + + mpris.LoopButton(player, colors), + ], +}); + +const PlayerBox = ( + player: MprisPlayer, + colors: Var, + overlay: AgsOverlay, +) => { + const widget = mpris.CoverArt(player, colors, { + class_name: `player ${player.name}`, + hexpand: true, + + start_widget: Top(player, overlay), + center_widget: Center(player, colors), + end_widget: Bottom(player, colors), + }); + + widget.visible = false; + + return widget; +}; + +export default () => { + const content = PlayerGesture({ + attribute: { + players: new Map(), + setup: false, + }, + + setup: (self) => { + self + .hook(Mpris, (_: AgsOverlay, bus_name: string) => { + const players = self.attribute.players; + + if (players.has(bus_name)) { + return; + } + + // Sometimes the signal doesn't give the bus_name + if (!bus_name) { + const player = Mpris.players.find((p) => { + return !players.has(p.bus_name); + }); + + if (player) { + bus_name = player.bus_name; + } + else { + return; + } + } + + // Get the one on top so we can move it up later + const previousFirst = self.overlays.at(-1); + + // Make the new player + const player = Mpris.getPlayer(bus_name); + const Colors = Variable(null); + + if (!player) { + return; + } + + players.set( + bus_name, + PlayerBox( + player, + Colors, + content.get_children()[0] as AgsOverlay, + ), + ); + self.overlays = Array.from(players.values()) + .map((widget) => widget) as Array; + + const includes = self.attribute + .includesWidget(previousFirst); + + // Select favorite player at startup + const attrs = self.attribute; + + if (!attrs.setup && players.has(FAVE_PLAYER)) { + attrs.moveToTop(players.get(FAVE_PLAYER)); + attrs.setup = true; + } + + // Move previousFirst on top again + else if (includes) { + attrs.moveToTop(previousFirst); + } + }, 'player-added') + + .hook(Mpris, (_: AgsOverlay, bus_name: string) => { + const players = self.attribute.players; + + if (!bus_name || !players.has(bus_name)) { + return; + } + + // Get the one on top so we can move it up later + const previousFirst = self.overlays.at(-1); + + // Remake overlays without deleted one + players.delete(bus_name); + self.overlays = Array.from(players.values()) + .map((widget) => widget) as Array; + + // Move previousFirst on top again + const includes = self.attribute + .includesWidget(previousFirst); + + if (includes) { + self.attribute.moveToTop(previousFirst); + } + }, 'player-closed'); + }, + }); + + return Box({ + class_name: 'media', + child: content, + }); +}; diff --git a/devices/wim/config/ags/js/misc/audio-icons.js b/devices/wim/config/ags/ts/misc/audio-icons.ts similarity index 100% rename from devices/wim/config/ags/js/misc/audio-icons.js rename to devices/wim/config/ags/ts/misc/audio-icons.ts diff --git a/devices/wim/config/ags/js/misc/background-fade.js b/devices/wim/config/ags/ts/misc/background-fade.ts similarity index 100% rename from devices/wim/config/ags/js/misc/background-fade.js rename to devices/wim/config/ags/ts/misc/background-fade.ts diff --git a/devices/wim/config/ags/js/misc/closer.js b/devices/wim/config/ags/ts/misc/closer.ts similarity index 70% rename from devices/wim/config/ags/js/misc/closer.js rename to devices/wim/config/ags/ts/misc/closer.ts index e3de2c17..d78093f8 100644 --- a/devices/wim/config/ags/js/misc/closer.js +++ b/devices/wim/config/ags/ts/misc/closer.ts @@ -1,11 +1,12 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; +// Types +import AgsWindow from 'types/widgets/window'; + export default () => { - Array.from(App.windows) - // @ts-expect-error + (Array.from(App.windows) as Array<[string, AgsWindow]>) .filter((w) => w[1].attribute?.close_on_unfocus && - // @ts-expect-error w[1].attribute?.close_on_unfocus !== 'stay') .forEach((w) => { App.closeWindow(w[0]); diff --git a/devices/wim/config/ags/js/misc/cursorbox.js b/devices/wim/config/ags/ts/misc/cursorbox.ts similarity index 86% rename from devices/wim/config/ags/js/misc/cursorbox.js rename to devices/wim/config/ags/ts/misc/cursorbox.ts index a2820c80..1ccb2786 100644 --- a/devices/wim/config/ags/js/misc/cursorbox.js +++ b/devices/wim/config/ags/ts/misc/cursorbox.ts @@ -5,25 +5,21 @@ import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js'; const { Gtk, Gdk } = imports.gi; const display = Gdk.Display.get_default(); -/** - * @typedef {import('types/widgets/eventbox').EventBoxProps} EventBoxProps - * @typedef {import('types/widgets/eventbox').default} EventBox - */ +import * as EventBoxTypes from 'types/widgets/eventbox'; +type CursorBox = EventBoxTypes.EventBoxProps & { + on_primary_click_release?(self: EventBoxTypes.default): void; + on_hover?(self: EventBoxTypes.default): void; + on_hover_lost?(self: EventBoxTypes.default): void; +}; -/** @param {EventBoxProps & { - * on_primary_click_release?: function(EventBox):void - * on_hover?: function(EventBox):void - * on_hover_lost?: function(EventBox):void - * }} o - */ export default ({ on_primary_click_release = () => {/**/}, on_hover = () => {/**/}, on_hover_lost = () => {/**/}, attribute, ...props -}) => { +}: CursorBox) => { // Make this variable to know if the function should // be executed depending on where the click is released const CanRun = Variable(true); diff --git a/devices/wim/config/ags/js/misc/persist.js b/devices/wim/config/ags/ts/misc/persist.ts similarity index 79% rename from devices/wim/config/ags/js/misc/persist.js rename to devices/wim/config/ags/ts/misc/persist.ts index 9a6580ca..e8108ae1 100644 --- a/devices/wim/config/ags/js/misc/persist.js +++ b/devices/wim/config/ags/ts/misc/persist.ts @@ -1,18 +1,17 @@ import { execAsync, readFileAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js'; const { get_home_dir } = imports.gi.GLib; +type Persist = { + name: string + gobject: typeof imports.gi.GObject + prop: string + condition?: boolean | string // If string, compare following props to this + whenTrue?: boolean | string + whenFalse?: boolean | string + signal?: string +}; + -/** - * @param {{ - * name: string - * gobject: typeof imports.gi.GObject - * prop: string - * condition?: boolean|string // if string, compare following props to this - * whenTrue?: boolean|string - * whenFalse?: boolean|string - * signal?: string - * }} o - */ export default ({ name, gobject, @@ -21,7 +20,7 @@ export default ({ whenTrue = condition, whenFalse = false, signal = 'changed', -}) => { +}: Persist) => { const cacheFile = `${get_home_dir()}/.cache/ags/.${name}`; const stateCmd = () => ['bash', '-c', diff --git a/devices/wim/config/ags/js/misc/popup.js b/devices/wim/config/ags/ts/misc/popup.ts similarity index 86% rename from devices/wim/config/ags/js/misc/popup.js rename to devices/wim/config/ags/ts/misc/popup.ts index c7ac8931..abf9b1eb 100644 --- a/devices/wim/config/ags/js/misc/popup.js +++ b/devices/wim/config/ags/ts/misc/popup.ts @@ -5,30 +5,30 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { Box, Overlay, Window } from 'resource:///com/github/Aylur/ags/widget.js'; import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; -/** - * @typedef {import('types/widgets/revealer').RevealerProps} RevProps - * @typedef {import('types/widgets/window').WindowProps} WinProps - * @typedef {import('types/widgets/window').default} Window - * @typedef {import('types/widgets/box').default} Box - * @typedef {import('gi://Gtk').Gtk.Widget} Widget - */ +// Types +type Allocation = typeof imports.gi.Gtk.Allocation; +type Widget = typeof imports.gi.Gtk.Widget; +import { RevealerProps } from 'types/widgets/revealer'; +import { WindowProps } from 'types/widgets/window'; +import AgsWindow from 'types/widgets/window'; +import AgsBox from 'types/widgets/box'; +import AgsOverlay from 'types/widgets/overlay'; +import { Binding } from 'types/service'; +type PopupWindow = WindowProps & { + transition?: RevealerProps['transition'] + transition_duration?: number + bezier?: string + on_open?(self: AgsWindow): void + on_close?(self: AgsWindow): void + blur?: boolean + close_on_unfocus?: 'none' | 'stay' | 'released' | 'clicked' + anchor?: Array + name: string +}; // FIXME: deal with overlay children? // TODO: make this a new class to be able to edit props -/** - * @param {WinProps & { - * transition?: RevProps['transition'] - * transition_duration?: number - * bezier?: string - * on_open?: function - * on_close?: function - * blur?: boolean - * close_on_unfocus?: 'none'|'stay'|'released'|'clicked' - * anchor?: Array - * name: string - * }} o - */ export default ({ transition = 'slide_down', transition_duration = 800, @@ -45,7 +45,7 @@ export default ({ blur = false, close_on_unfocus = 'released', ...props -}) => { +}: PopupWindow) => { const Child = Variable(child); const AntiClip = Variable(false); @@ -53,17 +53,11 @@ export default ({ transition !== 'crossfade'; const attribute = { - /** - * @param {import('gi://Gtk').Gtk.Allocation} alloc - * @param {'left'|'right'} side - */ set_x_pos: ( - alloc, - side = 'right', + alloc: Allocation, + side = 'right' as 'left' | 'right', ) => { - /** @type Window */ - // @ts-expect-error - const window = App.getWindow(name); + const window = App.getWindow(name) as AgsWindow; if (!window) { return; @@ -90,13 +84,12 @@ export default ({ get_child: () => Child.value, - /** @param {Widget} new_child */ - set_child: (new_child) => { + set_child: (new_child: Widget) => { Child.value = new_child; App.getWindow(name)?.child.show_all(); }, - // This is for my custom pointers.js + // This is for my custom pointers.js close_on_unfocus, }; @@ -175,8 +168,7 @@ export default ({ } if (needsAnticlipping) { - /** @param {number} position */ - const reorder_child = (position) => { + const reorder_child = (position: number) => { // If unanchored, we have another anticlip widget // so we can't change the order if (anchor.length !== 0) { @@ -211,7 +203,6 @@ export default ({ } }, - // @ts-expect-error children: Child.bind().transform((v) => { if (needsAnticlipping) { return [ @@ -239,23 +230,19 @@ export default ({ else { return [v]; } - }), + }) as Binding, })], setup: (self) => { self.on('get-child-position', (_, ch) => { - /** @type Box */ - // @ts-expect-error - const sizeBox = self.child; - // @ts-expect-error - const overlay = Child.value.get_parent(); + const overlay = (Child.value as Widget) + .get_parent() as AgsOverlay; if (ch === overlay) { const alloc = overlay.get_allocation(); - const setAlloc = /** @param {number} v */ - (v) => v - 2 < 0 ? 1 : v; + const setAlloc = (v: number) => v - 2 < 0 ? 1 : v; - sizeBox.css = ` + (self.child as AgsBox).css = ` min-height: ${setAlloc(alloc.height - 2)}px; min-width: ${setAlloc(alloc.width - 2)}px; `; @@ -271,12 +258,12 @@ export default ({ `, setup: (self) => { - let currentTimeout; + let currentTimeout: number; self.hook(App, (_, currentName, isOpen) => { if (currentName === name) { - // @ts-expect-error - const overlay = Child.value.get_parent(); + const overlay = (Child.value as Widget) + .get_parent() as AgsOverlay; const alloc = overlay.get_allocation(); const height = needsAnticlipping ? @@ -299,7 +286,7 @@ export default ({ currentTimeout = thisTimeout; } - let css; + let css = ''; /* Margin: top | right | bottom | left */ switch (transition) { @@ -388,6 +375,5 @@ export default ({ }), }); - return window; }; diff --git a/devices/wim/config/ags/js/misc/separator.js b/devices/wim/config/ags/ts/misc/separator.ts similarity index 58% rename from devices/wim/config/ags/js/misc/separator.js rename to devices/wim/config/ags/ts/misc/separator.ts index 9b194c22..60dfdbd3 100644 --- a/devices/wim/config/ags/js/misc/separator.js +++ b/devices/wim/config/ags/ts/misc/separator.ts @@ -1,15 +1,7 @@ import { Box } from 'resource:///com/github/Aylur/ags/widget.js'; -/** - * @param {number} size - * @param {{ - * vertical?: boolean - * css?: string - * props?: import('types/widgets/box').BoxProps - * }} o - */ -export default (size, { +export default (size: number, { vertical = false, css = '', ...props diff --git a/devices/wim/config/ags/js/notifications/base.js b/devices/wim/config/ags/ts/notifications/base.ts similarity index 89% rename from devices/wim/config/ags/js/notifications/base.js rename to devices/wim/config/ags/ts/notifications/base.ts index 45ecaafa..11992bd8 100644 --- a/devices/wim/config/ags/js/notifications/base.js +++ b/devices/wim/config/ags/ts/notifications/base.ts @@ -8,28 +8,34 @@ import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js'; const { GLib } = imports.gi; -/** @typedef {import('types/service/notifications').Notification} Notification */ +import Gesture from './gesture.js'; +import CursorBox from '../misc/cursorbox.js'; -/** @param {number} time */ -const setTime = (time) => { +// Types +import { Notification as NotifObj } from 'types/service/notifications.js'; +import AgsEventBox from 'types/widgets/eventbox.js'; +import { Client } from 'types/service/hyprland.js'; +type NotificationWidget = { + notif: NotifObj + slideIn?: 'Left' | 'Right' + command?(): void +}; + +const setTime = (time: number) => { return GLib.DateTime .new_from_unix_local(time) .format('%H:%M'); }; -/** @param {import('types/widgets/eventbox').default} box */ -const getDragState = (box) => box.get_parent()?.get_parent() - // @ts-expect-error - ?.get_parent()?.get_parent()?.get_parent()?.attribute.dragging; - -import Gesture from './gesture.js'; -import CursorBox from '../misc/cursorbox.js'; +const getDragState = (box: AgsEventBox) => (box.get_parent()?.get_parent() + ?.get_parent()?.get_parent()?.get_parent() as AgsEventBox) + ?.attribute.dragging; -/** @param {Notification} notif */ -const NotificationIcon = (notif) => { - /** @type function(import('types/widgets/eventbox').default):void */ - let iconCmd = () => {/**/}; +const NotificationIcon = (notif: NotifObj) => { + let iconCmd = (box: AgsEventBox):void => { + console.log(box); + }; if (notif._appEntry && Applications.query(notif._appEntry).length > 0) { const app = Applications.query(notif._appEntry)[0]; @@ -53,10 +59,8 @@ const NotificationIcon = (notif) => { } else { Hyprland.sendMessage('j/clients').then((msg) => { - /** @type {Array} */ - const clients = JSON.parse(msg); - - const classes = []; + const clients = JSON.parse(msg) as Array; + const classes = [] as Array; for (const key of clients) { if (key.class) { @@ -141,18 +145,11 @@ const NotificationIcon = (notif) => { // to know when there are notifs or not export const HasNotifs = Variable(false); -/** - * @param {{ - * notif: Notification - * slideIn?: 'Left'|'Right' - * command?: () => void - * }} o - */ export const Notification = ({ notif, slideIn = 'Left', - command = () => { /**/ }, -}) => { + command = () => {/**/}, +}: NotificationWidget) => { if (!notif) { return; } @@ -177,8 +174,7 @@ export const Notification = ({ }); // Add body to notif - // @ts-expect-error - notifWidget.child.add(Box({ + (notifWidget.child as AgsEventBox).add(Box({ class_name: `notification ${notif.urgency}`, vexpand: false, diff --git a/devices/wim/config/ags/js/notifications/center.js b/devices/wim/config/ags/ts/notifications/center.ts similarity index 90% rename from devices/wim/config/ags/js/notifications/center.js rename to devices/wim/config/ags/ts/notifications/center.ts index 9e759359..ae671049 100644 --- a/devices/wim/config/ags/js/notifications/center.js +++ b/devices/wim/config/ags/ts/notifications/center.ts @@ -7,17 +7,12 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { Notification, HasNotifs } from './base.js'; import CursorBox from '../misc/cursorbox.js'; -/** - * @typedef {import('types/service/notifications').Notification} NotifObj - * @typedef {import('types/widgets/box').default} Box - */ +// Types +import AgsBox from 'types/widgets/box.js'; +import { Notification as NotifObj } from 'resource:///com/github/Aylur/ags/service/notifications.js'; -/** - * @param {Box} box - * @param {NotifObj} notif - */ -const addNotif = (box, notif) => { +const addNotif = (box: AgsBox, notif: NotifObj) => { if (notif) { const NewNotif = Notification({ notif, @@ -58,11 +53,10 @@ const NotificationList = () => Box({ }, 'notified') .hook(Notifications, (box, id) => { - // @ts-expect-error - const notif = box.children.find((ch) => ch.attribute.id === id); + const notif = (box.children as Array) + .find((ch) => ch.attribute.id === id); if (notif?.sensitive) { - // @ts-expect-error notif.attribute.slideAway('Right'); } }, 'closed'); diff --git a/devices/wim/config/ags/js/notifications/gesture.js b/devices/wim/config/ags/ts/notifications/gesture.ts similarity index 94% rename from devices/wim/config/ags/js/notifications/gesture.js rename to devices/wim/config/ags/ts/notifications/gesture.ts index 7b5e67bc..94fedc28 100644 --- a/devices/wim/config/ags/js/notifications/gesture.js +++ b/devices/wim/config/ags/ts/notifications/gesture.ts @@ -8,6 +8,9 @@ import { HasNotifs } from './base.js'; const { Gdk, Gtk } = imports.gi; const display = Gdk.Display.get_default(); +// Types +import AgsBox from 'types/widgets/box.js'; + const MAX_OFFSET = 200; const OFFSCREEN = 300; const ANIM_DURATION = 500; @@ -94,19 +97,17 @@ export default ({ ready: false, id, - /** @param {'Left'|'Right'} side */ - slideAway: (side) => { + slideAway: (side: 'Left' | 'Right') => { // Slide away - // @ts-expect-error - widget.child.setCss(side === 'Left' ? slideLeft : slideRight); + (widget.child as AgsBox) + .setCss(side === 'Left' ? slideLeft : slideRight); // Make it uninteractable widget.sensitive = false; timeout(ANIM_DURATION - 100, () => { // Reduce height after sliding away - // @ts-expect-error - widget.child?.setCss(side === 'Left' ? + (widget.child as AgsBox)?.setCss(side === 'Left' ? squeezeLeft : squeezeRight); @@ -117,8 +118,7 @@ export default ({ HasNotifs.value = Notifications .notifications.length > 0; - // @ts-expect-error - widget.get_parent()?.remove(widget); + (widget.get_parent() as AgsBox)?.remove(widget); }); }); }, diff --git a/devices/wim/config/ags/js/notifications/main.js b/devices/wim/config/ags/ts/notifications/main.ts similarity index 100% rename from devices/wim/config/ags/js/notifications/main.js rename to devices/wim/config/ags/ts/notifications/main.ts diff --git a/devices/wim/config/ags/js/notifications/popup.js b/devices/wim/config/ags/ts/notifications/popup.ts similarity index 80% rename from devices/wim/config/ags/js/notifications/popup.js rename to devices/wim/config/ags/ts/notifications/popup.ts index e7bdcfd8..3e98f2c5 100644 --- a/devices/wim/config/ags/js/notifications/popup.js +++ b/devices/wim/config/ags/ts/notifications/popup.ts @@ -9,6 +9,9 @@ import { Notification } from './base.js'; const DELAY = 2000; +// Types +import AgsBox from 'types/widgets/box.js'; + export default () => Box({ vertical: true, @@ -16,8 +19,7 @@ export default () => Box({ css: 'padding: 1px;', setup: (self) => { - /** @param {number} id */ - const addPopup = (id) => { + const addPopup = (id: number) => { if (!id) { return; } @@ -42,32 +44,23 @@ export default () => Box({ } }; - /** - * @param {number} id - * @param {boolean} force - */ - const handleDismiss = (id, force = false) => { - // @ts-expect-error - const notif = self.children.find((ch) => ch.attribute.id === id); + const handleDismiss = (id: number, force = false) => { + const notif = (self.children as Array) + .find((ch) => ch.attribute.id === id); if (!notif) { return; } // If notif isn't hovered or was closed, slide away - // @ts-expect-error if (!notif.attribute.hovered || force) { - // @ts-expect-error notif.attribute.slideAway('Left'); } // If notif is hovered, delay close - // @ts-expect-error else if (notif.attribute.hovered) { const intervalId = interval(DELAY, () => { - // @ts-expect-error if (!notif.attribute.hovered && intervalId) { - // @ts-expect-error notif.attribute.slideAway('Left'); GLib.source_remove(intervalId); diff --git a/devices/wim/config/ags/js/on-screen-keyboard/gesture.js b/devices/wim/config/ags/ts/on-screen-keyboard/gesture.ts similarity index 84% rename from devices/wim/config/ags/js/on-screen-keyboard/gesture.js rename to devices/wim/config/ags/ts/on-screen-keyboard/gesture.ts index 6af7c32f..858e3da9 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/gesture.js +++ b/devices/wim/config/ags/ts/on-screen-keyboard/gesture.ts @@ -10,6 +10,10 @@ const KEY_N = 249; const HIDDEN_MARGIN = 340; const ANIM_DURATION = 700; +// Types +import AgsWindow from 'types/widgets/window.js'; +import AgsBox from 'types/widgets/box.js'; + const releaseAllKeys = () => { const keycodes = Array.from(Array(KEY_N).keys()); @@ -20,23 +24,21 @@ const releaseAllKeys = () => { ]).catch(print); }; -/** @param {import('types/widgets/window').default} window */ -export default (window) => { - // @ts-expect-error - window.child.setCss(`margin-bottom: -${HIDDEN_MARGIN}px;`); +export default (window: AgsWindow) => { const gesture = Gtk.GestureDrag.new(window); + const child = window.child as AgsBox; - let signals = []; + child.setCss(`margin-bottom: -${HIDDEN_MARGIN}px;`); + + let signals = [] as Array; window.attribute = { - /** @param {boolean} state */ - setVisible: (state) => { + setVisible: (state: boolean) => { if (state) { window.visible = true; window.attribute.setSlideDown(); - // @ts-expect-error - window.child.setCss(` + child.setCss(` transition: margin-bottom 0.7s cubic-bezier(0.36, 0, 0.66, -0.56); margin-bottom: 0px; @@ -51,8 +53,7 @@ export default (window) => { releaseAllKeys(); window.attribute.setSlideUp(); - // @ts-expect-error - window.child.setCss(` + child.setCss(` transition: margin-bottom 0.7s cubic-bezier(0.36, 0, 0.66, -0.56); margin-bottom: -${HIDDEN_MARGIN}px; @@ -90,8 +91,7 @@ export default (window) => { return; } - // @ts-expect-error - window.child.setCss(` + (window.child as AgsBox).setCss(` margin-bottom: ${offset - HIDDEN_MARGIN}px; `); }); @@ -101,8 +101,7 @@ export default (window) => { // End drag signals.push( gesture.connect('drag-end', () => { - // @ts-expect-error - window.child.setCss(` + (window.child as AgsBox).setCss(` transition: margin-bottom 0.5s ease-in-out; margin-bottom: -${HIDDEN_MARGIN}px; `); @@ -133,8 +132,7 @@ export default (window) => { return; } - // @ts-expect-error - window.child.setCss(` + (window.child as AgsBox).setCss(` margin-bottom: ${offset}px; `); }); @@ -144,8 +142,7 @@ export default (window) => { // End drag signals.push( gesture.connect('drag-end', () => { - // @ts-expect-error - window.child.setCss(` + (window.child as AgsBox).setCss(` transition: margin-bottom 0.5s ease-in-out; margin-bottom: 0px; `); diff --git a/devices/wim/config/ags/js/on-screen-keyboard/keyboard-layouts.js b/devices/wim/config/ags/ts/on-screen-keyboard/keyboard-layouts.ts similarity index 100% rename from devices/wim/config/ags/js/on-screen-keyboard/keyboard-layouts.js rename to devices/wim/config/ags/ts/on-screen-keyboard/keyboard-layouts.ts diff --git a/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js b/devices/wim/config/ags/ts/on-screen-keyboard/keyboard.ts similarity index 95% rename from devices/wim/config/ags/js/on-screen-keyboard/keyboard.js rename to devices/wim/config/ags/ts/on-screen-keyboard/keyboard.ts index a1947f48..5df94c76 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js +++ b/devices/wim/config/ags/ts/on-screen-keyboard/keyboard.ts @@ -15,9 +15,12 @@ const L_KEY_PER_ROW = [8, 7, 6, 6, 6, 4]; // eslint-disable-line const COLOR = 'rgba(0, 0, 0, 0.3)'; const SPACING = 4; +// Types +import AgsWindow from 'types/widgets/window.js'; +import AgsBox from 'types/widgets/box.js'; -/** @param {import('types/widgets/window').default} window */ -export default (window) => Box({ + +export default (window: AgsWindow) => Box({ vertical: true, children: [ CenterBox({ @@ -93,7 +96,7 @@ export default (window) => Box({ vertical: true, children: keyboardJson.keys.map((row, rowIndex) => { - const keys = []; + const keys = [] as Array; row.forEach((key, keyIndex) => { if (keyIndex < L_KEY_PER_ROW[rowIndex]) { @@ -135,7 +138,7 @@ export default (window) => Box({ vertical: true, children: keyboardJson.keys.map((row, rowIndex) => { - const keys = []; + const keys = [] as Array; row.forEach((key, keyIndex) => { if (keyIndex >= L_KEY_PER_ROW[rowIndex]) { diff --git a/devices/wim/config/ags/js/on-screen-keyboard/keys.js b/devices/wim/config/ags/ts/on-screen-keyboard/keys.ts similarity index 90% rename from devices/wim/config/ags/js/on-screen-keyboard/keys.js rename to devices/wim/config/ags/ts/on-screen-keyboard/keys.ts index f8e28593..6bcf67b2 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/keys.js +++ b/devices/wim/config/ags/ts/on-screen-keyboard/keys.ts @@ -44,10 +44,20 @@ const LSHIFT_CODE = 42; const LALT_CODE = 56; const LCTRL_CODE = 29; +// Types +import { Variable as Var } from 'types/variable.js'; +type Key = { + keytype: 'normal', + label: string, + labelShift?: string, + labelAltGr?: string, + shape: 'normal' | 'modkey', + keycode: number +}; -/** @param {Object} key */ -const ModKey = (key) => { - let Mod; + +const ModKey = (key: Key) => { + let Mod: Var; if (key.label === 'Super') { Mod = Super; @@ -77,17 +87,20 @@ const ModKey = (key) => { else if (key.label === 'Ctrl') { Mod = RCtrl; } + const label = Label({ + class_name: `mod ${key.label}`, + label: key.label, + }); const button = EventBox({ class_name: 'key', - on_primary_click_release: (self) => { + on_primary_click_release: () => { console.log('mod toggled'); execAsync(`ydotool key ${key.keycode}:${Mod.value ? 0 : 1}`); - // @ts-expect-error - self.child.toggleClassName('active', !Mod.value); + label.toggleClassName('active', !Mod.value); Mod.value = !Mod.value; }, @@ -96,8 +109,7 @@ const ModKey = (key) => { .hook(NormalClick, () => { Mod.value = false; - // @ts-expect-error - self.child.toggleClassName('active', false); + label.toggleClassName('active', false); execAsync(`ydotool key ${key.keycode}:0`); }) @@ -116,10 +128,7 @@ const ModKey = (key) => { self.toggleClassName('hover', false); }); }, - child: Label({ - class_name: `mod ${key.label}`, - label: key.label, - }), + child: label, }); return Box({ @@ -130,8 +139,7 @@ const ModKey = (key) => { }); }; -/** @param {Object} key */ -const RegularKey = (key) => { +const RegularKey = (key: Key) => { const widget = EventBox({ class_name: 'key', @@ -236,7 +244,6 @@ const RegularKey = (key) => { }); }; -/** @param {Object} key */ -export default (key) => key.keytype === 'normal' ? +export default (key: Key) => key.keytype === 'normal' ? RegularKey(key) : ModKey(key); diff --git a/devices/wim/config/ags/js/on-screen-keyboard/main.js b/devices/wim/config/ags/ts/on-screen-keyboard/main.ts similarity index 100% rename from devices/wim/config/ags/js/on-screen-keyboard/main.js rename to devices/wim/config/ags/ts/on-screen-keyboard/main.ts diff --git a/devices/wim/config/ags/js/osd/ctor.js b/devices/wim/config/ags/ts/osd/ctor.ts similarity index 50% rename from devices/wim/config/ags/js/osd/ctor.js rename to devices/wim/config/ags/ts/osd/ctor.ts index ac96f33e..59defae5 100644 --- a/devices/wim/config/ags/js/osd/ctor.js +++ b/devices/wim/config/ags/ts/osd/ctor.ts @@ -2,9 +2,29 @@ import { Box, Icon, ProgressBar } from 'resource:///com/github/Aylur/ags/widget. const Y_POS = 80; +// Types +import AgsBox from 'types/widgets/box'; +import { IconProps } from 'types/widgets/icon'; +import { GObject } from 'gi://GObject'; +import AgsStack from 'types/widgets/stack'; +type Widget = typeof imports.gi.Gtk.Widget; +import { Connectable } from 'types/widgets/widget'; +import AgsProgressBar from 'types/widgets/progressbar'; +type ConnectFunc = (self?: AgsProgressBar) => void; +type OSD = { + stack: AgsStack + icon: string | IconProps + info: { + mod: GObject.Object + signal?: string + logic?(self: AgsProgressBar): void + widget?: Widget + } +}; -export default ({ stack, icon, info }) => { - let connectFunc; + +export default ({ stack, icon, info }: OSD) => { + let connectFunc: ConnectFunc; const osd = Box({ css: `margin-bottom: ${Y_POS}px;`, @@ -27,13 +47,10 @@ export default ({ stack, icon, info }) => { // Handle requests to show the OSD // Different wether it's a bar or static if (info.logic) { - /** - * @param {import('types/widgets/box').default} self - * @returns {Promise} - */ - connectFunc = (self) => new Promise((r) => { - info.logic(self); - // @ts-expect-error + connectFunc = (self) => new Promise((r) => { + if (info.logic && self) { + info.logic(self); + } r(); }).then(() => stack.attribute.popup(osd)); } @@ -41,8 +58,8 @@ export default ({ stack, icon, info }) => { connectFunc = () => stack.attribute.popup(osd); } - // @ts-expect-error - osd.children[0].children[1].hook(info.mod, connectFunc, info.signal); + ((osd.children[0] as AgsBox).children[1] as Connectable) + .hook(info.mod, connectFunc, info.signal); return osd; }; diff --git a/devices/wim/config/ags/js/osd/main.js b/devices/wim/config/ags/ts/osd/main.ts similarity index 84% rename from devices/wim/config/ags/js/osd/main.js rename to devices/wim/config/ags/ts/osd/main.ts index 9f155222..7570a71c 100644 --- a/devices/wim/config/ags/js/osd/main.js +++ b/devices/wim/config/ags/ts/osd/main.ts @@ -4,9 +4,11 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { Stack } from 'resource:///com/github/Aylur/ags/widget.js'; import PopupWindow from '../misc/popup.js'; +import AgsBox from 'types/widgets/box.js'; +import AgsStack from 'types/widgets/stack.js'; // Import all the OSDs as an array -const OSDList = []; +const OSDList = [] as Array<(stack: AgsStack) => AgsBox>; import * as Modules from './osds.js'; for (const osd in Modules) { @@ -22,9 +24,7 @@ const OSDs = () => { transition: 'over_up_down', transition_duration, - attribute: { - popup: () => {/**/}, - }, + attribute: { popup: () => {/**/} }, }); // Send reference of stack to all items @@ -35,8 +35,7 @@ const OSDs = () => { timeout(1000, () => { let count = 0; - /** @param {import('types/widgets/box').default} osd */ - stack.attribute.popup = (osd) => { + stack.attribute.popup = (osd: AgsBox) => { ++count; stack.set_visible_child(osd); App.openWindow('osd'); diff --git a/devices/wim/config/ags/js/osd/osds.js b/devices/wim/config/ags/ts/osd/osds.ts similarity index 76% rename from devices/wim/config/ags/js/osd/osds.js rename to devices/wim/config/ags/ts/osd/osds.ts index 1e3f053f..d96e3748 100644 --- a/devices/wim/config/ags/js/osd/osds.js +++ b/devices/wim/config/ags/ts/osd/osds.ts @@ -16,20 +16,16 @@ globalThis.showSpeaker = () => { ShowSpeaker.value = !ShowSpeaker.value; }; -/** - * @typedef {import('types/widgets/stack').default} Stack - * @typedef {import('types/widgets/progressbar').default} ProgressBar - */ +// Types +import AgsStack from 'types/widgets/stack.js'; -/** @param {Stack} stack */ -export const SpeakerOSD = (stack) => OSD({ +export const SpeakerOSD = (stack: AgsStack) => OSD({ stack, icon: { icon: SpeakerIcon.bind() }, info: { mod: ShowSpeaker, - /** @param {ProgressBar} self */ logic: (self) => { if (!Audio.speaker) { return; @@ -44,30 +40,26 @@ export const SpeakerOSD = (stack) => OSD({ }, }); -/** @param {Stack} stack */ -export const ScreenBrightnessOSD = (stack) => OSD({ +export const ScreenBrightnessOSD = (stack: AgsStack) => OSD({ stack, icon: { icon: Brightness.bind('screenIcon') }, info: { mod: Brightness, signal: 'screen', - /** @param {ProgressBar} self */ logic: (self) => { self.value = Brightness.screen; }, }, }); -/** @param {Stack} stack */ -export const KbdBrightnessOSD = (stack) => OSD({ +export const KbdBrightnessOSD = (stack: AgsStack) => OSD({ stack, icon: 'keyboard-brightness-symbolic', info: { mod: Brightness, signal: 'kbd', - /** @param {ProgressBar} self */ logic: (self) => { if (!self.value) { self.value = Brightness.kbd / 2; @@ -80,15 +72,13 @@ export const KbdBrightnessOSD = (stack) => OSD({ }, }); -/** @param {Stack} stack */ -export const MicOSD = (stack) => OSD({ +export const MicOSD = (stack: AgsStack) => OSD({ stack, icon: { icon: MicIcon.bind() }, info: { mod: Audio, signal: 'microphone-changed', - /** @param {ProgressBar} self */ logic: (self) => { if (!Audio.microphone) { return; @@ -100,8 +90,7 @@ export const MicOSD = (stack) => OSD({ }, }); -/** @param {Stack} stack */ -export const CapsLockOSD = (stack) => OSD({ +export const CapsLockOSD = (stack: AgsStack) => OSD({ stack, icon: { icon: Brightness.bind('capsIcon') }, info: { diff --git a/devices/wim/config/ags/js/overview/clients.js b/devices/wim/config/ags/ts/overview/clients.ts similarity index 82% rename from devices/wim/config/ags/js/overview/clients.js rename to devices/wim/config/ags/ts/overview/clients.ts index 90f7cf3a..cbf23bbc 100644 --- a/devices/wim/config/ags/js/overview/clients.js +++ b/devices/wim/config/ags/ts/overview/clients.ts @@ -7,17 +7,16 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { WindowButton } from './dragndrop.js'; import * as VARS from './variables.js'; -/** - * @typedef {import('types/service/hyprland.js').Client} Client - * @typedef {import('types/widgets/revealer').default} Revealer - * @typedef {import('types/widgets/box').default} Box - */ +// Types +import { Client as HyprClient } from 'types/service/hyprland.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import AgsBox from 'types/widgets/box.js'; +import AgsButton from 'types/widgets/button.js'; +import AgsIcon from 'types/widgets/icon.js'; -/** @param {number} size */ -const scale = (size) => (size * VARS.SCALE) - VARS.MARGIN; +const scale = (size: number) => (size * VARS.SCALE) - VARS.MARGIN; -/** @param {Client} client */ -const getFontSize = (client) => { +const getFontSize = (client: HyprClient) => { const valX = scale(client.size[0]) * VARS.ICON_SCALE; const valY = scale(client.size[1]) * VARS.ICON_SCALE; @@ -26,21 +25,19 @@ const getFontSize = (client) => { return size <= 0 ? 0.1 : size; }; -/** @param {Client} client */ -const IconStyle = (client) => ` +const IconStyle = (client: HyprClient) => ` min-width: ${scale(client.size[0])}px; min-height: ${scale(client.size[1])}px; font-size: ${getFontSize(client)}px; `; -/** - * @param {Client} client - * @param {boolean} active - * @param {Array} clients - * @param {Box} box - */ -const Client = (client, active, clients, box) => { +const Client = ( + client: HyprClient, + active: Boolean, + clients: Array, + box: AgsBox, +) => { const wsName = String(client.workspace.name).replace('special:', ''); const wsId = client.workspace.id; const addr = `address:${client.address}`; @@ -116,20 +113,16 @@ const Client = (client, active, clients, box) => { }); }; -/** @param {Box} box */ -export const updateClients = (box) => { +export const updateClients = (box: AgsBox) => { Hyprland.sendMessage('j/clients').then((out) => { - /** @type Array */ - let clients = JSON.parse(out); + let clients = JSON.parse(out) as Array; clients = clients.filter((client) => client.class); box.attribute.workspaces.forEach( - /** @param {Revealer} workspace */ - (workspace) => { + (workspace: AgsRevealer) => { const fixed = workspace.attribute.get_fixed(); - /** @type Array */ - const toRemove = fixed.get_children(); + const toRemove = fixed.get_children() as Array; clients.filter((client) => client.workspace.id === workspace.attribute.id) @@ -161,14 +154,12 @@ export const updateClients = (box) => { } const newClient = [ - fixed.get_children().find( - /** @param {typeof WindowButton} ch */ - // @ts-expect-error - (ch) => ch.attribute.address === client.address, - ), + (fixed.get_children() as Array) + .find((ch) => + ch.attribute.address === client.address), client.at[0] * VARS.SCALE, client.at[1] * VARS.SCALE, - ]; + ] as [AgsRevealer, number, number]; // If it exists already if (newClient[0]) { @@ -182,9 +173,12 @@ export const updateClients = (box) => { // Set a timeout here to have an animation when the icon first appears timeout(1, () => { - newClient[0].child.child.className = - `window ${active}`; - newClient[0].child.child.setCss(IconStyle(client)); + ((newClient[0].child as AgsButton) + .child as AgsIcon) + .class_name = `window ${active}`; + + ((newClient[0].child as AgsButton) + .child as AgsIcon).setCss(IconStyle(client)); }); }); diff --git a/devices/wim/config/ags/js/overview/current-workspace.js b/devices/wim/config/ags/ts/overview/current-workspace.ts similarity index 64% rename from devices/wim/config/ags/js/overview/current-workspace.js rename to devices/wim/config/ags/ts/overview/current-workspace.ts index a6537fb4..f336e435 100644 --- a/devices/wim/config/ags/js/overview/current-workspace.js +++ b/devices/wim/config/ags/ts/overview/current-workspace.ts @@ -11,10 +11,11 @@ const DEFAULT_STYLE = ` border-radius: 10px; `; -/** - * @typedef {import('types/widgets/box').default} Box - * @typedef {import('types/widgets/revealer').default} Revealer - */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import AgsCenterBox from 'types/widgets/centerbox.js'; +import AgsEventBox from 'types/widgets/eventbox.js'; export const Highlighter = () => Box({ @@ -24,24 +25,18 @@ export const Highlighter = () => Box({ css: DEFAULT_STYLE, }); -/** - * @param {Box} main - * @param {Box} highlighter - */ -export const updateCurrentWorkspace = (main, highlighter) => { +export const updateCurrentWorkspace = (main: AgsBox, highlighter: AgsBox) => { const currentId = Hyprland.active.workspace.id; const row = Math.floor((currentId - 1) / VARS.WORKSPACE_PER_ROW); - // @ts-expect-error - const rowObject = main.children[0].children[row]; - const workspaces = rowObject.child.centerWidget.child - .get_children().filter( - /** @param {Revealer} w */ - (w) => w.reveal_child, - ); + const rowObject = (main.children[0] as AgsBox).children[row] as AgsRevealer; + const workspaces = ((((rowObject.child as AgsCenterBox) + .center_widget as AgsEventBox) + .child as AgsBox) + .get_children() as Array) + .filter((w) => w.reveal_child); const currentIndex = workspaces.findIndex( - /** @param {Revealer} w */ (w) => w.attribute.id === currentId, ); const left = currentIndex * ((VARS.SCREEN.X * VARS.SCALE) + PADDING); diff --git a/devices/wim/config/ags/js/overview/dragndrop.js b/devices/wim/config/ags/ts/overview/dragndrop.ts similarity index 75% rename from devices/wim/config/ags/js/overview/dragndrop.js rename to devices/wim/config/ags/ts/overview/dragndrop.ts index e7399446..732ae9e6 100644 --- a/devices/wim/config/ags/js/overview/dragndrop.js +++ b/devices/wim/config/ags/ts/overview/dragndrop.ts @@ -10,16 +10,19 @@ import { updateClients } from './clients.js'; const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; const display = Gdk.Display.get_default(); -/** - * @typedef {import('types/widgets/button').default} Button - * @typedef {import('types/widgets/button').ButtonProps} ButtonProps - * @typedef {import('types/widgets/eventbox').EventBoxProps=} EventBoxProps - * @typedef {import('types/widgets/box').default} Box - */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsButton from 'types/widgets/button.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import { ButtonProps } from 'types/widgets/button.js'; +import { EventBoxProps } from 'types/widgets/eventbox.js'; +type WindowButtonType = ButtonProps & { + address: string + mainBox: AgsBox +}; -/** @param {Button} widget */ -const createSurfaceFromWidget = (widget) => { +const createSurfaceFromWidget = (widget: AgsButton) => { const alloc = widget.get_allocation(); const surface = new Cairo.ImageSurface( Cairo.Format.ARGB32, @@ -38,19 +41,16 @@ const createSurfaceFromWidget = (widget) => { let hidden = 0; -/** @params {EventBoxProps} props */ -export const WorkspaceDrop = ({ ...props }) => EventBox({ +export const WorkspaceDrop = ({ ...props }: EventBoxProps) => EventBox({ ...props, setup: (self) => { self.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); self.on('drag-data-received', (_, _c, _x, _y, data) => { - // @ts-expect-error - let id = self.get_parent()?.attribute.id; + let id = (self.get_parent() as AgsRevealer)?.attribute.id; if (id < -1) { - // @ts-expect-error - id = self.get_parent()?.attribute.name; + id = (self.get_parent() as AgsRevealer)?.attribute.name; } else if (id === -1) { @@ -68,17 +68,11 @@ export const WorkspaceDrop = ({ ...props }) => EventBox({ }, }); -/** - * @param {ButtonProps & { - * address: string - * mainBox: Box - * }} o - */ export const WindowButton = ({ address, mainBox, ...props -}) => Button({ +}: WindowButtonType) => Button({ ...props, setup: (self) => { @@ -98,8 +92,7 @@ export const WindowButton = ({ context, createSurfaceFromWidget(self), ); - // @ts-expect-error - self.get_parent()?.set_reveal_child(false); + (self.get_parent() as AgsRevealer)?.set_reveal_child(false); }) .on('drag-end', () => { diff --git a/devices/wim/config/ags/js/overview/main.js b/devices/wim/config/ags/ts/overview/main.ts similarity index 90% rename from devices/wim/config/ags/js/overview/main.js rename to devices/wim/config/ags/ts/overview/main.ts index 324fdc29..2e8d808a 100644 --- a/devices/wim/config/ags/js/overview/main.js +++ b/devices/wim/config/ags/ts/overview/main.ts @@ -7,6 +7,10 @@ import { WorkspaceRow, getWorkspaces, updateWorkspaces } from './workspaces.js'; import { Highlighter, updateCurrentWorkspace } from './current-workspace.js'; import { updateClients } from './clients.js'; +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsOverlay from 'types/widgets/overlay.js'; + // TODO: have a 'page' for each monitor, arrows on both sides to loop through export const Overview = () => { @@ -80,8 +84,7 @@ export const Overview = () => { setup: (self) => { self.on('get-child-position', (_, ch) => { if (ch === mainBox && !self.attribute.closing) { - // @ts-expect-error - self.child.setCss(` + (self.child as AgsBox).setCss(` transition: min-height 0.2s ease, min-width 0.2s ease; min-height: ${mainBox.get_allocated_height()}px; min-width: ${mainBox.get_allocated_width()}px; @@ -125,14 +128,12 @@ export default () => { if (isOpen) { self.child = Overview(); self.show_all(); - // @ts-expect-error - self.child.attribute.get_child().attribute.update(); + (self.child as AgsOverlay) + .attribute.get_child().attribute.update(); } else { - // @ts-expect-error - self.child.attribute.closing = true; - // @ts-expect-error - self.child.child.css = ` + (self.child as AgsOverlay).attribute.closing = true; + ((self.child as AgsOverlay).child as AgsBox).css = ` min-height: 1px; min-width: 1px; transition: all diff --git a/devices/wim/config/ags/js/overview/variables.js b/devices/wim/config/ags/ts/overview/variables.ts similarity index 100% rename from devices/wim/config/ags/js/overview/variables.js rename to devices/wim/config/ags/ts/overview/variables.ts diff --git a/devices/wim/config/ags/js/overview/workspaces.js b/devices/wim/config/ags/ts/overview/workspaces.ts similarity index 73% rename from devices/wim/config/ags/js/overview/workspaces.js rename to devices/wim/config/ags/ts/overview/workspaces.ts index 48e7fc19..52358e2f 100644 --- a/devices/wim/config/ags/js/overview/workspaces.js +++ b/devices/wim/config/ags/ts/overview/workspaces.ts @@ -10,41 +10,35 @@ const DEFAULT_STYLE = ` min-height: ${VARS.SCREEN.Y * VARS.SCALE}px; `; -/** - * @typedef {import('types/widgets/box').default} Box - * @typedef {import('types/widgets/revealer').default} Revealer - */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import AgsCenterBox from 'types/widgets/centerbox.js'; +import AgsEventBox from 'types/widgets/eventbox.js'; -/** @param {Box} box */ -export const getWorkspaces = (box) => { - const children = []; +export const getWorkspaces = (box: AgsBox) => { + const children = [] as Array; - box.children.forEach((type) => { - // @ts-expect-error - type.children.forEach( - /** @param {Revealer} row */ + (box.children as Array).forEach((type) => { + (type.children as Array).forEach( (row) => { - // @ts-expect-error - row.child.centerWidget.child.children.forEach( - /** @param {Revealer} workspace */ - (workspace) => { + ((((row.child as AgsCenterBox) + ?.center_widget as AgsEventBox) + ?.child as AgsBox) + .children as Array) + .forEach((workspace) => { children.push(workspace); - }, - ); + }); }, ); }); - box.attribute.workspaces = children.sort((a, b) => - a.attribute.id - b.attribute.id); + box.attribute.workspaces = children.sort((a, b) => { + return a.attribute.id - b.attribute.id; + }); }; -/** - * @param {number} id - * @param {string} name - * @param {boolean} normal - */ -const Workspace = (id, name, normal = true) => { +const Workspace = (id: number, name: string, normal = true) => { const fixed = Fixed({}); const workspace = Revealer({ @@ -100,11 +94,7 @@ const Workspace = (id, name, normal = true) => { return workspace; }; -/** - * @param {string} class_name - * @param {number} i - */ -export const WorkspaceRow = (class_name, i) => { +export const WorkspaceRow = (class_name: string, i: number) => { const addWorkspace = Workspace( class_name === 'special' ? -1 : 1000, class_name === 'special' ? 'special' : '', @@ -161,11 +151,9 @@ export const WorkspaceRow = (class_name, i) => { }); }; -/** @param {Box} box */ -export const updateWorkspaces = (box) => { +export const updateWorkspaces = (box: AgsBox) => { Hyprland.workspaces.forEach((ws) => { - const currentWs = box.attribute.workspaces.find( - /** @param {Revealer} ch */ + const currentWs = (box.attribute.workspaces as Array).find( (ch) => ch.attribute.id === ws.id, ); @@ -179,21 +167,22 @@ export const updateWorkspaces = (box) => { } else { rowNo = Math.floor((ws.id - 1) / VARS.WORKSPACE_PER_ROW); - // @ts-expect-error - const wsQty = box.children[type].children.length; + const wsRow = box.children[type] as AgsBox; + const wsQty = wsRow.children.length; if (rowNo >= wsQty) { for (let i = wsQty; i <= rowNo; ++i) { - // @ts-expect-error - box.children[type].add(WorkspaceRow( + wsRow.add(WorkspaceRow( type ? 'special' : 'normal', i, )); } } } - // @ts-expect-error - const row = box.children[type].children[rowNo] - .child.centerWidget.child; + const row = ((((box.children[type] as AgsBox) + .children[rowNo] as AgsRevealer) + .child as AgsCenterBox) + .center_widget as AgsEventBox) + .child as AgsBox; row.add(Workspace(ws.id, type ? ws.name : '')); } @@ -201,13 +190,9 @@ export const updateWorkspaces = (box) => { // Make sure the order is correct box.attribute.workspaces.forEach( - /** - * @param {Revealer} workspace - * @param {number} i - */ - (workspace, i) => { - // @ts-expect-error - workspace?.get_parent()?.reorder_child(workspace, i); + (workspace: AgsRevealer, i: number) => { + (workspace?.get_parent() as AgsBox) + ?.reorder_child(workspace, i); }, ); box.show_all(); diff --git a/devices/wim/config/ags/js/powermenu.js b/devices/wim/config/ags/ts/powermenu.ts similarity index 100% rename from devices/wim/config/ags/js/powermenu.js rename to devices/wim/config/ags/ts/powermenu.ts diff --git a/devices/wim/config/ags/js/quick-settings/bluetooth.js b/devices/wim/config/ags/ts/quick-settings/bluetooth.ts similarity index 84% rename from devices/wim/config/ags/js/quick-settings/bluetooth.js rename to devices/wim/config/ags/ts/quick-settings/bluetooth.ts index ef780b3b..6601968a 100644 --- a/devices/wim/config/ags/js/quick-settings/bluetooth.js +++ b/devices/wim/config/ags/ts/quick-settings/bluetooth.ts @@ -8,13 +8,14 @@ import CursorBox from '../misc/cursorbox.js'; const SCROLL_THRESH_H = 200; const SCROLL_THRESH_N = 7; -/** - * @typedef {import('types/widgets/box').default} Box - * @typedef {import('types/service/bluetooth').BluetoothDevice} BluetoothDevice - */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsScrollable from 'types/widgets/scrollable.js'; +type ListBoxRow = typeof imports.gi.Gtk.ListBoxRow; +import { BluetoothDevice as BTDev } from 'types/service/bluetooth.js'; -/** @param {BluetoothDevice} dev */ -const BluetoothDevice = (dev) => Box({ + +const BluetoothDevice = (dev: BTDev) => Box({ class_name: 'menu-item', attribute: { dev }, @@ -121,14 +122,15 @@ export const BluetoothMenu = () => { child: ListBox({ setup: (self) => { - self.set_sort_func( - (a, b) => { - // @ts-expect-error - return b.get_children()[0].attribute.dev.paired - // eslint-disable-line - // @ts-expect-error - a.get_children()[0].attribute.dev.paired; - }, - ); + self.set_sort_func((a, b) => { + const bState = (b.get_children()[0] as AgsBox) + .attribute.dev.paired; + + const aState = (a.get_children()[0] as AgsBox) + .attribute.dev.paired; + + return bState - aState; + }); self.hook(Bluetooth, () => { // Get all devices @@ -174,15 +176,14 @@ export const BluetoothMenu = () => { SCROLL_THRESH_H, ); - const scroll = self.get_parent()?.get_parent(); + const scroll = (self.get_parent() as ListBoxRow) + ?.get_parent() as AgsScrollable; if (scroll) { const n_child = self.get_children().length; if (n_child > SCROLL_THRESH_N) { - // @ts-expect-error scroll.vscroll = 'always'; - // @ts-expect-error scroll.setCss(`min-height: ${height}px;`); // Make bottom scroll indicator appear only @@ -193,9 +194,7 @@ export const BluetoothMenu = () => { } } else { - // @ts-expect-error scroll.vscroll = 'never'; - // @ts-expect-error scroll.setCss(''); topArrow.reveal_child = false; bottomArrow.reveal_child = false; @@ -203,12 +202,10 @@ export const BluetoothMenu = () => { } // Trigger sort_func - self.get_children().forEach( - (ListBoxRow) => { - // @ts-expect-error - ListBoxRow.changed(); - }, - ); + (self.get_children() as Array) + .forEach((ch) => { + ch.changed(); + }); }); }, }), diff --git a/devices/wim/config/ags/js/quick-settings/button-grid.js b/devices/wim/config/ags/ts/quick-settings/button-grid.ts similarity index 69% rename from devices/wim/config/ags/js/quick-settings/button-grid.js rename to devices/wim/config/ags/ts/quick-settings/button-grid.ts index 3ed5cea0..c2d4da8e 100644 --- a/devices/wim/config/ags/js/quick-settings/button-grid.js +++ b/devices/wim/config/ags/ts/quick-settings/button-grid.ts @@ -13,29 +13,36 @@ import Separator from '../misc/separator.js'; import { NetworkMenu } from './network.js'; import { BluetoothMenu } from './bluetooth.js'; +// Types +import { GObject } from 'gi://GObject'; +import AgsBox from 'types/widgets/box.js'; +import AgsIcon from 'types/widgets/icon.js'; +import AgsLabel from 'types/widgets/label.js'; +import AgsRevealer from 'types/widgets/revealer.js'; +import { Variable as Var } from 'types/variable.js'; +type IconTuple = [ + GObject.Object, + (self: AgsIcon) => void, + signal?: string, +]; +type IndicatorTuple = [ + GObject.Object, + (self: AgsLabel) => void, + signal?: string, +]; +type GridButtonType = { + command?(): void + secondary_command?(): void + on_open?(menu: AgsRevealer): void + icon: string | IconTuple + indicator?: IndicatorTuple + menu?: any +}; + const SPACING = 28; -const ButtonStates = []; - -/** - * @typedef {import('types/widgets/widget').default} Widget - * @typedef {import('types/widgets/box').default} Box - * @typedef {import('types/widgets/icon').default} Icon - * @typedef {import('types/widgets/label').default} Label - * @typedef {import('types/widgets/revealer').default} Revealer - * @typedef {[any, function, (string|undefined)?]} BindTuple - */ +const ButtonStates = [] as Array>; -/** - * @param {{ - * command?: function - * secondary_command?: function - * on_open?: function(Revealer):void - * icon: string|BindTuple - * indicator?: BindTuple - * menu?: any - * }} o - */ const GridButton = ({ command = () => {/**/}, secondary_command = () => {/**/}, @@ -43,12 +50,11 @@ const GridButton = ({ icon, indicator, menu, -}) => { +}: GridButtonType) => { const Activated = Variable(false); ButtonStates.push(Activated); - let iconWidget; - /** @type Label */ + let iconWidget = Icon(); let indicatorWidget = Label(); // Allow setting icon dynamically or statically @@ -70,7 +76,6 @@ const GridButton = ({ class_name: 'grid-label', setup: (self) => { self - // @ts-expect-error .hook(...icon) .hook(Activated, () => { self.setCss(`color: ${Activated.value ? @@ -88,7 +93,6 @@ const GridButton = ({ truncate: 'end', max_width_chars: 12, setup: (self) => { - // @ts-expect-error self.hook(...indicator); }, }); @@ -139,17 +143,15 @@ const GridButton = ({ on_hover: (self) => { if (menu) { const rowMenu = - self.get_parent() - ?.get_parent() - ?.get_parent() - ?.get_parent() - // @ts-expect-error - ?.children[1]; + ((((self.get_parent() as AgsBox) + ?.get_parent() as AgsBox) + ?.get_parent() as AgsBox) + ?.get_parent() as AgsBox) + ?.children[1] as AgsBox; - const isSetup = rowMenu.get_children().find( - /** @param {Box} ch */ - (ch) => ch === menu, - ); + const isSetup = (rowMenu + .get_children() as Array) + .find((ch) => ch === menu); if (!isSetup) { rowMenu.add(menu); @@ -188,29 +190,27 @@ const GridButton = ({ }; const Row = ({ buttons }) => { + const child = Box({ + class_name: 'button-row', + hpack: 'center', + }); + const widget = Box({ vertical: true, children: [ - Box({ - class_name: 'button-row', - hpack: 'center', - }), - + child, Box({ vertical: true }), ], }); for (let i = 0; i < buttons.length; ++i) { if (i === buttons.length - 1) { - // @ts-expect-error - widget.children[0].add(buttons[i]); + child.add(buttons[i]); } else { - // @ts-expect-error - widget.children[0].add(buttons[i]); - // @ts-expect-error - widget.children[0].add(Separator(SPACING)); + child.add(buttons[i]); + child.add(Separator(SPACING)); } } @@ -227,17 +227,13 @@ const FirstRow = () => Row({ // TODO: connection editor }, - icon: [Network, - /** @param {Icon} self */ - (self) => { - self.icon = Network.wifi?.icon_name; - }], + icon: [Network, (self) => { + self.icon = Network.wifi?.icon_name; + }], - indicator: [Network, - /** @param {Label} self */ - (self) => { - self.label = Network.wifi?.ssid || Network.wired?.internet; - }], + indicator: [Network, (self) => { + self.label = Network.wifi?.ssid || Network.wired?.internet; + }], menu: NetworkMenu(), on_open: () => Network.wifi.scan(), @@ -263,26 +259,22 @@ const FirstRow = () => Row({ // TODO: bluetooth connection editor }, - icon: [Bluetooth, - /** @param {Icon} self */ - (self) => { - if (Bluetooth.enabled) { - self.icon = Bluetooth.connected_devices[0] ? - Bluetooth.connected_devices[0].icon_name : - 'bluetooth-active-symbolic'; - } - else { - self.icon = 'bluetooth-disabled-symbolic'; - } - }], + icon: [Bluetooth, (self) => { + if (Bluetooth.enabled) { + self.icon = Bluetooth.connected_devices[0] ? + Bluetooth.connected_devices[0].icon_name : + 'bluetooth-active-symbolic'; + } + else { + self.icon = 'bluetooth-disabled-symbolic'; + } + }], - indicator: [Bluetooth, - /** @param {Label} self */ - (self) => { - self.label = Bluetooth.connected_devices[0] ? - `${Bluetooth.connected_devices[0]}` : - 'Disconnected'; - }, 'notify::connected-devices'], + indicator: [Bluetooth, (self) => { + self.label = Bluetooth.connected_devices[0] ? + `${Bluetooth.connected_devices[0]}` : + 'Disconnected'; + }, 'notify::connected-devices'], menu: BluetoothMenu(), on_open: (menu) => { @@ -308,11 +300,9 @@ const SecondRow = () => Row({ .catch(print); }, - icon: [SpeakerIcon, - /** @param {Icon} self */ - (self) => { - self.icon = SpeakerIcon.value; - }], + icon: [SpeakerIcon, (self) => { + self.icon = SpeakerIcon.value; + }], }), GridButton({ @@ -326,11 +316,9 @@ const SecondRow = () => Row({ .catch(print); }, - icon: [MicIcon, - /** @param {Icon} self */ - (self) => { - self.icon = MicIcon.value; - }], + icon: [MicIcon, (self) => { + self.icon = MicIcon.value; + }], }), GridButton({ diff --git a/devices/wim/config/ags/js/quick-settings/main.js b/devices/wim/config/ags/ts/quick-settings/main.ts similarity index 100% rename from devices/wim/config/ags/js/quick-settings/main.js rename to devices/wim/config/ags/ts/quick-settings/main.ts diff --git a/devices/wim/config/ags/js/quick-settings/network.js b/devices/wim/config/ags/ts/quick-settings/network.ts similarity index 85% rename from devices/wim/config/ags/js/quick-settings/network.js rename to devices/wim/config/ags/ts/quick-settings/network.ts index 2905bc49..b6c63bce 100644 --- a/devices/wim/config/ags/js/quick-settings/network.js +++ b/devices/wim/config/ags/ts/quick-settings/network.ts @@ -10,11 +10,22 @@ import CursorBox from '../misc/cursorbox.js'; const SCROLL_THRESH_H = 200; const SCROLL_THRESH_N = 7; -/** @typedef {import('types/widgets/box').default} Box */ +// Types +import AgsBox from 'types/widgets/box.js'; +import AgsScrollable from 'types/widgets/scrollable.js'; +type ListBoxRow = typeof imports.gi.Gtk.ListBoxRow; +type APType = { + bssid: string + address: string + lastSeen: number + ssid: string + active: boolean + strength: number + iconName: string +}; -/** @param {any} ap */ -const AccessPoint = (ap) => { +const AccessPoint = (ap: APType) => { const widget = Box({ class_name: 'menu-item', attribute: { @@ -138,19 +149,22 @@ export const NetworkMenu = () => { child: ListBox({ setup: (self) => { - self.set_sort_func( - (a, b) => { - return b.get_children()[0] - // @ts-expect-error - .attribute.ap.value.strength - - // @ts-expect-error - a.get_children()[0].attribute.ap.value.strength; - }, - ); + self.set_sort_func((a, b) => { + const bState = (b.get_children()[0] as AgsBox) + .attribute.ap.value.strength; + + const aState = (a.get_children()[0] as AgsBox) + .attribute.ap.value.strength; + + return bState - aState; + }); self.hook(Network, () => { // Add missing APs - Network.wifi?.access_points.forEach((ap) => { + const currentAPs = Network.wifi + ?.access_points as Array; + + currentAPs.forEach((ap) => { if (ap.ssid !== 'Unknown') { if (APList.has(ap.ssid)) { const accesPoint = APList.get(ap.ssid) @@ -198,15 +212,14 @@ export const NetworkMenu = () => { SCROLL_THRESH_H, ); - const scroll = self.get_parent()?.get_parent(); + const scroll = (self.get_parent() as ListBoxRow) + ?.get_parent() as AgsScrollable; if (scroll) { const n_child = self.get_children().length; if (n_child > SCROLL_THRESH_N) { - // @ts-expect-error scroll.vscroll = 'always'; - // @ts-expect-error scroll.setCss(`min-height: ${height}px;`); // Make bottom scroll indicator appear only @@ -217,9 +230,7 @@ export const NetworkMenu = () => { } } else { - // @ts-expect-error scroll.vscroll = 'never'; - // @ts-expect-error scroll.setCss(''); topArrow.reveal_child = false; bottomArrow.reveal_child = false; @@ -227,12 +238,10 @@ export const NetworkMenu = () => { } // Trigger sort_func - self.get_children().forEach( - (ListBoxRow) => { - // @ts-expect-error - ListBoxRow.changed(); - }, - ); + (self.get_children() as Array) + .forEach((ch) => { + ch.changed(); + }); }); }, }), diff --git a/devices/wim/config/ags/js/quick-settings/slider-box.js b/devices/wim/config/ags/ts/quick-settings/slider-box.ts similarity index 100% rename from devices/wim/config/ags/js/quick-settings/slider-box.js rename to devices/wim/config/ags/ts/quick-settings/slider-box.ts diff --git a/devices/wim/config/ags/ts/quick-settings/toggle-button.ts b/devices/wim/config/ags/ts/quick-settings/toggle-button.ts new file mode 100644 index 00000000..fd2b35e5 --- /dev/null +++ b/devices/wim/config/ags/ts/quick-settings/toggle-button.ts @@ -0,0 +1,64 @@ +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; + +import { CenterBox, Icon, ToggleButton } from 'resource:///com/github/Aylur/ags/widget.js'; + +// Types +import AgsRevealer from 'types/widgets/revealer'; + +const { Gdk } = imports.gi; +const display = Gdk.Display.get_default(); + + +export default (rev: AgsRevealer) => { + const child = Icon({ + icon: `${App.configDir}/icons/down-large.svg`, + class_name: 'arrow', + css: '-gtk-icon-transform: rotate(180deg);', + }); + + const button = CenterBox({ + center_widget: ToggleButton({ + setup: (self) => { + // Open at startup if there are players + const id = Mpris.connect('changed', () => { + self.set_active(Mpris.players.length > 0); + Mpris.disconnect(id); + }); + + self + .on('toggled', () => { + if (self.get_active()) { + child + .setCss('-gtk-icon-transform: rotate(0deg);'); + rev.reveal_child = true; + } + else { + child + .setCss('-gtk-icon-transform: rotate(180deg);'); + rev.reveal_child = false; + } + }) + + // OnHover + .on('enter-notify-event', () => { + self.window.set_cursor(Gdk.Cursor.new_from_name( + display, + 'pointer', + )); + self.toggleClassName('hover', true); + }) + + // OnHoverLost + .on('leave-notify-event', () => { + self.window.set_cursor(null); + self.toggleClassName('hover', false); + }); + }, + + child, + }), + }); + + return button; +}; diff --git a/devices/wim/config/ags/js/setup.js b/devices/wim/config/ags/ts/setup.ts similarity index 100% rename from devices/wim/config/ags/js/setup.js rename to devices/wim/config/ags/ts/setup.ts diff --git a/modules/ags/default.nix b/modules/ags/default.nix index 222195c3..f947db8c 100644 --- a/modules/ags/default.nix +++ b/modules/ags/default.nix @@ -26,7 +26,6 @@ in { package = ags.packages.${pkgs.system}.default; extraPackages = with pkgs; [ libgudev - webkitgtk ]; }; @@ -35,6 +34,7 @@ in { ++ (with pkgs; [ # ags sassc + bun playerctl ## gui