Files
skills/docs/foundation/data/clipboard/src/manager/helpers.md
2026-05-29 08:20:56 +01:00

3.2 KiB

manager/helpers.ts

Purpose

Pure helpers shared between manager.ts and operations.ts. Keeps both files under the line budget by housing the navigator / window resolution, the structured failure builder, the focus-error detector, and the ClipboardItem build + read pipelines in a single, side-effect-free module that tests can exercise directly.

Features

  • resolveNavigator(nav?) — caller, global, or empty shim.
  • resolveWindow(win?) — caller, global, or undefined.
  • failure(code, message, method) — typed ClipboardResult failure.
  • isFocusError(cause) — detects the canonical focus-required rejection.
  • buildClipboardItems(data) — MIME → Blob map for ClipboardItem.
  • readClipboardItems(items, sanitizer?) — pulls text / html / image / blobs out of a list of ClipboardItem instances.

Usage: Basic

import { failure, CLIPBOARD_CODES } from "@suaveplan/clipboard";

const r = failure(CLIPBOARD_CODES.NO_CONTENT, "empty", "api");

Usage: Advanced

import {
    buildClipboardItems,
    readClipboardItems,
} from "@suaveplan/clipboard";

const items = buildClipboardItems({
    text: "hello",
    html: "<b>hi</b>",
    image: pngBlob,
    blobs: { "application/json": jsonBlob },
});

const clipboardItem = new ClipboardItem(items);
await navigator.clipboard.write([clipboardItem]);

// Round-trip back:
const read = await navigator.clipboard.read();
const data = await readClipboardItems(read, (html) => html.trim());

API Reference

resolveNavigator(nav?: ClipboardNavigator): ClipboardNavigator

Returns nav when defined, otherwise the global navigator, otherwise the empty object {}.

resolveWindow(win?: ClipboardWindowLike): ClipboardWindowLike | undefined

Returns win when defined, otherwise the global window, otherwise undefined.

failure<T>(code, message, method): ClipboardResult<T>

Returns { success: false, error: message, code, method }. Generic over T so call sites compose with their own result types.

isFocusError(cause: unknown): boolean

Returns true when cause is an Error whose name === "NotAllowedError" and whose message (case-insensitively) contains "focus". The browser signals "Clipboard needs page focus" with this exact shape.

buildClipboardItems(data: ClipboardData): Record<string, Blob>

Builds a MIME → Blob map for new ClipboardItem(...). Skips empty text and html. Defaults a typeless image blob to image/png.

readClipboardItems(items, sanitizer?): Promise<ClipboardData>

Iterates every entry of every ClipboardItem, populating text, html, image, and blobs. The first image-typed entry wins. The sanitizer (when supplied) is applied to the raw text/html payload before returning.

Implementation Notes

The helpers are intentionally side-effect-free and free of telemetry calls so the same code runs from runCopyText, runCopyRich, etc., without double-recording counters. buildClipboardItems and readClipboardItems are mirror images of each other — every MIME slot that buildClipboardItems writes is read back by readClipboardItems into the same ClipboardData shape. The focus-error detector matches both Chromium ("Document is not focused.") and WebKit ("Document not focused.") wording.