131 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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}"`;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
  };
 | 
						|
};
 |