9b78a6fa85
- Migrated native bindings from TypeScript wrappers to NAPI-RS generated modules with auto-generated type definitions and runtime enums. - Replaced chunk tree API with stateful ChunkState class supporting render, edit, and resolve operations with improved error handling. - Converted callback signatures to error-first pattern (error, result) for shell, PTY, glob, and grep operations. - Introduced type-safe enums for MacOSAppearance, GrepOutputMode, KeyEventType, ImageFormat, and AstMatchStrictness replacing string literals. - Refactored chunk tree implementation with dedicated modules for edit, indent, resolve, and state management with comprehensive validation. - Moved clipboard utilities from native bindings to coding-agent package with improved OSC 52 and Termux compatibility.
81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import { execSync } from "node:child_process";
|
|
import type { ClipboardImage } from "@oh-my-pi/pi-natives";
|
|
import * as native from "@oh-my-pi/pi-natives";
|
|
|
|
const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
|
|
/**
|
|
* Copy text to the system clipboard.
|
|
*
|
|
* Emits OSC 52 first when running in a real terminal (works over SSH/mosh),
|
|
* then attempts native clipboard copy as best-effort for local sessions.
|
|
* On Termux, tries `termux-clipboard-set` before native.
|
|
*
|
|
* @param text - UTF-8 text to place on the clipboard.
|
|
*/
|
|
export async function copyToClipboard(text: string): Promise<void> {
|
|
if (process.stdout.isTTY) {
|
|
const onError = (err: unknown) => {
|
|
process.stdout.off("error", onError);
|
|
// Prevent unhandled 'error' from crashing the process when stdout is a closed pipe.
|
|
if ((err as NodeJS.ErrnoException | null | undefined)?.code === "EPIPE") {
|
|
return;
|
|
}
|
|
};
|
|
try {
|
|
const encoded = Buffer.from(text).toString("base64");
|
|
const osc52 = `\x1b]52;c;${encoded}\x07`;
|
|
process.stdout.on("error", onError);
|
|
process.stdout.write(osc52, err => {
|
|
process.stdout.off("error", onError);
|
|
// If stdout is closed (e.g. piped to a process that exits early),
|
|
// ignore EPIPE and proceed with native clipboard best-effort.
|
|
if ((err as NodeJS.ErrnoException | null | undefined)?.code === "EPIPE") {
|
|
return;
|
|
}
|
|
});
|
|
} catch (err) {
|
|
process.stdout.off("error", onError);
|
|
if ((err as NodeJS.ErrnoException | null | undefined)?.code !== "EPIPE") {
|
|
// Ignore all write failures (OSC 52 is best-effort).
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also try native tools (best effort for local sessions)
|
|
try {
|
|
if (process.env.TERMUX_VERSION) {
|
|
try {
|
|
execSync("termux-clipboard-set", { input: text, timeout: 5000 });
|
|
return;
|
|
} catch {
|
|
// Fall through to native
|
|
}
|
|
}
|
|
|
|
await native.copyToClipboard(text);
|
|
} catch {
|
|
// Ignore — clipboard copy is best-effort
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read an image from the system clipboard.
|
|
*
|
|
* Returns null on Termux (no image clipboard support) or when no display
|
|
* server is available (headless/SSH without forwarding).
|
|
*
|
|
* @returns PNG payload or null when no image is available.
|
|
*/
|
|
export async function readImageFromClipboard(): Promise<ClipboardImage | null> {
|
|
if (process.env.TERMUX_VERSION) {
|
|
return null;
|
|
}
|
|
|
|
if (!hasDisplay) {
|
|
return null;
|
|
}
|
|
|
|
return (await native.readImageFromClipboard()) ?? null;
|
|
}
|