Skip to content

Instantly share code, notes, and snippets.

@khasky
Created April 8, 2026 06:36
Show Gist options
  • Select an option

  • Save khasky/0c630e4a1adce180c10a76057bb504b7 to your computer and use it in GitHub Desktop.

Select an option

Save khasky/0c630e4a1adce180c10a76057bb504b7 to your computer and use it in GitHub Desktop.
useCopyright React Hook
// On copy, rewrites clipboard text to append a random copyright line from the list (skips URLs, IP:port, and selections inside code/inputs).
import React from 'react';
import sample from 'lodash/sample';
const IP_ADDR_PORT_REGEX = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5})$/;
const URL_REGEX =
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;
const isURL = (str: string) => URL_REGEX.test(str);
const isIpAddrPort = (str: string) => IP_ADDR_PORT_REGEX.test(str);
function isInsideCodeElement(selection: Selection | null) {
if (!selection || !selection.anchorNode) return false;
let node: Node | null = selection.anchorNode;
while (node) {
if (
node.nodeType === Node.ELEMENT_NODE &&
['code', 'input'].includes((node as HTMLElement).tagName.toLowerCase())
) {
return true;
}
node = node.parentNode;
}
const activeElement = document.activeElement;
if (activeElement && activeElement.tagName.toLowerCase() === 'input') {
return true;
}
return false;
}
export function useCopyright(copyrightStrings: string[] = []) {
function copyListener(e: ClipboardEvent) {
if (copyrightStrings.length === 0) {
return;
}
const selection = window.getSelection();
const text = selection?.toString()?.trim();
if (text && !isURL(text) && !isIpAddrPort(text) && !isInsideCodeElement(selection)) {
const copyright = sample(copyrightStrings);
let setTo = `${text} ${copyright}`;
const textAsArr = text.split('.');
if (textAsArr.length > 1) {
const middleIndex = Math.floor(textAsArr.length / 2);
const part1 = textAsArr.slice(0, middleIndex).join('.');
const part2 = textAsArr.slice(middleIndex, textAsArr.length).join('.');
setTo = [part1, copyright, part2.substring(1, part2.length)].join('. ');
}
e?.clipboardData?.setData('text/plain', setTo);
e.preventDefault();
}
}
React.useEffect(() => {
document.addEventListener('copy', copyListener);
return () => document.removeEventListener('copy', copyListener);
}, []);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment