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.

1 Like

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);
})();