@@ -23,6 +23,7 @@
|
||||
- Fixed final bash tool cards to avoid rendering duplicate full-output truncation paths ([#4819](https://github.com/earendil-works/pi/issues/4819)).
|
||||
- Fixed bash tool truncation line counts to ignore the trailing newline as an extra output line ([#4818](https://github.com/earendil-works/pi/issues/4818)).
|
||||
- Fixed footer home-directory abbreviation to avoid shortening sibling paths that only share the same prefix ([#4878](https://github.com/earendil-works/pi/issues/4878)).
|
||||
- Fixed macOS Bun release binaries to resolve the native clipboard sidecar so Ctrl+V image paste can load `@mariozechner/clipboard` ([#4307](https://github.com/earendil-works/pi/issues/4307)).
|
||||
|
||||
## [0.75.4] - 2026-05-20
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { createRequire } from "module";
|
||||
import { dirname, join } from "path";
|
||||
import { pathToFileURL } from "url";
|
||||
|
||||
export type ClipboardModule = {
|
||||
setText: (text: string) => Promise<void>;
|
||||
@@ -6,17 +8,25 @@ export type ClipboardModule = {
|
||||
getImageBinary: () => Promise<Array<number>>;
|
||||
};
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
let clipboard: ClipboardModule | null = null;
|
||||
type ClipboardRequire = (id: string) => unknown;
|
||||
|
||||
const moduleRequire = createRequire(import.meta.url);
|
||||
const executableDirRequire = createRequire(pathToFileURL(join(dirname(process.execPath), "package.json")).href);
|
||||
const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
||||
|
||||
if (!process.env.TERMUX_VERSION && hasDisplay) {
|
||||
try {
|
||||
clipboard = require("@mariozechner/clipboard") as ClipboardModule;
|
||||
} catch {
|
||||
clipboard = null;
|
||||
export function loadClipboardNative(
|
||||
requires: readonly ClipboardRequire[] = [moduleRequire, executableDirRequire],
|
||||
): ClipboardModule | null {
|
||||
for (const requireClipboard of requires) {
|
||||
try {
|
||||
return requireClipboard("@mariozechner/clipboard") as ClipboardModule;
|
||||
} catch {
|
||||
// Try the next resolution root.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const clipboard = !process.env.TERMUX_VERSION && hasDisplay ? loadClipboardNative() : null;
|
||||
|
||||
export { clipboard };
|
||||
|
||||
31
packages/coding-agent/test/clipboard-native.test.ts
Normal file
31
packages/coding-agent/test/clipboard-native.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { type ClipboardModule, loadClipboardNative } from "../src/utils/clipboard-native.ts";
|
||||
|
||||
type ClipboardRequire = (id: string) => unknown;
|
||||
|
||||
const fakeClipboard: ClipboardModule = {
|
||||
setText: async () => {},
|
||||
hasImage: () => true,
|
||||
getImageBinary: async () => [1, 2, 3],
|
||||
};
|
||||
|
||||
describe("loadClipboardNative", () => {
|
||||
test("falls back to the next require root", () => {
|
||||
const primary = vi.fn<ClipboardRequire>(() => {
|
||||
throw new Error("missing from bundled root");
|
||||
});
|
||||
const fallback = vi.fn<ClipboardRequire>(() => fakeClipboard);
|
||||
|
||||
expect(loadClipboardNative([primary, fallback])).toBe(fakeClipboard);
|
||||
expect(primary).toHaveBeenCalledWith("@mariozechner/clipboard");
|
||||
expect(fallback).toHaveBeenCalledWith("@mariozechner/clipboard");
|
||||
});
|
||||
|
||||
test("returns null when no require root can load clipboard", () => {
|
||||
const missing = vi.fn<ClipboardRequire>(() => {
|
||||
throw new Error("missing");
|
||||
});
|
||||
|
||||
expect(loadClipboardNative([missing])).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user