pi notify and kitty mac

This commit is contained in:
Thomas G. Lopes
2026-03-02 09:25:34 +00:00
parent 6d81a0517e
commit 4b0bcef25b
2 changed files with 100 additions and 3 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ include colors.conf
# END_KITTY_THEME # END_KITTY_THEME
#: Startup {{{ #: Startup {{{
shell /usr/bin/fish -l -c "zellij; exec fish -l" shell /bin/sh -c 'FISH=/usr/bin/fish; [ -x /opt/homebrew/bin/fish ] && FISH=/opt/homebrew/bin/fish; exec $FISH -l -c "zellij; exec $FISH -l"'
#: }} #: }}
#: Keybindings (disable kitty tabs for Zellij) {{{ #: Keybindings (disable kitty tabs for Zellij) {{{
@@ -1838,7 +1838,7 @@ background_opacity 0.9
#: background_opacity. If you want to use both, you are probably #: background_opacity. If you want to use both, you are probably
#: better off just hiding the titlebar with hide_window_decorations. #: better off just hiding the titlebar with hide_window_decorations.
# macos_option_as_alt no macos_option_as_alt yes
#: Use the Option key as an Alt key on macOS. With this set to no, #: Use the Option key as an Alt key on macOS. With this set to no,
#: kitty will use the macOS native Option+Key to enter Unicode #: kitty will use the macOS native Option+Key to enter Unicode
+98 -1
View File
@@ -2,7 +2,7 @@
* Pi Done Notify Extension * Pi Done Notify Extension
* *
* Sends a native terminal notification when Pi finishes a prompt * Sends a native terminal notification when Pi finishes a prompt
* and is waiting for input. * and is waiting for input. Optionally plays a sound.
*/ */
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
@@ -74,9 +74,106 @@ function notify(title: string, body: string): void {
} }
} }
// Sound playback
function playSound(soundPath?: string): void {
const { execFile } = require("child_process");
if (process.platform === "darwin") {
// macOS: use afplay with system sound or custom path
const sound = soundPath || "/System/Library/Sounds/Glass.aiff";
execFile("afplay", [sound], (error: Error | null) => {
if (error) console.error("Failed to play sound:", error);
});
} else if (process.platform === "linux") {
// Linux: try paplay (PulseAudio), then pw-play (PipeWire), then aplay (ALSA)
const sound =
soundPath || "/usr/share/sounds/freedesktop/stereo/complete.oga";
execFile("paplay", [sound], (error: Error | null) => {
if (error) {
execFile("pw-play", [sound], (error2: Error | null) => {
if (error2) {
execFile("aplay", [sound]);
}
});
}
});
} else if (process.platform === "win32") {
// Windows: use PowerShell to play system sound
const sound =
soundPath || "C:\\Windows\\Media\\Windows Notify System Generic.wav";
execFile("powershell.exe", [
"-NoProfile",
"-Command",
`(New-Object Media.SoundPlayer '${sound}').PlaySync()`,
]);
}
}
// Settings state
interface NotifySettings {
soundEnabled: boolean;
soundPath?: string;
}
const CUSTOM_TYPE = "pi-done-notify-settings";
export default function (pi: ExtensionAPI) { export default function (pi: ExtensionAPI) {
let settings: NotifySettings = {
soundEnabled: true,
};
// Restore settings from session
pi.on("session_start", async (_event, ctx) => {
for (const entry of ctx.sessionManager.getEntries()) {
if (entry.type === "custom" && entry.customType === CUSTOM_TYPE) {
settings = entry.data as NotifySettings;
}
}
});
// Register /notify command
pi.registerCommand("notify", {
description: "Configure notification settings (sound on/off, or set sound path)",
handler: async (args, ctx) => {
const arg = args?.trim().toLowerCase();
if (!arg || arg === "status") {
ctx.ui.notify(
`Sound: ${settings.soundEnabled ? "on" : "off"}${settings.soundPath ? ` (${settings.soundPath})` : ""}`,
"info"
);
return;
}
if (arg === "on") {
settings.soundEnabled = true;
pi.appendEntry(CUSTOM_TYPE, settings);
ctx.ui.notify("Notification sound enabled", "success");
return;
}
if (arg === "off") {
settings.soundEnabled = false;
pi.appendEntry(CUSTOM_TYPE, settings);
ctx.ui.notify("Notification sound disabled", "info");
return;
}
// Treat as a sound path
settings.soundPath = args?.trim() || undefined;
pi.appendEntry(CUSTOM_TYPE, settings);
ctx.ui.notify(`Sound path set to: ${settings.soundPath}`, "success");
},
});
// Notify on agent end
pi.on("agent_end", async (_event, ctx) => { pi.on("agent_end", async (_event, ctx) => {
if (!ctx.hasUI) return; if (!ctx.hasUI) return;
notify("Pi", "Done. Ready for input."); notify("Pi", "Done. Ready for input.");
if (settings.soundEnabled) {
playSound(settings.soundPath);
}
}); });
} }