diff --git a/CHANGELOG.md b/CHANGELOG.md index ab249ae..47e2ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ All notable changes to `phalcon/debugbar` are documented here. The format is bas - Explain in `Provider` why the boot request uses `get()` and the history request uses `getShared()`. [#98](https://github.com/phalcon/debugbar/issues/98) - Show `true` and `false` for booleans in messages, show `[]`, `null` and the class name of an object in the grid, and time each query from its own start (0 when there is no start). [#99](https://github.com/phalcon/debugbar/issues/99) - Debug page: show the previous exceptions, fix the API links, read each file one time, drop the arguments from the `debugVar()` backtrace, match `\vendor\`, and end the line of a skipped `Dump` key. [#100](https://github.com/phalcon/debugbar/issues/100) +- Debug page: copy on `http://` pages, show "Copy failed" when a copy fails, and encode the paths in the PhpStorm and VS Code links. [#101](https://github.com/phalcon/debugbar/issues/101) - Test HTML writes, the collapse state and `localStorage` errors in the JS tests, and keep every listener in the fake DOM. [#103](https://github.com/phalcon/debugbar/issues/103) ## [0.4.0](https://github.com/phalcon/debugbar/releases/tag/v0.4.0) (2026-07-14) diff --git a/resources/assets/debug.js b/resources/assets/debug.js index d5a7623..f543dc8 100644 --- a/resources/assets/debug.js +++ b/resources/assets/debug.js @@ -25,7 +25,9 @@ } function escAttr(s) { - return String(s).replace(/"/g, """); + return String(s).replace(/[&"]/g, function (c) { + return { "&": "&", "\"": """ }[c]; + }); } /* ---- PHP syntax highlighter (no dependencies) ---- */ @@ -88,10 +90,30 @@ toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 1400); } + /* The clipboard API is only in secure contexts (https, localhost). */ + function copyWithTextarea(text) { + var area = document.createElement("textarea"); + var copied = false; + area.value = text; + area.setAttribute("readonly", ""); + area.style.position = "fixed"; + area.style.opacity = "0"; + document.body.appendChild(area); + area.select(); + try { copied = document.execCommand("copy"); } catch (e) {} + document.body.removeChild(area); + return copied; + } + function copy(text, msg) { if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(text).then(function () { toast(msg); }); + navigator.clipboard.writeText(text).then( + function () { toast(msg); }, + function () { toast("Copy failed"); } + ); + return; } + toast(copyWithTextarea(text) ? msg : "Copy failed"); } /* ---- theme ---- */ @@ -127,13 +149,16 @@ if (el.querySelector(".mini")) { continue; } var file = el.getAttribute("data-file"); var line = el.getAttribute("data-line") || ""; - var enc = encodeURI(file); + /* PhpStorm takes the path as one query value; VS Code takes a path, so encode each segment. */ + var phpstorm = "phpstorm://open?file=" + encodeURIComponent(file) + "&line=" + encodeURIComponent(line); + var vscode = "vscode://file" + file.split("/").map(encodeURIComponent).join("/") + + (line ? ":" + encodeURIComponent(line) : ""); var mini = document.createElement("span"); mini.className = "mini"; mini.innerHTML = '' + - 'PhpStorm' + - 'VS Code'; + 'PhpStorm' + + 'VS Code'; el.appendChild(mini); } }