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:
@@ -69,7 +69,6 @@ export const pollVariableBash = <T>(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the interval initially with the provided polling interval
|
|
||||||
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
|
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
|
||||||
intervalFn(pollIntrvl);
|
intervalFn(pollIntrvl);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import GTop from 'gi://GTop';
|
import GTop from 'gi://GTop';
|
||||||
|
|
||||||
const defaultCpuData: number = 0;
|
|
||||||
|
|
||||||
let previousCpuData = new GTop.glibtop_cpu();
|
let previousCpuData = new GTop.glibtop_cpu();
|
||||||
GTop.glibtop_get_cpu(previousCpuData);
|
GTop.glibtop_get_cpu(previousCpuData);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import options from "options";
|
import options from "options";
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
import GTop from 'gi://GTop';
|
|
||||||
|
|
||||||
// Module initializer
|
// Module initializer
|
||||||
import { module } from "../module"
|
import { module } from "../module"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HyprctlDeviceLayout, HyprctlKeyboard, KbLabelType } from "lib/types/customModules/kbLayout";
|
import { HyprctlDeviceLayout, HyprctlKeyboard, KbLabelType, LayoutKeys, LayoutValues } from "lib/types/customModules/kbLayout";
|
||||||
import { layoutMap } from "./layouts";
|
import { layoutMap } from "./layouts";
|
||||||
|
|
||||||
export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
|
export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
|
||||||
@@ -15,7 +15,8 @@ export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
|
|||||||
mainKb = keyboards[keyboards.length - 1];
|
mainKb = keyboards[keyboards.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout = mainKb['active_keymap'];
|
let layout: LayoutKeys = mainKb['active_keymap'] as LayoutKeys;
|
||||||
|
const foundLayout: LayoutValues = layoutMap[layout];
|
||||||
|
|
||||||
return format === "code" ? layoutMap[layout] || layout : layout;
|
return format === "code" ? foundLayout || layout : layout;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -581,4 +581,4 @@ export const layoutMap = {
|
|||||||
"Wolof": "SN",
|
"Wolof": "SN",
|
||||||
"Yakut": "RU (Sah)",
|
"Yakut": "RU (Sah)",
|
||||||
"Yoruba": "NG (Yoruba)"
|
"Yoruba": "NG (Yoruba)"
|
||||||
};
|
} as const;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Module } from "lib/types/bar";
|
import { Module } from "lib/types/bar";
|
||||||
|
import { BarButtonStyles } from "lib/types/options";
|
||||||
import options from "options";
|
import options from "options";
|
||||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||||
import { Binding } from "types/service";
|
import { Binding } from "types/service";
|
||||||
@@ -40,7 +41,7 @@ export const module = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
component: Widget.Box({
|
component: Widget.Box({
|
||||||
className: Utils.merge([style.bind("value"), showLabelBinding], (style: string, shwLabel: boolean) => {
|
className: Utils.merge([style.bind("value"), showLabelBinding], (style: BarButtonStyles, shwLabel: boolean) => {
|
||||||
const shouldShowLabel = shwLabel || showLabel;
|
const shouldShowLabel = shwLabel || showLabel;
|
||||||
const styleMap = {
|
const styleMap = {
|
||||||
default: "style1",
|
default: "style1",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ResourceLabelType } from 'lib/types/bar';
|
import { ResourceLabelType } from 'lib/types/bar';
|
||||||
import { GenericResourceData } from 'lib/types/customModules/generic';
|
import { GenericResourceData } from 'lib/types/customModules/generic';
|
||||||
|
import { InputHandlerEvents } from 'lib/types/customModules/utils';
|
||||||
import { Binding } from 'lib/utils';
|
import { Binding } from 'lib/utils';
|
||||||
import { openMenu } from 'modules/bar/utils';
|
import { openMenu } from 'modules/bar/utils';
|
||||||
import options from 'options';
|
import options from 'options';
|
||||||
@@ -75,7 +76,7 @@ export const inputHandler = (
|
|||||||
onMiddleClick,
|
onMiddleClick,
|
||||||
onScrollUp,
|
onScrollUp,
|
||||||
onScrollDown,
|
onScrollDown,
|
||||||
}
|
}: InputHandlerEvents
|
||||||
) => {
|
) => {
|
||||||
const sanitizeInput = (input: VariableType<string>): string => {
|
const sanitizeInput = (input: VariableType<string>): string => {
|
||||||
if (input === undefined) {
|
if (input === undefined) {
|
||||||
|
|||||||
5
globals.d.ts
vendored
5
globals.d.ts
vendored
@@ -1,9 +1,12 @@
|
|||||||
// globals.d.ts
|
// globals.d.ts
|
||||||
|
|
||||||
import { Variable as VariableType } from "types/variable";
|
import { Options, Variable as VariableType } from "types/variable";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var globalMousePos: VariableType<number[]>;
|
var globalMousePos: VariableType<number[]>;
|
||||||
|
var useTheme: Function;
|
||||||
|
var globalWeatherVar: VariableType<Weather>;
|
||||||
|
var options: Options
|
||||||
}
|
}
|
||||||
|
|
||||||
export { };
|
export { };
|
||||||
|
|||||||
16
globals/network.ts
Normal file
16
globals/network.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const WIFI_STATUS_MAP = {
|
||||||
|
unknown: "Status Unknown",
|
||||||
|
unmanaged: "Unmanaged",
|
||||||
|
unavailable: "Unavailable",
|
||||||
|
disconnected: "Disconnected",
|
||||||
|
prepare: "Preparing Connecting",
|
||||||
|
config: "Connecting",
|
||||||
|
need_auth: "Needs Authentication",
|
||||||
|
ip_config: "Requesting IP",
|
||||||
|
ip_check: "Checking Access",
|
||||||
|
secondaries: "Waiting on Secondaries",
|
||||||
|
activated: "Connected",
|
||||||
|
deactivating: "Disconnecting",
|
||||||
|
failed: "Connection Failed",
|
||||||
|
} as const;
|
||||||
|
|
||||||
24
globals/notification.ts
Normal file
24
globals/notification.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import icons from "modules/icons/index";
|
||||||
|
|
||||||
|
export const getNotificationIcon = (app_name: string, app_icon: string, app_entry: string) => {
|
||||||
|
let icon: string = icons.fallback.notification;
|
||||||
|
|
||||||
|
if (Utils.lookUpIcon(app_name) || Utils.lookUpIcon(app_name.toLowerCase() || "")) {
|
||||||
|
icon = Utils.lookUpIcon(app_name)
|
||||||
|
? app_name
|
||||||
|
: Utils.lookUpIcon(app_name.toLowerCase())
|
||||||
|
? app_name.toLowerCase()
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.lookUpIcon(app_icon) && icon === "") {
|
||||||
|
icon = app_icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.lookUpIcon(app_entry || "") && icon === "") {
|
||||||
|
icon = app_entry || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -3,6 +3,8 @@ import { bash, Notify } from "lib/utils";
|
|||||||
import icons from "lib/icons"
|
import icons from "lib/icons"
|
||||||
import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from "widget/settings/shared/FileChooser";
|
import { filterConfigForThemeOnly, loadJsonFile, saveConfigToFile } from "widget/settings/shared/FileChooser";
|
||||||
|
|
||||||
|
export const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
||||||
|
|
||||||
globalThis.useTheme = (filePath: string): void => {
|
globalThis.useTheme = (filePath: string): void => {
|
||||||
let importedConfig = loadJsonFile(filePath);
|
let importedConfig = loadJsonFile(filePath);
|
||||||
|
|
||||||
|
|||||||
13
globals/variables.ts
Normal file
13
globals/variables.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Opt } from "lib/option";
|
||||||
|
import { HexColor, RecursiveOptionsObject } from "lib/types/options";
|
||||||
|
|
||||||
|
export const isOpt = <T>(value: unknown): value is Opt<T> =>
|
||||||
|
typeof value === 'object' && value !== null && 'value' in value && value instanceof Opt;
|
||||||
|
|
||||||
|
export const isRecursiveOptionsObject = (value: unknown): value is RecursiveOptionsObject => {
|
||||||
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isHexColor = (value: string): value is HexColor => {
|
||||||
|
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import options from "options";
|
import options from "options";
|
||||||
import { UnitType, Weather } from "lib/types/weather.js";
|
import { UnitType, Weather, WeatherIconTitle, WeatherIcon } from "lib/types/weather.js";
|
||||||
import { DEFAULT_WEATHER } from "lib/types/defaults/weather.js";
|
import { DEFAULT_WEATHER } from "lib/types/defaults/weather.js";
|
||||||
import GLib from "gi://GLib?version=2.0"
|
import GLib from "gi://GLib?version=2.0";
|
||||||
|
|
||||||
import icons from "../modules/icons/index.js";
|
|
||||||
import { weatherIcons } from "modules/icons/weather.js";
|
import { weatherIcons } from "modules/icons/weather.js";
|
||||||
|
|
||||||
const { key, interval, location } = options.menus.clock.weather;
|
const { key, interval, location } = options.menus.clock.weather;
|
||||||
@@ -26,16 +24,16 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
try {
|
try {
|
||||||
if (typeof res !== "string") {
|
if (typeof res !== "string") {
|
||||||
return globalWeatherVar.value = DEFAULT_WEATHER;
|
return (globalWeatherVar.value = DEFAULT_WEATHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedWeather = JSON.parse(res);
|
const parsedWeather = JSON.parse(res);
|
||||||
|
|
||||||
if (Object.keys(parsedWeather).includes("error")) {
|
if (Object.keys(parsedWeather).includes("error")) {
|
||||||
return globalWeatherVar.value = DEFAULT_WEATHER;
|
return (globalWeatherVar.value = DEFAULT_WEATHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
return globalWeatherVar.value = parsedWeather;
|
return (globalWeatherVar.value = parsedWeather);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
globalWeatherVar.value = DEFAULT_WEATHER;
|
globalWeatherVar.value = DEFAULT_WEATHER;
|
||||||
console.warn(`Failed to parse weather data: ${error}`);
|
console.warn(`Failed to parse weather data: ${error}`);
|
||||||
@@ -45,12 +43,12 @@ const weatherIntervalFn = (weatherInterval: number, loc: string, weatherKey: str
|
|||||||
console.error(`Failed to fetch weather: ${err}`);
|
console.error(`Failed to fetch weather: ${err}`);
|
||||||
globalWeatherVar.value = DEFAULT_WEATHER;
|
globalWeatherVar.value = DEFAULT_WEATHER;
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Utils.merge([key.bind("value"), interval.bind("value"), location.bind("value")], (weatherKey, weatherInterval, loc) => {
|
Utils.merge([key.bind("value"), interval.bind("value"), location.bind("value")], (weatherKey, weatherInterval, loc) => {
|
||||||
if (!weatherKey) {
|
if (!weatherKey) {
|
||||||
return globalWeatherVar.value = DEFAULT_WEATHER;
|
return (globalWeatherVar.value = DEFAULT_WEATHER);
|
||||||
}
|
}
|
||||||
weatherIntervalFn(weatherInterval, loc, weatherKey);
|
weatherIntervalFn(weatherInterval, loc, weatherKey);
|
||||||
});
|
});
|
||||||
@@ -70,23 +68,28 @@ export const getWeatherIcon = (fahren: number) => {
|
|||||||
50: "",
|
50: "",
|
||||||
25: "",
|
25: "",
|
||||||
0: "",
|
0: "",
|
||||||
};
|
} as const;
|
||||||
const colors = {
|
const colors = {
|
||||||
100: "weather-color red",
|
100: "weather-color red",
|
||||||
75: "weather-color orange",
|
75: "weather-color orange",
|
||||||
50: "weather-color lavender",
|
50: "weather-color lavender",
|
||||||
25: "weather-color blue",
|
25: "weather-color blue",
|
||||||
0: "weather-color sky",
|
0: "weather-color sky",
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
const threshold =
|
type IconKeys = keyof typeof icons;
|
||||||
|
|
||||||
|
const threshold: IconKeys =
|
||||||
fahren < 0
|
fahren < 0
|
||||||
? 0
|
? 0
|
||||||
: [100, 75, 50, 25, 0].find((threshold) => threshold <= fahren);
|
: ([100, 75, 50, 25, 0] as IconKeys[]).find((threshold) => threshold <= fahren) || 0;
|
||||||
|
|
||||||
|
const icon = icons[threshold || 50];
|
||||||
|
const color = colors[threshold || 50];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icon: icons[threshold || 50],
|
icon,
|
||||||
color: colors[threshold || 50],
|
color,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,11 +98,15 @@ export const getWindConditions = (wthr: Weather, unt: UnitType) => {
|
|||||||
return `${Math.floor(wthr.current.wind_mph)} mph`;
|
return `${Math.floor(wthr.current.wind_mph)} mph`;
|
||||||
}
|
}
|
||||||
return `${Math.floor(wthr.current.wind_kph)} kph`;
|
return `${Math.floor(wthr.current.wind_kph)} kph`;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getRainChance = (wthr: Weather) => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`;
|
export const getRainChance = (wthr: Weather) => `${wthr.forecast.forecastday[0].day.daily_chance_of_rain}%`;
|
||||||
|
|
||||||
export const getWeatherStatusTextIcon = (wthr: Weather) => {
|
export const isValidWeatherIconTitle = (title: string): title is WeatherIconTitle => {
|
||||||
|
return title in weatherIcons;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWeatherStatusTextIcon = (wthr: Weather): WeatherIcon => {
|
||||||
let iconQuery = wthr.current.condition.text
|
let iconQuery = wthr.current.condition.text
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
@@ -108,7 +115,14 @@ export const getWeatherStatusTextIcon = (wthr: Weather) => {
|
|||||||
if (!wthr.current.is_day && iconQuery === "partly_cloudy") {
|
if (!wthr.current.is_day && iconQuery === "partly_cloudy") {
|
||||||
iconQuery = "partly_cloudy_night";
|
iconQuery = "partly_cloudy_night";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValidWeatherIconTitle(iconQuery)) {
|
||||||
return weatherIcons[iconQuery];
|
return weatherIcons[iconQuery];
|
||||||
|
} else {
|
||||||
|
console.warn(`Unknown weather icon title: ${iconQuery}`);
|
||||||
|
return weatherIcons["warning"];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
globalThis["globalWeatherVar"] = globalWeatherVar;
|
globalThis["globalWeatherVar"] = globalWeatherVar;
|
||||||
|
|
||||||
|
|||||||
10
globals/window.ts
Normal file
10
globals/window.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const WINDOW_LAYOUTS: string[] = [
|
||||||
|
'center',
|
||||||
|
'top',
|
||||||
|
'top-right',
|
||||||
|
'top-center',
|
||||||
|
'top-left',
|
||||||
|
'bottom-left',
|
||||||
|
'bottom-center',
|
||||||
|
'bottom-right'
|
||||||
|
];
|
||||||
@@ -10,7 +10,7 @@ export const substitutes = {
|
|||||||
"preferences-system": "emblem-system-symbolic",
|
"preferences-system": "emblem-system-symbolic",
|
||||||
"com.github.Aylur.ags-symbolic": "controls-symbolic",
|
"com.github.Aylur.ags-symbolic": "controls-symbolic",
|
||||||
"com.github.Aylur.ags": "controls-symbolic",
|
"com.github.Aylur.ags": "controls-symbolic",
|
||||||
}
|
} as const;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
missing: "image-missing-symbolic",
|
missing: "image-missing-symbolic",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { isHexColor } from "globals/variables"
|
||||||
import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
|
import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
|
||||||
|
|
||||||
type OptProps = {
|
type OptProps = {
|
||||||
@@ -49,7 +50,7 @@ export class Opt<T = unknown> extends Variable<T> {
|
|||||||
if (this.persistent)
|
if (this.persistent)
|
||||||
return;
|
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) {
|
if ((JSON.stringify(this.value) !== JSON.stringify(this.initial)) && isColor) {
|
||||||
this.value = this.initial
|
this.value = this.initial
|
||||||
return this.id
|
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)
|
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 => {
|
return Object.keys(object).flatMap(key => {
|
||||||
const obj: Opt = object[key]
|
const obj = object[key];
|
||||||
const id = path ? path + "." + key : key
|
const id = path ? path + "." + key : key;
|
||||||
|
|
||||||
if (obj instanceof Variable) {
|
if (obj instanceof Variable) {
|
||||||
obj.id = id
|
const optValue = obj as Opt;
|
||||||
return obj
|
optValue.id = id;
|
||||||
|
return optValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof obj === "object")
|
if (typeof obj === "object" && obj !== null) {
|
||||||
return getOptions(obj, id)
|
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") {
|
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)
|
opt.init(cacheFile)
|
||||||
|
|
||||||
Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
|
Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
|
||||||
|
|
||||||
const configFile = `${TMP}/${confFile}`
|
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.writeFileSync(JSON.stringify(values, null, 2), configFile)
|
||||||
Utils.monitorFile(configFile, () => {
|
Utils.monitorFile(configFile, () => {
|
||||||
const cache = JSON.parse(Utils.readFile(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))
|
if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value))
|
||||||
opt.value = cache[opt.id]
|
opt.value = cache[opt.id]
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function reset(
|
async function reset(
|
||||||
[opt, ...list] = getOptions(object),
|
[opt, ...list] = getOptions(object as Record<string, unknown>),
|
||||||
id = opt?.reset(),
|
id = opt?.reset(),
|
||||||
): Promise<Array<string>> {
|
): Promise<Array<string>> {
|
||||||
if (!opt)
|
if (!opt)
|
||||||
@@ -111,7 +114,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resetTheme(
|
async function resetTheme(
|
||||||
[opt, ...list] = getOptions(object),
|
[opt, ...list] = getOptions(object as Record<string, unknown>),
|
||||||
id = opt?.doResetColor(),
|
id = opt?.doResetColor(),
|
||||||
): Promise<Array<string>> {
|
): Promise<Array<string>> {
|
||||||
if (!opt)
|
if (!opt)
|
||||||
@@ -124,7 +127,7 @@ export function mkOptions<T extends object>(cacheFile: string, object: T, confFi
|
|||||||
|
|
||||||
return Object.assign(object, {
|
return Object.assign(object, {
|
||||||
configFile,
|
configFile,
|
||||||
array: () => getOptions(object),
|
array: () => getOptions(object as Record<string, unknown>),
|
||||||
async reset() {
|
async reset() {
|
||||||
return (await reset()).join("\n")
|
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")
|
return (await resetTheme()).join("\n")
|
||||||
},
|
},
|
||||||
handler(deps: string[], callback: () => void) {
|
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)))
|
if (deps.some(i => opt.id.startsWith(i)))
|
||||||
opt.connect("changed", callback)
|
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 KbLabelType = "layout" | "code";
|
||||||
export type KbIcon = "" | "" | "" | "" | "";
|
export type KbIcon = "" | "" | "" | "" | "";
|
||||||
|
|
||||||
@@ -26,3 +28,6 @@ export type HyprctlDeviceLayout = {
|
|||||||
touch: any[];
|
touch: any[];
|
||||||
switches: 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 = {
|
export type AccessPoint = {
|
||||||
bssid: string | null;
|
bssid: string | null;
|
||||||
address: string | null;
|
address: string | null;
|
||||||
@@ -8,3 +10,5 @@ export type AccessPoint = {
|
|||||||
frequency: number;
|
frequency: number;
|
||||||
iconName: string | undefined;
|
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 {
|
export interface NotificationArgs {
|
||||||
appName?: string;
|
appName?: string;
|
||||||
body?: string;
|
body?: string;
|
||||||
@@ -9,3 +11,5 @@ export interface NotificationArgs {
|
|||||||
timeout?: number;
|
timeout?: number;
|
||||||
transient?: boolean;
|
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 { Opt } from "lib/option";
|
||||||
import { Variable } from "types/variable";
|
import { Variable } from "types/variable";
|
||||||
|
|
||||||
|
export type RecursiveOptionsObject = {
|
||||||
|
[key: string]: RecursiveOptionsObject | Opt<string | number | boolean> | Opt<any>;
|
||||||
|
};
|
||||||
|
|
||||||
export type Unit = "imperial" | "metric";
|
export type Unit = "imperial" | "metric";
|
||||||
export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown";
|
export type PowerOptions = "sleep" | "reboot" | "logout" | "shutdown";
|
||||||
export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left" | "left" | "right";
|
export type NotificationAnchor = "top" | "top right" | "top left" | "bottom" | "bottom right" | "bottom left" | "left" | "right";
|
||||||
@@ -117,3 +121,4 @@ type MatugenVariation =
|
|||||||
| "vivid_3"
|
| "vivid_3"
|
||||||
|
|
||||||
type MatugenTheme = "light" | "dark";
|
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 UnitType = "imperial" | "metric";
|
||||||
|
|
||||||
export type Weather = {
|
export type Weather = {
|
||||||
@@ -107,3 +109,10 @@ export type Location = {
|
|||||||
localtime_epoch: number;
|
localtime_epoch: number;
|
||||||
localtime: string;
|
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 Exclusivity = 'normal' | 'ignore' | 'exclusive';
|
||||||
export type Anchor = "left" | "right" | "top" | "down";
|
export type Anchor = "left" | "right" | "top" | "down";
|
||||||
export type Transition = "none" | "crossfade" | "slide_right" | "slide_left" | "slide_up" | "slide_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 GLib from "gi://GLib?version=2.0"
|
||||||
import GdkPixbuf from "gi://GdkPixbuf";
|
import GdkPixbuf from "gi://GdkPixbuf";
|
||||||
import { NotificationArgs } from "types/utils/notify"
|
import { NotificationArgs } from "types/utils/notify"
|
||||||
|
import { SubstituteKeys } from "./types/utils";
|
||||||
|
|
||||||
export type Binding<T> = import("types/service").Binding<any, any, T>
|
export type Binding<T> = import("types/service").Binding<any, any, T>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns substitute icon || name || fallback icon
|
* @returns substitute icon || name || fallback icon
|
||||||
*/
|
*/
|
||||||
export function icon(name: string | null, fallback = icons.missing) {
|
export function icon(name: string | null, fallback = icons.missing) {
|
||||||
|
const validateSubstitute = (name: string): name is SubstituteKeys => name in substitutes;
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return fallback || ""
|
return fallback || ""
|
||||||
|
|
||||||
if (GLib.file_test(name, GLib.FileTest.EXISTS))
|
if (GLib.file_test(name, GLib.FileTest.EXISTS))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
const icon = (substitutes[name] || name)
|
let icon: string = name;
|
||||||
|
|
||||||
|
if (validateSubstitute(name)) {
|
||||||
|
icon = substitutes[name];
|
||||||
|
}
|
||||||
|
|
||||||
if (Utils.lookUpIcon(icon))
|
if (Utils.lookUpIcon(icon))
|
||||||
return icon
|
return icon
|
||||||
|
|
||||||
|
|||||||
@@ -196,4 +196,4 @@ export default {
|
|||||||
patchy_light_snow_with_thunder: "weather-snow-symbolic",
|
patchy_light_snow_with_thunder: "weather-snow-symbolic",
|
||||||
moderate_or_heavy_snow_with_thunder: "weather-snow-symbolic",
|
moderate_or_heavy_snow_with_thunder: "weather-snow-symbolic",
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|||||||
@@ -51,4 +51,4 @@ export const weatherIcons = {
|
|||||||
moderate_or_heavy_rain_with_thunder: "",
|
moderate_or_heavy_rain_with_thunder: "",
|
||||||
patchy_light_snow_with_thunder: "",
|
patchy_light_snow_with_thunder: "",
|
||||||
moderate_or_heavy_snow_with_thunder: "",
|
moderate_or_heavy_snow_with_thunder: "",
|
||||||
};
|
} as const;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const hyprland = await Service.import("hyprland");
|
const hyprland = await Service.import("hyprland");
|
||||||
|
import { DropdownMenuProps } from "lib/types/dropdownmenu";
|
||||||
import { Exclusivity } from "lib/types/widget";
|
import { Exclusivity } from "lib/types/widget";
|
||||||
import { bash } from "lib/utils";
|
import { bash } from "lib/utils";
|
||||||
import { Monitor } from "types/service/hyprland";
|
import { Monitor } from "types/service/hyprland";
|
||||||
@@ -99,7 +100,8 @@ setTimeout(() => {
|
|||||||
initRender.value = false;
|
initRender.value = false;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
export default ({
|
export default (
|
||||||
|
{
|
||||||
name,
|
name,
|
||||||
child,
|
child,
|
||||||
layout = "center",
|
layout = "center",
|
||||||
@@ -107,7 +109,8 @@ export default ({
|
|||||||
exclusivity = "ignore" as Exclusivity,
|
exclusivity = "ignore" as Exclusivity,
|
||||||
fixed = false,
|
fixed = false,
|
||||||
...props
|
...props
|
||||||
}) =>
|
}: DropdownMenuProps
|
||||||
|
) =>
|
||||||
Widget.Window({
|
Widget.Window({
|
||||||
name,
|
name,
|
||||||
class_names: [name, "dropdown-menu"],
|
class_names: [name, "dropdown-menu"],
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { WINDOW_LAYOUTS } from "globals/window";
|
||||||
|
import { LayoutFunction, Layouts, PopupWindowProps } from "lib/types/popupwindow";
|
||||||
import { Exclusivity, Transition } from "lib/types/widget";
|
import { Exclusivity, Transition } from "lib/types/widget";
|
||||||
|
|
||||||
type Opts = {
|
type Opts = {
|
||||||
@@ -32,7 +34,7 @@ const PopupRevealer = (name: string, child: any, transition = "slide_down" as Tr
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const Layout = (name: string, child: any, transition: Transition) => ({
|
const Layout: LayoutFunction = (name: string, child: any, transition: Transition) => ({
|
||||||
center: () =>
|
center: () =>
|
||||||
Widget.CenterBox(
|
Widget.CenterBox(
|
||||||
{},
|
{},
|
||||||
@@ -150,6 +152,10 @@ const Layout = (name: string, child: any, transition: Transition) => ({
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isValidLayout = (layout: string): layout is Layouts => {
|
||||||
|
return WINDOW_LAYOUTS.includes(layout);
|
||||||
|
};
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
name,
|
name,
|
||||||
child,
|
child,
|
||||||
@@ -157,8 +163,12 @@ export default ({
|
|||||||
transition,
|
transition,
|
||||||
exclusivity = "ignore" as Exclusivity,
|
exclusivity = "ignore" as Exclusivity,
|
||||||
...props
|
...props
|
||||||
}) =>
|
}: PopupWindowProps) => {
|
||||||
Widget.Window({
|
const layoutFn = isValidLayout(layout) ? layout : "center";
|
||||||
|
|
||||||
|
const layoutWidget = Layout(name, child, transition)[layoutFn]();
|
||||||
|
|
||||||
|
return Widget.Window({
|
||||||
name,
|
name,
|
||||||
class_names: [name, "popup-window"],
|
class_names: [name, "popup-window"],
|
||||||
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
||||||
@@ -167,6 +177,7 @@ export default ({
|
|||||||
exclusivity,
|
exclusivity,
|
||||||
layer: "top",
|
layer: "top",
|
||||||
anchor: ["top", "bottom", "right", "left"],
|
anchor: ["top", "bottom", "right", "left"],
|
||||||
child: Layout(name, child, transition)[layout](),
|
child: layoutWidget,
|
||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
const getIcon = (audioVol, isMuted) => {
|
const speakerIcons = {
|
||||||
const speakerIcons = {
|
|
||||||
101: "audio-volume-overamplified-symbolic",
|
101: "audio-volume-overamplified-symbolic",
|
||||||
66: "audio-volume-high-symbolic",
|
66: "audio-volume-high-symbolic",
|
||||||
34: "audio-volume-medium-symbolic",
|
34: "audio-volume-medium-symbolic",
|
||||||
1: "audio-volume-low-symbolic",
|
1: "audio-volume-low-symbolic",
|
||||||
0: "audio-volume-muted-symbolic",
|
0: "audio-volume-muted-symbolic",
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
const inputIcons = {
|
const inputIcons = {
|
||||||
|
101: "microphone-sensitivity-high-symbolic",
|
||||||
66: "microphone-sensitivity-high-symbolic",
|
66: "microphone-sensitivity-high-symbolic",
|
||||||
34: "microphone-sensitivity-medium-symbolic",
|
34: "microphone-sensitivity-medium-symbolic",
|
||||||
1: "microphone-sensitivity-low-symbolic",
|
1: "microphone-sensitivity-low-symbolic",
|
||||||
0: "microphone-disabled-symbolic",
|
0: "microphone-disabled-symbolic",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type IconVolumes = keyof typeof speakerIcons;
|
||||||
|
|
||||||
|
const getIcon = (audioVol: IconVolumes, isMuted: boolean) => {
|
||||||
|
const thresholds: IconVolumes[] = [101, 66, 34, 1, 0];
|
||||||
const icon = isMuted
|
const icon = isMuted
|
||||||
? 0
|
? 0
|
||||||
: [101, 66, 34, 1, 0].find((threshold) => threshold <= audioVol * 100);
|
: thresholds.find((threshold) => threshold <= audioVol * 100) || 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
spkr: speakerIcons[icon],
|
spkr: speakerIcons[icon],
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>) => {
|
|||||||
Widget.Label({
|
Widget.Label({
|
||||||
vpack: "start",
|
vpack: "start",
|
||||||
class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? "active" : ""} txt-icon`,
|
class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? "active" : ""} txt-icon`,
|
||||||
label: getBluetoothIcon(`${device["icon-name"]}-symbolic`),
|
label: getBluetoothIcon(`${device["icon_name"]}-symbolic`),
|
||||||
}),
|
}),
|
||||||
Widget.Box({
|
Widget.Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|||||||
@@ -1,35 +1,43 @@
|
|||||||
import { Weather } from "lib/types/weather.js";
|
import { Weather, WeatherIcon, WeatherIconTitle } from "lib/types/weather.js";
|
||||||
import { Variable } from "types/variable.js";
|
import { Variable } from "types/variable.js";
|
||||||
import { weatherIcons } from "modules/icons/weather.js";
|
import { weatherIcons } from "modules/icons/weather.js";
|
||||||
|
import { isValidWeatherIconTitle } from "globals/weather";
|
||||||
|
|
||||||
export const HourlyIcon = (theWeather: Variable<Weather>, getNextEpoch: any) => {
|
export const HourlyIcon = (theWeather: Variable<Weather>, getNextEpoch: any) => {
|
||||||
const getIconQuery = (wthr: Weather) => {
|
const getIconQuery = (wthr: Weather): WeatherIconTitle => {
|
||||||
|
|
||||||
const nextEpoch = getNextEpoch(wthr);
|
const nextEpoch = getNextEpoch(wthr);
|
||||||
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find(
|
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find(
|
||||||
(h) => h.time_epoch === nextEpoch,
|
(h) => h.time_epoch === nextEpoch,
|
||||||
);
|
);
|
||||||
|
|
||||||
let iconQuery = weatherAtEpoch?.condition.text
|
if (weatherAtEpoch === undefined) {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconQuery = weatherAtEpoch.condition.text
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replaceAll(" ", "_")
|
.replaceAll(" ", "_");
|
||||||
|| "warning"
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!weatherAtEpoch?.is_day && iconQuery === "partly_cloudy") {
|
if (!weatherAtEpoch?.is_day && iconQuery === "partly_cloudy") {
|
||||||
iconQuery = "partly_cloudy_night";
|
iconQuery = "partly_cloudy_night";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValidWeatherIconTitle(iconQuery)) {
|
||||||
return iconQuery;
|
return iconQuery;
|
||||||
|
} else {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
hpack: "center",
|
hpack: "center",
|
||||||
child: theWeather.bind("value").as((w) => {
|
child: theWeather.bind("value").as((w) => {
|
||||||
let weatherIcn = "-";
|
let weatherIcn: WeatherIcon;
|
||||||
|
|
||||||
const iconQuery = getIconQuery(w);
|
const iconQuery = getIconQuery(w);
|
||||||
weatherIcn = weatherIcons[iconQuery];
|
weatherIcn = weatherIcons[iconQuery] || weatherIcons["warning"];
|
||||||
return Widget.Label({
|
return Widget.Label({
|
||||||
hpack: "center",
|
hpack: "center",
|
||||||
class_name: "hourly-weather-icon txt-icon",
|
class_name: "hourly-weather-icon txt-icon",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const { terminal } = options;
|
|||||||
const { enable_gpu } = options.menus.dashboard.stats;
|
const { enable_gpu } = options.menus.dashboard.stats;
|
||||||
|
|
||||||
const Stats = () => {
|
const Stats = () => {
|
||||||
const divide = ([total, free]) => free / total;
|
const divide = ([total, free]: number[]) => free / total;
|
||||||
|
|
||||||
const formatSizeInGB = (sizeInKB: number) =>
|
const formatSizeInGB = (sizeInKB: number) =>
|
||||||
Number((sizeInKB / 1024 ** 2).toFixed(2));
|
Number((sizeInKB / 1024 ** 2).toFixed(2));
|
||||||
@@ -26,7 +26,8 @@ const Stats = () => {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return divide([100, cpuOut.split(/\s+/)[1].replace(",", ".")]);
|
const freeCpu = parseFloat(cpuOut.split(/\s+/)[1].replace(",", "."));
|
||||||
|
return divide([100, freeCpu]);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
const powerProfiles = await Service.import("powerprofiles");
|
const powerProfiles = await Service.import("powerprofiles");
|
||||||
|
import { PowerProfile, PowerProfileObject, PowerProfiles } from "lib/types/powerprofiles.js";
|
||||||
import icons from "../../../icons/index.js";
|
import icons from "../../../icons/index.js";
|
||||||
|
|
||||||
const EnergyProfiles = () => {
|
const EnergyProfiles = () => {
|
||||||
|
const isValidProfile = (profile: string): profile is PowerProfile =>
|
||||||
|
profile === "power-saver" || profile === "balanced" || profile === "performance";
|
||||||
|
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: "menu-section-container energy",
|
class_name: "menu-section-container energy",
|
||||||
vertical: true,
|
vertical: true,
|
||||||
@@ -21,13 +25,20 @@ const EnergyProfiles = () => {
|
|||||||
vpack: "fill",
|
vpack: "fill",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: powerProfiles.bind("profiles").as((profiles) => {
|
children: powerProfiles.bind("profiles").as((profiles: PowerProfiles) => {
|
||||||
return profiles.map((prof) => {
|
return profiles.map((prof: PowerProfileObject) => {
|
||||||
const ProfileLabels = {
|
const profileLabels = {
|
||||||
"power-saver": "Power Saver",
|
"power-saver": "Power Saver",
|
||||||
balanced: "Balanced",
|
balanced: "Balanced",
|
||||||
performance: "Performance",
|
performance: "Performance",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const profileType = prof.Profile;
|
||||||
|
|
||||||
|
if (!isValidProfile(profileType)) {
|
||||||
|
return profileLabels.balanced;
|
||||||
|
}
|
||||||
|
|
||||||
return Widget.Button({
|
return Widget.Button({
|
||||||
on_primary_click: () => {
|
on_primary_click: () => {
|
||||||
powerProfiles.active_profile = prof.Profile;
|
powerProfiles.active_profile = prof.Profile;
|
||||||
@@ -39,11 +50,11 @@ const EnergyProfiles = () => {
|
|||||||
children: [
|
children: [
|
||||||
Widget.Icon({
|
Widget.Icon({
|
||||||
class_name: "power-profile-icon",
|
class_name: "power-profile-icon",
|
||||||
icon: icons.powerprofile[prof.Profile],
|
icon: icons.powerprofile[profileType],
|
||||||
}),
|
}),
|
||||||
Widget.Label({
|
Widget.Label({
|
||||||
class_name: "power-profile-label",
|
class_name: "power-profile-label",
|
||||||
label: ProfileLabels[prof.Profile],
|
label: profileLabels[profileType],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
import { MprisPlayer } from "types/service/mpris.js";
|
import { MprisPlayer } from "types/service/mpris.js";
|
||||||
import icons from "../../../icons/index.js";
|
import icons from "../../../icons/index.js";
|
||||||
|
import { LoopStatus, PlaybackStatus } from "lib/types/mpris.js";
|
||||||
const media = await Service.import("mpris");
|
const media = await Service.import("mpris");
|
||||||
|
|
||||||
const Controls = (getPlayerInfo: Function) => {
|
const Controls = (getPlayerInfo: Function) => {
|
||||||
|
const isValidLoopStatus = (status: string): status is LoopStatus =>
|
||||||
|
["none", "track", "playlist"].includes(status);
|
||||||
|
|
||||||
|
const isValidPlaybackStatus = (status: string): status is PlaybackStatus =>
|
||||||
|
["playing", "paused", "stopped"].includes(status);
|
||||||
|
|
||||||
const isLoopActive = (player: MprisPlayer) => {
|
const isLoopActive = (player: MprisPlayer) => {
|
||||||
return player["loop-status"] !== null &&
|
return player["loop_status"] !== null &&
|
||||||
["track", "playlist"].includes(player["loop-status"].toLowerCase())
|
["track", "playlist"].includes(player["loop_status"].toLowerCase())
|
||||||
? "active"
|
? "active"
|
||||||
: "";
|
: "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const isShuffleActive = (player: MprisPlayer) => {
|
const isShuffleActive = (player: MprisPlayer) => {
|
||||||
return player["shuffle-status"] !== null && player["shuffle-status"]
|
return player["shuffle_status"] !== null && player["shuffle_status"]
|
||||||
? "active"
|
? "active"
|
||||||
: "";
|
: "";
|
||||||
};
|
};
|
||||||
@@ -98,13 +105,18 @@ const Controls = (getPlayerInfo: Function) => {
|
|||||||
media,
|
media,
|
||||||
"changed",
|
"changed",
|
||||||
() => {
|
() => {
|
||||||
const foundPlayer = getPlayerInfo();
|
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||||
if (foundPlayer === undefined) {
|
if (foundPlayer === undefined) {
|
||||||
return icons.mpris["paused"];
|
return icons.mpris["paused"];
|
||||||
}
|
}
|
||||||
return icons.mpris[
|
const playbackStatus = foundPlayer.play_back_status?.toLowerCase();
|
||||||
foundPlayer.play_back_status.toLowerCase()
|
|
||||||
];
|
if (playbackStatus && isValidPlaybackStatus(playbackStatus)) {
|
||||||
|
return icons.mpris[playbackStatus];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return icons.mpris["paused"];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@@ -161,18 +173,21 @@ const Controls = (getPlayerInfo: Function) => {
|
|||||||
child: Widget.Icon({
|
child: Widget.Icon({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(media, () => {
|
self.hook(media, () => {
|
||||||
const foundPlayer = getPlayerInfo();
|
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||||
|
|
||||||
if (foundPlayer === undefined) {
|
if (foundPlayer === undefined) {
|
||||||
self.icon = icons.mpris.loop["none"];
|
self.icon = icons.mpris.loop["none"];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.icon =
|
const loopStatus = foundPlayer.loop_status?.toLowerCase();
|
||||||
foundPlayer.loop_status === null
|
|
||||||
? icons.mpris.loop["none"]
|
if (loopStatus && isValidLoopStatus(loopStatus)) {
|
||||||
: icons.mpris.loop[
|
self.icon = icons.mpris.loop[loopStatus];
|
||||||
foundPlayer.loop_status?.toLowerCase()
|
}
|
||||||
];
|
else {
|
||||||
|
self.icon = icons.mpris.loop["none"];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -37,17 +37,17 @@ const Media = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isPlaying = media.players.find(
|
const isPlaying = media.players.find(
|
||||||
(p) => p["play-back-status"] === "Playing",
|
(p) => p["play_back_status"] === "Playing",
|
||||||
);
|
);
|
||||||
|
|
||||||
const playerStillExists = media.players.some(
|
const playerStillExists = media.players.some(
|
||||||
(p) => curPlayer.value === p["bus-name"],
|
(p) => curPlayer.value === p["bus_name"],
|
||||||
);
|
);
|
||||||
|
|
||||||
const nextPlayerUp = media.players.sort(
|
const nextPlayerUp = media.players.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
statusOrder[a["play-back-status"]] -
|
statusOrder[a["play_back_status"]] -
|
||||||
statusOrder[b["play-back-status"]],
|
statusOrder[b["play_back_status"]],
|
||||||
)[0].bus_name;
|
)[0].bus_name;
|
||||||
|
|
||||||
if (isPlaying || !playerStillExists) {
|
if (isPlaying || !playerStillExists) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Network } from "types/service/network.js";
|
import { Network } from "types/service/network.js";
|
||||||
import { AccessPoint } from "lib/types/network.js";
|
import { AccessPoint, WifiStatus } from "lib/types/network.js";
|
||||||
import { Variable } from "types/variable.js";
|
import { Variable } from "types/variable.js";
|
||||||
import { getWifiIcon } from "../utils.js";
|
import { getWifiIcon } from "../utils.js";
|
||||||
|
import { WIFI_STATUS_MAP } from "globals/network.js";
|
||||||
const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>, connecting: Variable<string>) => {
|
const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>, connecting: Variable<string>) => {
|
||||||
const getIdBySsid = (ssid: string, nmcliOutput: string) => {
|
const getIdBySsid = (ssid: string, nmcliOutput: string) => {
|
||||||
const lines = nmcliOutput.trim().split("\n");
|
const lines = nmcliOutput.trim().split("\n");
|
||||||
@@ -14,33 +15,36 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WifiStatusMap = {
|
const isValidWifiStatus = (status: string): status is WifiStatus => {
|
||||||
unknown: "Status Unknown",
|
return status in WIFI_STATUS_MAP;
|
||||||
unmanaged: "Unmanaged",
|
|
||||||
unavailable: "Unavailable",
|
|
||||||
disconnected: "Disconnected",
|
|
||||||
prepare: "Preparing Connecting",
|
|
||||||
config: "Connecting",
|
|
||||||
need_auth: "Needs Authentication",
|
|
||||||
ip_config: "Requesting IP",
|
|
||||||
ip_check: "Checking Access",
|
|
||||||
secondaries: "Waiting on Secondaries",
|
|
||||||
activated: "Connected",
|
|
||||||
deactivating: "Disconnecting",
|
|
||||||
failed: "Connection Failed",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getWifiStatus = () => {
|
||||||
|
const wifiState = network.wifi.state?.toLowerCase();
|
||||||
|
|
||||||
|
if (wifiState && isValidWifiStatus(wifiState)) {
|
||||||
|
return WIFI_STATUS_MAP[wifiState];
|
||||||
|
}
|
||||||
|
return WIFI_STATUS_MAP["unknown"];
|
||||||
|
}
|
||||||
|
|
||||||
self.hook(network, () => {
|
self.hook(network, () => {
|
||||||
Utils.merge([staging.bind("value"), connecting.bind("value")], () => {
|
Utils.merge([staging.bind("value"), connecting.bind("value")], () => {
|
||||||
// Sometimes the network service will yield a "this._device is undefined" when
|
// NOTE: Sometimes the network service will yield a "this._device is undefined" when
|
||||||
// trying to access the "access_points" property. So we must validate that
|
// trying to access the "access_points" property. So we must validate that
|
||||||
// it's not 'undefined'
|
// it's not 'undefined'
|
||||||
//
|
// --
|
||||||
// Also this is an AGS bug that needs to be fixed
|
// Also this is an AGS bug that needs to be fixed
|
||||||
let WAPs =
|
|
||||||
network.wifi._device !== undefined ? network.wifi["access-points"] : [];
|
// TODO: Remove @ts-ignore once AGS bug is fixed
|
||||||
|
// @ts-ignore
|
||||||
|
let WAPs = network.wifi._device !== undefined
|
||||||
|
? network.wifi["access_points"]
|
||||||
|
: [];
|
||||||
|
|
||||||
const dedupeWAPs = () => {
|
const dedupeWAPs = () => {
|
||||||
const dedupMap = {};
|
const dedupMap: Record<string, AccessPoint> = {};
|
||||||
WAPs.forEach((item: AccessPoint) => {
|
WAPs.forEach((item: AccessPoint) => {
|
||||||
if (item.ssid !== null && !Object.hasOwnProperty.call(dedupMap, item.ssid)) {
|
if (item.ssid !== null && !Object.hasOwnProperty.call(dedupMap, item.ssid)) {
|
||||||
dedupMap[item.ssid] = item;
|
dedupMap[item.ssid] = item;
|
||||||
@@ -153,8 +157,7 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
|||||||
child: Widget.Label({
|
child: Widget.Label({
|
||||||
hpack: "start",
|
hpack: "start",
|
||||||
class_name: "connection-status dim",
|
class_name: "connection-status dim",
|
||||||
label:
|
label: getWifiStatus()
|
||||||
WifiStatusMap[network.wifi.state.toLowerCase()],
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,22 +1,8 @@
|
|||||||
import icons from "../../../../icons/index.js";
|
import { Notification } from "types/service/notifications.js";
|
||||||
|
import { NotificationIcon } from "lib/types/notification.js";
|
||||||
const NotificationIcon = ({ app_entry, app_icon, app_name }) => {
|
import { getNotificationIcon } from "globals/notification";
|
||||||
let icon = icons.fallback.notification;
|
|
||||||
|
|
||||||
if (
|
|
||||||
Utils.lookUpIcon(app_name) ||
|
|
||||||
Utils.lookUpIcon(app_name.toLowerCase() || "")
|
|
||||||
)
|
|
||||||
icon = Utils.lookUpIcon(app_name)
|
|
||||||
? app_name
|
|
||||||
: Utils.lookUpIcon(app_name.toLowerCase())
|
|
||||||
? app_name.toLowerCase()
|
|
||||||
: "";
|
|
||||||
|
|
||||||
if (Utils.lookUpIcon(app_icon) && icon === "") icon = app_icon;
|
|
||||||
|
|
||||||
if (Utils.lookUpIcon(app_entry || "") && icon === "") icon = app_entry || "";
|
|
||||||
|
|
||||||
|
const NotificationIcon = ({ app_entry = "", app_icon = "", app_name = "" }: Partial<Notification>) => {
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
css: `
|
css: `
|
||||||
min-width: 2rem;
|
min-width: 2rem;
|
||||||
@@ -24,7 +10,7 @@ const NotificationIcon = ({ app_entry, app_icon, app_name }) => {
|
|||||||
`,
|
`,
|
||||||
child: Widget.Icon({
|
child: Widget.Icon({
|
||||||
class_name: "notification-icon menu",
|
class_name: "notification-icon menu",
|
||||||
icon,
|
icon: getNotificationIcon(app_name, app_icon, app_entry),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const NotificationPager = (curPage: Variable<number>) => {
|
|||||||
class_name: "notification-menu-pager",
|
class_name: "notification-menu-pager",
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
vexpand: false,
|
vexpand: false,
|
||||||
children: Utils.merge([curPage.bind("value"), displayedTotal.bind("value"), notifs.bind("notifications")], (currentPage, dispTotal, notifications) => {
|
children: Utils.merge([curPage.bind("value"), displayedTotal.bind("value"), notifs.bind("notifications")], (currentPage, dispTotal, _) => {
|
||||||
return [
|
return [
|
||||||
Widget.Button({
|
Widget.Button({
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import icons from "lib/icons";
|
|
||||||
import { PowerOptions } from "lib/types/options";
|
import { PowerOptions } from "lib/types/options";
|
||||||
import options from "options";
|
import options from "options";
|
||||||
import powermenu from "../power/helpers/actions";
|
import powermenu from "../power/helpers/actions";
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import options from "options.js";
|
|
||||||
import DropdownMenu from "../DropdownMenu.js";
|
import DropdownMenu from "../DropdownMenu.js";
|
||||||
import { PowerButton } from "./button.js";
|
import { PowerButton } from "./button.js";
|
||||||
|
|
||||||
const { showLabel } = options.menus.power;
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return DropdownMenu({
|
return DropdownMenu({
|
||||||
name: "powerdropdownmenu",
|
name: "powerdropdownmenu",
|
||||||
|
|||||||
@@ -1,22 +1,7 @@
|
|||||||
import icons from "../../icons/index.js";
|
import { Notification } from "types/service/notifications.js";
|
||||||
|
import { getNotificationIcon } from "globals/notification.js";
|
||||||
const NotificationIcon = ({ app_entry, app_icon, app_name }) => {
|
|
||||||
let icon = icons.fallback.notification;
|
|
||||||
|
|
||||||
if (
|
|
||||||
Utils.lookUpIcon(app_name) ||
|
|
||||||
Utils.lookUpIcon(app_name.toLowerCase() || "")
|
|
||||||
)
|
|
||||||
icon = Utils.lookUpIcon(app_name)
|
|
||||||
? app_name
|
|
||||||
: Utils.lookUpIcon(app_name.toLowerCase())
|
|
||||||
? app_name.toLowerCase()
|
|
||||||
: "";
|
|
||||||
|
|
||||||
if (Utils.lookUpIcon(app_icon) && icon === "") icon = app_icon;
|
|
||||||
|
|
||||||
if (Utils.lookUpIcon(app_entry || "") && icon === "") icon = app_entry || "";
|
|
||||||
|
|
||||||
|
const NotificationIcon = ({ app_entry = "", app_icon = "", app_name = "" }: Partial<Notification>) => {
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
css: `
|
css: `
|
||||||
min-width: 2rem;
|
min-width: 2rem;
|
||||||
@@ -24,7 +9,7 @@ const NotificationIcon = ({ app_entry, app_icon, app_name }) => {
|
|||||||
`,
|
`,
|
||||||
child: Widget.Icon({
|
child: Widget.Icon({
|
||||||
class_name: "notification-icon",
|
class_name: "notification-icon",
|
||||||
icon,
|
icon: getNotificationIcon(app_name, app_icon, app_entry)
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { OSDOrientation } from "lib/types/options";
|
|
||||||
import brightness from "services/Brightness"
|
import brightness from "services/Brightness"
|
||||||
const audio = await Service.import("audio")
|
const audio = await Service.import("audio")
|
||||||
|
|
||||||
export const OSDIcon = (ort: OSDOrientation) => {
|
export const OSDIcon = () => {
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: "osd-icon-container",
|
class_name: "osd-icon-container",
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const audio = await Service.import("audio")
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
enable,
|
enable,
|
||||||
|
duration,
|
||||||
orientation,
|
orientation,
|
||||||
location,
|
location,
|
||||||
active_monitor,
|
active_monitor,
|
||||||
@@ -21,8 +22,6 @@ hyprland.active.connect("changed", () => {
|
|||||||
curMonitor.value = hyprland.active.monitor.id;
|
curMonitor.value = hyprland.active.monitor.id;
|
||||||
})
|
})
|
||||||
|
|
||||||
const DELAY = 2500;
|
|
||||||
|
|
||||||
let count = 0
|
let count = 0
|
||||||
const handleReveal = (self: any, property: string) => {
|
const handleReveal = (self: any, property: string) => {
|
||||||
if (!enable.value) {
|
if (!enable.value) {
|
||||||
@@ -30,7 +29,7 @@ const handleReveal = (self: any, property: string) => {
|
|||||||
}
|
}
|
||||||
self[property] = true
|
self[property] = true
|
||||||
count++
|
count++
|
||||||
Utils.timeout(DELAY, () => {
|
Utils.timeout(duration.value, () => {
|
||||||
count--
|
count--
|
||||||
|
|
||||||
if (count === 0)
|
if (count === 0)
|
||||||
@@ -39,8 +38,6 @@ const handleReveal = (self: any, property: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const renderOSD = () => {
|
const renderOSD = () => {
|
||||||
|
|
||||||
|
|
||||||
return Widget.Revealer({
|
return Widget.Revealer({
|
||||||
transition: "crossfade",
|
transition: "crossfade",
|
||||||
reveal_child: false,
|
reveal_child: false,
|
||||||
@@ -72,16 +69,16 @@ const renderOSD = () => {
|
|||||||
children: orientation.bind("value").as(ort => {
|
children: orientation.bind("value").as(ort => {
|
||||||
if (ort === "vertical") {
|
if (ort === "vertical") {
|
||||||
return [
|
return [
|
||||||
OSDLabel(ort),
|
OSDLabel(),
|
||||||
OSDBar(ort),
|
OSDBar(ort),
|
||||||
OSDIcon(ort)
|
OSDIcon()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
OSDIcon(ort),
|
OSDIcon(),
|
||||||
OSDBar(ort),
|
OSDBar(ort),
|
||||||
OSDLabel(ort),
|
OSDLabel(),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { OSDOrientation } from "lib/types/options";
|
|
||||||
import brightness from "services/Brightness"
|
import brightness from "services/Brightness"
|
||||||
import options from "options"
|
import options from "options"
|
||||||
const audio = await Service.import("audio")
|
const audio = await Service.import("audio")
|
||||||
|
|
||||||
export const OSDLabel = (ort: OSDOrientation) => {
|
export const OSDLabel = () => {
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: "osd-label-container",
|
class_name: "osd-label-container",
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ const options = mkOptions(OPTIONS, {
|
|||||||
},
|
},
|
||||||
osd: {
|
osd: {
|
||||||
scaling: opt(100),
|
scaling: opt(100),
|
||||||
|
duration: opt(2500),
|
||||||
enable: opt(true),
|
enable: opt(true),
|
||||||
orientation: opt<OSDOrientation>("vertical"),
|
orientation: opt<OSDOrientation>("vertical"),
|
||||||
opacity: opt(100),
|
opacity: opt(100),
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import options from "options";
|
import options from "options";
|
||||||
import { bash, dependencies } from "lib/utils";
|
import { bash, dependencies } from "lib/utils";
|
||||||
import { MatugenColors } from "lib/types/options";
|
import { MatugenColors, RecursiveOptionsObject } from "lib/types/options";
|
||||||
import { initializeTrackers } from "./options_trackers";
|
import { initializeTrackers } from "./options_trackers";
|
||||||
import { generateMatugenColors, replaceHexValues } from "../services/matugen/index";
|
import { generateMatugenColors, replaceHexValues } from "../services/matugen/index";
|
||||||
|
import { isHexColor, isOpt, isRecursiveOptionsObject } from "globals/variables";
|
||||||
|
import { Opt } from "lib/option";
|
||||||
|
|
||||||
const deps = [
|
const deps = [
|
||||||
"font",
|
"font",
|
||||||
@@ -13,27 +15,37 @@ const deps = [
|
|||||||
"bar.battery.blocks",
|
"bar.battery.blocks",
|
||||||
];
|
];
|
||||||
|
|
||||||
function extractVariables(theme: typeof options.theme, prefix = "", matugenColors: MatugenColors | undefined) {
|
function extractVariables(
|
||||||
|
theme: RecursiveOptionsObject,
|
||||||
|
prefix = "",
|
||||||
|
matugenColors?: MatugenColors
|
||||||
|
): string[] {
|
||||||
let result = [] as string[];
|
let result = [] as string[];
|
||||||
for (let key in theme) {
|
for (let key in theme) {
|
||||||
if (theme.hasOwnProperty(key)) {
|
if (!theme.hasOwnProperty(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const value = theme[key];
|
const value = theme[key];
|
||||||
|
|
||||||
const newPrefix = prefix ? `${prefix}-${key}` : key;
|
const newPrefix = prefix ? `${prefix}-${key}` : key;
|
||||||
|
|
||||||
const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value.value);
|
const isColor = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value.value);
|
||||||
const replacedValue = isColor && matugenColors !== undefined ? replaceHexValues(value.value, matugenColors) : value.value;
|
const replacedValue = isColor && matugenColors !== undefined ? replaceHexValues(value.value, matugenColors) : value.value;
|
||||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
||||||
|
if (typeof value === 'function') {
|
||||||
|
result.push(`$${newPrefix}: ${replacedValue};`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) continue;
|
||||||
|
|
||||||
if (typeof value.value !== 'undefined') {
|
if (typeof value.value !== 'undefined') {
|
||||||
result.push(`$${newPrefix}: ${replacedValue};`);
|
result.push(`$${newPrefix}: ${replacedValue};`);
|
||||||
} else {
|
} else {
|
||||||
result = result.concat(extractVariables(value, newPrefix, matugenColors));
|
result = result.concat(extractVariables(value as RecursiveOptionsObject, newPrefix, matugenColors));
|
||||||
}
|
|
||||||
} else if (typeof value === 'function' && value.name === 'opt') {
|
|
||||||
result.push(`$${newPrefix}: ${replacedValue};`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
border-radius: if($osd-orientation =="vertical", 0em 0em $osd-radius $osd-radius, $osd-radius 0em 0em $osd-radius );
|
border-radius: if($osd-orientation =="vertical", 0em 0em $osd-radius $osd-radius, $osd-radius 0em 0em $osd-radius );
|
||||||
|
|
||||||
.osd-icon {
|
.osd-icon {
|
||||||
font-size: 2em;
|
font-size: 2.1em;
|
||||||
padding: if($osd-orientation =="vertical", 0.2em 0em, 0em 0.2em);
|
padding: if($osd-orientation =="vertical", 0.2em 0em, 0em 0.4em);
|
||||||
color: $osd-icon;
|
color: $osd-icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"noEmit": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
@@ -10,11 +11,14 @@
|
|||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"types",
|
"types",
|
||||||
"lib/types/globals",
|
"lib/types/globals",
|
||||||
],
|
],
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const OSDSettings = () => {
|
|||||||
children: [
|
children: [
|
||||||
Header('On Screen Display'),
|
Header('On Screen Display'),
|
||||||
Option({ opt: options.theme.osd.enable, title: 'Enabled', type: 'boolean' }),
|
Option({ opt: options.theme.osd.enable, title: 'Enabled', type: 'boolean' }),
|
||||||
|
Option({ opt: options.theme.osd.duration, title: 'Duration', type: 'number', min: 100, max: 10000, increment: 500 }),
|
||||||
Option({ opt: options.theme.osd.orientation, title: 'Orientation', type: 'enum', enums: ["horizontal", "vertical"] }),
|
Option({ opt: options.theme.osd.orientation, title: 'Orientation', type: 'enum', enums: ["horizontal", "vertical"] }),
|
||||||
Option({ opt: options.theme.osd.location, title: 'Position', subtitle: 'Position of the OSD on the screen', type: 'enum', enums: ["top left", "top", "top right", "right", "bottom right", "bottom", "bottom left", "left"] }),
|
Option({ opt: options.theme.osd.location, title: 'Position', subtitle: 'Position of the OSD on the screen', type: 'enum', enums: ["top left", "top", "top right", "right", "bottom right", "bottom", "bottom left", "left"] }),
|
||||||
Option({ opt: options.theme.osd.monitor, title: 'Monitor', subtitle: 'The ID of the monitor on which to display the OSD', type: 'number' }),
|
Option({ opt: options.theme.osd.monitor, title: 'Monitor', subtitle: 'The ID of the monitor on which to display the OSD', type: 'number' }),
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import Gtk from "gi://Gtk?version=3.0";
|
|||||||
import Gio from "gi://Gio"
|
import Gio from "gi://Gio"
|
||||||
import { bash, Notify } from "lib/utils";
|
import { bash, Notify } from "lib/utils";
|
||||||
import icons from "lib/icons"
|
import icons from "lib/icons"
|
||||||
|
import { Config } from "lib/types/filechooser";
|
||||||
|
import { hexColorPattern } from "globals/useTheme";
|
||||||
|
import { isHexColor } from "globals/variables";
|
||||||
|
|
||||||
const whiteListedThemeProp = [
|
const whiteListedThemeProp = [
|
||||||
"theme.bar.buttons.style"
|
"theme.bar.buttons.style"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const loadJsonFile = (filePath: string): Config | null => {
|
||||||
// Helper functions
|
|
||||||
export const loadJsonFile = (filePath: string): object | null => {
|
|
||||||
let file = Gio.File.new_for_path(filePath as string);
|
let file = Gio.File.new_for_path(filePath as string);
|
||||||
let [success, content] = file.load_contents(null);
|
let [success, content] = file.load_contents(null);
|
||||||
|
|
||||||
@@ -32,10 +33,8 @@ export const saveConfigToFile = (config: object, filePath: string): void => {
|
|||||||
dataOutputStream.close(null);
|
dataOutputStream.close(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
export const filterConfigForThemeOnly = (config: Config): Config => {
|
||||||
|
let filteredConfig: Config = {};
|
||||||
export const filterConfigForThemeOnly = (config: object) => {
|
|
||||||
let filteredConfig = {};
|
|
||||||
|
|
||||||
for (let key in config) {
|
for (let key in config) {
|
||||||
if (typeof config[key] === 'string' && hexColorPattern.test(config[key])) {
|
if (typeof config[key] === 'string' && hexColorPattern.test(config[key])) {
|
||||||
@@ -47,8 +46,8 @@ export const filterConfigForThemeOnly = (config: object) => {
|
|||||||
return filteredConfig;
|
return filteredConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterConfigForNonTheme = (config: object) => {
|
export const filterConfigForNonTheme = (config: Config): Config => {
|
||||||
let filteredConfig = {};
|
let filteredConfig: Config = {};
|
||||||
for (let key in config) {
|
for (let key in config) {
|
||||||
if (whiteListedThemeProp.includes(key)) {
|
if (whiteListedThemeProp.includes(key)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -75,12 +74,11 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => {
|
|||||||
let jsonObject = JSON.parse(jsonString);
|
let jsonObject = JSON.parse(jsonString);
|
||||||
|
|
||||||
// Function to filter hex color pairs
|
// Function to filter hex color pairs
|
||||||
const filterHexColorPairs = (jsonObject: object) => {
|
const filterHexColorPairs = (jsonObject: Config) => {
|
||||||
const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
let filteredObject: Config = {};
|
||||||
let filteredObject = {};
|
|
||||||
|
|
||||||
for (let key in jsonObject) {
|
for (let key in jsonObject) {
|
||||||
if (typeof jsonObject[key] === 'string' && hexColorPattern.test(jsonObject[key])) {
|
if (typeof jsonObject[key] === 'string' && isHexColor(jsonObject[key])) {
|
||||||
filteredObject[key] = jsonObject[key];
|
filteredObject[key] = jsonObject[key];
|
||||||
} else if (whiteListedThemeProp.includes(key)) {
|
} else if (whiteListedThemeProp.includes(key)) {
|
||||||
filteredObject[key] = jsonObject[key];
|
filteredObject[key] = jsonObject[key];
|
||||||
@@ -92,9 +90,8 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Function to filter out hex color pairs (keep only non-hex color value)
|
// Function to filter out hex color pairs (keep only non-hex color value)
|
||||||
const filterOutHexColorPairs = (jsonObject: object) => {
|
const filterOutHexColorPairs = (jsonObject: Config) => {
|
||||||
const hexColorPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
let filteredObject: Config = {};
|
||||||
let filteredObject = {};
|
|
||||||
|
|
||||||
for (let key in jsonObject) {
|
for (let key in jsonObject) {
|
||||||
// do not add key-value pair if its in whiteListedThemeProp
|
// do not add key-value pair if its in whiteListedThemeProp
|
||||||
@@ -102,7 +99,7 @@ export const saveFileDialog = (filePath: string, themeOnly: boolean): void => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(typeof jsonObject[key] === 'string' && hexColorPattern.test(jsonObject[key]))) {
|
if (!(typeof jsonObject[key] === 'string' && isHexColor(jsonObject[key]))) {
|
||||||
filteredObject[key] = jsonObject[key];
|
filteredObject[key] = jsonObject[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,8 +189,19 @@ export const importFiles = (themeOnly: boolean = false): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (response === Gtk.ResponseType.ACCEPT) {
|
if (response === Gtk.ResponseType.ACCEPT) {
|
||||||
let filePath = dialog.get_filename();
|
let filePath: string | null = dialog.get_filename();
|
||||||
let importedConfig = loadJsonFile(filePath as string);
|
|
||||||
|
if (filePath === null) {
|
||||||
|
Notify({
|
||||||
|
summary: "Failed to import",
|
||||||
|
body: "No file selected.",
|
||||||
|
iconName: icons.ui.warning,
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let importedConfig = loadJsonFile(filePath);
|
||||||
|
|
||||||
if (!importedConfig) {
|
if (!importedConfig) {
|
||||||
dialog.destroy();
|
dialog.destroy();
|
||||||
|
|||||||
Reference in New Issue
Block a user