Selkies
Developer ReferenceWeb client coreLib

lib/clipboard-sync

Client/server clipboard synchronization, shared by both transports.

The pieces are factories the cores compose: createClipboardSync owns the server-clipboard cache and the change-only signature (unchanged content never re-crosses the transport in either direction), createMultipartClipboardState reassembles multipart server pushes, createTaggedClipboardFetch marks the connect-time cache-only fetch, createLocalClipboardSender is the focus-driven local-to-server path, createDeferredClipboardWriter lands server pushes on engines that reject clipboard writes outside a user activation, localClipboardBlocker names what stops a local write at all, and createClipboardGestures wires the copy and paste keystrokes. The transports differ only in the hooks they inject: how a request or payload is sent and the enablement gates, which are closures re-read per event so runtime settings changes apply immediately.

Interfaces

MultipartClipboardState

Defined in: lib/clipboard-sync.js:128

Properties

begin
begin: (mime, total) => void;

Defined in: lib/clipboard-sync.js:129

Arms a transfer.

Parameters
ParameterType
mimestring
totalnumber
Returns

void

push
push: (b64) => void;

Defined in: lib/clipboard-sync.js:130

Accumulates one base64 chunk.

Parameters
ParameterType
b64string
Returns

void

assemble
assemble: () => object;

Defined in: lib/clipboard-sync.js:131

Joins the chunks and resets; null when no transfer is in progress.

Returns

object

base64
base64: string;
mimeType
mimeType: string;
totalSize
totalSize: number;
reset
reset: () => void;

Defined in: lib/clipboard-sync.js:133

Drops the transfer.

Returns

void

inProgress
inProgress: boolean;

Defined in: lib/clipboard-sync.js:134

mimeType
mimeType: string;

Defined in: lib/clipboard-sync.js:135

totalSize
totalSize: number;

Defined in: lib/clipboard-sync.js:136

Declared size in bytes.

receivedSize
receivedSize: number;

Defined in: lib/clipboard-sync.js:137

Decoded bytes accumulated so far.


TaggedClipboardFetch

Defined in: lib/clipboard-sync.js:197

Properties

arm
arm: () => void;

Defined in: lib/clipboard-sync.js:198

Records that the server tags the reply.

Returns

void

armLegacyWindow
armLegacyWindow: (ms) => void;

Defined in: lib/clipboard-sync.js:199

Starts the timed fallback after sending cr.

Parameters
ParameterType
msnumber
Returns

void

consume
consume: () => boolean;

Defined in: lib/clipboard-sync.js:201

Whether the next payload is the fetch reply.

Returns

boolean


LocalClipboardSender

Defined in: lib/clipboard-sync.js:242

Properties

readAndSend
readAndSend: () => Promise<void>;

Defined in: lib/clipboard-sync.js:243

Reads the local clipboard and pushes any content to the server.

Returns

Promise<void>

maybeInitial
maybeInitial: () => Promise<void>;

Defined in: lib/clipboard-sync.js:245

The connect-time one-shot send.

Returns

Promise<void>

getSendInFlight
getSendInFlight: () => Promise<void>;

Defined in: lib/clipboard-sync.js:246

The send the paste-ordering hold awaits, or null.

Returns

Promise<void>


DeferredClipboardWriter

Defined in: lib/clipboard-sync.js:353

Properties

write
write: (attempt, callbacks?) => Promise<boolean>;

Defined in: lib/clipboard-sync.js:354

Runs an async clipboard write now, stashing it for the next gesture on an activation rejection.

Parameters
ParameterType
attempt() => Promise<void>
callbacks?{ onSuccess?: () => void; onFailure?: (err) => void; }
callbacks.onSuccess?() => void
callbacks.onFailure?(err) => void
Returns

Promise<boolean>

flush
flush: () => void;

Defined in: lib/clipboard-sync.js:357

Retries the stashed write.

Returns

void

getInFlight
getInFlight: () => Promise<boolean>;

Defined in: lib/clipboard-sync.js:358

The most recent attempt, immediate or flushed, or null.

Returns

Promise<boolean>


ClipboardSync

Defined in: lib/clipboard-sync.js:469

Properties

sig
sig: (data, mime?) => string;

