UNPKG

@earendil-works/pi-coding-agent

Version:

Coding agent CLI with read, bash, edit, write tools and session management

98 lines • 4.22 kB
import { Worker } from "node:worker_threads"; import { IMAGE_RESIZE_WORKER_RESPONSE_TYPE, resizeImageInProcess, } from "./image-resize-core.js"; function toTransferableBytes(input) { // Transfer detaches the buffer, so transfer a worker-owned copy and leave the // caller's bytes intact. return new Uint8Array(input); } function isResizeImageWorkerResponse(value) { return (value !== null && typeof value === "object" && "type" in value && value.type === IMAGE_RESIZE_WORKER_RESPONSE_TYPE); } function createResizeWorker(workerSpecifier) { return new Worker(workerSpecifier); } async function resizeImageInWorker(workerSpecifier, inputBytes, mimeType, options) { const worker = createResizeWorker(workerSpecifier); try { const inputBytesForWorker = toTransferableBytes(inputBytes); return await new Promise((resolve, reject) => { let settled = false; const settle = (result) => { if (settled) return; settled = true; resolve(result); }; const fail = (error) => { if (settled) return; settled = true; reject(error); }; worker.on("message", (message) => { // Ignore messages Node posts on the worker channel itself, such as // `{ "watch:require": [...] }` under `node --watch` (nodejs/node#65044). if (!isResizeImageWorkerResponse(message)) return; if ("error" in message) fail(new Error(message.error)); else settle(message.result); }); worker.once("error", fail); worker.once("exit", (code) => { if (!settled) { fail(new Error(`Image resize worker exited with code ${code}`)); } }); worker.postMessage({ inputBytes: inputBytesForWorker, mimeType, options, }, [inputBytesForWorker.buffer]); }); } finally { // Await termination so a serial resize never starts a worker while this one is // still shutting down; that overlap aborted the process on macOS with Node 24 (#10695). await worker.terminate().catch(() => undefined); } } /** * Resize an image to fit within the specified max dimensions and encoded file size. * Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not * block the TUI event loop. If the worker cannot be loaded (for example in some * Bun compiled executable layouts), fall back to in-process resizing so image * reads still work. */ export async function resizeImage(inputBytes, mimeType, options) { const isTypeScriptRuntime = import.meta.url.endsWith(".ts"); const workerUrl = new URL(isTypeScriptRuntime ? "./image-resize-worker.ts" : "./image-resize-worker.js", import.meta.url); // Bun compiled executables resolve worker entrypoints by string path, not via // new URL(..., import.meta.url). Try the string path first under Bun so the // release binary uses the embedded worker instead of falling back in-process. if (typeof process.versions.bun === "string") { try { return await resizeImageInWorker("./src/utils/image-resize-worker.ts", inputBytes, mimeType, options); } catch { } } try { return await resizeImageInWorker(workerUrl, inputBytes, mimeType, options); } catch { return resizeImageInProcess(inputBytes, mimeType, options); } } /** * Format a dimension note for resized images. * This helps the model understand the coordinate mapping. */ export function formatDimensionNote(result) { if (!result.wasResized) { return undefined; } const scale = result.originalWidth / result.width; return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`; } //# sourceMappingURL=image-resize.js.map