diff --git a/assets/client/client.css b/assets/client/client.css
index d978bf2b4..31cf213fe 100644
--- a/assets/client/client.css
+++ b/assets/client/client.css
@@ -2,6 +2,7 @@
position: fixed;
height: 40px;
width: 40px;
+ box-sizing: border-box;
padding-left: 5px;
padding-right: 5px;
border-radius: 10px;
@@ -40,11 +41,26 @@
background: white;
border: 1px solid #e2e8f0;
border-radius: 8px;
+ box-sizing: border-box;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
min-width: 160px;
z-index: 10000;
display: none;
font-size: 14px;
+ font-family:
+ ui-sans-serif,
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Helvetica,
+ Arial,
+ sans-serif;
+}
+
+#live-debugger-debug-tooltip p {
+ margin: 0;
}
#live-debugger-debug-tooltip .live-debugger-tooltip-option {
@@ -82,6 +98,16 @@
pointer-events: none;
padding: 8px 12px;
color: #333;
+ font-family:
+ ui-sans-serif,
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Helvetica,
+ Arial,
+ sans-serif;
}
.live-debugger-tooltip-content {
diff --git a/assets/client/client.js b/assets/client/client.js
index 8492c9790..90f6e8779 100644
--- a/assets/client/client.js
+++ b/assets/client/client.js
@@ -38,10 +38,36 @@ window.document.addEventListener('DOMContentLoaded', async () => {
debugChannel.push('pong', resp);
});
- initElementInspection({ baseURL, debugChannel, socketID: mainSocketID });
- initTooltip();
- initDebugMenu(metaTag, sessionURL, debugChannel);
- initHighlight(debugChannel);
+ const shadowHost = document.createElement('div');
+ shadowHost.style.position = 'absolute';
+ shadowHost.style.width = '0px';
+ shadowHost.style.height = '0px';
+ shadowHost.style.left = '0px';
+ shadowHost.style.top = '0px';
+ shadowHost.style.zIndex = '2147483647';
+ document.body.appendChild(shadowHost);
+
+ const shadowRoot = shadowHost.attachShadow({ mode: 'closed' });
+
+ const cssLink = document.createElement('link');
+ cssLink.rel = 'stylesheet';
+ cssLink.href = `${baseURL}/assets/live_debugger/client.css`;
+ shadowRoot.appendChild(cssLink);
+
+ const { debugButton } = initDebugMenu(
+ metaTag,
+ sessionURL,
+ debugChannel,
+ shadowRoot
+ );
+ initElementInspection({
+ baseURL,
+ debugChannel,
+ socketID: mainSocketID,
+ debugButton,
+ });
+ initTooltip(shadowRoot);
+ initHighlight(debugChannel, shadowRoot);
}
console.info(`LiveDebugger available at: ${baseURL}`);
diff --git a/assets/client/components/debug_menu.js b/assets/client/components/debug_menu.js
index 474de5288..6d25a421e 100644
--- a/assets/client/components/debug_menu.js
+++ b/assets/client/components/debug_menu.js
@@ -3,31 +3,57 @@ import initDebugOptions from './debug_options/debug_options';
import { dispatchCustomEvent } from '../utils/dom';
import { isDebugButtonEnabled } from '../utils/meta';
-export default function initDebugMenu(metaTag, liveDebuggerURL, debugChannel) {
+export default function initDebugMenu(
+ metaTag,
+ liveDebuggerURL,
+ debugChannel,
+ shadowRoot
+) {
const debugButton = initDebugButton();
const debugMenu = initDebugOptions({ liveDebuggerURL, debugChannel });
+ let suppressOutsideClick = false;
- if (isDebugButtonEnabled(metaTag)) {
- document.body.appendChild(debugButton);
- document.body.appendChild(debugMenu);
- }
+ const suppressNext = () => {
+ suppressOutsideClick = true;
+ setTimeout(() => {
+ suppressOutsideClick = false;
+ }, 0);
+ };
+
+ debugButton.addEventListener('click', suppressNext, true);
+ debugMenu.addEventListener('click', suppressNext, true);
+
+ const mount = () => {
+ shadowRoot.appendChild(debugButton);
+ shadowRoot.appendChild(debugMenu);
+ };
+
+ const unmount = () => {
+ debugButton.remove();
+ debugMenu.remove();
+ };
+
+ if (isDebugButtonEnabled(metaTag)) mount();
debugChannel.on('toggle-debug-button', ({ enabled }) => {
if (enabled) {
- document.body.appendChild(debugButton);
- document.body.appendChild(debugMenu);
+ mount();
} else {
- debugButton.remove();
- debugMenu.remove();
+ unmount();
}
});
// Hide menu when clicking outside
document.addEventListener('click', (event) => {
- if (
- !debugButton.contains(event.target) &&
- !debugMenu.contains(event.target)
- ) {
+ const path = event.composedPath?.() ?? [event.target];
+ const clickedInside =
+ path.includes(debugButton) ||
+ path.includes(debugMenu) ||
+ path.some((node) => node?.getRootNode?.() === shadowRoot);
+
+ if (suppressOutsideClick) return;
+
+ if (!clickedInside) {
dispatchCustomEvent('lvdbg:click-outside-debug-menu');
}
});
diff --git a/assets/client/components/tooltip/tooltip.js b/assets/client/components/tooltip/tooltip.js
index bd4a04052..4ba6c10c9 100644
--- a/assets/client/components/tooltip/tooltip.js
+++ b/assets/client/components/tooltip/tooltip.js
@@ -5,25 +5,25 @@ import { addTooltipArrow } from './tooltip_arrow';
const tooltipID = 'live-debugger-tooltip';
const highlightElementID = 'live-debugger-highlight-element';
-function getHighlightElement() {
- return document.getElementById(highlightElementID);
+function getHighlightElement(shadowRoot) {
+ return shadowRoot.querySelector(`#${highlightElementID}`);
}
-function removeTooltip() {
- const existingTooltip = document.getElementById(tooltipID);
+function removeTooltip(shadowRoot) {
+ const existingTooltip = shadowRoot.querySelector(`#${tooltipID}`);
if (existingTooltip) {
existingTooltip.remove();
}
}
-function showTooltip(data) {
- const highlightElement = getHighlightElement();
+function showTooltip(data, shadowRoot) {
+ const highlightElement = getHighlightElement(shadowRoot);
if (!highlightElement) {
return;
}
- removeTooltip();
- const tooltip = createTooltip(data);
+ removeTooltip(shadowRoot);
+ const tooltip = createTooltip(data, shadowRoot);
const positionData = positionTooltip(tooltip, highlightElement);
if (positionData) {
@@ -36,9 +36,9 @@ function showTooltip(data) {
}
}
-function handleTooltipResize() {
- const tooltip = document.getElementById(tooltipID);
- const highlightElement = getHighlightElement();
+function handleTooltipResize(shadowRoot) {
+ const tooltip = shadowRoot.querySelector(`#${tooltipID}`);
+ const highlightElement = getHighlightElement(shadowRoot);
if (tooltip && highlightElement) {
const positionData = positionTooltip(tooltip, highlightElement);
@@ -54,22 +54,26 @@ function handleTooltipResize() {
}
}
-function handleShowTooltipEvent(event) {
- showTooltip(event.detail);
+function handleShowTooltipEvent(event, shadowRoot) {
+ showTooltip(event.detail, shadowRoot);
}
-function handleRemoveTooltipEvent() {
- removeTooltip();
+function handleRemoveTooltipEvent(shadowRoot) {
+ removeTooltip(shadowRoot);
}
-function setupEventListeners() {
- window.addEventListener('resize', handleTooltipResize);
- window.addEventListener('scroll', handleTooltipResize);
+function setupEventListeners(shadowRoot) {
+ window.addEventListener('resize', () => handleTooltipResize(shadowRoot));
+ window.addEventListener('scroll', () => handleTooltipResize(shadowRoot));
- document.addEventListener('lvdbg:show-tooltip', handleShowTooltipEvent);
- document.addEventListener('lvdbg:remove-tooltip', handleRemoveTooltipEvent);
+ document.addEventListener('lvdbg:show-tooltip', (event) =>
+ handleShowTooltipEvent(event, shadowRoot)
+ );
+ document.addEventListener('lvdbg:remove-tooltip', () =>
+ handleRemoveTooltipEvent(shadowRoot)
+ );
}
-export default function initTooltip() {
- setupEventListeners();
+export default function initTooltip(shadowRoot) {
+ setupEventListeners(shadowRoot);
}
diff --git a/assets/client/components/tooltip/tooltip_creator.js b/assets/client/components/tooltip/tooltip_creator.js
index fcc3febed..60329f20e 100644
--- a/assets/client/components/tooltip/tooltip_creator.js
+++ b/assets/client/components/tooltip/tooltip_creator.js
@@ -41,13 +41,13 @@ function setModuleName(tooltip, data) {
moduleName.textContent = data.module || 'Element';
}
-export function createTooltip(data) {
+export function createTooltip(data, shadowRoot) {
const tooltip = createElement(tooltipHtml);
setModuleName(tooltip, data);
setTypeIcon(tooltip, data);
populateInfoSection(tooltip, data);
- document.body.appendChild(tooltip);
+ shadowRoot.appendChild(tooltip);
return tooltip;
}
diff --git a/assets/client/services/highlight.js b/assets/client/services/highlight.js
index 1fcbe2501..b6c385853 100644
--- a/assets/client/services/highlight.js
+++ b/assets/client/services/highlight.js
@@ -59,8 +59,8 @@ function createHighlightElement(activeElement, detail, id) {
return highlight;
}
-function removeHighlightElement() {
- const highlightElement = document.getElementById(highlightElementID);
+function removeHighlightElement(shadowRoot) {
+ const highlightElement = shadowRoot.querySelector(`#${highlightElementID}`);
if (highlightElement) {
highlightElement.remove();
@@ -69,8 +69,8 @@ function removeHighlightElement() {
dispatchCustomEvent('lvdbg:remove-tooltip');
}
-function handleHighlight({ detail }) {
- let highlightElement = document.getElementById(highlightElementID);
+function handleHighlight({ detail }, shadowRoot) {
+ let highlightElement = shadowRoot.querySelector(`#${highlightElementID}`);
if (highlightElement) {
highlightElement.remove();
@@ -95,13 +95,13 @@ function handleHighlight({ detail }) {
highlightElementID
);
- document.body.appendChild(highlightElement);
+ shadowRoot.appendChild(highlightElement);
showTooltip(detail);
}
}
-function handleHighlightResize() {
- const highlight = document.getElementById(highlightElementID);
+function handleHighlightResize(shadowRoot) {
+ const highlight = shadowRoot.querySelector(`#${highlightElementID}`);
if (highlight) {
const activeElement = document.querySelector(
`[${highlight.dataset.attr}="${highlight.dataset.val}"]`
@@ -115,7 +115,7 @@ function handleHighlightResize() {
}
}
-function handlePulse({ detail }) {
+function handlePulse({ detail }, shadowRoot) {
const activeElement = document.querySelector(
`[${detail.attr}="${detail.val}"]`
);
@@ -127,7 +127,7 @@ function handlePulse({ detail }) {
highlightPulseElementID
);
- document.body.appendChild(highlightPulse);
+ shadowRoot.appendChild(highlightPulse);
const w = highlightPulse.offsetWidth;
const h = highlightPulse.offsetHeight;
@@ -186,13 +186,21 @@ function showTooltip(detail) {
dispatchCustomEvent('lvdbg:show-tooltip', props);
}
-export default function initHighlight(debugChannel) {
- document.addEventListener('lvdbg:inspect-highlight', handleHighlight);
- document.addEventListener('lvdbg:inspect-pulse', handlePulse);
- document.addEventListener('lvdbg:inspect-clear', removeHighlightElement);
+export default function initHighlight(debugChannel, shadowRoot) {
+ document.addEventListener('lvdbg:inspect-highlight', (event) =>
+ handleHighlight(event, shadowRoot)
+ );
+ document.addEventListener('lvdbg:inspect-pulse', (event) =>
+ handlePulse(event, shadowRoot)
+ );
+ document.addEventListener('lvdbg:inspect-clear', () =>
+ removeHighlightElement(shadowRoot)
+ );
- debugChannel.on('highlight', (e) => handleHighlight({ detail: e }));
- debugChannel.on('pulse', (e) => handlePulse({ detail: e }));
+ debugChannel.on('highlight', (e) =>
+ handleHighlight({ detail: e }, shadowRoot)
+ );
+ debugChannel.on('pulse', (e) => handlePulse({ detail: e }, shadowRoot));
- window.addEventListener('resize', handleHighlightResize);
+ window.addEventListener('resize', () => handleHighlightResize(shadowRoot));
}
diff --git a/assets/client/services/inspect.js b/assets/client/services/inspect.js
index aade844a1..6fbd017b3 100644
--- a/assets/client/services/inspect.js
+++ b/assets/client/services/inspect.js
@@ -4,6 +4,7 @@ export default function initElementInspection({
baseURL,
debugChannel,
socketID,
+ debugButton,
}) {
let inspectMode = false;
let lastID = null;
@@ -141,7 +142,6 @@ export default function initElementInspection({
inspectMode = false;
lastID = null;
- const debugButton = document.getElementById('live-debugger-debug-button');
if (debugButton) debugButton.classList.remove('live-debugger-inspect-mode');
pushClearEvent();
@@ -162,7 +162,6 @@ export default function initElementInspection({
inspectMode = true;
- const debugButton = document.getElementById('live-debugger-debug-button');
if (debugButton) debugButton.classList.add('live-debugger-inspect-mode');
document.body.classList.add('live-debugger-inspect-mode');
diff --git a/lib/live_debugger.ex b/lib/live_debugger.ex
index 781737a91..66be8be5f 100644
--- a/lib/live_debugger.ex
+++ b/lib/live_debugger.ex
@@ -17,7 +17,6 @@ defmodule LiveDebugger do
@default_drainer [shutdown: 1000]
@js_path "assets/live_debugger/client.js"
- @css_path "assets/live_debugger/client.css"
@phoenix_path "assets/phoenix/phoenix.js"
def start(_type, _args) do
@@ -121,13 +120,11 @@ defmodule LiveDebugger do
version = Application.spec(@app_name)[:vsn] |> to_string()
live_debugger_js_url = "#{live_debugger_url}/#{@js_path}"
- live_debugger_css_url = "#{live_debugger_url}/#{@css_path}"
live_debugger_phoenix_url = "#{live_debugger_url}/#{@phoenix_path}"
assigns = %{
url: live_debugger_url,
js_url: live_debugger_js_url,
- css_url: live_debugger_css_url,
phoenix_url: live_debugger_phoenix_url,
browser_features?: browser_features?,
version: version,
diff --git a/lib/live_debugger/client/config_component.ex b/lib/live_debugger/client/config_component.ex
index c58c9f8c1..029572bc7 100644
--- a/lib/live_debugger/client/config_component.ex
+++ b/lib/live_debugger/client/config_component.ex
@@ -8,7 +8,6 @@ defmodule LiveDebugger.Client.ConfigComponent do
attr(:url, :string, required: true)
attr(:js_url, :string, required: true)
- attr(:css_url, :string, required: true)
attr(:phoenix_url, :string, required: true)
attr(:browser_features?, :boolean, default: true)
attr(:version, :string, default: nil)
@@ -20,7 +19,6 @@ defmodule LiveDebugger.Client.ConfigComponent do
<%= if @browser_features? do %>
-
<% end %>