URL: https://dev.epicgames.com/community/assistant/fortnite/conversations/*
Issue:
When copying code from the Epic Games Assistant conversation pages, the copied text includes escaped HTML entities (e.g., <
, >
, &
) instead of the original characters like <
, >
, and &
.
Expected Behavior:
Copied code should retain its original formatting. For example:
verse
OnBegin<override>()<suspends> : void =
Actual Behavior:
Clipboard content after copying appears as:
verse
OnBegin<override>()<suspends> : void =
Impact:
This behavior breaks code formatting and causes confusion when pasting into external editors. Developers may unknowingly use malformed code due to improper character encoding.
Steps to Reproduce:
- Visit a conversation page like
https://dev.epicgames.com/community/assistant/fortnite/conversations/
- Copy a code snippet displayed by the assistant
- Paste it into a plain text editor (e.g., Notepad or VS Code)
- Observe that special characters are encoded
Suggestion:
Ensure copy handlers provide raw source text to clipboard without escaping characters unnecessarily. If innerHTML is used for retrieval, consider decoding or switching to textContent.
I’ve temporarily fixed it on my end using this script so if anyone wants to use it, here’s the fix via VioletMonkey.
// ==UserScript==
// @name Epic Dev Clipboard Fix
// @match https://dev.epicgames.com/community/assistant/fortnite*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
// Decode HTML entities into their literal characters
const decodeEntities = str => {
const txt = document.createElement("textarea");
txt.innerHTML = str;
return txt.value;
};
// Your core interception logic in one init function
function initClipboardFix() {
// 1) Intercept DataTransfer.setData
const origSetData = DataTransfer.prototype.setData;
DataTransfer.prototype.setData = function(format, data) {
if (format === "text/plain") {
data = decodeEntities(data);
}
return origSetData.call(this, format, data);
};
// 2) Intercept navigator.clipboard.writeText
if (navigator.clipboard && navigator.clipboard.writeText) {
const origWrite = navigator.clipboard.writeText;
navigator.clipboard.writeText = function(text) {
return origWrite.call(this, decodeEntities(text));
};
}
// 3) Fallback copy-event handler (capture phase)
document.addEventListener("copy", e => {
let raw = e.clipboardData.getData("text/plain");
let clean = decodeEntities(raw);
e.clipboardData.setData("text/plain", clean);
e.preventDefault();
}, true);
}
// Run once on initial load
initClipboardFix();
// Re-run on SPA navigation (pushState, replaceState, popstate)
const wrapHistoryMethod = method => {
const orig = history[method];
history[method] = function(...args) {
const result = orig.apply(this, args);
initClipboardFix();
return result;
};
};
wrapHistoryMethod("pushState");
wrapHistoryMethod("replaceState");
window.addEventListener("popstate", initClipboardFix);
})();