Added strict type checking to the project. (#236)
* Implement strict typing (WIP). * changes * Finish type checks * Fix notification icon, matugen settings and update tsconfig. * OSD Styling updates and added the ability to configure OSD duration.
This commit is contained in:
@@ -10,7 +10,7 @@ export const substitutes = {
|
||||
"preferences-system": "emblem-system-symbolic",
|
||||
"com.github.Aylur.ags-symbolic": "controls-symbolic",
|
||||
"com.github.Aylur.ags": "controls-symbolic",
|
||||
}
|
||||
} as const;
|
||||
|
||||
export default {
|
||||
missing: "image-missing-symbolic",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isHexColor } from "globals/variables"
|
||||
import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
|
||||
|
||||
type OptProps = {
|
||||
@@ -49,7 +50,7 @@ export class Opt<T = unknown> extends Variable<T> {
|
||||
if (this.persistent)
|
||||
return;
|
||||
|
||||
const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(`${this.value}`);
|
||||
const isColor = isHexColor(this.value as string);
|
||||
if ((JSON.stringify(this.value) !== JSON.stringify(this.initial)) && isColor) {
|
||||
this.value = this.initial
|
||||
return this.id
|
||||
@@ -60,35 +61,37 @@ export class Opt<T = unknown> extends Variable<T> {
|
||||
|
||||
export const opt = <T>(initial: T, opts?: OptProps) => new Opt(initial, opts)
|
||||
|
||||
function getOptions(object: object, path = ""): Opt[] {
|
||||
function getOptions(object: Record<string, unknown>, path = ""): Opt[] {
|
||||
return Object.keys(object).flatMap(key => {
|
||||
const obj: Opt = object[key]
|
||||
const id = path ? path + "." + key : key
|
||||
const obj = object[key];
|
||||
const id = path ? path + "." + key : key;
|
||||
|
||||
if (obj instanceof Variable) {
|
||||
obj.id = id
|
||||
return obj
|
||||
const optValue = obj as Opt;
|
||||
optValue.id = id;
|
||||
return optValue;
|
||||
}
|
||||
|
||||
if (typeof obj === "object")
|
||||
return getOptions(obj, id)
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
return getOptions(obj as Record<string, unknown>, id); // Recursively process nested objects
|
||||
}
|
||||
|
||||
return []
|
||||
})
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
export function mkOptions<T extends object>(cacheFile: string, object: T, confFile: string = "config.json") {
|
||||
for (const opt of getOptions(object))
|
||||
for (const opt of getOptions(object as Record<string, unknown>))
|
||||
opt.init(cacheFile)
|
||||
|
||||
Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
|
||||
|
||||
const configFile = `${TMP}/${confFile}`
|
||||
const values = getOptions(object).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {})
|
||||
const values = getOptions(object as Record<string, unknown>).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {})
|
||||
Utils.writeFileSync(JSON.stringify(values, null, 2), configFile)
|
||||
Utils.monitorFile(configFile, () => {
|
||||
const cache = JSON.parse(Utils.readFile(configFile) || "{}")
|
||||
for (const opt of getOptions(object)) {
|
||||
for (const opt of getOptions(object as Record<string, unknown>)) {
|
||||
if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value))
|
||||
opt.value = cache[opt.id]
|
||||
}
|
||||
@@ -99,7 +102,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
||||
}
|
||||
|
||||
async function reset(
|
||||
[opt, ...list] = getOptions(object),
|
||||
[opt, ...list] = getOptions(object as Record<string, unknown>),
|
||||
id = opt?.reset(),
|
||||
): Promise<Array<string>> {
|
||||
if (!opt)
|
||||
@@ -111,7 +114,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
||||
}
|
||||
|
||||
async function resetTheme(
|
||||
[opt, ...list] = getOptions(object),
|
||||
[opt, ...list] = getOptions(object as Record<string, unknown>),
|
||||
id = opt?.doResetColor(),
|
||||
): Promise<Array<string>> {
|
||||
if (!opt)
|
||||
@@ -124,7 +127,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
||||
|
||||
return Object.assign(object, {
|
||||
configFile,
|
||||
array: () => getOptions(object),
|
||||
array: () => getOptions(object as Record<string, unknown>),
|
||||
async reset() {
|
||||
return (await reset()).join("\n")
|
||||
},
|
||||
@@ -132,7 +135,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
||||
return (await resetTheme()).join("\n")
|
||||
},
|
||||
handler(deps: string[], callback: () => void) {
|
||||
for (const opt of getOptions(object)) {
|
||||
for (const opt of getOptions(object as Record<string, unknown>)) {
|
||||
if (deps.some(i => opt.id.startsWith(i)))
|
||||
opt.connect("changed", callback)
|
||||
}
|
||||
|
||||
5
lib/types/customModules/kbLayout.d.ts
vendored
5
lib/types/customModules/kbLayout.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { layoutMap } from "customModules/kblayout/layouts";
|
||||
|
||||
export type KbLabelType = "layout" | "code";
|
||||
export type KbIcon = "" | "" | "" | "" | "";
|
||||
|
||||
@@ -26,3 +28,6 @@ export type HyprctlDeviceLayout = {
|
||||
touch: any[];
|
||||
switches: any[];
|
||||
};
|
||||
|
||||
export type LayoutKeys = keyof typeof layoutMap;
|
||||
export type LayoutValues = typeof layoutMap[LayoutKeys];
|
||||
|
||||
9
lib/types/customModules/utils.d.ts
vendored
9
lib/types/customModules/utils.d.ts
vendored
@@ -0,0 +1,9 @@
|
||||
import { Binding } from "lib/utils";
|
||||
|
||||
export type InputHandlerEvents = {
|
||||
onPrimaryClick?: Binding,
|
||||
onSecondaryClick?: Binding,
|
||||
onMiddleClick?: Binding,
|
||||
onScrollUp?: Binding,
|
||||
onScrollDown?: Binding,
|
||||
}
|
||||
|
||||
10
lib/types/dropdownmenu.d.ts
vendored
Normal file
10
lib/types/dropdownmenu.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { WindowProps } from "types/widgets/window";
|
||||
|
||||
export type DropdownMenuProps = {
|
||||
name: string;
|
||||
child: any;
|
||||
layout?: string;
|
||||
transition?: any;
|
||||
exclusivity?: Exclusivity;
|
||||
fixed?: boolean;
|
||||
} & WindowProps;
|
||||
3
lib/types/filechooser.d.ts
vendored
Normal file
3
lib/types/filechooser.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export type Config = {
|
||||
[key: string]: string | number | boolean | object;
|
||||
}
|
||||
3
lib/types/mpris.d.ts
vendored
Normal file
3
lib/types/mpris.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export type LoopStatus = 'none' | 'track' | 'playlist';
|
||||
export type PlaybackStatus = 'playing' | 'paused' | 'stopped';
|
||||
|
||||
4
lib/types/network.d.ts
vendored
4
lib/types/network.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { WIFI_STATUS_MAP } from "globals/network";
|
||||
|
||||
export type AccessPoint = {
|
||||
bssid: string | null;
|
||||
address: string | null;
|
||||
@@ -8,3 +10,5 @@ export type AccessPoint = {
|
||||
frequency: number;
|
||||
iconName: string | undefined;
|
||||
}
|
||||
|
||||
export type WifiStatus = keyof typeof WIFI_STATUS_MAP;
|
||||
|
||||
4
lib/types/notification.d.ts
vendored
4
lib/types/notification.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import icons from "modules/icons/index";
|
||||
|
||||
export interface NotificationArgs {
|
||||
appName?: string;
|
||||
body?: string;
|
||||
@@ -9,3 +11,5 @@ export interface NotificationArgs {
|
||||
timeout?: number;
|
||||
transient?: boolean;
|
||||
}
|
||||
|
||||
export type NotificationIcon = keyof typeof icons.notifications;
|
||||
|
||||
5
lib/types/options.d.ts
vendored
5
lib/types/options.d.ts
vendored
@@ -1,6 +1,10 @@
|
||||
import { Opt } from "lib/option";
|
||||
import { Variable } from "types/variable";
|
||||
|
||||
export type RecursiveOptionsObject = {
|
||||
[key: string]: RecursiveOptionsObject | Opt<string | number | boolean> | Opt<any>;
|
||||
};
|
||||
|
||||
export type Unit = "imperial" | "metric";
|
||||
export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown";
|
||||
export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left" | "left" | "right";
|
||||
@@ -117,3 +121,4 @@ type MatugenVariation =
|
||||
| "vivid_3"
|
||||
|
||||
type MatugenTheme = "light" | "dark";
|
||||
|
||||
|
||||
27
lib/types/popupwindow.d.ts
vendored
Normal file
27
lib/types/popupwindow.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Widget } from "types/widgets/widget";
|
||||
import { WindowProps } from "types/widgets/window";
|
||||
import { Transition } from "./widget";
|
||||
|
||||
export type PopupWindowProps = {
|
||||
name: string;
|
||||
child: any;
|
||||
layout?: Layouts;
|
||||
transition?: any;
|
||||
exclusivity?: Exclusivity;
|
||||
} & WindowProps;
|
||||
|
||||
export type LayoutFunction = (
|
||||
name: string,
|
||||
child: Widget,
|
||||
transition: Transition
|
||||
) => {
|
||||
center: () => Widget;
|
||||
top: () => Widget;
|
||||
"top-right": () => Widget;
|
||||
"top-center": () => Widget;
|
||||
"top-left": () => Widget;
|
||||
"bottom-left": () => Widget;
|
||||
"bottom-center": () => Widget;
|
||||
"bottom-right": () => Widget;
|
||||
};
|
||||
export type Layouts = 'center' | 'top' | 'top-right' | 'top-center' | 'top-left' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
8
lib/types/powerprofiles.d.ts
vendored
Normal file
8
lib/types/powerprofiles.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import icons from "modules/icons/index";
|
||||
import PowerProfiles from "types/service/powerprofiles.js"
|
||||
|
||||
export type PowerProfiles = InstanceType<typeof PowerProfiles>;
|
||||
export type PowerProfile = "power-saver" | "balanced" | "performance";
|
||||
export type PowerProfileObject = {
|
||||
[key: string]: string;
|
||||
}
|
||||
3
lib/types/utils.d.ts
vendored
Normal file
3
lib/types/utils.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { substitutes } from "lib/icons";
|
||||
|
||||
type SubstituteKeys = keyof typeof substitutes;
|
||||
9
lib/types/weather.d.ts
vendored
9
lib/types/weather.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { weatherIcons } from "modules/icons/weather";
|
||||
|
||||
export type UnitType = "imperial" | "metric";
|
||||
|
||||
export type Weather = {
|
||||
@@ -107,3 +109,10 @@ export type Location = {
|
||||
localtime_epoch: number;
|
||||
localtime: string;
|
||||
}
|
||||
|
||||
export type TemperatureIconColorMap = {
|
||||
[key: number]: string;
|
||||
}
|
||||
|
||||
export type WeatherIconTitle = keyof typeof weatherIcons;
|
||||
export type WeatherIcon = typeof weatherIcons[WeatherIconTitle];
|
||||
|
||||
3
lib/types/widget.d.ts
vendored
3
lib/types/widget.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
export type Exclusivity = 'normal' | 'ignore' | 'exclusive';
|
||||
export type Anchor = "left" | "right" | "top" | "down";
|
||||
export type Transition = "none" | "crossfade" | "slide_right" | "slide_left" | "slide_up" | "slide_down";
|
||||
|
||||
// Window
|
||||
export type Layouts = 'center' | 'top' | 'top-right' | 'top-center' | 'top-left' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
|
||||
11
lib/utils.ts
11
lib/utils.ts
@@ -8,20 +8,29 @@ import Gdk from "gi://Gdk"
|
||||
import GLib from "gi://GLib?version=2.0"
|
||||
import GdkPixbuf from "gi://GdkPixbuf";
|
||||
import { NotificationArgs } from "types/utils/notify"
|
||||
import { SubstituteKeys } from "./types/utils";
|
||||
|
||||
export type Binding<T> = import("types/service").Binding<any, any, T>
|
||||
|
||||
|
||||
/**
|
||||
* @returns substitute icon || name || fallback icon
|
||||
*/
|
||||
export function icon(name: string | null, fallback = icons.missing) {
|
||||
const validateSubstitute = (name: string): name is SubstituteKeys => name in substitutes;
|
||||
|
||||
if (!name)
|
||||
return fallback || ""
|
||||
|
||||
if (GLib.file_test(name, GLib.FileTest.EXISTS))
|
||||
return name
|
||||
|
||||
const icon = (substitutes[name] || name)
|
||||
let icon: string = name;
|
||||
|
||||
if (validateSubstitute(name)) {
|
||||
icon = substitutes[name];
|
||||
}
|
||||
|
||||
if (Utils.lookUpIcon(icon))
|
||||
return icon
|
||||
|
||||
|
||||
Reference in New Issue
Block a user