mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-09 19:45:17 +02:00
notification initial commit
This commit is contained in:
parent
0bb256879c
commit
8595190bd4
5 changed files with 91 additions and 1 deletions
|
|
@ -32,10 +32,11 @@ import started from "electron-squirrel-startup";
|
|||
import { execSync, exec, execFileSync } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { init as initChromeSync } from "@x/core/dist/knowledge/chrome-extension/server/server.js";
|
||||
import { registerBrowserControlService } from "@x/core/dist/di/container.js";
|
||||
import { registerBrowserControlService, registerNotificationService } from "@x/core/dist/di/container.js";
|
||||
import { browserViewManager, BROWSER_PARTITION } from "./browser/view.js";
|
||||
import { setupBrowserEventForwarding } from "./browser/ipc.js";
|
||||
import { ElectronBrowserControlService } from "./browser/control-service.js";
|
||||
import { ElectronNotificationService } from "./notification/electron-notification-service.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
|
|
@ -231,6 +232,7 @@ app.whenReady().then(async () => {
|
|||
await initConfigs();
|
||||
|
||||
registerBrowserControlService(new ElectronBrowserControlService());
|
||||
registerNotificationService(new ElectronNotificationService());
|
||||
|
||||
setupIpcHandlers();
|
||||
setupBrowserEventForwarding();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { BrowserWindow, Notification, shell } from "electron";
|
||||
import type { INotificationService, NotifyInput } from "@x/core/dist/application/notification/service.js";
|
||||
|
||||
const HTTP_URL = /^https?:\/\//i;
|
||||
|
||||
export class ElectronNotificationService implements INotificationService {
|
||||
isSupported(): boolean {
|
||||
return Notification.isSupported();
|
||||
}
|
||||
|
||||
notify({ title = "Rowboat", message, link }: NotifyInput): void {
|
||||
const notification = new Notification({
|
||||
title,
|
||||
body: message,
|
||||
});
|
||||
|
||||
notification.on("click", () => {
|
||||
if (link && HTTP_URL.test(link)) {
|
||||
shell.openExternal(link).catch((err) => {
|
||||
console.error("[notification] failed to open link:", err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.focusMainWindow();
|
||||
});
|
||||
|
||||
notification.show();
|
||||
}
|
||||
|
||||
private focusMainWindow(): void {
|
||||
const [win] = BrowserWindow.getAllWindows();
|
||||
if (!win) return;
|
||||
if (win.isMinimized()) win.restore();
|
||||
win.show();
|
||||
win.focus();
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import { getAccessToken } from "../../auth/tokens.js";
|
|||
import { API_URL } from "../../config/env.js";
|
||||
import { updateContent, updateTrackBlock } from "../../knowledge/track/fileops.js";
|
||||
import type { IBrowserControlService } from "../browser-control/service.js";
|
||||
import type { INotificationService } from "../notification/service.js";
|
||||
// Parser libraries are loaded dynamically inside parseFile.execute()
|
||||
// to avoid pulling pdfjs-dist's DOM polyfills into the main bundle.
|
||||
// Import paths are computed so esbuild cannot statically resolve them.
|
||||
|
|
@ -1514,4 +1515,37 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
|||
}
|
||||
},
|
||||
},
|
||||
|
||||
'notify-user': {
|
||||
description: "Show a native OS notification to the user. Clicking the notification opens the provided link in the default browser, or focuses the Rowboat app if no link is given.",
|
||||
inputSchema: z.object({
|
||||
title: z.string().min(1).max(120).optional().describe("Bold headline shown at the top of the notification. Defaults to 'Rowboat'."),
|
||||
message: z.string().min(1).describe("Body text of the notification."),
|
||||
link: z.string().url().refine((v) => /^https?:\/\//i.test(v), {
|
||||
message: "link must be an http:// or https:// URL",
|
||||
}).optional().describe("Optional http(s) URL opened when the user clicks the notification."),
|
||||
}),
|
||||
isAvailable: async () => {
|
||||
try {
|
||||
return container.resolve<INotificationService>('notificationService').isSupported();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
execute: async ({ title, message, link }: { title?: string; message: string; link?: string }) => {
|
||||
try {
|
||||
const service = container.resolve<INotificationService>('notificationService');
|
||||
if (!service.isSupported()) {
|
||||
return { success: false, error: 'Notifications are not supported on this system' };
|
||||
}
|
||||
service.notify({ title, message, link });
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
10
apps/x/packages/core/src/application/notification/service.ts
Normal file
10
apps/x/packages/core/src/application/notification/service.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export interface NotifyInput {
|
||||
title?: string;
|
||||
message: string;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
export interface INotificationService {
|
||||
isSupported(): boolean;
|
||||
notify(input: NotifyInput): void;
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import { FSAgentScheduleRepo, IAgentScheduleRepo } from "../agent-schedule/repo.
|
|||
import { FSAgentScheduleStateRepo, IAgentScheduleStateRepo } from "../agent-schedule/state-repo.js";
|
||||
import { FSSlackConfigRepo, ISlackConfigRepo } from "../slack/repo.js";
|
||||
import type { IBrowserControlService } from "../application/browser-control/service.js";
|
||||
import type { INotificationService } from "../application/notification/service.js";
|
||||
|
||||
const container = createContainer({
|
||||
injectionMode: InjectionMode.PROXY,
|
||||
|
|
@ -49,3 +50,9 @@ export function registerBrowserControlService(service: IBrowserControlService):
|
|||
browserControlService: asValue(service),
|
||||
});
|
||||
}
|
||||
|
||||
export function registerNotificationService(service: INotificationService): void {
|
||||
container.register({
|
||||
notificationService: asValue(service),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue