Minor: Refactor the code-base for better organization and compartmentalization. (#934)

* Clean up unused code

* Fix media player formatting issue for labels with new line characteres.

* Refactor the media player handlers into a class.

* More code cleanup and organize shared weather utils into distinct classes.

* Flatten some nesting.

* Move weather manager in dedicated class and build HTTP Utility class for Rest API calling.

* Remove logs

* Rebase master merge

* Reorg code (WIP)

* More reorg

* Delete utility scripts

* Reorg options

* Finish moving all options over

* Fix typescript issues

* Update options imports to default

* missed update

* Screw barrel files honestly, work of the devil.

* Only initialize power profiles if power-profiles-daemon is running.

* Fix window positioning and weather service naming

* style dir

* More organization

* Restructure types to be closer to their source

* Remove lib types and constants

* Update basic weather object to be saner with extensibility.

* Service updates

* Fix initialization strategy for services.

* Fix Config Manager to only emit changed objects and added missing temp converters.

* Update storage service to handle unit changes.

* Added cpu temp sensor auto-discovery

* Added missing JSDocs to services

* remove unused

* Migrate to network service.

* Fix network password issue.

* Move out password input into helper

* Rename password mask constant to be less double-negativey.

* Dropdown menu rename

* Added a component to edit JSON in the settings dialog (rough/WIP)

* Align settings

* Add and style JSON Editor.

* Adjust padding

* perf(shortcuts):  avoid unnecessary polling when shortcuts are disabled

Stops the recording poller when shortcuts are disabled, preventing redundant polling and reducing resource usage.

* Fix types and return value if shortcut not enabled.

* Move the swww daemon checking process outside of the wallpaper service into a dedicated deamon lifecyle processor.

* Add more string formatters and use title case for weather status (as it was).

* Fix startup errors.

* Rgba fix

* Remove zod from dependencies

---------

Co-authored-by: KernelDiego <gonzalezdiego.contact@gmail.com>
This commit is contained in:
Jas Singh
2025-05-26 19:45:11 -07:00
committed by GitHub
parent 436dcbfcf2
commit 8cf5806766
532 changed files with 13134 additions and 8669 deletions

View File

@@ -0,0 +1,257 @@
import { errorHandler } from 'src/core/errors/handler';
import { SystemUtilities } from 'src/core/system/SystemUtilities';
import { ServiceStatus } from 'src/core/system/types';
const RED = '\x1b[31m';
const GREEN = '\x1b[32m';
const YELLOW = '\x1b[33m';
const RESET = '\x1b[0m';
const BOLD = '\x1b[1m';
const STATUS_INSTALLED = '(INSTALLED)';
const STATUS_ACTIVE = '(ACTIVE)';
const STATUS_DISABLED = '(DISABLED)';
const STATUS_MISSING = '(MISSING)';
/**
* Colors a given text using ANSI color codes.
*
* @description Wraps the provided text with ANSI color codes.
*
* @param text - The text to color.
* @param color - The ANSI color code to use.
*/
function colorText(text: string, color: string): string {
return `${color}${text}${RESET}`;
}
/**
* Determines the status string and color for a dependency based on its type and checks.
*
* @description Returns the formatted line indicating the status of the given dependency.
*
* @param dep - The dependency to check.
*/
function getDependencyStatus(dep: Dependency): string {
let status: ServiceStatus | 'INSTALLED' | 'MISSING';
switch (dep.type) {
case 'executable':
status = SystemUtilities.checkExecutable(dep.check) ? 'INSTALLED' : 'MISSING';
break;
case 'library':
status = SystemUtilities.checkLibrary(dep.check) ? 'INSTALLED' : 'MISSING';
break;
case 'service':
status = SystemUtilities.checkServiceStatus(dep.check);
break;
default:
status = 'MISSING';
}
let color: string;
let textStatus: string;
switch (status) {
case 'ACTIVE':
textStatus = STATUS_ACTIVE;
color = GREEN;
break;
case 'INSTALLED':
textStatus = STATUS_INSTALLED;
color = GREEN;
break;
case 'DISABLED':
textStatus = STATUS_DISABLED;
color = YELLOW;
break;
case 'MISSING':
default:
textStatus = STATUS_MISSING;
color = RED;
break;
}
if (dep.description === undefined) {
return ` ${colorText(textStatus, color)} ${dep.package}`;
}
return ` ${colorText(textStatus, color)} ${dep.package}: ${dep.description ?? ''}`;
}
/**
* Checks all dependencies and returns a formatted output.
*
* @description Gathers the status of both required and optional dependencies and formats the result.
*/
export function checkDependencies(): string {
try {
const dependencies: Dependency[] = [
{
package: 'wireplumber',
required: true,
type: 'executable',
check: ['wireplumber'],
},
{
package: 'libgtop',
required: true,
type: 'library',
check: ['gtop-2.0'],
},
{
package: 'bluez',
required: true,
type: 'service',
check: ['bluetooth.service'],
},
{
package: 'bluez-utils',
required: true,
type: 'executable',
check: ['bluetoothctl'],
},
{
package: 'networkmanager',
required: true,
type: 'service',
check: ['NetworkManager.service'],
},
{
package: 'dart-sass',
required: true,
type: 'executable',
check: ['sass'],
},
{
package: 'wl-clipboard',
required: true,
type: 'executable',
check: ['wl-copy', 'wl-paste'],
},
{
package: 'upower',
required: true,
type: 'service',
check: ['upower.service'],
},
{
package: 'aylurs-gtk-shell',
required: true,
type: 'executable',
check: ['ags'],
},
{
package: 'python',
required: false,
type: 'executable',
check: ['python', 'python3'],
description: 'GPU usage tracking (NVidia only)',
},
{
package: 'python-gpustat',
required: false,
type: 'executable',
check: ['gpustat'],
description: 'GPU usage tracking (NVidia only)',
},
{
package: 'pywal',
required: false,
type: 'executable',
check: ['wal'],
description: 'Pywal hook for wallpapers',
},
{
package: 'pacman-contrib',
required: false,
type: 'executable',
check: ['paccache', 'rankmirrors'],
description: 'Checking for pacman updates',
},
{
package: 'power-profiles-daemon',
required: false,
type: 'service',
check: ['power-profiles-daemon.service'],
description: 'Switch power profiles',
},
{
package: 'swww',
required: false,
type: 'executable',
check: ['swww'],
description: 'Setting wallpapers',
},
{
package: 'grimblast',
required: false,
type: 'executable',
check: ['grimblast'],
description: 'For the snapshot shortcut',
},
{
package: 'brightnessctl',
required: false,
type: 'executable',
check: ['brightnessctl'],
description: 'To control keyboard and screen brightness',
},
{
package: 'btop',
required: false,
type: 'executable',
check: ['btop'],
description: 'To view system resource usage',
},
{
package: 'wf-recorder',
required: false,
type: 'executable',
check: ['wf-recorder'],
description: 'To use the built-in screen recorder',
},
{
package: 'hyprpicker',
required: false,
type: 'executable',
check: ['hyprpicker'],
description: 'To use the preset color picker shortcut',
},
{
package: 'matugen',
required: false,
type: 'executable',
check: ['matugen'],
description: 'To use wallpaper-based color schemes',
},
];
let output = `${BOLD}Required Dependencies:${RESET}\n`;
for (const dep of dependencies.filter((d) => d.required)) {
output += getDependencyStatus(dep) + '\n';
}
output += `\n${BOLD}Optional Dependencies:${RESET}\n`;
for (const dep of dependencies.filter((d) => !d.required)) {
output += getDependencyStatus(dep) + '\n';
}
return output;
} catch (error) {
errorHandler(error);
}
}
type DependencyType = 'executable' | 'library' | 'service';
type Dependency = {
package: string;
required: boolean;
type: DependencyType;
check: string[];
description?: string;
};

View File

@@ -0,0 +1,221 @@
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
import AstalWp from 'gi://AstalWp?version=0.1';
import { Command } from '../../types';
import { execAsync, Gio, GLib } from 'astal';
import { checkDependencies } from './checkDependencies';
import { getSystrayItems } from 'src/services/cli/helpers/systray';
import { idleInhibit } from 'src/lib/window/visibility';
import { errorHandler } from 'src/core/errors/handler';
import { clearNotifications } from 'src/lib/shared/notifications';
import options from 'src/configuration';
import { listCpuTempSensors } from './listSensors';
const { clearDelay } = options.notifications;
const notifdService = AstalNotifd.get_default();
const audio = AstalWp.get_default();
export const utilityCommands: Command[] = [
{
name: 'systrayItems',
aliases: ['sti'],
description: 'Gets a list of IDs for the current applications in the system tray.',
category: 'System',
args: [],
handler: (): string => {
try {
return getSystrayItems() ?? 'No items found!';
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'clearNotifications',
aliases: ['cno'],
description: 'Clears all of the notifications that currently exist.',
category: 'System',
args: [],
handler: (): string => {
try {
const allNotifications = notifdService.get_notifications();
clearNotifications(allNotifications, clearDelay.get());
return 'Notifications cleared successfully.';
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'toggleDnd',
aliases: ['dnd'],
description: 'Toggled the Do Not Disturb mode for notifications.',
category: 'System',
args: [],
handler: (): string => {
try {
notifdService.set_dont_disturb(!notifdService.dontDisturb);
return notifdService.dontDisturb ? 'Enabled' : 'Disabled';
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'adjustVolume',
aliases: ['vol'],
description: 'Adjusts the volume of the default audio output device.',
category: 'System',
args: [
{
name: 'volume',
description: 'A positive or negative number to adjust the volume by.',
type: 'number',
required: true,
},
],
handler: (args: Record<string, unknown>): number => {
try {
const speaker = audio?.defaultSpeaker;
if (speaker === undefined) {
throw new Error('A default speaker was not found.');
}
const volumeInput = Number(args['volume']) / 100;
if (options.menus.volume.raiseMaximumVolume.get()) {
speaker.set_volume(Math.min(speaker.volume + volumeInput, 1.5));
} else {
speaker.set_volume(Math.min(speaker.volume + volumeInput, 1));
}
return Math.round((speaker.volume + volumeInput) * 100);
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'isInhibiting',
aliases: ['isi'],
description: 'Returns the status of the Idle Inhibitor.',
category: 'System',
args: [],
handler: (): boolean => {
try {
return idleInhibit.get();
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'idleInhibit',
aliases: ['idi'],
description:
'Enables/Disables the Idle Inhibitor. Toggles the Inhibitor if no parameter is provided.',
category: 'System',
args: [
{
name: 'shouldInhibit',
description: 'The boolean value that enables/disables the inhibitor.',
type: 'boolean',
required: false,
},
],
handler: (args: Record<string, unknown>): boolean => {
try {
const shouldInhibit = args['shouldInhibit'] ?? idleInhibit.get() === false;
idleInhibit.set(Boolean(shouldInhibit));
return idleInhibit.get();
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'migrateConfig',
aliases: ['mcfg'],
description: 'Migrates the configuration file from the old location to the new one.',
category: 'System',
args: [],
handler: (): string => {
const oldPath = `${GLib.get_user_cache_dir()}/ags/hyprpanel/options.json`;
try {
const oldFile = Gio.File.new_for_path(oldPath);
const newFile = Gio.File.new_for_path(CONFIG_FILE);
if (oldFile.query_exists(null)) {
oldFile.move(newFile, Gio.FileCopyFlags.OVERWRITE, null, null);
return `Configuration file moved to ${CONFIG_FILE}`;
} else {
return `Old configuration file does not exist at ${oldPath}`;
}
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'checkDependencies',
aliases: ['chd'],
description: 'Checks the status of required and optional dependencies.',
category: 'System',
args: [],
handler: (): string => {
try {
return checkDependencies();
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'listCpuSensors',
aliases: ['lcs'],
description: 'Lists all available CPU temperature sensors and shows the current one.',
category: 'System',
args: [],
handler: (): string => {
try {
return listCpuTempSensors();
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'restart',
aliases: ['r'],
description: 'Restarts HyprPanel.',
category: 'System',
args: [],
handler: (): string => {
try {
execAsync('bash -c "hyprpanel -q; hyprpanel"');
return '';
} catch (error) {
errorHandler(error);
}
},
},
{
name: 'quit',
aliases: ['q'],
description: 'Quits HyprPanel.',
category: 'System',
args: [],
handler: (): string => {
try {
execAsync('bash -c "hyprpanel -q"');
return '';
} catch (error) {
errorHandler(error);
}
},
},
];

View File

@@ -0,0 +1,34 @@
import { CpuTempSensorDiscovery } from 'src/services/system/cputemp/sensorDiscovery';
import CpuTempService from 'src/services/system/cputemp';
/**
* Lists all available CPU temperature sensors and shows which one is currently active
*/
export function listCpuTempSensors(): string {
const sensors = CpuTempSensorDiscovery.getAllSensors();
const cpuTempService = new CpuTempService();
cpuTempService.initialize();
const currentSensor = cpuTempService.currentSensorPath;
let outputMessage = '';
outputMessage += 'Available CPU Temperature Sensors:\n';
outputMessage += '==================================\n';
if (sensors.length === 0) {
outputMessage += 'No temperature sensors found on the system.\n';
return outputMessage;
}
for (const sensor of sensors) {
const isCurrent = sensor.path === currentSensor;
const marker = isCurrent ? ' [CURRENT]' : '';
outputMessage += `${sensor.type.padEnd(8)} | ${sensor.name.padEnd(20)} | ${sensor.path}${marker}\n`;
}
outputMessage += `Auto-discovered sensor: ${CpuTempSensorDiscovery.discover() || 'None'}\n`;
cpuTempService.destroy();
return outputMessage;
}