Amp-Thread-ID: https://ampcode.com/threads/T-a6cf4bd2-36d0-4ef8-ada6-a8e2850fcacf Co-authored-by: Amp <amp@ampcode.com>
99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
// Communication adapter that works in both Electron and Web modes
|
|
import { io, Socket } from "socket.io-client";
|
|
|
|
type EventCallback = (event: any, ...args: any[]) => void;
|
|
|
|
export interface IPCAdapter {
|
|
send(channel: string, ...args: any[]): void;
|
|
invoke(channel: string, ...args: any[]): Promise<any>;
|
|
on(channel: string, callback: EventCallback): void;
|
|
}
|
|
|
|
class ElectronAdapter implements IPCAdapter {
|
|
private ipcRenderer: any;
|
|
|
|
constructor() {
|
|
this.ipcRenderer = require("electron").ipcRenderer;
|
|
}
|
|
|
|
send(channel: string, ...args: any[]): void {
|
|
this.ipcRenderer.send(channel, ...args);
|
|
}
|
|
|
|
invoke(channel: string, ...args: any[]): Promise<any> {
|
|
return this.ipcRenderer.invoke(channel, ...args);
|
|
}
|
|
|
|
on(channel: string, callback: EventCallback): void {
|
|
this.ipcRenderer.on(channel, callback);
|
|
}
|
|
}
|
|
|
|
class WebAdapter implements IPCAdapter {
|
|
private socket: Socket;
|
|
private responseHandlers = new Map<number, { resolve: (value: any) => void; reject: (reason?: any) => void }>();
|
|
private requestCounter = 0;
|
|
|
|
constructor() {
|
|
this.socket = io();
|
|
|
|
// Handle invoke responses from server
|
|
this.socket.on("invoke-response", (requestId: number, error: any, result: any) => {
|
|
const handler = this.responseHandlers.get(requestId);
|
|
if (handler) {
|
|
this.responseHandlers.delete(requestId);
|
|
if (error) {
|
|
handler.reject(error);
|
|
} else {
|
|
handler.resolve(result);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
send(channel: string, ...args: any[]): void {
|
|
this.socket.emit(channel, ...args);
|
|
}
|
|
|
|
invoke(channel: string, ...args: any[]): Promise<any> {
|
|
return new Promise((resolve, reject) => {
|
|
const requestId = this.requestCounter++;
|
|
this.responseHandlers.set(requestId, { resolve, reject });
|
|
|
|
// Set timeout for invoke
|
|
setTimeout(() => {
|
|
if (this.responseHandlers.has(requestId)) {
|
|
this.responseHandlers.delete(requestId);
|
|
reject(new Error(`Invoke timeout: ${channel}`));
|
|
}
|
|
}, 10000);
|
|
|
|
this.socket.emit("invoke", requestId, channel, ...args);
|
|
});
|
|
}
|
|
|
|
on(channel: string, callback: EventCallback): void {
|
|
this.socket.on(channel, (...args: any[]) => {
|
|
// Mimic Electron's event object structure
|
|
const event = {};
|
|
callback(event, ...args);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Detect environment and create appropriate adapter
|
|
function createAdapter(): IPCAdapter {
|
|
// Check if running in Electron
|
|
const isElectron = typeof window !== "undefined" &&
|
|
typeof (window as any).process !== "undefined" &&
|
|
(window as any).process.type === "renderer";
|
|
|
|
if (isElectron) {
|
|
return new ElectronAdapter();
|
|
} else {
|
|
return new WebAdapter();
|
|
}
|
|
}
|
|
|
|
export const ipcAdapter = createAdapter();
|