UNPKG

@mirawision/copily

Version:

A comprehensive clipboard manipulation library for TypeScript, providing functionalities for copying/pasting text, HTML, JSON, images, files, and smart content detection.

308 lines (307 loc) 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fallbackCopyText = exports.clearClipboard = exports.pasteFile = exports.copyFile = exports.pasteImage = exports.copyImage = exports.pasteJSON = exports.copyJSON = exports.pasteHTML = exports.copyHTML = exports.pasteText = exports.copyText = void 0; const types_1 = require("./types"); const utils_1 = require("./utils"); /** * Copy plain text to the system clipboard. * * Requires a secure context and Clipboard API support. * * @param text Plain text to copy. * @returns Resolves when the text is written to the clipboard. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @throws {ClipboardPermissionError} If the write operation is denied or fails. * @example * await copyText('Hello world'); */ async function copyText(text) { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { await navigator.clipboard.writeText(text); } catch (error) { throw new types_1.ClipboardPermissionError('Failed to copy text to clipboard'); } } exports.copyText = copyText; /** * Read plain text from the system clipboard. * * @returns Resolves to the clipboard text. Returns an empty string if the * clipboard is empty or cannot be read. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @example * const text = await pasteText(); */ async function pasteText() { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { return await navigator.clipboard.readText(); } catch (error) { // Return empty string if clipboard is empty or not accessible return ''; } } exports.pasteText = pasteText; /** * Copy HTML content to the clipboard with a plain-text fallback. * * The HTML is sanitized before being written. A text/plain version (tags * stripped) is also provided for consumers that only support plain text. * * @param html HTML string to copy. * @returns Resolves when the HTML is written to the clipboard. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @throws {ClipboardPermissionError} If the write operation fails. * @example * await copyHTML('<strong>Hello</strong>'); */ async function copyHTML(html) { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } const sanitizedHTML = (0, utils_1.sanitizeHTML)(html); try { const clipboardItem = new ClipboardItem({ 'text/html': new Blob([sanitizedHTML], { type: 'text/html' }), 'text/plain': new Blob([html.replace(/<[^>]*>/g, '')], { type: 'text/plain' }) }); await navigator.clipboard.write([clipboardItem]); } catch (error) { throw new types_1.ClipboardPermissionError('Failed to copy HTML to clipboard'); } } exports.copyHTML = copyHTML; /** * Read HTML content from the clipboard. * * Attempts to read 'text/html' first, falling back to plain text via * {@link pasteText} when HTML is not present. * * @returns Resolves to the HTML string (or plain text). Returns an empty * string on failure. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. */ async function pasteHTML() { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { const items = await navigator.clipboard.read(); for (const item of items) { if (item.types.includes('text/html')) { const htmlBlob = await item.getType('text/html'); return await htmlBlob.text(); } } // Fallback to plain text return await pasteText(); } catch (error) { return ''; } } exports.pasteHTML = pasteHTML; /** * Copy a JSON-serializable object to the clipboard as formatted text. * * @param obj Object to stringify and copy. * @returns Resolves when the JSON string is written to the clipboard. * @example * await copyJSON({ id: 1 }); */ async function copyJSON(obj) { const jsonString = JSON.stringify(obj, null, 2); await copyText(jsonString); } exports.copyJSON = copyJSON; /** * Parse the clipboard content as JSON. * * @returns Resolves to the parsed object. * @throws {ClipboardFormatError} If the clipboard content is not valid JSON * or cannot be parsed. * @example * const data = await pasteJSON(); */ async function pasteJSON() { const text = await pasteText(); if (!(0, utils_1.isJSON)(text)) { throw new types_1.ClipboardFormatError('Clipboard content is not valid JSON'); } try { return JSON.parse(text); } catch (error) { throw new types_1.ClipboardFormatError('Failed to parse clipboard content as JSON'); } } exports.pasteJSON = pasteJSON; /** * Copy an image to the clipboard. * * Accepts a Blob, an HTMLImageElement, or a URL string which will be fetched * and converted to a Blob. * * @param image Image Blob, HTMLImageElement, or URL string. * @returns Resolves when the image is written to the clipboard. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @throws {ClipboardPermissionError} If the write operation fails. */ async function copyImage(image) { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } let imageBlob; if (image instanceof Blob) { imageBlob = image; } else { imageBlob = await (0, utils_1.imageToBlob)(image); } try { const clipboardItem = new ClipboardItem({ [imageBlob.type]: imageBlob }); await navigator.clipboard.write([clipboardItem]); } catch (error) { throw new types_1.ClipboardPermissionError('Failed to copy image to clipboard'); } } exports.copyImage = copyImage; /** * Read an image from the clipboard. * * @returns Resolves to an image Blob, or null if no image is present. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. */ async function pasteImage() { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { const items = await navigator.clipboard.read(); for (const item of items) { for (const type of item.types) { if (type.startsWith('image/')) { const imageBlob = await item.getType(type); return imageBlob; } } } return null; } catch (error) { return null; } } exports.pasteImage = pasteImage; /** * Copy a file to the clipboard. * * @param file File or Blob to copy. * @returns Resolves when the file is written to the clipboard. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @throws {ClipboardPermissionError} If the write operation fails. */ async function copyFile(file) { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { const clipboardItem = new ClipboardItem({ [file.type]: file }); await navigator.clipboard.write([clipboardItem]); } catch (error) { throw new types_1.ClipboardPermissionError('Failed to copy file to clipboard'); } } exports.copyFile = copyFile; /** * Read a non-image file from the clipboard. * * The first non-image item is returned as a File, or null if not found. * * @returns Resolves to a File instance or null if none is present. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. */ async function pasteFile() { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { const items = await navigator.clipboard.read(); for (const item of items) { for (const type of item.types) { if (!type.startsWith('image/')) { const fileBlob = await item.getType(type); return new File([fileBlob], 'clipboard-file', { type }); } } } return null; } catch (error) { return null; } } exports.pasteFile = pasteFile; /** * Clear clipboard content by writing an empty text payload. * * @returns Resolves when the clipboard has been cleared. * @throws {ClipboardUnsupportedError} If the Clipboard API is unavailable. * @throws {ClipboardPermissionError} If the write operation fails. */ async function clearClipboard() { if (!(0, utils_1.supportsClipboardAPI)()) { throw new types_1.ClipboardUnsupportedError(); } try { // Write empty content to clear clipboard const emptyItem = new ClipboardItem({ 'text/plain': new Blob([''], { type: 'text/plain' }) }); await navigator.clipboard.write([emptyItem]); } catch (error) { throw new types_1.ClipboardPermissionError('Failed to clear clipboard'); } } exports.clearClipboard = clearClipboard; /** * Legacy fallback to copy text using `document.execCommand('copy')`. * * This is synchronous and intended as a last-resort for environments without * Clipboard API support. * * @param text Plain text to copy. * @example * fallbackCopyText('hello'); */ function fallbackCopyText(text) { const textArea = (0, utils_1.createTemporaryElement)(); try { textArea.value = text; textArea.select(); textArea.setSelectionRange(0, 99999); // For mobile devices const successful = document.execCommand('copy'); if (!successful) { throw new Error('execCommand copy failed'); } } finally { (0, utils_1.removeTemporaryElement)(textArea); } } exports.fallbackCopyText = fallbackCopyText;