Defined in: lib/clipboard-sync.js:470

Content signature.

Parameters
ParameterType
datastring | ArrayBuffer | Uint8Array<ArrayBufferLike> | Blob
mime?string
Returns

string

shouldSend
shouldSend: (data, mime?) => boolean;

Defined in: lib/clipboard-sync.js:472

Change-only gate.

Parameters
ParameterType
datastring | ArrayBuffer | Uint8Array<ArrayBufferLike> | Blob
mime?string
Returns

boolean

markSynced
markSynced: (data, mime?) => void;

Defined in: lib/clipboard-sync.js:474

Records content as synced, on transfer success.

Parameters
ParameterType
datastring | ArrayBuffer | Uint8Array<ArrayBufferLike> | Blob
mime?string
Returns

void

resolveServer
resolveServer: (text?, blob?, mime?, bytes?) => void;

Defined in: lib/clipboard-sync.js:476

Caches fresh server data and settles pending requests.

Parameters
ParameterType
text?string
blob?Blob
mime?string
bytes?Uint8Array
Returns

void

captureLocalImageSig
captureLocalImageSig: () => Promise<void>;

Defined in: lib/clipboard-sync.js:478

Records the browser's re-encoded form of the image just written locally.

Returns

Promise<void>

request
request: (wantBinary) => Promise<string | Blob>;

Defined in: lib/clipboard-sync.js:480

Requests the server clipboard.

Parameters
ParameterType
wantBinaryboolean
Returns

Promise<string | Blob>

copyViaExecCommand
copyViaExecCommand: (textPromise) => Promise<void>;

Defined in: lib/clipboard-sync.js:482

Last-resort copy through execCommand.

Parameters
ParameterType
textPromisePromise<string>
Returns

Promise<void>

lastText
lastText: string;

Defined in: lib/clipboard-sync.js:484

lastBlob
lastBlob: Blob;

Defined in: lib/clipboard-sync.js:485

lastMime
lastMime: string;

Defined in: lib/clipboard-sync.js:486

Type Aliases

LocalClipboardContent

type LocalClipboardContent = 
  | {
  kind: "text";
  text: string;
}
  | {
  kind: "image";
  blob: Blob;
  mime: string;
};

Defined in: lib/clipboard-sync.js:25

Type Parameters

Type Parameter

Variables

CLIPBOARD_PREVIEW_LIMIT

const CLIPBOARD_PREVIEW_LIMIT: number;

Defined in: lib/clipboard-sync.js:445

Longest server clipboard text the dashboards are shown, in characters.

Functions

reencodeBlobAsPng()

function reencodeBlobAsPng(blob): Promise<Blob>;

Defined in: lib/clipboard-sync.js:38

Re-encodes a raster blob as PNG.

Chromium's async clipboard accepts only image/png on write, but a source may offer only JPEG, BMP or WebP, so the blob is decoded with the browser's own decoders and re-encoded first.

Parameters

ParameterTypeDescription
blobBlobThe image.

Returns

Promise<Blob>

The PNG.

Throws

When the blob is undecodable (a dimensionless SVG) or the encode fails.


localClipboardBlocker()

function localClipboardBlocker(): string;

Defined in: lib/clipboard-sync.js:62

Why the browser cannot be asked to write the local clipboard, or null when it can.

Both engines expose navigator.clipboard in a secure context only, so a deployment served over http:// on anything but localhost has no clipboard API at all: a server image then lands nowhere, and saying so is the difference between a bug report and a certificate.

Returns

string

The reason, ready to show.


writeImageToLocalClipboard()

function writeImageToLocalClipboard(blob, mime): Promise<void>;

Defined in: lib/clipboard-sync.js:76

Writes a server image to the local clipboard, PNG-normalized through reencodeBlobAsPng.

Parameters

ParameterTypeDescription
blobBlobThe image.
mimestringIts type.

Returns

Promise<void>

Throws

When the type is undecodable or the clipboard write fails.


readLocalClipboard()

function readLocalClipboard(binaryEnabled): Promise<LocalClipboardContent>;

Defined in: lib/clipboard-sync.js:91

Reads the local clipboard for the focus and gesture send path.

