Developer Assistant AI copy code button BUG

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., &lt;, &gt;, &amp;) 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&lt;override&gt;()&lt;suspends&gt; : 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:

  1. Visit a conversation page like https://dev.epicgames.com/community/assistant/fortnite/conversations/
  2. Copy a code snippet displayed by the assistant
  3. Paste it into a plain text editor (e.g., Notepad or VS Code)
  4. 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);
})();