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,185 @@
import { App, Gtk } from 'astal/gtk3';
import Astal from 'gi://Astal?version=3.0';
import { bind, Binding, Variable } from 'astal';
import { idleInhibit } from 'src/lib/window/visibility';
import { WidgetRegistry } from './WidgetRegistry';
import { getLayoutForMonitor, isLayoutEmpty } from '../utils/monitors';
import options from 'src/configuration';
/**
* Responsible for the bar UI layout and positioning
*/
export class BarLayout {
private _hyprlandMonitor: number;
private _gdkMonitor: number;
private _widgetRegistry: WidgetRegistry;
private _visibilityVar: Variable<boolean>;
private _classNameVar: Variable<string>;
private _anchorVar: Variable<Astal.WindowAnchor>;
private _layerVar: Variable<Astal.Layer>;
private _borderLocationVar: Binding<string>;
private _barSectionsVar: {
left: Variable<JSX.Element[]>;
middle: Variable<JSX.Element[]>;
right: Variable<JSX.Element[]>;
};
constructor(gdkMonitor: number, hyprlandMonitor: number, widgetRegistry: WidgetRegistry) {
this._gdkMonitor = gdkMonitor;
this._hyprlandMonitor = hyprlandMonitor;
this._widgetRegistry = widgetRegistry;
this._visibilityVar = Variable(true);
this._classNameVar = Variable('bar');
this._anchorVar = Variable(
Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
);
this._layerVar = Variable(Astal.Layer.TOP);
this._borderLocationVar = Variable('bar-panel')();
this._barSectionsVar = {
left: Variable([]),
middle: Variable([]),
right: Variable([]),
};
this._initializeReactiveVariables();
}
public render(): JSX.Element {
return (
<window
inhibit={bind(idleInhibit)}
name={`bar-${this._hyprlandMonitor}`}
namespace={`bar-${this._hyprlandMonitor}`}
className={this._classNameVar()}
application={App}
monitor={this._gdkMonitor}
visible={this._visibilityVar()}
anchor={this._anchorVar()}
layer={this._layerVar()}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
onDestroy={() => this._cleanup()}
>
<box className="bar-panel-container">
<centerbox
css="padding: 1px;"
hexpand
className={this._borderLocationVar}
startWidget={
<box className="box-left" hexpand>
{this._barSectionsVar.left()}
</box>
}
centerWidget={
<box className="box-center" halign={Gtk.Align.CENTER}>
{this._barSectionsVar.middle()}
</box>
}
endWidget={
<box className="box-right" halign={Gtk.Align.END}>
{this._barSectionsVar.right()}
</box>
}
/>
</box>
</window>
);
}
private _initializeReactiveVariables(): void {
this._initializeVisibilityVariables();
this._initializePositionVariables();
this._initializeAppearanceVariables();
this._initializeSectionVariables();
}
private _initializeVisibilityVariables(): void {
const { layouts } = options.bar;
this._visibilityVar = Variable.derive([bind(layouts)], (currentLayouts) => {
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
return !isLayoutEmpty(foundLayout);
});
this._classNameVar = Variable.derive([bind(layouts)], (currentLayouts) => {
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
return !isLayoutEmpty(foundLayout) ? 'bar' : '';
});
}
/**
* Initialize variables related to bar positioning
*/
private _initializePositionVariables(): void {
const { location } = options.theme.bar;
this._anchorVar = Variable.derive([bind(location)], (loc) => {
if (loc === 'bottom') {
return Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
}
return Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
});
}
private _initializeAppearanceVariables(): void {
const { location: borderLocation } = options.theme.bar.border;
this._layerVar = this._createLayerVariable();
this._borderLocationVar = bind(borderLocation).as((brdrLcn) =>
brdrLcn !== 'none' ? 'bar-panel withBorder' : 'bar-panel',
);
}
private _createLayerVariable(): Variable<Astal.Layer> {
return Variable.derive([bind(options.theme.bar.layer), bind(options.tear)], (barLayer, tear) => {
if (tear && barLayer === 'overlay') {
return Astal.Layer.TOP;
}
return this._getLayerFromConfig(barLayer);
});
}
private _getLayerFromConfig(barLayer: string): Astal.Layer {
const layerMap: Record<string, Astal.Layer> = {
overlay: Astal.Layer.OVERLAY,
top: Astal.Layer.TOP,
bottom: Astal.Layer.BOTTOM,
background: Astal.Layer.BACKGROUND,
};
return layerMap[barLayer] ?? Astal.Layer.TOP;
}
private _initializeSectionVariables(): void {
this._barSectionsVar = {
left: this._createSectionBinding('left'),
middle: this._createSectionBinding('middle'),
right: this._createSectionBinding('right'),
};
}
private _createSectionBinding(section: 'left' | 'middle' | 'right'): Variable<JSX.Element[]> {
const { layouts } = options.bar;
return Variable.derive([bind(layouts)], (currentLayouts) => {
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
return foundLayout[section]
.filter((mod) => this._widgetRegistry.hasWidget(mod))
.map((widget) => this._widgetRegistry.createWidget(widget, this._hyprlandMonitor));
});
}
private _cleanup(): void {
this._visibilityVar.drop();
this._classNameVar.drop();
this._anchorVar.drop();
this._layerVar.drop();
this._barSectionsVar.left.drop();
this._barSectionsVar.middle.drop();
this._barSectionsVar.right.drop();
}
}