Chromium's read()/getType() throw DataError on large text and some images while readText() still returns the text, so every such failure falls back to it rather than dropping the sync.

Parameters

ParameterTypeDescription
binaryEnabledbooleanWhether images may be read.

Returns

Promise<LocalClipboardContent>

The content, or null when empty.

Throws

Only genuinely unexpected errors, for the caller to log.


createMultipartClipboardState()

function createMultipartClipboardState(): MultipartClipboardState;

Defined in: lib/clipboard-sync.js:150

Multipart server-to-client clipboard download state.

The decoded byte count is tracked incrementally from the base64 lengths, so nothing decodes on the main thread until the caller assembles. A truncated stream must never be delivered as content: callers compare receivedSize against totalSize before assembling, or the decoded byte length after, and discard on a mismatch.

Returns

MultipartClipboardState


createTaggedClipboardFetch()

function createTaggedClipboardFetch(): TaggedClipboardFetch;

Defined in: lib/clipboard-sync.js:214

Tracker for the connect-time cache-only clipboard fetch (cr).

The reply must populate the sync cache and preview but never be written to the local clipboard, which would clobber whatever the user copied just before connecting. A tagging server marks the answering payload deterministically; for a server that never tags, a short timed window stands in, so a dropped reply cannot swallow a later genuine push.

Returns

TaggedClipboardFetch


createLocalClipboardSender()

function createLocalClipboardSender(hooks): LocalClipboardSender;

Defined in: lib/clipboard-sync.js:273

Focus and gesture driven local-to-server clipboard sync.

readAndSend is serialized so the paste-ordering hold can hold Ctrl/Cmd+V until the send settles. maybeInitial covers a focused Chromium tab, which gets no focus event after connect and would otherwise leave the server on its stale clipboard until the first alt-tab; it runs only when clipboard read is already granted, since it must never raise a prompt at load.

Parameters

ParameterTypeDescription
hooks{ isChromium: boolean; isSharedMode: () => boolean; canSync: () => boolean; canRead: () => boolean; binaryEnabled: () => boolean; sendClipboardData: (data, mime?) => Promise<void>; dedupeText?: boolean; getDeferredWriteInFlight?: () => Promise<any>; }-
hooks.isChromiumbooleanEngine flag.
hooks.isSharedMode() => booleanViewer sessions never send.
hooks.canSync() => booleanClipboard sync enabled.
hooks.canRead() => booleanLocal-to-server direction enabled.
hooks.binaryEnabled() => booleanWhether images are sent.
hooks.sendClipboardData(data, mime?) => Promise<void>Transport send.
hooks.dedupeText?booleanSuppresses re-sending unchanged text; the WebRTC core's behavior, while the WebSocket core sends per event and dedupes at the server.
hooks.getDeferredWriteInFlight?() => Promise<any>The deferred writer's pending write, awaited before reading.

Returns

LocalClipboardSender


readAndSend()

function readAndSend(): Promise<void>;

Defined in: lib/clipboard-sync.js:293

A server push still settling through the deferred writer must land before this read: reading around the flush returns the pre-push content, which then reads as a change and bounces the stale value back to the server.

Returns

Promise<void>


createDeferredClipboardWriter()

function createDeferredClipboardWriter(): DeferredClipboardWriter;

Defined in: lib/clipboard-sync.js:381

Deferred local-clipboard writer for server pushes.

Firefox and WebKit reject navigator.clipboard writes outside a transient user activation, and a server push handler never has one, so on an activation or focus rejection the write is stashed and retried on the next real gesture instead of being lost. Only the newest pending write is kept, since the clipboard is last-value-wins: a monotonic sequence lets a failed newer write replace an older stash while a flushed stash that fails again can never clobber a write that arrived during its attempt. The paste-ordering hold awaits the in-flight attempt so a server-to-client write lands before a paste reads the local clipboard; otherwise the stash flushes on the paste's own keydown and lands just after the read, and the first paste is one behind. The flush rides keydown and pointerdown, which carry a user activation, and focus and visibilitychange, which land the write the instant Chromium accepts it again (it rejects writes from an unfocused document), well before the user's next paste.

Returns

DeferredClipboardWriter


attemptOnce()

function attemptOnce(w): Promise<boolean>;

Defined in: lib/clipboard-sync.js:405

