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}"`;
|
|
}
|
|
}
|
|
},
|
|
};
|
|
};
|