[Build] Start refactor

This commit is contained in:
2026-02-02 16:01:56 +01:00
parent c38907ec39
commit 10a5c775be
561 changed files with 1936094 additions and 13878 deletions

View File

@@ -0,0 +1,26 @@
import { GLib, Variable } from "astal";
import { Gtk } from "astal/gtk4";
const Time = ({ format = "%a, %e.%m %H:%M:%S" }) => {
const time = Variable<string>("").poll(
1000,
() => GLib.DateTime.new_now_local().format(format)!,
);
return (
<menubutton
cssClasses={["time", "bar-button"]}
hexpand
halign={Gtk.Align.CENTER}
>
<label onDestroy={() => time.drop()} label={time()} halign={Gtk.Align.CENTER}></label>
<popover>
<Gtk.Calendar />
</popover>
</menubutton>
);
};
export default {
Time,
};

View File

@@ -0,0 +1,146 @@
import {
bind,
exec,
readFile
} from 'astal';
import AstalHyprland from 'gi://AstalHyprland';
import {
Gtk
} from 'astal/gtk4';
import Pango from 'gi://Pango?version=1.0';
const hypr = AstalHyprland.get_default();
const Workspace = () => {
return (
<box>
{bind( hypr, 'workspaces' ).as( wss => wss
.filter( ws => !( ws.id >= -99 && ws.id <= -2 ) ) // filter out special workspaces
.sort( ( a, b ) => a.id - b.id )
.map( ws => (
<button
cssClasses={bind( hypr, 'focusedWorkspace' ).as( fw => ws === fw
? [
'focused-workspace-button',
'workspace-button',
]
: [ 'workspace-button' ], )}
onButtonPressed={() => ws.focus()}
child={<label label={String( ws.id )}></label>}
></button>
) ), )}
</box>
);
};
/**
* Displays the name of the currently active window and provides a popover for
* displaying all available clients
*/
const ActiveWindow = () => {
const focused = bind( hypr, 'focusedClient' );
const WindowPopover = (): Gtk.Popover => {
// Set up boxes + Popover
const popover = new Gtk.Popover();
const popoverBox = WindowPopoverBox();
popover.set_child( popoverBox );
return popover;
};
const windowPopover = WindowPopover();
// ───────────────────────────────────────────────────────────────────
// Return fully assembled HyprlandFocusedClient box
// ───────────────────────────────────────────────────────────────────
return (
<box visible={focused.as( Boolean )}>
<button
onClicked={() => windowPopover.popup()}
cssClasses={[ 'bar-button' ]}
child={
focused.as( client => client && (
<label
maxWidthChars={70}
ellipsize={Pango.EllipsizeMode.END}
label={bind( client, 'title' ).as( String )} />
), )
}></button>
{windowPopover}
</box >
);
};
type submaps = 'device' | 'launch' | 'workspace' | 'windowing' | 'screenshotting' | 'notifications' | '';
const ModeStatus = () => {
let isUsingHyprvim = false;
try {
const path = exec( 'bash -c "cd ~ && pwd"' ) + '/.config/hyprvim';
isUsingHyprvim = readFile( path ).trim() === 'y';
} catch ( e ) {
printerr( 'Failed to read hyprvim config', e );
}
const label = new Gtk.Label();
if ( !isUsingHyprvim ) return label;
print( '==> Using hyprvim config' );
const map = {
'device': 'DEV',
'launch': 'LNC',
'workspace': 'WSP',
'windowing': 'WIN',
'screenshotting': 'SCS',
'notifications': 'NOT',
'': 'NRM'
};
label.label = map[''];
label.cssClasses = [ 'mode-status' ];
// TODO: Possibly add popover to it that lists binds
hypr.connect( 'submap', ( _, name: submaps ) => {
label.label = map[name];
label.cssClasses = [
'mode-status',
name + '-mode'
];
} );
return label;
};
const WindowPopoverBox = () => {
return <box vertical>
<label label={'Available Windows'} cssClasses={[ 'title-2' ]}></label>
<Gtk.Separator marginTop={5} marginBottom={5}></Gtk.Separator>
<box vertical>
{bind( hypr, 'clients' ).as( clients => {
return clients.map( client => {
return <button child={
<box>
<label label={bind( client, 'workspace' ).as( w => `(WS ${ w.name })` )}></label>
<label label={bind( client, 'initialClass' ).as( c => `[${ c }]` )}></label>
<label label={bind( client, 'title' )}></label>
</box>
}
onClicked={() => client.focus()}
></button>;
} );
} )}
</box>
</box>;
};
export default {
Workspace,
ActiveWindow,
ModeStatus
};

