feat: add notifications for MacOS using Applescript (#160)
yolo'ed it with codex. Let me know if this looks good to you. https://github.com/openai/codex/issues/148 tested with: ``` npm run build:dev ``` <img width="377" alt="Screenshot 2025-04-16 at 18 12 01" src="https://github.com/user-attachments/assets/79aa799b-b0b9-479d-84f1-bfb83d34bfb9" />
This commit is contained in:
@@ -15,7 +15,7 @@ import { formatCommandForDisplay } from "../../format-command.js";
|
||||
import { useConfirmation } from "../../hooks/use-confirmation.js";
|
||||
import { useTerminalSize } from "../../hooks/use-terminal-size.js";
|
||||
import { AgentLoop } from "../../utils/agent/agent-loop.js";
|
||||
import { log, isLoggingEnabled } from "../../utils/agent/log.js";
|
||||
import { isLoggingEnabled, log } from "../../utils/agent/log.js";
|
||||
import { ReviewDecision } from "../../utils/agent/review.js";
|
||||
import { OPENAI_BASE_URL } from "../../utils/config.js";
|
||||
import { createInputItem } from "../../utils/input-utils.js";
|
||||
@@ -28,8 +28,9 @@ import HelpOverlay from "../help-overlay.js";
|
||||
import HistoryOverlay from "../history-overlay.js";
|
||||
import ModelOverlay from "../model-overlay.js";
|
||||
import { Box, Text } from "ink";
|
||||
import { exec } from "node:child_process";
|
||||
import OpenAI from "openai";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { inspect } from "util";
|
||||
|
||||
type Props = {
|
||||
@@ -126,6 +127,8 @@ export default function TerminalChat({
|
||||
additionalWritableRoots,
|
||||
fullStdout,
|
||||
}: Props): React.ReactElement {
|
||||
// Desktop notification setting
|
||||
const notify = config.notify;
|
||||
const [model, setModel] = useState<string>(config.model);
|
||||
const [lastResponseId, setLastResponseId] = useState<string | null>(null);
|
||||
const [items, setItems] = useState<Array<ResponseItem>>([]);
|
||||
@@ -284,6 +287,49 @@ export default function TerminalChat({
|
||||
};
|
||||
}, [loading, confirmationPrompt]);
|
||||
|
||||
// Notify desktop with a preview when an assistant response arrives
|
||||
const prevLoadingRef = useRef<boolean>(false);
|
||||
useEffect(() => {
|
||||
// Only notify when notifications are enabled
|
||||
if (!notify) {
|
||||
prevLoadingRef.current = loading;
|
||||
return;
|
||||
}
|
||||
if (
|
||||
prevLoadingRef.current &&
|
||||
!loading &&
|
||||
confirmationPrompt == null &&
|
||||
items.length > 0
|
||||
) {
|
||||
if (process.platform === "darwin") {
|
||||
// find the last assistant message
|
||||
const assistantMessages = items.filter(
|
||||
(i) => i.type === "message" && i.role === "assistant",
|
||||
);
|
||||
const last = assistantMessages[assistantMessages.length - 1];
|
||||
if (last) {
|
||||
const text = last.content
|
||||
.map((c) => {
|
||||
if (c.type === "output_text") {
|
||||
return c.text;
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.join("")
|
||||
.trim();
|
||||
const preview = text.replace(/\n/g, " ").slice(0, 100);
|
||||
const safePreview = preview.replace(/"/g, '\\"');
|
||||
const title = "Codex CLI";
|
||||
const cwd = PWD;
|
||||
exec(
|
||||
`osascript -e 'display notification "${safePreview}" with title "${title}" subtitle "${cwd}" sound name "Ping"'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
prevLoadingRef.current = loading;
|
||||
}, [notify, loading, confirmationPrompt, items, PWD]);
|
||||
|
||||
// Let's also track whenever the ref becomes available
|
||||
const agent = agentRef.current;
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user