diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py index e9a5b4e83..fc955baa7 100644 --- a/debug_toolbar/panels/templates/panel.py +++ b/debug_toolbar/panels/templates/panel.py @@ -7,6 +7,7 @@ from django.core import signing from django.db.models.query import QuerySet, RawQuerySet from django.template import RequestContext, Template +from django.templatetags.static import static from django.test.signals import template_rendered from django.test.utils import instrumented_test_render from django.urls import path @@ -225,3 +226,9 @@ def generate_stats(self, request, response): "context_processors": context_processors, } ) + + @property + def scripts(self): + scripts = super().scripts + scripts.append(static("debug_toolbar/js/copy_context.js")) + return scripts diff --git a/debug_toolbar/static/debug_toolbar/js/copy_context.js b/debug_toolbar/static/debug_toolbar/js/copy_context.js new file mode 100644 index 000000000..1452dff92 --- /dev/null +++ b/debug_toolbar/static/debug_toolbar/js/copy_context.js @@ -0,0 +1,52 @@ +document.addEventListener("click", (event) => { + if (event.target.classList.contains("copy-context")) { + const context = event.target.getAttribute("data-context"); + + if (context) { + try { + const decodedContext = decodeUnicode(context); + const listContext = makeList(decodedContext); + + navigator.clipboard + .writeText(listContext) + .then(() => { + const originalText = event.target.textContent; + event.target.textContent = "Copied!"; + + setTimeout(() => { + event.target.textContent = originalText; + }, 2000); + }) + .catch((error) => { + console.error("Failed to copy context:", error); + }); + } catch (error) { + console.error("Error processing context:", error); + } + } + } +}); + +/** + * Decodes escaped Unicode characters in a string. + * + * @param {string} text - The text containing escaped Unicode characters. + * @returns {string} - Decoded text. + */ +function decodeUnicode(text) { + return text.replace(/\\u[\dA-Fa-f]{4}/g, (match) => { + return String.fromCharCode( + Number.parseInt(match.replace("\\u", ""), 16) + ); + }); +} + +/** + * Wraps multiple JSON objects into a list format. + * + * @param {string} text - The raw text containing multiple JSON objects. + * @returns {string} - Properly formatted JSON array string. + */ +function makeList(text) { + return `[${text.replace(/\n(?=\{)/g, ",")}]`; +} diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html index 121c086a8..6747fbe73 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/templates.html +++ b/debug_toolbar/templates/debug_toolbar/panels/templates.html @@ -20,6 +20,9 @@

{% blocktrans count templates|length as template_count %}Template{% plural %
{% trans "Toggle context" %} + {{ template.context }}