View File

@@ -0,0 +1,55 @@
import { CustomModules } from '../customModules';
export type WidgetFactory = (monitor: number) => JSX.Element;
/**
* Manages registration and creation of widgets
*/
export class WidgetRegistry {
private _widgets: Record<string, WidgetFactory> = {};
private _initialized = false;
constructor(coreWidgets: Record<string, WidgetFactory>) {
this._widgets = { ...coreWidgets };
}
/**
* Initialize the registry with core and custom widgets
*/
public async initialize(): Promise<void> {
if (this._initialized) return;
try {
const customWidgets = await CustomModules.build();
this._widgets = {
...this._widgets,
...customWidgets,
};
this._initialized = true;
} catch (error) {
console.error('Failed to initialize widget registry:', error);
throw error;
}
}
/**
* Check if a widget is registered
*/
public hasWidget(name: string): boolean {
return Object.keys(this._widgets).includes(name);
}
/**
* Create an instance of a widget
*/
public createWidget(name: string, monitor: number): JSX.Element {
if (!this.hasWidget(name)) {
console.error(`Widget "${name}" not found`);
return <box />;
}
return this._widgets[name](monitor);
}
}

View File

@@ -0,0 +1,61 @@
import { BatteryLabel } from '../modules/battery';
import { Bluetooth } from '../modules/bluetooth';
import { Cava } from '../modules/cava';
import { Clock } from '../modules/clock';
import { Cpu } from '../modules/cpu';
import { CpuTemp } from '../modules/cputemp';
import { Hypridle } from '../modules/hypridle';
import { Hyprsunset } from '../modules/hyprsunset';
import { KbInput } from '../modules/kblayout';
import { Media } from '../modules/media';
import { Menu } from '../modules/menu';
import { Microphone } from '../modules/microphone';
import { Netstat } from '../modules/netstat';
import { Network } from '../modules/network';
import { Notifications } from '../modules/notifications';
import { Power } from '../modules/power';
import { Ram } from '../modules/ram';
import { ModuleSeparator } from '../modules/separator';
import { Storage } from '../modules/storage';
import { Submap } from '../modules/submap';
import { SysTray } from '../modules/systray';
import { Updates } from '../modules/updates';
import { Volume } from '../modules/volume';
import { Weather } from '../modules/weather';
import { ClientTitle } from '../modules/window_title';
import { Workspaces } from '../modules/workspaces';
import { WorldClock } from '../modules/worldclock';
import { WidgetContainer } from '../shared/widgetContainer';
import { WidgetFactory } from './WidgetRegistry';
export function getCoreWidgets(): Record<string, WidgetFactory> {
return {
battery: () => WidgetContainer(BatteryLabel()),
dashboard: () => WidgetContainer(Menu()),
workspaces: (monitor: number) => WidgetContainer(Workspaces(monitor)),
windowtitle: () => WidgetContainer(ClientTitle()),
media: () => WidgetContainer(Media()),
notifications: () => WidgetContainer(Notifications()),
volume: () => WidgetContainer(Volume()),
network: () => WidgetContainer(Network()),
bluetooth: () => WidgetContainer(Bluetooth()),
clock: () => WidgetContainer(Clock()),
systray: () => WidgetContainer(SysTray()),
microphone: () => WidgetContainer(Microphone()),
ram: () => WidgetContainer(Ram()),
cpu: () => WidgetContainer(Cpu()),
cputemp: () => WidgetContainer(CpuTemp()),
storage: () => WidgetContainer(Storage()),
netstat: () => WidgetContainer(Netstat()),
kbinput: () => WidgetContainer(KbInput()),
updates: () => WidgetContainer(Updates()),
submap: () => WidgetContainer(Submap()),
weather: () => WidgetContainer(Weather()),
power: () => WidgetContainer(Power()),
hyprsunset: () => WidgetContainer(Hyprsunset()),
hypridle: () => WidgetContainer(Hypridle()),
cava: () => WidgetContainer(Cava()),
worldclock: () => WidgetContainer(WorldClock()),
separator: () => ModuleSeparator(),
};
}