Runs one write. An activation rejection (a synthetic event, or a blurred tab) stashes it for the next gesture unless something newer replaced it; any other error reaches onFailure.

The attempt is started inside a promise chain so that a caller passing a plain expression -- navigator.clipboard.write(...), which throws outright where the browser exposes no clipboard -- fails the same way an async one does, instead of throwing past onFailure.

Parameters

ParameterType
wany

Returns

Promise<boolean>


write()

function write(attempt, __namedParameters?): Promise<boolean>;

Defined in: lib/clipboard-sync.js:435

Runs attempt now; on an activation or focus rejection queues it for the next gesture. onSuccess fires whenever the write eventually lands, onFailure only for non-activation errors.

Parameters

ParameterType
attemptany
__namedParameters{ }

Returns

Promise<boolean>


clipboardPreviewMessage()

function clipboardPreviewMessage(text): object;

Defined in: lib/clipboard-sync.js:458

The clipboardContentUpdate message carrying server clipboard text to the dashboards.

A multi-MB payload structured-clones through postMessage and lands in a controlled textarea, freezing the page, while the UI only needs a bounded preview. The truncated flag tells the dashboard to render it read-only so a blur cannot echo the cut-down text back over the real server clipboard.

Parameters

ParameterTypeDescription
textstringThe server clipboard text.

Returns

object

type
type: string;
text
text: string;
truncated
truncated: boolean;
totalLength
totalLength: number;

createClipboardSync()

function createClipboardSync(hooks): ClipboardSync;

Defined in: lib/clipboard-sync.js:510

Server-clipboard cache, change-only signature and the Ctrl/Cmd+C request queue with its one-behind guard.

The server reads its clipboard the instant REQUEST_CLIPBOARD arrives, racing ahead of the application writing the new selection, so a request stays open until an incoming value differs from the value cached when it was made. The wire protocol carries no request id, so any server push can settle the oldest pending request; the timeout plus the cache bound the impact.

Exactly one value is current at a time, the latest synced in either direction: remembering older signatures would suppress legitimately re-copying content copied before an intervening value. Beside it lives the browser's re-encoded form of the latest inbound image, since writing a pushed image recompresses it and the focus read-back would otherwise read as new and echo once; it follows the synced signature's lifetime.

Parameters

ParameterTypeDescription
hooks{ sendRequest: () => void; }-
hooks.sendRequest() => voidEmits REQUEST_CLIPBOARD on the transport.

Returns

ClipboardSync


sigOf()

function sigOf(data, mime): object;

Defined in: lib/clipboard-sync.js:535

Both signature forms of a value. Text and byte-backed values are content-hashed so two distinct payloads of equal size still differ; a bare Blob, whose bytes are not in hand, gets the size-only legacy form, which also rides along with hashed binary signatures so the two can be cross-matched.

Parameters

ParameterType
dataany
mimeany

Returns

object

full
full: string;
legacy
legacy: string;

shouldSend()

function shouldSend(data, mime): boolean;

Defined in: lib/clipboard-sync.js:565

Change-only gate: true while this content and mime differ from the last synced value. Read-only: the caller marks the content synced through markSynced only after the transfer completes, so a failed transfer never permanently suppresses re-sending the same content. The legacy compare suppresses echoes of content whose receive-side signature was stored without bytes.

Parameters

ParameterType
dataany
mimeany

Returns

boolean


markSynced()

function markSynced(data, mime): void;

Defined in: lib/clipboard-sync.js:572

Records content as synced; called on transfer success.

Parameters

ParameterType
dataany
mimeany

Returns

void


resolveServer()

function resolveServer(
   text, 
   blob, 
   mime, 
   bytes
): void;

Defined in: lib/clipboard-sync.js:582

Caches fresh server data and settles pending requests through the one-behind guard. bytes, when the receive path has them, make the stored signature content-hashed so it matches what shouldSend computes for the same data.

Parameters

ParameterType
textany
blobany
mimeany
bytesany

Returns

void


captureLocalImageSig()

function captureLocalImageSig(): Promise<void>;

Defined in: lib/clipboard-sync.js:613

