Zendesk — copy title of ticket v1.0.0
← Back to User Scripts
Script Content
// ==UserScript==
// @name Zendesk: Copy Title of Ticket
// @namespace https://www.timhilton.xyz/user-scripts
// @version 2.0.1
// @description Displays a copy link in the ticket title.
// @author Tim Hilton using Jules and GitHub Copilot
// @match https://*.zendesk.com/agent/tickets/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const LOG_PREFIX = '[Zendesk: Copy Title]';
console.debug(`${LOG_PREFIX} Script initialized`);
const injectStyles = () => {
const style = document.createElement('style');
style.textContent = `
section[data-theme='light'] .copy-title-icon svg path {
fill: rgb(92, 105, 112);
}
section[data-theme='dark'] .copy-title-icon svg path {
fill: rgb(145, 156, 165);
}
.copy-title-icon {
border-radius: 4px;
transition: border-color 0.25s ease-in-out, box-shadow 0.1s ease-in-out, background-color 0.25s ease-in-out, color 0.25s ease-in-out, outline-color 0.1s ease-in-out, z-index 0.25s ease-in-out;
}
.copy-title-icon:hover {
background-color: rgba(38, 148, 214, 0.08);
color: rgb(176, 184, 190);
}
`;
document.head.appendChild(style);
};
const COPY_ICON_SVG = `
`;
const TICK_ICON_SVG = `
`;
const handleCopyClick = (icon, titleInput) => {
console.debug(`${LOG_PREFIX} handleCopyClick() called`);
const title = titleInput.value;
const organisationElement = document.querySelector('[data-tracking-id="tabs-nav-item-organizations"]');
const organisation = organisationElement ? organisationElement.textContent.trim() : 'Unknown Organisation';
const ticketIdMatch = window.location.href.match(/tickets\/(\d+)/);
const ticketId = ticketIdMatch ? ticketIdMatch[1] : 'Unknown Ticket ID';
const formattedString = `${ticketId} ${organisation} — ${title}`;
const url = window.location.href;
console.debug(`${LOG_PREFIX} Copying formatted string and URL to clipboard...`);
navigator.clipboard.writeText(url).then(() => {
navigator.clipboard.writeText(formattedString).then(() => {
console.log(`${LOG_PREFIX} 🎉 Successfully copied title to clipboard!`);
icon.innerHTML = TICK_ICON_SVG;
setTimeout(() => {
if (document.body.contains(icon)) {
icon.innerHTML = COPY_ICON_SVG;
}
}, 1000);
});
});
};
const setupCopyIcon = () => {
console.debug(`${LOG_PREFIX} setupCopyIcon() called`);
if (document.querySelector('.copy-title-icon')) {
console.debug(`${LOG_PREFIX} Copy icon already exists, skipping`);
return;
}
const titleInput = document.querySelector('input[data-test-id="omni-header-subject"]');
if (!titleInput) {
console.debug(`${LOG_PREFIX} Title input not found`);
return;
}
console.debug(`${LOG_PREFIX} Title input found`);
const thirdAncestor = titleInput.parentElement?.parentElement?.parentElement;
if (!thirdAncestor) {
console.debug(`${LOG_PREFIX} Third ancestor not found`);
return;
}
const icon = document.createElement('div');
icon.innerHTML = COPY_ICON_SVG;
icon.classList.add('copy-title-icon');
icon.style.width = '32px';
icon.style.height = '32px';
icon.style.display = 'flex';
icon.style.alignItems = 'center';
icon.style.justifyContent = 'center';
icon.style.cursor = 'pointer';
icon.style.paddingLeft = '10px';
icon.style.paddingRight = '10px';
icon.addEventListener('click', () => handleCopyClick(icon, titleInput));
thirdAncestor.after(icon);
console.log(`${LOG_PREFIX} 🎉 Copy icon added successfully!`);
};
injectStyles();
let mutationCount = 0;
const observer = new MutationObserver(() => {
mutationCount++;
console.debug(`${LOG_PREFIX} MutationObserver fired (#${mutationCount})`);
setupCopyIcon();
});
console.debug(`${LOG_PREFIX} Starting MutationObserver on document.body`);
observer.observe(document.body, {
childList: true,
subtree: true,
});
})();