Files
openchamber/packages/web/server/lib/notifications/emitter-runtime.js
T
YifanandBohdan Triapitsyn bee9d19f3a feat(web): add WebSocket transport for message event streaming with SSE fallback (#764)
* feat: add websocket message stream transport

* fix: avoid false missing session directories in sidebar

* fix: re-probe project root session directories

* refactor: use button group for message stream transport

* fix: resolve chat input hook dependency warning

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-17 11:07:26 +03:00

77 lines
1.9 KiB
JavaScript

export const createNotificationEmitterRuntime = (dependencies) => {
const {
process,
getDesktopNotifyEnabled,
desktopNotifyPrefix,
getUiNotificationClients,
getBroadcastGlobalUiEvent,
} = dependencies;
const writeSseEvent = (res, payload) => {
res.write(`data: ${JSON.stringify(payload)}\n\n`);
};
const emitDesktopNotification = (payload) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!desktopNotifyEnabled) {
return;
}
if (!payload || typeof payload !== 'object') {
return;
}
try {
// One-line protocol consumed by the Tauri shell.
process.stdout.write(`${desktopNotifyPrefix}${JSON.stringify(payload)}\n`);
} catch {
// ignore
}
};
const broadcastUiNotification = (payload) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!payload || typeof payload !== 'object') {
return;
}
const syntheticPayload = {
type: 'openchamber:notification',
properties: {
...payload,
// Tell the UI whether the sidecar stdout notification channel is active.
// When true, the desktop UI should skip this SSE notification to avoid duplicates.
// When false (e.g. tauri dev), the UI must handle this SSE notification itself.
desktopStdoutActive: desktopNotifyEnabled,
},
};
const broadcastGlobalUiEvent = typeof getBroadcastGlobalUiEvent === 'function'
? getBroadcastGlobalUiEvent()
: null;
if (broadcastGlobalUiEvent) {
broadcastGlobalUiEvent(syntheticPayload);
return;
}
const clients = getUiNotificationClients();
if (clients.size === 0) {
return;
}
for (const res of clients) {
try {
writeSseEvent(res, syntheticPayload);
} catch {
// ignore
}
}
};
return {
writeSseEvent,
emitDesktopNotification,
broadcastUiNotification,
};
};