After a server image is written to the local clipboard, records the browser's re-encoded representation so the next focus read is recognized as the same content instead of echoed back. Needs clipboard read permission and focus and is silently skipped otherwise; the worst case is one redundant round trip, never a loop. The capture is anchored to the synced signature at entry: a sync in either direction landing mid-read makes it stale, and storing it would suppress a legitimate later copy.

Returns

Promise<void>


request()

function request(wantBinary): Promise<string | Blob>;

Defined in: lib/clipboard-sync.js:642

Requests the server clipboard and resolves with the next fresh value.

After two seconds the request settles so the ClipboardItem promise, and the browser's transient-activation window, can never hang: with a cached value that differs from the baseline recorded at request time it resolves, otherwise it rejects, since resolving with the baseline-equal cache would settle the copy with stale content exactly when the session-start cache is empty or stale.

Parameters

ParameterTypeDescription
wantBinarybooleanWhether an image is wanted rather than text.

Returns

Promise<string | Blob>


copyViaExecCommand()

function copyViaExecCommand(textPromise): Promise<void>;

Defined in: lib/clipboard-sync.js:677

Last-resort copy for browsers that reject navigator.clipboard.write (older Firefox and Safari): execCommand('copy') from a hidden textarea. Awaiting the promise first can outlive the Ctrl/Cmd+C transient activation, hence last resort. A rejected request or an empty value writes nothing: either would clobber the user's local clipboard with pre-copy content.

Parameters

ParameterTypeDescription
textPromisePromise<string>The pending server text.

Returns

Promise<void>


createClipboardGestures()

function createClipboardGestures(hooks): object;

Defined in: lib/clipboard-sync.js:756

Keyboard and paste gesture wiring for clipboard sync.

Owns the three window-level pieces around the per-transport read and send functions:

  • Paste-ordering hold: a Ctrl/Cmd+V arriving while the local clipboard is still being read or sent would depart the ordered channel before the clipboard content and paste the previous value on the server. The chord's key events are swallowed, held until the send flushes (bounded), then replayed in order for the input stack.
  • Non-Chromium Ctrl/Cmd+C: Safari and Firefox reject navigator.clipboard from focus and message handlers, which have no transient activation, so the server clipboard is written inside the copy gesture through a ClipboardItem whose blob is a Promise, with execCommand('copy') as last resort.
  • Non-Chromium paste-to-server: driven by the paste event's synchronous clipboardData. There is deliberately no Ctrl/Cmd+V navigator.clipboard read: WebKit rejects it from keydown, Firefox re-raises its paste prompt, and it would double-send next to the paste event.

Gestures in page form fields (the settings UI) are left alone; the stream's overlay input is exempt. Consumed gestures are never preventDefaulted: the chord must still reach the remote session.

Parameters

ParameterTypeDescription
hooks{ isChromium: boolean; clipboardSync: ClipboardSync; sendClipboardData: (data, mime?) => Promise<void>; canSync: () => boolean; canRead: () => boolean; canWrite: () => boolean; binaryEnabled: () => boolean; getSendInFlight: () => Promise<any>; getDeferredWriteInFlight?: () => Promise<any>; }-
hooks.isChromiumbooleanEngine flag.
hooks.clipboardSyncClipboardSyncThe server-clipboard state.
hooks.sendClipboardData(data, mime?) => Promise<void>Transport send.
hooks.canSync() => booleanClipboard sync enabled.
hooks.canRead() => booleanLocal-to-server direction enabled.
hooks.canWrite() => booleanServer-to-local direction enabled.
hooks.binaryEnabled() => booleanWhether images are sent.
hooks.getSendInFlight() => Promise<any>The local sender's pending send.
hooks.getDeferredWriteInFlight?() => Promise<any>The deferred writer's pending write.

Returns

object

Listener registration.

wire
wire: () => void;
Returns

void

unwire
unwire: () => void;
Returns

void


dropHeldPasteKeydowns()

function dropHeldPasteKeydowns(): void;

Defined in: lib/clipboard-sync.js:795