View File

@@ -0,0 +1,204 @@
import AstalBattery from 'gi://AstalBattery';
import AstalBluetooth from 'gi://AstalBluetooth';
import AstalNetwork from 'gi://AstalNetwork';
import AstalWp from 'gi://AstalWp';
import Brightness from '../../../util/brightness';
import {
Gtk
} from 'astal/gtk4';
import QuickActions from '../../QuickActions/QuickActions';
import {
bind
} from 'astal';
import {
execAsync
} from 'astal';
const STATE = AstalNetwork.DeviceState;
const QuickView = () => {
const qa = QuickActions.QuickActions();
const showQuickActions = () => {
qa.popup();
};
return (
<button
onClicked={() => showQuickActions()}
cssClasses={[ 'quick-action-button' ]}
child={
<box>
<BatteryWidget></BatteryWidget>
<Audio></Audio>
<BluetoothWidget></BluetoothWidget>
<NetworkWidget></NetworkWidget>
<image iconName={'system-shutdown-symbolic'}></image>
{qa}
</box>
}
></button>
);
};
const NetworkWidget = () => {
const network = AstalNetwork.get_default();
return (
<box>
<image
iconName={bind( network, 'state' ).as( state => {
if ( state === AstalNetwork.State.CONNECTING ) {
return 'chronometer-reset-symbolic';
} else if (
state === AstalNetwork.State.CONNECTED_LOCAL
|| state === AstalNetwork.State.CONNECTED_SITE
|| state === AstalNetwork.State.CONNECTED_GLOBAL
) {
return 'network-wired-activated-symbolic';
} else {
return 'paint-unknown-symbolic';
}
} )}
cssClasses={[
'network-widget',
'quick-view-symbol'
]}
visible={bind( network.wifi, 'state' ).as( state => state !== STATE.ACTIVATED, )}
></image>
<image
iconName={bind( network.wifi, 'state' ).as( state => {
if ( state === STATE.ACTIVATED ) {
return network.wifi.iconName;
} else {
return '';
}
} )}
tooltipText={bind( network.wifi, 'ssid' )}
cssClasses={[
'network-widget',
'quick-view-symbol'
]}
visible={bind( network.wifi, 'state' ).as( state => state === STATE.ACTIVATED, )}
></image>
</box>
);
};
const BluetoothWidget = () => {
const bluetooth = AstalBluetooth.get_default();
const enabled = bind( bluetooth, 'isPowered' );
const connected = bind( bluetooth, 'isConnected' );
// For each connected BT device, render status
return (
<box>
<box visible={enabled.as( e => e )}>
<image
iconName={'bluetooth-active-symbolic'}
visible={connected.as( c => c )}
></image>
<image
iconName={'bluetooth-disconnected-symbolic'}
visible={connected.as( c => !c )}
></image>
</box>
<image
iconName={'bluetooth-disabled-symbolic'}
visible={enabled.as( e => !e )}
></image>
<box>
{bind( bluetooth, 'devices' ).as( devices => {
return devices.map( device => {
return (
<image
iconName={bind( device, 'icon' ).as( icon => icon, )}
visible={bind( device, 'connected' )}
tooltipText={bind( device, 'batteryPercentage' ).as( n => {
return device.get_name() + ': ' + n + '%';
}, )}
></image>
);
} );
} )}
</box>
</box>
);
};
let hasSentNotification = false;
const BatteryWidget = () => {
const battery = AstalBattery.get_default();
if ( battery.get_is_present() ) {
return (
<image
iconName={bind( battery, 'batteryIconName' ).as( icon => icon )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( battery, 'percentage' ).as( p => {
const level = Math.round( p * 100 );
if ( level < 20 && !hasSentNotification ) {
hasSentNotification = true;
execAsync( 'bash -c "notify-send \'Battery level below 20%\'"' );
}
return `Battery Level: ${ level }%`;
} )}
></image>
);
} else {
return <box></box>;
}
// Else, no battery available -> Don't show the widget
};
const BrightnessWidget = () => {
const brightness = Brightness.get_default();
const screen_brightness = bind( brightness, 'screen' );
return (
<box cssClasses={[ 'quick-view-symbol' ]}>
<image iconName={'brightness-high-symbolic'}></image>
<label
label={screen_brightness.as( b => '' + Math.round( 100 * b ) )}
visible={bind( brightness, 'screenAvailable' )}
></label>
</box>
);
};
const Audio = () => {
const wireplumber = AstalWp.get_default();
if ( wireplumber ) {
return (
<box orientation={Gtk.Orientation.HORIZONTAL}>
<image
iconName={bind( wireplumber.defaultSpeaker, 'volumeIcon' ).as( icon => icon, )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( wireplumber.defaultSpeaker, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image>
<image
iconName={bind( wireplumber.defaultMicrophone,
'volumeIcon', ).as( icon => icon )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( wireplumber.defaultMicrophone, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image>
</box>
);
} else {
print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing', );
return <image iconName={'action-unavailable-symbolic'}></image>;
}
};
// cssClasses={[ 'quick-view-symbol' ]}
export default {
QuickView,
BrightnessWidget
};

View File

@@ -0,0 +1,105 @@
import {
Gtk
} from 'astal/gtk4';
import {
execAsync
} from 'astal';
import sysinfo from '../sysinfo';
const info = () => {
return (
<box vertical>
<label
label={'System Information'}
cssClasses={[ 'title-2' ]}
></label>
<Gtk.Separator marginTop={5} marginBottom={10}></Gtk.Separator>
<label
vexpand
halign={Gtk.Align.START}
hexpand
label={sysinfo.ramUsed( used => {
return 'RAM: ' + used + ` (${ sysinfo.ramUtil.get() }%)`;
} )}
></label>
<label
label={sysinfo.systemStats( stats => {
return `CPU: ${ stats.cpuTemp }, ${ stats.cpuClk }
GPU: ${ stats.gpuTemp }, ${ stats.gpuClk } (${ stats.vram } / ${ stats.availableVRAM })
Kernel: ${ stats.kernel }`;
} )}
></label>
<Gtk.Separator marginTop={10}></Gtk.Separator>
<button
onClicked={() => execAsync( '/bin/sh -c "kitty --hold fish -c \'fastfetch\'"' )}
child={
<label label={'View FastFetch'}></label>
}></button>
</box>
);
};
const SystemInformationPanel = () => {
const popover = new Gtk.Popover();
popover.set_child( info() );
return popover;
};
const panel = SystemInformationPanel();
const SystemInfo = () => {
sysinfo.startSysInfoFetcher();
const openSysInfo = async () => {
panel.popup();
sysinfo.refreshStats();
};
if ( sysinfo.enabled ) {
return (
<button
onClicked={() => openSysInfo()}
child={
<box tooltipText={sysinfo.ramUsed( v => v )}>
<box
cssClasses={[ 'quick-view-symbol' ]}
>
<image
iconName={'power-profile-performance-symbolic'}
marginEnd={1}
></image>
<label
label={sysinfo.cpuUtil( util => util )}
marginEnd={5}
></label>
</box>
<box
cssClasses={[ 'quick-view-symbol' ]}
>
<image iconName={'memory'}></image>
<label label={sysinfo.ramUtil( util => util )}></label>
</box>
<box
cssClasses={[ 'quick-view-symbol' ]}
>
<image iconName={'show-gpu-effects-symbolic'}></image>
<label label={sysinfo.gpuUtil( util => util )}></label>
</box>
{panel}
</box>
}
cssClasses={[ 'bar-button' ]}
></button>
);
} else {
return <image iconName={'action-unavailable-symbolic'}></image>;
}
};
export default {
SystemInfo,
panel,
};

View File

@@ -0,0 +1,9 @@
interface Stats {
kernel: string;
cpuTemp: string;
cpuClk: string;
gpuTemp: string;
gpuClk: string;
vram: string;
availableVRAM: string;
}