[AGS] Redo basic setup

This commit is contained in:
2025-04-19 15:20:50 +02:00
parent a9c7b7d7ee
commit 78e472beb8
18 changed files with 806 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
import { createQuickActionsMenu } from "./QuickActions";
import { GLib } from "astal";
import { Astal, Gdk, Gtk } from "astal/gtk3";
const Bar = (gdkmonitor: Gdk.Monitor) => {
return (
<window gdkmonitor={gdkmonitor}
cssClasses={["Bar"]}>
<box orientation={Gtk.Orientation.HORIZONTAL} spacing={10}>
<box>
</box>
<label>{windowTitle}</label>
<box>
<tray />
<button icon="quickaction" menu={quickActionMenu} />
</box>
</box>
</window>
);
}
export default Bar;

View File

@@ -0,0 +1,76 @@
import Gtk from "gi://Gtk";
import AstalNetwork from "gi://AstalNetwork";
import AstalBluetooth from "gi://AstalBluetooth";
import { exec } from "gi://GLib";
let quickActionsMenu;
// Toggle WiFi connection
function toggleWiFi() {
const network = AstalNetwork.get_default();
const wifi = network.get_wifi()!;
const state = wifi.get_state();
if (state === AstalNetwork.WifiState.DISCONNECTED) {
wifi.connect();
} else {
wifi.disconnect();
}
}
// Toggle Bluetooth power state
function toggleBluetooth() {
const bluetooth = AstalBluetooth.get_default();
const adapter = bluetooth.get_adapter();
const powered = adapter?.get_powered();
adapter?.set_powered(!powered);
}
// Adjust volume or microphone (opens pavucontrol)
function adjustVolume() {
exec("pavucontrol");
}
function adjustMic() {
exec("pavucontrol");
}
// Power menu
function showPowerMenu() {
exec("power-menu");
}
// Show WiFi network picker
function pickWiFi() {
const wifi = AstalNetwork.get_default().get_wifi();
wifi.show_picker();
}
// Show Bluetooth device picker
function pickBluetooth() {
const bluetooth = AstalBluetooth.get_default();
bluetooth.show_picker();
}
// Create menu using JSX
function createQuickActionsMenu() {
quickActionsMenu = (
<menu>
<item label="📶 WiFi" onActivate={toggleWiFi} />
<item label="→ Pick Network" onActivate={pickWiFi} />
<item label="🔵 Bluetooth" onActivate={toggleBluetooth} />
<item label="→ Pick Device" onActivate={pickBluetooth} />
<item label="🔊 Volume" onActivate={adjustVolume} />
<item label="🎙️ Microphone" onActivate={adjustMic} />
<item label="🔌 Power" onActivate={showPowerMenu} />
</menu>
);
return quickActionsMenu;
}
// Export the function
export { createQuickActionsMenu };

View File

@@ -0,0 +1,57 @@
import AstalTray from "gi://AstalTray";
import { bind } from "astal";
import AstalHyprland from "gi://AstalHyprland";
const SysTray = () => {
const tray = AstalTray.get_default();
return <box className="SysTray">
{bind(tray, "items").as( items => items.map( item => (
<button
tooltipMarkup={bind(item, "tooltipMarkup")}
usePopover={false}
actionGroup={bind(item, "actionGroup").as(ag => ["dbusmenu", ag])}
menuModel={bind(item, "menuModel")}>
<icon gicon={bind(item, "gicon")} />
</button>
) ) ) }
</box>
}
const HyprlandWorkspace = () => {
const hypr = AstalHyprland.get_default()
return <box className={"HyprlandWorkspaces"}>
{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
className={bind(hypr, "focusedWorkspace").as(fw =>
ws === fw ? "HyprlandFocusedWorkspace" : "")}
onClicked={() => ws.focus()}>
{ws.id}
</button>
))
)}
</box>
}
const HyprlandActiveWindow = () => {
const hypr = AstalHyprland.get_default();
const focused = bind( hypr, "focusedClient" );
return <box className={"HyprlandFocusedClients"} visible={focused.as(Boolean)}>
{focused.as( client => (
client && <label label={bind( client, "title" ).as( String )} />
))}
</box>
}
export default {
HyprlandWorkspace,
HyprlandActiveWindow,
SysTray
}

View File

@@ -0,0 +1,37 @@
@import "colors";
.toggle-row {
background-color: $bg;
border-radius: 12px;
margin: 6px 0;
overflow: hidden;
border: 1px solid $border;
button {
padding: 10px 16px;
font-size: 14px;
transition: background 0.2s ease;
border: none;
background: transparent;
&:hover {
background-color: $hover;
}
}
.toggle {
flex: 1;
background-color: transparent;
text-align: left;
&.active {
background-color: $accent;
color: white;
}
}
.arrow {
width: 40px;
background-color: transparent;
text-align: center;
}
}

View File

@@ -0,0 +1,68 @@
import AstalHyprland from "gi://AstalHyprland";
const getAvailableWorkspaces = (): number[] => {
const workspaces: number[] = [];
AstalHyprland.get_default().get_workspaces().forEach( val => {
workspaces.push( val.get_id() );
} )
return workspaces;
}
const getCurrentWorkspaceID = (): number => {
return AstalHyprland.get_default().get_focused_workspace().get_id();
}
const getCurrentWindowTitle = (): string => {
return AstalHyprland.get_default().get_focused_client().get_title();
}
/**
* Get the workspace ID of a window by its address
* @param address - The address of the window
* @returns The workspace ID
*/
const getWorkspaceIDOfWindowByAddress = ( address: string ): number => {
AstalHyprland.get_default().get_clients().forEach( client => {
if ( client.get_address() === address ) {
return client.get_workspace().get_id();
}
} );
return -1;
}
interface HyprlandSubscriptions {
[key: string]: ( data: string ) => void;
}
const hooks: HyprlandSubscriptions = {};
/**
* Add an event listener for Hyprland events.
* @param event - A hyprland IPC event. See https://wiki.hyprland.org/IPC/. Useful events include: urgent, windowtitlev2, workspace, createworkspacev2, destroyworkspacev2, activewindowv2
* @param cb - The callback function
*/
const subscribeToUpdates = ( event: string, cb: ( data: string ) => void ): void => {
hooks[ event ] = cb;
}
/**
* Listen to events. Must be called at some point if events are to be listened for
*/
const listen = () => {
AstalHyprland.get_default().connect( "event", ( name: string, data: string ) => {
if ( hooks[ name ] ) {
hooks[ name ]( data );
}
} );
}
export default {
getAvailableWorkspaces,
getCurrentWorkspaceID,
getCurrentWindowTitle,
getWorkspaceIDOfWindowByAddress,
subscribeToUpdates,
listen
}