import { GObject, bind, exec, readFile } from 'astal'; import AstalHyprland from 'gi://AstalHyprland'; import AstalTray from 'gi://AstalTray'; import { Gtk } from 'astal/gtk4'; import Pango from 'gi://Pango?version=1.0'; const hypr = AstalHyprland.get_default(); const SYNC = GObject.BindingFlags.SYNC_CREATE; const SysTray = () => { const trayBox = new Gtk.Box( { 'cssClasses': [ 'bar-button' ] } ); const tray = AstalTray.get_default(); const trayItems = new Map(); const trayAddedHandler = tray.connect( 'item-added', ( _, id ) => { const item = tray.get_item( id ); const popover = Gtk.PopoverMenu.new_from_model( item.menu_model ); const icon = new Gtk.Image(); const button = new Gtk.MenuButton( { popover, 'child': icon, 'cssClasses': [ 'tray-item' ], } ); item.bind_property( 'gicon', icon, 'gicon', SYNC ); popover.insert_action_group( 'dbusmenu', item.action_group ); item.connect( 'notify::action-group', () => { popover.insert_action_group( 'dbusmenu', item.action_group ); } ); trayItems.set( id, button ); trayBox.append( button ); } ); const trayRemovedHandler = tray.connect( 'item-removed', ( _, id ) => { const button = trayItems.get( id ); if ( button ) { trayBox.remove( button ); button.run_dispose(); trayItems.delete( id ); } } ); trayBox.connect( 'destroy', () => { tray.disconnect( trayAddedHandler ); tray.disconnect( trayRemovedHandler ); } ); return trayBox; }; const Workspace = () => { return ( {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 => ( ) ), )} ); }; /** * 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 ( {windowPopover} ); }; type submaps = 'device' | 'launch' | 'workspace' | 'windowing' | 'screenshotting' | 'notifications' | ''; const ModeStatus = () => { let isUsingHyprmode = false; try { const path = exec( 'bash -c "cd ~ && pwd"' ) + '/.config/hyprmode'; isUsingHyprmode = readFile( path ).trim() === 'y'; } catch ( e ) { printerr( 'Failed to read hyprmode config', e ); } const label = new Gtk.Label(); if ( !isUsingHyprmode ) return label; print( '==> Using hyprmode config' ); const map = { 'device': 'D', 'launch': 'L', 'workspace': 'W', 'windowing': 'M', 'screenshotting': 'S', 'notifications': 'N', '': 'N' }; label.label = 'N'; 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 {bind( hypr, 'clients' ).as( clients => { return clients.map( client => { return ; } ); } )} ; }; export default { Workspace, ActiveWindow, SysTray, ModeStatus };