Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/printer/src/transport/ipc-print.electron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface ElectronIpc {
invoke: (channel: string, args: unknown) => Promise<unknown>;
}

function getIpc(): ElectronIpc {
const w = window as {
ipcRenderer?: ElectronIpc;
electronAPI?: { ipcRenderer?: ElectronIpc };
};
const ipc = w.ipcRenderer ?? w.electronAPI?.ipcRenderer;
if (!ipc) throw new Error('Electron ipcRenderer not available');
return ipc;
}

export const PRINT_TIMEOUT_MS = 30_000;

export async function ipcPrintRaw(
channel: string,
args: Record<string, unknown>,
timeoutMessage: string
): Promise<void> {
const ipc = getIpc();
const data = args.data;
if (!(data instanceof Uint8Array)) {
throw new Error('ipcPrintRaw expected args.data to be a Uint8Array');
}

let timeoutId: ReturnType<typeof setTimeout> | undefined;
try {
await Promise.race([
ipc.invoke(channel, { ...args, data: Array.from(data) }),
new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => reject(new Error(timeoutMessage)), PRINT_TIMEOUT_MS);
}),
]);
} finally {
if (timeoutId !== undefined) clearTimeout(timeoutId);
}
}
78 changes: 29 additions & 49 deletions packages/printer/src/transport/network-adapter.electron.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,37 @@
import type { PrinterTransport } from "../types";
import { ipcPrintRaw, PRINT_TIMEOUT_MS } from './ipc-print.electron';

interface ElectronIpc {
invoke: (channel: string, args: unknown) => Promise<unknown>;
}

function getIpc(): ElectronIpc {
const ipc = (window as any).ipcRenderer as ElectronIpc | undefined;
if (!ipc) throw new Error("Electron ipcRenderer not available");
return ipc;
}

const PRINT_TIMEOUT_MS = 30_000;
import type { PrinterTransport } from '../types';

/**
* Electron network adapter.
* Sends raw ESC/POS bytes to a printer via TCP through the main process.
*/
export class NetworkAdapter implements PrinterTransport {
readonly name = "network-electron";

constructor(
private host: string,
private port: number = 9100,
_vendor?: string,
) {}

async printRaw(data: Uint8Array): Promise<void> {
const ipc = getIpc();
// Send as regular array — Uint8Array doesn't serialize across IPC cleanly
const result = Promise.race([
ipc.invoke("print-raw-tcp", {
host: this.host,
port: this.port,
data: Array.from(data),
}),
new Promise<never>((_, reject) =>
setTimeout(
() =>
reject(new Error(`Print timed out after ${PRINT_TIMEOUT_MS}ms`)),
PRINT_TIMEOUT_MS,
),
),
]);
await result;
}

async printHtml(_html: string): Promise<void> {
throw new Error(
"NetworkAdapter does not support HTML printing. Use printRaw instead.",
);
}

async disconnect(): Promise<void> {
// TCP connections are per-request; nothing to clean up
}
readonly name = 'network-electron';

constructor(
private host: string,
private port: number = 9100,
_vendor?: string
) {}

async printRaw(data: Uint8Array): Promise<void> {
await ipcPrintRaw(
'print-raw-tcp',
{
host: this.host,
port: this.port,
data,
},
`Print timed out after ${PRINT_TIMEOUT_MS}ms`
);
}

async printHtml(_html: string): Promise<void> {
throw new Error('NetworkAdapter does not support HTML printing. Use printRaw instead.');
}

async disconnect(): Promise<void> {
// TCP connections are per-request; nothing to clean up
}
}
38 changes: 7 additions & 31 deletions packages/printer/src/transport/serial-adapter.electron.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import type { PrinterTransport } from '../types';

interface ElectronIpc {
invoke: (channel: string, args: unknown) => Promise<unknown>;
}
import { ipcPrintRaw, PRINT_TIMEOUT_MS } from './ipc-print.electron';

function getIpc(): ElectronIpc {
const w = window as {
ipcRenderer?: ElectronIpc;
electronAPI?: { ipcRenderer?: ElectronIpc };
};
const ipc = w.ipcRenderer ?? w.electronAPI?.ipcRenderer;
if (!ipc) throw new Error('Electron ipcRenderer not available');
return ipc;
}

const PRINT_TIMEOUT_MS = 30_000;
import type { PrinterTransport } from '../types';

/** Electron serial adapter — sends raw bytes to the main process, which writes to the serial device
* (macOS /dev/cu.*, Linux /dev/rfcomm*) for OS-paired Bluetooth Classic printers. */
Expand All @@ -24,21 +10,11 @@ export class SerialElectronAdapter implements PrinterTransport {
constructor(private deviceKey: string) {}

async printRaw(data: Uint8Array): Promise<void> {
const ipc = getIpc();
let timeoutId: ReturnType<typeof setTimeout> | undefined;
try {
await Promise.race([
ipc.invoke('print-raw-serial', { device: this.deviceKey, data: Array.from(data) }),
new Promise<never>((_, reject) => {
timeoutId = setTimeout(
() => reject(new Error(`Serial print timed out after ${PRINT_TIMEOUT_MS}ms`)),
PRINT_TIMEOUT_MS
);
}),
]);
} finally {
if (timeoutId !== undefined) clearTimeout(timeoutId);
}
await ipcPrintRaw(
'print-raw-serial',
{ device: this.deviceKey, data },
`Serial print timed out after ${PRINT_TIMEOUT_MS}ms`
);
}

async printHtml(_html: string): Promise<void> {
Expand Down
33 changes: 7 additions & 26 deletions packages/printer/src/transport/usb-adapter.electron.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import type { PrinterTransport } from '../types';

interface ElectronIpc {
invoke: (channel: string, args: unknown) => Promise<unknown>;
}
import { ipcPrintRaw, PRINT_TIMEOUT_MS } from './ipc-print.electron';

function getIpc(): ElectronIpc {
const w = window as {
ipcRenderer?: ElectronIpc;
electronAPI?: { ipcRenderer?: ElectronIpc };
};
const ipc = w.ipcRenderer ?? w.electronAPI?.ipcRenderer;
if (!ipc) throw new Error('Electron ipcRenderer not available');
return ipc;
}

const PRINT_TIMEOUT_MS = 30_000;
import type { PrinterTransport } from '../types';

/** Electron USB adapter — sends raw bytes to the main process, which writes to the USB endpoint. */
export class UsbElectronAdapter implements PrinterTransport {
Expand All @@ -23,16 +9,11 @@ export class UsbElectronAdapter implements PrinterTransport {
constructor(private deviceKey: string) {}

async printRaw(data: Uint8Array): Promise<void> {
const ipc = getIpc();
await Promise.race([
ipc.invoke('print-raw-usb', { device: this.deviceKey, data: Array.from(data) }),
new Promise<never>((_, reject) =>
setTimeout(
() => reject(new Error(`USB print timed out after ${PRINT_TIMEOUT_MS}ms`)),
PRINT_TIMEOUT_MS
)
),
]);
await ipcPrintRaw(
'print-raw-usb',
{ device: this.deviceKey, data },
`USB print timed out after ${PRINT_TIMEOUT_MS}ms`
);
}

async printHtml(_html: string): Promise<void> {
Expand Down
Loading