const detectWindowManager = () => { if (process.env.NIRI_SOCKET) return 'niri'; if (process.env.HYPRLAND_INSTANCE_SIGNATURE) return 'hyprland'; if (process.env.SWAYSOCK) return 'sway'; return null; }; const findWindow = async ($, wm, pid) => { try { switch (wm) { case 'niri': { const output = await $`niri msg --json windows`.text(); const windows = JSON.parse(output); const window = windows.find(w => w.app_id?.toLowerCase().includes('opencode') || w.title?.toLowerCase().includes('opencode')); return window ? { id: window.id.toString(), type: 'niri' } : null; } case 'hyprland': { const output = await $`hyprctl clients -j`.text(); const clients = JSON.parse(output); const window = clients.find(c => c.pid === pid || c.title?.toLowerCase().includes('opencode')); return window ? { id: window.address, type: 'hyprland' } : null; } case 'sway': { const output = await $`swaymsg -t get_tree`.text(); const tree = JSON.parse(output); const findNode = (node) => { if (node.pid === pid || node.name?.toLowerCase().includes('opencode')) { return node; } if (node.nodes) { for (const child of node.nodes) { const found = findNode(child); if (found) return found; } } if (node.floating_nodes) { for (const child of node.floating_nodes) { const found = findNode(child); if (found) return found; } } return null; }; const window = findNode(tree); return window ? { id: window.id.toString(), type: 'sway' } : null; } default: return null; } } catch (error) { console.error(`Failed to find window for ${wm}:`, error); return null; } }; const focusWindow = async ($, windowInfo) => { try { switch (windowInfo.type) { case 'niri': await $`niri msg action focus-window --id ${windowInfo.id}`; break; case 'hyprland': await $`hyprctl dispatch focuswindow address:${windowInfo.id}`; break; case 'sway': await $`swaymsg [con_id=${windowInfo.id}] focus`; break; } } catch (error) { console.error(`Failed to focus window:`, error); } }; export const SwayNotificationCenter = async ({ project, client, $, directory, worktree }) => { const wm = detectWindowManager(); console.log(`SwayNC notification plugin initialized (WM: ${wm || 'unknown'})`); return { event: async ({ event }) => { if (event.type === "session.idle") { const pid = process.pid; const dir = directory || worktree || "unknown"; const iconPath = `${process.env.HOME}/.config/opencode/opencode.png`; try { const windowInfo = wm ? await findWindow($, wm, pid) : null; const notifyCmd = [ "notify-send", "-a", "OpenCode", "-u", "normal", "-i", iconPath, "-h", `string:x-opencode-dir:${dir}`, ...(windowInfo ? ["-A", `focus=Focus Window`] : []), "OpenCode Ready", `Waiting for input\nDirectory: ${dir}` ]; if (windowInfo) { import("child_process").then(({ spawn }) => { const child = spawn(notifyCmd[0], notifyCmd.slice(1), { detached: true, stdio: 'ignore' }); child.unref(); let output = ''; if (child.stdout) { child.stdout.on('data', (data) => { output += data.toString(); }); child.on('close', () => { if (output.trim() === "focus") { focusWindow($, windowInfo).catch(() => {}); } }); } }).catch(() => {}); } else { $`${notifyCmd}`.catch(() => {}); } } catch (error) { console.error("Notification error:", error); await $`notify-send -a "OpenCode" -u normal -i ${iconPath} "OpenCode Ready" "Waiting for input in ${dir}"`; } } }, }; };