playwright-mcp
Version:
Playwright integration for ModelContext
244 lines (240 loc) • 9.56 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true});// src/mcp/toolbox.ts
var injectToolbox = () => {
window.addEventListener("DOMContentLoaded", function() {
const inIframe = window.self !== window.top;
if (inIframe) {
return;
}
if (document.querySelector("#mcp-sidebar")) {
return;
}
const pickingState = {
activePickingType: null,
mouseMoveHandler: null,
clickHandler: null
};
const toggleSidebar = (expanded) => {
const sidebar = document.querySelector("#mcp-sidebar");
const toggleButton = document.querySelector("#mcp-sidebar-toggle-button");
if (sidebar && toggleButton) {
const width = parseInt(sidebar.style.width);
sidebar.style.transform = expanded ? "translateX(0)" : `translateX(${width}px)`;
toggleButton.style.right = expanded ? `${width}px` : "0";
toggleButton.textContent = expanded ? "\u27E9" : "\u27E8";
localStorage.setItem("mcp-sidebar-expanded", expanded.toString());
}
};
const mcpStopPicking = () => {
if (pickingState.mouseMoveHandler) {
document.removeEventListener("mousemove", pickingState.mouseMoveHandler);
}
if (pickingState.clickHandler) {
document.removeEventListener("click", pickingState.clickHandler, true);
}
const previewOverlay = document.querySelector("#mcp-highlight-overlay-preview");
if (previewOverlay) {
previewOverlay.remove();
}
pickingState.activePickingType = null;
toggleSidebar(true);
};
const mcpStartPicking = (pickingType) => {
pickingState.activePickingType = pickingType;
toggleSidebar(false);
pickingState.mouseMoveHandler = (e) => {
const element = document.elementFromPoint(e.clientX, e.clientY);
const sidebar = document.querySelector("#mcp-sidebar");
const expandButton = document.querySelector("#mcp-sidebar-toggle-button");
if (!element || sidebar && sidebar.contains(element) || expandButton && expandButton.contains(element) || element.closest('[id^="mcp-highlight-overlay"]')) return;
let overlay = document.querySelector("#mcp-highlight-overlay-preview");
if (!overlay) {
overlay = document.createElement("div");
overlay.id = "mcp-highlight-overlay-preview";
overlay.style.cssText = `
position: fixed;
border: 1px dashed #4CAF50;
background: rgba(76, 175, 80, 0.1);
pointer-events: none;
z-index: 999998;
transition: all 0.2s ease;
`;
document.body.appendChild(overlay);
}
const rect = element.getBoundingClientRect();
overlay.style.top = rect.top + "px";
overlay.style.left = rect.left + "px";
overlay.style.width = rect.width + "px";
overlay.style.height = rect.height + "px";
};
pickingState.clickHandler = async (event) => {
const element = document.elementFromPoint(event.clientX, event.clientY);
const sidebar = document.querySelector("#mcp-sidebar");
const expandButton = document.querySelector("#mcp-sidebar-toggle-button");
if (!element || sidebar && sidebar.contains(element) || expandButton && expandButton.contains(element) || element.closest('[id^="mcp-highlight-overlay"]')) return;
event.stopPropagation();
event.preventDefault();
let message;
if (pickingState.activePickingType === "DOM") {
const html = element.outerHTML;
message = {
type: "DOM",
content: html,
windowUrl: window.location.href
};
} else {
const previewOverlay = document.querySelector("#mcp-highlight-overlay-preview");
if (previewOverlay) {
previewOverlay.style.display = "none";
}
const screenshotId = `screenshot-${Math.random().toString(36).substring(2)}`;
element.setAttribute("data-screenshot-id", screenshotId);
const screenshot = await window.takeScreenshot(`[data-screenshot-id="${screenshotId}"]`);
element.removeAttribute("data-screenshot-id");
if (previewOverlay) {
previewOverlay.style.display = "block";
}
message = {
type: "Image",
content: screenshot,
windowUrl: window.location.href
};
}
mcpStopPicking();
window.onElementPicked(message);
};
document.addEventListener("mousemove", pickingState.mouseMoveHandler);
document.addEventListener("click", pickingState.clickHandler, true);
};
window.mcpStartPicking = mcpStartPicking;
window.mcpStopPicking = mcpStopPicking;
const getSidebarWidth = () => {
const defaultWidth = localStorage.getItem("mcp-sidebar-width") || "500";
return parseInt(defaultWidth);
};
const createSidebar = () => {
const sidebar = document.createElement("div");
sidebar.id = "mcp-sidebar";
const defaultWidth = getSidebarWidth();
sidebar.style.cssText = `
position: fixed;
top: 0;
right: 0;
width: ${defaultWidth}px;
height: 100vh;
background: #f5f5f5;
border-left: 1px solid rgb(228, 228, 231);
z-index: 999999;
display: flex;
flex-direction: column;
overflow: hidden;
transition: transform 0.3s ease;
`;
const iframe = document.createElement("iframe");
iframe.name = "toolbox-frame";
iframe.src = "http://localhost:5174/";
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
`;
const resizeHandle = document.createElement("div");
resizeHandle.id = "mcp-resize-handle";
resizeHandle.style.cssText = `
position: absolute;
left: 0;
top: 0;
width: 4px;
height: 100%;
cursor: ew-resize;
background: transparent;
`;
let isResizing = false;
let lastX = 0;
const originalProperties = {};
const startResize = (e) => {
isResizing = true;
lastX = e.clientX;
const overlay = document.createElement("div");
overlay.className = "resize-overlay";
overlay.style.cssText = "position:absolute;top:0;left:0;right:0;bottom:0;z-index:1000;";
sidebar.appendChild(overlay);
document.addEventListener("mousemove", resize);
document.addEventListener("mouseup", stopResize);
originalProperties.bodyUserSelect = document.body.style.userSelect;
document.body.style.userSelect = "none";
const toggleButton = document.querySelector("#mcp-sidebar-toggle-button");
if (toggleButton) {
originalProperties.toggleButtonTransition = toggleButton.style.transition;
toggleButton.style.transition = "";
}
};
const resize = (e) => {
if (!isResizing) return;
const deltaX = lastX - e.clientX;
const newWidth = Math.min(
Math.max(400, sidebar.offsetWidth + deltaX),
window.innerWidth * 0.8
);
sidebar.style.width = `${newWidth}px`;
const toggleButton = document.querySelector("#mcp-sidebar-toggle-button");
if (toggleButton) {
toggleButton.style.right = `${newWidth}px`;
}
localStorage.setItem("mcp-sidebar-width", newWidth.toString());
lastX = e.clientX;
};
const stopResize = () => {
if (!isResizing) return;
isResizing = false;
const overlay = sidebar.querySelector(".resize-overlay");
if (overlay) sidebar.removeChild(overlay);
document.removeEventListener("mousemove", resize);
document.removeEventListener("mouseup", stopResize);
document.body.style.userSelect = originalProperties.bodyUserSelect;
const toggleButton = document.querySelector("#mcp-sidebar-toggle-button");
if (toggleButton) {
toggleButton.style.transition = originalProperties.toggleButtonTransition;
}
};
resizeHandle.addEventListener("mousedown", startResize);
sidebar.appendChild(resizeHandle);
sidebar.appendChild(iframe);
document.body.appendChild(sidebar);
};
const createSidebarToggleButton = () => {
const toggleButton = document.createElement("button");
toggleButton.id = "mcp-sidebar-toggle-button";
toggleButton.textContent = "\u27E9";
toggleButton.setAttribute("data-skip-recording", "true");
const sidebarWidth = getSidebarWidth();
toggleButton.style.cssText = `
position: fixed;
right: ${sidebarWidth}px;
top: 50%;
transform: translateY(-50%);
background: #f5f5f5;
border: 1px solid rgb(228, 228, 231);
border-right: none;
border-radius: 4px 0 0 4px;
font-size: 20px;
cursor: pointer;
padding: 8px;
color: rgb(17, 24, 39);
z-index: 999999;
transition: right 0.3s ease;
`;
document.body.appendChild(toggleButton);
let isExpanded = localStorage.getItem("mcp-sidebar-expanded") !== "false";
if (!isExpanded) {
toggleSidebar(false);
}
toggleButton.addEventListener("click", () => {
isExpanded = !isExpanded;
toggleSidebar(isExpanded);
});
};
createSidebar();
createSidebarToggleButton();
});
};
exports.injectToolbox = injectToolbox;