← Files DataARCHIVED FILE

templates/data-app/inline/clipboard.js

1.59 KB · Sep 30, 2026 · 23:19 UTC

↓ Download file

/** Native selection copy also works in hosts that disallow the async Clipboard API. */
export function copySelection(content, trigger) {
  const document = trigger.ownerDocument;
  const selection = document.getSelection();
  const ranges = Array.from({ length: selection.rangeCount }, (_, i) => selection.getRangeAt(i).cloneRange());
  const target = document.createElement("div");
  target.contentEditable = "true";
  target.style.cssText = "position:fixed;left:-10000px;top:0;white-space:pre";
  target.append(content);
  document.body.append(target);
  try {
    target.focus();
    const range = document.createRange();
    range.selectNodeContents(target);
    selection.removeAllRanges();
    selection.addRange(range);
    if (!document.execCommand("copy")) throw new Error("Copy is unavailable in this view.");
  } finally {
    target.remove();
    selection.removeAllRanges();
    for (const range of ranges) if (range.commonAncestorContainer.isConnected) selection.addRange(range);
    trigger.focus({ preventScroll: true });
  }
}

export async function imageForClipboard(blob) {
  const url = await new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(blob);
  });
  const image = new Image();
  image.src = url;
  await image.decode();
  return image;
}

export async function copyPng(png, trigger) {
  try {
    await navigator.clipboard.write([new ClipboardItem({ "image/png": png })]);
  } catch { copySelection(await imageForClipboard(await png), trigger); }
}

SHA-256: 36bccf385663e72a4b34adae090b453333a3f7a75a2c79ffe2fbc29bba829d8f