import { Gtk, timeout, Widget } from 'astal'; import AstalHyprland from 'gi://AstalHyprland?version=0.1'; const Hyprland = AstalHyprland.get_default(); const URGENT_DURATION = 1000; const Workspace = ({ id = 0 }) => ( { Hyprland.message_async(`dispatch workspace ${id}`, () => { /**/ }); }} > { const update = ( _: Widget.Box, client?: AstalHyprland.Client, ) => { const workspace = Hyprland.get_workspace(id); const occupied = workspace && workspace.get_clients().length > 0; self.toggleClassName('occupied', occupied); if (!client) { return; } const isUrgent = client && client.get_workspace().get_id() === id; if (isUrgent) { self.toggleClassName('urgent', true); // Only show for a sec when urgent is current workspace if (Hyprland.get_focused_workspace().get_id() === id) { timeout(URGENT_DURATION, () => { self.toggleClassName('urgent', false); }); } } }; update(self); self .hook(Hyprland, 'event', () => update(self)) // Deal with urgent windows .hook(Hyprland, 'urgent', update) .hook(Hyprland, 'notify::focused-workspace', () => { if (Hyprland.get_focused_workspace().get_id() === id) { self.toggleClassName('urgent', false); } }); }} /> ); export default () => { const L_PADDING = 2; const WS_WIDTH = 30; const updateHighlight = (self: Widget.Box) => { const currentId = Hyprland.get_focused_workspace().get_id().toString(); const indicators = ((self.get_parent() as Widget.Overlay) .child as Widget.Box) .children as Widget.Revealer[]; const currentIndex = indicators.findIndex((w) => w.name === currentId); if (currentIndex >= 0) { self.css = `margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`; } }; const highlight = ( { self.hook(Hyprland, 'notify::focused-workspace', updateHighlight); }} /> ) as Widget.Box; let workspaces: Widget.Revealer[] = []; return ( { const refresh = () => { (self.children as Widget.Revealer[]).forEach((rev) => { rev.reveal_child = false; }); workspaces.forEach((ws) => { ws.reveal_child = true; }); }; const updateWorkspaces = () => { Hyprland.get_workspaces().forEach((ws) => { const currentWs = (self.children as Widget.Revealer[]) .find((ch) => ch.name === ws.id.toString()); if (!currentWs && ws.id > 0) { self.add(Workspace({ id: ws.id })); } }); // Make sure the order is correct workspaces.forEach((workspace, i) => { (workspace.get_parent() as Widget.Box) .reorder_child(workspace, i); }); }; const updateAll = () => { workspaces = (self.children as Widget.Revealer[]) .filter((ch) => { return Hyprland.get_workspaces().find((ws) => { return ws.id.toString() === ch.name; }); }) .sort((a, b) => parseInt(a.name ?? '0') - parseInt(b.name ?? '0')); updateWorkspaces(); refresh(); // Make sure the highlight doesn't go too far const TEMP_TIMEOUT = 100; timeout(TEMP_TIMEOUT, () => updateHighlight(highlight)); }; updateAll(); self.hook(Hyprland, 'event', updateAll); }} /> ); };