From 20ec5c40fd9468b15bb5b86c7af32d8b23182294 Mon Sep 17 00:00:00 2001 From: Simon Klee Date: Thu, 10 Sep 2026 15:29:09 +0200 Subject: [PATCH] fix(tui): strip NUL characters before clipboard writes OpenTUI rejects clipboard text containing NUL before any destination, so shell output with embedded NULs made /copy fail entirely. Drop NULs at the shared TUI write boundary so the rest of the transcript can copy. --- packages/tui/src/clipboard.ts | 3 ++- packages/tui/test/clipboard.test.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/tui/src/clipboard.ts b/packages/tui/src/clipboard.ts index cd7a6ce1a170..9374fc84417a 100644 --- a/packages/tui/src/clipboard.ts +++ b/packages/tui/src/clipboard.ts @@ -51,7 +51,8 @@ export function createClipboardAdapter(clipboard: CoreClipboardService): OwnedCl throw new Error(`Unexpected clipboard MIME type: ${result.representation.mimeType}`) }, async write(text) { - const result = await clipboard.writeText(text, { + // OpenTUI rejects NUL before any destination; host clipboard text cannot contain it. + const result = await clipboard.writeText(text.replaceAll("\0", ""), { destination: "all-available", selection: "clipboard", }) diff --git a/packages/tui/test/clipboard.test.ts b/packages/tui/test/clipboard.test.ts index 6f4997f03032..a3870f626300 100644 --- a/packages/tui/test/clipboard.test.ts +++ b/packages/tui/test/clipboard.test.ts @@ -102,6 +102,19 @@ test("uses all available routes but skips the process host remotely", async () = expect(writes).toEqual({ host: 0, terminal: 1 }) }) +test("removes NUL characters before writing", async () => { + const writes: string[] = [] + const clipboard = createClipboardAdapter( + coreClipboard({ + onWrite: (text) => writes.push(text), + }), + ) + + expect(await clipboard.write("before\0after")).toBeUndefined() + expect(await clipboard.write("clean")).toBeUndefined() + expect(writes).toEqual(["beforeafter", "clean"]) +}) + test("rejects only when no clipboard route accepted the write", async () => { const writes: [string, ClipboardWriteOptions][] = [] const failure = new Error("native clipboard failed")