Skip to content

Clipboard API

client.clipboard reads and replaces the system clipboard in three separate formats — text, images and files — each behind its own permission, so an app asks only for what it needs.

Availability

OperationPermissionNotes
readTextsystem.clipboard.readReads content put there by any application.
writeTextsystem.clipboard.writeReplaces the clipboard.
readImagesystem.clipboard.image.readRGBA pixels.
writeImagesystem.clipboard.image.writeRGBA pixels.
copyFiles, cutFilesystem.clipboard.files.write, plus files.download (copy) or files.move (cut)Windows, with a connected workspace.
pasteFilessystem.clipboard.files.read and files.uploadWindows.

Text and image access work in any workspace, including the local desktop. File clipboard operations need a connected file service and the native file clipboard, which today means Windows.

Interface

ts
interface ClipboardImage { width: number; height: number; rgba: Uint8Array }   // row-major, top to bottom

interface AppClipboardAPI {
  readText(signal?: AbortSignal): Promise<string>;
  writeText(text: string, signal?: AbortSignal): Promise<void>;
  readImage(signal?: AbortSignal): Promise<ClipboardImage>;
  writeImage(image: ClipboardImage, signal?: AbortSignal): Promise<void>;
  copyFiles(entries: RemoteEntryLocation[], signal?: AbortSignal): Promise<void>;
  cutFile(entry: RemoteEntryLocation, signal?: AbortSignal): Promise<void>;
  pasteFiles(destination: RemoteFileLocation, signal?: AbortSignal): Promise<RemoteTransfer[]>;
}

Text is limited to 4 Mi UTF-16 units and images to 64 MiB of RGBA data. Both are streamed in chunks by the SDK, so your code sees one call.

Examples

Text, in both directions:

ts
const text = await client.clipboard.readText();
await client.clipboard.writeText(`${text}\n— copied from My Notes`);

An image, drawn into a canvas:

ts
const image = await client.clipboard.readImage();
const canvas = new OffscreenCanvas(image.width, image.height);
const context = canvas.getContext("2d")!;
context.putImageData(new ImageData(new Uint8ClampedArray(image.rgba), image.width, image.height), 0, 0);

Writing one back requires exactly width * height * 4 bytes:

ts
await client.clipboard.writeImage({ width, height, rgba });

Publishing remote files so the user can paste them into their file manager, then pasting files in:

ts
await client.clipboard.copyFiles(selection.map((e) => ({ binding, path: e.path, revision: e.revision! })));

const transfers = await client.clipboard.pasteFiles({ binding, path: currentFolder.path });
for (const transfer of transfers) {
  try { await transfer.run(); } finally { await transfer.close(); }
}

pasteFiles returns transfers, so progress and cancellation work exactly as on the Transfers API page. When the clipboard holds no files it resolves to an empty array rather than failing.

Errors

CodeMessageMeaning
invalidClipboard text must be a string.Wrong type.
invalidClipboard text must not exceed 4 Mi UTF-16 units.Too much text.
invalidProvide positive image dimensions with a representable RGBA length.Dimensions and buffer disagree.
invalidCut a current file entry from an accepted workspace binding.Stale or foreign entry.
invalidCopy current file entries from one workspace binding.Entries from several connections.
busyA clipboard read is already active in this window.One read at a time.
busyFinish or cancel the previous clipboard publication first.A file publication is still running.
unavailableNative file clipboard export is unavailable.Not Windows, or no native clipboard.
abortedClipboard publication ended after cancellation or a connection change. It may have completed; do not retry automatically.Genuinely uncertain.
failedUnable to read clipboard text. / Unable to replace clipboard text.The OS refused.

Lifecycle and permissions

One read and one write may be active per window; a second call fails with busy rather than interleaving. Reads and writes take a snapshot before any awaiting, so a clipboard change mid-transfer cannot splice two contents together, and an image is published only when complete.

Publishing files to the system clipboard is not instant: the desktop scans the selection first, and the offer stays valid only while ShellCanvas and the connection remain open — the bytes stream when the user pastes. A cancellation cannot retract an offer the operating system has already accepted.

Remember what these permissions mean to a user: clipboard read sees whatever they last copied anywhere, including passwords. Ask for the narrowest format you need, and read only in response to a user action.

ShellCanvas documentation