The in-flight transfer failed or never settled: injecting the held V now would paste stale content, so the held keydowns are dropped. The swallowed keyups (V and the chord's modifiers) are still replayed, as losing a modifier keyup would leave it stuck server-side.

Returns

void


holdPasteWhileClipboardInFlight()

function holdPasteWhileClipboardInFlight(ev): void;

Defined in: lib/clipboard-sync.js:818

Capture-phase key listener implementing the paste-ordering hold.

A paste chord is held while a send is in flight or a server-to-client local-clipboard write is still landing, since the paste would otherwise read the old value; any KeyV event is held while a replay is queued, so its keyup cannot overtake the held keydown, and so are the chord's modifier keyups, since a Ctrl keyup overtaking the replayed V would break the chord server-side and type a literal v. The hold waits for the current read/send and deferred write, then re-checks, as a follow-on transfer may have started meanwhile (the deferred write flushed by this very keydown); replay happens only once nothing is pending, and on failure or an expired bound the paste is dropped rather than injected with stale content.

Parameters

ParameterTypeDescription
evKeyboardEvent-

Returns

void


onCopyKeydown()

function onCopyKeydown(event): void;

Defined in: lib/clipboard-sync.js:864

Non-Chromium Ctrl/Cmd+C: writes the server clipboard inside the gesture.

Only text/plain is advertised: a Ctrl/Cmd+C cannot synchronously know whether the server's current clipboard is an image, and a stale cached MIME type would build a malformed ClipboardItem. Server images are delivered by the push handler instead. Autorepeat is ignored so it cannot spam REQUEST_CLIPBOARD.

Parameters

ParameterTypeDescription
eventKeyboardEvent-

Returns

void


onPaste()

function onPaste(event): void;

Defined in: lib/clipboard-sync.js:898

Non-Chromium paste-to-server from the event's synchronous clipboard data, preferring an image when binary clipboard is on and the payload carries one.

Parameters

ParameterTypeDescription
eventClipboardEvent-

Returns

void


wire()

function wire(): void;

Defined in: lib/clipboard-sync.js:922

Registers the listeners; called before input attaches so the hold runs first.

Returns

void


unwire()

function unwire(): void;

Defined in: lib/clipboard-sync.js:932

Removes the listeners wire registered.

Returns

void

On this page

InterfacesMultipartClipboardStatePropertiesbeginParametersReturnspushParametersReturnsassembleReturnsbase64mimeTypetotalSizeresetReturnsinProgressmimeTypetotalSizereceivedSizeTaggedClipboardFetchPropertiesarmReturnsarmLegacyWindowParametersReturnsconsumeReturnsLocalClipboardSenderPropertiesreadAndSendReturnsmaybeInitialReturnsgetSendInFlightReturnsDeferredClipboardWriterPropertieswriteParametersReturnsflushReturnsgetInFlightReturnsClipboardSyncPropertiessigParametersReturnsshouldSendParametersReturnsmarkSyncedParametersReturnsresolveServerParametersReturnscaptureLocalImageSigReturnsrequestParametersReturnscopyViaExecCommandParametersReturnslastTextlastBloblastMimeType AliasesLocalClipboardContentType ParametersVariablesCLIPBOARD_PREVIEW_LIMITFunctionsreencodeBlobAsPng()ParametersReturnsThrowslocalClipboardBlocker()ReturnswriteImageToLocalClipboard()ParametersReturnsThrowsreadLocalClipboard()ParametersReturnsThrowscreateMultipartClipboardState()ReturnscreateTaggedClipboardFetch()ReturnscreateLocalClipboardSender()ParametersReturnsreadAndSend()ReturnscreateDeferredClipboardWriter()ReturnsattemptOnce()ParametersReturnswrite()ParametersReturnsclipboardPreviewMessage()ParametersReturnstypetexttruncatedtotalLengthcreateClipboardSync()ParametersReturnssigOf()ParametersReturnsfulllegacyshouldSend()ParametersReturnsmarkSynced()ParametersReturnsresolveServer()ParametersReturnscaptureLocalImageSig()Returnsrequest()ParametersReturnscopyViaExecCommand()ParametersReturnscreateClipboardGestures()ParametersReturnswireReturnsunwireReturnsdropHeldPasteKeydowns()ReturnsholdPasteWhileClipboardInFlight()ParametersReturnsonCopyKeydown()ParametersReturnsonPaste()ParametersReturnswire()Returnsunwire()Returns
Edit on GitHub